diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 7e078184b7..b4f1851bc3 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -492,6 +492,14 @@ Turf and target are separate in case you want to teleport some distance from a t
assembled += A
return assembled
+//Return a list of atoms in a location of a given type. Can be refined to look for pixel-shift.
+/proc/get_atoms_of_type(var/atom/here, var/type, var/check_shift, var/shift_x = 0, var/shift_y = 0)
+ . = list()
+ if(here)
+ for(var/atom/thing in here)
+ if(istype(thing, type) && (check_shift && thing.pixel_x == shift_x && thing.pixel_y == shift_y))
+ . += thing
+
//Step-towards method of determining whether one atom can see another. Similar to viewers()
/proc/can_see(atom/source, atom/target, length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate.
var/turf/current = get_turf(source)
@@ -756,6 +764,29 @@ Turf and target are separate in case you want to teleport some distance from a t
loc = loc.loc
return null
+/proc/pixel_shift_dir(var/dir, var/amount_x = 32, var/amount_y = 32) //Returns a list with pixel_shift values that will shift an object's icon one tile in the direction passed.
+ amount_x = min(max(0, amount_x), 32) //No less than 0, no greater than 32.
+ amount_y = min(max(0, amount_x), 32)
+ var/list/shift = list("x" = 0, "y" = 0)
+ switch(dir)
+ if(NORTH)
+ shift["y"] = amount_y
+ if(SOUTH)
+ shift["y"] = -amount_y
+ if(EAST)
+ shift["x"] = amount_x
+ if(WEST)
+ shift["x"] = -amount_x
+ if(NORTHEAST)
+ shift = list("x" = amount_x, "y" = amount_y)
+ if(NORTHWEST)
+ shift = list("x" = -amount_x, "y" = amount_y)
+ if(SOUTHEAST)
+ shift = list("x" = amount_x, "y" = -amount_y)
+ if(SOUTHWEST)
+ shift = list("x" = -amount_x, "y" = -amount_y)
+
+ return shift
//For objects that should embed, but make no sense being is_sharp or is_pointed()
//e.g: rods
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 6455b4ccb6..7d40df8aa4 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -126,8 +126,16 @@
I.add_mob_blood(src)
var/turf/location = get_turf(src)
if(iscarbon(src))
- var/mob/living/carbon/C = src
- C.bleed(totitemdamage)
+ if(ishuman(src))
+ var/mob/living/carbon/human/H = src
+ var/armorsave = H.getarmor(H, "melee")
+ if(armorsave >= 30) // armor is useful again!
+ H.bleed_rate += round((totitemdamage * 0.15)) // internal bleeding, took it on the joint, what have you.
+ else
+ H.bleed_rate += round((totitemdamage * 0.50)) //it's not a 1:1 deletion of blood, but it's worrysome enough that you should get treated asap
+ else
+ var/mob/living/carbon/C = src
+ C.bleed(totitemdamage)
else
add_splatter_floor(location)
if(totitemdamage >= 10 && get_dist(user, src) <= 1) //people with TK won't get smeared with blood
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 1af15b0383..842b230b53 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -35,9 +35,12 @@
icon_state += "-old"
add_blood_DNA(list("Non-human DNA" = "A+"))
-/obj/effect/decal/cleanable/blood/splatter
+/obj/effect/decal/cleanable/blood/splats
random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5")
+/obj/effect/decal/cleanable/blood/splatter
+ random_icon_states = list("splatter1", "splatter2", "splatter3", "splatter4", "splatter5")
+
/obj/effect/decal/cleanable/blood/tracks
icon_state = "tracks"
desc = "They look like tracks left by wheels."
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 5610cbad9a..2826ef97a4 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -17,15 +17,15 @@
var/self_delay = 50
/obj/item/stack/medical/attack(mob/living/M, mob/user)
-
- if(M.stat == DEAD && !stop_bleeding)
+//Let's see if this isn't a horrid mistake. Removes death check from applying medical stack stuff. should help.
+/* if(M.stat == DEAD && !stop_bleeding)
var/t_him = "it"
if(M.gender == MALE)
t_him = "him"
else if(M.gender == FEMALE)
t_him = "her"
to_chat(user, "\The [M] is dead, you cannot help [t_him]!")
- return
+ return */
if(!iscarbon(M) && !isanimal(M))
to_chat(user, "You don't know how to apply \the [src] to [M]!")
@@ -125,7 +125,7 @@
icon_state = "gauze"
stop_bleeding = 1800
self_delay = 20
- max_amount = 12
+ max_amount = 36 //bleeding is a major issue now, let's be nice to our medical team
/obj/item/stack/medical/gauze/attackby(obj/item/I, mob/user, params)
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index b9a75f88af..0041801872 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -76,8 +76,13 @@
L.forceMove(drop_location())
L.emote("scream")
if(iscarbon(L))
- var/mob/living/carbon/C = L
- C.bleed(30)
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ H.bleed_rate += 10
+ H.bleed(10) //less instant blood damage because you're bleeding, and bleeding is bad news.
+ else
+ var/mob/living/carbon/C = L
+ C.bleed(30)
else
L.add_splatter_floor()
L.adjustBruteLoss(30)
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 770e344636..80036a60e0 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -2,6 +2,9 @@
BLOOD SYSTEM
****************************************************/
+#define EXOTIC_BLEED_MULTIPLIER 4 //Multiplies the actually bled amount by this number for the purposes of turf reaction calculations.
+
+
/mob/living/carbon/human/proc/suppress_bloodloss(amount)
if(bleedsuppress)
return
@@ -80,6 +83,9 @@
var/obj/item/bodypart/BP = X
var/brutedamage = BP.brute_dam
+ if(BP.status == BODYPART_ROBOTIC) //for the moment, synth limbs won't bleed, but soon, my pretty.
+ continue
+
//We want an accurate reading of .len
listclearnulls(BP.embedded_objects)
temp_bleed += 0.5*BP.embedded_objects.len
@@ -105,9 +111,11 @@
/mob/living/carbon/human/bleed(amt)
amt *= physiology.bleed_mod
if(!(NOBLOOD in dna.species.species_traits))
- ..()
-
-
+ .=..()
+ if(dna.species.exotic_blood && .) // Do we have exotic blood, and have we left any on the ground?
+ var/datum/reagent/R = GLOB.chemical_reagents_list[get_blood_id()]
+ if(istype(R) && isturf(loc))
+ R.reaction_turf(get_turf(src), amt * EXOTIC_BLEED_MULTIPLIER)
/mob/living/proc/restore_blood()
blood_volume = initial(blood_volume)
@@ -259,7 +267,7 @@
. = safe
//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, shift_x, shift_y)
if(get_blood_id() == null)
return
if(!T)
@@ -274,6 +282,8 @@
drop.drips++
drop.add_overlay(pick(drop.random_icon_states))
drop.transfer_mob_blood_dna(src)
+ src.transfer_blood_to(drop, 2)
+ drop.update_icon()
return
else
temp_blood_DNA = list()
@@ -282,17 +292,30 @@
else
drop = new(T, get_static_viruses())
drop.transfer_mob_blood_dna(src)
+ src.transfer_blood_to(drop, 2)
+ drop.update_icon()
return
// Find a blood decal or create a new one.
- var/obj/effect/decal/cleanable/blood/B = locate() in T
+ var/obj/effect/decal/cleanable/blood/splats/B = locate() in T
+ var/list/bloods = get_atoms_of_type(T, B, TRUE, 0, 0) //Get all the non-projectile-splattered blood on this turf (not pixel-shifted).
+ if(shift_x || shift_y)
+ bloods = get_atoms_of_type(T, B, TRUE, shift_x, shift_y) //Get all the projectile-splattered blood at these pixels on this turf (pixel-shifted).
+ B = locate() in bloods
if(!B)
- B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses())
- if (B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit
+ B = new /obj/effect/decal/cleanable/blood/splats(T, get_static_viruses())
+ if(B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit
B.bloodiness += BLOOD_AMOUNT_PER_DECAL
B.transfer_mob_blood_dna(src) //give blood info to the blood decal.
+ src.transfer_blood_to(B, 10) //very heavy bleeding, should logically leave larger pools
if(temp_blood_DNA)
B.blood_DNA |= temp_blood_DNA
+ B.pixel_x = (shift_x)
+ B.pixel_y = (shift_y)
+ B.update_icon()
+ if(shift_x || shift_y)
+ B.layer = BELOW_MOB_LAYER //So the blood lands ontop of things like posters, windows, etc.
+
/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip)
if(!(NOBLOOD in dna.species.species_traits))
@@ -313,6 +336,43 @@
if(!B)
B = new(T)
+/mob/living/proc/add_splash_floor(turf/T)
+ if(get_blood_id() == null)
+ return
+ if(!T)
+ T = get_turf(src)
+
+ var/list/temp_blood_DNA
+
+ // Find a blood decal or create a new one.
+ var/obj/effect/decal/cleanable/blood/B = locate() in T
+ if(!B)
+ B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses())
+ if(B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit
+ B.bloodiness += BLOOD_AMOUNT_PER_DECAL
+ B.transfer_mob_blood_dna(src) //give blood info to the blood decal.
+ if(temp_blood_DNA)
+ B.blood_DNA |= temp_blood_DNA
+
+/mob/living/carbon/human/add_splash_floor(turf/T)
+ if(!(NOBLOOD in dna.species.species_traits))
+ ..()
+
+/mob/living/carbon/alien/add_splash_floor(turf/T)
+ if(!T)
+ T = get_turf(src)
+ var/obj/effect/decal/cleanable/blood/splatter/B = locate() in T.contents
+ if(!B)
+ B = new(T)
+ B.blood_DNA["UNKNOWN DNA"] = "X*"
+
+/mob/living/silicon/robot/add_splash_floor(turf/T)
+ if(!T)
+ T = get_turf(src)
+ var/obj/effect/decal/cleanable/oil/B = locate() in T.contents
+ if(!B)
+ B = new(T)
+
//This is a terrible way of handling it.
/mob/living/proc/ResetBloodVol()
if(ishuman(src))
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 28cc41c9ca..bbfd1db6b9 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1687,7 +1687,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
bloody = 1
var/turf/location = H.loc
if(istype(location))
- H.bleed(totitemdamage)
+ H.bleed_rate += 2 //This usually stacks pretty quickly, death by a thousand cuts, etc etc.
if(get_dist(user, H) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(H)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 44185b601f..b5c859432c 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -174,11 +174,38 @@
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, bloodtype_to_color(H.dna.blood_type))
else
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, bloodtype_to_color())
+
if(iscarbon(L))
- var/mob/living/carbon/C = L
- C.bleed(damage)
- else
- L.add_splatter_floor(target_loca)
+ if(ishuman(L)
+ var/mob/living/carbon/human/H = L
+ var/armorsave
+ if(flag == "bullet")
+ armorsave = H.getarmor(B, "bullet")
+ else if(flag == "energy")
+ armorsave = H.getarmor(B, "energy")
+ else if(flag == "magic")
+ armorsave = H.getarmor(B, "magic")
+ if(armorsave >= 30) // armor is useful again, yay.
+ H.bleed_rate += (damage * 0.15) // a tiny bit of shrapnel and internal bleeding, I guess.
+ else
+ H.bleed_rate += (damage * 0.50) //it's not a 1:1 deletion of blood, but it's worrysome enough that you should get treated asap
+ else
+ var/mob/living/carbon/C = L
+ C.bleed(damage)
+
+ if(prob(33))
+ var/list/shift = list("x" = 0, "y" = 0)
+ var/turf/step_over = get_step(target_loca, splatter_dir)
+
+ if(get_splatter_blockage(step_over, target, splatter_dir, target_loca)) //If you can't cross the tile or any of its relevant obstacles...
+ shift = pixel_shift_dir(splatter_dir) //Pixel shift the blood there instead (so you can't see wallsplatter through walls).
+ else
+ target_loca = step_over
+ L.add_splatter_floor(target_loca, shift_x = shift["x"], shift_y = shift["y"])
+ if(ishuman(L))
+ for(var/mob/living/carbon/human/M in step_over) //Bloody the mobs who're infront of the spray.
+ M.bloody_hands = rand(2, 4)
+
else if(impact_effect_type && !hitscan)
new impact_effect_type(target_loca, hitx, hity)
@@ -213,6 +240,10 @@
return L.apply_effects(stun, knockdown, unconscious, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter)
+/obj/item/projectile/proc/get_splatter_blockage(var/turf/step_over, var/atom/target, var/splatter_dir, var/target_loca) //Check whether the place we want to splatter blood is blocked (i.e. by windows).
+ if(step_over.density && !step_over.CanPass(target, step_over, 1)) //Preliminary simple check.
+ return TRUE
+
/obj/item/projectile/proc/vol_by_damage()
if(src.damage)
return CLAMP((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 9341fb6c25..8ef623df73 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -27,6 +27,8 @@
return 1
add_mob_blood(C)
C.bleed(40)
+ var/mob/living/carbon/human/H = C
+ H.bleed_rate += 40
var/direction = pick(GLOB.cardinals)
var/t_range = rand(2,max(throw_range/2, 2))
var/turf/target_turf = get_turf(src)
@@ -53,6 +55,8 @@
var/organ_spilled = 0
var/turf/T = get_turf(C)
C.bleed(50)
+ var/mob/living/carbon/human/H = C
+ H.bleed_rate += 60
playsound(get_turf(C), 'sound/misc/splort.ogg', 80, 1)
for(var/X in C.internal_organs)
var/obj/item/organ/O = X