The Great Fire Extinguisher Refactor (#28005)

* Creation (suffering), success

* AGHHHH! SPACES!

* Update watertank.dm

* comment update

* Apply suggestions from code review

Co-authored-by: Charlie Nolan <funnyman3595@gmail.com>
Signed-off-by: CRUNCH <143041327+Fordoxia@users.noreply.github.com>

---------

Signed-off-by: CRUNCH <143041327+Fordoxia@users.noreply.github.com>
Co-authored-by: Charlie Nolan <funnyman3595@gmail.com>
This commit is contained in:
CRUNCH
2025-02-09 18:13:20 +00:00
committed by GitHub
parent 2ca7c0fd7e
commit d9e6e40ed0
4 changed files with 155 additions and 177 deletions
+61 -48
View File
@@ -17,12 +17,16 @@
attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed")
dog_fashion = /datum/dog_fashion/back
resistance_flags = FIRE_PROOF
new_attack_chain = TRUE
var/max_water = 50
var/safety = TRUE
var/refilling = FALSE
/// FALSE by default, turfs picked from a spray are random, set to TRUE to make it always have at least one water effect per row
/// If `TRUE`, using in hand will toggle the extinguisher's safety. This must be set to `FALSE` for extinguishers with different firing modes (i.e. backpacks).
var/has_safety = TRUE
/// If `TRUE`, the extinguisher will not fire.
var/safety_active = TRUE
/// When `FALSE`, turfs picked from a spray are random. When `TRUE`, it always has at least one water effect per row.
var/precision = FALSE
var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled
/// Sets the `cooling_temperature` of the water reagent datum inside of the extinguisher when it is refilled.
var/cooling_power = 2
COOLDOWN_DECLARE(last_use)
/obj/item/extinguisher/mini
@@ -31,8 +35,8 @@
icon_state = "miniFE0"
item_state = "miniFE"
base_icon_state = "miniFE"
hitsound = null //it is much lighter, after all.
flags = null //doesn't CONDUCT
hitsound = null // It is much lighter, after all.
flags = null // Non-conductive, not made of metal.
throwforce = 2
w_class = WEIGHT_CLASS_SMALL
force = 3
@@ -42,8 +46,8 @@
/obj/item/extinguisher/examine(mob/user)
. = ..()
. += "<span class='notice'>The safety is [safety ? "on" : "off"].</span>"
if(has_safety)
. += "<span class='notice'>The safety is [safety_active ? "on" : "off"].</span>"
/obj/item/extinguisher/Initialize(mapload)
. = ..()
@@ -52,72 +56,81 @@
reagents.add_reagent("water", max_water)
ADD_TRAIT(src, TRAIT_CAN_POINT_WITH, ROUNDSTART_TRAIT)
/obj/item/extinguisher/attack_self__legacy__attackchain(mob/user as mob)
safety = !safety
icon_state = "[base_icon_state][!safety]"
to_chat(user, "<span class='notice'>You [safety ? "enable" : "disable"] [src]'s safety.</span>")
/obj/item/extinguisher/activate_self(mob/user)
if(..())
return
/obj/item/extinguisher/attack_obj__legacy__attackchain(obj/O, mob/living/user, params)
if(AttemptRefill(O, user))
refilling = TRUE
return FALSE
else
return ..()
// Backpack extinguishers have no safety mechanism.
if(!has_safety)
return
/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user)
safety_active = !safety_active
icon_state = "[base_icon_state][!safety_active]"
to_chat(user, "<span class='notice'>You [safety_active ? "enable" : "disable"] [src]'s safety.</span>")
return ITEM_INTERACT_COMPLETE
/obj/item/extinguisher/interact_with_atom(atom/target, mob/living/user, list/modifiers)
. = ..()
if(attempt_refill(target, user))
return ITEM_INTERACT_COMPLETE
if(extinguisher_spray(target, user))
return ITEM_INTERACT_COMPLETE
/obj/item/extinguisher/ranged_interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(extinguisher_spray(target, user))
return ITEM_INTERACT_COMPLETE
/obj/item/extinguisher/proc/attempt_refill(atom/target, mob/user)
if(!istype(target, /obj/structure/reagent_dispensers/watertank) || !target.Adjacent(user))
return FALSE
var/old_safety = safety
safety = TRUE
if(reagents.total_volume == reagents.maximum_volume)
to_chat(user, "<span class='notice'>\The [src] is already full!</span>")
safety = old_safety
to_chat(user, "<span class='notice'>[src] is already full.</span>")
return TRUE
var/obj/structure/reagent_dispensers/watertank/W = target
var/transferred = W.reagents.trans_to(src, max_water)
if(transferred > 0)
to_chat(user, "<span class='notice'>\The [src] has been refilled by [transferred] units.</span>")
playsound(loc, 'sound/effects/refill.ogg', 50, TRUE, -6)
for(var/datum/reagent/water/R in reagents.reagent_list)
R.cooling_temperature = cooling_power
else
if(!transferred)
to_chat(user, "<span class='notice'>\The [W] is empty!</span>")
safety = old_safety
return TRUE
to_chat(user, "<span class='notice'>[src] has been refilled with [transferred] units.</span>")
playsound(loc, 'sound/effects/refill.ogg', 50, TRUE, -6)
for(var/datum/reagent/water/R in reagents.reagent_list)
R.cooling_temperature = cooling_power
return TRUE
/obj/item/extinguisher/afterattack__legacy__attackchain(atom/target, mob/user, flag)
. = ..()
//TODO; Add support for reagents in water.
if(target.loc == user)//No more spraying yourself when putting your extinguisher away
return
/obj/item/extinguisher/proc/extinguisher_spray(atom/A, mob/living/user)
. = TRUE
if(refilling)
refilling = FALSE
return
if(safety)
return ..()
if(reagents.total_volume < 1)
to_chat(user, "<span class='danger'>[src] is empty.</span>")
return
// Violence, please!
if(safety_active)
return FALSE
if(!COOLDOWN_FINISHED(src, last_use))
return
COOLDOWN_START(src, last_use, 2 SECONDS)
if(reagents.total_volume < 1)
to_chat(user, "<span class='danger'>[src] is empty.</span>")
return
if(A.loc == user)
return
COOLDOWN_START(src, last_use, 2 SECONDS)
if(reagents.chem_temp > 300 || reagents.chem_temp < 280)
add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K")
add_attack_logs(user, A, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K")
playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3)
var/direction = get_dir(src, target)
var/direction = get_dir(src, A)
if(isobj(user.buckled) && !user.buckled.anchored && !istype(user.buckled, /obj/vehicle))
INVOKE_ASYNC(src, PROC_REF(buckled_speed_move), user.buckled, direction)
else
user.newtonian_move(turn(direction, 180))
var/turf/T = get_turf(target)
var/turf/T = get_turf(A)
var/turf/T1 = get_step(T, turn(direction, 90))
var/turf/T2 = get_step(T, turn(direction, -90))
var/list/the_targets = list(T, T1, T2)
@@ -142,7 +142,7 @@
return
/proc/check_tank_exists(parent_tank, mob/living/carbon/human/M, obj/O)
if(!parent_tank || !istype(parent_tank, /obj/item/watertank)) //To avoid weird issues from admin spawns
if(!parent_tank || (!istype(parent_tank, /obj/item/watertank) && !istype(parent_tank, /obj/item/mod/module/firefighting_tank))) //To avoid weird issues from admin spawns
return FALSE
else
return TRUE
@@ -218,11 +218,12 @@
name = "extinguisher nozzle"
desc = "A heavy duty nozzle attached to a firefighter's backpack tank."
icon = 'icons/obj/watertank.dmi'
icon_state = "atmos_nozzle"
icon_state = "atmos_nozzle_1"
item_state = "nozzleatmos"
safety = 0
has_safety = FALSE
safety_active = FALSE
max_water = 500
precision = 1
precision = TRUE
cooling_power = 5
w_class = WEIGHT_CLASS_HUGE
flags = NODROP //Necessary to ensure that the nozzle and tank never seperate
@@ -230,10 +231,23 @@
var/obj/item/watertank/tank
/// What mode are we currently in?
var/nozzle_mode = EXTINGUISHER
/// Are we overusing the metal synthesizer? can be used 5 times in quick succession, regains 1 use per 10 seconds
var/metal_synthesis_cooldown = 0
/// Is our nanofrost on cooldown?
var/nanofrost_cooldown = FALSE
/// How many shots of metal foam do we have?
var/metal_synthesis_charge = 5
/// Time to refill 1 charge of metal foam.
var/metal_regen_time = 2 SECONDS
/// Refire delay for nanofrost chunks.
var/nanofrost_cooldown_time = 2 SECONDS
COOLDOWN_DECLARE(nanofrost_cooldown)
/obj/item/extinguisher/mini/nozzle/examine(mob/user)
. = ..()
switch(nozzle_mode)
if(EXTINGUISHER)
. += "<span class='notice'>[src] is currently set to extinguishing mode.</span>"
if(NANOFROST)
. += "<span class='notice'>[src] is currently set to nanofrost mode.</span>"
if(METAL_FOAM)
. += "<span class='notice'>[src] is currently set to metal foam mode.</span>"
/obj/item/extinguisher/mini/nozzle/Initialize(mapload)
if(!check_tank_exists(loc, src))
@@ -255,67 +269,88 @@
if(tank && loc != tank.loc)
forceMove(tank)
/obj/item/extinguisher/mini/nozzle/attack_self__legacy__attackchain(mob/user)
/obj/item/extinguisher/mini/nozzle/activate_self(mob/user)
..()
switch(nozzle_mode)
if(EXTINGUISHER)
nozzle_mode = NANOFROST
tank.icon_state = "waterbackpackatmos_1"
to_chat(user, "Swapped to nanofrost launcher")
if(NANOFROST)
nozzle_mode = METAL_FOAM
tank.icon_state = "waterbackpackatmos_2"
to_chat(user, "Swapped to metal foam synthesizer")
if(METAL_FOAM)
nozzle_mode = EXTINGUISHER
tank.icon_state = "waterbackpackatmos_0"
to_chat(user, "Swapped to water extinguisher")
update_icon(UPDATE_ICON_STATE)
return ITEM_INTERACT_COMPLETE
/obj/item/extinguisher/mini/nozzle/update_icon_state()
switch(nozzle_mode)
if(EXTINGUISHER)
icon_state = "atmos_nozzle_1"
tank.icon_state = "waterbackpackatmos_0"
if(NANOFROST)
icon_state = "atmos_nozzle_2"
tank.icon_state = "waterbackpackatmos_1"
if(METAL_FOAM)
icon_state = "atmos_nozzle_3"
tank.icon_state = "waterbackpackatmos_2"
/obj/item/extinguisher/mini/nozzle/dropped(mob/user)
..()
if(istype(tank, /obj/item/mod/module/firefighting_tank))
return
to_chat(user, "<span class='notice'>The nozzle snaps back onto the tank!</span>")
tank.on = FALSE
loc = tank
/obj/item/extinguisher/mini/nozzle/afterattack__legacy__attackchain(atom/target, mob/user)
/obj/item/extinguisher/mini/nozzle/extinguisher_spray(atom/A, mob/living/user)
if(nozzle_mode == EXTINGUISHER)
..()
return
var/Adj = user.Adjacent(target)
if(Adj)
AttemptRefill(target, user)
return ..()
. = TRUE
switch(nozzle_mode)
if(NANOFROST)
if(Adj)
return //Safety check so you don't blast yourself trying to refill your tank
if(reagents.total_volume < 100)
to_chat(user, "<span class='notice'>You need at least 100 units of water to use the nanofrost launcher!</span>")
to_chat(user, "<span class='warning'>You need at least 100 units of water to use the nanofrost launcher!</span>")
return
if(nanofrost_cooldown)
to_chat(user, "<span class='notice'>Nanofrost launcher is still recharging.</span>")
if(COOLDOWN_TIMELEFT(src, nanofrost_cooldown))
to_chat(user, "<span class='warning'>The nanofrost launcher is still recharging!</span>")
return
nanofrost_cooldown = TRUE
COOLDOWN_START(src, nanofrost_cooldown, nanofrost_cooldown_time)
reagents.remove_any(100)
var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src))
var/obj/effect/nanofrost_container/iceball = new /obj/effect/nanofrost_container(get_turf(src))
log_game("[key_name(user)] used Nanofrost at [get_area(user)] ([user.x], [user.y], [user.z]).")
playsound(src,'sound/items/syringeproj.ogg', 40, TRUE)
A.throw_at(target, 6, 2, user)
iceball.throw_at(A, 6, 2, user)
sleep(2)
A.Smoke()
addtimer(VARSET_CALLBACK(src, nanofrost_cooldown, FALSE))
if(METAL_FOAM)
if(!Adj)
return
if(metal_synthesis_cooldown >= 5)
to_chat(user, "<span class='notice'>Metal foam mix is still being synthesized.</span>")
return
var/obj/effect/particle_effect/foam/metal/F = new /obj/effect/particle_effect/foam/metal(get_turf(target), TRUE)
F.spread_amount = 0
metal_synthesis_cooldown++
addtimer(CALLBACK(src, PROC_REF(metal_cooldown)), 10 SECONDS)
iceball.Smoke()
return
/obj/item/extinguisher/mini/nozzle/proc/metal_cooldown()
metal_synthesis_cooldown--
if(METAL_FOAM)
if(!user.Adjacent(A) || !isturf(A))
return
if(metal_synthesis_charge <= 0)
to_chat(user, "<span class='warning'>Metal foam mix is still being synthesized!</span>")
return
if(reagents.total_volume < 10)
to_chat(user, "<span class='warning'>You need at least 10 units of water to use the metal foam synthesizer!</span>")
return
var/obj/effect/particle_effect/foam/metal/foam = new /obj/effect/particle_effect/foam/metal(get_turf(A), TRUE)
foam.spread_amount = 0
reagents.remove_any(10)
metal_synthesis_charge--
addtimer(CALLBACK(src, PROC_REF(refill_metal_charge)), metal_regen_time)
return
/obj/item/extinguisher/mini/nozzle/proc/refill_metal_charge()
metal_synthesis_charge++
/obj/effect/nanofrost_container
name = "nanofrost container"
@@ -120,7 +120,7 @@
/mob/living/simple_animal/pet/dog/corgi/UnarmedAttack(atom/A)
if(istype(inventory_back, /obj/item/extinguisher))
var/obj/item/extinguisher/E = inventory_back
if(E.AttemptRefill(A, src))
if(E.attempt_refill(A, src))
return
return ..()
+18 -88
View File
@@ -159,49 +159,31 @@
/// Atmos water tank module
/obj/item/mod/module/firefighting_tank
name = "MOD firefighting tank"
desc = "A refrigerated and pressurized module tank with an extinguisher nozzle, intended to fight fires. Swaps between extinguisher, nanofrost launcher, and metal foam dispenser for breaches. Nanofrost converts plasma in the air to nitrogen, but only if it is combusting at the time."
icon_state = "firefighting_tank"
module_type = MODULE_ACTIVE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN * 3
device = /obj/item/extinguisher/mini/mod
#define EXTINGUISHER 0
#define NANOFROST 1
#define METAL_FOAM 2
/obj/item/extinguisher/mini/mod
/obj/item/mod/module/firefighting_tank
name = "MOD firefighting tank"
desc = "A refrigerated and pressurized module tank with an extinguisher nozzle, intended to fight fires. \
Swaps between extinguisher, nanofrost launcher, and metal foam dispenser for breaches. Nanofrost converts plasma in the air to nitrogen, but only if it is combusting at the time.\
The smaller volume compared to a dedicated firefighting backpack means that non-water modes suffer from longer cooldowns."
icon_state = "firefighting_tank"
module_type = MODULE_ACTIVE
complexity = 2
active_power_cost = DEFAULT_CHARGE_DRAIN * 3
incompatible_modules = list(/obj/item/mod/module/firefighting_tank)
device = /obj/item/extinguisher/mini/nozzle/mod
// Used by nozzle code.
var/volume = 500
/obj/item/extinguisher/mini/nozzle/mod
name = "modsuit extinguisher nozzle"
desc = "A heavy duty nozzle attached to a modsuit's internal tank."
icon = 'icons/obj/watertank.dmi'
icon_state = "atmos_nozzle_1"
item_state = "nozzleatmos"
safety = 0
max_water = 500
precision = 1
cooling_power = 5
w_class = WEIGHT_CLASS_HUGE
flags = NODROP // Necessary to ensure that the nozzle and tank never seperate
var/nozzle_mode = EXTINGUISHER
var/metal_synthesis_charge = 5
COOLDOWN_DECLARE(nanofrost_cooldown)
metal_regen_time = 5 SECONDS
nanofrost_cooldown_time = 10 SECONDS
/obj/item/extinguisher/mini/mod/attack_self__legacy__attackchain(mob/user)
switch(nozzle_mode)
if(EXTINGUISHER)
nozzle_mode = NANOFROST
to_chat(user, "<span class='notice'>Swapped to nanofrost launcher.</span>")
if(NANOFROST)
nozzle_mode = METAL_FOAM
to_chat(user, "<span class='notice'>Swapped to metal foam synthesizer.</span>")
if(METAL_FOAM)
nozzle_mode = EXTINGUISHER
to_chat(user, "<span class='notice'>Swapped to water extinguisher.</span>")
update_icon(UPDATE_ICON_STATE)
/obj/item/extinguisher/mini/mod/update_icon_state()
/obj/item/extinguisher/mini/nozzle/mod/update_icon_state()
switch(nozzle_mode)
if(EXTINGUISHER)
icon_state = "atmos_nozzle_1"
@@ -210,58 +192,6 @@
if(METAL_FOAM)
icon_state = "atmos_nozzle_3"
/obj/item/extinguisher/mini/mod/examine(mob/user)
. = ..()
switch(nozzle_mode)
if(EXTINGUISHER)
. += "<span class='notice'>[src] is currently set to extinguishing mode.</span>"
if(NANOFROST)
. += "<span class='notice'>[src] is currently set to nanofrost mode.</span>"
if(METAL_FOAM)
. += "<span class='notice'>[src] is currently set to metal foam mode.</span>"
/obj/item/extinguisher/mini/mod/afterattack__legacy__attackchain(atom/target, mob/user)
var/is_adjacent = user.Adjacent(target)
if(is_adjacent && AttemptRefill(target, user))
return
switch(nozzle_mode)
if(EXTINGUISHER)
..()
if(NANOFROST)
if(reagents.total_volume < 100)
to_chat(user, "<span class='warning'>You need at least 100 units of water to use the nanofrost launcher!</span>")
return
if(!COOLDOWN_FINISHED(src, nanofrost_cooldown))
to_chat(user, "<span class='warning'>Nanofrost launcher is still recharging.</span>")
return
COOLDOWN_START(src, nanofrost_cooldown, 10 SECONDS)
reagents.remove_any(100)
var/obj/effect/nanofrost_container/A = new /obj/effect/nanofrost_container(get_turf(src))
log_game("[key_name(user)] used Nanofrost at [get_area(user)] ([user.x], [user.y], [user.z]).")
playsound(src, 'sound/items/syringeproj.ogg', 40, TRUE)
A.throw_at(target, 6, 2, user)
sleep(2)
A.Smoke()
if(METAL_FOAM)
if(!is_adjacent|| !isturf(target))
return
if(metal_synthesis_charge <= 0)
to_chat(user, "<span class='warning'>Metal foam mix is still being synthesized.</span>")
return
if(reagents.total_volume < 10)
to_chat(user, "<span class='warning'>You need at least 10 units of water to use the metal foam synthesizer!</span>")
return
var/obj/effect/particle_effect/foam/metal/F = new /obj/effect/particle_effect/foam/metal(get_turf(target), TRUE)
F.spread_amount = 0
reagents.remove_any(10)
metal_synthesis_charge--
addtimer(CALLBACK(src, PROC_REF(decrease_metal_charge)), 5 SECONDS)
/obj/item/extinguisher/mini/mod/proc/decrease_metal_charge()
metal_synthesis_charge++
#undef EXTINGUISHER
#undef NANOFROST
#undef METAL_FOAM