From af87e274edbc54bbc48442d55a3373494ae6c699 Mon Sep 17 00:00:00 2001 From: warriorstar-orion Date: Sun, 25 May 2025 11:35:12 -0400 Subject: [PATCH] refactor explosive holopara bomb to component (#29338) * refactor explosive holopara bomb to component * fix examine range * proper signals * fixing this oneoff for airlocks as a high-priority target * proper support for pull --- code/__DEFINES/dcs/atom_signals.dm | 4 + code/__DEFINES/misc_defines.dm | 3 + .../components/direct_explosive_trap.dm | 99 +++++++++++++++++++ code/game/atoms.dm | 2 +- .../guardian/types/explosive_guardian.dm | 86 ++-------------- code/game/machinery/doors/door.dm | 1 + code/modules/mob/living/living.dm | 1 + paradise.dme | 1 + 8 files changed, 120 insertions(+), 77 deletions(-) create mode 100644 code/datums/components/direct_explosive_trap.dm diff --git a/code/__DEFINES/dcs/atom_signals.dm b/code/__DEFINES/dcs/atom_signals.dm index 39bca29e114..17b7ce1200e 100644 --- a/code/__DEFINES/dcs/atom_signals.dm +++ b/code/__DEFINES/dcs/atom_signals.dm @@ -123,6 +123,10 @@ ///called on /living, when pull is attempted, but before it completes, from base of [/mob/living/proc/start_pulling]: (atom/movable/thing, force) #define COMSIG_LIVING_TRY_PULL "living_try_pull" #define COMSIG_LIVING_CANCEL_PULL (1 << 0) +#define COMSIG_ATOM_PULLED "atom_pulled" + +///from base of atom/Bumped(atom/bumped_atom) +#define COMSIG_ATOM_BUMPED "atom_bumped" ///from base of atom/expose_reagents(): (/list, /datum/reagents, chemholder, volume_modifier) #define COMSIG_ATOM_EXPOSE_REAGENTS "atom_expose_reagents" diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm index 7d646927949..1c29b183f14 100644 --- a/code/__DEFINES/misc_defines.dm +++ b/code/__DEFINES/misc_defines.dm @@ -757,3 +757,6 @@ do { \ #define BRIDGE_SPAWN_TOO_WIDE 1 #define BRIDGE_SPAWN_TOO_NARROW 2 #define BRIDGE_SPAWN_BAD_TERRAIN 3 + +#define DIRECT_EXPLOSIVE_TRAP_DEFUSE 1 +#define DIRECT_EXPLOSIVE_TRAP_IGNORE 2 diff --git a/code/datums/components/direct_explosive_trap.dm b/code/datums/components/direct_explosive_trap.dm new file mode 100644 index 00000000000..b2ba85d7e02 --- /dev/null +++ b/code/datums/components/direct_explosive_trap.dm @@ -0,0 +1,99 @@ +/** + * Responds to certain signals and 'explodes' on the person using the item. + */ +/datum/component/direct_explosive_trap + /// An optional mob to inform about explosions + var/mob/living/saboteur + /// Amount of force to apply + var/explosive_force + /// Optional additional target checks before we go off + var/datum/callback/explosive_check + /// Signals which set off the bomb, must pass a mob as the first non-source argument + var/list/triggering_signals + +/datum/component/direct_explosive_trap/Initialize( + mob/living/saboteur_, + explosive_force_ = EXPLODE_HEAVY, + expire_time_ = 1 MINUTES, + glow_colour_ = COLOR_RED, + datum/callback/explosive_check_, + list/triggering_signals_ = list( + // TODO: This is where COMSIG_ATTACK_HAND would be + // if we could actually rely on it to call its parent + COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY, + COMSIG_INTERACT_USER, + COMSIG_ATTACK_BY, + COMSIG_ATOM_BUMPED, + COMSIG_CLICK_ALT, + COMSIG_ATOM_PULLED, + COMSIG_MOUSEDROP_ONTO, + ) +) + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + saboteur = saboteur_ + explosive_force = explosive_force_ + explosive_check = explosive_check_ + triggering_signals = triggering_signals_ + + if(expire_time_ > 0) + addtimer(CALLBACK(src, PROC_REF(bomb_expired)), expire_time_, TIMER_DELETE_ME) + +/datum/component/direct_explosive_trap/RegisterWithParent() + if(!(COMSIG_PARENT_EXAMINE in triggering_signals)) // Maybe you're being extra mean with this one + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(on_examined)) + RegisterSignals(parent, triggering_signals, PROC_REF(explode)) + if(!isnull(saboteur)) + RegisterSignal(saboteur, COMSIG_PARENT_QDELETING, PROC_REF(on_bomber_deleted)) + +/datum/component/direct_explosive_trap/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_PARENT_EXAMINE) + triggering_signals) + if(!isnull(saboteur)) + UnregisterSignal(saboteur, COMSIG_PARENT_QDELETING) + +/datum/component/direct_explosive_trap/Destroy(force) + if(isnull(saboteur)) + return ..() + UnregisterSignal(saboteur, COMSIG_PARENT_QDELETING) + saboteur = null + return ..() + +/// Let people know something is up +/datum/component/direct_explosive_trap/proc/on_examined(datum/source, mob/user, list/examine_text) + SIGNAL_HANDLER // COMSIG_PARENT_EXAMINE + if(get_dist(source, user) <= 2) + examine_text += "Looks odd!" + +/// Blow up +/datum/component/direct_explosive_trap/proc/explode(atom/source, mob/living/victim) + SIGNAL_HANDLER // triggering_signals + if(!isliving(victim)) + return + if(!isnull(explosive_check)) + var/result = explosive_check.Invoke(source, victim) + if(result == DIRECT_EXPLOSIVE_TRAP_DEFUSE) + qdel(src) + return + else if(result == DIRECT_EXPLOSIVE_TRAP_IGNORE) + return + to_chat(victim, "[source] was boobytrapped!") + if(!isnull(saboteur)) + to_chat(saboteur, "Success! Your trap on [source] caught [victim.name]!") + playsound(source, 'sound/effects/explosion2.ogg', 200, TRUE) + victim.ex_act(explosive_force) + // A bomb went off in your hands. Actually lets people follow up with it if + // they bait someone, right now it is unreliable. + victim.Stun(3 SECONDS) + qdel(src) + +/// Called if we sit too long without going off +/datum/component/direct_explosive_trap/proc/bomb_expired() + if(!isnull(saboteur)) + to_chat(saboteur, "Failure! Your trap on [parent] didn't catch anyone this time.") + qdel(src) + +/// Don't hang a reference to the person who placed the bomb +/datum/component/direct_explosive_trap/proc/on_bomber_deleted() + SIGNAL_HANDLER + saboteur = null diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 3b4f7a781ca..c3a6cb33bc7 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -345,7 +345,7 @@ // want to get rid of this we need to move bumping in its entirety to signal // handlers, which is scarier. set waitfor = FALSE - return + SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, AM) /// Convenience proc to see if a container is open for chemistry handling /atom/proc/is_open_container() diff --git a/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm b/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm index 6f2574ad9b0..70121417183 100644 --- a/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/types/explosive_guardian.dm @@ -24,17 +24,24 @@ return if(isobj(A) && can_plant(A)) if(bomb_cooldown <= world.time && stat == CONSCIOUS) - var/obj/item/guardian_bomb/B = new /obj/item/guardian_bomb(get_turf(A)) + A.AddComponent( \ + /datum/component/direct_explosive_trap, \ + saboteur_ = src, \ + explosive_check_ = CALLBACK(src, PROC_REF(validate_target))) add_attack_logs(src, A, "booby trapped (summoner: [summoner])") to_chat(src, "Success! Bomb on [A] armed!") if(summoner) to_chat(summoner, "Your guardian has primed [A] to explode!") bomb_cooldown = world.time + default_bomb_cooldown - B.spawner = src - B.disguise (A) else to_chat(src, "Your power is on cooldown! You must wait another [max(round((bomb_cooldown - world.time)*0.1, 0.1), 0)] seconds before you can place next bomb.") +/mob/living/simple_animal/hostile/guardian/bomb/proc/validate_target(atom/source, mob/living/target) + if(target == summoner) + add_attack_logs(target, source, "booby trap defused") + to_chat(target, "You knew this because of your link with your guardian, so you smartly defuse the bomb.") + return DIRECT_EXPLOSIVE_TRAP_DEFUSE + /mob/living/simple_animal/hostile/guardian/bomb/proc/can_plant(atom/movable/A) if(ismecha(A)) var/obj/mecha/target = A @@ -45,76 +52,3 @@ to_chat(src, "You can't disguise disposal units as a bomb!") return FALSE return TRUE - -/obj/item/guardian_bomb - name = "bomb" - desc = "You shouldn't be seeing this!" - var/obj/stored_obj - var/mob/living/spawner - -/obj/item/guardian_bomb/proc/disguise(obj/A) - A.forceMove(src) - stored_obj = A - opacity = A.opacity - anchored = A.anchored - density = A.density - appearance = A.appearance - dir = A.dir - move_resist = A.move_resist - addtimer(CALLBACK(src, PROC_REF(disable)), 600) - -/obj/item/guardian_bomb/CanPass(atom/movable/mover, border_dir) - return stored_obj.CanPass(mover, border_dir) - -/obj/item/guardian_bomb/proc/disable() - add_attack_logs(null, stored_obj, "booby trap expired") - stored_obj.forceMove(get_turf(src)) - if(spawner) - to_chat(spawner, "Failure! Your trap on [stored_obj] didn't catch anyone this time.") - qdel(src) - -/obj/item/guardian_bomb/proc/detonate(mob/living/user) - if(!istype(user)) - return - to_chat(user, "[src] was boobytrapped!") - if(isguardian(spawner)) - var/mob/living/simple_animal/hostile/guardian/G = spawner - if(user == G.summoner) - add_attack_logs(user, stored_obj, "booby trap defused") - to_chat(user, "You knew this because of your link with your guardian, so you smartly defuse the bomb.") - stored_obj.forceMove(get_turf(loc)) - qdel(src) - return - add_attack_logs(user, stored_obj, "booby trap TRIGGERED (spawner: [spawner])") - to_chat(spawner, "Success! Your trap on [src] caught [user]!") - stored_obj.forceMove(get_turf(loc)) - playsound(get_turf(src),'sound/effects/explosion2.ogg', 200, 1) - user.ex_act(EXPLODE_HEAVY) - user.Stun(3 SECONDS)//A bomb went off in your hands. Actually lets people follow up with it if they bait someone, right now it is unreliable. - qdel(src) - -/obj/item/guardian_bomb/attackby__legacy__attackchain(obj/item/W, mob/living/user) - detonate(user) - -/obj/item/guardian_bomb/attack_hand(mob/user) - detonate(user) - -/obj/item/guardian_bomb/MouseDrop_T(obj/item/I, mob/living/user) - detonate(user) - -/obj/item/guardian_bomb/AltClick(mob/living/user) - detonate(user) - -/obj/item/guardian_bomb/MouseDrop(mob/living/user) - detonate(user) - -/obj/item/guardian_bomb/Bumped(mob/living/user) - detonate(user) - -/obj/item/guardian_bomb/can_be_pulled(mob/living/user) - detonate(user) - -/obj/item/guardian_bomb/examine(mob/user) - . = stored_obj.examine(user) - if(get_dist(user, src) <= 2) - . += "Looks odd!" diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 143de7aee6e..a18bf84f09b 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -106,6 +106,7 @@ return ..() /obj/machinery/door/Bumped(atom/AM) + . = ..() if(operating || emagged || foam_level) return if(ismob(AM)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index eb8bbd4ea32..e5cedcf2abe 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1018,6 +1018,7 @@ AM.pulledby = src if(pullin) pullin.update_icon(UPDATE_ICON_STATE) + SEND_SIGNAL(AM, COMSIG_ATOM_PULLED, src) for(var/log_pulltype in GLOB.log_pulltypes) if(istype(AM, log_pulltype)) diff --git a/paradise.dme b/paradise.dme index 09a306d4dcf..be15c1007bd 100644 --- a/paradise.dme +++ b/paradise.dme @@ -497,6 +497,7 @@ #include "code\datums\components\deadchat_control.dm" #include "code\datums\components\debris.dm" #include "code\datums\components\defibrillator.dm" +#include "code\datums\components\direct_explosive_trap.dm" #include "code\datums\components\drift.dm" #include "code\datums\components\ducttape.dm" #include "code\datums\components\edit_complainer.dm"