Files
Bubberstation/code/modules/mining/boulder_processing/_boulder_processing.dm
ArcaneMusic 47430a4c04 A smattering of boulder-related QOL and fixes. (#86834)
## About The Pull Request

This pull request does a little bit of cleanup that I came across awhile
ago while looking at #85081, then forgot about, then came back up when I
was looking at some additional cleanup that needed to be done.

Reorganizes the handle_wave_conclusion function such that it can take a
force arg, to force a ore vent to be completed for debug purposes.
This also fixes a minor bug where vents, when successfully completed,
will still show a warning alert that the waves were failed, and that the
vent has closed up.

Grammar fix to the examine of boulder processing machines.

Moves the can-move behavior of boulders on conveyor belts and during
regular stacking to an early return over a late return (Thank you
Goofball for pointing that out).

Artifact boulders will now default to their artifact icon_state whenever
possible.

Finally, adds an additional sanity check to boulder processing to check
for custom material length, to attempt to avoid zero-content boulders
existing and running into the afforementioned #85081

## Why It's Good For The Game

Largely applies cleanup to several aspects of the boulder processing
system, and a handful of (hopefully) performance related rearrangements
to the existing layout of boulder processing code.

I can't for sure say that it'll fix the linked issue, due to the fact
that I could not for some reason re-create the issue in local testing,
but I'm hopeful that it's some kind of nebulous sanity-related issue.

Cleans up grammar in some spots, and provides a useful debug tool for
admin purposes when you just want a vent to flip. Might be a good
justification for a ore-manager admin panel later? 🤷

## Changelog

🆑
fix: Artifact boulders should keep their alien icon even after a first
round of processing.
fix: Boulders are less likely to exist with zero materials after
processing.
fix: Boulders should be slightly less laggy on conveyor belts.
fix: Grammar of refinery/smeltery examine is corrected.
/🆑
2024-09-28 04:32:15 +02:00

426 lines
14 KiB
Plaintext

/obj/machinery/bouldertech
name = "bouldertech brand refining machine"
desc = "You shouldn't be seeing this! And bouldertech isn't even a real company!"
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "ore_redemption"
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.5
anchored = TRUE
density = TRUE
/// What is the efficiency of minerals produced by the machine?
var/refining_efficiency = 1
/// How much durability of an boulder can we reduce
var/boulders_processing_count = 2
/// How many boulders can we hold maximum?
var/boulders_held_max = 1
/// What sound plays when a thing operates?
var/usage_sound = 'sound/machines/mining/wooping_teleport.ogg'
/// Silo link to its materials list.
var/datum/component/remote_materials/silo_materials
/// Mining points held by the machine for miners.
var/points_held = 0
///The action verb to display to players
var/action = "processing"
/// Cooldown associated with the sound played for collecting mining points.
COOLDOWN_DECLARE(sound_cooldown)
/// Cooldown associated with taking in boulds.
COOLDOWN_DECLARE(accept_cooldown)
/obj/machinery/bouldertech/Initialize(mapload)
. = ..()
silo_materials = AddComponent(
/datum/component/remote_materials, \
mapload, \
mat_container_flags = MATCONTAINER_NO_INSERT \
)
register_context()
/obj/machinery/bouldertech/post_machine_initialize()
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/machinery/bouldertech/Destroy()
silo_materials = null
return ..()
/obj/machinery/bouldertech/on_deconstruction(disassembled)
if(length(contents))
for(var/obj/item/boulder/boulder in contents)
remove_boulder(boulder)
/obj/machinery/bouldertech/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = CONTEXTUAL_SCREENTIP_SET
if(isnull(held_item))
context[SCREENTIP_CONTEXT_RMB] = "Remove Boulder"
return
if(istype(held_item, /obj/item/boulder))
context[SCREENTIP_CONTEXT_LMB] = "Insert boulder"
else if(istype(held_item, /obj/item/card/id) && points_held > 0)
context[SCREENTIP_CONTEXT_LMB] = "Claim mining points"
else if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel"
else if(held_item.tool_behaviour == TOOL_WRENCH)
context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]Anchor"
else if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR)
context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
/obj/machinery/bouldertech/examine(mob/user)
. = ..()
. += span_notice("The machine reads that it has [span_bold("[points_held] mining points")] stored. Swipe an ID to claim them.")
. += span_notice("Click to remove a stored boulder.")
var/boulder_count = 0
for(var/obj/item/boulder/potential_boulder in contents)
boulder_count += 1
. += span_notice("Storage capacity = <b>[boulder_count]/[boulders_held_max] boulders</b>.")
. += span_notice("Can process up to <b>[boulders_processing_count] boulders</b> at a time.")
if(anchored)
. += span_notice("It's [EXAMINE_HINT("anchored")] in place.")
else
. += span_warning("It needs to be [EXAMINE_HINT("anchored")] to start operations.")
. += span_notice("Its maintenance panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].")
if(panel_open)
. += span_notice("The whole machine can be [EXAMINE_HINT("pried")] apart.")
/obj/machinery/bouldertech/update_icon_state()
. = ..()
var/suffix = ""
if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER)))
suffix = "-off"
icon_state ="[initial(icon_state)][suffix]"
/obj/machinery/bouldertech/CanAllowThrough(atom/movable/mover, border_dir)
if(!anchored)
return FALSE
if(istype(mover, /obj/item/boulder))
return can_process_boulder(mover)
if(isgolem(mover))
return can_process_golem(mover)
return ..()
/**
* Can we process the boulder, checks only the boulders state & machines capacity
* Arguments
*
* * obj/item/boulder/new_boulder - the boulder we are checking
*/
/obj/machinery/bouldertech/proc/can_process_boulder(obj/item/boulder/new_boulder)
PRIVATE_PROC(TRUE)
SHOULD_BE_PURE(TRUE)
//machine not operational
if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER)))
return FALSE
//not a valid boulder
if(!istype(new_boulder) || QDELETED(new_boulder))
return FALSE
//someone is still processing this
if(new_boulder.processed_by)
return FALSE
//no space to hold boulders
var/boulder_count = 0
for(var/obj/item/boulder/potential_boulder in contents)
boulder_count += 1
if(boulder_count >= boulders_held_max)
return FALSE
//did we cooldown enough to accept a boulder
return COOLDOWN_FINISHED(src, accept_cooldown)
/**
* Accepts a boulder into the machine. Used when a boulder is first placed into the machine.
* Arguments
*
* * obj/item/boulder/new_boulder - the boulder to accept
*/
/obj/machinery/bouldertech/proc/accept_boulder(obj/item/boulder/new_boulder)
PRIVATE_PROC(TRUE)
if(!can_process_boulder(new_boulder))
return FALSE
new_boulder.forceMove(src)
COOLDOWN_START(src, accept_cooldown, 1.5 SECONDS)
return TRUE
/**
* Can we maim this golem
* Arguments
*
* * [rockman][mob/living/carbon/human] - the golem we are trying to main
*/
/obj/machinery/bouldertech/proc/can_process_golem(mob/living/carbon/human/rockman)
PRIVATE_PROC(TRUE)
SHOULD_BE_PURE(TRUE)
//not operatinal
if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER)))
return FALSE
//still in cooldown
if(!COOLDOWN_FINISHED(src, accept_cooldown))
return FALSE
//not processable
if(!istype(rockman) || QDELETED(rockman) || rockman.body_position != LYING_DOWN)
return FALSE
return TRUE
/**
* Accepts a golem to be processed, mainly for memes
* Arguments
*
* * [rockman][mob/living/carbon/human] - the golem we are trying to main
*/
/obj/machinery/bouldertech/proc/accept_golem(mob/living/carbon/human/rockman)
PRIVATE_PROC(TRUE)
if(!can_process_golem(rockman))
return
if(!use_energy(active_power_usage * 1.5, force = FALSE))
say("Not enough energy!")
return
maim_golem(rockman)
playsound(src, usage_sound, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
COOLDOWN_START(src, accept_cooldown, 3 SECONDS)
/// What effects actually happens to a golem when it is "processed"
/obj/machinery/bouldertech/proc/maim_golem(mob/living/carbon/human/rockman)
PROTECTED_PROC(TRUE)
Shake(duration = 1 SECONDS)
rockman.visible_message(span_warning("[rockman] is processed by [src]!"), span_userdanger("You get processed into bits by [src]!"))
rockman.investigate_log("was gibbed by [src] for being a golem", INVESTIGATE_DEATHS)
rockman.gib(DROP_ALL_REMAINS)
/obj/machinery/bouldertech/proc/on_entered(datum/source, atom/movable/atom_movable)
SIGNAL_HANDLER
if(istype(atom_movable, /obj/item/boulder))
INVOKE_ASYNC(src, PROC_REF(accept_boulder), atom_movable)
return
if(isgolem(atom_movable))
INVOKE_ASYNC(src, PROC_REF(accept_golem), atom_movable)
return
/**
* Looks for a boost to the machine's efficiency, and applies it if found.
* Applied more on the chemistry integration but can be used for other things if desired.
*/
/obj/machinery/bouldertech/proc/check_for_boosts()
PROTECTED_PROC(TRUE)
refining_efficiency = initial(refining_efficiency) //Reset refining efficiency to 100%.
/**
* Checks if this machine can process this material
* Arguments
*
* * datum/material/mat - the material to process
*/
/obj/machinery/bouldertech/proc/can_process_material(datum/material/mat)
PROTECTED_PROC(TRUE)
return FALSE
/obj/machinery/bouldertech/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(panel_open || user.combat_mode)
return NONE
if(istype(tool, /obj/item/boulder))
var/obj/item/boulder/my_boulder = tool
if(!accept_boulder(my_boulder))
balloon_alert_to_viewers("cannot accept!")
return ITEM_INTERACT_BLOCKING
balloon_alert_to_viewers("accepted")
return ITEM_INTERACT_SUCCESS
if(istype(tool, /obj/item/card/id))
if(points_held <= 0)
balloon_alert_to_viewers("no points to claim!")
if(!COOLDOWN_FINISHED(src, sound_cooldown))
return ITEM_INTERACT_BLOCKING
COOLDOWN_START(src, sound_cooldown, 1.5 SECONDS)
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, FALSE)
return ITEM_INTERACT_BLOCKING
var/obj/item/card/id/id_card = tool
var/amount = tgui_input_number(user, "How many mining points do you wish to claim? ID Balance: [id_card.registered_account.mining_points], stored mining points: [points_held]", "Transfer Points", max_value = points_held, min_value = 0, round_value = 1)
if(!amount)
return ITEM_INTERACT_BLOCKING
if(amount > points_held)
amount = points_held
id_card.registered_account.mining_points += amount
points_held = round(points_held - amount)
to_chat(user, span_notice("You claim [amount] mining points from \the [src] to [id_card]."))
return ITEM_INTERACT_SUCCESS
return NONE
/obj/machinery/bouldertech/wrench_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
if(default_unfasten_wrench(user, tool, time = 1.5 SECONDS) == SUCCESSFUL_UNFASTEN)
if(anchored)
begin_processing()
else
end_processing()
update_appearance(UPDATE_ICON_STATE)
return ITEM_INTERACT_SUCCESS
/obj/machinery/bouldertech/screwdriver_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-off", initial(icon_state), tool))
update_appearance(UPDATE_ICON_STATE)
return ITEM_INTERACT_SUCCESS
/obj/machinery/bouldertech/crowbar_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
if(default_deconstruction_crowbar(tool))
return ITEM_INTERACT_SUCCESS
/obj/machinery/bouldertech/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
return
if(!anchored)
balloon_alert(user, "anchor it first!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(panel_open)
balloon_alert(user, "close panel!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
var/obj/item/boulder/boulder = locate(/obj/item/boulder) in src
if(!boulder)
balloon_alert_to_viewers("no boulders to remove!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(!remove_boulder(boulder))
balloon_alert_to_viewers("no space to remove!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/**
* Accepts a boulder into the machinery, then converts it into minerals.
* If the boulder can be fully processed by this machine, we take the materials, insert it into the silo, and destroy the boulder.
* If the boulder has materials left, we make a copy of the boulder to hold the processable materials, take the processable parts, and eject the original boulder.
* Arguments
*
* * obj/item/boulder/chosen_boulder - The boulder to being breaking down into minerals.
*/
/obj/machinery/bouldertech/proc/breakdown_boulder(obj/item/boulder/chosen_boulder)
PRIVATE_PROC(TRUE)
if(QDELETED(chosen_boulder))
return
if(chosen_boulder.loc != src)
return
if(!use_energy(active_power_usage, force = FALSE))
say("Not enough energy!")
return
//if boulders are kept inside because there is no space to eject them, then they could be reprocessed, lets avoid that
if(!chosen_boulder.processed_by)
check_for_boosts()
//here we loop through the boulder's ores
var/list/rejected_mats = list()
for(var/datum/material/possible_mat as anything in chosen_boulder.custom_materials)
var/quantity = chosen_boulder.custom_materials[possible_mat] * refining_efficiency
if(!can_process_material(possible_mat))
rejected_mats[possible_mat] = quantity
continue
points_held = round(points_held + (quantity * possible_mat.points_per_unit * MINING_POINT_MACHINE_MULTIPLIER)) // put point total here into machine
if(!silo_materials.mat_container.insert_amount_mat(quantity, possible_mat))
rejected_mats[possible_mat] = quantity
//puts back materials that couldn't be processed
chosen_boulder.set_custom_materials(rejected_mats, refining_efficiency)
//break the boulder down if we have processed all its materials
if(!length(chosen_boulder.custom_materials))
playsound(loc, usage_sound, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
if(istype(chosen_boulder, /obj/item/boulder/artifact))
points_held = round((points_held + MINER_POINT_MULTIPLIER) * MINING_POINT_MACHINE_MULTIPLIER) /// Artifacts give bonus points!
chosen_boulder.break_apart()
return//We've processed all the materials in the boulder, so we can just destroy it in break_apart.
chosen_boulder.processed_by = src
//eject the boulder since we are done with it
remove_boulder(chosen_boulder)
/obj/machinery/bouldertech/process()
if(!anchored || panel_open || !is_operational || (machine_stat & (BROKEN | NOPOWER)))
return
var/boulders_found = FALSE
var/boulders_processed = boulders_processing_count
for(var/obj/item/boulder/potential_boulder in contents)
boulders_found = TRUE
if(boulders_processed <= 0)
break //Try again next time
boulders_processed--
if(potential_boulder.durability > 0)
potential_boulder.durability -= 1
if(potential_boulder.durability > 0)
continue
breakdown_boulder(potential_boulder)
boulders_found = FALSE
//when the boulder is removed it plays sound and displays a balloon alert. don't overlap when that happens
if(boulders_found)
playsound(loc, usage_sound, 29, FALSE, SHORT_RANGE_SOUND_EXTRARANGE)
balloon_alert_to_viewers(action)
/**
* Ejects a boulder from the machine. Used when a boulder is finished processing, or when a boulder can't be processed.
* Arguments
*
* * obj/item/boulder/specific_boulder - the boulder to remove
*/
/obj/machinery/bouldertech/proc/remove_boulder(obj/item/boulder/specific_boulder)
PRIVATE_PROC(TRUE)
if(QDELETED(specific_boulder))
return TRUE
if(locate(/obj/item/boulder) in loc) //There is an boulder in our loc. it has be removed so we don't clog up our loc with even more boulders
return FALSE
if(!length(specific_boulder.custom_materials))
specific_boulder.break_apart()
return TRUE
//Reset durability to little random lower value cause we have crushed it so many times
var/size = specific_boulder.boulder_size
if(size == BOULDER_SIZE_SMALL)
specific_boulder.durability = rand(2, BOULDER_SIZE_SMALL - 1)
else
specific_boulder.durability = rand(BOULDER_SIZE_SMALL, size - 1)
specific_boulder.processed_by = src //so we don't take in the boulder again after we just ejected it
specific_boulder.forceMove(drop_location())
specific_boulder.processed_by = null //now since move is done we can safely clear the reference
playsound(loc, 'sound/machines/ping.ogg', 50, FALSE)
return TRUE