Skip to content

Commit

Permalink
The Particlening (#3239)
Browse files Browse the repository at this point in the history
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

Fixes #1793

This adds a fuck ton of particles to the game. These are almost entirely
ported from tgstation, specifically:
tgstation/tgstation#71110 (I was unable to get
the editor to work, so this is just the backend fancy stuff)
tgstation/tgstation#59869 (particles)
tgstation/tgstation#60515  (particles)
tgstation/tgstation#74555 (particles)

And because atomization is not in my name, I have also done added my own
spin on this, and that is making blood show up as a particle effect,
instead of mysteriously showing up on the floor. I also made the
mysteriously showing up on the floor bit more clearer if you are
spilling a lot of blood.

Because of the above, I went further and ported
DaedalusDock/daedalusdock#79 (blood squirts
only) as well for being shot, and some additions I made myself. I am
aware using sleep() is bad practice, but I'm not sure how I would avoid
using it here outside of rearranging how a lot of things work, so ill
just say its "fine"

Also gibbing doesn't destroy your body anymore, however it STILL will be
very hard to fix you reguardless, since you still fall apart like a lego
character, it's just you no longer need to stuff them into a legion
corpse.

And during testing since it annoyed me, bloodloss effects are more
pronounced and harder to ignore, taken mostly from
https://github.com/tgstation/TerraGov-Marine-Corps 's bloodloss effects
but less severe in a way.

And one last thing, bone breaking has sound effects, from Terragov
above. Cronch.

## Why It's Good For The Game

While i'm not convinced over using particles over hand drawn sprite
effects, I cannot deny i see the potential in them, especially here!


