diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index e9feb17043e..fe309999a47 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -453,6 +453,10 @@ if(gravitystate) for(var/mob/living/carbon/human/M in A) thunk(M) + for(var/obj/effect/decal/cleanable/blood/B in A) + B.splat(B) + for(var/obj/effect/decal/cleanable/vomit/V in A) + V.splat(V) /area/proc/thunk(mob/living/carbon/human/M) if(!istype(M)) // Rather not have non-humans get hit with a THUNK diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 753033dac89..f8b9af86ea4 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -981,6 +981,8 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) return var/obj/effect/decal/cleanable/vomit/this = new type(src) + if(!this.gravity_check) + this.newtonian_move(dir) // Make toxins vomit look different if(toxvomit) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index e92f196cd4c..98ca3badcda 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -1,4 +1,5 @@ #define DRYING_TIME 5 * 60 * 10 //for 1 unit of depth in puddle (amount var) +#define ALWAYS_IN_GRAVITY 2 GLOBAL_LIST_EMPTY(splatter_cache) @@ -21,9 +22,12 @@ GLOBAL_LIST_EMPTY(splatter_cache) var/amount = 5 var/dry_timer = 0 var/off_floor = FALSE - + var/image/weightless_image + inertia_move_delay = 1 // so they dont collide with who emitted them /obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C) + if(C == src) + return FALSE if(C.blood_DNA) blood_DNA |= C.blood_DNA.Copy() if(bloodiness) @@ -31,12 +35,15 @@ GLOBAL_LIST_EMPTY(splatter_cache) C.bloodiness += bloodiness return ..() - /obj/effect/decal/cleanable/blood/Initialize(mapload) . = ..() + weightless_image = new() update_icon() - if(type == /obj/effect/decal/cleanable/blood/gibs) + + if(!gravity_check) + //weightless blood cannot dry return + if(!.) dry_timer = addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1), TIMER_STOPPABLE) @@ -46,9 +53,37 @@ GLOBAL_LIST_EMPTY(splatter_cache) return ..() /obj/effect/decal/cleanable/blood/update_icon() + var/turf/T = get_turf(src) + check_gravity(T) + + if((T && (T.density)) || !gravity_check || locate(/obj/structure/window/) in T || locate(/obj/structure/grille/) in T) + off_floor = TRUE + layer = ABOVE_MOB_LAYER + plane = GAME_PLANE + if(basecolor == "rainbow") basecolor = "#[pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF"))]" + color = basecolor + + if(!gravity_check) + if(prob(50)) + animate_float(src, -1, rand(30,120)) + else + animate_levitate(src, -1, rand(30,120)) + + if(weightless_image.icon_state) + icon_state = weightless_image.icon_state + + overlays -= weightless_image + color = "#FFFFFF" + icon = 'icons/effects/blood_weightless.dmi' + weightless_image = image(icon, icon_state) + icon_state = "empty" + weightless_image.icon += basecolor + overlays += weightless_image + else + overlays.Cut() ..() /obj/effect/decal/cleanable/blood/proc/dry() @@ -56,6 +91,88 @@ GLOBAL_LIST_EMPTY(splatter_cache) desc = drydesc color = adjust_brightness(color, -50) amount = 0 + gravity_check = ALWAYS_IN_GRAVITY + animate(src) + + if(isspaceturf(loc)) + var/turf/T = get_turf(src) + if(!locate(/obj/structure/grille/) in T && !locate(/obj/structure/window/) in T) + qdel(src) //no free floating dried blood in space, thatd look weird + +/obj/effect/decal/cleanable/blood/ex_act() + . = ..() + update_icon() + +/obj/effect/decal/cleanable/blood/proc/splat(atom/AT) + if(gravity_check) //only floating blood can splat :C + return + var/turf/T = get_turf(AT) + if(try_merging_decal(T)) + return + if(loc != T) + forceMove(T) //move to the turf to splatter on + animate(src) //stop floating + gravity_check = ALWAYS_IN_GRAVITY + icon = initial(icon) + icon_state = weightless_image.icon_state + layer = initial(layer) + plane = initial(plane) + update_icon() + +/obj/effect/decal/cleanable/blood/try_merging_decal(turf/T) + ..() + +/obj/effect/decal/cleanable/blood/Process_Spacemove(movement_dir) + if(gravity_check) + return TRUE + + if(has_gravity(src)) + if(!gravity_check) + splat(get_step(src, movement_dir)) + return TRUE + + if(pulledby && !pulledby.pulling) + return TRUE + + if(throwing) + return TRUE + + return FALSE + + +/obj/effect/decal/cleanable/blood/Bump(atom/A, yes) + if(gravity_check) + return ..() + if(iswallturf(A) || istype(A, /obj/structure/window)) + splat(A) + return + else if(A.density) + splat(get_turf(A)) + return + + if(ishuman(A)) + bloodyify_human(A) + return + + ..() + +/obj/effect/decal/cleanable/blood/proc/bloodyify_human(mob/living/carbon/human/H) + if(inertia_dir && H.inertia_dir == inertia_dir) //if they are moving the same direction we are, no collison + return + + var/list/obj/item/things_to_potentially_bloody = list() + var/count = amount + 1 + + for(var/obj/item/i in H.contents) + things_to_potentially_bloody += i + + if(length(things_to_potentially_bloody)) + for(var/i in 1 to count) + things_to_potentially_bloody[rand(1, length(things_to_potentially_bloody))].add_blood(blood_DNA, basecolor) + count-- + qdel(src) + else + splat(get_turf(H)) /obj/effect/decal/cleanable/blood/attack_hand(mob/living/carbon/human/user) ..() @@ -144,6 +261,7 @@ GLOBAL_LIST_EMPTY(splatter_cache) mergeable_decal = FALSE var/image/giblets var/fleshcolor = "#FFFFFF" + gravity_check = ALWAYS_IN_GRAVITY /obj/effect/decal/cleanable/blood/gibs/Destroy() giblets = null @@ -218,3 +336,4 @@ GLOBAL_LIST_EMPTY(splatter_cache) return FALSE #undef DRYING_TIME +#undef ALWAYS_IN_GRAVITY diff --git a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm index 0ef4606ffdd..625c70555bf 100644 --- a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm +++ b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm @@ -1,3 +1,5 @@ +#define ALWAYS_IN_GRAVITY 3 + /obj/effect/decal/cleanable/generic name = "clutter" desc = "Someone should clean that up." @@ -141,12 +143,74 @@ gender = PLURAL density = FALSE layer = TURF_LAYER + plane = FLOOR_PLANE icon = 'icons/effects/blood.dmi' icon_state = "vomit_1" random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4") no_clear = TRUE scoop_reagents = list("vomit" = 5) + +/obj/effect/decal/cleanable/vomit/Initialize(mapload) + . = ..() + var/turf/T = get_turf(src) + gravity_check = has_gravity(src, T) + if(loc != T) + forceMove(T) + if(!gravity_check) + layer = MOB_LAYER + plane = GAME_PLANE + if(prob(50)) + animate_float(src, -1, rand(30, 120)) + else + animate_levitate(src, -1, rand(30, 120)) + icon = 'icons/effects/blood_weightless.dmi' + +/obj/effect/decal/cleanable/vomit/Bump(atom/A, yes) + . = ..() + if(A.density) + splat(A) + +/obj/effect/decal/cleanable/vomit/Crossed(atom/movable/AM, oldloc) + if(!gravity_check) + splat(AM) + ..() + +/obj/effect/decal/cleanable/vomit/proc/splat(atom/A) + if(gravity_check) + return + var/turf/T = get_turf(A) + if(try_merging_decal(T)) + return + if(loc != T) + forceMove(T) + icon = initial(icon) + gravity_check = ALWAYS_IN_GRAVITY + if(T.density || locate(/obj/structure/window) in T) + layer = ABOVE_WINDOW_LAYER + plane = GAME_PLANE + else + layer = initial(layer) + plane = initial(plane) + animate(src) + +/obj/effect/decal/cleanable/vomit/Process_Spacemove(movement_dir) + if(gravity_check) + return 1 + + if(has_gravity(src)) + if(!gravity_check) + splat(get_step(src, movement_dir)) + return 1 + + if(pulledby && !pulledby.pulling) + return 1 + + if(throwing) + return 1 + + return 0 + /obj/effect/decal/cleanable/vomit/green name = "green vomit" desc = "It's all gummy. Ew." @@ -229,3 +293,5 @@ icon = 'icons/effects/blood.dmi' icon_state = "xfloor1" random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") + +#undef ALWAYS_IN_GRAVITY diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index b50635e67a3..9ebce846386 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -1,5 +1,6 @@ // 5 seconds #define TRACKS_CRUSTIFY_TIME 50 +#define ALWAYS_IN_GRAVITY 2 // color-dir-dry GLOBAL_LIST_EMPTY(fluidtrack_cache) @@ -15,6 +16,7 @@ GLOBAL_LIST_EMPTY(fluidtrack_cache) gender = PLURAL random_icon_states = null amount = 0 + gravity_check = ALWAYS_IN_GRAVITY //BLOODY FOOTPRINTS /obj/effect/decal/cleanable/blood/footprints @@ -28,7 +30,7 @@ GLOBAL_LIST_EMPTY(fluidtrack_cache) var/exited_dirs = 0 var/base_alpha = BLOODY_FOOTPRINT_BASE_ALPHA blood_state = BLOOD_STATE_HUMAN //the icon state to load images from - + gravity_check = ALWAYS_IN_GRAVITY /obj/effect/decal/cleanable/blood/footprints/Crossed(atom/movable/O, oldloc) ..() @@ -146,3 +148,5 @@ GLOBAL_LIST_EMPTY(fluidtrack_cache) if(basecolor == COLOR_BLOOD_MACHINE) return FALSE return TRUE + +#undef ALWAYS_IN_GRAVITY diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 60550fa61e3..b3457fc2149 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -1,8 +1,16 @@ +#define ALWAYS_IN_GRAVITY 2 + /obj/effect/decal/cleanable + ///when Initialized() its icon_state will be chosen from this list var/list/random_icon_states = list() - var/bloodiness = 0 //0-100, amount of blood in this decal, used for making footprints and affecting the alpha of bloody footprints - var/mergeable_decal = TRUE //when two of these are on a same tile or do we need to merge them into just one? - plane = FLOOR_PLANE //prevents Ambient Occlusion effects around it ; Set to GAME_PLANE in Initialize() if on a wall + ///0-100, amount of blood in this decal, used for making footprints and affecting the alpha of bloody footprints + var/bloodiness = 0 + ///when another of the same type is made on the same tile will they merge --- YES=TRUE; NO=FLASE + var/mergeable_decal = TRUE + ///prevents Ambient Occlusion effects around it ; Set to GAME_PLANE in Initialize() if on a wall + plane = FLOOR_PLANE + ///for blood n vomit in zero G --- IN GRAVITY=TRUE; NO GRAVITY=FALSE + var/gravity_check = TRUE /obj/effect/decal/cleanable/proc/replace_decal(obj/effect/decal/cleanable/C) // Returns true if we should give up in favor of the pre-existing decal if(mergeable_decal) @@ -20,7 +28,14 @@ //This is on /cleanable because fuck this ancient mess /obj/effect/decal/cleanable/blood/Crossed(atom/movable/O) ..() - if(!off_floor && ishuman(O)) + + if(!ishuman(O)) + return + + if(!gravity_check && ishuman(O)) + bloodyify_human(O) + + if(!off_floor) var/mob/living/carbon/human/H = O var/obj/item/organ/external/l_foot = H.get_organ("l_foot") var/obj/item/organ/external/r_foot = H.get_organ("r_foot") @@ -71,12 +86,8 @@ /obj/effect/decal/cleanable/Initialize(mapload) . = ..() - if(loc && isturf(loc)) - for(var/obj/effect/decal/cleanable/C in loc) - if(C != src && C.type == type && !QDELETED(C)) - if(replace_decal(C)) - qdel(src) - return TRUE + if(try_merging_decal()) + return TRUE if(random_icon_states && length(src.random_icon_states) > 0) src.icon_state = pick(src.random_icon_states) if(smoothing_flags) @@ -89,3 +100,22 @@ if(smoothing_flags) QUEUE_SMOOTH_NEIGHBORS(src) return ..() + +/obj/effect/decal/cleanable/proc/try_merging_decal(turf/T) + if(!T) + T = loc + if(isturf(T)) + for(var/obj/effect/decal/cleanable/C in T) + if(C != src && C.type == type && !QDELETED(C)) + if(C.gravity_check && replace_decal(C)) + qdel(src) + return TRUE + return FALSE + +/obj/effect/decal/cleanable/proc/check_gravity(turf/T) + if(isnull(T)) + T = get_turf(src) + if(gravity_check != ALWAYS_IN_GRAVITY) + gravity_check = has_gravity(src, T) + +#undef ALWAYS_IN_GRAVITY diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 4ec0b242f0d..9b308992549 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -495,7 +495,7 @@ emp_act bloody = 1 var/turf/location = loc if(issimulatedturf(location)) - add_splatter_floor(location) + add_splatter_floor(location, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) if(ishuman(user)) var/mob/living/carbon/human/H = user if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 374a7339c2c..2124e289438 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1235,11 +1235,11 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ if(green) if(!no_text) visible_message("[src] vomits up some green goo!","You vomit up some green goo!") - location.add_vomit_floor(FALSE, TRUE) + add_vomit_floor(FALSE, TRUE) else if(!no_text) visible_message("[src] pukes all over [p_themselves()]!","You puke all over yourself!") - location.add_vomit_floor(TRUE) + add_vomit_floor(TRUE) /mob/proc/AddSpell(obj/effect/proc_holder/spell/S) mob_spell_list += S diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index eb00d779687..150e792f070 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -156,6 +156,8 @@ direct = newdir n = get_step(mob, direct) + mob.last_movement_dir = direct + var/prev_pulling_loc = null if(mob.pulling) prev_pulling_loc = mob.pulling.loc diff --git a/code/modules/mob/mob_vars.dm b/code/modules/mob/mob_vars.dm index 3d128502f52..d2e991a8e78 100644 --- a/code/modules/mob/mob_vars.dm +++ b/code/modules/mob/mob_vars.dm @@ -187,6 +187,9 @@ var/last_movement = -100 // Last world.time the mob actually moved of its own accord. + /// The direction they last moved + var/last_movement_dir + var/last_logout = 0 var/resize = 1 //Badminnery resize diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index cb6a7391559..f16ef7cecd8 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -87,9 +87,9 @@ blood_volume = max(blood_volume - amt, 0) if(isturf(loc)) //Blood loss still happens in locker, floor stays clean if(amt >= 10) - add_splatter_floor(loc) + add_splatter_floor(loc, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) else - add_splatter_floor(loc, 1) + add_splatter_floor(loc, 1, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) /mob/living/carbon/human/bleed(amt) amt *= physiology.bleed_mod @@ -108,7 +108,7 @@ blood_volume = max(blood_volume - amt, 0) if(prob(10 * amt)) // +5% chance per internal bleeding site that we'll cough up blood on a given tick. custom_emote(EMOTE_VISIBLE, "coughs up blood!") - add_splatter_floor(loc, 1) + add_splatter_floor(loc, 1, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) return 1 else if(amt >= 1 && prob(5 * amt)) // +2.5% chance per internal bleeding site that we'll cough up blood on a given tick. Must be bleeding internally in more than one place to have a chance at this. vomit(0, 1) @@ -256,7 +256,7 @@ return list("O-", "O+") //to add a splatter of blood or other mob liquid. -/mob/living/proc/add_splatter_floor(turf/T, small_drip, shift_x, shift_y) +/mob/living/proc/add_splatter_floor(turf/T, small_drip, shift_x, shift_y, emittor_intertia) if((get_blood_id() != "blood") && (get_blood_id() != "slimejelly"))//is it blood or welding fuel? return if(!T) @@ -269,9 +269,14 @@ // 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 if(drop) + if(emittor_intertia) + drop.newtonian_move(emittor_intertia) if(drop.drips < 5) drop.drips++ - drop.overlays |= pick(drop.random_icon_states) + var/image/I = image(drop.icon, drop.random_icon_states) + I.icon += drop.basecolor + drop.overlays |= I + drop.transfer_mob_blood_dna(src) drop.basecolor = b_data["blood_color"] drop.update_icon() @@ -284,6 +289,8 @@ drop.transfer_mob_blood_dna(src) drop.basecolor = b_data["blood_color"] drop.update_icon() + if(emittor_intertia) + drop.newtonian_move(emittor_intertia) return // Find a blood decal or create a new one. @@ -305,12 +312,14 @@ if(shift_x || shift_y) B.off_floor = TRUE B.layer = BELOW_MOB_LAYER //So the blood lands ontop of things like posters, windows, etc. + if(emittor_intertia) + B.newtonian_move(emittor_intertia) -/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip, shift_x, shift_y) +/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip, shift_x, shift_y, emittor_intertia) if(!(NO_BLOOD in dna.species.species_traits)) ..() -/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip, shift_x, shift_y) +/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip, shift_x, shift_y, emittor_intertia) if(!T) T = get_turf(src) diff --git a/icons/effects/blood_weightless.dmi b/icons/effects/blood_weightless.dmi new file mode 100644 index 00000000000..9d9e3ea92f7 Binary files /dev/null and b/icons/effects/blood_weightless.dmi differ