diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index 6172121533a..b2fad9e88d2 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -22239,7 +22239,7 @@
/obj/machinery/conveyor{
id = "mining"
},
-/obj/machinery/bouldertech/brm,
+/obj/machinery/brm,
/turf/open/floor/iron,
/area/station/cargo/miningfoundry)
"hZP" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index 3b78dacec06..f1137f100e9 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -41240,7 +41240,7 @@
},
/obj/machinery/power/apc/auto_name/directional/north,
/obj/structure/cable,
-/obj/machinery/bouldertech/brm,
+/obj/machinery/brm,
/turf/open/floor/iron,
/area/mine/production)
"mHu" = (
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 6b8edaaba10..443f7049dea 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -23833,7 +23833,7 @@
/obj/machinery/conveyor{
id = "mining"
},
-/obj/machinery/bouldertech/brm,
+/obj/machinery/brm,
/turf/open/floor/iron,
/area/station/cargo/miningoffice)
"iIf" = (
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index 9a1372065a6..63a8be2d8cf 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -73293,7 +73293,7 @@
/area/station/security/detectives_office/private_investigators_office)
"thH" = (
/obj/structure/cable,
-/obj/machinery/bouldertech/brm,
+/obj/machinery/brm,
/obj/machinery/conveyor{
dir = 4;
id = "mining"
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index 57379c2353e..82c5377741f 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -17448,7 +17448,7 @@
/turf/open/floor/iron/dark,
/area/station/cargo/miningdock/oresilo)
"fdc" = (
-/obj/machinery/bouldertech/brm,
+/obj/machinery/brm,
/obj/machinery/conveyor{
dir = 8;
id = "mining"
diff --git a/code/controllers/subsystem/ore_generation.dm b/code/controllers/subsystem/ore_generation.dm
index 162bc2e99d9..ca8aa09d611 100644
--- a/code/controllers/subsystem/ore_generation.dm
+++ b/code/controllers/subsystem/ore_generation.dm
@@ -6,11 +6,11 @@ SUBSYSTEM_DEF(ore_generation)
runlevels = RUNLEVEL_GAME
/// All ore vents that are currently producing boulders.
- var/list/processed_vents = list()
- /// All the boulders that have been produced by ore vents to be pulled by BRM machines.
- var/list/available_boulders = list()
+ var/list/obj/structure/ore_vent/processed_vents = list()
/// All the ore vents that are currently in the game, not just the ones that are producing boulders.
- var/list/possible_vents = list()
+ var/list/obj/structure/ore_vent/possible_vents = list()
+ /// All the boulders that have been produced by ore vents to be pulled by BRM machines.
+ var/list/obj/item/boulder/available_boulders = list()
/**
* A list of all the minerals that are being mined by ore vents. We reset this list every time cave generation is done.
* Generally Should be empty by the time initialize ends on lavaland.
@@ -18,6 +18,7 @@ SUBSYSTEM_DEF(ore_generation)
* If we call cave_generation more than once, we copy a list from the lists in lists/ores_spawned.dm
*/
var/list/ore_vent_minerals = list()
+
/// A tracker of how many of each ore vent size we have in the game. Useful for tracking purposes.
var/list/ore_vent_sizes = list(
LARGE_VENT_TYPE = 0,
@@ -60,7 +61,7 @@ SUBSYSTEM_DEF(ore_generation)
return SS_INIT_SUCCESS
/datum/controller/subsystem/ore_generation/fire(resumed)
- available_boulders = list() // reset upon new fire.
+ available_boulders.Cut() // reset upon new fire.
for(var/obj/structure/ore_vent/current_vent as anything in processed_vents)
var/local_vent_count = 0
@@ -71,6 +72,4 @@ SUBSYSTEM_DEF(ore_generation)
if(local_vent_count >= MAX_BOULDERS_PER_VENT)
continue //We don't want to be accountable for literally hundreds of unprocessed boulders for no reason.
- var/obj/item/boulder/new_rock = current_vent.produce_boulder()
- available_boulders += new_rock
-
+ available_boulders += current_vent.produce_boulder()
diff --git a/code/datums/wires/brm.dm b/code/datums/wires/brm.dm
deleted file mode 100644
index c00d7875035..00000000000
--- a/code/datums/wires/brm.dm
+++ /dev/null
@@ -1,14 +0,0 @@
-/datum/wires/brm
- holder_type = /obj/machinery/bouldertech/brm
- proper_name = "Boulder Retrieval Matrix"
-
-/datum/wires/brm/New(atom/holder)
- add_duds(1)
- return ..()
-
-/datum/wires/brm/on_pulse(wire)
- var/obj/machinery/bouldertech/brm/brm_holder = holder
- if(brm_holder.panel_open)
- return
- brm_holder.toggle_auto_on()
-
diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
index 8e5f2d1dfc5..382cf60ddc6 100644
--- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
@@ -1559,7 +1559,7 @@
/obj/item/circuitboard/machine/brm
name = "Boulder Retrieval Matrix"
greyscale_colors = CIRCUIT_COLOR_SUPPLY
- build_path = /obj/machinery/bouldertech/brm
+ build_path = /obj/machinery/brm
req_components = list(
/datum/stock_part/capacitor = 1,
/datum/stock_part/scanning_module = 1,
@@ -1571,10 +1571,9 @@
greyscale_colors = CIRCUIT_COLOR_SUPPLY
build_path = /obj/machinery/bouldertech/refinery
req_components = list(
- /obj/item/reagent_containers/cup/beaker = 1,
/obj/item/assembly/igniter/condenser = 1,
/datum/stock_part/servo = 2,
- /datum/stock_part/matter_bin = 1,
+ /datum/stock_part/matter_bin = 2,
)
/obj/item/circuitboard/machine/smelter
diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm
index fe663efec48..08a4394346a 100644
--- a/code/game/objects/structures/lavaland/ore_vent.dm
+++ b/code/game/objects/structures/lavaland/ore_vent.dm
@@ -2,6 +2,7 @@
#define MINERAL_TYPE_OPTIONS_RANDOM 4
#define OVERLAY_OFFSET_START 0
#define OVERLAY_OFFSET_EACH 5
+#define MINERALS_PER_BOULDER 3
/obj/structure/ore_vent
name = "ore vent"
@@ -22,13 +23,8 @@
var/unique_vent = FALSE
/// What icon_state do we use when the ore vent has been tapped?
var/icon_state_tapped = "ore_vent_active"
-
/// A weighted list of what minerals are contained in this vent, with weight determining how likely each mineral is to be picked in produced boulders.
var/list/mineral_breakdown = list()
- /// How many rolls on the mineral_breakdown list are made per boulder produced? EG: 3 rolls means 3 minerals per boulder, with order determining percentage.
- var/minerals_per_boulder = 3
- /// How many minerals are picked to be in the ore vent? These are added to the mineral_breakdown list.
- var/minerals_per_breakdown = MINERAL_TYPE_OPTIONS_RANDOM
/// What size boulders does this vent produce?
var/boulder_size = BOULDER_SIZE_SMALL
/// Reference to this ore vent's NODE drone, to track wave success.
@@ -67,9 +63,9 @@
var/artifact_chance = 0
/// We use a cooldown to prevent the wave defense from being started multiple times.
COOLDOWN_DECLARE(wave_cooldown)
+ /// We use a cooldown to prevent players from tapping boulders rapidly from vents.
COOLDOWN_DECLARE(manual_vent_cooldown)
-
/obj/structure/ore_vent/Initialize(mapload)
if(mapload)
generate_description()
@@ -121,14 +117,14 @@
if(do_after(user, boulder_size * 1 SECONDS, src))
user.apply_damage(20, STAMINA)
playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
- produce_boulder()
+ produce_boulder(TRUE)
visible_message(span_notice("You've successfully produced a boulder! Boy are your arms tired."))
/obj/structure/ore_vent/attack_basic_mob(mob/user, list/modifiers)
. = ..()
if(!HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
return
- produce_boulder()
+ produce_boulder(TRUE)
/obj/structure/ore_vent/is_buckle_possible(mob/living/target, force, check_loc)
. = ..()
@@ -155,17 +151,6 @@
context[SCREENTIP_CONTEXT_LMB] = "Scan vent"
return CONTEXTUAL_SCREENTIP_SET
-/**
- * Picks n types of materials to pack into a boulder created by this ore vent, where n is this vent's minerals_per_boulder.
- * Then assigns custom_materials based on boulder_size, assigned via the ore_quantity_function
- */
-/obj/structure/ore_vent/proc/create_mineral_contents()
- var/list/refined_list = list()
- for(var/iteration in 1 to minerals_per_boulder)
- var/datum/material/material = pick_weight(mineral_breakdown)
- refined_list[material] += ore_quantity_function(iteration)
- return refined_list
-
/**
* This proc is called when the ore vent is initialized, in order to determine what minerals boulders it spawns can contain.
* The materials available are determined by SSore_generation.ore_vent_minerals, which is a list of all minerals that can be contained in ore vents for a given cave generation.
@@ -372,22 +357,38 @@
* @params apply_cooldown Should we apply a cooldown to producing boulders? Default's false, used by manual boulder production (goldgrubs, golems, etc).
*/
/obj/structure/ore_vent/proc/produce_boulder(apply_cooldown = FALSE)
- if(!COOLDOWN_FINISHED(src, manual_vent_cooldown))
+ RETURN_TYPE(/obj/item/boulder)
+
+ //cooldown applies only for manual processing by hand
+ if(apply_cooldown && !COOLDOWN_FINISHED(src, manual_vent_cooldown))
return
+
+ //produce the boulder
var/obj/item/boulder/new_rock
if(prob(artifact_chance))
new_rock = new /obj/item/boulder/artifact(loc)
else
new_rock = new /obj/item/boulder(loc)
- var/list/mats_list = create_mineral_contents()
Shake(duration = 1.5 SECONDS)
+
+ //decorate the boulder with materials
+ var/list/mats_list = list()
+ for(var/iteration in 1 to MINERALS_PER_BOULDER)
+ var/datum/material/material = pick_weight(mineral_breakdown)
+ mats_list[material] += ore_quantity_function(iteration)
new_rock.set_custom_materials(mats_list)
- new_rock.flavor_boulder(src)
+
+ //set size & durability
+ new_rock.boulder_size = boulder_size
+ new_rock.durability = rand(2, boulder_size) //randomize durability a bit for some flavor.
+ new_rock.boulder_string = boulder_icon_state
+ new_rock.update_appearance(UPDATE_ICON_STATE)
+
+ //start the cooldown & return the boulder
if(apply_cooldown)
COOLDOWN_START(src, manual_vent_cooldown, 10 SECONDS)
return new_rock
-
//comes with the station, and is already tapped.
/obj/structure/ore_vent/starter_resources
name = "active ore vent"
@@ -402,12 +403,6 @@
)
/obj/structure/ore_vent/random
- /// Static list of ore vent types, for random generation.
- var/static/list/ore_vent_types = list(
- BOULDER_SIZE_SMALL,
- BOULDER_SIZE_MEDIUM,
- BOULDER_SIZE_LARGE,
- )
/obj/structure/ore_vent/random/Initialize(mapload)
. = ..()
@@ -446,7 +441,7 @@
/mob/living/simple_animal/hostile/asteroid/wolf,
)
ore_vent_options = list(
- "small",
+ SMALL_VENT_TYPE,
)
/obj/structure/ore_vent/random/icebox/lower
@@ -459,12 +454,11 @@
/mob/living/simple_animal/hostile/asteroid/wolf,
)
ore_vent_options = list(
- "small",
- "medium",
- "large",
+ SMALL_VENT_TYPE = 3,
+ MEDIUM_VENT_TYPE = 5,
+ LARGE_VENT_TYPE = 7,
)
-
/obj/structure/ore_vent/boss
name = "menacing ore vent"
desc = "An ore vent, brimming with underground ore. This one has an evil aura about it. Better be careful."
@@ -536,3 +530,4 @@
#undef MINERAL_TYPE_OPTIONS_RANDOM
#undef OVERLAY_OFFSET_START
#undef OVERLAY_OFFSET_EACH
+#undef MINERALS_PER_BOULDER
diff --git a/code/modules/mining/boulder_processing/_boulder_processing.dm b/code/modules/mining/boulder_processing/_boulder_processing.dm
index 3372f77ebb5..d7d4be2557e 100644
--- a/code/modules/mining/boulder_processing/_boulder_processing.dm
+++ b/code/modules/mining/boulder_processing/_boulder_processing.dm
@@ -3,185 +3,264 @@
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 many boulders can we process maximum per loop?
- var/boulders_processing_max = 1
- /// What boulder(s) are we holding?
- var/list/boulders_contained = list()
+ /// 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
- /// Does this machine have a mineral storage link to the silo?
- var/holds_minerals = FALSE
- /// What materials do we accept and process out of boulders? Removing iron from an iron/glass boulder would leave a boulder with glass.
- var/list/processable_materials = list()
- /// If we get a boulder with one of these inside, we'll drop it back out.
- var/static/list/drop_if_contained = list(
- /obj/item/relic,
- )
-
/// What sound plays when a thing operates?
var/usage_sound = 'sound/machines/mining/wooping_teleport.ogg'
- /// Cooldown associated with the usage_sound played.
- COOLDOWN_DECLARE(sound_cooldown)
-
/// Silo link to it's materials list.
var/datum/component/remote_materials/silo_materials
- ///Does this machine hold mining points?
- var/holds_mining_points = FALSE
/// 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()
- if(holds_minerals)
- silo_materials = AddComponent(/datum/component/remote_materials, mapload)
/obj/machinery/bouldertech/LateInitialize()
. = ..()
- if(!holds_minerals)
- return
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/machinery/bouldertech/Destroy()
- boulders_contained = null
silo_materials = null
return ..()
-/obj/machinery/bouldertech/update_icon_state()
- . = ..()
- if(anchored)
- icon_state ="[initial(icon_state)]"
- else
- icon_state ="[initial(icon_state)]-off"
-
-/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)
- update_appearance(UPDATE_ICON_STATE)
- START_PROCESSING(SSmachines, src)
- return ITEM_INTERACT_SUCCESS
-
-/obj/machinery/bouldertech/screwdriver_act(mob/living/user, obj/item/tool)
- . = ..()
- if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-off", initial(icon_state), tool))
- return ITEM_INTERACT_SUCCESS
-
-/obj/machinery/bouldertech/crowbar_act(mob/living/user, obj/item/tool)
- . = ..()
- if(default_pry_open(tool, close_after_pry = TRUE, closed_density = FALSE))
- return ITEM_INTERACT_SUCCESS
- if(default_deconstruction_crowbar(tool))
- return ITEM_INTERACT_SUCCESS
-
-/obj/machinery/bouldertech/attackby(obj/item/attacking_item, mob/user, params)
- if(holds_minerals && istype(attacking_item, /obj/item/boulder))
- var/obj/item/boulder/my_boulder = attacking_item
- update_boulder_count()
- if(!accept_boulder(my_boulder))
- balloon_alert_to_viewers("full!")
- return
- balloon_alert_to_viewers("accepted")
- START_PROCESSING(SSmachines, src)
- return TRUE
- if(istype(attacking_item, /obj/item/card/id) && holds_mining_points)
- if(points_held <= 0)
- balloon_alert_to_viewers("no points to claim!")
- if(!COOLDOWN_FINISHED(src, sound_cooldown))
- return TRUE
- COOLDOWN_START(src, sound_cooldown, 1.5 SECONDS)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, FALSE)
- return FALSE
- var/obj/item/card/id/id_card = attacking_item
- 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 TRUE
- 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 TRUE
- return ..()
-
-/obj/machinery/bouldertech/attack_hand_secondary(mob/user, list/modifiers)
- . = ..()
- if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
- return
- if(!anchored)
- balloon_alert(user, "anchor first!")
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
- if(!holds_minerals)
- return SECONDARY_ATTACK_CONTINUE_CHAIN
- if(!length(boulders_contained))
- balloon_alert_to_viewers("No boulders to remove!")
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
- remove_boulder(pick(boulders_contained))
- return SECONDARY_ATTACK_CONTINUE_CHAIN
-
/obj/machinery/bouldertech/on_deconstruction(disassembled)
if(length(contents))
for(var/obj/item/boulder/boulder in contents)
remove_boulder(boulder)
-/obj/machinery/bouldertech/process()
- if(!anchored)
- return PROCESS_KILL
- var/stop_processing_check = FALSE
- var/boulders_concurrent = boulders_processing_max ///How many boulders can we touch this process() call
- for(var/obj/item/potential_boulder as anything in boulders_contained)
- if(QDELETED(potential_boulder))
- boulders_contained -= potential_boulder
- break
- if(boulders_concurrent <= 0)
- break //Try again next time
+/obj/machinery/bouldertech/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = CONTEXTUAL_SCREENTIP_SET
- if(!istype(potential_boulder, /obj/item/boulder))
- potential_boulder.forceMove(drop_location())
- CRASH("\The [src] had a non-boulder in it's boulders contained!")
+ if(isnull(held_item))
+ context[SCREENTIP_CONTEXT_RMB] = "Remove Boulder"
+ return
- var/obj/item/boulder/boulder = potential_boulder
- if(boulder.durability < 0)
- CRASH("\The [src] had a boulder with negative durability!")
- if(!check_for_processable_materials(boulder.custom_materials)) //Checks for any new materials we can process.
- boulders_concurrent-- //We count skipped boulders
- remove_boulder(boulder)
- continue
- boulders_concurrent--
- boulder.durability-- //One less durability to the processed boulder.
- if(COOLDOWN_FINISHED(src, sound_cooldown))
- COOLDOWN_START(src, sound_cooldown, 1.5 SECONDS)
- playsound(loc, usage_sound, 29, FALSE, SHORT_RANGE_SOUND_EXTRARANGE) //This can get annoying. One play per process() call.
- stop_processing_check = TRUE
- if(boulder.durability <= 0)
- breakdown_boulder(boulder) //Crack that bouwlder open!
- continue
- if(!stop_processing_check)
- playsound(src.loc, 'sound/machines/ping.ogg', 50, FALSE)
- return PROCESS_KILL
+ 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 = [boulder_count]/[boulders_held_max] boulders.")
+ . += span_notice("Can process upto [boulders_processing_count] boulders at a time.")
+
+ if(anchored)
+ . += span_notice("Its [EXAMINE_HINT("anchored")] in place.")
+ else
+ . += span_warning("It needs to be [EXAMINE_HINT("anchored")] to start operations.")
+
+ . += span_notice("Its maintainence 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 || !is_operational || (machine_stat & (BROKEN | NOPOWER)) || panel_open)
+ suffix = "-off"
+ icon_state ="[initial(icon_state)][suffix]"
+
+/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/CanAllowThrough(atom/movable/mover, border_dir)
if(!anchored)
return FALSE
- if(boulders_contained.len >= boulders_held_max)
- return FALSE
if(istype(mover, /obj/item/boulder))
var/obj/item/boulder/boulder = mover
- return boulder.can_get_processed()
+ return can_process_boulder(boulder)
return ..()
-/obj/machinery/bouldertech/examine(mob/user)
+/**
+ * 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 just processed 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)
+ if(!can_process_boulder(new_boulder))
+ return FALSE
+
+ new_boulder.forceMove(src)
+
+ COOLDOWN_START(src, accept_cooldown, 1.5 SECONDS)
+
+ return TRUE
+
+/obj/machinery/bouldertech/proc/on_entered(datum/source, atom/movable/atom_movable)
+ SIGNAL_HANDLER
+
+ if(!can_process_boulder(atom_movable))
+ return
+
+ INVOKE_ASYNC(src, PROC_REF(accept_boulder), atom_movable)
+
+/**
+ * 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/attackby(obj/item/attacking_item, mob/user, params)
+ if(panel_open)
+ return ..()
+
+ if(istype(attacking_item, /obj/item/boulder))
+ . = TRUE
+ var/obj/item/boulder/my_boulder = attacking_item
+ if(!accept_boulder(my_boulder))
+ balloon_alert_to_viewers("cannot accept!")
+ return
+ balloon_alert_to_viewers("accepted")
+ return
+
+ if(istype(attacking_item, /obj/item/card/id))
+ . = TRUE
+ if(points_held <= 0)
+ balloon_alert_to_viewers("no points to claim!")
+ if(!COOLDOWN_FINISHED(src, sound_cooldown))
+ return
+ COOLDOWN_START(src, sound_cooldown, 1.5 SECONDS)
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 30, FALSE)
+ return
+
+ var/obj/item/card/id/id_card = attacking_item
+ 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
+ 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
+
+ return ..()
+
+/obj/machinery/bouldertech/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
- if(holds_mining_points)
- . += span_notice("The machine reads that it has [span_bold("[points_held] mining points")] stored. Swipe an ID to claim them.")
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
+ return
+ if(!anchored)
+ balloon_alert(user, "anchor 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.
@@ -190,159 +269,93 @@
* @param 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 FALSE
if(chosen_boulder.loc != src)
return FALSE
- if(!length(chosen_boulder.custom_materials))
- qdel(chosen_boulder)
- playsound(loc, 'sound/weapons/drill.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- update_boulder_count()
- return FALSE
- if(isnull(silo_materials))
- return FALSE
- //here we loop through the boulder's ores
- var/list/processable_ores = list()
- var/tripped = FALSE
- //If a material is in the boulder's custom_materials, but not in the processable_materials list, we add it to the processable_ores list to add back to a leftover boulder.
- for(var/datum/material/possible_mat as anything in chosen_boulder.custom_materials)
- if(!is_type_in_list(possible_mat, processable_materials))
- continue
- var/quantity = chosen_boulder.custom_materials[possible_mat]
- points_held = round(points_held + (quantity * possible_mat.points_per_unit * MINING_POINT_MACHINE_MULTIPLIER)) // put point total here into machine
- processable_ores += possible_mat
- processable_ores[possible_mat] = quantity
- chosen_boulder.custom_materials -= possible_mat //Remove it from the boulder now that it's tracked
- tripped = TRUE
- if(!tripped)
- remove_boulder(chosen_boulder)
- say("Nothing to process!")
- return FALSE //we shouldn't spend more time processing a boulder with contents we don't care about.
- use_power(BASE_MACHINE_ACTIVE_CONSUMPTION)
- check_for_boosts() //Calls the relevant behavior for boosting the machine's efficiency, if able.
- var/is_artifact = (istype(chosen_boulder, /obj/item/boulder/artifact)) //We need to know if it's an artifact so we can carry it over to the new boulder.
- var/obj/item/boulder/disposable_boulder = new (src)
- disposable_boulder.custom_materials = processable_ores
- silo_materials.insert_item(disposable_boulder, refining_efficiency)
- qdel(disposable_boulder)
+ //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()
- refining_efficiency = initial(refining_efficiency) //Reset refining efficiency to 100% now that we've processed any relevant ores.
- if(!length(chosen_boulder.custom_materials))
- playsound(loc, 'sound/weapons/drill.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- if(is_artifact)
- points_held = round((points_held + MINER_POINT_MULTIPLIER) * MINING_POINT_MACHINE_MULTIPLIER) /// Artifacts give bonus points!
- chosen_boulder.break_apart()
- update_boulder_count()
- return TRUE //We've processed all the materials in the boulder, so we can just destroy it in break_apart.
+ //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
+ use_power(active_power_usage)
- chosen_boulder.restart_processing_cooldown() //So that we don't pick it back up!
- chosen_boulder.durability = rand(chosen_boulder.boulder_size, chosen_boulder.boulder_size + BOULDER_SIZE_SMALL) //Reset durability to a random value between the boulder's size and a little more.
+ //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 TRUE //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)
- return TRUE
-/**
- * Accepts a boulder into the machine. Used when a boulder is first placed into the machine.
- * @param new_boulder The boulder to be accepted.
- */
-/obj/machinery/bouldertech/proc/accept_boulder(obj/item/boulder/new_boulder)
- if(isnull(new_boulder))
- return FALSE
- if(boulders_contained.len >= boulders_held_max) //Full already
- return FALSE
- if(!istype(new_boulder)) //Can't be processed
- return FALSE
- if(!new_boulder.custom_materials) //Shouldn't happen, but just in case.
- qdel(new_boulder)
- playsound(loc, 'sound/weapons/drill.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- return FALSE
- new_boulder.forceMove(src)
- boulders_contained += new_boulder
- SSore_generation.available_boulders -= new_boulder
- START_PROCESSING(SSmachines, src) //Starts processing if we aren't already.
- return TRUE
+/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.
- * @param drop_turf The location to eject the boulder to. If null, it will eject to the machine's drop_location().
- * @param specific_boulder The boulder to be ejected.
+ * Arguments
+ *
+ * * obj/item/boulder/specific_boulder - the boulder to remove
*/
-/obj/machinery/bouldertech/proc/remove_boulder(obj/item/boulder/specific_boulder, turf/drop_turf = null)
- if(isnull(specific_boulder))
- CRASH("remove_boulder() called with no boulder!")
- if(!length(specific_boulder.custom_materials))
- qdel(specific_boulder)
- update_boulder_count()
- playsound(loc, 'sound/weapons/drill.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- return FALSE
- specific_boulder.restart_processing_cooldown() //Reset the cooldown so we don't pick it back up by the same machine.
- if(isturf(drop_turf))
- specific_boulder.forceMove(drop_turf)
- else
- specific_boulder.forceMove(drop_location())
- if(!update_boulder_count())
+/obj/machinery/bouldertech/proc/remove_boulder(obj/item/boulder/specific_boulder)
+ PRIVATE_PROC(TRUE)
+
+ if(QDELETED(specific_boulder))
return TRUE
- STOP_PROCESSING(SSmachines, src)
- balloon_alert_to_viewers("clear!")
+ 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
+
+ //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
-
-/**
- * Getter proc to determine how many boulders are contained in the machine.
- * Also adds their reference to the boulders_contained list.
- */
-/obj/machinery/bouldertech/proc/update_boulder_count()
- boulders_contained = list()
- for(var/obj/item/boulder/boulder in contents)
- boulders_contained += boulder
- return boulders_contained.len
-
-/obj/machinery/bouldertech/proc/on_entered(datum/source, atom/movable/atom_movable)
- SIGNAL_HANDLER
- INVOKE_ASYNC(src, PROC_REF(accept_boulder), atom_movable)
-
-/**
- * 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()
- refining_efficiency = initial(refining_efficiency) //Reset refining efficiency to 100%.
-
-/**
- * Checks if a custom_material is in a list of processable materials in the machine.
- * @param list/custom_material A list of materials, presumably taken from a boulder. If a material that this machine can process is in this list, it will return true, inclusively.
- */
-/obj/machinery/bouldertech/proc/check_for_processable_materials(list/boulder_mats)
- for(var/material as anything in boulder_mats)
- if(is_type_in_list(material, processable_materials))
- return TRUE
- return FALSE
-
-///Beacon to launch a new mining setup when activated. For testing and speed!
-/obj/item/boulder_beacon
- name = "boulder beacon"
- desc = "N.T. approved boulder beacon, toss it down and you will have a full bouldertech mining station."
- icon = 'icons/obj/machines/floor.dmi'
- icon_state = "floor_beacon"
- /// Number of activations left on this beacon. Uses will be removed as the beacon is used and each triggers a different machine to be spawned from it.
- var/uses = 3
-
-/obj/item/boulder_beacon/attack_self()
- loc.visible_message(span_warning("\The [src] begins to beep loudly!"))
- addtimer(CALLBACK(src, PROC_REF(launch_payload)), 1 SECONDS)
-
-/**
- * Spawns a new bouldertech machine from the beacon, then removes a use from the beacon.
- * Use one spawns a BRM teleporter, then a refinery, and lastly a smelter.
- */
-/obj/item/boulder_beacon/proc/launch_payload()
- playsound(src, SFX_SPARKS, 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- switch(uses)
- if(3)
- new /obj/machinery/bouldertech/brm(drop_location())
- if(2)
- new /obj/machinery/bouldertech/refinery(drop_location())
- if(1)
- new /obj/machinery/bouldertech/refinery/smelter(drop_location())
- qdel(src)
- uses--
diff --git a/code/modules/mining/boulder_processing/beacon.dm b/code/modules/mining/boulder_processing/beacon.dm
new file mode 100644
index 00000000000..576e05baf53
--- /dev/null
+++ b/code/modules/mining/boulder_processing/beacon.dm
@@ -0,0 +1,28 @@
+///Beacon to launch a new mining setup when activated. For testing and speed!
+/obj/item/boulder_beacon
+ name = "boulder beacon"
+ desc = "N.T. approved boulder beacon, toss it down and you will have a full bouldertech mining station."
+ icon = 'icons/obj/machines/floor.dmi'
+ icon_state = "floor_beacon"
+ /// Number of activations left on this beacon. Uses will be removed as the beacon is used and each triggers a different machine to be spawned from it.
+ var/uses = 3
+
+/obj/item/boulder_beacon/attack_self()
+ visible_message(span_warning("\The [src] begins to beep loudly!"))
+ addtimer(CALLBACK(src, PROC_REF(launch_payload)), 1 SECONDS)
+
+/**
+ * Spawns a new bouldertech machine from the beacon, then removes a use from the beacon.
+ * Use one spawns a BRM teleporter, then a refinery, and lastly a smelter.
+ */
+/obj/item/boulder_beacon/proc/launch_payload()
+ playsound(src, SFX_SPARKS, 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ switch(uses)
+ if(3)
+ new /obj/machinery/brm(drop_location())
+ if(2)
+ new /obj/machinery/bouldertech/refinery(drop_location())
+ if(1)
+ new /obj/machinery/bouldertech/refinery/smelter(drop_location())
+ qdel(src)
+ uses--
diff --git a/code/modules/mining/boulder_processing/boulder.dm b/code/modules/mining/boulder_processing/boulder.dm
index 77260eef088..699358c6adb 100644
--- a/code/modules/mining/boulder_processing/boulder.dm
+++ b/code/modules/mining/boulder_processing/boulder.dm
@@ -12,38 +12,15 @@
throw_range = 2
throw_speed = 0.5
drag_slowdown = 1.5 // It's still a big rock.
+
///When a refinery machine is working on this boulder, we'll set this. Re reset when the process is finished, but the boulder may still be refined/operated on further.
- var/obj/machinery/bouldertech/processed_by = null
+ var/obj/machinery/processed_by = null
/// How many steps of refinement this boulder has gone through. Starts at 5-8, goes down one each machine process.
var/durability = 5
/// What was the size of the boulder when it was spawned? This is used for inheiriting the icon_state.
var/boulder_size = BOULDER_SIZE_SMALL
/// Used in inheriting the icon_state from our parent vent in update_icon.
var/boulder_string = "boulder"
- /// Cooldown used to prevents boulders from getting processed back into a machine immediately after being processed.
- COOLDOWN_DECLARE(processing_cooldown)
-
- /// Static list of all minerals to populate gulag boulders with.
- var/list/static/gulag_minerals = list(
- /datum/material/diamond = 1,
- /datum/material/gold = 8,
- /datum/material/iron = 95,
- /datum/material/plasma = 30,
- /datum/material/silver = 20,
- /datum/material/titanium = 8,
- /datum/material/uranium = 3,
- )
- /// Static list of all minerals to populate gulag boulders with, but with bluespace added where safe.
- var/list/static/expanded_gulag_minerals = list(
- /datum/material/bluespace = 1,
- /datum/material/diamond = 1,
- /datum/material/gold = 8,
- /datum/material/iron = 94,
- /datum/material/plasma = 30,
- /datum/material/silver = 20,
- /datum/material/titanium = 8,
- /datum/material/uranium = 3,
- )
/obj/item/boulder/Initialize(mapload)
. = ..()
@@ -55,18 +32,37 @@
processed_by = null
return ..()
+/obj/item/boulder/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ if(held_item?.tool_behaviour == TOOL_MINING || HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
+ context[SCREENTIP_CONTEXT_RMB] = "Crush boulder into ore"
+ return CONTEXTUAL_SCREENTIP_SET
+
/obj/item/boulder/examine(mob/user)
. = ..()
. += span_notice("This boulder would take [durability] more steps to refine or break.")
+ if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
+ . += span_notice("You can crush this boulder with your bare hands.")
/obj/item/boulder/examine_more(mob/user)
. = ..()
. += span_notice("[span_bold("Boulders")] can either be cracked open by [span_bold("mining tools")], or processed into sheets with [span_bold("refineries or smelters")]. Undisturbed boulders can be collected by the [span_bold("BRM")].")
-/obj/item/boulder/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
- if(held_item?.tool_behaviour == TOOL_MINING || HAS_TRAIT(user, TRAIT_BOULDER_BREAKER))
- context[SCREENTIP_CONTEXT_RMB] = "Crush boulder into ore"
- return CONTEXTUAL_SCREENTIP_SET
+/obj/item/boulder/update_icon_state()
+ . = ..()
+ switch(boulder_size)
+ if(BOULDER_SIZE_SMALL)
+ icon_state = "[boulder_string]_small"
+ if(BOULDER_SIZE_MEDIUM)
+ icon_state = "[boulder_string]_medium"
+ if(BOULDER_SIZE_LARGE)
+ icon_state = "[boulder_string]_large"
+ else
+ icon_state = "[boulder_string]_small"
+
+/obj/item/boulder/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ if(istype(mover, /obj/item/boulder)) //This way, boulders can only go one at a time on conveyor belts, but everyone else can go through.
+ return FALSE
/obj/item/boulder/attack_self(mob/user, list/modifiers)
. = ..()
@@ -84,11 +80,6 @@
manual_process(null, user, INATE_BOULDER_SPEED_MULTIPLIER)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/item/boulder/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
- if(istype(mover, /obj/item/boulder)) //This way, boulders can only go one at a time on conveyor belts, but everyone else can go through.
- return FALSE
-
/obj/item/boulder/attackby_secondary(obj/item/weapon, mob/user, params)
. = ..()
if(HAS_TRAIT(user, TRAIT_BOULDER_BREAKER) || HAS_TRAIT(weapon, TRAIT_BOULDER_BREAKER))
@@ -107,18 +98,6 @@
manual_process(null, user, INATE_BOULDER_SPEED_MULTIPLIER) //A little hacky but it works around the speed of the blackboard task selection process for now.
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/item/boulder/update_icon_state()
- . = ..()
- switch(boulder_size)
- if(BOULDER_SIZE_SMALL)
- icon_state = "[boulder_string]_small"
- if(BOULDER_SIZE_MEDIUM)
- icon_state = "[boulder_string]_medium"
- if(BOULDER_SIZE_LARGE)
- icon_state = "[boulder_string]_large"
- else
- icon_state = "[boulder_string]_small"
-
/**
* This is called when a boulder is processed by a mob or tool, and reduces the durability of the boulder.
* @param obj/item/weapon The weapon that is being used to process the boulder, that we pull toolspeed from. If null, we use the override_speed_multiplier instead.
@@ -185,104 +164,12 @@
cracked_ore = new cracked_ore_type (drop_location(), quantity)
SSblackbox.record_feedback("tally", "ore_mined", quantity, cracked_ore)
-/**
- * Handles the boulder's processing cooldown to check if it's ready to be processed again.
- */
-/obj/item/boulder/proc/can_get_processed()
- return COOLDOWN_FINISHED(src, processing_cooldown)
-
-/**
- * Starts the boulder's processing cooldown.
- */
-/obj/item/boulder/proc/restart_processing_cooldown()
- COOLDOWN_START(src, processing_cooldown, 2 SECONDS)
-
-/**
- * Moves boulder contents to the drop location, and then deletes the boulder.
- */
+///Moves boulder contents to the drop location, and then deletes the boulder.
/obj/item/boulder/proc/break_apart()
- var/list/quips = list("Clang!", "Crack!", "Bang!", "Clunk!", "Clank!")
if(length(contents))
+ var/list/quips = list("Clang!", "Crack!", "Bang!", "Clunk!", "Clank!")
visible_message(span_notice("[pick(quips)] Something falls out of \the [src]!"))
playsound(loc, 'sound/effects/picaxe1.ogg', 60, FALSE)
for(var/obj/item/content as anything in contents)
content.forceMove(get_turf(src))
qdel(src)
-
-/**
- * This is called when a boulder is spawned from a vent, and is used to set the boulder's icon as well as durability.
- * We also set our boulder_size variable, which is used for inheiriting the icon_state later on if processed.
- * @param obj/structure/ore_vent/parent_vent The vent that spawned this boulder to generate consistent boulder icons. If null, we use the default size.
- * @param size The size of the boulder to spawn. If parent_vent is defined, this is ignored.
- * @param is_artifact Whether or not this boulder is an artifact boulder. If true, we use the artifact boulder icon state regardless of size.
- */
-/obj/item/boulder/proc/flavor_boulder(obj/structure/ore_vent/parent_vent, size = BOULDER_SIZE_SMALL, is_artifact = FALSE)
- var/durability_min = size
- var/durability_max = size + BOULDER_SIZE_SMALL
- if(parent_vent)
- durability_min = parent_vent.boulder_size
- durability_max = parent_vent.boulder_size + BOULDER_SIZE_SMALL
- durability = rand(durability_min, durability_max) //randomize durability a bit for some flavor.
- boulder_size = size
- if(parent_vent)
- boulder_size = parent_vent.boulder_size
- boulder_string = parent_vent.boulder_icon_state
- update_appearance(UPDATE_ICON_STATE)
-
-/**
- * Unique proc for gulag-style boulders, which adds a random amount of minerals to the boulder.
- */
-/obj/item/boulder/proc/add_gulag_minerals()
- var/datum/material/new_material = pick_weight(gulag_minerals)
- var/list/new_mats = list()
- new_mats[new_material] = SHEET_MATERIAL_AMOUNT * rand(1,3) //We only want a few sheets of material in the gulag boulders
- set_custom_materials(new_mats)
-
-/obj/item/boulder/artifact
- name = "artifact boulder"
- desc = "This boulder is brimming with strange energy. Cracking it open could contain something unusual for science."
- icon_state = "boulder_artifact"
- /// This is the type of item that will be inside the boulder. Default is a strange object.
- var/artifact_type = /obj/item/relic
- /// References to the relic inside the boulder, if any.
- var/obj/item/artifact_inside
-
-/obj/item/boulder/artifact/Initialize(mapload)
- . = ..()
- artifact_inside = new artifact_type(src) /// This could be poggers for archaeology in the future.
-
-/obj/item/boulder/artifact/Destroy(force)
- QDEL_NULL(artifact_inside)
- return ..()
-
-/obj/item/boulder/artifact/convert_to_ore()
- . = ..()
- artifact_inside.forceMove(drop_location())
- artifact_inside = null
-
-/obj/item/boulder/artifact/break_apart()
- artifact_inside = null
- return ..()
-
-
-/obj/item/boulder/gulag
- name = "low-quality boulder"
- desc = "This rocks. It's a low quality boulder, so it's probably not worth as much."
-
-/obj/item/boulder/gulag/Initialize(mapload)
- . = ..()
- add_gulag_minerals(gulag_minerals)
-
-/obj/item/boulder/gulag_expanded
- name = "low-density boulder"
- desc = "This rocks. It's not very well packed, and can't contain as many minerals."
-
-/obj/item/boulder/gulag_expanded/Initialize(mapload)
- . = ..()
- add_gulag_minerals(expanded_gulag_minerals)
-
-/obj/item/boulder/shabby
- name = "shabby boulder"
- desc = "A bizzare, twisted boulder. Wait, wait no, it's just a rock."
- custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.1, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.1)
- durability = 1
diff --git a/code/modules/mining/boulder_processing/boulder_types.dm b/code/modules/mining/boulder_processing/boulder_types.dm
new file mode 100644
index 00000000000..08ed961404c
--- /dev/null
+++ b/code/modules/mining/boulder_processing/boulder_types.dm
@@ -0,0 +1,76 @@
+///Boulders with special artificats that can give higher mining points
+/obj/item/boulder/artifact
+ name = "artifact boulder"
+ desc = "This boulder is brimming with strange energy. Cracking it open could contain something unusual for science."
+ icon_state = "boulder_artifact"
+ /// This is the type of item that will be inside the boulder. Default is a strange object.
+ var/artifact_type = /obj/item/relic
+ /// References to the relic inside the boulder, if any.
+ var/obj/item/artifact_inside
+
+/obj/item/boulder/artifact/Initialize(mapload)
+ . = ..()
+ artifact_inside = new artifact_type(src) /// This could be poggers for archaeology in the future.
+
+/obj/item/boulder/artifact/Destroy(force)
+ QDEL_NULL(artifact_inside)
+ return ..()
+
+/obj/item/boulder/artifact/convert_to_ore()
+ . = ..()
+ artifact_inside.forceMove(drop_location())
+ artifact_inside = null
+
+/obj/item/boulder/artifact/break_apart()
+ artifact_inside = null
+ return ..()
+
+///Boulders usually spawned in lavaland labour camp area
+/obj/item/boulder/gulag
+ name = "low-quality boulder"
+ desc = "This rocks. It's a low quality boulder, so it's probably not worth as much."
+
+/obj/item/boulder/gulag/Initialize(mapload)
+ . = ..()
+
+ /// Static list of all minerals to populate gulag boulders with.
+ var/list/static/gulag_minerals = list(
+ /datum/material/diamond = 1,
+ /datum/material/gold = 8,
+ /datum/material/iron = 95,
+ /datum/material/plasma = 30,
+ /datum/material/silver = 20,
+ /datum/material/titanium = 8,
+ /datum/material/uranium = 3,
+ )
+
+ set_custom_materials(list(pick_weight(gulag_minerals) = SHEET_MATERIAL_AMOUNT * rand(1, 3)))
+
+///Boulders usually spawned in lavaland labour camp area but with bluespace material
+/obj/item/boulder/gulag_expanded
+ name = "low-density boulder"
+ desc = "This rocks. It's not very well packed, and can't contain as many minerals."
+
+/obj/item/boulder/gulag_expanded/Initialize(mapload)
+ . = ..()
+
+ /// Static list of all minerals to populate gulag boulders with, but with bluespace added where safe.
+ var/list/static/expanded_gulag_minerals = list(
+ /datum/material/bluespace = 1,
+ /datum/material/diamond = 1,
+ /datum/material/gold = 8,
+ /datum/material/iron = 94,
+ /datum/material/plasma = 30,
+ /datum/material/silver = 20,
+ /datum/material/titanium = 8,
+ /datum/material/uranium = 3,
+ )
+
+ set_custom_materials(list(pick_weight(expanded_gulag_minerals) = SHEET_MATERIAL_AMOUNT * rand(1, 3)))
+
+///lowgrade boulder, most commonly spawned
+/obj/item/boulder/shabby
+ name = "shabby boulder"
+ desc = "A bizzare, twisted boulder. Wait, wait no, it's just a rock."
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.1, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.1)
+ durability = 1
diff --git a/code/modules/mining/boulder_processing/brm.dm b/code/modules/mining/boulder_processing/brm.dm
index c76ec9dabbd..8c1205f5d4e 100644
--- a/code/modules/mining/boulder_processing/brm.dm
+++ b/code/modules/mining/boulder_processing/brm.dm
@@ -1,187 +1,319 @@
+///Sound played when boulders are teleported manually by hand
#define MANUAL_TELEPORT_SOUND 'sound/machines/mining/manual_teleport.ogg'
+///Sound played when boulders are teleported automatically in process()
#define AUTO_TELEPORT_SOUND 'sound/machines/mining/auto_teleport.ogg'
+///Time taken to spawn a boulder, also the cooldown applied before the next manual teleportation
+#define TELEPORTATION_TIME (1.5 SECONDS)
+///Cooldown for automatic teleportation after processing boulders_processing_max number of boulders
+#define BATCH_COOLDOWN (3 SECONDS)
+///Special case when we are trying to teleport a boulder but there is already another boulder in our loc
+#define TURF_BLOCKED_BY_BOULDER -1
-/obj/machinery/bouldertech/brm
+/obj/machinery/brm
name = "boulder retrieval matrix"
desc = "A teleportation matrix used to retrieve boulders excavated by mining NODEs from ore vents."
+ icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "brm"
+ active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.5
circuit = /obj/item/circuitboard/machine/brm
- usage_sound = MANUAL_TELEPORT_SOUND
processing_flags = START_PROCESSING_MANUALLY
- boulders_held_max = 2
+ anchored = TRUE
+ density = TRUE
+
+ /// How many boulders can we process maximum per loop?
+ var/boulders_processing_max = 1
/// Are we trying to actively collect boulders automatically?
var/toggled_on = FALSE
- /// How long does it take to collect a boulder?
- var/teleportation_time = 1.5 SECONDS
+ ///Have we finished processing a full batch of boulders
+ var/batch_processing = FALSE
+
/// Cooldown used for left click teleportation.
COOLDOWN_DECLARE(manual_teleport_cooldown)
+ /// Cooldown used for automatic teleportation after processing boulders_processing_max number of boulders.
+ COOLDOWN_DECLARE(batch_start_cooldown)
-/obj/machinery/bouldertech/brm/Initialize(mapload)
+/obj/machinery/brm/Initialize(mapload)
. = ..()
- set_wires(new /datum/wires/brm(src))
+ register_context()
+
+/obj/machinery/brm/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = CONTEXTUAL_SCREENTIP_SET
+
+ if(isnull(held_item))
+ context[SCREENTIP_CONTEXT_LMB] = "Teleport single boulder"
+ context[SCREENTIP_CONTEXT_RMB] = "Toggle [toggled_on ? "Off" : "On"] automatic boulder retrieval"
+ return
+
+ if(!isnull(held_item))
+ if(held_item.tool_behaviour == TOOL_WRENCH)
+ context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "" : "Un"] Anchor"
+ return
+ if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
+ context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] Panel"
+ return
+
+ if(panel_open)
+ if(held_item.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
+
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/machinery/brm/examine(mob/user)
+ . = ..()
+ . += span_notice("The small screen reads there are [span_boldnotice("[SSore_generation.available_boulders.len] boulders")] available to teleport.")
+ . += span_notice("Can collect upto [boulders_processing_max] boulders at a time.")
+ . += span_notice("Automatic boulder retrival can be toggled [EXAMINE_HINT("[toggled_on ? "Off" : "On"]")] with [EXAMINE_HINT("Right Click")].")
+
+ if(anchored)
+ . += span_notice("Its [EXAMINE_HINT("anchored")] in place.")
+ else
+ . += span_warning("It needs to be [EXAMINE_HINT("anchored")] to start operations.")
+
+ . += span_notice("Its maintainence 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/brm/update_icon_state()
+ icon_state = initial(icon_state)
+
+ if(!anchored || !is_operational || machine_stat & (BROKEN | NOPOWER) || panel_open)
+ icon_state = "[icon_state]-off"
+ return
+
+ if(toggled_on)
+ icon_state = "[icon_state]-toggled"
+ return
-/obj/machinery/bouldertech/brm/Destroy()
- QDEL_NULL(wires)
return ..()
-/obj/machinery/bouldertech/brm/attack_hand(mob/living/user, list/modifiers)
- . = ..()
- if(!handle_teleport_conditions(user))
- return
- pre_collect_boulder()
+/obj/machinery/brm/wrench_act(mob/living/user, obj/item/tool)
+ . = ITEM_INTERACT_BLOCKING
+ if(default_unfasten_wrench(user, tool, time = 1.5 SECONDS) == SUCCESSFUL_UNFASTEN)
+ update_appearance(UPDATE_ICON_STATE)
+ return ITEM_INTERACT_SUCCESS
- COOLDOWN_START(src, manual_teleport_cooldown, teleportation_time)
+/obj/machinery/brm/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/brm/attack_robot(mob/user)
- if(!handle_teleport_conditions(user))
- return
- pre_collect_boulder()
+/obj/machinery/brm/crowbar_act(mob/living/user, obj/item/tool)
+ . = ITEM_INTERACT_BLOCKING
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
- COOLDOWN_START(src, manual_teleport_cooldown, teleportation_time)
-
-/obj/machinery/bouldertech/brm/attackby(obj/item/attacking_item, mob/user, params)
- if(is_wire_tool(attacking_item) && panel_open)
- wires.interact(user)
+///To allow boulders on a conveyer belt to move unobstructed if multiple machines are made on a single line
+/obj/machinery/brm/CanAllowThrough(atom/movable/mover, border_dir)
+ if(!anchored)
+ return FALSE
+ if(istype(mover, /obj/item/boulder))
return TRUE
return ..()
-/obj/machinery/bouldertech/brm/attack_hand_secondary(mob/user, list/modifiers)
+/obj/machinery/brm/RefreshParts()
. = ..()
- if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+
+ boulders_processing_max = 0
+ for(var/datum/stock_part/part in component_parts)
+ boulders_processing_max += part.tier
+
+ boulders_processing_max = ROUND_UP((boulders_processing_max / 12) * 7)
+
+/obj/machinery/brm/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(. || panel_open)
return
- if(!anchored)
- balloon_alert(user, "anchor first!")
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
- toggle_auto_on(user)
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
-/obj/machinery/bouldertech/brm/attack_robot_secondary(mob/user, list/modifiers)
- . = ..()
- if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ if(!handle_teleport_conditions(user))
return
- if(!anchored)
- balloon_alert(user, "anchor first!")
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
- toggle_auto_on(user)
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/machinery/bouldertech/brm/process()
- if(SSore_generation.available_boulders.len < 1)
- say("No boulders to collect. Entering idle mode.")
- toggled_on = FALSE
- update_appearance(UPDATE_ICON_STATE)
- return PROCESS_KILL
- for(var/i in 1 to boulders_processing_max)
- if(pre_collect_boulder())
- continue
- toggled_on = FALSE
- update_appearance(UPDATE_ICON_STATE)
- return PROCESS_KILL
- for(var/obj/item/boulder/ground_rocks in loc.contents)
- boulders_contained += ground_rocks
- if(boulders_contained.len < boulders_held_max)
- continue
- toggled_on = FALSE
- boulders_contained.Cut()
- update_appearance(UPDATE_ICON_STATE)
- return PROCESS_KILL
+ var/result = pre_collect_boulder()
+ if(result == TURF_BLOCKED_BY_BOULDER)
+ balloon_alert(user, "no space")
+ else if(result)
+ balloon_alert(user, "teleporting")
+ COOLDOWN_START(src, manual_teleport_cooldown, TELEPORTATION_TIME)
-/obj/machinery/bouldertech/brm/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
- context[SCREENTIP_CONTEXT_LMB] = "Teleport single boulder"
- context[SCREENTIP_CONTEXT_RMB] = "Toggle automatic boulder retrieval"
- return CONTEXTUAL_SCREENTIP_SET
-
-/obj/machinery/bouldertech/brm/examine(mob/user)
- . = ..()
- . += span_notice("The small screen reads there are [span_boldnotice("[SSore_generation.available_boulders.len] boulders")] available to teleport.")
-
-/obj/machinery/bouldertech/brm/RefreshParts()
- . = ..()
- var/scanner_stack = 0
- var/laser_stack = 0
- for(var/datum/stock_part/scanning_module/scanner in component_parts)
- scanner_stack += scanner.tier
- boulders_processing_max = scanner_stack
- for(var/datum/stock_part/micro_laser/laser in component_parts)
- laser_stack += laser.tier
- boulders_held_max = laser_stack + 1
-
-/obj/machinery/bouldertech/brm/update_icon_state()
- if(toggled_on && !panel_open)
- icon_state = "[initial(icon_state)]-toggled"
- return
- return ..()
+ return TRUE
/**
* Handles qualifiers for enabling teleportation of boulders.
* Returns TRUE if the teleportation can proceed, FALSE otherwise.
+ * Arguments
+ *
+ * * mob/user - the mob to inform if conditions aren't met
*/
-/obj/machinery/bouldertech/brm/proc/handle_teleport_conditions(mob/user)
+/obj/machinery/brm/proc/handle_teleport_conditions(mob/user)
+ PRIVATE_PROC(TRUE)
+
if(!COOLDOWN_FINISHED(src, manual_teleport_cooldown))
return FALSE
if(panel_open)
balloon_alert(user, "close panel first!")
return FALSE
+ if(batch_processing)
+ balloon_alert(user, "batch still processing!")
+ return FALSE
playsound(src, MANUAL_TELEPORT_SOUND, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
return TRUE
-/**
- * Begins to collect a boulder from the available boulders list in SSore_generation.
- * Boulders must not be processed by another BRM or machine, and must be in the available boulders list.
- * A selected boulder is picked randomly.
- * The actual movement is then handled by collect_boulder() after a timed callback.
- */
-/obj/machinery/bouldertech/brm/proc/pre_collect_boulder()
- if(!SSore_generation.available_boulders.len)
- playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
- balloon_alert_to_viewers("no boulders to collect!")
- return FALSE //Nothing to collect
- var/obj/item/boulder/random_boulder = pick(SSore_generation.available_boulders)
- if(random_boulder.processed_by)
- return FALSE
+/obj/machinery/brm/attack_robot(mob/user)
+ . = ..()
+ if(. || panel_open)
+ return
+ if(!handle_teleport_conditions(user))
+ return
+
+ var/result = pre_collect_boulder()
+ if(result == TURF_BLOCKED_BY_BOULDER)
+ balloon_alert(user, "no space")
+ else if(result)
+ balloon_alert(user, "teleporting")
+
+ COOLDOWN_START(src, manual_teleport_cooldown, TELEPORTATION_TIME)
- random_boulder.processed_by = src
- random_boulder.Shake(duration = 1.5 SECONDS)
- SSore_generation.available_boulders -= random_boulder
- addtimer(CALLBACK(src, PROC_REF(collect_boulder), random_boulder), 1.5 SECONDS)
return TRUE
-/**
- * Collects a boulder from the available boulders list in SSore_generation.
- * Handles the movement of the boulder as well as visual effects on the BRM.
- * @param obj/item/boulder/random_boulder The boulder to collect.
- */
-/obj/machinery/bouldertech/brm/proc/collect_boulder(obj/item/boulder/random_boulder)
- flick("brm-flash", src)
- if(QDELETED(random_boulder))
- playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
- balloon_alert_to_viewers("target lost!")
- return FALSE
- playsound(src, AUTO_TELEPORT_SOUND, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- random_boulder.forceMove(drop_location())
- random_boulder.processed_by = null
- random_boulder.pixel_x = rand(-2, 2)
- random_boulder.pixel_y = rand(-2, 2)
- balloon_alert_to_viewers("boulder appears!")
- random_boulder.visible_message(span_warning("[random_boulder] suddenly appears!"))
- use_power(BASE_MACHINE_ACTIVE_CONSUMPTION * 0.1)
- return TRUE
+/obj/machinery/brm/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
+ return
+ if(!anchored)
+ balloon_alert(user, "anchor first!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ toggle_auto_on(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/**
* Toggles automatic boulder retrieval on.
* Adjusts the teleportation sound, icon state, and begins processing.
* @param mob/user The user who toggled the BRM.
*/
-/obj/machinery/bouldertech/brm/proc/toggle_auto_on(mob/user)
+/obj/machinery/brm/proc/toggle_auto_on(mob/user)
+ PRIVATE_PROC(TRUE)
+
if(panel_open)
- if(user)
- balloon_alert(user, "close panel first!")
+ balloon_alert(user, "close panel first!")
return
- toggled_on = TRUE
- START_PROCESSING(SSmachines, src)
+ if(!anchored)
+ balloon_alert(user, "anchor first!")
+ return
+ if(!is_operational || machine_stat & (BROKEN | NOPOWER))
+ return
+
+ toggled_on = ! toggled_on
+ if(toggled_on)
+ begin_processing()
+ else
+ end_processing()
update_appearance(UPDATE_ICON_STATE)
- usage_sound = AUTO_TELEPORT_SOUND
+
+/obj/machinery/brm/attack_robot_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
+ return
+ if(!anchored)
+ balloon_alert(user, "anchor first!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ toggle_auto_on(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/machinery/brm/process()
+ if(!toggled_on)
+ return PROCESS_KILL
+
+ //have some cooldown after processing the previous batch of boulders
+ if(!COOLDOWN_FINISHED(src, batch_start_cooldown))
+ return
+
+ pre_collect_boulder(FALSE, boulders_processing_max)
+
+/**
+ * Begins to collect a boulder from the available boulders list in SSore_generation.
+ * Boulders must be in the available boulders list.
+ * A selected boulder is picked randomly.
+ * The actual movement is then handled by collect_boulder() after a timed callback.
+ * Arguments
+ *
+ * * feedback - should we play sound and display allert if now boulders are available
+ * * boulders_remaining - how many boulders we want to try & collect spawning a boulder every TELEPORTATION_TIME seconds
+ * * new_batch - is this the very 1st boulder processed from boulders_remaining. Used to wait for all the boulders to be collected
+ */
+/obj/machinery/brm/proc/pre_collect_boulder(feedback = TRUE, boulders_remaining = 1, new_batch = TRUE)
+ PRIVATE_PROC(TRUE)
+
+ if(!anchored || panel_open || !is_operational || machine_stat & (BROKEN | NOPOWER))
+ return FALSE
+
+ //There is an boulder in our loc. it has be removed so we don't clog up our loc with even more boulders
+ if(locate(/obj/item/boulder) in loc)
+ batch_processing = FALSE
+ return TURF_BLOCKED_BY_BOULDER
+
+ //no more boulders
+ if(!SSore_generation.available_boulders.len)
+ if(feedback)
+ playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ balloon_alert_to_viewers("no boulders to collect!")
+ batch_processing = FALSE
+ return FALSE
+
+ //we are trying to process a new batch of boulders
+ if(new_batch)
+ if(batch_processing) //the previous one hasen't completed yet, wait
+ return FALSE
+ batch_processing = TRUE
+
+ var/obj/item/boulder/random_boulder = pick(SSore_generation.available_boulders)
+ if(random_boulder.processed_by)
+ return FALSE
+ SSore_generation.available_boulders -= random_boulder
+ random_boulder.processed_by = src
+ random_boulder.Shake(shake_interval = TELEPORTATION_TIME)
+ addtimer(CALLBACK(src, PROC_REF(collect_boulder), random_boulder, feedback, boulders_remaining), TELEPORTATION_TIME)
+ return TRUE
+
+/**
+ * Callback to spawn a boulder collected in pre_collect_boulder(). Can be used to collect
+ * multiple boulders by setting boulders_remaining but must only be called by pre_collect_boulder()
+ * and not directly
+ * Arguments
+ *
+ * * obj/item/boulder/random_boulder - the boulder we are trying to move out
+ * * feedback - see pre_collect_boulder()
+ * * boulders_remaining - passed back to pre_collect_boulder() if count > 0
+ */
+/obj/machinery/brm/proc/collect_boulder(obj/item/boulder/random_boulder, feedback, boulders_remaining)
+ if(QDELETED(random_boulder))
+ playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ balloon_alert_to_viewers("target lost!")
+ return FALSE
+
+ if(locate(/obj/item/boulder) in loc)
+ batch_processing = FALSE
+ return
+
+ flick("brm-flash", src)
+ playsound(src, toggled_on ? AUTO_TELEPORT_SOUND : MANUAL_TELEPORT_SOUND, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ random_boulder.forceMove(drop_location())
+ random_boulder.pixel_x = rand(-2, 2)
+ random_boulder.pixel_y = rand(-2, 2)
+ random_boulder.processed_by = null
+ balloon_alert_to_viewers("boulder appears!")
+ use_power(active_power_usage)
+
+ boulders_remaining -= 1
+ if(boulders_remaining <= 0)
+ COOLDOWN_START(src, batch_start_cooldown, BATCH_COOLDOWN)
+ batch_processing = FALSE
+ return TRUE
+ else
+ return pre_collect_boulder(feedback, boulders_remaining, FALSE)
#undef MANUAL_TELEPORT_SOUND
#undef AUTO_TELEPORT_SOUND
+#undef TELEPORTATION_TIME
+#undef BATCH_COOLDOWN
+#undef TURF_BLOCKED_BY_BOULDER
diff --git a/code/modules/mining/boulder_processing/refinery.dm b/code/modules/mining/boulder_processing/refinery.dm
index 764816ec6d3..662bb3e75e1 100644
--- a/code/modules/mining/boulder_processing/refinery.dm
+++ b/code/modules/mining/boulder_processing/refinery.dm
@@ -7,44 +7,34 @@
name = "boulder refinery"
desc = "BR for short. Accepts boulders and refines non-metallic ores into sheets using internal chemicals."
icon_state = "stacker"
- holds_minerals = TRUE
- processable_materials = list(
- /datum/material/glass,
- /datum/material/plasma,
- /datum/material/diamond,
- /datum/material/bluespace,
- /datum/material/bananium,
- /datum/material/plastic,
- )
circuit = /obj/item/circuitboard/machine/refinery
usage_sound = 'sound/machines/mining/refinery.ogg'
- holds_mining_points = TRUE
+ action = "crushing"
-/// okay so var that holds mining points to claim
-/// add total of pts from minerals mined in parent proc
-/// then, little mini UI showing points to collect?
+/obj/machinery/bouldertech/refinery/can_process_material(datum/material/possible_mat)
+ var/static/list/processable_materials
+ if(!length(processable_materials))
+ processable_materials = list(
+ /datum/material/glass,
+ /datum/material/plasma,
+ /datum/material/diamond,
+ /datum/material/bluespace,
+ /datum/material/bananium,
+ /datum/material/plastic,
+ )
+ return is_type_in_list(possible_mat, processable_materials)
/obj/machinery/bouldertech/refinery/RefreshParts()
. = ..()
- var/manipulator_stack = 0
- var/matter_bin_stack = 0
- for(var/datum/stock_part/servo/servo in component_parts)
- manipulator_stack += servo.tier - 1
- boulders_processing_max = clamp(manipulator_stack, 1, 6)
+
+ boulders_held_max = 0
for(var/datum/stock_part/matter_bin/bin in component_parts)
- matter_bin_stack += bin.tier
- boulders_held_max = matter_bin_stack
-
-
-/obj/machinery/bouldertech/refinery/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
- if(istype(held_item, /obj/item/boulder))
- context[SCREENTIP_CONTEXT_LMB] = "Insert boulder"
- if(istype(held_item, /obj/item/card/id) && points_held > 0)
- context[SCREENTIP_CONTEXT_LMB] = "Claim mining points"
- context[SCREENTIP_CONTEXT_RMB] = "Remove boulder"
- return CONTEXTUAL_SCREENTIP_SET
+ boulders_held_max += bin.tier
+ boulders_processing_count = 0
+ for(var/datum/stock_part/servo/servo in component_parts)
+ boulders_processing_count += servo.tier
+ boulders_processing_count = ROUND_UP((boulders_processing_count / 8) * boulders_held_max)
/**
* Your other new favorite industrial waste magnet!
@@ -55,36 +45,43 @@
name = "boulder smelter"
desc = "BS for short. Accept boulders and refines metallic ores into sheets."
icon_state = "smelter"
- processable_materials = list(
- /datum/material/iron,
- /datum/material/titanium,
- /datum/material/silver,
- /datum/material/gold,
- /datum/material/uranium,
- /datum/material/mythril,
- /datum/material/adamantine,
- /datum/material/runite,
- )
light_system = OVERLAY_LIGHT
- light_range = 1
- light_power = 2
+ light_range = 2
+ light_power = 3
light_color = "#ffaf55"
- light_on = FALSE
circuit = /obj/item/circuitboard/machine/smelter
usage_sound = 'sound/machines/mining/smelter.ogg'
+ action = "smelting"
-/obj/machinery/bouldertech/refinery/smelter/RefreshParts()
+/obj/machinery/bouldertech/refinery/smelter/Initialize(mapload)
. = ..()
- light_power = boulders_processing_max
+ set_light_on(TRUE)
-/obj/machinery/bouldertech/refinery/smelter/accept_boulder(obj/item/boulder/new_boulder)
+/obj/machinery/bouldertech/refinery/smelter/can_process_material(datum/material/possible_mat)
+ var/static/list/processable_materials
+ if(!length(processable_materials))
+ processable_materials = list(
+ /datum/material/iron,
+ /datum/material/titanium,
+ /datum/material/silver,
+ /datum/material/gold,
+ /datum/material/uranium,
+ )
+ return is_type_in_list(possible_mat, processable_materials)
+
+/obj/machinery/bouldertech/refinery/smelter/set_light_on(new_value)
+ if(panel_open || !anchored || !is_operational || machine_stat & (BROKEN | NOPOWER))
+ new_value = FALSE
+ return ..()
+
+/obj/machinery/bouldertech/refinery/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/screwdriver)
. = ..()
- if(.)
- set_light_on(TRUE)
- return TRUE
+ set_light_on(TRUE)
-/obj/machinery/bouldertech/refinery/smelter/process()
+/obj/machinery/bouldertech/refinery/default_unfasten_wrench(mob/user, obj/item/wrench, time)
. = ..()
- if(. == PROCESS_KILL)
- set_light_on(FALSE)
+ set_light_on(TRUE)
+
+/obj/machinery/bouldertech/refinery/smelter/on_set_is_operational(old_value)
+ set_light_on(TRUE)
diff --git a/icons/obj/machines/mining_machines.dmi b/icons/obj/machines/mining_machines.dmi
index 91f7f434ad3..c1a4076e3a2 100644
Binary files a/icons/obj/machines/mining_machines.dmi and b/icons/obj/machines/mining_machines.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 5930f2bd0ec..44599fa69d9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1834,7 +1834,6 @@
#include "code\datums\wires\airlock.dm"
#include "code\datums\wires\apc.dm"
#include "code\datums\wires\autolathe.dm"
-#include "code\datums\wires\brm.dm"
#include "code\datums\wires\conveyor.dm"
#include "code\datums\wires\ecto_sniffer.dm"
#include "code\datums\wires\emitter.dm"
@@ -4442,7 +4441,9 @@
#include "code\modules\mining\shelters.dm"
#include "code\modules\mining\voucher_sets.dm"
#include "code\modules\mining\boulder_processing\_boulder_processing.dm"
+#include "code\modules\mining\boulder_processing\beacon.dm"
#include "code\modules\mining\boulder_processing\boulder.dm"
+#include "code\modules\mining\boulder_processing\boulder_types.dm"
#include "code\modules\mining\boulder_processing\brm.dm"
#include "code\modules\mining\boulder_processing\refinery.dm"
#include "code\modules\mining\equipment\explorer_gear.dm"
diff --git a/tools/UpdatePaths/Scripts/81358_brm.txt b/tools/UpdatePaths/Scripts/81358_brm.txt
new file mode 100644
index 00000000000..c0970d41fa2
--- /dev/null
+++ b/tools/UpdatePaths/Scripts/81358_brm.txt
@@ -0,0 +1 @@
+/obj/machinery/bouldertech/brm : /obj/machinery/brm{@OLD}
\ No newline at end of file