diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 88ffb6d2447..a41aacb953c 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -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) . = ..() - . += "The safety is [safety ? "on" : "off"]." - + if(has_safety) + . += "The safety is [safety_active ? "on" : "off"]." /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, "You [safety ? "enable" : "disable"] [src]'s safety.") +/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, "You [safety_active ? "enable" : "disable"] [src]'s safety.") + 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, "\The [src] is already full!") - safety = old_safety + to_chat(user, "[src] is already full.") 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, "\The [src] has been refilled by [transferred] units.") - 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, "\The [W] is empty!") - safety = old_safety + return TRUE + + to_chat(user, "[src] has been refilled with [transferred] units.") + 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, "[src] is empty.") - 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, "[src] is empty.") + 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) diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm index 4722417ad46..d8289c434da 100644 --- a/code/game/objects/items/weapons/tanks/watertank.dm +++ b/code/game/objects/items/weapons/tanks/watertank.dm @@ -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) + . += "[src] is currently set to extinguishing mode." + if(NANOFROST) + . += "[src] is currently set to nanofrost mode." + if(METAL_FOAM) + . += "[src] is currently set to metal foam mode." /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, "The nozzle snaps back onto the tank!") 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, "You need at least 100 units of water to use the nanofrost launcher!") + to_chat(user, "You need at least 100 units of water to use the nanofrost launcher!") return - if(nanofrost_cooldown) - to_chat(user, "Nanofrost launcher is still recharging.") + + if(COOLDOWN_TIMELEFT(src, nanofrost_cooldown)) + to_chat(user, "The nanofrost launcher is still recharging!") 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, "Metal foam mix is still being synthesized.") - 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, "Metal foam mix is still being synthesized!") + return + + if(reagents.total_volume < 10) + to_chat(user, "You need at least 10 units of water to use the metal foam synthesizer!") + 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" diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 3abfbf56dc3..07b75d1cb4a 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -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 ..() diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm index 2efd024af58..c5fcbd9c84d 100644 --- a/code/modules/mod/modules/modules_engineering.dm +++ b/code/modules/mod/modules/modules_engineering.dm @@ -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, "Swapped to nanofrost launcher.") - if(NANOFROST) - nozzle_mode = METAL_FOAM - to_chat(user, "Swapped to metal foam synthesizer.") - if(METAL_FOAM) - nozzle_mode = EXTINGUISHER - to_chat(user, "Swapped to water extinguisher.") - 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) - . += "[src] is currently set to extinguishing mode." - if(NANOFROST) - . += "[src] is currently set to nanofrost mode." - if(METAL_FOAM) - . += "[src] is currently set to metal foam mode." - -/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, "You need at least 100 units of water to use the nanofrost launcher!") - return - if(!COOLDOWN_FINISHED(src, nanofrost_cooldown)) - to_chat(user, "Nanofrost launcher is still recharging.") - 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, "Metal foam mix is still being synthesized.") - return - if(reagents.total_volume < 10) - to_chat(user, "You need at least 10 units of water to use the metal foam synthesizer!") - 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