effect cleanup (#7502)

yeah, also updates emissive blockers
This commit is contained in:
Letter N
2026-01-27 12:44:16 -05:00
committed by GitHub
parent fcdae04ae0
commit d50ea14baa
26 changed files with 622 additions and 324 deletions
+12 -14
View File
@@ -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()
+44 -20
View File
@@ -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)
+107
View File
@@ -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 "<div align='center' valign='bottom' style='position:relative; top:0px; left:0px'>[round(S.get_integrity())]%</div>"
@@ -0,0 +1,32 @@
/obj/effect/particle_effect
name = "particle effect"
pass_flags = ATOM_PASS_TABLE | ATOM_PASS_GRILLE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
/datum/effect_system
var/number = 3
var/cardinals = 0
var/turf/location
var/atom/holder
var/setup = 0
/datum/effect_system/Destroy()
holder = null
location = null
return..()
/datum/effect_system/proc/set_up(n = 3, c = 0, turf/loc)
if(n > 10)
n = 10
number = n
cardinals = c
location = loc
setup = 1
/datum/effect_system/proc/attach(atom/atom)
holder = atom
/datum/effect_system/proc/start()
@@ -1,44 +1,3 @@
/* 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.
*/
/obj/effect/particle_effect
name = "effect"
icon = 'icons/effects/effects.dmi'
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
integrity_flags = INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF
pass_flags = ATOM_PASS_TABLE | ATOM_PASS_GRILLE
/datum/effect_system
var/number = 3
var/cardinals = 0
var/turf/location
var/atom/holder
var/setup = 0
/datum/effect_system/Destroy()
holder = null
location = null
return..()
/datum/effect_system/proc/set_up(n = 3, c = 0, turf/loc)
if(n > 10)
n = 10
number = n
cardinals = c
location = loc
setup = 1
/datum/effect_system/proc/attach(atom/atom)
holder = atom
/datum/effect_system/proc/start()
/////////////////////////////////////////////
// GENERIC STEAM SPREAD SYSTEM
@@ -87,76 +46,7 @@ steam.start() -- spawns the effect
spawn(20)
qdel(steam)
/////////////////////////////////////////////
//SPARK SYSTEM (like steam system)
// The attach(atom/atom) proc is optional, and can be called to attach the effect
// to something, like the RCD, so then you can just call start() and the sparks
// will always spawn at the items location.
/////////////////////////////////////////////
/obj/effect/particle_effect/sparks
name = "sparks"
icon = 'icons/effects/effects.dmi'
icon_state = "sparks"
var/amount = 6.0
anchored = 1.0
mouse_opacity = 0
/obj/effect/particle_effect/sparks/Initialize(mapload)
. = ..()
playsound(src, /datum/soundbyte/sparks, 100, 1)
var/turf/T = src.loc
if (istype(T, /turf))
T.hotspot_expose(1000,100)
QDEL_IN(src, 5 SECONDS)
/obj/effect/particle_effect/sparks/Destroy()
var/turf/T = src.loc
if (istype(T, /turf))
T.hotspot_expose(1000,100)
return ..()
/obj/effect/particle_effect/sparks/Move()
..()
var/turf/T = src.loc
if (istype(T, /turf))
T.hotspot_expose(1000,100)
return
/datum/effect_system/spark_spread
var/total_sparks = 0 // To stop it being spammed and lagging!
/datum/effect_system/spark_spread/set_up(n = 3, c = 0, loca)
if(n > 10)
n = 10
number = n
cardinals = c
if(istype(loca, /turf/))
location = loca
else
location = get_turf(loca)
/datum/effect_system/spark_spread/start()
var/i = 0
for(i=0, i<src.number, i++)
if(src.total_sparks > 20)
return
spawn(0)
if(holder)
src.location = get_turf(holder)
var/obj/effect/particle_effect/sparks/sparks = new /obj/effect/particle_effect/sparks(src.location)
src.total_sparks++
var/direction
if(src.cardinals)
direction = pick(GLOB.cardinal)
else
direction = pick(GLOB.alldirs)
for(i=0, i<pick(1,2,3), i++)
sleep(5)
if(isloc(sparks.loc) && !QDELETED(sparks))
step(sparks,direction)
spawn(20)
src.total_sparks--
@@ -0,0 +1,87 @@
/////////////////////////////////////////////
//SPARK SYSTEM (like steam system)
// The attach(atom/atom) proc is optional, and can be called to attach the effect
// to something, like the RCD, so then you can just call start() and the sparks
// will always spawn at the items location.
/////////////////////////////////////////////
/obj/effect/particle_effect/sparks
name = "sparks"
icon_state = "sparks"
anchored = TRUE
light_range = 1.5
light_power = 0.8
light_color = LIGHT_COLOR_FIRE
var/amount = 6.0
/obj/effect/particle_effect/sparks/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
/obj/effect/particle_effect/sparks/LateInitialize()
flick(icon_state, src)
playsound(src, /datum/soundbyte/sparks, 100, 1)
var/turf/location = loc
if(isturf(location))
affect_location(location, just_initialized = TRUE)
QDEL_IN(src, 2 SECONDS)
/obj/effect/particle_effect/sparks/Destroy()
var/turf/location = loc
if(isturf(location))
affect_location(location)
return ..()
/obj/effect/particle_effect/sparks/Move()
..()
var/turf/location = loc
if(isturf(location))
affect_location(location)
/*
* Apply the effects of this spark to its location.
*
* When the spark is first created, Cross() and Crossed() don't get called,
* so for the first initialization, we make sure to specifically invoke the
* behavior of the spark on all the mobs and objects in the location.
* turf/location - The place the spark is affectiong
* 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)
// SEND_SIGNAL(location, COMSIG_ATOM_TOUCHED_SPARKS, src) // for plasma floors; other floor types only have to worry about the mysterious HAZARDOUS sparks
/datum/effect_system/spark_spread
var/total_sparks = 0 // To stop it being spammed and lagging!
/datum/effect_system/spark_spread/set_up(n = 3, c = 0, loca)
if(n > 10)
n = 10
number = n
cardinals = c
if(istype(loca, /turf/))
location = loca
else
location = get_turf(loca)
/datum/effect_system/spark_spread/start()
var/i = 0
for(i=0, i<src.number, i++)
if(src.total_sparks > 20)
return
spawn(0)
if(holder)
src.location = get_turf(holder)
var/obj/effect/particle_effect/sparks/sparks = new /obj/effect/particle_effect/sparks(src.location)
src.total_sparks++
var/direction
if(src.cardinals)
direction = pick(GLOB.cardinal)
else
direction = pick(GLOB.alldirs)
for(i=0, i<pick(1,2,3), i++)
sleep(5)
if(isloc(sparks.loc) && !QDELETED(sparks))
step(sparks,direction)
spawn(20)
src.total_sparks--
-58
View File
@@ -1,58 +0,0 @@
/proc/gibs(atom/location, var/datum/dna/MobDNA, gibber_type = /obj/effect/gibspawner/generic, var/fleshcolor, var/bloodcolor)
new gibber_type(location,MobDNA,fleshcolor,bloodcolor)
/obj/effect/gibspawner
var/sparks = 0 //whether sparks spread on Gib()
var/list/gibtypes = list()
var/list/gibamounts = list()
var/list/gibdirections = list() //of lists
var/fleshcolor //Used for gibbed humans.
var/bloodcolor //Used for gibbed humans.
/obj/effect/gibspawner/Initialize(mapload, datum/dna/MobDNA, fleshcolor, bloodcolor)
..()
if(fleshcolor)
src.fleshcolor = fleshcolor
if(bloodcolor)
src.bloodcolor = bloodcolor
Gib(loc,MobDNA)
return INITIALIZE_HINT_QDEL
/obj/effect/gibspawner/proc/Gib(atom/location, datum/dna/MobDNA = null)
if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len)
to_chat(world, SPAN_WARNING("Gib list length mismatch!"))
return
var/obj/effect/debris/cleanable/blood/gibs/gib = null
if(sparks)
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread()
s.set_up(2, 1, get_turf(location)) // Not sure if it's safe to pass an arbitrary object to set_up, todo
s.start()
for(var/i = 1, i<= gibtypes.len, i++)
if(gibamounts[i])
for(var/j = 1, j<= gibamounts[i], j++)
var/gibType = gibtypes[i]
gib = new gibType(location)
// Apply human species colouration to masks.
if(fleshcolor)
gib.fleshcolor = fleshcolor
if(bloodcolor)
gib.basecolor = bloodcolor
gib.update_icon()
gib.blood_DNA = list()
if(MobDNA)
gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type
else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey
gib.blood_DNA["Non-human DNA"] = "A+"
if(istype(location,/turf/))
var/list/directions = gibdirections[i]
if(directions.len)
gib.streak(directions)
qdel(src)
+6 -1
View File
@@ -1,6 +1,11 @@
/obj/effect/overlay
name = "overlay"
integrity_flags = INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF | INTEGRITY_INDESTRUCTIBLE
/obj/effect/overlay/singularity_act()
return
/obj/effect/overlay/singularity_pull(atom/singularity, current_size)
return
/obj/effect/overlay/palmtree_r
name = "Palm tree"
+53 -44
View File
@@ -3,10 +3,20 @@
* You know, so multiple particle effects can exist at once. Also because some objects do not display particles due to how their visuals are built.
*/
/obj/effect/abstract/particle_holder
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = ABOVE_MOB_LAYER
name = "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 //movable appearance_flags plus KEEP_APART and KEEP_TOGETHER
vis_flags = VIS_INHERIT_PLANE
layer = ABOVE_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
var/atom/parent
/// Typepath of the last location we're in, if it's different when moved then we need to update vis contents.
var/last_attached_location_type
/// The main item we're attached to at the moment, particle holders hold particles for something.
@@ -14,58 +24,57 @@
/// Besides the item we're also sometimes attached to other stuff! (items held emitting particles on a mob).
var/datum/weakref/weak_additional
/obj/effect/abstract/particle_holder/Initialize(mapload, particle_path = /particles/smoke)
/obj/effect/abstract/particle_holder/Initialize(mapload, particle_path = /particles/smoke, particle_flags = NONE)
. = ..()
if(!loc)
stack_trace("particle holder was created with no loc!")
return INITIALIZE_HINT_QDEL
if(ismovable(loc))
RegisterSignal(loc, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
RegisterSignal(loc, COMSIG_PARENT_QDELETING, PROC_REF(on_qdel))
weak_attached = WEAKREF(loc)
particles = new particle_path
update_visual_contents(loc)
// We nullspace ourselves because some objects use their contents (e.g. storage) and some items may drop everything in their contents on deconstruct.
parent = loc
loc = null
// Mouse opacity can get set to opaque by some objects when placed into the object's contents (storage containers).
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
src.particle_flags = particle_flags
particles = new particle_path()
// /atom doesn't have vis_contents, /turf and /atom/movable do
var/atom/movable/lie_about_areas = parent
lie_about_areas.vis_contents += src
RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(parent_deleted))
if(particle_flags & PARTICLE_ATTACH_MOB)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
on_move(parent, null, NORTH)
/obj/effect/abstract/particle_holder/Destroy(force)
var/atom/movable/attached = weak_attached.resolve()
var/atom/movable/additional_attached
if(weak_additional)
additional_attached = weak_additional.resolve()
if(attached)
attached.vis_contents -= src
UnregisterSignal(loc, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))
if(additional_attached)
additional_attached.vis_contents -= src
QDEL_NULL(particles)
parent = null
return ..()
/// Signal called when parent is moved.
/// Non movables don't delete contents on destroy, so we gotta do this
/obj/effect/abstract/particle_holder/proc/parent_deleted(datum/source)
SIGNAL_HANDLER
qdel(src)
/// signal called when a parent that's been hooked into this moves
/// does a variety of checks to ensure overrides work out properly
/obj/effect/abstract/particle_holder/proc/on_move(atom/movable/attached, atom/oldloc, direction)
SIGNAL_HANDLER
if(attached.loc.type != last_attached_location_type)
update_visual_contents(attached)
/// Signal called when parent is deleted.
/obj/effect/abstract/particle_holder/proc/on_qdel(atom/movable/attached, force)
SIGNAL_HANDLER
qdel(src)//our parent is gone and we need to be as well
if(!(particle_flags & PARTICLE_ATTACH_MOB))
return
/**
* Logic proc for particle holders, aka where they move.
* Subtypes of particle holders can override this for particles that should always be turf level or do special things when repositioning.
* This base subtype has some logic for items, as the loc of items becomes mobs very often hiding the particles.
*/
/obj/effect/abstract/particle_holder/proc/update_visual_contents(atom/movable/attached_to)
// Remove old.
if(weak_additional)
var/atom/movable/resolved_location = weak_additional.resolve()
if(resolved_location)
resolved_location.vis_contents -= src
// Add to new.
if(isitem(attached_to) && ismob(attached_to.loc)) //special case we want to also be emitting from the mob
var/mob/particle_mob = attached_to.loc
last_attached_location_type = attached_to.loc
weak_additional = WEAKREF(particle_mob)
//remove old
if(ismob(oldloc))
var/mob/particle_mob = oldloc
particle_mob.vis_contents -= src
// If we're sitting in a mob, we want to emit from it too, for vibes and shit
if(ismob(attached.loc))
var/mob/particle_mob = attached.loc
particle_mob.vis_contents += src
// Readd to ourselves.
attached_to.vis_contents |= src
/// Sets the particles position to the passed coordinates
/obj/effect/abstract/particle_holder/proc/set_particle_position(x = 0, y = 0, z = 0)
particles.position = list(x, y, z)
@@ -14,6 +14,14 @@
gravity = list(0, 0.95)
grow = 0.05
/particles/smoke/burning
position = list(0, 0, 0)
/particles/smoke/burning/small
spawning = 1
scale = list(0.8, 0.8)
velocity = list(0, 0.4, 0)
/particles/smoke/steam
icon_state = list("steam_1" = 1, "steam_2" = 1, "steam_3" = 2)
fade = 1.5 SECONDS
@@ -22,3 +30,40 @@
spawning = 1
velocity = list(0, 0.3, 0)
friction = 0.25
/particles/smoke/steam/bad
icon_state = list("steam_1" = 1, "smoke_1" = 1, "smoke_2" = 1, "smoke_3" = 1)
spawning = 2
velocity = list(0, 0.25, 0)
/particles/smoke/steam/mald
icon_state = list("steam_1" = 1, "steam_2" = 1, "steam_3" = 2)
velocity = list(0, 0.25, 0)
lifespan = 1 SECONDS
fade = 0.5 SECONDS
position = list(-1, 12, 0)
/particles/smoke/cig
icon_state = list("steam_1" = 2, "steam_2" = 1, "steam_3" = 1)
count = 1
spawning = 0.05 // used to pace it out roughly in time with breath ticks
position = list(-6, -2, 0)
gravity = list(0, 0.75, 0)
lifespan = 0.75 SECONDS
fade = 0.75 SECONDS
velocity = list(0, 0.2, 0)
scale = 0.5
grow = 0.01
friction = 0.5
color = "#d0d0d09d"
/particles/smoke/cig/big
icon_state = list("steam_1" = 1, "steam_2" = 2, "steam_3" = 2)
gravity = list(0, 0.5, 0)
velocity = list(0, 0.1, 0)
lifespan = 1 SECONDS
fade = 1 SECONDS
grow = 0.1
scale = 0.75
spawning = 1
friction = 0.75
+56 -27
View File
@@ -5,47 +5,76 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal)
desc = "Looks unstable. Best to test it with the clown."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "portal"
density = TRUE
anchored = TRUE
integrity_flags = INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF
density = TRUE // dense for receiving bumbs
light_range = 3
light_power = 1
light_color = COLOR_BLUE_LIGHT
var/obj/item/target = null
var/creator = null
/obj/effect/portal/Bumped(mob/M as mob|obj)
if(istype(M,/mob) && !(istype(M,/mob/living)))
return //do not send ghosts, zshadows, ai eyes, etc
teleport(M)
/obj/effect/portal/Move(newloc)
for(var/T in newloc)
if(istype(T, /obj/effect/portal))
return FALSE
return ..()
/obj/effect/portal/Crossed(AM as mob|obj)
// Prevents portals spawned by jaunter/handtele from floating into space when relocated to an adjacent tile.
/obj/effect/portal/newtonian_move()
return TRUE
/obj/effect/portal/attackby(obj/item/W, mob/user, list/modifiers)
if(user && Adjacent(user))
teleport(user)
return TRUE
/obj/effect/portal/Bumped(atom/movable/bumper)
teleport(bumper)
/obj/effect/portal/attack_hand(mob/user,)
. = ..()
teleport(AM)
/obj/effect/portal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args)
if(istype(user) && !(istype(user,/mob/living)))
return //do not send ghosts, zshadows, ai eyes, etc
spawn(0)
src.teleport(user)
if(.)
return
return
if(Adjacent(user))
teleport(user)
/obj/effect/portal/attack_robot(mob/living/user)
if(Adjacent(user))
teleport(user)
/obj/effect/portal/Initialize(mapload, ...)
. = ..()
QDEL_IN(src, 30 SECONDS)
/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj)
if(istype(M, /obj/effect)) //sparks don't teleport
/obj/effect/portal/singularity_pull(atom/singularity, current_size)
return
/obj/effect/portal/singularity_act()
return
/obj/effect/portal/Destroy()
target = null
creator = null
return ..()
/obj/effect/portal/attack_ghost(mob/observer/dead/ghost)
if(!teleport(ghost, force = TRUE))
return ..()
return TRUE
/obj/effect/portal/proc/teleport(atom/movable/moving, force = FALSE)
if(!force && (!istype(moving) || iseffect(moving)|| (!isobj(moving) && !ismob(moving)))) //Things that shouldn't teleport.
return
if (M.anchored&&istype(M, /obj/vehicle/sealed/mecha))
var/turf/real_target = get_turf(target)
if(!istype(real_target))
return FALSE
if(!force && (!ismecha(moving) && moving.anchored))
return
if (icon_state == "portal1")
return
if (!( target ))
qdel(src)
return
if (istype(M, /atom/movable))
do_teleport(M, target, 1) ///You will appear adjacent to the beacon
/obj/effect/portal/attack_ghost(mob/user)
. = ..()
if(target)
user.forceMove(get_turf(target))
do_teleport(moving, real_target, 1) ///You will appear adjacent to the beacon
@@ -1,3 +1,64 @@
/proc/gibs(atom/location, var/datum/dna/MobDNA, gibber_type = /obj/effect/gibspawner/generic, var/fleshcolor, var/bloodcolor)
new gibber_type(location,MobDNA,fleshcolor,bloodcolor)
/obj/effect/gibspawner
icon_state = "gibspawner"// For the map editor
var/list/gibtypes = list() // Assoc list of typepaths of the gib decals to spawn to amount to spawn
var/list/gibdirections = list() // Lists of possible directions to spread each gib decal type towards.
var/sparks = 0 //whether sparks spread on Gib()
var/list/gibamounts = list()
var/fleshcolor //Used for gibbed humans.
var/bloodcolor //Used for gibbed humans.
/obj/effect/gibspawner/Initialize(mapload, datum/dna/MobDNA, fleshcolor, bloodcolor)
..()
if(fleshcolor)
src.fleshcolor = fleshcolor
if(bloodcolor)
src.bloodcolor = bloodcolor
Gib(loc,MobDNA)
return INITIALIZE_HINT_QDEL
/obj/effect/gibspawner/proc/Gib(atom/location, datum/dna/MobDNA = null)
if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len)
to_chat(world, SPAN_WARNING("Gib list length mismatch!"))
return
var/obj/effect/debris/cleanable/blood/gibs/gib = null
if(sparks)
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread()
s.set_up(2, 1, get_turf(location)) // Not sure if it's safe to pass an arbitrary object to set_up, todo
s.start()
for(var/i = 1, i<= gibtypes.len, i++)
if(gibamounts[i])
for(var/j = 1, j<= gibamounts[i], j++)
var/gibType = gibtypes[i]
gib = new gibType(location)
// Apply human species colouration to masks.
if(fleshcolor)
gib.fleshcolor = fleshcolor
if(bloodcolor)
gib.basecolor = bloodcolor
gib.update_icon()
gib.blood_DNA = list()
if(MobDNA)
gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.b_type
else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey
gib.blood_DNA["Non-human DNA"] = "A+"
if(istype(location,/turf/))
var/list/directions = gibdirections[i]
if(directions.len)
gib.streak(directions)
qdel(src)
/obj/effect/gibspawner/generic
gibtypes = list(/obj/effect/debris/cleanable/blood/gibs,/obj/effect/debris/cleanable/blood/gibs,/obj/effect/debris/cleanable/blood/gibs/core)
gibamounts = list(2,2,1)
@@ -4,8 +4,6 @@
icon_state = "arrow"
plane = POINT_PLANE
duration = (2.5 SECONDS)
anchored = TRUE
mouse_opacity = 0
/obj/effect/temp_visual/point/Initialize(mapload, set_invis = 0)
. = ..()
@@ -5,10 +5,9 @@
icon_state = "nothing"
layer = ABOVE_MOB_LAYER
anchored = TRUE
mouse_opacity = 0
appearance_flags = 0
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/effect/projectile/singularity_pull()
/obj/effect/projectile/singularity_pull(atom/singularity, current_size)
return
/obj/effect/projectile/singularity_act()
@@ -1,19 +1,22 @@
//temporary visual effects
/obj/effect/temp_visual
icon = 'icons/effects/effects.dmi'
icon_state = "nothing"
anchored = TRUE
layer = ABOVE_MOB_LAYER
mouse_opacity = 0
var/duration = 10 //in deciseconds
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
///time, in deciseconds, that this object will exist
var/duration = 10
///if true, will pick a random direction when created.
var/randomdir = TRUE
///id of the deletion timer
var/timerid
/obj/effect/temp_visual/Initialize(mapload)
. = ..()
if(randomdir)
dir = pick(list(NORTH, SOUTH, EAST, WEST))
timerid = QDEL_IN(src, duration)
setDir(pick(GLOB.cardinal))
timerid = QDEL_IN_STOPPABLE(src, duration)
/obj/effect/temp_visual/Destroy()
. = ..()
@@ -22,17 +25,13 @@
/obj/effect/temp_visual/singularity_act()
return
/obj/effect/temp_visual/singularity_pull()
return
/obj/effect/temp_visual/legacy_ex_act()
/obj/effect/temp_visual/singularity_pull(atom/singularity, current_size)
return
/obj/effect/temp_visual/dir_setting
randomdir = FALSE
/obj/effect/temp_visual/dir_setting/Initialize(mapload, setDir)
if(setDir)
dir = setDir
return ..()
/obj/effect/temp_visual/dir_setting/Initialize(mapload, set_dir)
if(set_dir)
setDir(set_dir)
. = ..()