diff --git a/code/__DEFINES/generators.dm b/code/__DEFINES/generators.dm new file mode 100644 index 000000000000..e9d373c9a65d --- /dev/null +++ b/code/__DEFINES/generators.dm @@ -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" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index de8091fca186..9659ac17c64b 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -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. diff --git a/code/__DEFINES/particles.dm b/code/__DEFINES/particles.dm new file mode 100644 index 000000000000..5657566a63bb --- /dev/null +++ b/code/__DEFINES/particles.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/vv.dm b/code/__DEFINES/vv.dm index 5d9522b18ee4..602473b6086d 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -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" diff --git a/code/__HELPERS/generators.dm b/code/__HELPERS/generators.dm new file mode 100644 index 000000000000..d50df7deba19 --- /dev/null +++ b/code/__HELPERS/generators.dm @@ -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, ", ") diff --git a/code/datums/looping_sounds/weather.dm b/code/datums/looping_sounds/weather.dm index 4398e7d5b1b2..0e26b4592eb0 100644 --- a/code/datums/looping_sounds/weather.dm +++ b/code/datums/looping_sounds/weather.dm @@ -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. diff --git a/code/datums/weather/weather_types/rain.dm b/code/datums/weather/weather_types/rain.dm index fbbb0269ed2a..591a569b19ec 100644 --- a/code/datums/weather/weather_types/rain.dm +++ b/code/datums/weather/weather_types/rain.dm @@ -47,3 +47,17 @@ desc = "Storm with rain and lightning." weather_message = "The clouds blacken and the sky starts to flash as thunder strikes down!" 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 diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 3dd327dda08c..5de5510bec7b 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -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" @@ -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 diff --git a/code/game/objects/effects/particle_emitter.dm b/code/game/objects/effects/particle_emitter.dm index 3ee4ab8ed461..cc4210f742a1 100644 --- a/code/game/objects/effects/particle_emitter.dm +++ b/code/game/objects/effects/particle_emitter.dm @@ -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 diff --git a/code/game/objects/effects/particles/acid.dm b/code/game/objects/effects/particles/acid.dm new file mode 100644 index 000000000000..5ce0984991d8 --- /dev/null +++ b/code/game/objects/effects/particles/acid.dm @@ -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) diff --git a/code/game/objects/effects/particles/fire.dm b/code/game/objects/effects/particles/fire.dm new file mode 100644 index 000000000000..fb20dc778e5e --- /dev/null +++ b/code/game/objects/effects/particles/fire.dm @@ -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 diff --git a/code/game/objects/effects/particles/misc.dm b/code/game/objects/effects/particles/misc.dm new file mode 100644 index 000000000000..18db20d63798 --- /dev/null +++ b/code/game/objects/effects/particles/misc.dm @@ -0,0 +1,32 @@ +// General or un-matched particles, make a new file if a few can be sorted together. +/particles/pollen + icon = 'icons/effects/particles/pollen.dmi' + icon_state = "pollen" + 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/echo + icon = 'icons/effects/particles/echo.dmi' + icon_state = list("echo1" = 1, "echo2" = 1, "echo3" = 2) + width = 480 + height = 480 + count = 1000 + spawning = 0.5 + lifespan = 2 SECONDS + fade = 1 SECONDS + gravity = list(0, -0.1) + position = generator(GEN_BOX, list(-240, -240), list(240, 240), NORMAL_RAND) + drift = generator(GEN_VECTOR, list(-0.1, 0), list(0.1, 0)) + rotation = generator(GEN_NUM, 0, 360, NORMAL_RAND) diff --git a/code/game/objects/effects/particles/slime.dm b/code/game/objects/effects/particles/slime.dm new file mode 100644 index 000000000000..5cef9c976257 --- /dev/null +++ b/code/game/objects/effects/particles/slime.dm @@ -0,0 +1,22 @@ +/// Slime particles. +/particles/slime + 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 = "#707070a0" + lifespan = 1.5 SECONDS + fade = 1 SECONDS + grow = -0.025 + gravity = list(0, -0.05) + position = generator(GEN_BOX, list(-8,-16,0), list(8,16,0), NORMAL_RAND) + spin = generator(GEN_NUM, -15, 15, NORMAL_RAND) + scale = list(0.75, 0.75) + +/// Rainbow slime particles. +/particles/slime/rainbow + gradient = list(0, "#f00a", 3, "#0ffa", 6, "#f00a", "loop", "space"=COLORSPACE_HSL) + color_change = 0.2 + color = generator(GEN_NUM, 0, 6, UNIFORM_RAND) diff --git a/code/game/objects/effects/particles/smoke.dm b/code/game/objects/effects/particles/smoke.dm new file mode 100644 index 000000000000..72807e778f56 --- /dev/null +++ b/code/game/objects/effects/particles/smoke.dm @@ -0,0 +1,66 @@ +// All the smoke variant particles. +/particles/smoke + icon = 'icons/effects/particles/smoke.dmi' + icon_state = list("smoke_1" = 1, "smoke_2" = 1, "smoke_3" = 2) + width = 100 + height = 100 + count = 1000 + spawning = 4 + lifespan = 1.5 SECONDS + fade = 1 SECONDS + velocity = list(0, 0.4, 0) + position = list(6, 0, 0) + drift = generator(GEN_SPHERE, 0, 2, NORMAL_RAND) + friction = 0.2 + gravity = list(0, 0.95) + grow = 0.05 + +/particles/smoke/turf_fire + position = generator(GEN_SPHERE, 16, 24, NORMAL_RAND) + +/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 + +/particles/smoke/steam/mild + 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/vent + position = generator(GEN_SPHERE, 16, 16, NORMAL_RAND) + lifespan = 2 SECONDS + spawning = 3 + +/particles/smoke/steam/vent/low + spawning = 1 + velocity = list(0, 0.3, 0) + friction = 0.25 + +/particles/smoke/steam/vent/high + spawning = 8 + velocity = list(0, 0.25, 0) + count = 1000 + +/particles/smoke/ash + icon_state = list("ash_1" = 2, "ash_2" = 2, "ash_3" = 1, "smoke_1" = 3, "smoke_2" = 2) + count = 500 + spawning = 1 + lifespan = 1 SECONDS + fade = 0.2 SECONDS + fadein = 0.7 SECONDS + position = generator(GEN_VECTOR, list(-3, 5, 0), list(3, 6.5, 0), NORMAL_RAND) + velocity = generator(GEN_VECTOR, list(-0.1, 0.4, 0), list(0.1, 0.5, 0), NORMAL_RAND) diff --git a/code/game/objects/effects/particles/water.dm b/code/game/objects/effects/particles/water.dm new file mode 100644 index 000000000000..456617cb5d8a --- /dev/null +++ b/code/game/objects/effects/particles/water.dm @@ -0,0 +1,17 @@ +// Water related particles. +/particles/droplets + icon = 'icons/effects/particles/generic.dmi' + icon_state = list("dot"=2,"drop"=1) + width = 32 + height = 36 + count = 5 + spawning = 0.2 + lifespan = 1 SECONDS + fade = 0.5 SECONDS + color = "#549EFF" + position = generator(GEN_BOX, list(-9,-9,0), list(9,18,0), NORMAL_RAND) + scale = generator(GEN_VECTOR, list(0.9,0.9), list(1.1,1.1), NORMAL_RAND) + gravity = list(0, -0.9) + +/particles/droplets/blood + spawning = 0.2 diff --git a/code/game/objects/effects/turf_fire.dm b/code/game/objects/effects/turf_fire.dm index a0c9e0f95a9b..735d8226edfa 100644 --- a/code/game/objects/effects/turf_fire.dm +++ b/code/game/objects/effects/turf_fire.dm @@ -65,7 +65,7 @@ /obj/effect/abstract/turf_fire/Initialize(mapload, power, fire_color) . = ..() - particles = new /particles/lava + particles = new /particles/smoke/turf_fire var/turf/open/open_turf = loc if(open_turf.turf_fire) return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 78cfa10a2e0b..97b8c4348353 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -210,6 +210,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, "fire", 0) if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF)) resistance_flags |= ON_FIRE + burning_particles = new(src, /particles/smoke/burning) SSfire_burning.processing[src] = src update_appearance() return 1 @@ -226,6 +227,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e if(resistance_flags & ON_FIRE) resistance_flags &= ~ON_FIRE update_appearance() + QDEL_NULL(burning_particles) SSfire_burning.processing -= src ///Called when the obj is hit by a tesla bolt. diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index d4ad3f0e679e..0ffeaa673b53 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -43,6 +43,8 @@ vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace. + var/obj/effect/abstract/particle_holder/burning_particles + FASTDMM_PROP(\ pinned_vars = list("name", "dir")\ ) diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 0fca2bcca6ee..1698f90ec7cd 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -1056,3 +1056,39 @@ T.air.adjust_moles(GAS_CO2, -amt) T.atmos_spawn_air("o2=[amt];TEMP=293.15") lastcycle = world.time + +/obj/structure/fluff/steam_vent + name = "steam vent" + desc = "A outlet for steam, usually for water coming in contact with steam pipes." + icon = 'icons/obj/structures.dmi' + icon_state = "steamvent" + deconstructible = FALSE + layer = GAS_PUMP_LAYER + + var/particle_to_spawn = /particles/smoke/steam/vent + var/obj/effect/particle_holder/part_hold + +/obj/structure/fluff/steam_vent/Initialize() + . = ..() + part_hold = new(get_turf(src)) + part_hold.layer = EDGED_TURF_LAYER + part_hold.particles = new particle_to_spawn() + underlays.Cut() + +/obj/structure/fluff/steam_vent/Destroy() + . = ..() + QDEL_NULL(part_hold) + +/obj/structure/fluff/steam_vent/low + particle_to_spawn = /particles/smoke/steam/vent/low + +/obj/structure/fluff/steam_vent/high + particle_to_spawn = /particles/smoke/steam/vent/high + +/obj/effect/particle_holder + name = "" + anchored = TRUE + mouse_opacity = 0 + +/obj/effect/particle_emitter/Initialize(mapload, time) + . = ..() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 49c3823cf1ce..5420cc06b490 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -32,6 +32,9 @@ hitsound_type = PROJECTILE_HITSOUND_GLASS + /// If some inconsiderate jerk has had their blood spilled on this window, thus making it cleanable + var/bloodied = FALSE + /obj/structure/window/examine(mob/user) . = ..() if(flags_1 & NODECONSTRUCT_1) diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index eb2132940bd9..90fd6610721c 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -220,19 +220,13 @@ /turf/open/lava/smooth/airless initial_gas_mix = AIRLESS_ATMOS -/particles/lava - width = 700 - height = 700 - count = 1000 - spawning = 1 - lifespan = 3 SECONDS - fade = 2 SECONDS - position = generator("circle", 16, 24, NORMAL_RAND) - drift = generator("vector", list(-0.2, -0.2), list(0.2, 0.2)) - velocity = generator("circle", -6, 6, NORMAL_RAND) - friction = 0.15 - gradient = list(0,LIGHT_COLOR_FLARE , 0.75, COLOR_ALMOST_BLACK) - color_change = 0.125 +/obj/effect/particle_holder + name = "" + anchored = TRUE + mouse_opacity = 0 + +/obj/effect/particle_emitter/Initialize(mapload, time) + . = ..() /obj/effect/particle_emitter/lava - particles = new/particles/lava + particles = new/particles/embers/lava diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 1910347e4fdd..8e2962e70d2f 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -51,19 +51,31 @@ if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS) if(prob(10)) to_chat(src, "You feel terribly bloated.") + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - if(prob(5)) + + if(prob(1)) to_chat(src, "You feel [word].") - adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1)) + if(oxyloss < 20) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) - if(prob(5)) - blur_eyes(6) + if(eye_blurry < 50) + adjust_blurriness(5) + if(oxyloss < 40) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + else + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1)) + + if(prob(15)) + Unconscious(rand(2 SECONDS,6 SECONDS)) to_chat(src, "You feel very [word].") + if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) - adjustOxyLoss(5) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + adjustToxLoss(2) if(prob(15)) - Unconscious(rand(20,60)) + Unconscious(rand(2 SECONDS,6 SECONDS)) to_chat(src, "You feel extremely [word].") if(-INFINITY to BLOOD_VOLUME_SURVIVE) if(!HAS_TRAIT(src, TRAIT_NODEATH)) @@ -81,25 +93,76 @@ BP.adjust_bleeding(0.1, BLOOD_LOSS_DAMAGE_MAXIMUM) limb_bleed += BP.bleeding + var/message_cooldown = 5 SECONDS + var/bleeeding_wording +// var/bleed_change_wording + switch(limb_bleed) + if(0 to 0.5) + bleeeding_wording = "You hear droplets of blood drip down." + message_cooldown *= 2.5 + if(0.5 to 1) + bleeeding_wording = "You feel your blood flow quietly to the floor." + message_cooldown *= 2 + if(1 to 2) + bleeeding_wording = "The flow of blood leaving your body onto the ground is worrying..." + message_cooldown *= 1.7 + if(2 to 4) + bleeeding_wording = "You're losing blood very fast, which is freaking you out!" + message_cooldown *= 1.5 + if(4 to INFINITY) + bleeeding_wording = "Your heartbeat beats unstably fast as you lose a massive amount of blood!!" + if(limb_bleed && !bleedsuppress && !HAS_TRAIT(src, TRAIT_FAKEDEATH)) bleed(limb_bleed) + if(!blood_particle) + blood_particle = new(src, /particles/droplets/blood, PARTICLE_ATTACH_MOB) + blood_particle.particles.color = dna.blood_type.color //mouthful + blood_particle.particles.spawning = (limb_bleed/2) + blood_particle.particles.count = (round(clamp((limb_bleed * 2), 1, INFINITY))) + + if(COOLDOWN_FINISHED(src, bloodloss_message) && bleeeding_wording) + to_chat(src, span_warning("[bleeeding_wording]")) + COOLDOWN_START(src, bloodloss_message, message_cooldown) + else + if(blood_particle) + QDEL_NULL(blood_particle) + //Makes a blood drop, leaking amt units of blood from the mob /mob/living/carbon/proc/bleed(amt) if(blood_volume) blood_volume = max(blood_volume - amt, 0) if (prob(sqrt(amt)*BLOOD_DRIP_RATE_MOD)) if(isturf(src.loc) && !isgroundlessturf(src.loc)) //Blood loss still happens in locker, floor stays clean - if(amt >= 10) - add_splatter_floor(src.loc) + if(amt >= 2) + add_splatter_floor(src.loc, amt = amt) else - add_splatter_floor(src.loc, 1) + add_splatter_floor(src.loc, TRUE, amt) /mob/living/carbon/human/bleed(amt) amt *= physiology.bleed_mod if(!(NOBLOOD in dna.species.species_traits)) ..() +/** + * This proc is a helper for spraying blood for things like slashing/piercing wounds and dismemberment. + * + * The strength of the splatter in the second argument determines how much it can dirty and how far it can go + * + * Arguments: + * * splatter_direction: Which direction the blood is flying + * * splatter_strength: How many tiles it can go, and how many items it can pass over and dirty + */ +/mob/living/carbon/proc/spray_blood(splatter_direction, splatter_strength = 3) + if(!isturf(loc)) + return + var/obj/effect/decal/cleanable/blood/hitsplatter/our_splatter = new(loc) + +// our_splatter.transfer_mob_blood_dna(return_blood_DNA(src)) + our_splatter.blood_dna_info = get_blood_dna_list() + our_splatter.transfer_mob_blood_dna(src) + var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength) + INVOKE_ASYNC(our_splatter, TYPE_PROC_REF(/obj/effect/decal/cleanable/blood/hitsplatter, fly_towards), targ, splatter_strength) /mob/living/proc/restore_blood() @@ -229,13 +292,14 @@ return blood_type.color //to add a splatter of blood or other mob liquid. -/mob/living/proc/add_splatter_floor(turf/T, small_drip) +/mob/living/proc/add_splatter_floor(turf/T, small_drip, amt) if(get_blood_id() != /datum/reagent/blood) return if(!T) T = get_turf(src) var/list/temp_blood_DNA + if(small_drip) // Only a certain number of drips (or one large splatter) can be on a given turf. var/obj/effect/decal/cleanable/blood/drip/drop = locate() in T @@ -248,7 +312,7 @@ else temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter qdel(drop)//the drip is replaced by a bigger splatter - else + else if (amt < 2) drop = new(T, get_static_viruses()) drop.transfer_mob_blood_dna(src) return @@ -261,7 +325,11 @@ B = candidate break if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) + if(amt > 4) + B = new /obj/effect/decal/cleanable/blood(T, get_static_viruses()) + else + B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) + if(QDELETED(B)) //Give it up return B.bloodiness = min((B.bloodiness + BLOOD_AMOUNT_PER_DECAL), BLOOD_POOL_MAX) @@ -269,11 +337,11 @@ if(temp_blood_DNA) B.add_blood_DNA(temp_blood_DNA) -/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip) +/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip, amt) if(!(NOBLOOD in dna.species.species_traits)) ..() -/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip) +/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip, amt) if(!T) T = get_turf(src) var/obj/effect/decal/cleanable/xenoblood/B = locate() in T.contents @@ -281,7 +349,7 @@ B = new(T) B.add_blood_DNA(list("UNKNOWN DNA" = "X*")) -/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip) +/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip, amt) if(!T) T = get_turf(src) var/obj/effect/decal/cleanable/oil/B = locate() in T.contents diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 43cefa251e34..6a34e87fdf42 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -653,3 +653,20 @@ ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) else if(getOxyLoss() <= 50) REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + +/mob/living/carbon/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE) + var/mob/living/carbon/human/current_user = src //is this a good idea? who can say? + var/armor = run_armor_check(def_zone, P.flag, P.armour_penetration, silent = TRUE) + var/on_hit_state = P.on_hit(src, armor, piercing_hit) + if(!P.nodamage && on_hit_state != BULLET_ACT_BLOCK && !QDELETED(src)) //QDELETED literally just for the instagib rifle. Yeah. + apply_damage(P.damage, P.damage_type, def_zone, armor, sharpness = TRUE) + if(P.damage-armor >= 15 && P.damage_type == BRUTE && (!armor || prob(40) || P.damage-armor >= 25)) + spray_blood(get_dir(P.starting,src), (P.damage-armor)/5) + var/obj/item/bodypart/targeted_bodypart = null + bleed((P.damage-armor)/2) + + recoil_camera(src, clamp((P.damage-armor)/4,0.5,10), clamp((P.damage-armor)/4,0.5,10), P.damage/8, P.Angle) + apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, armor, P.stamina, P.jitter, P.paralyze, P.immobilize) + if(P.dismemberment) + check_projectile_dismemberment(P, def_zone) + return on_hit_state ? BULLET_ACT_HIT : BULLET_ACT_BLOCK diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm index 8c1a36c2061b..1804a1497187 100644 --- a/code/modules/mob/living/carbon/death.dm +++ b/code/modules/mob/living/carbon/death.dm @@ -31,6 +31,9 @@ if(prob(50)) step(W, pick(GLOB.alldirs)) var/atom/Tsec = drop_location() + var/amount_of_streams_to_spawn = rand(2,4) + for(var/i in 1 to amount_of_streams_to_spawn) + spray_blood(pick(GLOB.alldirs), rand(1,6)) for(var/mob/M in src) M.forceMove(Tsec) visible_message("[M] bursts out of [src]!") diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 567523c11d79..df006ead1f39 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -77,3 +77,7 @@ /// How many "units of blood" we have on our hands var/blood_in_hands = 0 + ///blood particle effect + var/obj/effect/abstract/particle_holder/blood_particle + + COOLDOWN_DECLARE(bloodloss_message) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 6615edb051f0..0687d90da75b 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -1,4 +1,4 @@ -/mob/living/gib(no_brain, no_organs, no_bodyparts) +/mob/living/gib(no_brain, no_organs, no_bodyparts, safe_gib = FALSE) var/prev_lying = lying_angle if(stat != DEAD) death(TRUE) @@ -12,7 +12,8 @@ spread_bodyparts(no_brain, no_organs) spawn_gibs(no_bodyparts) - qdel(src) + if(!safe_gib) + qdel(src) /mob/living/proc/gib_animation() return diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index f8d8a9a49384..0431ae9aad73 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -777,7 +777,11 @@ if (bone_status == BONE_FLAG_NORMAL && body_part & LEGS) // Because arms are not legs owner.set_broken_legs(owner.broken_legs + 1) bone_status = BONE_FLAG_BROKEN - addtimer(CALLBACK(owner, TYPE_PROC_REF(/atom, visible_message), "You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound."), 1 SECONDS) +// addtimer(CALLBACK(src, PROC_REF(break_bone_feedback), 1 SECONDS)) testing sommething + +///obj/item/bodypart/proc/break_bone_feedback() + owner.visible_message("You hear a cracking sound coming from [owner]'s [name].", "You feel something crack in your [name]!", "You hear an awful cracking sound.") + playsound(owner, list('sound/health/bone/bone_break1.ogg','sound/health/bone/bone_break2.ogg','sound/health/bone/bone_break3.ogg','sound/health/bone/bone_break4.ogg','sound/health/bone/bone_break5.ogg','sound/health/bone/bone_break6.ogg'), 100, FALSE, -1) /obj/item/bodypart/proc/fix_bone() // owner.update_inv_splints() breaks diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi index f7e2e158d422..aed7e9b4fbf4 100644 Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ diff --git a/icons/effects/particles/bonfire.dmi b/icons/effects/particles/bonfire.dmi new file mode 100644 index 000000000000..e8e2e36346da Binary files /dev/null and b/icons/effects/particles/bonfire.dmi differ diff --git a/icons/effects/particles/echo.dmi b/icons/effects/particles/echo.dmi new file mode 100644 index 000000000000..60a243a8a7be Binary files /dev/null and b/icons/effects/particles/echo.dmi differ diff --git a/icons/effects/particles/generic.dmi b/icons/effects/particles/generic.dmi new file mode 100644 index 000000000000..dfbb1a47a6ef Binary files /dev/null and b/icons/effects/particles/generic.dmi differ diff --git a/icons/effects/particles/goop.dmi b/icons/effects/particles/goop.dmi new file mode 100644 index 000000000000..673c1a7ad5b6 Binary files /dev/null and b/icons/effects/particles/goop.dmi differ diff --git a/icons/effects/particles/pollen.dmi b/icons/effects/particles/pollen.dmi new file mode 100644 index 000000000000..559c4d1846f6 Binary files /dev/null and b/icons/effects/particles/pollen.dmi differ diff --git a/icons/effects/particles/smoke.dmi b/icons/effects/particles/smoke.dmi new file mode 100644 index 000000000000..4a3239499b96 Binary files /dev/null and b/icons/effects/particles/smoke.dmi differ diff --git a/icons/effects/weather_effects.dmi b/icons/effects/weather_effects.dmi index a8a7185af500..f76e34ec71ec 100644 Binary files a/icons/effects/weather_effects.dmi and b/icons/effects/weather_effects.dmi differ diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi index f5f04901af2a..af3c5cd4be2e 100644 Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ diff --git a/shiptest.dme b/shiptest.dme index 0686ea00c049..09e6f8be9c16 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -67,6 +67,7 @@ #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\forensics.dm" +#include "code\__DEFINES\generators.dm" #include "code\__DEFINES\guns.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\icon_smoothing.dm" @@ -105,6 +106,7 @@ #include "code\__DEFINES\obj_flags.dm" #include "code\__DEFINES\overmap.dm" #include "code\__DEFINES\paper.dm" +#include "code\__DEFINES\particles.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\plumbing.dm" @@ -190,6 +192,7 @@ #include "code\__HELPERS\files.dm" #include "code\__HELPERS\filters.dm" #include "code\__HELPERS\game.dm" +#include "code\__HELPERS\generators.dm" #include "code\__HELPERS\global_lists.dm" #include "code\__HELPERS\heap.dm" #include "code\__HELPERS\icon_smoothing.dm" @@ -1121,6 +1124,12 @@ #include "code\game\objects\effects\effect_system\effects_smoke.dm" #include "code\game\objects\effects\effect_system\effects_sparks.dm" #include "code\game\objects\effects\effect_system\effects_water.dm" +#include "code\game\objects\effects\particles\acid.dm" +#include "code\game\objects\effects\particles\fire.dm" +#include "code\game\objects\effects\particles\misc.dm" +#include "code\game\objects\effects\particles\slime.dm" +#include "code\game\objects\effects\particles\smoke.dm" +#include "code\game\objects\effects\particles\water.dm" #include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\bundle.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" diff --git a/sound/ambience/storm_indoors.ogg b/sound/ambience/storm_indoors.ogg new file mode 100644 index 000000000000..62e9014a05ff Binary files /dev/null and b/sound/ambience/storm_indoors.ogg differ diff --git a/sound/ambience/storm_outdoors.ogg b/sound/ambience/storm_outdoors.ogg new file mode 100644 index 000000000000..35ae8e5297d6 Binary files /dev/null and b/sound/ambience/storm_outdoors.ogg differ diff --git a/sound/effects/splatter.ogg b/sound/effects/splatter.ogg new file mode 100644 index 000000000000..1c678cfe1268 Binary files /dev/null and b/sound/effects/splatter.ogg differ diff --git a/sound/health/bone/bone_break1.ogg b/sound/health/bone/bone_break1.ogg new file mode 100644 index 000000000000..dd2d22ec792b Binary files /dev/null and b/sound/health/bone/bone_break1.ogg differ diff --git a/sound/health/bone/bone_break2.ogg b/sound/health/bone/bone_break2.ogg new file mode 100644 index 000000000000..aa2537f894de Binary files /dev/null and b/sound/health/bone/bone_break2.ogg differ diff --git a/sound/health/bone/bone_break3.ogg b/sound/health/bone/bone_break3.ogg new file mode 100644 index 000000000000..9f66324be3b2 Binary files /dev/null and b/sound/health/bone/bone_break3.ogg differ diff --git a/sound/health/bone/bone_break4.ogg b/sound/health/bone/bone_break4.ogg new file mode 100644 index 000000000000..bbdfac1ecff3 Binary files /dev/null and b/sound/health/bone/bone_break4.ogg differ diff --git a/sound/health/bone/bone_break5.ogg b/sound/health/bone/bone_break5.ogg new file mode 100644 index 000000000000..dfee0e9baa72 Binary files /dev/null and b/sound/health/bone/bone_break5.ogg differ diff --git a/sound/health/bone/bone_break6.ogg b/sound/health/bone/bone_break6.ogg new file mode 100644 index 000000000000..d41cc8d7cf54 Binary files /dev/null and b/sound/health/bone/bone_break6.ogg differ