Files
Bubberstation/code/game/objects/effects/shared_particle_holder.dm
SmArtKar cf6dbf06aa Converts hot springs to use proper shared particles (#90359)
## About The Pull Request

Converts hot spring turfs to shared particles instead of pseudo-cached
per-turf holders. This does come with some ugliness in
add/remove_shared_particles code due to /area not having vis_contents,
but that code shouldn't be touched by most developers who don't know
what's going on anyways.

I'm not sure if the "cached" particles even worked in practice, and if
they had any clientside perf improvements.

## Why It's Good For The Game

Better perf, 

## Changelog
🆑
code: Converted hot springs to a new shared particles system for better
clientside performance.
/🆑
2025-04-29 17:15:52 -06:00

101 lines
4.8 KiB
Plaintext

#define SHARED_PARTICLE_HOLDER_INDEX 1
#define SHARED_PARTICLE_USER_NUM_INDEX 2
// Assoc list of particle type/key -> list(list of particle holders, number of particle users)
GLOBAL_LIST_EMPTY(shared_particles)
//A more abstract version of particle holder not bound to a specific object
/obj/effect/abstract/shared_particle_holder
name = "shared particle holder"
desc = "How are you reading this? Please make a bug report :)"
appearance_flags = KEEP_APART|KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE|RESET_COLOR
vis_flags = VIS_INHERIT_PLANE
layer = ABOVE_ALL_MOB_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
/// Holds info about how this particle emitter works
/// See \code\__DEFINES\particles.dm
var/particle_flags = NONE
/obj/effect/abstract/shared_particle_holder/Initialize(mapload, particle_path = /particles/smoke, particle_flags = NONE)
. = ..()
// Shouldn't exist outside of nullspace
loc = null
src.particle_flags = particle_flags
particles = new particle_path()
/obj/effect/abstract/shared_particle_holder/Destroy(force)
QDEL_NULL(particles)
return ..()
/* Adds (or creates and adds) a shared particle holder
* Shared particle holders are held in nullspace and added to vis_contents of all atoms using it
* in order to save clientside performance by making clients only render 3-5 particle holders
* for 400 objects using them. This should be prioritized over normal particles when possible if it is known
* that there will be a lot of objects using certain particles.
* custom_key can be used to create a new pool of already existing particle type in case you're planning to edit holder's color or properties
* pool_size controls how many particle holders per type are created. Any objects over this cap will pick an existing holder from the pool.
*
* Now, this code seems fucked up, that's because this is meant to support both objects (and mobs) and turfs, *however* areas are special
* and don't have vis_contents, so to avoid copypaste code we do this weirdness
*/
/atom/proc/add_shared_particles(particle_type, custom_key = null, particle_flags = NONE, pool_size = 3)
var/atom/movable/play_pretend = src
var/particle_key = custom_key || "[particle_type]"
if (!GLOB.shared_particles[particle_key])
GLOB.shared_particles[particle_key] = list(list(new /obj/effect/abstract/shared_particle_holder(null, particle_type, particle_flags)), 1)
play_pretend.vis_contents += GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX][1]
return GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX][1]
var/list/type_holders = GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX]
for (var/obj/effect/abstract/shared_particle_holder/particle_holder as anything in type_holders)
if (particle_holder in play_pretend.vis_contents)
return particle_holder
if (length(type_holders) < pool_size)
var/obj/effect/abstract/shared_particle_holder/new_holder = new(null, particle_type, particle_flags)
type_holders += new_holder
play_pretend.vis_contents += new_holder
GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] += 1
return new_holder
var/obj/effect/abstract/shared_particle_holder/particle_holder = pick(type_holders)
play_pretend.vis_contents += particle_holder
GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] += 1
return particle_holder
/area/add_shared_particles(particle_type, custom_key = null, particle_flags = NONE, pool_size = 3)
CRASH("add_shared_particles was called on an area [src] ([type]) trying to add [particle_type]! Only turfs and movables support shared particles.")
/* Removes shared particles from object's vis_contents and disposes of it if nothing uses that type/key of particle
* particle_key can be either a type (if no custom_key was passed) or said custom_key
*/
/atom/proc/remove_shared_particles(particle_key, delete_on_empty = TRUE)
if (!particle_key)
return
if (ispath(particle_key))
particle_key = "[particle_key]"
if (!GLOB.shared_particles[particle_key])
return
var/atom/movable/play_pretend = src
var/list/type_holders = GLOB.shared_particles[particle_key][SHARED_PARTICLE_HOLDER_INDEX]
for (var/obj/effect/abstract/shared_particle_holder/particle_holder as anything in type_holders)
if (!(particle_holder in play_pretend.vis_contents))
continue
play_pretend.vis_contents -= particle_holder
GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] -= 1
if (delete_on_empty && GLOB.shared_particles[particle_key][SHARED_PARTICLE_USER_NUM_INDEX] <= 0)
QDEL_LIST(type_holders)
GLOB.shared_particles -= particle_key
return
/area/remove_shared_particles(particle_key, delete_on_empty = TRUE)
CRASH("remove_shared_particles was called on an area [src] ([type]) trying to add [particle_key]! Only turfs and movables support shared particles.")
#undef SHARED_PARTICLE_HOLDER_INDEX
#undef SHARED_PARTICLE_USER_NUM_INDEX