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