From d50ea14baaf6d67bf1f0bc51f4e567b4f9508efc Mon Sep 17 00:00:00 2001 From: Letter N <24603524+LetterN@users.noreply.github.com> Date: Wed, 28 Jan 2026 01:44:16 +0800 Subject: [PATCH] effect cleanup (#7502) yeah, also updates emissive blockers --- citadel.dme | 8 +- code/__DEFINES/_flags/particles_flags.dm | 5 + code/__DEFINES/is_helpers.dm | 1 + code/__DEFINES/qdel.dm | 1 + code/__DEFINES/rendering/lighting.dm | 6 +- code/__HELPERS/generators.dm | 21 ++++ code/controllers/verbs.dm | 14 ++- code/game/atoms/movable/movable.dm | 48 ++++++-- code/game/machinery/computer/computer.dm | 2 +- code/game/objects/effects/_effect.dm | 26 ++--- code/game/objects/effects/bump_teleporter.dm | 64 ++++++---- code/game/objects/effects/countdown.dm | 107 +++++++++++++++++ .../effects/effect_system/effect_system.dm | 32 +++++ .../effects_other.dm} | 110 ------------------ .../effects/effect_system/effects_sparks.dm | 87 ++++++++++++++ code/game/objects/effects/gibs.dm | 58 --------- code/game/objects/effects/overlays.dm | 7 +- code/game/objects/effects/particle_holder.dm | 97 ++++++++------- code/game/objects/effects/particles/smoke.dm | 45 +++++++ code/game/objects/effects/portals.dm | 83 ++++++++----- .../objects/effects/spawners/gibspawner.dm | 61 ++++++++++ .../effects/temporary_visuals/point.dm | 2 - .../projectiles/projectile_effects.dm | 5 +- .../effects/temporary_visuals/temp_visual.dm | 27 +++-- .../atmospherics/machinery/air_alarm.dm | 2 +- code/modules/power/supermatter/supermatter.dm | 27 +++-- 26 files changed, 622 insertions(+), 324 deletions(-) create mode 100644 code/__DEFINES/_flags/particles_flags.dm create mode 100644 code/__HELPERS/generators.dm create mode 100644 code/game/objects/effects/countdown.dm create mode 100644 code/game/objects/effects/effect_system/effect_system.dm rename code/game/objects/effects/{effect_system.dm => effect_system/effects_other.dm} (79%) create mode 100644 code/game/objects/effects/effect_system/effects_sparks.dm delete mode 100644 code/game/objects/effects/gibs.dm diff --git a/citadel.dme b/citadel.dme index c036ab5e4e4..21f4d74198a 100644 --- a/citadel.dme +++ b/citadel.dme @@ -124,6 +124,7 @@ #include "code\__DEFINES\_flags\interaction_flags.dm" #include "code\__DEFINES\_flags\item_flags.dm" #include "code\__DEFINES\_flags\obj_flags.dm" +#include "code\__DEFINES\_flags\particles_flags.dm" #include "code\__DEFINES\_flags\turf_flags.dm" #include "code\__DEFINES\admin\admin.dm" #include "code\__DEFINES\admin\bans.dm" @@ -413,6 +414,7 @@ #include "code\__HELPERS\do_after.dm" #include "code\__HELPERS\events.dm" #include "code\__HELPERS\game.dm" +#include "code\__HELPERS\generators.dm" #include "code\__HELPERS\global_lists.dm" #include "code\__HELPERS\guid.dm" #include "code\__HELPERS\heap.dm" @@ -1737,9 +1739,8 @@ #include "code\game\objects\donator_rewards\toys.dm" #include "code\game\objects\effects\_effect.dm" #include "code\game\objects\effects\bump_teleporter.dm" -#include "code\game\objects\effects\effect_system.dm" +#include "code\game\objects\effects\countdown.dm" #include "code\game\objects\effects\explosion_particles.dm" -#include "code\game\objects\effects\gibs.dm" #include "code\game\objects\effects\item_pickup_ghost.dm" #include "code\game\objects\effects\manifest.dm" #include "code\game\objects\effects\mines.dm" @@ -1770,6 +1771,9 @@ #include "code\game\objects\effects\decals\misc.dm" #include "code\game\objects\effects\decals\remains.dm" #include "code\game\objects\effects\decals\warning_stripes.dm" +#include "code\game\objects\effects\effect_system\effect_system.dm" +#include "code\game\objects\effects\effect_system\effects_other.dm" +#include "code\game\objects\effects\effect_system\effects_sparks.dm" #include "code\game\objects\effects\map_effects\beam_point.dm" #include "code\game\objects\effects\map_effects\effect_emitter.dm" #include "code\game\objects\effects\map_effects\map_effects.dm" diff --git a/code/__DEFINES/_flags/particles_flags.dm b/code/__DEFINES/_flags/particles_flags.dm new file mode 100644 index 00000000000..5657566a63b --- /dev/null +++ b/code/__DEFINES/_flags/particles_flags.dm @@ -0,0 +1,5 @@ +// /obj/effect/abstract/particle_holder/var/particle_flags +// Flags that effect how a particle holder displays something + +/// If we're inside something inside a mob, display off that mob too +#define PARTICLE_ATTACH_MOB (1<<0) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index ccfb7dd1814..53a9251ae89 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -74,6 +74,7 @@ GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur"))) #define is_reagent_container(O) (istype(O, /obj/item/reagent_containers)) +#define iseffect(O) (istype(O, /obj/effect)) //Areas //Mobs diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index c1b5a591da5..5880a5dae35 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -61,6 +61,7 @@ /// qdel something in a specific amount of time. returns a timer ID. #define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE) +#define QDEL_IN_STOPPABLE(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time, TIMER_STOPPABLE) /// qdel something in a specific amount of real (wall) time. returns a timer ID. #define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) /// qdel's something and nulls it out diff --git a/code/__DEFINES/rendering/lighting.dm b/code/__DEFINES/rendering/lighting.dm index 07d2351c932..68ac9f04913 100644 --- a/code/__DEFINES/rendering/lighting.dm +++ b/code/__DEFINES/rendering/lighting.dm @@ -121,9 +121,11 @@ //! Emissive blocking. /// Uses vis_overlays to leverage caching so that very few new items need to be made for the overlay. For anything that doesn't change outline or opaque area much or at all. -#define EMISSIVE_BLOCK_GENERIC 1 +#define EMISSIVE_BLOCK_GENERIC 0 /// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans. -#define EMISSIVE_BLOCK_UNIQUE 2 +#define EMISSIVE_BLOCK_UNIQUE 1 +/// Don't block any emissives. Useful for things like, pieces of paper? +#define EMISSIVE_BLOCK_NONE 2 /// The color matrix applied to binary emissive overlays. Should be solely dependent on alpha and not have RGB overlap with [EM_BLOCK_COLOR]. #define EMISSIVE_COLOR list(0,0,0,0, 0,0,0,0, 0,0,0,0, 1,1,1,1, 0,0,0,0) diff --git a/code/__HELPERS/generators.dm b/code/__HELPERS/generators.dm new file mode 100644 index 00000000000..7eecc866f85 --- /dev/null +++ b/code/__HELPERS/generators.dm @@ -0,0 +1,21 @@ +/** + * returns the arguments given to a generator and manually extracts them from the internal byond object + * returns: + * * flat list of strings for args given to the generator. + * * Note: this means things like "list(1,2,3)" will need to be processed + */ +/proc/return_generator_args(generator/target) + var/string_repr = "[target]" //the name of the generator is the string representation of its _binobj, which also contains its args + string_repr = copytext(string_repr, 11, length(string_repr)) // strips extraneous data + string_repr = replacetext(string_repr, "\"", "") // removes the " around the type + return splittext(string_repr, ", ") + +/generator/proc/RandList() + var/possible_vector = Rand() + var/vector_length = length(possible_vector) + if(vector_length == 0) + return possible_vector + . = list() + for(var/i in 1 to vector_length) + . += possible_vector[i] + return . diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 421c2fc318e..bf1a8366fe6 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,9 +1,7 @@ -//TODO: rewrite and standardise all controller datums to the datum/controller type -//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done - // Clickable stat() button. /obj/effect/statclick name = "Initializing..." + blocks_emissive = EMISSIVE_BLOCK_NONE icon = null var/target @@ -13,16 +11,20 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick) . = ..() name = text src.target = target + if(isdatum(target)) //Harddel man bad + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(cleanup)) /obj/effect/statclick/Destroy() target = null return ..() +/obj/effect/statclick/proc/cleanup() + SIGNAL_HANDLER + qdel(src) + /obj/effect/statclick/proc/update(text) - if(name == text) - return src // let's ont change for no reason shall we name = text - return name + return src /obj/effect/statclick/statpanel_click(client/C, action) Click() diff --git a/code/game/atoms/movable/movable.dm b/code/game/atoms/movable/movable.dm index 03880dd2636..c9c87468cbd 100644 --- a/code/game/atoms/movable/movable.dm +++ b/code/game/atoms/movable/movable.dm @@ -152,8 +152,8 @@ var/throw_speed_scaling_exponential = THROW_SPEED_SCALING_CONSTANT_DEFAULT //? Emissives - /// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] - var/blocks_emissive = FALSE + /// Either [EMISSIVE_BLOCK_NONE], [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE] + var/blocks_emissive = EMISSIVE_BLOCK_NONE /// Internal holder for emissive blocker object, do not use directly use; use blocks_emissive /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/emissive_blocker/em_block @@ -183,6 +183,14 @@ loc = null return ..() +/mutable_appearance/emissive_blocker + +/mutable_appearance/emissive_blocker/New() + . = ..() + // Need to do this here because it's overridden by the parent call + // This is a microop which is the sole reason why this child exists, because its static this is a really cheap way to set color without setting or checking it every time we create an atom + color = EM_BLOCK_COLOR + /atom/movable/Initialize(mapload) . = ..() // WARNING WARNING SHITCODE THIS MEANS THAT ONLY TURFS RECEIVE MAPLOAD ENTERED @@ -190,17 +198,37 @@ // TODO: what would tg do (but maybe not that much component signal abuse?) if(!mapload) loc?.Entered(src, null) - switch(blocks_emissive) - if(EMISSIVE_BLOCK_GENERIC) - var/mutable_appearance/gen_emissive_blocker = mutable_appearance(icon, icon_state, plane = EMISSIVE_PLANE, alpha = src.alpha) - gen_emissive_blocker.color = GLOB.em_block_color - gen_emissive_blocker.dir = dir - gen_emissive_blocker.appearance_flags |= appearance_flags - add_overlay(gen_emissive_blocker) - if(EMISSIVE_BLOCK_UNIQUE) + +#if EMISSIVE_BLOCK_GENERIC != 0 + #error EMISSIVE_BLOCK_GENERIC is expected to be 0 to facilitate a weird optimization hack where we rely on it being the most common. + #error Read the comment in code/game/atoms_movable.dm for details. +#endif + + // This one is incredible. + // `if (x) else { /* code */ }` is surprisingly fast, and it's faster than a switch, which is seemingly not a jump table. + // From what I can tell, a switch case checks every single branch individually, although sane, is slow in a hot proc like this. + // So, we make the most common `blocks_emissive` value, EMISSIVE_BLOCK_GENERIC, 0, getting to the fast else branch quickly. + // If it fails, then we can check over every value it can be (here, EMISSIVE_BLOCK_UNIQUE is the only one that matters). + // This saves several hundred milliseconds of init time. + if (blocks_emissive) + if (blocks_emissive == EMISSIVE_BLOCK_UNIQUE) add_emissive_blocker() + else + var/static/mutable_appearance/emissive_blocker/blocker = new() + blocker.icon = icon + blocker.icon_state = icon_state + blocker.dir = dir + blocker.appearance_flags = appearance_flags // | EMISSIVE_APPEARANCE_FLAGS + blocker.plane = EMISSIVE_PLANE + // Ok so this is really cursed, but I want to set with this blocker cheaply while + // Still allowing it to be removed from the overlays list later + // So I'm gonna flatten it, then insert the flattened overlay into overlays AND the managed overlays list, directly + // I'm sorry + var/mutable_appearance/flat = blocker.appearance + add_overlay(flat) /atom/movable/Destroy(force) + QDEL_NULL(em_block) if(reagents) QDEL_NULL(reagents) unbuckle_all_mobs(BUCKLE_OP_FORCE) diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 5b436bcf264..7846e51504f 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -20,7 +20,7 @@ depth_level = 8 depth_projected = TRUE climb_allowed = TRUE - //blocks_emissive = FALSE + //blocks_emissive = EMISSIVE_BLOCK_NONE var/processing = FALSE var/icon_keyboard = "generic_key" diff --git a/code/game/objects/effects/_effect.dm b/code/game/objects/effects/_effect.dm index 14ae0396a50..a5b922f1ffb 100644 --- a/code/game/objects/effects/_effect.dm +++ b/code/game/objects/effects/_effect.dm @@ -1,25 +1,20 @@ /** - * effect supertype - * - * currently does nothing - * - * "weakly interactive visual objects" should usually go under this, especially if they're temporary/short lasting + * Effects are mostly temporary visual effects like sparks, smoke, as well as decals, etc... * * these, however, have full object damgae/interaction support, so it's not limited to those * * however, at a certain point, do consider using /structure or /machinery instead. */ /obj/effect - anchored = TRUE - integrity_enabled = FALSE - density = FALSE - opacity = FALSE - icon = 'icons/effects/effects.dmi' + integrity_flags = INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF | INTEGRITY_INDESTRUCTIBLE move_resist = INFINITY obj_flags = NONE + blocks_emissive = EMISSIVE_BLOCK_GENERIC + integrity_enabled = FALSE + + anchored = TRUE vis_flags = VIS_INHERIT_PLANE - // blocks_emissive = EMI /obj/effect/fire_act() return @@ -30,22 +25,25 @@ /obj/effect/blob_act(obj/structure/blob/B) return +/obj/effect/legacy_ex_act(severity, target) + return FALSE + /obj/effect/singularity_act() qdel(src) /// The abstract effect ignores even more effects and is often typechecked for atoms that should truly not be fucked with. /obj/effect/abstract -/obj/effect/abstract/singularity_pull() +/obj/effect/abstract/singularity_pull(atom/singularity, current_size) return /obj/effect/abstract/singularity_act() return -/obj/effect/abstract/has_gravity(turf/T) +/obj/effect/abstract/has_gravity(turf/gravity_turf) return FALSE -/obj/effect/dummy/singularity_pull() +/obj/effect/dummy/singularity_pull(atom/singularity, current_size) return /obj/effect/dummy/singularity_act() diff --git a/code/game/objects/effects/bump_teleporter.dm b/code/game/objects/effects/bump_teleporter.dm index 8237a54dd14..cf7c3ce6c87 100644 --- a/code/game/objects/effects/bump_teleporter.dm +++ b/code/game/objects/effects/bump_teleporter.dm @@ -1,34 +1,58 @@ -var/list/obj/effect/bump_teleporter/BUMP_TELEPORTERS = list() - +/// Abstract effect, that when a mob touches it, it will forceMove them to the teleporter-exit point (that matches the ID set map-side). /obj/effect/bump_teleporter - name = "bump-teleporter" + name = "bump teleporter (forceMove)" + desc = "Use me when you want to move every single mob without any exceptions." icon = 'icons/mob/screen1.dmi' icon_state = "x2" - var/id = null //id of this bump_teleporter. - var/id_target = null //id of bump_teleporter which this moves you to. - invisibility = 101 //nope, can't see this - anchored = 1 - density = 1 - opacity = 0 + invisibility = INVISIBILITY_ABSTRACT //nope, can't see this + anchored = TRUE + density = TRUE + opacity = FALSE + /// id of this bump_teleporter. + var/id = null + /// id of bump_teleporter which this moves you to. + var/id_target = null + /// List of all teleporters in the world. + var/static/list/AllTeleporters /obj/effect/bump_teleporter/Initialize(mapload) . = ..() - BUMP_TELEPORTERS += src + LAZYADD(AllTeleporters, src) /obj/effect/bump_teleporter/Destroy() - BUMP_TELEPORTERS -= src + LAZYREMOVE(AllTeleporters, src) return ..() -/obj/effect/bump_teleporter/Bumped(atom/user) - if(!ismob(user)) - //user.loc = src.loc //Stop at teleporter location +/obj/effect/bump_teleporter/singularity_act() + return + +/obj/effect/bump_teleporter/singularity_pull(atom/singularity, current_size) + return + +/obj/effect/bump_teleporter/Bumped(atom/movable/bumper) + if(!validate_setup(bumper)) return + for(var/obj/effect/bump_teleporter/teleporter in AllTeleporters) + if(teleporter.id == id_target) + teleport_action(bumper, get_turf(teleporter)) //Teleport to location with correct id. + return + + stack_trace("Bump_teleporter [src] could not find a teleporter with id [id_target]!") + +/// Check to see if our teleporter was set up correctly mapside. Return TRUE if everything is fine, FALSE if not. +/obj/effect/bump_teleporter/proc/validate_setup(atom/movable/checkable) + if(!ismob(checkable)) + return FALSE + if(!id_target) - //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. - return + var/message = "Bump teleporter [src] at [AREACOORD(src)] has no id_target set." + stack_trace(message) + log_mapping(message) + return FALSE - for(var/obj/effect/bump_teleporter/BT in BUMP_TELEPORTERS) - if(BT.id == src.id_target) - usr.loc = BT.loc //Teleport to location with correct id. - return + return TRUE + +/// Actually move our target atom from one position to another. Return TRUE if everything is fine. Override this proc on subtypes for specific teleportation methods. +/obj/effect/bump_teleporter/proc/teleport_action(atom/movable/target, turf/destination) + target.locationTransitForceMove(destination) diff --git a/code/game/objects/effects/countdown.dm b/code/game/objects/effects/countdown.dm new file mode 100644 index 00000000000..bc480126a1c --- /dev/null +++ b/code/game/objects/effects/countdown.dm @@ -0,0 +1,107 @@ +/obj/effect/countdown + name = "countdown" + desc = "We're leaving together\n\ + But still it's farewell\n\ + And maybe we'll come back\n\ + To Earth, who can tell?" + + invisibility = INVISIBILITY_OBSERVER + anchored = TRUE + plane = OBSERVER_PLANE + color = "#ff0000" // text color + var/text_size = 3 // larger values clip when the displayed text is larger than 2 digits. + var/started = FALSE + var/displayed_text + var/atom/attached_to + +/obj/effect/countdown/Initialize(mapload) + . = ..() + attach(loc) + +/obj/effect/countdown/examine(mob/user) + . = ..() + . += "This countdown is displaying: [displayed_text]." + +/obj/effect/countdown/proc/attach(atom/A) + attached_to = A + var/turf/loc_turf = get_turf(A) + if(!loc_turf) + RegisterSignal(attached_to, COMSIG_MOVABLE_MOVED, PROC_REF(retry_attach), TRUE) + else + forceMove(loc_turf) + +/obj/effect/countdown/proc/retry_attach() + SIGNAL_HANDLER + + var/turf/loc_turf = get_turf(attached_to) + if(!loc_turf) + return + forceMove(loc_turf) + UnregisterSignal(attached_to, COMSIG_MOVABLE_MOVED) + +/obj/effect/countdown/proc/start() + if(!started) + START_PROCESSING(SSprocess_5fps, src) + started = TRUE + +/obj/effect/countdown/proc/stop() + if(started) + maptext = null + STOP_PROCESSING(SSprocess_5fps, src) + started = FALSE + +/obj/effect/countdown/proc/get_value() + // Get the value from our atom + return + +/obj/effect/countdown/process() + if(!attached_to || QDELETED(attached_to)) + qdel(src) + forceMove(get_turf(attached_to)) + var/new_val = get_value() + if(new_val == displayed_text) + return + displayed_text = new_val + + if(displayed_text) + maptext = MAPTEXT("[displayed_text]") + else + maptext = null + +/obj/effect/countdown/Destroy() + attached_to = null + STOP_PROCESSING(SSprocess_5fps, src) + . = ..() + +/obj/effect/countdown/singularity_pull(atom/singularity, current_size) + return + +/obj/effect/countdown/singularity_act() + return + +/obj/effect/countdown/nuclearbomb + name = "nuclear bomb countdown" + color = "#81FF14" + +// /obj/effect/countdown/nuclearbomb/get_value() +// var/obj/machinery/nuclearbomb/N = attached_to +// if(!istype(N)) +// return +// else if(N.timing) +// return round(N.get_time_left(), 1) + +/obj/effect/countdown/supermatter + name = "supermatter damage" + color = "#00ff80" + pixel_y = 8 + +/obj/effect/countdown/supermatter/attach(atom/A) + . = ..() + if(istype(A, /obj/machinery/power/supermatter)) + pixel_y = -12 + +/obj/effect/countdown/supermatter/get_value() + var/obj/machinery/power/supermatter/S = attached_to + if(!istype(S)) + return + return "