![Screenshot_15726](https://github.com/user-attachments/assets/714b60d7-46f0-429f-a94a-8b13220fb39e)


![Screenshot_15727](https://github.com/user-attachments/assets/8b00ad2b-13ba-4971-b2b4-dbf1975dce57)


![Screenshot_15728](https://github.com/user-attachments/assets/b78f91bf-0462-41a9-88aa-5f7b76c222ac)


![Screenshot_15729](https://github.com/user-attachments/assets/32e05c97-457c-4c11-a7c1-fe6f1aa6235f)


![Screenshot_15730](https://github.com/user-attachments/assets/27a6b03a-ca73-41e1-bfee-9a3cc8a25404)


## Changelog

:cl: Baystation12, Kapu1178, rye-rice
add: Particles!
add: Bleeding has better feedback
add: Bone breaking now has sound effects
add: Getting shot now throws blood squirts! Live through the somme for
REAL this time!
balance: gibbing no longer destroys your chest, no more legion
transfers!
fix: Lava particles should no longer destroy your FPS

/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------

Co-authored-by: retlaw34 <[email protected]>
  • Loading branch information
rye-rice and rye-rice authored Aug 26, 2024
1 parent 65575cc commit 81ae963
Show file tree
Hide file tree
Showing 46 changed files with 647 additions and 38 deletions.
15 changes: 15 additions & 0 deletions code/__DEFINES/generators.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//generator types
#define GEN_NUM "num"
#define GEN_VECTOR "vector"
#define GEN_BOX "box"
#define GEN_COLOR "color"
#define GEN_CIRCLE "circle"
#define GEN_SPHERE "sphere"
#define GEN_SQUARE "square"
#define GEN_CUBE "cube"

///particle editor var modifiers
#define P_DATA_GENERATOR "generator"
#define P_DATA_ICON_ADD "icon_add"
#define P_DATA_ICON_REMOVE "icon_remove"
#define P_DATA_ICON_WEIGHT "icon_edit"
2 changes: 2 additions & 0 deletions code/__DEFINES/is_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#define isweakref(D) (istype(D, /datum/weakref))

#define isgenerator(A) (istype(A, /generator))

//Turfs
//#define isturf(A) (istype(A, /turf)) This is actually a byond built-in. Added here for completeness sake.

Expand Down
5 changes: 5 additions & 0 deletions code/__DEFINES/particles.dm
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions code/__DEFINES/vv.dm
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#define VV_HK_AUTO_RENAME "auto_rename"
#define VV_HK_RADIATE "radiate"
#define VV_HK_EDIT_FILTERS "edit_filters"
#define VV_HK_EDIT_PARTICLES "edit_particles"

// /obj
#define VV_HK_OSAY "osay"
Expand Down
11 changes: 11 additions & 0 deletions code/__HELPERS/generators.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* 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 it's _binobj, which also contains it's 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, ", ")
17 changes: 17 additions & 0 deletions code/datums/looping_sounds/weather.dm
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,20 @@

/datum/looping_sound/weather/rain/indoors
volume = 30

/datum/looping_sound/weather/rain/storm
mid_sounds = list(
'sound/ambience/storm_outdoors.ogg' = 1
)
mid_length = 20.03 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind.
start_sound = 'sound/ambience/acidrain_start.ogg'
start_length = null
end_sound = null
volume = 50

/datum/looping_sound/weather/rain/storm/indoors
volume = 30
mid_sounds = list(
'sound/ambience/storm_indoors.ogg' = 1
)
mid_length = 20.03 SECONDS // The lengths for the files vary, but the longest is ten seconds, so this will make it sound like intermittent wind.
14 changes: 14 additions & 0 deletions code/datums/weather/weather_types/rain.dm
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,17 @@
desc = "Storm with rain and lightning."
weather_message = "<span class='warning'>The clouds blacken and the sky starts to flash as thunder strikes down!</span>"
thunder_chance = 10

/datum/weather/rain/heavy/storm_intense
name = "storm"
desc = "Storm with rain and lightning."
weather_overlay = "storm_very"
thunder_chance = 20
weather_color = "#a3daf7"
weather_duration_lower = 420690
weather_duration_upper = 420690

sound_active_outside = /datum/looping_sound/weather/rain/storm/indoors
sound_active_inside = /datum/looping_sound/weather/rain/storm
sound_weak_outside = /datum/looping_sound/weather/rain/storm/indoors
sound_weak_inside = /datum/looping_sound/weather/rain/storm
131 changes: 131 additions & 0 deletions code/game/objects/effects/decals/cleanable/humans.dm
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5")
dryname = "dried tracks"
drydesc = "Some old bloody tracks left by wheels. Machines are evil, perhaps."
///Absorb the /squirt subtype when it exists on the turf
var/absorb_squirts = TRUE

/obj/effect/decal/cleanable/blood/tracks
icon_state = "tracks"
Expand Down Expand Up @@ -278,3 +280,132 @@
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
return 1
return 0

/obj/effect/decal/cleanable/blood/hitsplatter
name = "blood splatter"
pass_flags = PASSTABLE | PASSGRILLE
icon_state = "hitsplatter1"
random_icon_states = list("hitsplatter1", "hitsplatter2", "hitsplatter3")
/// The turf we just came from, so we can back up when we hit a wall
var/turf/prev_loc
/// The cached info about the blood
var/list/blood_dna_info
/// Skip making the final blood splatter when we're done, like if we're not in a turf
var/skip = FALSE
/// How many tiles/items/people we can paint red
var/splatter_strength = 3
/// Insurance so that we don't keep moving once we hit a stoppoint
var/hit_endpoint = FALSE
// ///Absorb the /squirt subtype when it exists on the turf
// var/absorb_squirts = TRUE

/obj/effect/decal/cleanable/blood/hitsplatter/Initialize(mapload, splatter_strength)
. = ..()
prev_loc = loc //Just so we are sure prev_loc exists
if(splatter_strength)
src.splatter_strength = splatter_strength

/obj/effect/decal/cleanable/blood/hitsplatter/Destroy()
if(isturf(loc) && !skip)
playsound(src, 'sound/effects/splatter.ogg', 60, TRUE, -1)
if(blood_dna_info)
loc.add_blood_DNA(blood_dna_info)
return ..()

/// Set the splatter up to fly through the air until it rounds out of steam or hits something. Contains sleep() pending imminent moveloop rework, don't call without async'ing it
/obj/effect/decal/cleanable/blood/hitsplatter/proc/fly_towards(turf/target_turf, range)
splatter_strength = range
for(var/i in 1 to range)
step_towards(src,target_turf)
sleep(2) // Will be resolved pending Potato's moveloop rework
for(var/atom/iter_atom in get_turf(src))
if(hit_endpoint)
return
if(splatter_strength <= 0)
break

if(isitem(iter_atom))
iter_atom.add_blood_DNA(blood_dna_info)
splatter_strength--
else if(ishuman(iter_atom))
var/mob/living/carbon/human/splashed_human = iter_atom
if(splashed_human.wear_suit)
splashed_human.wear_suit.add_blood_DNA(blood_dna_info)
splashed_human.update_inv_wear_suit() //updates mob overlays to show the new blood (no refresh)
if(splashed_human.w_uniform)
splashed_human.w_uniform.add_blood_DNA(blood_dna_info)
splashed_human.update_inv_w_uniform() //updates mob overlays to show the new blood (no refresh)
splatter_strength--

if(splatter_strength <= 0) // we used all the puff so we delete it.
qdel(src)
return

var/obj/effect/decal/cleanable/blood/newsplatter
if(splatter_strength <= 3.5)
newsplatter = new /obj/effect/decal/cleanable/blood/squirt(get_turf(src), get_dir(prev_loc, loc), blood_dna_info)
else
newsplatter = new /obj/effect/decal/cleanable/blood/splatter(get_turf(src))
newsplatter.add_blood_DNA(blood_dna_info)
prev_loc = loc

qdel(src)
return

/obj/effect/decal/cleanable/blood/hitsplatter/Bump(atom/bumped_atom)
if(!iswallturf(bumped_atom) && !istype(bumped_atom, /obj/structure/window))
qdel(src)
return

if(istype(bumped_atom, /obj/structure/window))
var/obj/structure/window/bumped_window = bumped_atom
if(!bumped_window.fulltile)
qdel(src)
return

hit_endpoint = TRUE
if(isturf(prev_loc))
abstract_move(bumped_atom)
skip = TRUE
//Adjust pixel offset to make splatters appear on the wall
if(istype(bumped_atom, /obj/structure/window))
land_on_window(bumped_atom)
else
var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new(prev_loc)
final_splatter.pixel_x = (dir == EAST ? 32 : (dir == WEST ? -32 : 0))
final_splatter.pixel_y = (dir == NORTH ? 32 : (dir == SOUTH ? -32 : 0))
else // This will only happen if prev_loc is not even a turf, which is highly unlikely.
abstract_move(bumped_atom)
qdel(src)

/// A special case for hitsplatters hitting windows, since those can actually be moved around, store it in the window and slap it in the vis_contents
/obj/effect/decal/cleanable/blood/hitsplatter/proc/land_on_window(obj/structure/window/the_window)
if(!the_window.fulltile)
return
var/obj/effect/decal/cleanable/blood/splatter/over_window/final_splatter = new
final_splatter.forceMove(the_window)
the_window.vis_contents += final_splatter
the_window.bloodied = TRUE
qdel(src)

/obj/effect/decal/cleanable/blood/squirt
name = "blood trail"
icon_state = "squirt"
random_icon_states = null

/obj/effect/decal/cleanable/blood/squirt/Initialize(mapload, direction, list/blood_dna)
. = ..()
dir = direction
var/obj/effect/decal/cleanable/blood/splatter/existing_blood = locate() in get_turf(src)
if(existing_blood?.absorb_squirts)
if(blood_dna)
existing_blood.add_blood_DNA(blood_dna)
existing_blood.bloodiness = min((existing_blood.bloodiness + bloodiness), BLOOD_AMOUNT_PER_DECAL)
return INITIALIZE_HINT_QDEL

/obj/effect/decal/cleanable/blood/splatter/over_window // special layer/plane set to appear on windows
layer = ABOVE_WINDOW_LAYER
plane = GAME_PLANE
turf_loc_check = FALSE
alpha = 180
absorb_squirts = FALSE
71 changes: 67 additions & 4 deletions code/game/objects/effects/particle_emitter.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,70 @@
/obj/effect/particle_emitter
name = ""
///objects can only have one particle on them at a time, so we use these abstract effects to hold and display the effects. 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
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 //movable appearance_flags plus KEEP_APART and KEEP_TOGETHER
vis_flags = VIS_INHERIT_PLANE
layer = ABOVE_ALL_MOB_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
mouse_opacity = 0
/// Holds info about how this particle emitter works
/// See \code\__DEFINES\particles.dm
var/particle_flags = NONE

/obj/effect/particle_emitter/Initialize(mapload, time)
var/atom/parent

/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
// 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)
QDEL_NULL(particles)
parent = null
return ..()

/// 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(!(particle_flags & PARTICLE_ATTACH_MOB))
return

//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

/// Sets the particles position to the passed coordinate list (X, Y, Z)
/// See [https://www.byond.com/docs/ref/#/{notes}/particles] for position documentation
/obj/effect/abstract/particle_holder/proc/set_particle_position(list/pos)
particles.position = pos
15 changes: 15 additions & 0 deletions code/game/objects/effects/particles/acid.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Acid related particles.
/particles/acid
icon = 'icons/effects/particles/goop.dmi'
icon_state = list("goop_1" = 6, "goop_2" = 2, "goop_3" = 1)
width = 100
height = 100
count = 100
spawning = 0.5
color = "#00ea2b80" //to get 96 alpha
lifespan = 1.5 SECONDS
fade = 1 SECONDS
grow = -0.025
gravity = list(0, 0.15)
position = generator(GEN_SPHERE, 0, 16, NORMAL_RAND)
spin = generator(GEN_NUM, -15, 15, NORMAL_RAND)
55 changes: 55 additions & 0 deletions code/game/objects/effects/particles/fire.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Fire related particles.
/particles/bonfire
icon = 'icons/effects/particles/bonfire.dmi'
icon_state = "bonfire"
width = 100
height = 100
count = 1000
spawning = 4
lifespan = 0.7 SECONDS
fade = 1 SECONDS
grow = -0.01
velocity = list(0, 0)
position = generator(GEN_CIRCLE, 0, 16, NORMAL_RAND)
drift = generator(GEN_VECTOR, list(0, -0.2), list(0, 0.2))
gravity = list(0, 0.95)
scale = generator(GEN_VECTOR, list(0.3, 0.3), list(1,1), NORMAL_RAND)
rotation = 30
spin = generator(GEN_NUM, -20, 20)

/particles/embers
icon = 'icons/effects/particles/generic.dmi'
icon_state = list("dot" = 4,"cross" = 1,"curl" = 1)
width = 64
height = 96
count = 500
spawning = 5
lifespan = 3 SECONDS
fade = 1 SECONDS
color = 0
color_change = 0.05
gradient = list("#FBAF4D", "#FCE6B6", "#FD481C")
position = generator(GEN_BOX, list(-12,-16,0), list(12,16,0), NORMAL_RAND)
drift = generator(GEN_VECTOR, list(-0.1,0), list(0.1,0.025))
spin = generator(GEN_NUM, list(-15,15), NORMAL_RAND)
scale = generator(GEN_VECTOR, list(0.5,0.5), list(2,2), NORMAL_RAND)

/particles/embers/lava
width = 700
height = 700
gradient = list(LIGHT_COLOR_FLARE, LIGHT_COLOR_FLARE , COLOR_ALMOST_BLACK)
spawning = 1

/particles/lava
width = 700
height = 700
count = 500
spawning = 1
lifespan = 4 SECONDS
fade = 2 SECONDS
position = generator(GEN_CIRCLE, 16, 24, NORMAL_RAND)
drift = generator(GEN_VECTOR, list(-0.2, -0.2), list(0.6, 0.6))
velocity = generator(GEN_CIRCLE, -6, 6, NORMAL_RAND)
friction = 0.15
gradient = list(0,LIGHT_COLOR_FLARE , 0.75, COLOR_ALMOST_BLACK)
color_change = 0.125
Loading

0 comments on commit 81ae963

Please sign in to comment.