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
This commit is contained in:
warriorstar-orion
2025-05-25 15:35:12 +00:00
committed by GitHub
parent 72b000b0e1
commit af87e274ed
8 changed files with 120 additions and 77 deletions
+4
View File
@@ -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"
+3
View File
@@ -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
@@ -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 += "<span class='notice'>Looks odd!</span>"
/// 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, "<span class='danger'>[source] was boobytrapped!</span>")
if(!isnull(saboteur))
to_chat(saboteur, "<span class='danger'>Success! Your trap on [source] caught [victim.name]!</span>")
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, "<span class='danger'>Failure! Your trap on [parent] didn't catch anyone this time.</span>")
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
+1 -1
View File
@@ -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()
@@ -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, "<span class='danger'>Success! Bomb on [A] armed!</span>")
if(summoner)
to_chat(summoner, "<span class='warning'>Your guardian has primed [A] to explode!</span>")
bomb_cooldown = world.time + default_bomb_cooldown
B.spawner = src
B.disguise (A)
else
to_chat(src, "<span class='danger'>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.</span>")
/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, "<span class='danger'>You knew this because of your link with your guardian, so you smartly defuse the bomb.</span>")
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, "<span class='warning'>You can't disguise disposal units as a bomb!</span>")
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, "<span class='danger'>Failure! Your trap on [stored_obj] didn't catch anyone this time.</span>")
qdel(src)
/obj/item/guardian_bomb/proc/detonate(mob/living/user)
if(!istype(user))
return
to_chat(user, "<span class='danger'>[src] was boobytrapped!</span>")
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, "<span class='danger'>You knew this because of your link with your guardian, so you smartly defuse the bomb.</span>")
stored_obj.forceMove(get_turf(loc))
qdel(src)
return
add_attack_logs(user, stored_obj, "booby trap TRIGGERED (spawner: [spawner])")
to_chat(spawner, "<span class='danger'>Success! Your trap on [src] caught [user]!</span>")
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)
. += "<span class='notice'>Looks odd!</span>"
+1
View File
@@ -106,6 +106,7 @@
return ..()
/obj/machinery/door/Bumped(atom/AM)
. = ..()
if(operating || emagged || foam_level)
return
if(ismob(AM))
+1
View File
@@ -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))
+1
View File
@@ -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"