mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 06:29:23 +01:00
Makes smoke and foam attempt to fill the available space. (#65281)
Have you ever noticed that the chemical smoke and chemical foam reactions are a lot less effective in confined spaces? This is because they currently attempt to spread to all tiles within n steps of their origin. If they can't expand onto a tile they get blocked and the expanding cloud/flood misses out on all the tiles that would be in range, but that can't be reached. Obviously smoke and foam getting blocked by walls and the like makes intuitive sense, but it seemed a bit nonsensical that walls would basically delete a significant chunk of an expanding, amoebic mass. The solution I came up with is making smoke and foam expand until they cover a certain area, with a shared tracker for the target size and total size of the flood. The flood will simply expand as normal until it covers the desired target area. Blocked expansions just don't count and will be made up for with expansion elsewhere. Attendant to these changes are a whole bunch of minor code improvement to smoke, foam, and one for wizard spells because I was already in the area and :pain:. There have been some minor balance changes to the chemical smoke and foam reactions: I converted them over to passing the desired area of the resulting smoke cloud/foam flood. The old equation for the resulting area was along the lines of 2sqrt(x)(sqrt(x) + 1) + 1 given reaction volume x and given unobstructed expansion. I've made them just pass around 2x instead. This is actually less than they used to try for, but now they're guaranteed to reach that unless the flood is fully contained. Not entirely certain if buff or nerf. Probably buff on the station. Also, foam dilution is now based on covered area instead of target expansion range. Since this scales faster than it used to foam has been effectively nerfed at high volumes. To compensate for this I removed the jank 6/7 effect multiplier and increased the base reagent scaling a bit. Again, not certain if buff or nerf.
This commit is contained in:
@@ -57,8 +57,8 @@
|
||||
/datum/effect_system/explosion/smoke
|
||||
|
||||
/datum/effect_system/explosion/smoke/proc/create_smoke()
|
||||
var/datum/effect_system/smoke_spread/S = new
|
||||
S.set_up(2, location)
|
||||
var/datum/effect_system/fluid_spread/smoke/S = new
|
||||
S.set_up(2, location = location)
|
||||
S.start()
|
||||
|
||||
/datum/effect_system/explosion/smoke/start()
|
||||
|
||||
@@ -1,386 +0,0 @@
|
||||
// Foam
|
||||
// Similar to smoke, but slower and mobs absorb its reagent through their exposed skin.
|
||||
#define ALUMINUM_FOAM 1
|
||||
#define IRON_FOAM 2
|
||||
#define RESIN_FOAM 3
|
||||
|
||||
|
||||
/obj/effect/particle_effect/foam
|
||||
name = "foam"
|
||||
icon_state = "foam"
|
||||
opacity = FALSE
|
||||
anchored = TRUE
|
||||
density = FALSE
|
||||
layer = EDGED_TURF_LAYER
|
||||
plane = GAME_PLANE_UPPER
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
var/amount = 3
|
||||
animate_movement = NO_STEPS
|
||||
var/metal = 0
|
||||
var/lifetime = 40
|
||||
var/reagent_divisor = 7
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(
|
||||
/turf/open/space/transit,
|
||||
/turf/open/chasm,
|
||||
/turf/open/lava,
|
||||
))
|
||||
var/slippery_foam = TRUE
|
||||
|
||||
/obj/effect/particle_effect/foam/firefighting
|
||||
name = "firefighting foam"
|
||||
lifetime = 20 //doesn't last as long as normal foam
|
||||
amount = 0 //no spread
|
||||
slippery_foam = FALSE
|
||||
var/absorbed_plasma = 0
|
||||
|
||||
/obj/effect/particle_effect/foam/firefighting/Initialize(mapload)
|
||||
. = ..()
|
||||
RemoveElement(/datum/element/atmos_sensitive)
|
||||
|
||||
/obj/effect/particle_effect/foam/firefighting/process()
|
||||
..()
|
||||
|
||||
var/turf/open/T = get_turf(src)
|
||||
var/obj/effect/hotspot/hotspot = (locate(/obj/effect/hotspot) in T)
|
||||
if(hotspot && istype(T) && T.air)
|
||||
qdel(hotspot)
|
||||
var/datum/gas_mixture/G = T.air
|
||||
if(G.gases[/datum/gas/plasma])
|
||||
var/plas_amt = min(30,G.gases[/datum/gas/plasma][MOLES]) //Absorb some plasma
|
||||
G.gases[/datum/gas/plasma][MOLES] -= plas_amt
|
||||
absorbed_plasma += plas_amt
|
||||
if(G.temperature > T20C)
|
||||
G.temperature = max(G.temperature/2,T20C)
|
||||
G.garbage_collect()
|
||||
T.air_update_turf(FALSE, FALSE)
|
||||
|
||||
/obj/effect/particle_effect/foam/firefighting/kill_foam()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
|
||||
if(absorbed_plasma)
|
||||
var/obj/effect/decal/cleanable/plasma/P = (locate(/obj/effect/decal/cleanable/plasma) in get_turf(src))
|
||||
if(!P)
|
||||
P = new(loc)
|
||||
P.reagents.add_reagent(/datum/reagent/stable_plasma, absorbed_plasma)
|
||||
|
||||
flick("[icon_state]-disolve", src)
|
||||
QDEL_IN(src, 5)
|
||||
|
||||
/obj/effect/particle_effect/foam/firefighting/foam_mob(mob/living/L)
|
||||
if(!istype(L))
|
||||
return
|
||||
L.adjust_wet_stacks(2)
|
||||
|
||||
/obj/effect/particle_effect/foam/metal
|
||||
name = "aluminium foam"
|
||||
metal = ALUMINUM_FOAM
|
||||
icon_state = "mfoam"
|
||||
slippery_foam = FALSE
|
||||
|
||||
/obj/effect/particle_effect/foam/metal/smart
|
||||
name = "smart foam"
|
||||
|
||||
/obj/effect/particle_effect/foam/metal/iron
|
||||
name = "iron foam"
|
||||
metal = IRON_FOAM
|
||||
|
||||
/obj/effect/particle_effect/foam/metal/resin
|
||||
name = "resin foam"
|
||||
metal = RESIN_FOAM
|
||||
|
||||
/obj/effect/particle_effect/foam/long_life
|
||||
lifetime = 150
|
||||
|
||||
/obj/effect/particle_effect/foam/Initialize(mapload)
|
||||
. = ..()
|
||||
create_reagents(1000) //limited by the size of the reagent holder anyway.
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
playsound(src, 'sound/effects/bubbles2.ogg', 80, TRUE, -3)
|
||||
AddElement(/datum/element/atmos_sensitive, mapload)
|
||||
|
||||
/obj/effect/particle_effect/foam/ComponentInitialize()
|
||||
. = ..()
|
||||
if(slippery_foam)
|
||||
AddComponent(/datum/component/slippery, 100)
|
||||
|
||||
/obj/effect/particle_effect/foam/Destroy()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/effect/particle_effect/foam/proc/kill_foam()
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
switch(metal)
|
||||
if(ALUMINUM_FOAM)
|
||||
new /obj/structure/foamedmetal(get_turf(src))
|
||||
if(IRON_FOAM)
|
||||
new /obj/structure/foamedmetal/iron(get_turf(src))
|
||||
if(RESIN_FOAM)
|
||||
new /obj/structure/foamedmetal/resin(get_turf(src))
|
||||
flick("[icon_state]-disolve", src)
|
||||
QDEL_IN(src, 5)
|
||||
|
||||
/obj/effect/particle_effect/foam/smart/kill_foam() //Smart foam adheres to area borders for walls
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
if(metal)
|
||||
var/turf/T = get_turf(src)
|
||||
if(isspaceturf(T)) //Block up any exposed space
|
||||
T.PlaceOnTop(/turf/open/floor/plating/foam, flags = CHANGETURF_INHERIT_AIR)
|
||||
for(var/direction in GLOB.cardinals)
|
||||
var/turf/cardinal_turf = get_step(T, direction)
|
||||
if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf!
|
||||
new/obj/structure/foamedmetal(T)
|
||||
break
|
||||
flick("[icon_state]-disolve", src)
|
||||
QDEL_IN(src, 5)
|
||||
|
||||
/obj/effect/particle_effect/foam/process()
|
||||
lifetime--
|
||||
if(lifetime < 1)
|
||||
kill_foam()
|
||||
return
|
||||
|
||||
var/fraction = 1/initial(reagent_divisor)
|
||||
for(var/obj/O in range(0,src))
|
||||
if(O.type == src.type)
|
||||
continue
|
||||
if(isturf(O.loc))
|
||||
var/turf/T = O.loc
|
||||
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE))
|
||||
continue
|
||||
if(lifetime % reagent_divisor)
|
||||
reagents.expose(O, VAPOR, fraction)
|
||||
var/hit = 0
|
||||
for(var/mob/living/L in range(0,src))
|
||||
hit += foam_mob(L)
|
||||
if(hit)
|
||||
lifetime++ //this is so the decrease from mobs hit and the natural decrease don't cumulate.
|
||||
var/T = get_turf(src)
|
||||
if(lifetime % reagent_divisor)
|
||||
reagents.expose(T, VAPOR, fraction)
|
||||
|
||||
if(--amount < 0)
|
||||
return
|
||||
spread_foam()
|
||||
|
||||
/obj/effect/particle_effect/foam/proc/foam_mob(mob/living/L)
|
||||
if(lifetime<1)
|
||||
return FALSE
|
||||
if(!istype(L))
|
||||
return FALSE
|
||||
var/fraction = 1/initial(reagent_divisor)
|
||||
if(lifetime % reagent_divisor)
|
||||
reagents.expose(L, VAPOR, fraction)
|
||||
lifetime--
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/foam/proc/spread_foam()
|
||||
var/turf/t_loc = get_turf(src)
|
||||
//This should just be atmos adjacent turfs, come on guys
|
||||
for(var/turf/T in t_loc.reachableAdjacentTurfs())
|
||||
var/obj/effect/particle_effect/foam/foundfoam = locate() in T //Don't spread foam where there's already foam!
|
||||
if(foundfoam)
|
||||
continue
|
||||
|
||||
if(is_type_in_typecache(T, blacklisted_turfs))
|
||||
continue
|
||||
|
||||
for(var/mob/living/L in T)
|
||||
foam_mob(L)
|
||||
var/obj/effect/particle_effect/foam/F = new src.type(T)
|
||||
F.amount = amount
|
||||
reagents.copy_to(F, (reagents.total_volume))
|
||||
F.add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
||||
F.metal = metal
|
||||
|
||||
/obj/effect/particle_effect/foam/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
|
||||
return exposed_temperature > 475
|
||||
|
||||
/obj/effect/particle_effect/foam/atmos_expose(datum/gas_mixture/air, exposed_temperature)
|
||||
if(prob(max(0, exposed_temperature - 475))) //foam dissolves when heated
|
||||
kill_foam()
|
||||
|
||||
|
||||
///////////////////////////////////////////////
|
||||
//FOAM EFFECT DATUM
|
||||
/datum/effect_system/foam_spread
|
||||
/// the size of the foam spread.
|
||||
var/amount = 10
|
||||
/// Stupid hack alertm exists to hold chems for us
|
||||
var/atom/movable/chem_holder/chemholder
|
||||
effect_type = /obj/effect/particle_effect/foam
|
||||
var/metal = 0
|
||||
|
||||
|
||||
/datum/effect_system/foam_spread/metal
|
||||
effect_type = /obj/effect/particle_effect/foam/metal
|
||||
|
||||
|
||||
/datum/effect_system/foam_spread/metal/smart
|
||||
effect_type = /obj/effect/particle_effect/foam/smart
|
||||
|
||||
|
||||
/datum/effect_system/foam_spread/long
|
||||
effect_type = /obj/effect/particle_effect/foam/long_life
|
||||
|
||||
/datum/effect_system/foam_spread/New()
|
||||
..()
|
||||
chemholder = new()
|
||||
chemholder.create_reagents(1000, NO_REACT)
|
||||
|
||||
/datum/effect_system/foam_spread/Destroy()
|
||||
QDEL_NULL(chemholder)
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/foam_spread/set_up(amt=5, loca, datum/reagents/carry = null, metaltype = 0)
|
||||
if(isturf(loca))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
|
||||
amount = round(sqrt(amt / 2), 1)
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
if(metaltype)
|
||||
metal = metaltype
|
||||
|
||||
/datum/effect_system/foam_spread/start()
|
||||
var/obj/effect/particle_effect/foam/F = new effect_type(location)
|
||||
var/foamcolor = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
// To prevent insane reagent multiplication with 1u foam
|
||||
// I am capping amount of reagent foam recieves by limiting how low it can go
|
||||
// Any radius of foam less than 3 makes foam recieve same amount of reagents as foam of radius 3
|
||||
// Maximum multiplication of reagents is about 166% (3 times as low as before, it was about 500% with 1u foam)
|
||||
//
|
||||
// amount is radius of the foam
|
||||
// 10u foam has radius of 3
|
||||
// 5u foam has radius of 2
|
||||
// 1u foam has radius of 1
|
||||
var/effective_amount = chemholder.reagents.total_volume / max(amount, 3)
|
||||
chemholder.reagents.copy_to(F, effective_amount)
|
||||
F.add_atom_colour(foamcolor, FIXED_COLOUR_PRIORITY)
|
||||
F.amount = amount
|
||||
F.metal = metal
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// FOAM STRUCTURE. Formed by metal foams. Dense and opaque, but easy to break
|
||||
/obj/structure/foamedmetal
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "metalfoam"
|
||||
density = TRUE
|
||||
opacity = TRUE // changed in New()
|
||||
anchored = TRUE
|
||||
layer = EDGED_TURF_LAYER
|
||||
plane = GAME_PLANE_UPPER
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
name = "foamed metal"
|
||||
desc = "A lightweight foamed metal wall that can be used as base to construct a wall."
|
||||
gender = PLURAL
|
||||
max_integrity = 20
|
||||
can_atmos_pass = ATMOS_PASS_DENSITY
|
||||
obj_flags = CAN_BE_HIT | BLOCK_Z_IN_DOWN | BLOCK_Z_IN_UP
|
||||
///Var used to prevent spamming of the construction sound
|
||||
var/next_beep = 0
|
||||
|
||||
/obj/structure/foamedmetal/Initialize(mapload)
|
||||
. = ..()
|
||||
air_update_turf(TRUE, TRUE)
|
||||
|
||||
/obj/structure/foamedmetal/Destroy()
|
||||
air_update_turf(TRUE, FALSE)
|
||||
. = ..()
|
||||
|
||||
/obj/structure/foamedmetal/Move()
|
||||
var/turf/T = loc
|
||||
. = ..()
|
||||
move_update_air(T)
|
||||
|
||||
/obj/structure/foamedmetal/attack_paw(mob/user, list/modifiers)
|
||||
return attack_hand(user, modifiers)
|
||||
|
||||
/obj/structure/foamedmetal/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
playsound(src.loc, 'sound/weapons/tap.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/foamedmetal/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
to_chat(user, span_warning("You hit [src] but bounce off it!"))
|
||||
playsound(src.loc, 'sound/weapons/tap.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/foamedmetal/attackby(obj/item/W, mob/user, params)
|
||||
///A speed modifier for how fast the wall is build
|
||||
var/platingmodifier = 1
|
||||
if(HAS_TRAIT(user, TRAIT_QUICK_BUILD))
|
||||
platingmodifier = 0.7
|
||||
if(next_beep <= world.time)
|
||||
next_beep = world.time + 1 SECONDS
|
||||
playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE)
|
||||
add_fingerprint(user)
|
||||
|
||||
if(!istype(W, /obj/item/stack/sheet))
|
||||
return ..()
|
||||
|
||||
var/obj/item/stack/sheet/sheet_for_plating = W
|
||||
if(istype(sheet_for_plating, /obj/item/stack/sheet/iron))
|
||||
if(sheet_for_plating.get_amount() < 2)
|
||||
to_chat(user, span_warning("You need two sheets of iron to finish a wall on [src]!"))
|
||||
return
|
||||
to_chat(user, span_notice("You start adding plating to the foam structure..."))
|
||||
if (do_after(user, 40*platingmodifier, target = src))
|
||||
if(!sheet_for_plating.use(2))
|
||||
return
|
||||
to_chat(user, span_notice("You add the plating."))
|
||||
var/turf/T = get_turf(src)
|
||||
T.PlaceOnTop(/turf/closed/wall/metal_foam_base)
|
||||
transfer_fingerprints_to(T)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
add_hiddenprint(user)
|
||||
|
||||
/obj/structure/foamedmetal/iron
|
||||
max_integrity = 50
|
||||
icon_state = "ironfoam"
|
||||
|
||||
//Atmos Backpack Resin, transparent, prevents atmos and filters the air
|
||||
/obj/structure/foamedmetal/resin
|
||||
name = "\improper ATMOS Resin"
|
||||
desc = "A lightweight, transparent resin used to suffocate fires, scrub the air of toxins, and restore the air to a safe temperature. It can be used as base to construct a wall."
|
||||
opacity = FALSE
|
||||
icon_state = "atmos_resin"
|
||||
alpha = 120
|
||||
max_integrity = 10
|
||||
pass_flags_self = PASSGLASS
|
||||
|
||||
/obj/structure/foamedmetal/resin/Initialize(mapload)
|
||||
. = ..()
|
||||
if(isopenturf(loc))
|
||||
var/turf/open/O = loc
|
||||
O.ClearWet()
|
||||
if(O.air)
|
||||
var/datum/gas_mixture/G = O.air
|
||||
G.temperature = 293.15
|
||||
for(var/obj/effect/hotspot/H in O)
|
||||
qdel(H)
|
||||
var/list/G_gases = G.gases
|
||||
for(var/I in G_gases)
|
||||
if(I == /datum/gas/oxygen || I == /datum/gas/nitrogen)
|
||||
continue
|
||||
G_gases[I][MOLES] = 0
|
||||
G.garbage_collect()
|
||||
for(var/obj/machinery/atmospherics/components/unary/U in O)
|
||||
if(!U.welded)
|
||||
U.welded = TRUE
|
||||
U.update_appearance()
|
||||
U.visible_message(span_danger("[U] sealed shut!"))
|
||||
for(var/mob/living/L in O)
|
||||
L.extinguish_mob()
|
||||
for(var/obj/item/Item in O)
|
||||
Item.extinguish()
|
||||
|
||||
#undef ALUMINUM_FOAM
|
||||
#undef IRON_FOAM
|
||||
#undef RESIN_FOAM
|
||||
@@ -1,381 +0,0 @@
|
||||
/////////////////////////////////////////////
|
||||
//// SMOKE SYSTEMS
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke
|
||||
name = "smoke"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "smoke"
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
opacity = FALSE
|
||||
plane = ABOVE_GAME_PLANE
|
||||
layer = FLY_LAYER
|
||||
anchored = TRUE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
animate_movement = FALSE
|
||||
var/amount = 4
|
||||
var/lifetime = 5
|
||||
var/opaque = 1 //whether the smoke can block the view when in enough amount
|
||||
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/fade_out(frames = 16)
|
||||
if(alpha == 0) //Handle already transparent case
|
||||
return
|
||||
if(frames == 0)
|
||||
frames = 1 //We will just assume that by 0 frames, the coder meant "during one frame".
|
||||
var/step = alpha / frames
|
||||
for(var/i in 1 to frames)
|
||||
alpha -= step
|
||||
if(alpha < 160)
|
||||
set_opacity(0) //if we were blocking view, we aren't now because we're fading out
|
||||
stoplag()
|
||||
|
||||
/obj/effect/particle_effect/smoke/Initialize(mapload)
|
||||
. = ..()
|
||||
create_reagents(1000)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
|
||||
/obj/effect/particle_effect/smoke/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/kill_smoke()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
INVOKE_ASYNC(src, .proc/fade_out)
|
||||
QDEL_IN(src, 10)
|
||||
|
||||
/obj/effect/particle_effect/smoke/process()
|
||||
lifetime--
|
||||
if(lifetime < 1)
|
||||
kill_smoke()
|
||||
return FALSE
|
||||
for(var/mob/living/L in range(0,src))
|
||||
smoke_mob(L)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/smoke_mob(mob/living/carbon/C)
|
||||
if(!istype(C))
|
||||
return FALSE
|
||||
if(lifetime<1)
|
||||
return FALSE
|
||||
if(C.internal != null || C.has_smoke_protection())
|
||||
return FALSE
|
||||
if(C.smoke_delay)
|
||||
return FALSE
|
||||
C.smoke_delay++
|
||||
addtimer(CALLBACK(src, .proc/remove_smoke_delay, C), 10)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/remove_smoke_delay(mob/living/carbon/C)
|
||||
if(C)
|
||||
C.smoke_delay = 0
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/spread_smoke()
|
||||
var/turf/t_loc = get_turf(src)
|
||||
if(!t_loc)
|
||||
return
|
||||
var/list/newsmokes = list()
|
||||
for(var/turf/T in t_loc.get_atmos_adjacent_turfs())
|
||||
var/obj/effect/particle_effect/smoke/foundsmoke = locate() in T //Don't spread smoke where there's already smoke!
|
||||
if(foundsmoke)
|
||||
continue
|
||||
for(var/mob/living/L in T)
|
||||
smoke_mob(L)
|
||||
var/obj/effect/particle_effect/smoke/S = new type(T)
|
||||
reagents.copy_to(S, reagents.total_volume)
|
||||
S.setDir(pick(GLOB.cardinals))
|
||||
S.amount = amount-1
|
||||
S.add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
||||
S.lifetime = lifetime
|
||||
if(S.amount>0)
|
||||
if(opaque)
|
||||
S.set_opacity(TRUE)
|
||||
newsmokes.Add(S)
|
||||
|
||||
//the smoke spreads rapidly but not instantly
|
||||
for(var/obj/effect/particle_effect/smoke/SM in newsmokes)
|
||||
addtimer(CALLBACK(SM, /obj/effect/particle_effect/smoke.proc/spread_smoke), 1)
|
||||
|
||||
|
||||
/datum/effect_system/smoke_spread
|
||||
var/amount = 10
|
||||
effect_type = /obj/effect/particle_effect/smoke
|
||||
|
||||
/datum/effect_system/smoke_spread/set_up(radius = 5, loca)
|
||||
if(isturf(loca))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
amount = radius
|
||||
|
||||
/datum/effect_system/smoke_spread/start()
|
||||
if(holder)
|
||||
location = get_turf(holder)
|
||||
var/obj/effect/particle_effect/smoke/S = new effect_type(location)
|
||||
S.amount = amount
|
||||
if(S.amount)
|
||||
S.spread_smoke()
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Bad smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke/bad
|
||||
lifetime = 8
|
||||
|
||||
/obj/effect/particle_effect/smoke/bad/Initialize(mapload)
|
||||
. = ..()
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = .proc/on_entered,
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
/obj/effect/particle_effect/smoke/bad/smoke_mob(mob/living/carbon/M)
|
||||
. = ..()
|
||||
if(.)
|
||||
M.drop_all_held_items()
|
||||
M.adjustOxyLoss(1)
|
||||
M.emote("cough")
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/smoke/bad/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
SIGNAL_HANDLER
|
||||
if(istype(arrived, /obj/projectile/beam))
|
||||
var/obj/projectile/beam/beam = arrived
|
||||
beam.damage *= 0.5
|
||||
|
||||
/datum/effect_system/smoke_spread/bad
|
||||
effect_type = /obj/effect/particle_effect/smoke/bad
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Nanofrost smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke/freezing
|
||||
name = "nanofrost smoke"
|
||||
color = "#B2FFFF"
|
||||
opaque = FALSE
|
||||
|
||||
/datum/effect_system/smoke_spread/freezing
|
||||
effect_type = /obj/effect/particle_effect/smoke/freezing
|
||||
var/blast = 0
|
||||
var/temperature = 2
|
||||
var/weldvents = TRUE
|
||||
var/distcheck = TRUE
|
||||
|
||||
/datum/effect_system/smoke_spread/freezing/proc/Chilled(atom/A)
|
||||
if(isopenturf(A))
|
||||
var/turf/open/T = A
|
||||
if(T.air)
|
||||
var/datum/gas_mixture/G = T.air
|
||||
if(!distcheck || get_dist(T, location) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air
|
||||
G.temperature = temperature
|
||||
T.air_update_turf(FALSE, FALSE)
|
||||
for(var/obj/effect/hotspot/H in T)
|
||||
qdel(H)
|
||||
var/list/G_gases = G.gases
|
||||
if(G_gases[/datum/gas/plasma])
|
||||
G.assert_gas(/datum/gas/nitrogen)
|
||||
G_gases[/datum/gas/nitrogen][MOLES] += (G_gases[/datum/gas/plasma][MOLES])
|
||||
G_gases[/datum/gas/plasma][MOLES] = 0
|
||||
G.garbage_collect()
|
||||
if (weldvents)
|
||||
for(var/obj/machinery/atmospherics/components/unary/U in T)
|
||||
if(!isnull(U.welded) && !U.welded) //must be an unwelded vent pump or vent scrubber.
|
||||
U.welded = TRUE
|
||||
U.update_appearance()
|
||||
U.visible_message(span_danger("[U] is frozen shut!"))
|
||||
for(var/mob/living/L in T)
|
||||
L.extinguish_mob()
|
||||
for(var/obj/item/Item in T)
|
||||
Item.extinguish()
|
||||
|
||||
/datum/effect_system/smoke_spread/freezing/set_up(radius = 5, loca, blast_radius = 0)
|
||||
..()
|
||||
blast = blast_radius
|
||||
|
||||
/datum/effect_system/smoke_spread/freezing/start()
|
||||
if(blast)
|
||||
for(var/turf/T in RANGE_TURFS(blast, location))
|
||||
Chilled(T)
|
||||
..()
|
||||
|
||||
/datum/effect_system/smoke_spread/freezing/decon
|
||||
temperature = 293.15
|
||||
distcheck = FALSE
|
||||
weldvents = FALSE
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Sleep smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke/sleeping
|
||||
color = "#9C3636"
|
||||
lifetime = 10
|
||||
|
||||
/obj/effect/particle_effect/smoke/sleeping/smoke_mob(mob/living/carbon/M)
|
||||
if(..())
|
||||
M.Sleeping(200)
|
||||
M.emote("cough")
|
||||
return 1
|
||||
|
||||
/datum/effect_system/smoke_spread/sleeping
|
||||
effect_type = /obj/effect/particle_effect/smoke/sleeping
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke/chem
|
||||
lifetime = 10
|
||||
|
||||
|
||||
/obj/effect/particle_effect/smoke/chem/process()
|
||||
. = ..()
|
||||
if(.)
|
||||
var/turf/T = get_turf(src)
|
||||
var/fraction = 1/initial(lifetime)
|
||||
for(var/atom/movable/AM in T)
|
||||
if(AM.type == src.type)
|
||||
continue
|
||||
if(T.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(AM, TRAIT_T_RAY_VISIBLE))
|
||||
continue
|
||||
reagents.expose(AM, TOUCH, fraction)
|
||||
|
||||
reagents.expose(T, TOUCH, fraction)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/smoke/chem/smoke_mob(mob/living/carbon/M)
|
||||
if(lifetime<1)
|
||||
return FALSE
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
var/mob/living/carbon/C = M
|
||||
if(C.internal != null || C.has_smoke_protection())
|
||||
return FALSE
|
||||
var/fraction = 1/initial(lifetime)
|
||||
reagents.copy_to(C, fraction*reagents.total_volume)
|
||||
reagents.expose(M, INGEST, fraction)
|
||||
return TRUE
|
||||
|
||||
|
||||
|
||||
/datum/effect_system/smoke_spread/chem
|
||||
/// Evil evil hack so we have something to "hold" our reagents
|
||||
var/atom/movable/chem_holder/chemholder
|
||||
effect_type = /obj/effect/particle_effect/smoke/chem
|
||||
|
||||
/datum/effect_system/smoke_spread/chem/New()
|
||||
..()
|
||||
chemholder = new()
|
||||
chemholder.create_reagents(1000, NO_REACT)
|
||||
|
||||
/datum/effect_system/smoke_spread/chem/Destroy()
|
||||
QDEL_NULL(chemholder)
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/smoke_spread/chem/set_up(datum/reagents/carry = null, radius = 1, loca, silent = FALSE)
|
||||
if(isturf(loca))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
amount = radius
|
||||
carry.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
if(!silent)
|
||||
var/contained = ""
|
||||
for(var/reagent in carry.reagent_list)
|
||||
contained += " [reagent] "
|
||||
if(contained)
|
||||
contained = "\[[contained]\]"
|
||||
|
||||
var/where = "[AREACOORD(location)]"
|
||||
if(carry.my_atom?.fingerprintslast) //Some reagents don't have a my_atom in some cases
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "[ADMIN_LOOKUPFLW(M)] "
|
||||
if(!istype(carry.my_atom, /obj/machinery/plumbing))
|
||||
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].")
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
if(!istype(carry.my_atom, /obj/machinery/plumbing))
|
||||
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.")
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
|
||||
/datum/effect_system/smoke_spread/chem/start()
|
||||
var/mixcolor = mix_color_from_reagents(chemholder.reagents.reagent_list)
|
||||
if(holder)
|
||||
location = get_turf(holder)
|
||||
var/obj/effect/particle_effect/smoke/chem/S = new effect_type(location)
|
||||
|
||||
if(chemholder.reagents.total_volume > 1) // can't split 1 very well
|
||||
chemholder.reagents.copy_to(S, chemholder.reagents.total_volume)
|
||||
|
||||
if(mixcolor)
|
||||
S.add_atom_colour(mixcolor, FIXED_COLOUR_PRIORITY) // give the smoke color, if it has any to begin with
|
||||
S.amount = amount
|
||||
if(S.amount)
|
||||
S.spread_smoke() //calling process right now so the smoke immediately attacks mobs.
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Transparent smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
//Same as the base type, but the smoke produced is not opaque
|
||||
/datum/effect_system/smoke_spread/transparent
|
||||
effect_type = /obj/effect/particle_effect/smoke/transparent
|
||||
|
||||
/obj/effect/particle_effect/smoke/transparent
|
||||
opaque = FALSE
|
||||
|
||||
/proc/do_smoke(range=0, location=null, smoke_type=/obj/effect/particle_effect/smoke)
|
||||
var/datum/effect_system/smoke_spread/smoke = new
|
||||
smoke.effect_type = smoke_type
|
||||
smoke.set_up(range, location)
|
||||
smoke.start()
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Bad Smoke (But Green (and Black))
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke/bad/green
|
||||
name = "green smoke"
|
||||
color = "#00FF00"
|
||||
opaque = FALSE
|
||||
|
||||
/datum/effect_system/smoke_spread/bad/green
|
||||
effect_type = /obj/effect/particle_effect/smoke/bad/green
|
||||
|
||||
/obj/effect/particle_effect/smoke/bad/black
|
||||
name = "black smoke"
|
||||
color = "#383838"
|
||||
opaque = FALSE
|
||||
|
||||
/datum/effect_system/smoke_spread/bad/black
|
||||
effect_type = /obj/effect/particle_effect/smoke/bad/black
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Quick smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/obj/effect/particle_effect/smoke/quick
|
||||
lifetime = 1
|
||||
opaque = FALSE
|
||||
|
||||
/datum/effect_system/smoke_spread/quick
|
||||
effect_type = /obj/effect/particle_effect/smoke/quick
|
||||
|
||||
/obj/effect/particle_effect/smoke/chem/quick
|
||||
lifetime = 2 //under lifetime 1, this kills itself the first time it processes, not working. i hate smoke code
|
||||
opaque = FALSE
|
||||
alpha = 100
|
||||
|
||||
/datum/effect_system/smoke_spread/chem/quick
|
||||
effect_type = /obj/effect/particle_effect/smoke/chem/quick
|
||||
@@ -0,0 +1,126 @@
|
||||
/////////////////////////////////////////////
|
||||
//// SMOKE SYSTEMS
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* A group of fluid objects.
|
||||
*/
|
||||
/datum/fluid_group
|
||||
/// The set of fluid objects currently in this group.
|
||||
var/list/nodes
|
||||
/// The number of fluid object that this group wants to have contained.
|
||||
var/target_size
|
||||
/// The total number of fluid objects that have ever been in this group.
|
||||
var/total_size = 0
|
||||
|
||||
/datum/fluid_group/New(target_size = 0)
|
||||
. = ..()
|
||||
src.nodes = list()
|
||||
src.target_size = target_size
|
||||
|
||||
/datum/fluid_group/Destroy(force)
|
||||
QDEL_LAZYLIST(nodes)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Adds a fluid node to this fluid group.
|
||||
*
|
||||
* Is a noop if the node is already in the group.
|
||||
* Removes the node from any other fluid groups it is in.
|
||||
* Syncs the group of the node with the group it is being added to (this one).
|
||||
* Increments the total size of the fluid group.
|
||||
*
|
||||
* Arguments:
|
||||
* - [node][/obj/effect/particle_effect/fluid]: The fluid node that is going to be added to this group.
|
||||
*
|
||||
* Returns:
|
||||
* - [TRUE]: If the node to be added is in this group by the end of the proc.
|
||||
* - [FALSE]: Otherwise.
|
||||
*/
|
||||
/datum/fluid_group/proc/add_node(obj/effect/particle_effect/fluid/node)
|
||||
if(!istype(node))
|
||||
CRASH("Attempted to add non-fluid node [isnull(node) ? "NULL" : node] to a fluid group.")
|
||||
if(QDELING(node))
|
||||
CRASH("Attempted to add qdeling node to a fluid group")
|
||||
|
||||
if(node.group)
|
||||
if(node.group == src)
|
||||
return TRUE
|
||||
if(!node.group.remove_node(node))
|
||||
return FALSE
|
||||
|
||||
nodes += node
|
||||
node.group = src
|
||||
total_size++
|
||||
return TRUE
|
||||
|
||||
|
||||
/**
|
||||
* Removes a fluid node from this fluid group.
|
||||
*
|
||||
* Is a noop if the node is not in this group.
|
||||
* Nulls the nodes fluid group ref to sync it with its new state.
|
||||
* DOES NOT decrement the total size of the fluid group.
|
||||
*
|
||||
* Arguments:
|
||||
* - [node][/obj/effect/particle_effect/fluid]: The fluid node that is going to be removed from this group.
|
||||
*
|
||||
* Returns:
|
||||
* - [TRUE]: If the node to be removed is not in the group by the end of the proc.
|
||||
*/
|
||||
/datum/fluid_group/proc/remove_node(obj/effect/particle_effect/fluid/node)
|
||||
if(node.group != src)
|
||||
return TRUE
|
||||
|
||||
nodes -= node
|
||||
node.group = null
|
||||
return TRUE // Note: does not decrement total size since we don't want the group to expand again when it begins to dissipate or it will never stop.
|
||||
|
||||
|
||||
/**
|
||||
* A particle effect that belongs to a fluid group.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid
|
||||
name = "fluid"
|
||||
/// The fluid group that this particle effect belongs to.
|
||||
var/datum/fluid_group/group
|
||||
/// What SSfluid bucket this particle effect is currently in.
|
||||
var/tmp/effect_bucket
|
||||
/// The index of the fluid spread bucket this is being spread in.
|
||||
var/tmp/spread_bucket
|
||||
|
||||
/obj/effect/particle_effect/fluid/Initialize(mapload, datum/fluid_group/group, obj/effect/particle_effect/fluid/source)
|
||||
. = ..()
|
||||
if(!group)
|
||||
group = source?.group || new
|
||||
group.add_node(src)
|
||||
|
||||
/obj/effect/particle_effect/fluid/Destroy()
|
||||
group.remove_node(src)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Attempts to spread this fluid node to wherever it can spread.
|
||||
*
|
||||
* Exact results vary by subtype implementation.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/proc/spread()
|
||||
CRASH("The base fluid spread proc is not implemented and should not be called. You called it.")
|
||||
|
||||
|
||||
/**
|
||||
* A factory which produces fluid groups.
|
||||
*/
|
||||
/datum/effect_system/fluid_spread
|
||||
effect_type = /obj/effect/particle_effect/fluid
|
||||
/// The amount of smoke to produce.
|
||||
var/amount = 10
|
||||
|
||||
/datum/effect_system/fluid_spread/set_up(range = 1, amount = DIAMOND_AREA(range), atom/location, ...)
|
||||
src.location = get_turf(location)
|
||||
src.amount = amount
|
||||
|
||||
/datum/effect_system/fluid_spread/start()
|
||||
var/location = holder ? get_turf(holder) : src.location
|
||||
var/obj/effect/particle_effect/fluid/flood = new effect_type(location, new /datum/fluid_group(amount))
|
||||
flood.spread()
|
||||
@@ -0,0 +1,429 @@
|
||||
/// The minimum foam range required to start diluting the reagents past the minimum dilution rate.
|
||||
#define MINIMUM_FOAM_DILUTION_RANGE 3
|
||||
/// The minumum foam-area based divisor used to decrease foam exposure volume.
|
||||
#define MINIMUM_FOAM_DILUTION DIAMOND_AREA(MINIMUM_FOAM_DILUTION_RANGE)
|
||||
/// The effective scaling of the reagents in the foam. (Total delivered at or below [MINIMUM_FOAM_DILUTION])
|
||||
#define FOAM_REAGENT_SCALE 3.2
|
||||
|
||||
/**
|
||||
* ## Foam
|
||||
*
|
||||
* Similar to smoke, but slower and mobs absorb its reagent through their exposed skin.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/foam
|
||||
name = "foam"
|
||||
icon_state = "foam"
|
||||
opacity = FALSE
|
||||
anchored = TRUE
|
||||
density = FALSE
|
||||
layer = EDGED_TURF_LAYER
|
||||
plane = GAME_PLANE_UPPER
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
animate_movement = NO_STEPS
|
||||
/// The types of turfs that this foam cannot spread to.
|
||||
var/static/list/blacklisted_turfs = typecacheof(list(
|
||||
/turf/open/space/transit,
|
||||
/turf/open/chasm,
|
||||
/turf/open/lava,
|
||||
))
|
||||
/// The typepath for what this foam leaves behind when it dissipates.
|
||||
var/atom/movable/result_type = null
|
||||
/// Whether or not this foam can produce a remnant movable if something of the same type is already on its turf.
|
||||
var/allow_duplicate_results = TRUE
|
||||
/// The amount of time this foam stick around for before it dissipates.
|
||||
var/lifetime = 8 SECONDS
|
||||
/// Whether or not this foam should be slippery.
|
||||
var/slippery_foam = TRUE
|
||||
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/Initialize(mapload)
|
||||
. = ..()
|
||||
create_reagents(1000)
|
||||
playsound(src, 'sound/effects/bubbles2.ogg', 80, TRUE, -3)
|
||||
AddElement(/datum/element/atmos_sensitive, mapload)
|
||||
SSfoam.start_processing(src)
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/ComponentInitialize()
|
||||
. = ..()
|
||||
if(slippery_foam)
|
||||
AddComponent(/datum/component/slippery, 100)
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/Destroy()
|
||||
SSfoam.stop_processing(src)
|
||||
if (spread_bucket)
|
||||
SSfoam.cancel_spread(src)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Makes the foam dissipate and create whatever remnants it must.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/foam/proc/kill_foam()
|
||||
SSfoam.stop_processing(src)
|
||||
if (spread_bucket)
|
||||
SSfoam.cancel_spread(src)
|
||||
make_result()
|
||||
flick("[icon_state]-disolve", src)
|
||||
QDEL_IN(src, 0.5 SECONDS)
|
||||
|
||||
/**
|
||||
* Makes the foam leave behind something when it dissipates.
|
||||
*
|
||||
* Returns the thing the foam leaves behind for further modification by subtypes.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/foam/proc/make_result()
|
||||
if(isnull(result_type))
|
||||
return null
|
||||
|
||||
var/atom/location = loc
|
||||
return (!allow_duplicate_results && (locate(result_type) in location)) || (new result_type(location))
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/process(delta_time)
|
||||
var/ds_delta_time = delta_time SECONDS
|
||||
lifetime -= ds_delta_time
|
||||
if(lifetime <= 0)
|
||||
kill_foam()
|
||||
return
|
||||
|
||||
var/fraction = (ds_delta_time * MINIMUM_FOAM_DILUTION) / (initial(lifetime) * max(MINIMUM_FOAM_DILUTION, group.total_size))
|
||||
var/turf/location = loc
|
||||
for(var/obj/object in location)
|
||||
if(object == src)
|
||||
continue
|
||||
if(location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(object, TRAIT_T_RAY_VISIBLE))
|
||||
continue
|
||||
reagents.expose(object, VAPOR, fraction)
|
||||
|
||||
var/hit = 0
|
||||
for(var/mob/living/foamer in location)
|
||||
hit += foam_mob(foamer, delta_time)
|
||||
if(hit)
|
||||
lifetime += ds_delta_time //this is so the decrease from mobs hit and the natural decrease don't cumulate.
|
||||
|
||||
reagents.expose(location, VAPOR, fraction)
|
||||
|
||||
/**
|
||||
* Applies the effect of this foam to a mob.
|
||||
*
|
||||
* Arguments:
|
||||
* - [foaming][/mob/living]: The mob that this foam is acting on.
|
||||
* - delta_time: The amount of time that this foam is acting on them over.
|
||||
*
|
||||
* Returns:
|
||||
* - [TRUE]: If the foam was successfully applied to the mob. Used to scale how quickly foam dissipates according to the number of mobs it is applied to.
|
||||
* - [FALSE]: Otherwise.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/foam/proc/foam_mob(mob/living/foaming, delta_time)
|
||||
if(lifetime <= 0)
|
||||
return FALSE
|
||||
if(!istype(foaming))
|
||||
return FALSE
|
||||
|
||||
delta_time = min(delta_time SECONDS, lifetime)
|
||||
var/fraction = (delta_time * MINIMUM_FOAM_DILUTION) / (initial(lifetime) * max(MINIMUM_FOAM_DILUTION, group.total_size))
|
||||
reagents.expose(foaming, VAPOR, fraction)
|
||||
lifetime -= delta_time
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/spread(delta_time = 0.2 SECONDS)
|
||||
if(group.total_size > group.target_size)
|
||||
return
|
||||
var/turf/location = get_turf(src)
|
||||
if(!istype(location))
|
||||
return FALSE
|
||||
|
||||
for(var/turf/spread_turf as anything in location.reachableAdjacentTurfs())
|
||||
var/obj/effect/particle_effect/fluid/foam/foundfoam = locate() in spread_turf //Don't spread foam where there's already foam!
|
||||
if(foundfoam)
|
||||
continue
|
||||
if(is_type_in_typecache(spread_turf, blacklisted_turfs))
|
||||
continue
|
||||
|
||||
for(var/mob/living/foaming in spread_turf)
|
||||
foam_mob(foaming, delta_time)
|
||||
|
||||
var/obj/effect/particle_effect/fluid/foam/spread_foam = new type(spread_turf, group, src)
|
||||
reagents.copy_to(spread_foam, (reagents.total_volume))
|
||||
spread_foam.add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
||||
spread_foam.result_type = result_type
|
||||
SSfoam.queue_spread(spread_foam)
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
|
||||
return exposed_temperature > 475
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/atmos_expose(datum/gas_mixture/air, exposed_temperature)
|
||||
if(prob(max(0, exposed_temperature - 475))) //foam dissolves when heated
|
||||
kill_foam()
|
||||
|
||||
/// A factory for foam fluid floods.
|
||||
/datum/effect_system/fluid_spread/foam
|
||||
effect_type = /obj/effect/particle_effect/fluid/foam
|
||||
/// A container for all of the chemicals we distribute through the foam.
|
||||
var/datum/reagents/chemholder
|
||||
/// The amount that
|
||||
var/reagent_scale = FOAM_REAGENT_SCALE
|
||||
/// What type of thing the foam should leave behind when it dissipates.
|
||||
var/atom/movable/result_type = null
|
||||
|
||||
|
||||
/datum/effect_system/fluid_spread/foam/New()
|
||||
..()
|
||||
chemholder = new(1000, NO_REACT)
|
||||
|
||||
/datum/effect_system/fluid_spread/foam/Destroy()
|
||||
QDEL_NULL(chemholder)
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/fluid_spread/foam/set_up(range = 1, amount = DIAMOND_AREA(range), atom/location = null, datum/reagents/carry = null, result_type = null)
|
||||
. = ..()
|
||||
carry?.copy_to(chemholder, carry.total_volume)
|
||||
if(!isnull(result_type))
|
||||
src.result_type = result_type
|
||||
|
||||
/datum/effect_system/fluid_spread/foam/start()
|
||||
var/obj/effect/particle_effect/fluid/foam/foam = new effect_type(location, new /datum/fluid_group(amount))
|
||||
var/foamcolor = mix_color_from_reagents(chemholder.reagent_list)
|
||||
if(reagent_scale > 1) // Make room in case we were created by a particularly stuffed payload.
|
||||
foam.reagents.maximum_volume *= reagent_scale
|
||||
chemholder.copy_to(foam, chemholder.total_volume, reagent_scale) // Foam has an amplifying effect on the reagents it is supplied with. This is balanced by the reagents being diluted as the area the foam covers increases.
|
||||
foam.add_atom_colour(foamcolor, FIXED_COLOUR_PRIORITY)
|
||||
if(!isnull(result_type))
|
||||
foam.result_type = result_type
|
||||
SSfoam.queue_spread(foam)
|
||||
|
||||
|
||||
// Long lasting foam
|
||||
/// A foam variant which lasts for an extended amount of time.
|
||||
/obj/effect/particle_effect/fluid/foam/long_life
|
||||
lifetime = 30 SECONDS
|
||||
|
||||
/// A factory which produces foam with an extended lifespan.
|
||||
/datum/effect_system/fluid_spread/foam/long
|
||||
effect_type = /obj/effect/particle_effect/fluid/foam/long_life
|
||||
reagent_scale = FOAM_REAGENT_SCALE * (30 / 8)
|
||||
|
||||
|
||||
// Firefighting foam
|
||||
/// A variant of foam which absorbs plasma in the air if there is a fire.
|
||||
/obj/effect/particle_effect/fluid/foam/firefighting
|
||||
name = "firefighting foam"
|
||||
lifetime = 20 //doesn't last as long as normal foam
|
||||
result_type = /obj/effect/decal/cleanable/plasma
|
||||
allow_duplicate_results = FALSE
|
||||
slippery_foam = FALSE
|
||||
/// The amount of plasma gas this foam has absorbed. To be deposited when the foam dissipates.
|
||||
var/absorbed_plasma = 0
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/firefighting/Initialize(mapload)
|
||||
. = ..()
|
||||
RemoveElement(/datum/element/atmos_sensitive)
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/firefighting/process()
|
||||
..()
|
||||
|
||||
var/turf/open/location = loc
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
var/obj/effect/hotspot/hotspot = locate() in location
|
||||
if(!(hotspot && location.air))
|
||||
return
|
||||
|
||||
QDEL_NULL(hotspot)
|
||||
var/datum/gas_mixture/air = location.air
|
||||
var/list/gases = air.gases
|
||||
if (gases[/datum/gas/plasma])
|
||||
var/scrub_amt = min(30, gases[/datum/gas/plasma][MOLES]) //Absorb some plasma
|
||||
gases[/datum/gas/plasma][MOLES] -= scrub_amt
|
||||
absorbed_plasma += scrub_amt
|
||||
if (air.temperature > T20C)
|
||||
air.temperature = max(air.temperature / 2, T20C)
|
||||
air.garbage_collect()
|
||||
location.air_update_turf(FALSE, FALSE)
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/firefighting/make_result()
|
||||
var/atom/movable/deposit = ..()
|
||||
if(istype(deposit) && deposit.reagents && absorbed_plasma > 0)
|
||||
deposit.reagents.add_reagent(/datum/reagent/stable_plasma, absorbed_plasma)
|
||||
absorbed_plasma = 0
|
||||
return deposit
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/firefighting/foam_mob(mob/living/foaming, delta_time)
|
||||
if(!istype(foaming))
|
||||
return
|
||||
foaming.adjust_wet_stacks(2)
|
||||
|
||||
|
||||
// Metal foam
|
||||
|
||||
/// A foam variant which
|
||||
/obj/effect/particle_effect/fluid/foam/metal
|
||||
name = "aluminium foam"
|
||||
result_type = /obj/structure/foamedmetal
|
||||
icon_state = "mfoam"
|
||||
slippery_foam = FALSE
|
||||
|
||||
/// A factory which produces aluminium metal foam.
|
||||
/datum/effect_system/fluid_spread/foam/metal
|
||||
effect_type = /obj/effect/particle_effect/fluid/foam/metal
|
||||
|
||||
/// FOAM STRUCTURE. Formed by metal foams. Dense and opaque, but easy to break
|
||||
/obj/structure/foamedmetal
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "metalfoam"
|
||||
density = TRUE
|
||||
opacity = TRUE // changed in New()
|
||||
anchored = TRUE
|
||||
layer = EDGED_TURF_LAYER
|
||||
plane = GAME_PLANE_UPPER
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
name = "foamed metal"
|
||||
desc = "A lightweight foamed metal wall that can be used as base to construct a wall."
|
||||
gender = PLURAL
|
||||
max_integrity = 20
|
||||
can_atmos_pass = ATMOS_PASS_DENSITY
|
||||
obj_flags = CAN_BE_HIT | BLOCK_Z_IN_DOWN | BLOCK_Z_IN_UP
|
||||
///Var used to prevent spamming of the construction sound
|
||||
var/next_beep = 0
|
||||
|
||||
/obj/structure/foamedmetal/Initialize(mapload)
|
||||
. = ..()
|
||||
air_update_turf(TRUE, TRUE)
|
||||
|
||||
/obj/structure/foamedmetal/Destroy()
|
||||
air_update_turf(TRUE, FALSE)
|
||||
. = ..()
|
||||
|
||||
/obj/structure/foamedmetal/Move()
|
||||
var/turf/T = loc
|
||||
. = ..()
|
||||
move_update_air(T)
|
||||
|
||||
/obj/structure/foamedmetal/attack_paw(mob/user, list/modifiers)
|
||||
return attack_hand(user, modifiers)
|
||||
|
||||
/obj/structure/foamedmetal/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
playsound(src.loc, 'sound/weapons/tap.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/foamedmetal/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
to_chat(user, span_warning("You hit [src] but bounce off it!"))
|
||||
playsound(src.loc, 'sound/weapons/tap.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/foamedmetal/attackby(obj/item/W, mob/user, params)
|
||||
///A speed modifier for how fast the wall is build
|
||||
var/platingmodifier = 1
|
||||
if(HAS_TRAIT(user, TRAIT_QUICK_BUILD))
|
||||
platingmodifier = 0.7
|
||||
if(next_beep <= world.time)
|
||||
next_beep = world.time + 1 SECONDS
|
||||
playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE)
|
||||
add_fingerprint(user)
|
||||
|
||||
if(!istype(W, /obj/item/stack/sheet))
|
||||
return ..()
|
||||
|
||||
var/obj/item/stack/sheet/sheet_for_plating = W
|
||||
if(istype(sheet_for_plating, /obj/item/stack/sheet/iron))
|
||||
if(sheet_for_plating.get_amount() < 2)
|
||||
to_chat(user, span_warning("You need two sheets of iron to finish a wall on [src]!"))
|
||||
return
|
||||
to_chat(user, span_notice("You start adding plating to the foam structure..."))
|
||||
if (do_after(user, 40 * platingmodifier, target = src))
|
||||
if(!sheet_for_plating.use(2))
|
||||
return
|
||||
to_chat(user, span_notice("You add the plating."))
|
||||
var/turf/T = get_turf(src)
|
||||
T.PlaceOnTop(/turf/closed/wall/metal_foam_base)
|
||||
transfer_fingerprints_to(T)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
add_hiddenprint(user)
|
||||
|
||||
/// A metal foam variant which produces slightly sturdier walls.
|
||||
/obj/effect/particle_effect/fluid/foam/metal/iron
|
||||
name = "iron foam"
|
||||
result_type = /obj/structure/foamedmetal/iron
|
||||
|
||||
/// A factory which produces iron metal foam.
|
||||
/datum/effect_system/fluid_spread/foam/metal/iron
|
||||
effect_type = /obj/effect/particle_effect/fluid/foam/metal/iron
|
||||
|
||||
/// A variant of metal foam walls with higher durability.
|
||||
/obj/structure/foamedmetal/iron
|
||||
max_integrity = 50
|
||||
icon_state = "ironfoam"
|
||||
|
||||
/// A variant of metal foam which only produces walls at area boundaries.
|
||||
/obj/effect/particle_effect/fluid/foam/metal/smart
|
||||
name = "smart foam"
|
||||
|
||||
/// A factory which produces smart aluminium metal foam.
|
||||
/datum/effect_system/fluid_spread/foam/metal/smart
|
||||
effect_type = /obj/effect/particle_effect/fluid/foam/metal/smart
|
||||
|
||||
/obj/effect/particle_effect/fluid/foam/metal/smart/make_result() //Smart foam adheres to area borders for walls
|
||||
var/turf/open/location = loc
|
||||
if(isspaceturf(location))
|
||||
location.PlaceOnTop(/turf/open/floor/plating/foam)
|
||||
|
||||
for(var/cardinal in GLOB.cardinals)
|
||||
var/turf/cardinal_turf = get_step(location, cardinal)
|
||||
if(get_area(cardinal_turf) != get_area(location))
|
||||
return ..()
|
||||
return null
|
||||
|
||||
/datum/effect_system/fluid_spread/foam/metal/resin
|
||||
effect_type = /obj/effect/particle_effect/fluid/foam/metal/resin
|
||||
|
||||
/// A foam variant which produces atmos resin walls.
|
||||
/obj/effect/particle_effect/fluid/foam/metal/resin
|
||||
name = "resin foam"
|
||||
result_type = /obj/structure/foamedmetal/resin
|
||||
|
||||
/// Atmos Backpack Resin, transparent, prevents atmos and filters the air
|
||||
/obj/structure/foamedmetal/resin
|
||||
name = "\improper ATMOS Resin"
|
||||
desc = "A lightweight, transparent resin used to suffocate fires, scrub the air of toxins, and restore the air to a safe temperature. It can be used as base to construct a wall."
|
||||
opacity = FALSE
|
||||
icon_state = "atmos_resin"
|
||||
alpha = 120
|
||||
max_integrity = 10
|
||||
pass_flags_self = PASSGLASS
|
||||
|
||||
/obj/structure/foamedmetal/resin/Initialize(mapload)
|
||||
. = ..()
|
||||
var/turf/open/location = loc
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
location.ClearWet()
|
||||
if(location.air)
|
||||
var/datum/gas_mixture/air = location.air
|
||||
air.temperature = 293.15
|
||||
for(var/obj/effect/hotspot/fire in location)
|
||||
qdel(fire)
|
||||
|
||||
var/list/gases = air.gases
|
||||
for(var/gas_type in gases)
|
||||
switch(gas_type)
|
||||
if(/datum/gas/oxygen, /datum/gas/nitrogen)
|
||||
continue
|
||||
else
|
||||
gases[gas_type][MOLES] = 0
|
||||
air.garbage_collect()
|
||||
|
||||
for(var/obj/machinery/atmospherics/components/unary/comp in location)
|
||||
if(!comp.welded)
|
||||
comp.welded = TRUE
|
||||
comp.update_appearance()
|
||||
comp.visible_message(span_danger("[comp] sealed shut!"))
|
||||
|
||||
for(var/mob/living/potential_tinder in location)
|
||||
potential_tinder.extinguish_mob()
|
||||
for(var/obj/item/potential_tinder in location)
|
||||
potential_tinder.extinguish()
|
||||
@@ -0,0 +1,438 @@
|
||||
/**
|
||||
* A fluid which spreads through the air affecting every mob it engulfs.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke
|
||||
name = "smoke"
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "smoke"
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
opacity = TRUE
|
||||
plane = ABOVE_GAME_PLANE
|
||||
layer = FLY_LAYER
|
||||
anchored = TRUE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
animate_movement = FALSE
|
||||
/// How long the smoke sticks around before it dissipates.
|
||||
var/lifetime = 10 SECONDS
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/Initialize(mapload, datum/fluid_group/group, ...)
|
||||
. = ..()
|
||||
create_reagents(1000)
|
||||
setDir(pick(GLOB.cardinals))
|
||||
SSsmoke.start_processing(src)
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/Destroy()
|
||||
SSsmoke.stop_processing(src)
|
||||
if (spread_bucket)
|
||||
SSsmoke.cancel_spread(src)
|
||||
return ..()
|
||||
|
||||
|
||||
/**
|
||||
* Makes the smoke fade out and then deletes it.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke/proc/kill_smoke()
|
||||
SSsmoke.stop_processing(src)
|
||||
if (spread_bucket)
|
||||
SSsmoke.cancel_spread(src)
|
||||
INVOKE_ASYNC(src, .proc/fade_out)
|
||||
QDEL_IN(src, 1 SECONDS)
|
||||
|
||||
/**
|
||||
* Animates the smoke gradually fading out of visibility.
|
||||
* Also makes the smoke turf transparent as it passes 160 alpha.
|
||||
*
|
||||
* Arguments:
|
||||
* - frames = 0.8 [SECONDS]: The amount of time the smoke should fade out over.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke/proc/fade_out(frames = 0.8 SECONDS)
|
||||
if(alpha == 0) //Handle already transparent case
|
||||
if(opacity)
|
||||
set_opacity(FALSE)
|
||||
return
|
||||
|
||||
if(frames == 0)
|
||||
set_opacity(FALSE)
|
||||
alpha = 0
|
||||
return
|
||||
|
||||
var/time_to_transparency = round(((alpha - 160) / alpha) * frames)
|
||||
if(time_to_transparency >= 1)
|
||||
addtimer(CALLBACK(src, /atom.proc/set_opacity, FALSE), time_to_transparency)
|
||||
else
|
||||
set_opacity(FALSE)
|
||||
animate(src, time = frames, alpha = 0)
|
||||
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/spread(delta_time = 0.1 SECONDS)
|
||||
if(group.total_size > group.target_size)
|
||||
return
|
||||
var/turf/t_loc = get_turf(src)
|
||||
if(!t_loc)
|
||||
return
|
||||
|
||||
for(var/turf/spread_turf in t_loc.get_atmos_adjacent_turfs())
|
||||
if(group.total_size > group.target_size)
|
||||
break
|
||||
if(locate(type) in spread_turf)
|
||||
continue // Don't spread smoke where there's already smoke!
|
||||
for(var/mob/living/smoker in spread_turf)
|
||||
smoke_mob(smoker, delta_time)
|
||||
|
||||
var/obj/effect/particle_effect/fluid/smoke/spread_smoke = new type(spread_turf, group)
|
||||
reagents.copy_to(spread_smoke, reagents.total_volume)
|
||||
spread_smoke.add_atom_colour(color, FIXED_COLOUR_PRIORITY)
|
||||
spread_smoke.lifetime = lifetime
|
||||
|
||||
// the smoke spreads rapidly, but not instantly
|
||||
SSfoam.queue_spread(spread_smoke)
|
||||
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/process(delta_time)
|
||||
lifetime -= delta_time SECONDS
|
||||
if(lifetime <= 0)
|
||||
kill_smoke()
|
||||
return FALSE
|
||||
for(var/mob/living/smoker in loc) // In case smoke somehow winds up in a locker or something this should still behave sanely.
|
||||
smoke_mob(smoker, delta_time)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Handles the effects of this smoke on any mobs it comes into contact with.
|
||||
*
|
||||
* Arguments:
|
||||
* - [smoker][/mob/living/carbon]: The mob that is being exposed to this smoke.
|
||||
* - delta_time: A scaling factor for the effects this has. Primarily based off of tick rate to normalize effects to units of rate/sec.
|
||||
*
|
||||
* Returns whether the smoke effect was applied to the mob.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke/proc/smoke_mob(mob/living/carbon/smoker, delta_time)
|
||||
if(!istype(smoker))
|
||||
return FALSE
|
||||
if(lifetime < 1)
|
||||
return FALSE
|
||||
if(smoker.internal != null || smoker.has_smoke_protection())
|
||||
return FALSE
|
||||
if(smoker.smoke_delay)
|
||||
return FALSE
|
||||
|
||||
smoker.smoke_delay = TRUE
|
||||
addtimer(VARSET_CALLBACK(smoker, smoke_delay, FALSE), 1 SECONDS)
|
||||
return TRUE
|
||||
|
||||
/// A factory which produces clouds of smoke.
|
||||
/datum/effect_system/fluid_spread/smoke
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Transparent smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/// Same as the base type, but the smoke produced is not opaque
|
||||
/datum/effect_system/fluid_spread/smoke/transparent
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/transparent
|
||||
|
||||
/// Same as the base type, but is not opaque.
|
||||
/obj/effect/particle_effect/fluid/smoke/transparent
|
||||
opacity = FALSE
|
||||
|
||||
/**
|
||||
* A helper proc used to spawn small puffs of smoke.
|
||||
*
|
||||
* Arguments:
|
||||
* - range: The amount of smoke to produce as number of steps from origin covered.
|
||||
* - amount: The amount of smoke to produce as the total desired coverage area. Autofilled from the range arg if not set.
|
||||
* - location: Where to produce the smoke cloud.
|
||||
* - smoke_type: The smoke typepath to spawn.
|
||||
*/
|
||||
/proc/do_smoke(range = 0, amount = DIAMOND_AREA(range), location = null, smoke_type = /obj/effect/particle_effect/fluid/smoke)
|
||||
var/datum/effect_system/fluid_spread/smoke/smoke = new
|
||||
smoke.effect_type = smoke_type
|
||||
smoke.set_up(amount = amount, location = location)
|
||||
smoke.start()
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Quick smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/// Smoke that dissipates as quickly as possible.
|
||||
/obj/effect/particle_effect/fluid/smoke/quick
|
||||
lifetime = 1 SECONDS
|
||||
opacity = FALSE
|
||||
|
||||
/// A factory which produces smoke that dissipates as quickly as possible.
|
||||
/datum/effect_system/fluid_spread/smoke/quick
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/quick
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Bad smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/// Smoke that makes you cough and reduces the power of lasers.
|
||||
/obj/effect/particle_effect/fluid/smoke/bad
|
||||
lifetime = 16 SECONDS
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/bad/Initialize(mapload)
|
||||
. = ..()
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = .proc/on_entered,
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/bad/smoke_mob(mob/living/carbon/smoker)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
smoker.drop_all_held_items()
|
||||
smoker.adjustOxyLoss(1)
|
||||
smoker.emote("cough")
|
||||
|
||||
/**
|
||||
* Reduces the power of any beam projectile that passes through the smoke.
|
||||
*
|
||||
* Arguments:
|
||||
* - [source][/datum]: The location that has just been entered. If [/datum/element/connect_loc] is working this is [src.loc][/atom/var/loc].
|
||||
* - [arrived][/atom/movable]: The atom that has just entered the source location.
|
||||
* - [old_loc][/atom]: The location the entering atom just was in.
|
||||
* - [old_locs][/list/atom]: The set of locations the entering atom was just in.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke/bad/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
|
||||
SIGNAL_HANDLER
|
||||
if(istype(arrived, /obj/projectile/beam))
|
||||
var/obj/projectile/beam/beam = arrived
|
||||
beam.damage *= 0.5
|
||||
|
||||
/// A factory which produces smoke that makes you cough.
|
||||
/datum/effect_system/fluid_spread/smoke/bad
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/bad
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Bad Smoke (But Green (and Black))
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/// Green smoke that makes you cough.
|
||||
/obj/effect/particle_effect/fluid/smoke/bad/green
|
||||
name = "green smoke"
|
||||
color = "#00FF00"
|
||||
opacity = FALSE
|
||||
|
||||
/// A factory which produces green smoke that makes you cough.
|
||||
/datum/effect_system/fluid_spread/smoke/bad/green
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/bad/green
|
||||
|
||||
/// Black smoke that makes you cough. (Actually dark grey)
|
||||
/obj/effect/particle_effect/fluid/smoke/bad/black
|
||||
name = "black smoke"
|
||||
color = "#383838"
|
||||
opacity = FALSE
|
||||
|
||||
/// A factory which produces black smoke that makes you cough.
|
||||
/datum/effect_system/fluid_spread/smoke/bad/black
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/bad/black
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Nanofrost smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/// Light blue, transparent smoke which is usually paired with a blast that chills every turf in the area.
|
||||
/obj/effect/particle_effect/fluid/smoke/freezing
|
||||
name = "nanofrost smoke"
|
||||
color = "#B2FFFF"
|
||||
opacity = FALSE
|
||||
|
||||
/// A factory which produces light blue, transparent smoke and a blast that chills every turf in the area.
|
||||
/datum/effect_system/fluid_spread/smoke/freezing
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/freezing
|
||||
/// The radius in which to chill every open turf.
|
||||
var/blast = 0
|
||||
/// The temperature to set the turfs air temperature to.
|
||||
var/temperature = 2
|
||||
/// Whether to weld every vent and air scrubber in the affected area shut.
|
||||
var/weldvents = TRUE
|
||||
/// Whether to make sure each affected turf is actually within range before cooling it.
|
||||
var/distcheck = TRUE
|
||||
|
||||
/**
|
||||
* Chills an open turf.
|
||||
*
|
||||
* Forces the air temperature to a specific value.
|
||||
* Transmutes all of the plasma in the air into nitrogen.
|
||||
* Extinguishes all fires and burning objects/mobs in the turf.
|
||||
* May freeze all vents and vent scrubbers shut.
|
||||
*
|
||||
* Arguments:
|
||||
* - [chilly][/turf/open]: The open turf to chill
|
||||
*/
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/proc/Chilled(turf/open/chilly)
|
||||
if(!istype(chilly))
|
||||
return
|
||||
|
||||
if(chilly.air)
|
||||
var/datum/gas_mixture/air = chilly.air
|
||||
if(!distcheck || get_dist(location, chilly) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air
|
||||
air.temperature = temperature
|
||||
|
||||
var/list/gases = air.gases
|
||||
if(gases[/datum/gas/plasma])
|
||||
air.assert_gas(/datum/gas/nitrogen)
|
||||
gases[/datum/gas/nitrogen][MOLES] += gases[/datum/gas/plasma][MOLES]
|
||||
gases[/datum/gas/plasma][MOLES] = 0
|
||||
air.garbage_collect()
|
||||
|
||||
for(var/obj/effect/hotspot/fire in chilly)
|
||||
qdel(fire)
|
||||
chilly.air_update_turf(FALSE, FALSE)
|
||||
|
||||
if(weldvents)
|
||||
for(var/obj/machinery/atmospherics/components/unary/comp in chilly)
|
||||
if(!isnull(comp.welded) && !comp.welded) //must be an unwelded vent pump or vent scrubber.
|
||||
comp.welded = TRUE
|
||||
comp.update_appearance()
|
||||
comp.visible_message(span_danger("[comp] is frozen shut!"))
|
||||
|
||||
// Extinguishes everything in the turf
|
||||
for(var/mob/living/potential_tinder in chilly)
|
||||
potential_tinder.extinguish_mob()
|
||||
for(var/obj/item/potential_tinder in chilly)
|
||||
potential_tinder.extinguish()
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/set_up(range = 5, amount = DIAMOND_AREA(range), atom/location, blast_radius = 0)
|
||||
. = ..()
|
||||
blast = blast_radius
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/start()
|
||||
if(blast)
|
||||
for(var/turf/T in RANGE_TURFS(blast, location))
|
||||
Chilled(T)
|
||||
return ..()
|
||||
|
||||
/// A variant of the base freezing smoke formerly used by the vent decontamination event.
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/decon
|
||||
temperature = 293.15
|
||||
distcheck = FALSE
|
||||
weldvents = FALSE
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Sleep smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/// Smoke which knocks you out if you breathe it in.
|
||||
/obj/effect/particle_effect/fluid/smoke/sleeping
|
||||
color = "#9C3636"
|
||||
lifetime = 20 SECONDS
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/sleeping/smoke_mob(mob/living/carbon/smoker, delta_time)
|
||||
if(..())
|
||||
smoker.Sleeping(20 SECONDS)
|
||||
smoker.emote("cough")
|
||||
return TRUE
|
||||
|
||||
/// A factory which produces sleeping smoke.
|
||||
/datum/effect_system/fluid_spread/smoke/sleeping
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/sleeping
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Chem smoke
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Smoke which contains reagents which it applies to everything it comes into contact with.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke/chem
|
||||
lifetime = 20 SECONDS
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/chem/process(delta_time)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
var/turf/location = get_turf(src)
|
||||
var/fraction = (delta_time SECONDS) / initial(lifetime)
|
||||
for(var/atom/movable/thing as anything in location)
|
||||
if(thing == src)
|
||||
continue
|
||||
if(location.underfloor_accessibility < UNDERFLOOR_INTERACTABLE && HAS_TRAIT(thing, TRAIT_T_RAY_VISIBLE))
|
||||
continue
|
||||
reagents.expose(thing, TOUCH, fraction)
|
||||
|
||||
reagents.expose(location, TOUCH, fraction)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/fluid/smoke/chem/smoke_mob(mob/living/carbon/smoker, delta_time)
|
||||
if(lifetime < 1)
|
||||
return FALSE
|
||||
if(!istype(smoker))
|
||||
return FALSE
|
||||
if(smoker.internal != null || smoker.has_smoke_protection())
|
||||
return FALSE
|
||||
|
||||
var/fraction = (delta_time SECONDS) / initial(lifetime)
|
||||
reagents.copy_to(smoker, reagents.total_volume, fraction)
|
||||
reagents.expose(smoker, INGEST, fraction)
|
||||
return TRUE
|
||||
|
||||
|
||||
/// A factory which produces clouds of chemical bearing smoke.
|
||||
/datum/effect_system/fluid_spread/smoke/chem
|
||||
/// Evil evil hack so we have something to "hold" our reagents
|
||||
var/datum/reagents/chemholder
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/chem
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/New()
|
||||
..()
|
||||
chemholder = new(1000, NO_REACT)
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/Destroy()
|
||||
QDEL_NULL(chemholder)
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/set_up(range = 1, amount = DIAMOND_AREA(range), atom/location = null, datum/reagents/carry = null, silent = FALSE)
|
||||
. = ..()
|
||||
carry?.copy_to(chemholder, carry.total_volume)
|
||||
|
||||
if(silent)
|
||||
return
|
||||
|
||||
var/list/contained_reagents = list()
|
||||
for(var/datum/reagent/reagent as anything in chemholder.reagent_list)
|
||||
contained_reagents += "[reagent.volume]u [reagent]"
|
||||
|
||||
var/where = "[AREACOORD(location)]"
|
||||
var/contained = length(contained_reagents) ? "[contained_reagents.Join(", ", " \[", "\]")] @ [chemholder.chem_temp]K" : null
|
||||
if(carry.my_atom?.fingerprintslast) //Some reagents don't have a my_atom in some cases
|
||||
var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
var/more = ""
|
||||
if(M)
|
||||
more = "[ADMIN_LOOKUPFLW(M)] "
|
||||
if(!istype(carry.my_atom, /obj/machinery/plumbing))
|
||||
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].")
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].")
|
||||
else
|
||||
if(!istype(carry.my_atom, /obj/machinery/plumbing))
|
||||
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.")
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.")
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/start()
|
||||
var/start_loc = holder ? get_turf(holder) : src.location
|
||||
var/mixcolor = mix_color_from_reagents(chemholder.reagent_list)
|
||||
var/obj/effect/particle_effect/fluid/smoke/chem/smoke = new effect_type(start_loc, new /datum/fluid_group(amount))
|
||||
chemholder.copy_to(smoke, chemholder.total_volume)
|
||||
|
||||
if(mixcolor)
|
||||
smoke.add_atom_colour(mixcolor, FIXED_COLOUR_PRIORITY) // give the smoke color, if it has any to begin with
|
||||
smoke.spread() // Making the smoke spread immediately.
|
||||
|
||||
/**
|
||||
* A version of chemical smoke with a very short lifespan.
|
||||
*/
|
||||
/obj/effect/particle_effect/fluid/smoke/chem/quick
|
||||
lifetime = 4 SECONDS
|
||||
opacity = FALSE
|
||||
alpha = 100
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/quick
|
||||
effect_type = /obj/effect/particle_effect/fluid/smoke/chem/quick
|
||||
Reference in New Issue
Block a user