From f4ba8c4017909c692edfbb72eb322a746ba063ed Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sat, 11 Mar 2023 02:57:18 +0100 Subject: [PATCH] [MIRROR] Reworks sabotage progression traitor objective [MDB IGNORE] (#19766) * Reworks sabotage progression traitor objective (#73750) ## About The Pull Request This one is a little bit of a more abstract change than my last traitor objective rebalancing PR. The idea here is that I think that the objectives which have you sabotage a frequently used machine (lathes or the ORM, but we can maybe expand it to others following this?) are potentially pretty good but limitations in how the objective are tracked mean that people go about them in a boring way which rarely inconveniences anyone. I have reworked these objectives so that you can choose to apply a provided item to the machine over 10 seconds. The next interaction following this (clicking on it, or in the case of the ORM loading any ore into it) will cause the machine to explode, destroying itself and lightly damaging people nearby (explosion damage is... variable, but it's a light explosion so it won't instantly kill anyone). There is a 0.5 second grace period between the bomb triggering and the explosion actually happening, which players can use to perform some kind of tactical reaction or simply to feel regret. Here's a video: https://www.youtube.com/watch?v=xNIaHsA9YFE Trapped machines will intermittently beep, though at a volume which is hard to detect from more than two tiles away, and can be defused by using a screwdriver on the machine. If you choose to do this then you will get a little bonus to TC and reputation, but only if the trap is successfully sprung. This means that sabotaging a machine in an empty department is unlikely to lead to quick success. You can also still do it with screwdriver and crowbar way if you are boring but this means you will only get reputation and not any telecrystals. The two objectives to destroy a radio server or the research machine remain as they were. Also I refactored how we set which machines are targets for these objectives so it doesn't iterate over every machine in the world every time. ## Why It's Good For The Game Traitor objectives (and antagonist objectives in general) should: - Present you with a somewhat engaging task - Encourage you to interact with at least one other player (at least by "attempting to sneak past them") - Move the state of the round forward in some way, usually by creating inconvenience for at least one other person Prior to this change you could, and people often did, complete this objective by accessing a machine that you already _have_ access to, using a screwdriver and crowbar on it, and then reassembling it. This prompts interaction with nobody, and inconveniences nobody. Following this change people will be encouraged to: - Actually destroy the machine. - Probably harm somebody. - Potentially cause some collateral damage. - Make loud sounds and flashing lights to delight people of all ages. Even if you opt to set the device in a little-used department and then blow yourself up, that will prompt you to go and interact with someone in medbay and will also look pretty suspicious in a way that's much harder to track with the previous objective. Also I might reuse this component again in the future, you never know. ## Changelog :cl: Jacquerel, sprite by J(Clearly Lying) balance: Traitor objectives which ask you to destroy lathes or the ORM provide an optional bonus if you _booby trap_ the machine using a provided tool. This causes the machine to explode the next time it is interacted with. balance: Performing the objective without rigging the machine to explode awards no TC, using it awards more TC than it did previously. /:cl: * Reworks sabotage progression traitor objective --------- Co-authored-by: Jacquerel Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com> --- code/__DEFINES/dcs/signals/signals_global.dm | 2 + code/__DEFINES/dcs/signals/signals_object.dm | 3 + code/__DEFINES/dcs/signals/signals_traitor.dm | 3 + code/__DEFINES/do_afters.dm | 1 + .../components/interaction_booby_trap.dm | 102 ++++++++ code/datums/looping_sounds/item_sounds.dm | 8 + code/game/machinery/_machinery.dm | 5 + .../traitor/objectives/destroy_machinery.dm | 76 ------ .../traitor/objectives/sabotage_machinery.dm | 242 ++++++++++++++++++ code/modules/mining/machine_redemption.dm | 28 +- icons/obj/weapons/items_and_weapons.dmi | Bin 93255 -> 93653 bytes tgstation.dme | 3 +- 12 files changed, 383 insertions(+), 90 deletions(-) create mode 100644 code/datums/components/interaction_booby_trap.dm delete mode 100644 code/modules/antagonists/traitor/objectives/destroy_machinery.dm create mode 100644 code/modules/antagonists/traitor/objectives/sabotage_machinery.dm diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index bafb53f1ffd..f0938ea68ee 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -51,6 +51,8 @@ #define LINKED_UP (1<<0) /// an obj/item is created! (obj/item/created_item) #define COMSIG_GLOB_NEW_ITEM "!new_item" +/// an obj/machinery is created! (obj/machinery/created_machine) +#define COMSIG_GLOB_NEW_MACHINE "!new_machine" /// a client (re)connected, after all /client/New() checks have passed : (client/connected_client) #define COMSIG_GLOB_CLIENT_CONNECT "!client_connect" /// a weather event of some kind occured diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 36c28708959..3f0edab3f75 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -416,3 +416,6 @@ /// from /obj/item/detective_scanner/scan(): (mob/user, list/extra_data) #define COMSIG_DETECTIVE_SCANNED "det_scanned" + +/// from /obj/machinery/mineral/ore_redemption/pickup_item when it successfully picks something up +#define COMSIG_ORM_COLLECTED_ORE "orm_collected_ore" diff --git a/code/__DEFINES/dcs/signals/signals_traitor.dm b/code/__DEFINES/dcs/signals/signals_traitor.dm index 666ff575837..66aebd7ae95 100644 --- a/code/__DEFINES/dcs/signals/signals_traitor.dm +++ b/code/__DEFINES/dcs/signals/signals_traitor.dm @@ -21,6 +21,9 @@ #define COMPONENT_FORCE_PLACEMENT (1<<0) #define COMPONENT_FORCE_FAIL_PLACEMENT (1<<1) +/// Called when a machine a traitor has booby trapped triggers its payload +#define COMSIG_TRAITOR_MACHINE_TRAP_TRIGGERED "traitor_machine_trap_triggered" + /// Called when a device a traitor has planted effects someone's mood. Pass the mind of the viewer. #define COMSIG_DEMORALISING_EVENT "traitor_demoralise_event" /// Called when you finish drawing some graffiti so we can register more signals on it. Pass the graffiti effect. diff --git a/code/__DEFINES/do_afters.dm b/code/__DEFINES/do_afters.dm index 693c0dba6f5..f46710643d0 100644 --- a/code/__DEFINES/do_afters.dm +++ b/code/__DEFINES/do_afters.dm @@ -5,3 +5,4 @@ #define DOAFTER_SOURCE_CLIMBING_LADDER "doafter_climbingladder" #define DOAFTER_SOURCE_SPIDER "doafter_spider" #define DOAFTER_SOURCE_HEAL_TOUCH "doafter_heal_touch" +#define DOAFTER_SOURCE_PLANTING_DEVICE "doafter_planting_device" diff --git a/code/datums/components/interaction_booby_trap.dm b/code/datums/components/interaction_booby_trap.dm new file mode 100644 index 00000000000..03892f3e93b --- /dev/null +++ b/code/datums/components/interaction_booby_trap.dm @@ -0,0 +1,102 @@ +/** + * Attached to an atom, creates an explosion when it is interacted with + */ +/datum/component/interaction_booby_trap + /// Explosion radius of light damage + var/explosion_light_range + /// Explosion radius of heavy damage + var/explosion_heavy_range + /// Sound to play when triggered + var/triggered_sound + /// Time to wait between being triggered and blowing up + var/trigger_delay + /// Looping sound to clue people in that something is up + var/datum/looping_sound/active_sound_loop + /// Using this tool on the atom will defuse the explosive + var/defuse_tool + /// List of additional signals which should make this explode + var/list/additional_triggers + /// Callback to run when we're going to explode + var/datum/callback/on_triggered_callback + /// Callback to run when we've been defused + var/datum/callback/on_defused_callback + /// Time until we explode + var/explode_timer + +/datum/component/interaction_booby_trap/Initialize( + explosion_light_range = 3, + explosion_heavy_range = 1, // So we destroy some machine components + triggered_sound = 'sound/machines/triple_beep.ogg', + trigger_delay = 0.5 SECONDS, + sound_loop_type = /datum/looping_sound/trapped_machine_beep, + defuse_tool = TOOL_SCREWDRIVER, + additional_triggers = list(), + datum/callback/on_triggered_callback = null, + datum/callback/on_defused_callback = null, +) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + src.explosion_light_range = explosion_light_range + src.explosion_heavy_range = explosion_heavy_range + src.triggered_sound = triggered_sound + src.trigger_delay = trigger_delay + src.defuse_tool = defuse_tool + src.additional_triggers = additional_triggers + src.on_triggered_callback = on_triggered_callback + src.on_defused_callback = on_defused_callback + if (sound_loop_type) + active_sound_loop = new sound_loop_type(parent) + active_sound_loop.start() + + RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_touched)) + RegisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE, PROC_REF(on_examine)) + if (defuse_tool) + RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(defuse_tool), PROC_REF(on_defused)) + if (length(additional_triggers)) + RegisterSignals(parent, additional_triggers, PROC_REF(trigger_explosive)) + +/datum/component/interaction_booby_trap/Destroy(force, silent) + UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_TOOL_ACT(defuse_tool), COMSIG_PARENT_EXAMINE_MORE) + additional_triggers) + QDEL_NULL(active_sound_loop) + QDEL_NULL(on_triggered_callback) + QDEL_NULL(on_defused_callback) + return ..() + +/// Called when someone touches the parent atom with their hands, we want to blow up +/datum/component/interaction_booby_trap/proc/on_touched(atom/source) + SIGNAL_HANDLER + trigger_explosive(source) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/// Start a countdown until destruction +/datum/component/interaction_booby_trap/proc/trigger_explosive(atom/source) + SIGNAL_HANDLER + if (explode_timer) + return + explode_timer = addtimer(CALLBACK(src, PROC_REF(explode), source), 0.5 SECONDS) + source.balloon_alert_to_viewers("beep") + playsound(parent, triggered_sound, 50, FALSE) + return + +/// Blow up the parent atom and delete ourselves +/datum/component/interaction_booby_trap/proc/explode(atom/source) + on_triggered_callback?.Invoke(source) + var/turf/origin_turf = get_turf(source) + new /obj/effect/temp_visual/explosion/fast(origin_turf) + EX_ACT(source, EXPLODE_HEAVY, source) + explosion(origin = origin_turf, light_impact_range = explosion_light_range, heavy_impact_range = explosion_heavy_range, explosion_cause = src) + qdel(src) + +/// Defuse the bomb and delete ourselves +/datum/component/interaction_booby_trap/proc/on_defused(atom/source, mob/user, obj/item/tool) + SIGNAL_HANDLER + on_defused_callback?.Invoke(source, user, tool) + qdel(src) + return COMPONENT_BLOCK_TOOL_ATTACK + +/// Give people a little hint +/datum/component/interaction_booby_trap/proc/on_examine(atom/source, mob/examiner, list/examine_list) + SIGNAL_HANDLER + var/defuse_hint = (defuse_tool) ? "Perhaps [tool_behaviour_name(defuse_tool)] could help..." : "" + examine_list += span_warning("There's a light flashing red inside the maintenance panel. [defuse_hint]") diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm index 9a7028aab6e..93de93ec94b 100644 --- a/code/datums/looping_sounds/item_sounds.dm +++ b/code/datums/looping_sounds/item_sounds.dm @@ -18,3 +18,11 @@ mid_sounds = list('sound/items/taperecorder/taperecorder_hiss_mid.ogg' = 1) start_sound = list('sound/items/taperecorder/taperecorder_hiss_start.ogg' = 1) volume = 10 + +/datum/looping_sound/trapped_machine_beep + mid_sounds = list('sound/machines/beep.ogg' = 1) + mid_length = 10 SECONDS + mid_length_vary = 5 SECONDS + falloff_exponent = 10 + falloff_distance = 1 + volume = 5 diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 8fc4f302304..677717356b1 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -183,6 +183,7 @@ if(HAS_TRAIT(SSstation, STATION_TRAIT_BOTS_GLITCHED)) randomize_language_if_on_station() + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_MACHINE, src) return INITIALIZE_HINT_LATELOAD @@ -1200,3 +1201,7 @@ if(isliving(user)) last_used_time = world.time last_user_mobtype = user.type + +/// Called if this machine is supposed to be a sabotage machine objective. +/obj/machinery/proc/add_as_sabotage_target() + return diff --git a/code/modules/antagonists/traitor/objectives/destroy_machinery.dm b/code/modules/antagonists/traitor/objectives/destroy_machinery.dm deleted file mode 100644 index 29cc7e42856..00000000000 --- a/code/modules/antagonists/traitor/objectives/destroy_machinery.dm +++ /dev/null @@ -1,76 +0,0 @@ -/datum/traitor_objective_category/destroy_machinery - name = "Destroy Protolathe" - objectives = list( - /datum/traitor_objective/destroy_machinery = 1, - /datum/traitor_objective/destroy_machinery/high_risk = 1 - ) - -/datum/traitor_objective/destroy_machinery - name = "Destroy the %MACHINE%" - description = "Destroy the %MACHINE% to cause disarray and disrupt the operations of the %JOB%'s department." - - progression_reward = list(2 MINUTES, 8 MINUTES) - telecrystal_reward = list(0, 1) - - progression_minimum = 0 MINUTES - progression_maximum = 10 MINUTES - - /// The maximum amount of this type of objective a traitor can have. - var/maximum_allowed = 2 - /// The possible target machinery and the jobs tied to each one. - var/list/applicable_jobs = list( - JOB_RESEARCH_DIRECTOR = /obj/machinery/rnd/production/protolathe/department/science, - JOB_CHIEF_MEDICAL_OFFICER = /obj/machinery/rnd/production/techfab/department/medical, - JOB_CHIEF_ENGINEER = /obj/machinery/rnd/production/protolathe/department/engineering, - JOB_HEAD_OF_PERSONNEL = /obj/machinery/rnd/production/techfab/department/service, - JOB_SHAFT_MINER = /obj/machinery/mineral/ore_redemption, - JOB_QUARTERMASTER = /obj/machinery/rnd/production/techfab/department/cargo, - ) - /// Whether this can bypass the maximum_allowed value or not - var/allow_more_than_max = FALSE - /// The chosen job. Used to check for duplicates - var/chosen_job - -/datum/traitor_objective/destroy_machinery/high_risk - progression_reward = list(5 MINUTES, 10 MINUTES) - telecrystal_reward = list(3, 4) - - progression_minimum = 15 MINUTES - progression_maximum = 30 MINUTES - allow_more_than_max = TRUE - applicable_jobs = list( - JOB_STATION_ENGINEER = /obj/machinery/telecomms/hub, - JOB_SCIENTIST = /obj/machinery/rnd/server, - ) - -/datum/traitor_objective/destroy_machinery/can_generate_objective(datum/mind/generating_for, list/possible_duplicates) - if(length(possible_duplicates) >= maximum_allowed && !allow_more_than_max) - return FALSE - return TRUE - -/datum/traitor_objective/destroy_machinery/generate_objective(datum/mind/generating_for, list/possible_duplicates) - for(var/datum/traitor_objective/destroy_machinery/objective as anything in possible_duplicates) - applicable_jobs -= objective.chosen_job - if(!length(applicable_jobs)) - return FALSE - var/list/obj/machinery/possible_machines = list() - while(length(possible_machines) <= 0 && length(applicable_jobs) > 0) - var/target_head = pick(applicable_jobs) - var/obj/machinery/machine_to_find = applicable_jobs[target_head] - applicable_jobs -= target_head - - chosen_job = target_head - for(var/obj/machinery/machine as anything in GLOB.machines) - if(istype(machine, machine_to_find) && is_station_level(machine.z)) - possible_machines += machine - - if(!length(possible_machines)) - return FALSE - - for(var/obj/machinery/machine as anything in possible_machines) - AddComponent(/datum/component/traitor_objective_register, machine, succeed_signals = list(COMSIG_PARENT_QDELETING)) - - replace_in_name("%JOB%", lowertext(chosen_job)) - replace_in_name("%MACHINE%", possible_machines[1].name) - return TRUE - diff --git a/code/modules/antagonists/traitor/objectives/sabotage_machinery.dm b/code/modules/antagonists/traitor/objectives/sabotage_machinery.dm new file mode 100644 index 00000000000..b5e26c725ad --- /dev/null +++ b/code/modules/antagonists/traitor/objectives/sabotage_machinery.dm @@ -0,0 +1,242 @@ +/// Datum which manages references to things we are instructed to destroy +GLOBAL_DATUM_INIT(objective_machine_handler, /datum/objective_target_machine_handler, new()) + +/// Marks a machine as a possible traitor sabotage target +/proc/add_sabotage_machine(source, typepath) + LAZYADD(GLOB.objective_machine_handler.machine_instances_by_path[typepath], source) + return typepath + +/// Traitor objective to destroy a machine the crew cares about +/datum/traitor_objective_category/sabotage_machinery + name = "Sabotage Worksite" + objectives = list( + /datum/traitor_objective/sabotage_machinery/trap = 1, + /datum/traitor_objective/sabotage_machinery/destroy = 1, + ) + +/datum/traitor_objective/sabotage_machinery + name = "Sabotage the %MACHINE%" + description = "Abstract objective holder which shouldn't appear in your uplink." + abstract_type = /datum/traitor_objective/sabotage_machinery + + /// The maximum amount of this type of objective a traitor can have, set to 0 for no limit. + var/maximum_allowed = 0 + /// The possible target machinery and the jobs tied to each one. + var/list/applicable_jobs = list() + /// The chosen job. Used to check for duplicates + var/chosen_job + +/datum/traitor_objective/sabotage_machinery/can_generate_objective(datum/mind/generating_for, list/possible_duplicates) + if(!maximum_allowed) + return TRUE + if(length(possible_duplicates) >= maximum_allowed) + return FALSE + return TRUE + +/datum/traitor_objective/sabotage_machinery/generate_objective(datum/mind/generating_for, list/possible_duplicates) + var/list/possible_jobs = applicable_jobs.Copy() + for(var/datum/traitor_objective/sabotage_machinery/objective as anything in possible_duplicates) + possible_jobs -= objective.chosen_job + for(var/available_job in possible_jobs) + var/job_machine_path = possible_jobs[available_job] + if (!length(GLOB.objective_machine_handler.machine_instances_by_path[job_machine_path])) + possible_jobs -= available_job + if(!length(possible_jobs)) + return FALSE + + chosen_job = pick(possible_jobs) + var/list/obj/machinery/possible_machines = GLOB.objective_machine_handler.machine_instances_by_path[possible_jobs[chosen_job]] + for(var/obj/machinery/machine as anything in possible_machines) + prepare_machine(machine) + + replace_in_name("%JOB%", lowertext(chosen_job)) + replace_in_name("%MACHINE%", possible_machines[1].name) + return TRUE + +/// Marks a given machine as our target +/datum/traitor_objective/sabotage_machinery/proc/prepare_machine(obj/machinery/machine) + AddComponent(/datum/component/traitor_objective_register, machine, succeed_signals = list(COMSIG_PARENT_QDELETING)) + +// Destroy machines which are in annoying locations, are annoying when destroyed, and aren't directly interacted with +/datum/traitor_objective/sabotage_machinery/destroy + name = "Destroy the %MACHINE%" + description = "Destroy the %MACHINE% to cause disarray and disrupt the operations of the %JOB%'s department." + + progression_reward = list(5 MINUTES, 10 MINUTES) + telecrystal_reward = list(3, 4) + + progression_minimum = 15 MINUTES + progression_maximum = 30 MINUTES + + applicable_jobs = list( + JOB_STATION_ENGINEER = /obj/machinery/telecomms/hub, + JOB_SCIENTIST = /obj/machinery/rnd/server, + ) + +// Rig machines which are in public locations to explode when interacted with +/datum/traitor_objective/sabotage_machinery/trap + name = "Sabotage the %MACHINE%" + description = "Destroy the %MACHINE% to cause disarray and disrupt the operations of the %JOB%'s department. If you can get another crew member to destroy the machine using the provided booby trap, you will be rewarded with an additional %PROGRESSION% reputation and %TC% telecrystals." + + progression_reward = list(2 MINUTES, 4 MINUTES) + telecrystal_reward = 0 // Only from completing the bonus objective + + progression_minimum = 0 MINUTES + progression_maximum = 10 MINUTES + + maximum_allowed = 2 + applicable_jobs = list( + JOB_CHIEF_ENGINEER = /obj/machinery/rnd/production/protolathe/department/engineering, + JOB_CHIEF_MEDICAL_OFFICER = /obj/machinery/rnd/production/techfab/department/medical, + JOB_HEAD_OF_PERSONNEL = /obj/machinery/rnd/production/techfab/department/service, + JOB_QUARTERMASTER = /obj/machinery/rnd/production/techfab/department/cargo, + JOB_RESEARCH_DIRECTOR = /obj/machinery/rnd/production/protolathe/department/science, + JOB_SHAFT_MINER = /obj/machinery/mineral/ore_redemption, + ) + + /// Bonus reward to grant if you booby trap successfully + var/bonus_tc = 2 + /// Bonus progression to grant if you booby trap successfully + var/bonus_progression = 5 MINUTES + /// The trap device we give out + var/obj/item/traitor_machine_trapper/tool + +/datum/traitor_objective/sabotage_machinery/trap/generate_objective(datum/mind/generating_for, list/possible_duplicates) + . = ..() + if (!.) + return FALSE + + replace_in_name("%TC%", bonus_tc) + replace_in_name("%PROGRESSION%", DISPLAY_PROGRESSION(bonus_progression)) + return TRUE + +/datum/traitor_objective/sabotage_machinery/trap/prepare_machine(obj/machinery/machine) + RegisterSignal(machine, COMSIG_TRAITOR_MACHINE_TRAP_TRIGGERED, PROC_REF(sabotage_success)) + return ..() + +/// Called when you successfully proc the booby trap, gives a bonus reward +/datum/traitor_objective/sabotage_machinery/trap/proc/sabotage_success(obj/machinery/machine) + progression_reward += bonus_progression + telecrystal_reward += bonus_tc + succeed_objective() + +/datum/traitor_objective/sabotage_machinery/trap/generate_ui_buttons(mob/user) + var/list/buttons = list() + if(!tool) + buttons += add_ui_button("", "Pressing this will materialize an explosive trap in your hand, which you can conceal within the target machine", "wifi", "summon_gear") + return buttons + +/datum/traitor_objective/sabotage_machinery/trap/ui_perform_action(mob/living/user, action) + . = ..() + switch(action) + if("summon_gear") + if(tool) + return + tool = new(user.drop_location()) + user.put_in_hands(tool) + tool.balloon_alert(user, "a booby trap materializes in your hand") + tool.target_machine_path = applicable_jobs[chosen_job] + +/// Item which you use on a machine to cause it to explode next time someone interacts with it +/obj/item/traitor_machine_trapper + name = "suspicious device" + desc = "It looks dangerous." + icon = 'icons/obj/weapons/items_and_weapons.dmi' + icon_state = "boobytrap" + + /// Light explosion range, to hurt the person using the machine + var/explosion_range = 3 + /// The type of object on which this can be planted on. + var/obj/machinery/target_machine_path + /// The time it takes to deploy the bomb. + var/deploy_time = 10 SECONDS + +/obj/item/traitor_machine_trapper/examine(mob/user) + . = ..() + if(!user.mind?.has_antag_datum(/datum/antagonist/traitor)) + return + if(target_machine_path) + . += span_notice("This device must be placed by clicking on a [initial(target_machine_path.name)] with it. It can be removed with a screwdriver.") + . += span_notice("Remember, you may leave behind fingerprints on the device. Wear gloves when handling it to be safe!") + +/obj/item/traitor_machine_trapper/afterattack(atom/movable/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!user.Adjacent(target)) + return + if(!istype(target, target_machine_path)) + balloon_alert(user, "invalid target!") + return + . |= AFTERATTACK_PROCESSED_ITEM + balloon_alert(user, "planting device...") + if(!do_after(user, delay = deploy_time, target = src, interaction_key = DOAFTER_SOURCE_PLANTING_DEVICE)) + return + target.AddComponent(\ + /datum/component/interaction_booby_trap,\ + additional_triggers = list(COMSIG_ORM_COLLECTED_ORE),\ + on_triggered_callback = CALLBACK(src, PROC_REF(on_triggered)),\ + on_defused_callback = CALLBACK(src, PROC_REF(on_defused)),\ + ) + RegisterSignal(target, COMSIG_PARENT_QDELETING, GLOBAL_PROC_REF(qdel), src) + moveToNullspace() + +/// Called when applied trap is triggered, mark success +/obj/item/traitor_machine_trapper/proc/on_triggered(atom/machine) + SEND_SIGNAL(machine, COMSIG_TRAITOR_MACHINE_TRAP_TRIGGERED) + qdel(src) + +/// Called when applied trap has been defused, retrieve this item from nullspace +/obj/item/traitor_machine_trapper/proc/on_defused(atom/machine, mob/defuser, obj/item/tool) + UnregisterSignal(machine, COMSIG_PARENT_QDELETING) + playsound(machine, 'sound/effects/structure_stress/pop3.ogg', 100, vary = TRUE) + forceMove(get_turf(machine)) + visible_message(span_warning("A [src] falls out from the [machine]!")) + +/// Datum which manages references to things we are instructed to destroy +/datum/objective_target_machine_handler + /// Existing instances of machines organised by typepath + var/list/machine_instances_by_path = list() + +/datum/objective_target_machine_handler/New() + . = ..() + RegisterSignal(SSdcs, COMSIG_GLOB_NEW_MACHINE, PROC_REF(on_machine_created)) + RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(finalise_valid_targets)) + +/// Adds a newly created machine to our list of machines, if we need it +/datum/objective_target_machine_handler/proc/on_machine_created(datum/source, obj/machinery/new_machine) + SIGNAL_HANDLER + new_machine.add_as_sabotage_target() + +/// Confirm that everything added to the list is a valid target, then prevent new targets from being added +/datum/objective_target_machine_handler/proc/finalise_valid_targets() + SIGNAL_HANDLER + for (var/machine_type in machine_instances_by_path) + for (var/obj/machinery/machine as anything in machine_instances_by_path[machine_type]) + var/turf/place = get_turf(machine) + if(!place || !is_station_level(place.z)) + machine_instances_by_path[machine_type] -= machine + continue + RegisterSignal(machine, COMSIG_PARENT_QDELETING, PROC_REF(machine_destroyed)) + UnregisterSignal(SSdcs, COMSIG_GLOB_NEW_MACHINE) + +/datum/objective_target_machine_handler/proc/machine_destroyed(atom/machine) + SIGNAL_HANDLER + // Sadly can't do a direct typepath association because of some map helper subtypes + for (var/machine_type in machine_instances_by_path) + machine_instances_by_path[machine_type] -= machine + +// Mark valid machines as targets, add a new entry here if you add a new potential target + +/obj/machinery/telecomms/hub/add_as_sabotage_target() + return add_sabotage_machine(src, /obj/machinery/telecomms/hub) // Not always our specific type because of map helper subtypes + +/obj/machinery/rnd/server/add_as_sabotage_target() + return add_sabotage_machine(src, type) + +/obj/machinery/rnd/production/protolathe/department/add_as_sabotage_target() + return add_sabotage_machine(src, type) + +/obj/machinery/rnd/production/techfab/department/add_as_sabotage_target() + return add_sabotage_machine(src, type) + +/obj/machinery/mineral/ore_redemption/add_as_sabotage_target() + return add_sabotage_machine(src, type) diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index c8b14a20310..f37092fcfc8 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -48,34 +48,36 @@ . += span_notice("Alt-click to rotate the input and output direction.") /// Turns ore into its refined type, and sends it to its material container -/obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O) - if(QDELETED(O)) +/obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/gathered_ore) + if(QDELETED(gathered_ore)) return var/datum/component/material_container/mat_container = materials.mat_container if (!mat_container) return - if(O.refined_type == null) + if(gathered_ore.refined_type == null) return - if(O?.refined_type) - points += O.points * point_upgrade * O.amount + if(gathered_ore?.refined_type) + points += gathered_ore.points * point_upgrade * gathered_ore.amount - var/material_amount = mat_container.get_item_material_amount(O, BREAKDOWN_FLAGS_ORM) + var/material_amount = mat_container.get_item_material_amount(gathered_ore, BREAKDOWN_FLAGS_ORM) if(!material_amount) - qdel(O) //no materials, incinerate it + qdel(gathered_ore) //no materials, incinerate it - else if(!mat_container.has_space(material_amount * O.amount)) //if there is no space, eject it - unload_mineral(O) + else if(!mat_container.has_space(material_amount * gathered_ore.amount)) //if there is no space, eject it + unload_mineral(gathered_ore) else - var/list/stack_mats = O.get_material_composition(BREAKDOWN_FLAGS_ORM) + var/list/stack_mats = gathered_ore.get_material_composition(BREAKDOWN_FLAGS_ORM) var/mats = stack_mats & mat_container.materials - var/amount = O.amount - mat_container.insert_item(O, ore_multiplier, breakdown_flags=BREAKDOWN_FLAGS_ORM) //insert it + var/amount = gathered_ore.amount + mat_container.insert_item(gathered_ore, ore_multiplier, breakdown_flags=BREAKDOWN_FLAGS_ORM) //insert it materials.silo_log(src, "smelted", amount, "someone", mats) - qdel(O) + qdel(gathered_ore) + + SEND_SIGNAL(src, COMSIG_ORM_COLLECTED_ORE) /// Returns the amount of a specific alloy design, based on the accessible materials /obj/machinery/mineral/ore_redemption/proc/can_smelt_alloy(datum/design/D) diff --git a/icons/obj/weapons/items_and_weapons.dmi b/icons/obj/weapons/items_and_weapons.dmi index 584ae5f7d19fe7e3c6b6d2528d06b8c665efdb0f..2ae955091f65b7445716b295a58f087798b8bb3f 100644 GIT binary patch delta 13319 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 delta 12914 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%0Dll? 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