mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 13:38:41 +01:00
Refactors effect_system (#94999)
## About The Pull Request This PR refactors ``effect_system``s to be a bit easier to use by getting rid of ``set_up``, allowing ``attach()`` to be chained into ``start()`` and refactoring most direct system usages in our code to use helper procs. ``set_up`` was unnecessary and only existed to allow ``New``'s behavior to be fully overriden, which is not required if we split sparks/lightning/steam into a new ``/datum/effect_system/basic`` subtype which houses the effect spreading behavior. This allows us to roll all logic from ``set_up`` into ``New`` and cut down on code complexity. Chaining setup as ``system.attach(src).start()`` also helps a bit in case no helper method exists I've added ``do_chem_smoke`` and ``do_foam`` helpers, which respectively allow chemical smoke or foam to be spawned easily without having to manually create effect datums and reagent holders. Also turns out we've had some nonfunctional effect systems which either never set themselves up, or never started, so I fixed those while I was at it (mostly by moving them to aforementioned helper procs) ## Why It's Good For The Game Cleaner code, makes it significantly easier for users to work with. Also most of our effect system usage was copypasta which was passing booleans as numbers, while perfectly fine helper procs existed in our code. ## Changelog 🆑 refactor: Refactored sparks, foam, smoke, and other miscellaneous effect systems. refactor: Vapes now have consistent rigging with cigs using the new system. fix: Fixed some effects never working. /🆑
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
/obj/effect/shield
|
||||
name = "shield"
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "wave2"
|
||||
layer = ABOVE_NORMAL_TURF_LAYER
|
||||
flags_1 = PREVENT_CLICK_UNDER_1
|
||||
anchored = TRUE
|
||||
var/old_heat_capacity
|
||||
|
||||
/obj/effect/shield/Initialize(mapload)
|
||||
. = ..()
|
||||
var/turf/location = get_turf(src)
|
||||
old_heat_capacity=location.heat_capacity
|
||||
location.heat_capacity = INFINITY
|
||||
|
||||
/obj/effect/shield/Destroy()
|
||||
var/turf/location = get_turf(src)
|
||||
location.heat_capacity=old_heat_capacity
|
||||
return ..()
|
||||
|
||||
/obj/effect/shield/singularity_act()
|
||||
return
|
||||
|
||||
/obj/effect/shield/singularity_pull(atom/singularity, current_size)
|
||||
return
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/* This is an attempt to make some easily reusable "particle" type effect, to stop the code
|
||||
constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one
|
||||
defined, then set up when it is created with New(). Then this same system can just be reused each time
|
||||
it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam
|
||||
would spawn and follow the beaker, even if it is carried or thrown.
|
||||
/*
|
||||
* This is an attempt to make some easily reusable "particle" type effect, to stop the code
|
||||
* constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one
|
||||
* defined, then set up when it is created with New(). Then this same system can just be reused each time
|
||||
* it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam
|
||||
* would spawn and follow the beaker, even if it is carried or thrown.
|
||||
*/
|
||||
|
||||
#define PER_SYSTEM_PARTICLE_CAP 20
|
||||
|
||||
/obj/effect/particle_effect
|
||||
name = "particle effect"
|
||||
@@ -17,36 +19,61 @@ would spawn and follow the beaker, even if it is carried or thrown.
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system
|
||||
var/number = 3
|
||||
var/cardinals_only = FALSE
|
||||
var/turf/location
|
||||
var/atom/holder
|
||||
var/effect_type
|
||||
var/total_effects = 0
|
||||
var/autocleanup = FALSE //will delete itself after use
|
||||
// Does not contain any behaviors and should not be used by itself
|
||||
abstract_type = /datum/effect_system
|
||||
/// Turf on which to spawn the effects
|
||||
var/turf/location = null
|
||||
/// Atom that is spawning the particles whose location we're following
|
||||
var/atom/holder = null
|
||||
|
||||
/datum/effect_system/New(turf/location)
|
||||
. = ..()
|
||||
src.location = get_turf(location)
|
||||
|
||||
/datum/effect_system/Destroy()
|
||||
holder = null
|
||||
location = null
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/proc/set_up(number = 3, cardinals_only = FALSE, location)
|
||||
src.number = min(number, 10)
|
||||
src.cardinals_only = cardinals_only
|
||||
src.location = get_turf(location)
|
||||
|
||||
/datum/effect_system/proc/attach(atom/atom)
|
||||
holder = atom
|
||||
/// Instruct the effect system to start following an atom. Can be chained into .start()
|
||||
/datum/effect_system/proc/attach(atom/new_holder)
|
||||
RETURN_TYPE(/datum/effect_system)
|
||||
holder = new_holder
|
||||
return src
|
||||
|
||||
/// Start the effect system
|
||||
/datum/effect_system/proc/start()
|
||||
return
|
||||
|
||||
/// Basic effect system which spawns a certain number of moving effects
|
||||
/datum/effect_system/basic
|
||||
/// Total number of particles to spawn
|
||||
var/amount = 3
|
||||
/// Should we pick among cardinals or all directions when deciding where the particle should move
|
||||
var/cardinals_only = FALSE
|
||||
/// Typepath of the effect to spawn
|
||||
var/effect_type = null
|
||||
/// Total amount of effects we currently have active
|
||||
var/total_effects = 0
|
||||
/// Should the system delete itself after finishing?
|
||||
var/autocleanup = FALSE
|
||||
|
||||
/datum/effect_system/basic/New(turf/location, amount = null, cardinals_only = null)
|
||||
. = ..()
|
||||
if (!isnull(amount))
|
||||
src.amount = amount
|
||||
if (!isnull(cardinals_only))
|
||||
src.cardinals_only = cardinals_only
|
||||
|
||||
/datum/effect_system/basic/start()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
for(var/i in 1 to number)
|
||||
if(total_effects > 20)
|
||||
for(var/i in 1 to amount)
|
||||
if(total_effects > PER_SYSTEM_PARTICLE_CAP)
|
||||
return
|
||||
generate_effect()
|
||||
|
||||
/datum/effect_system/proc/generate_effect()
|
||||
/datum/effect_system/basic/proc/generate_effect()
|
||||
if(holder)
|
||||
location = get_turf(holder)
|
||||
var/obj/effect/effect = new effect_type(location)
|
||||
@@ -56,15 +83,17 @@ would spawn and follow the beaker, even if it is carried or thrown.
|
||||
direction = pick(GLOB.cardinals)
|
||||
else
|
||||
direction = pick(GLOB.alldirs)
|
||||
var/step_amt = pick(1,2,3)
|
||||
|
||||
var/step_amt = rand(1, 3)
|
||||
var/step_delay = 5
|
||||
|
||||
var/datum/move_loop/loop = GLOB.move_manager.move(effect, direction, step_delay, timeout = step_delay * step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
|
||||
RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(decrement_total_effect))
|
||||
if (autocleanup)
|
||||
RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(decrement_total_effect))
|
||||
|
||||
/datum/effect_system/proc/decrement_total_effect(datum/source)
|
||||
/datum/effect_system/basic/proc/decrement_total_effect(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
total_effects--
|
||||
if(!autocleanup || total_effects > 0)
|
||||
return
|
||||
QDEL_IN(src, 2 SECONDS)
|
||||
if(total_effects == 0)
|
||||
QDEL_IN(src, 2 SECONDS)
|
||||
|
||||
#undef PER_SYSTEM_PARTICLE_CAP
|
||||
|
||||
@@ -9,23 +9,20 @@
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/effect/particle_effect/expl_particles/LateInitialize()
|
||||
var/step_amt = pick(25;1,50;2,100;3,200;4)
|
||||
|
||||
var/step_amt = pick(25;1, 50;2, 100;3, 200;4)
|
||||
var/datum/move_loop/loop = GLOB.move_manager.move(src, pick(GLOB.alldirs), 1, timeout = step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
|
||||
RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(end_particle))
|
||||
|
||||
/obj/effect/particle_effect/expl_particles/proc/end_particle(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
if(QDELETED(src))
|
||||
return
|
||||
qdel(src)
|
||||
if (!QDELETED(src))
|
||||
qdel(src)
|
||||
|
||||
/datum/effect_system/expl_particles
|
||||
number = 10
|
||||
/datum/effect_system/basic/expl_particles
|
||||
amount = 10
|
||||
|
||||
/datum/effect_system/expl_particles/start()
|
||||
for(var/i in 1 to number)
|
||||
new /obj/effect/particle_effect/expl_particles(location)
|
||||
/datum/effect_system/basic/expl_particles/generate_effect()
|
||||
new /obj/effect/particle_effect/expl_particles(location)
|
||||
|
||||
/obj/effect/explosion
|
||||
name = "fire"
|
||||
@@ -45,21 +42,16 @@
|
||||
|
||||
/datum/effect_system/explosion
|
||||
|
||||
/datum/effect_system/explosion/set_up(location)
|
||||
src.location = get_turf(location)
|
||||
|
||||
/datum/effect_system/explosion/start()
|
||||
new/obj/effect/explosion( location )
|
||||
var/datum/effect_system/expl_particles/P = new/datum/effect_system/expl_particles()
|
||||
P.set_up(10, 0, location)
|
||||
P.start()
|
||||
new /obj/effect/explosion(location)
|
||||
var/datum/effect_system/basic/expl_particles/boom_particles = new(location)
|
||||
boom_particles.start()
|
||||
|
||||
/datum/effect_system/explosion/smoke
|
||||
|
||||
/datum/effect_system/explosion/smoke/proc/create_smoke()
|
||||
var/datum/effect_system/fluid_spread/smoke/S = new
|
||||
S.set_up(2, holder = holder, location = location)
|
||||
S.start()
|
||||
var/datum/effect_system/fluid_spread/smoke/smoke_system = new(location, range = 2)
|
||||
smoke_system.attach(holder).start()
|
||||
|
||||
/datum/effect_system/explosion/smoke/start()
|
||||
..()
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//////// Attach a trail to any object, that spawns when it moves (like for the jetpack)
|
||||
/// just pass in the object to attach it to in set_up
|
||||
/// Then do start() to start it and stop() to stop it, obviously
|
||||
/// and don't call start() in a loop that will be repeated otherwise it'll get spammed!
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/datum/effect_system/trail_follow
|
||||
var/turf/oldposition
|
||||
var/active = FALSE
|
||||
var/allow_overlap = FALSE
|
||||
var/auto_process = TRUE
|
||||
var/qdel_in_time = 10
|
||||
var/fadetype = "ion_fade"
|
||||
var/fade = TRUE
|
||||
var/nograv_required = FALSE
|
||||
|
||||
/datum/effect_system/trail_follow/set_up(atom/atom)
|
||||
attach(atom)
|
||||
oldposition = get_turf(atom)
|
||||
|
||||
/datum/effect_system/trail_follow/Destroy()
|
||||
oldposition = null
|
||||
stop()
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/trail_follow/proc/stop()
|
||||
oldposition = null
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
active = FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system/trail_follow/start()
|
||||
oldposition = get_turf(holder)
|
||||
if(!check_conditions())
|
||||
return FALSE
|
||||
if(auto_process)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
active = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system/trail_follow/process()
|
||||
generate_effect()
|
||||
|
||||
/datum/effect_system/trail_follow/generate_effect()
|
||||
if(!check_conditions())
|
||||
return stop()
|
||||
if(oldposition && !(oldposition == get_turf(holder)))
|
||||
if(!oldposition.has_gravity() || !nograv_required)
|
||||
var/obj/effect/E = new effect_type(oldposition)
|
||||
set_dir(E)
|
||||
if(fade)
|
||||
flick(fadetype, E)
|
||||
E.icon_state = ""
|
||||
if(qdel_in_time)
|
||||
QDEL_IN(E, qdel_in_time)
|
||||
oldposition = get_turf(holder)
|
||||
|
||||
/datum/effect_system/trail_follow/proc/check_conditions()
|
||||
if(!get_turf(holder))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system/trail_follow/steam
|
||||
effect_type = /obj/effect/particle_effect/steam
|
||||
|
||||
/obj/effect/particle_effect/ion_trails
|
||||
name = "ion trails"
|
||||
icon_state = "ion_trails"
|
||||
anchored = TRUE
|
||||
|
||||
/obj/effect/particle_effect/ion_trails/flight
|
||||
icon_state = "ion_trails_flight"
|
||||
|
||||
/datum/effect_system/trail_follow/ion
|
||||
effect_type = /obj/effect/particle_effect/ion_trails
|
||||
nograv_required = TRUE
|
||||
qdel_in_time = 20
|
||||
|
||||
/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/I)
|
||||
I.setDir(holder.dir)
|
||||
|
||||
/datum/effect_system/trail_follow/ion/grav_allowed
|
||||
nograv_required = FALSE
|
||||
|
||||
//Reagent-based explosion effect
|
||||
|
||||
/datum/effect_system/reagents_explosion
|
||||
var/amount // TNT equivalent
|
||||
var/flashing_factor = null // factor of how powerful the flash effect relatively to the explosion
|
||||
var/flaming_factor = null // factor of how powerful the flame effect is relatively to explosion
|
||||
var/explosion_message = 1 //whether we show a message to mobs.
|
||||
|
||||
/datum/effect_system/reagents_explosion/set_up(amt, loca, flash_fact = null, flame_fact = null, message = TRUE)
|
||||
amount = amt
|
||||
explosion_message = message
|
||||
if(isturf(loca))
|
||||
location = loca
|
||||
else
|
||||
location = get_turf(loca)
|
||||
flashing_factor = flash_fact
|
||||
flaming_factor = flame_fact
|
||||
|
||||
/// Starts the explosion. The explosion_source is as part of logging and identifying the source of the explosion for logs.
|
||||
/datum/effect_system/reagents_explosion/start(atom/explosion_source = null)
|
||||
if(!explosion_source)
|
||||
stack_trace("Reagent explosion triggered without a source atom. This explosion may have incomplete logging.")
|
||||
|
||||
if(explosion_message)
|
||||
location.visible_message(span_danger("The solution violently explodes!"), span_hear("You hear an explosion!"))
|
||||
|
||||
dyn_explosion(location, amount, flash_range = flashing_factor, flame_range = flaming_factor, explosion_cause = explosion_source)
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
//Reagent-based explosion effect
|
||||
|
||||
/datum/effect_system/reagents_explosion
|
||||
/// Explosive power
|
||||
var/amount
|
||||
/// Factor of how powerful the flash effect relatively to the explosion
|
||||
var/flashing_factor = null
|
||||
/// Factor of how powerful the flame effect is relatively to explosion
|
||||
var/flaming_factor = null
|
||||
/// Whether we show a message to mobs.
|
||||
var/explosion_message = 1
|
||||
|
||||
/datum/effect_system/reagents_explosion/New(turf/location, amount, flash_fact = null, flame_fact = null, message = TRUE)
|
||||
. = ..()
|
||||
src.amount = amount
|
||||
explosion_message = message
|
||||
if (!isturf(location))
|
||||
location = get_turf(location)
|
||||
flashing_factor = flash_fact
|
||||
flaming_factor = flame_fact
|
||||
|
||||
/// Starts the explosion. The explosion_source is as part of logging and identifying the source of the explosion for logs.
|
||||
/datum/effect_system/reagents_explosion/start(atom/explosion_source = null)
|
||||
if(!explosion_source)
|
||||
stack_trace("Reagent explosion triggered without a source atom. This explosion may have incomplete logging.")
|
||||
if(explosion_message)
|
||||
location.visible_message(span_danger("The solution violently explodes!"), span_hear("You hear an explosion!"))
|
||||
dyn_explosion(location, amount, flash_range = flashing_factor, flame_range = flaming_factor, explosion_cause = explosion_source)
|
||||
@@ -5,13 +5,13 @@
|
||||
// will always spawn at the items location.
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/proc/do_sparks(number, cardinal_only, datum/source)
|
||||
var/datum/effect_system/spark_spread/sparks = new
|
||||
sparks.set_up(number, cardinal_only, source)
|
||||
/proc/do_sparks(number, cardinal_only, atom/source, atom/holder = null, spark_type = /datum/effect_system/basic/spark_spread)
|
||||
var/datum/effect_system/basic/spark_spread/sparks = new spark_type(get_turf(source), number, cardinal_only)
|
||||
if (holder)
|
||||
sparks.attach(holder)
|
||||
sparks.autocleanup = TRUE
|
||||
sparks.start()
|
||||
|
||||
|
||||
/obj/effect/particle_effect/sparks
|
||||
name = "sparks"
|
||||
icon_state = "sparks"
|
||||
@@ -32,7 +32,7 @@
|
||||
var/turf/location = loc
|
||||
if(isturf(location))
|
||||
affect_location(location, just_initialized = TRUE)
|
||||
QDEL_IN(src, 20)
|
||||
QDEL_IN(src, 2 SECONDS)
|
||||
|
||||
/obj/effect/particle_effect/sparks/Destroy()
|
||||
var/turf/location = loc
|
||||
@@ -41,11 +41,15 @@
|
||||
return ..()
|
||||
|
||||
/obj/effect/particle_effect/sparks/Move()
|
||||
..()
|
||||
. = ..()
|
||||
var/turf/location = loc
|
||||
if(isturf(location))
|
||||
affect_location(location)
|
||||
|
||||
/obj/effect/particle_effect/sparks/quantum
|
||||
name = "quantum sparks"
|
||||
icon_state = "quantum_sparks"
|
||||
|
||||
/*
|
||||
* Apply the effects of this spark to its location.
|
||||
*
|
||||
@@ -56,7 +60,7 @@
|
||||
* just_initialized - If the spark is just being created, and we need to manually affect everything in the location
|
||||
*/
|
||||
/obj/effect/particle_effect/sparks/proc/affect_location(turf/location, just_initialized = FALSE)
|
||||
location.hotspot_expose(1000,100)
|
||||
location.hotspot_expose(1000, 100)
|
||||
SEND_SIGNAL(location, COMSIG_ATOM_TOUCHED_SPARKS, src) // for plasma floors; other floor types only have to worry about the mysterious HAZARDOUS sparks
|
||||
if(just_initialized)
|
||||
for(var/atom/movable/singed in location)
|
||||
@@ -79,27 +83,23 @@
|
||||
if(reagents && !(reagents.flags & SEALED_CONTAINER))
|
||||
reagents.expose_temperature(1000) // we set this at 1000 because that's the max reagent temp for a chem heater, higher temps require more than sparks
|
||||
return
|
||||
|
||||
if(ishuman(singed))
|
||||
var/mob/living/carbon/human/singed_human = singed
|
||||
for(var/obj/item/anything in singed_human.get_visible_items())
|
||||
sparks_touched(src, anything)
|
||||
|
||||
/datum/effect_system/spark_spread
|
||||
/datum/effect_system/basic/spark_spread
|
||||
effect_type = /obj/effect/particle_effect/sparks
|
||||
|
||||
/datum/effect_system/spark_spread/quantum
|
||||
/datum/effect_system/basic/spark_spread/quantum
|
||||
effect_type = /obj/effect/particle_effect/sparks/quantum
|
||||
|
||||
|
||||
//electricity
|
||||
|
||||
/obj/effect/particle_effect/sparks/electricity
|
||||
name = "lightning"
|
||||
icon_state = "electricity"
|
||||
|
||||
/obj/effect/particle_effect/sparks/quantum
|
||||
name = "quantum sparks"
|
||||
icon_state = "quantum_sparks"
|
||||
|
||||
/datum/effect_system/lightning_spread
|
||||
/datum/effect_system/basic/lightning_spread
|
||||
effect_type = /obj/effect/particle_effect/sparks/electricity
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
/////////////////////////////////////////////
|
||||
//////// Attach a trail to any object, that spawns when it moves (like for the jetpack)
|
||||
/// just pass in the object to attach it to in set_up
|
||||
/// Then do start() to start it and stop() to stop it, obviously
|
||||
/// and don't call start() in a loop that will be repeated otherwise it'll get spammed!
|
||||
/////////////////////////////////////////////
|
||||
|
||||
/datum/effect_system/trail_follow
|
||||
/// Previous position of the atom we're tracking
|
||||
var/turf/oldposition
|
||||
/// Are we currently spawning particles?
|
||||
var/active = FALSE
|
||||
/// Can the particles be spawned ontop of eachother?
|
||||
var/allow_overlap = FALSE
|
||||
/// Should we automatically start processing ourselves?
|
||||
var/auto_process = TRUE
|
||||
/// Delay before we delete the particles
|
||||
var/qdel_in_time = 1 SECONDS
|
||||
/// Typepath we should spawn
|
||||
var/effect_type = null
|
||||
/// Should we flick an icon state and blank out the particles afterwards?
|
||||
var/fade = TRUE
|
||||
/// icon_state to flick on our particles
|
||||
var/fadetype = "ion_fade"
|
||||
/// Are we restricted to zero-g only?
|
||||
var/nograv_required = FALSE
|
||||
|
||||
/datum/effect_system/trail_follow/New(turf/location)
|
||||
. = ..()
|
||||
attach(location)
|
||||
oldposition = location
|
||||
|
||||
/datum/effect_system/trail_follow/Destroy()
|
||||
oldposition = null
|
||||
stop()
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/trail_follow/proc/stop()
|
||||
oldposition = null
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
active = FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system/trail_follow/start()
|
||||
oldposition = get_turf(holder)
|
||||
if(!check_conditions())
|
||||
return FALSE
|
||||
if(auto_process)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
active = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system/trail_follow/process()
|
||||
generate_effect()
|
||||
|
||||
/datum/effect_system/trail_follow/proc/generate_effect()
|
||||
if(!check_conditions())
|
||||
return stop()
|
||||
|
||||
if(!oldposition || oldposition == get_turf(holder))
|
||||
oldposition = get_turf(holder)
|
||||
return
|
||||
|
||||
if(nograv_required && oldposition.has_gravity())
|
||||
oldposition = get_turf(holder)
|
||||
return
|
||||
|
||||
var/obj/effect/particle = new effect_type(oldposition)
|
||||
set_dir(particle)
|
||||
if(fade)
|
||||
flick(fadetype, particle)
|
||||
particle.icon_state = ""
|
||||
|
||||
if(qdel_in_time)
|
||||
QDEL_IN(particle, qdel_in_time)
|
||||
|
||||
/datum/effect_system/trail_follow/proc/check_conditions()
|
||||
if(!get_turf(holder))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/effect_system/trail_follow/proc/set_dir(obj/effect/effect)
|
||||
effect.setDir(holder.dir)
|
||||
|
||||
/datum/effect_system/trail_follow/steam
|
||||
effect_type = /obj/effect/particle_effect/steam
|
||||
|
||||
/obj/effect/particle_effect/ion_trails
|
||||
name = "ion trails"
|
||||
icon_state = "ion_trails"
|
||||
anchored = TRUE
|
||||
|
||||
/obj/effect/particle_effect/ion_trails/flight
|
||||
icon_state = "ion_trails_flight"
|
||||
|
||||
/datum/effect_system/trail_follow/ion
|
||||
effect_type = /obj/effect/particle_effect/ion_trails
|
||||
nograv_required = TRUE
|
||||
qdel_in_time = 2 SECONDS
|
||||
|
||||
/datum/effect_system/trail_follow/ion/grav_allowed
|
||||
nograv_required = FALSE
|
||||
@@ -4,15 +4,17 @@
|
||||
name = "water"
|
||||
icon_state = "extinguish"
|
||||
pass_flags = PASSTABLE | PASSMACHINE | PASSSTRUCTURE | PASSGRILLE | PASSBLOB | PASSVEHICLE
|
||||
var/life = 15
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
/// Amount of turfs we pass before deleting ourselves
|
||||
var/life = 15
|
||||
|
||||
/obj/effect/particle_effect/water/Initialize(mapload)
|
||||
. = ..()
|
||||
QDEL_IN(src, 70)
|
||||
QDEL_IN(src, 7 SECONDS)
|
||||
|
||||
/obj/effect/particle_effect/water/Move(turf/newloc)
|
||||
if (--src.life < 1)
|
||||
life -= 1
|
||||
if (life <= 0)
|
||||
qdel(src)
|
||||
return FALSE
|
||||
return ..()
|
||||
@@ -20,8 +22,7 @@
|
||||
/obj/effect/particle_effect/water/Bump(atom/A)
|
||||
if(reagents)
|
||||
reagents.expose(A)
|
||||
if(A.reagents)
|
||||
A.reagents.expose_temperature(-25)
|
||||
A.reagents?.expose_temperature(reagents.chem_temp)
|
||||
return ..()
|
||||
|
||||
///Extinguisher snowflake
|
||||
@@ -71,17 +72,11 @@
|
||||
/////////////////////////////////////////////
|
||||
// GENERIC STEAM SPREAD SYSTEM
|
||||
|
||||
//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location)
|
||||
// Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location)
|
||||
// The attach(atom/atom) proc is optional, and can be called to attach the effect
|
||||
// to something, like a smoking beaker, so then you can just call start() and the steam
|
||||
// will always spawn at the items location, even if it's moved.
|
||||
|
||||
/* Example:
|
||||
*var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() -- creates new system
|
||||
*steam.set_up(5, 0, mob.loc) -- sets up variables
|
||||
*OPTIONAL: steam.attach(mob)
|
||||
*steam.start() -- spawns the effect
|
||||
*/
|
||||
/////////////////////////////////////////////
|
||||
/obj/effect/particle_effect/steam
|
||||
name = "steam"
|
||||
@@ -90,11 +85,7 @@
|
||||
|
||||
/obj/effect/particle_effect/steam/Initialize(mapload)
|
||||
. = ..()
|
||||
QDEL_IN(src, 20)
|
||||
QDEL_IN(src, 2 SECONDS)
|
||||
|
||||
/datum/effect_system/steam_spread
|
||||
/datum/effect_system/basic/steam_spread
|
||||
effect_type = /obj/effect/particle_effect/steam
|
||||
|
||||
/obj/effect/particle_effect/water/Bump(atom/A)
|
||||
if(A.reagents && reagents)
|
||||
A.reagents.expose_temperature(reagents.chem_temp)
|
||||
|
||||
@@ -115,13 +115,16 @@
|
||||
* A factory which produces fluid groups.
|
||||
*/
|
||||
/datum/effect_system/fluid_spread
|
||||
effect_type = /obj/effect/particle_effect/fluid
|
||||
/// The amount of smoke to produce.
|
||||
/// The amount of fluid to produce.
|
||||
var/amount = 10
|
||||
/// Type of the effect we're spawning
|
||||
var/effect_type = /obj/effect/particle_effect/fluid
|
||||
|
||||
/datum/effect_system/fluid_spread/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location, ...)
|
||||
src.holder = holder
|
||||
src.location = location
|
||||
/datum/effect_system/fluid_spread/New(turf/location, range = 1, amount = null, atom/holder = null)
|
||||
. = ..()
|
||||
attach(holder)
|
||||
if (isnull(amount))
|
||||
amount = DIAMOND_AREA(range)
|
||||
src.amount = amount
|
||||
|
||||
/datum/effect_system/fluid_spread/start(log = FALSE)
|
||||
@@ -144,7 +147,6 @@
|
||||
var/blame_msg
|
||||
if (holder)
|
||||
holder.transfer_fingerprints_to(flood) // This is important. If this doesn't exist thermobarics are annoying to adjudicate.
|
||||
|
||||
source_msg = "from inside of [ismob(holder) ? ADMIN_LOOKUPFLW(holder) : ADMIN_VERBOSEJMP(holder)]"
|
||||
var/lastkey = holder.fingerprintslast
|
||||
if (lastkey)
|
||||
|
||||
@@ -178,31 +178,59 @@
|
||||
if(prob(max(0, exposed_temperature - 475))) //foam dissolves when heated
|
||||
kill_foam()
|
||||
|
||||
/// Proc to quickly spawn foam
|
||||
/// reagent_type can accept a list of reagents, optionally as a key-value pair with values overriding reagent_volume if not null
|
||||
/proc/do_foam(range = 1, atom/holder = null, turf/location = null, datum/reagent/reagent_type = null, reagent_volume = 10, datum/reagents/carry = null, amount = null, log = FALSE, datum/effect_system/fluid_spread/foam/foam_type = /datum/effect_system/fluid_spread/foam, result_type = null, stop_reactions = FALSE, reagent_scale = FOAM_REAGENT_SCALE)
|
||||
if (carry || isnull(reagent_type))
|
||||
var/datum/effect_system/fluid_spread/foam/foam = new foam_type(location, range, amount, holder || location, carry, result_type, stop_reactions, reagent_scale)
|
||||
foam.start(log = log)
|
||||
return
|
||||
|
||||
if (ispath(reagent_type, /datum/reagent))
|
||||
var/datum/reagents/foam_reagents = new /datum/reagents(reagent_volume)
|
||||
foam_reagents.add_reagent(reagent_type, reagent_volume)
|
||||
var/datum/effect_system/fluid_spread/foam/foam = new foam_type(location, range, amount, holder || location, foam_reagents, result_type, stop_reactions, reagent_scale)
|
||||
foam.start(log = log)
|
||||
return
|
||||
|
||||
|
||||
if (!islist(reagent_type))
|
||||
CRASH("do_foam passed a non-reagent path, non-list reagent_type [reagent_type]!")
|
||||
|
||||
var/list/reagent_list = reagent_type
|
||||
var/chem_volume = 0
|
||||
for (var/chem_type in reagent_list)
|
||||
chem_volume += reagent_list[chem_type] || reagent_volume
|
||||
|
||||
var/datum/reagents/foam_reagents = new /datum/reagents(chem_volume)
|
||||
for (var/chem_type in reagent_list)
|
||||
foam_reagents.add_reagent(chem_type, reagent_list[chem_type] || reagent_volume)
|
||||
|
||||
var/datum/effect_system/fluid_spread/foam/foam = new foam_type(location, range, amount, holder || location, foam_reagents, result_type, stop_reactions, reagent_scale)
|
||||
foam.start(log = log)
|
||||
|
||||
/// 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/datum/reagents/chemholder = null
|
||||
/// The amount that we multiply the payload by
|
||||
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()
|
||||
..()
|
||||
/datum/effect_system/fluid_spread/foam/New(turf/location, range = 1, amount = null, atom/holder = null, datum/reagents/carry = null, result_type = null, stop_reactions = FALSE, reagent_scale = FOAM_REAGENT_SCALE)
|
||||
. = ..()
|
||||
chemholder = new(1000, NO_REACT)
|
||||
carry?.trans_to(chemholder, carry.total_volume, no_react = stop_reactions, copy_only = TRUE)
|
||||
if(!isnull(result_type))
|
||||
src.result_type = result_type
|
||||
src.reagent_scale = reagent_scale
|
||||
|
||||
/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/holder, atom/location = null, datum/reagents/carry = null, result_type = null, stop_reactions = FALSE)
|
||||
. = ..()
|
||||
carry?.trans_to(chemholder, carry.total_volume, no_react = stop_reactions, copy_only = TRUE)
|
||||
if(!isnull(result_type))
|
||||
src.result_type = result_type
|
||||
|
||||
/datum/effect_system/fluid_spread/foam/start(log = FALSE)
|
||||
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)
|
||||
@@ -235,7 +263,6 @@
|
||||
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
|
||||
@@ -468,9 +495,7 @@
|
||||
|
||||
/obj/effect/spawner/foam_starter/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
var/datum/effect_system/fluid_spread/foam/foam = new foam_type()
|
||||
foam.set_up(foam_size, holder = src, location = loc)
|
||||
var/datum/effect_system/fluid_spread/foam/foam = new foam_type(loc, foam_size, holder = src)
|
||||
foam.start()
|
||||
|
||||
/obj/effect/spawner/foam_starter/small
|
||||
|
||||
@@ -165,12 +165,13 @@
|
||||
* - 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.
|
||||
* - smoke_type - Typepath for the effect system to use
|
||||
* - effect_type: The smoke typepath to spawn.
|
||||
* - log: Should the system log the smoke spawned?
|
||||
*/
|
||||
/proc/do_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, smoke_type = /obj/effect/particle_effect/fluid/smoke, log = FALSE)
|
||||
var/datum/effect_system/fluid_spread/smoke/smoke = new
|
||||
smoke.effect_type = smoke_type
|
||||
smoke.set_up(amount = amount, holder = holder, location = location)
|
||||
/proc/do_smoke(range = 0, atom/holder = null, location = null, amount = null, smoke_type = /datum/effect_system/fluid_spread/smoke, effect_type = /obj/effect/particle_effect/fluid/smoke, log = FALSE)
|
||||
var/datum/effect_system/fluid_spread/smoke/smoke = new smoke_type(location, range, amount, holder)
|
||||
smoke.effect_type = effect_type
|
||||
smoke.start(log = log)
|
||||
|
||||
/////////////////////////////////////////////
|
||||
@@ -205,7 +206,6 @@
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
smoker.drop_all_held_items()
|
||||
smoker.adjust_oxy_loss(1)
|
||||
smoker.emote("cough")
|
||||
@@ -275,6 +275,10 @@
|
||||
/// Whether to make sure each affected turf is actually within range before cooling it.
|
||||
var/distcheck = TRUE
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/New(turf/location, range = 1, amount = null, atom/holder = null, blast_radius = 0)
|
||||
. = ..()
|
||||
blast = blast_radius
|
||||
|
||||
/**
|
||||
* Chills an open turf.
|
||||
*
|
||||
@@ -286,7 +290,7 @@
|
||||
* Arguments:
|
||||
* - [chilly][/turf/open]: The open turf to chill
|
||||
*/
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/proc/Chilled(turf/open/chilly)
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/proc/chill_turf(turf/open/chilly)
|
||||
if(!istype(chilly))
|
||||
return
|
||||
|
||||
@@ -319,14 +323,10 @@
|
||||
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/holder, atom/location, blast_radius = 0)
|
||||
. = ..()
|
||||
blast = blast_radius
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/freezing/start(log = FALSE)
|
||||
if(blast)
|
||||
for(var/turf/T in RANGE_TURFS(blast, location))
|
||||
Chilled(T)
|
||||
chill_turf(T)
|
||||
return ..()
|
||||
|
||||
/// A variant of the base freezing smoke formerly used by the vent decontamination event.
|
||||
@@ -398,13 +398,33 @@
|
||||
return TRUE
|
||||
|
||||
/// Helper to quickly create a cloud of reagent smoke
|
||||
/proc/do_chem_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, log = FALSE, datum/effect_system/fluid_spread/smoke/chem/smoke_type = /datum/effect_system/fluid_spread/smoke/chem)
|
||||
var/datum/reagents/smoke_reagents = new/datum/reagents(reagent_volume)
|
||||
smoke_reagents.add_reagent(reagent_type, reagent_volume)
|
||||
/// reagent_type can accept a list of reagents, optionally as a key-value pair with values overriding reagent_volume if not null
|
||||
/proc/do_chem_smoke(range = 0, atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, datum/reagents/carry = null, carry_limit = null, log = FALSE, amount = null, datum/effect_system/fluid_spread/smoke/chem/smoke_type = /datum/effect_system/fluid_spread/smoke/chem, silent = TRUE)
|
||||
if (carry)
|
||||
var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type(location, range, amount, holder || location, carry, carry_limit, silent)
|
||||
smoke.start(log = log)
|
||||
return
|
||||
|
||||
var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type
|
||||
smoke.attach(location)
|
||||
smoke.set_up(amount = amount, holder = holder, location = location, carry = smoke_reagents, silent = TRUE)
|
||||
if (ispath(reagent_type, /datum/reagent))
|
||||
var/datum/reagents/smoke_reagents = new /datum/reagents(reagent_volume)
|
||||
smoke_reagents.add_reagent(reagent_type, reagent_volume)
|
||||
var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type(location, range, amount, holder || location, smoke_reagents, carry_limit, silent)
|
||||
smoke.start(log = log)
|
||||
return
|
||||
|
||||
if (!islist(reagent_type))
|
||||
CRASH("do_chem_smoke passed a non-reagent path, non-list reagent_type [reagent_type]!")
|
||||
|
||||
var/list/reagent_list = reagent_type
|
||||
var/chem_volume = 0
|
||||
for (var/chem_type in reagent_list)
|
||||
chem_volume += reagent_list[chem_type] || reagent_volume
|
||||
|
||||
var/datum/reagents/smoke_reagents = new /datum/reagents(chem_volume)
|
||||
for (var/chem_type in reagent_list)
|
||||
smoke_reagents.add_reagent(chem_type, reagent_list[chem_type] || reagent_volume)
|
||||
|
||||
var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type(location, range, amount, holder || location, smoke_reagents, carry_limit, silent)
|
||||
smoke.start(log = log)
|
||||
|
||||
/// A factory which produces clouds of chemical bearing smoke.
|
||||
@@ -413,18 +433,10 @@
|
||||
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/holder, atom/location = null, datum/reagents/carry = null, silent = FALSE)
|
||||
/datum/effect_system/fluid_spread/smoke/chem/New(turf/location, range = 1, amount = null, atom/holder = null, datum/reagents/carry = null, carry_limit = null, silent = FALSE)
|
||||
. = ..()
|
||||
carry?.trans_to(chemholder, carry.total_volume, copy_only = TRUE)
|
||||
chemholder = new(1000, NO_REACT)
|
||||
carry?.trans_to(chemholder, isnull(carry_limit) ? carry.total_volume : carry_limit, copy_only = TRUE)
|
||||
|
||||
if(silent)
|
||||
return
|
||||
@@ -436,18 +448,22 @@
|
||||
var/where = "[AREACOORD(location)]"
|
||||
var/contained = length(contained_reagents) ? "\[[contained_reagents.Join(", ")]\] @ [chemholder.chem_temp]K" : null
|
||||
var/area/fluid_area = get_area(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) && !(fluid_area.area_flags & QUIET_LOGS)) // I like to be able to see my logs thank you
|
||||
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) && !(fluid_area.area_flags & QUIET_LOGS)) // Deathmatch has way too much smoke to log
|
||||
// Some reagents don't have a my_atom in some cases
|
||||
if(!carry.my_atom?.fingerprintslast)
|
||||
// Deathmatch has way too much smoke to log
|
||||
if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS))
|
||||
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.")
|
||||
return
|
||||
|
||||
var/mob/bomber = get_mob_by_key(carry.my_atom.fingerprintslast)
|
||||
if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) // I like to be able to see my logs thank you
|
||||
message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [bomber ? "[ADMIN_LOOKUPFLW(bomber)] " : carry.my_atom.fingerprintslast].")
|
||||
log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].")
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/Destroy()
|
||||
QDEL_NULL(chemholder)
|
||||
return ..()
|
||||
|
||||
/datum/effect_system/fluid_spread/smoke/chem/start(log = FALSE)
|
||||
var/start_loc = holder ? get_turf(holder) : src.location
|
||||
|
||||
Reference in New Issue
Block a user