Ports flying bloodsplatters from TG from Hippie for wounds (#18530)

* added, time to fix bugs

* requirements

* fix runtime

* yog changes

* Update atoms_movable.dm

* forgot this (very important)
This commit is contained in:
Molti
2023-05-02 00:53:52 -05:00
committed by GitHub
parent 048fbdbf68
commit 37e76e61bf
27 changed files with 209 additions and 44 deletions

View File

@@ -108,8 +108,9 @@
* * silent: Not actually necessary I don't think, was originally used for demoting wounds so they wouldn't make new messages, but I believe old_wound took over that, I may remove this shortly * * silent: Not actually necessary I don't think, was originally used for demoting wounds so they wouldn't make new messages, but I believe old_wound took over that, I may remove this shortly
* * old_wound: If our new wound is a replacement for one of the same time (promotion or demotion), we can reference the old one just before it's removed to copy over necessary vars * * old_wound: If our new wound is a replacement for one of the same time (promotion or demotion), we can reference the old one just before it's removed to copy over necessary vars
* * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
* * attack_direction: For bloodsplatters, if relevant
*/ */
/datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE) /datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE, attack_direction = null)
if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner) || !L.is_organic_limb()) if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner) || !L.is_organic_limb())
qdel(src) qdel(src)
return return
@@ -159,7 +160,7 @@
playsound(L.owner, sound_effect, 70 + 20 * severity, TRUE) playsound(L.owner, sound_effect, 70 + 20 * severity, TRUE)
if(!demoted) if(!demoted)
wound_injury(old_wound) wound_injury(old_wound, attack_direction = attack_direction)
second_wind() second_wind()
/// Remove the wound from whatever it's afflicting, and cleans up whateverstatus effects it had or modifiers it had on interaction times. ignore_limb is used for detachments where we only want to forget the victim /// Remove the wound from whatever it's afflicting, and cleans up whateverstatus effects it had or modifiers it had on interaction times. ignore_limb is used for detachments where we only want to forget the victim
@@ -190,16 +191,16 @@
* * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/slash/severe * * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/slash/severe
* * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
*/ */
/datum/wound/proc/replace_wound(new_type, smited = FALSE) /datum/wound/proc/replace_wound(new_type, smited = FALSE, attack_direction = attack_direction)
var/datum/wound/new_wound = new new_type var/datum/wound/new_wound = new new_type
already_scarred = TRUE already_scarred = TRUE
remove_wound(replaced=TRUE) remove_wound(replaced=TRUE)
new_wound.apply_wound(limb, old_wound = src, smited = smited) new_wound.apply_wound(limb, old_wound = src, smited = smited, attack_direction = attack_direction)
. = new_wound . = new_wound
qdel(src) qdel(src)
/// The immediate negative effects faced as a result of the wound /// The immediate negative effects faced as a result of the wound
/datum/wound/proc/wound_injury(datum/wound/old_wound = null) /datum/wound/proc/wound_injury(datum/wound/old_wound = null, attack_direction = null)
return return
/// Proc called to change the variable `limb` and react to the event. /// Proc called to change the variable `limb` and react to the event.
@@ -313,7 +314,7 @@
return (!QDELETED(src) && limb) return (!QDELETED(src) && limb)
/// When our parent bodypart is hurt /// When our parent bodypart is hurt
/datum/wound/proc/receive_damage(wounding_type, wounding_dmg, wound_bonus) /datum/wound/proc/receive_damage(wounding_type, wounding_dmg, wound_bonus, attack_direction)
return return
/// Called from cryoxadone and pyroxadone when they're proc'ing. Wounds will slowly be fixed separately from other methods when these are in effect. crappy name but eh /// Called from cryoxadone and pyroxadone when they're proc'ing. Wounds will slowly be fixed separately from other methods when these are in effect. crappy name but eh

View File

@@ -35,7 +35,7 @@
/* /*
Overwriting of base procs Overwriting of base procs
*/ */
/datum/wound/blunt/wound_injury(datum/wound/old_wound = null) /datum/wound/blunt/wound_injury(datum/wound/old_wound = null, attack_direction = null)
// hook into gaining/losing gauze so crit bone wounds can re-enable/disable depending if they're slung or not // hook into gaining/losing gauze so crit bone wounds can re-enable/disable depending if they're slung or not
RegisterSignals(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_GAUZE_DESTROYED), .proc/update_inefficiencies) RegisterSignals(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_GAUZE_DESTROYED), .proc/update_inefficiencies)
@@ -348,7 +348,7 @@
regen_ticks_needed = 240 // ticks every 2 seconds, 480 seconds, so roughly 8 minutes default regen_ticks_needed = 240 // ticks every 2 seconds, 480 seconds, so roughly 8 minutes default
// doesn't make much sense for "a" bone to stick out of your head // doesn't make much sense for "a" bone to stick out of your head
/datum/wound/blunt/critical/apply_wound(obj/item/bodypart/L, silent, datum/wound/old_wound, smited) /datum/wound/blunt/critical/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited, attack_direction = null)
if(L.body_zone == BODY_ZONE_HEAD) if(L.body_zone == BODY_ZONE_HEAD)
occur_text = "splits open, exposing a bare, cracked skull through the flesh and blood" occur_text = "splits open, exposing a bare, cracked skull through the flesh and blood"
examine_desc = "has an unsettling indent, with bits of skull poking out" examine_desc = "has an unsettling indent, with bits of skull poking out"

View File

@@ -12,7 +12,7 @@
already_scarred = TRUE // We manually assign scars for dismembers through endround missing limbs and aheals already_scarred = TRUE // We manually assign scars for dismembers through endround missing limbs and aheals
/// Our special proc for our special dismembering, the wounding type only matters for what text we have /// Our special proc for our special dismembering, the wounding type only matters for what text we have
/datum/wound/loss/proc/apply_dismember(obj/item/bodypart/dismembered_part, wounding_type=WOUND_SLASH, outright = FALSE) /datum/wound/loss/proc/apply_dismember(obj/item/bodypart/dismembered_part, wounding_type=WOUND_SLASH, outright = FALSE, attack_direction)
if(!istype(dismembered_part) || !dismembered_part.owner || !(dismembered_part.body_zone in viable_zones) || isalien(dismembered_part.owner) || !dismembered_part.can_dismember()) if(!istype(dismembered_part) || !dismembered_part.owner || !(dismembered_part.body_zone in viable_zones) || isalien(dismembered_part.owner) || !dismembered_part.can_dismember())
qdel(src) qdel(src)
return return
@@ -49,6 +49,8 @@
set_limb(dismembered_part) set_limb(dismembered_part)
second_wind() second_wind()
log_wound(victim, src) log_wound(victim, src)
if(wounding_type != WOUND_BURN && victim.blood_volume)
victim.spray_blood(attack_direction, severity)
dismembered_part.dismember(wounding_type == WOUND_BURN ? BURN : BRUTE) dismembered_part.dismember(wounding_type == WOUND_BURN ? BURN : BRUTE)
qdel(src) qdel(src)
return TRUE return TRUE

View File

@@ -23,8 +23,10 @@
/// If we let off blood when hit, the max blood lost is this * the incoming damage /// If we let off blood when hit, the max blood lost is this * the incoming damage
var/internal_bleeding_coefficient var/internal_bleeding_coefficient
/datum/wound/pierce/wound_injury(datum/wound/old_wound) /datum/wound/pierce/wound_injury(datum/wound/old_wound = null, attack_direction = null)
blood_flow = initial_flow blood_flow = initial_flow
if(attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY(victim))
victim.spray_blood(attack_direction, severity)
/datum/wound/pierce/receive_damage(wounding_type, wounding_dmg, wound_bonus) /datum/wound/pierce/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(victim.stat == DEAD || wounding_dmg < 5) if(victim.stat == DEAD || wounding_dmg < 5)

View File

@@ -30,13 +30,15 @@
/// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate)) /// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate))
var/datum/scar/highest_scar var/datum/scar/highest_scar
/datum/wound/slash/wound_injury(datum/wound/slash/old_wound = null) /datum/wound/slash/wound_injury(datum/wound/slash/old_wound = null, attack_direction = null)
blood_flow = initial_flow blood_flow = initial_flow
if(old_wound) if(old_wound)
blood_flow = max(old_wound.blood_flow, initial_flow) blood_flow = max(old_wound.blood_flow, initial_flow)
if(old_wound.severity > severity && old_wound.highest_scar) if(old_wound.severity > severity && old_wound.highest_scar)
highest_scar = old_wound.highest_scar highest_scar = old_wound.highest_scar
old_wound.highest_scar = null old_wound.highest_scar = null
else if(attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY(victim))
victim.spray_blood(attack_direction, severity)
if(!highest_scar) if(!highest_scar)
highest_scar = new highest_scar = new

View File

@@ -1270,7 +1270,7 @@
* * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present * * dealt_bare_wound_bonus- The bare_wound_bonus, if one was specified *and applied*, of the wounding attack. Not shown if armor was present
* * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods * * base_roll- Base wounding ability of an attack is a random number from 1 to (dealt_damage ** WOUND_DAMAGE_EXPONENT). This is the number that was rolled in there, before mods
*/ */
/proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll) /proc/log_wound(atom/victim, datum/wound/suffered_wound, dealt_damage, dealt_wound_bonus, dealt_bare_wound_bonus, base_roll, attack_direction = null)
if(QDELETED(victim) || !suffered_wound) if(QDELETED(victim) || !suffered_wound)
return return
var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote? var/message = "has suffered: [suffered_wound][suffered_wound.limb ? " to [suffered_wound.limb.name]" : null]"// maybe indicate if it's a promote/demote?
@@ -1287,6 +1287,9 @@
if(dealt_bare_wound_bonus) if(dealt_bare_wound_bonus)
message += " | BWB: [dealt_bare_wound_bonus]" message += " | BWB: [dealt_bare_wound_bonus]"
if(attack_direction)
message += " | AtkDir: [attack_direction]"
victim.log_message(message, LOG_ATTACK, color="blue") victim.log_message(message, LOG_ATTACK, color="blue")

View File

@@ -296,6 +296,17 @@
for(var/atom/movable/AM in buckled_mobs) for(var/atom/movable/AM in buckled_mobs)
AM.set_glide_size(target) AM.set_glide_size(target)
/**
* meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts)
* if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this
* most of the time you want forceMove()
*/
/atom/movable/proc/abstract_move(atom/new_loc)
var/atom/old_loc = loc
var/direction = get_dir(old_loc, new_loc)
loc = new_loc
Moved(old_loc, direction, TRUE)
//////////////////////////////////////// ////////////////////////////////////////
// Here's where we rewrite how byond handles movement except slightly different // Here's where we rewrite how byond handles movement except slightly different
// To be removed on step_ conversion // To be removed on step_ conversion

View File

@@ -29,6 +29,12 @@
icon_state = "gibbl1" icon_state = "gibbl1"
random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5") random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5")
/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
/obj/effect/decal/cleanable/blood/tracks /obj/effect/decal/cleanable/blood/tracks
icon_state = "tracks" icon_state = "tracks"
desc = "They look like tracks left by wheels." desc = "They look like tracks left by wheels."
@@ -209,8 +215,102 @@
/obj/effect/decal/cleanable/blood/footprints/can_bloodcrawl_in() /obj/effect/decal/cleanable/blood/footprints/can_bloodcrawl_in()
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY)) if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
return 1 return TRUE
return 0 return FALSE
/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
/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/wounds/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)
for(var/i in 1 to range)
step_towards(src,target_turf)
sleep(2) // Will be resolved pending Potato's moveloop rework
prev_loc = loc
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
qdel(src)
/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/kilo /obj/effect/decal/cleanable/blood/kilo
name = "remember kilo" name = "remember kilo"

View File

@@ -121,7 +121,12 @@
if(do_after(user, src.cleanspeed, target)) if(do_after(user, src.cleanspeed, target))
to_chat(user, span_notice("You clean \the [target.name].")) to_chat(user, span_notice("You clean \the [target.name]."))
target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
target.set_opacity(initial(target.opacity)) var/obj/structure/window/our_window = target
if(our_window.bloodied)
for(var/obj/effect/decal/cleanable/blood/iter_blood in our_window)
our_window.vis_contents -= iter_blood
qdel(iter_blood)
our_window.bloodied = FALSE
decreaseUses(user) decreaseUses(user)
else else
user.visible_message("[user] begins to clean \the [target.name] with [src]...", span_notice("You begin to clean \the [target.name] with [src]...")) user.visible_message("[user] begins to clean \the [target.name] with [src]...", span_notice("You begin to clean \the [target.name] with [src]..."))

View File

@@ -25,8 +25,9 @@
var/mutable_appearance/crack_overlay var/mutable_appearance/crack_overlay
var/real_explosion_block //ignore this, just use explosion_block var/real_explosion_block //ignore this, just use explosion_block
var/breaksound = "shatter" var/breaksound = "shatter"
var/hitsound = 'sound/effects/Glasshit.ogg' var/hitsound = 'sound/effects/Glasshit.ogg'
/// 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) /obj/structure/window/examine(mob/user)
. = ..() . = ..()

View File

@@ -56,3 +56,10 @@
for(var/mob/living/enterer as anything in arrived.get_all_contents_type(/mob/living)) for(var/mob/living/enterer as anything in arrived.get_all_contents_type(/mob/living))
to_chat(enterer, span_userdanger("This was a bad idea...")) to_chat(enterer, span_userdanger("This was a bad idea..."))
enterer.dust(TRUE, FALSE, TRUE) enterer.dust(TRUE, FALSE, TRUE)
/// This type of cordon will block ghosts from passing through it. Useful for stuff like Away Missions, where you feasibly want to block ghosts from entering to keep a certain map section a secret.
/turf/cordon/secret
name = "secret cordon (ghost blocking)"
/turf/cordon/secret/attack_ghost(mob/dead/observer/user)
return FALSE

View File

@@ -1305,3 +1305,21 @@
/mob/living/carbon/proc/force_drink_text(obj/O, mob/living/carbon/C, mob/user) /mob/living/carbon/proc/force_drink_text(obj/O, mob/living/carbon/C, mob/user)
return dna?.species.force_drink_text(O, C, user) return dna?.species.force_drink_text(O, C, user)
/**
* 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.add_blood_DNA(return_blood_DNA())
our_splatter.blood_dna_info = get_blood_dna_list()
var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength)
INVOKE_ASYNC(our_splatter, /obj/effect/decal/cleanable/blood/hitsplatter/.proc/fly_towards, targ, splatter_strength)

View File

@@ -179,8 +179,9 @@
affecting = bodyparts[1] affecting = bodyparts[1]
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
send_item_attack_message(I, user, affecting.name, affecting) send_item_attack_message(I, user, affecting.name, affecting)
if(I.force) if(I.force)
apply_damage(I.force, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.sharpness) var/attack_direction = get_dir(user, src)
apply_damage(I.force, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.sharpness, attack_direction = attack_direction)
if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC)
if(prob(33)) if(prob(33))
I.add_mob_blood(src) I.add_mob_blood(src)

View File

@@ -1,6 +1,6 @@
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) /mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone) SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone)
var/hit_percent = (100-blocked)/100 var/hit_percent = (100-blocked)/100
if(!damage || hit_percent <= 0) if(!damage || hit_percent <= 0)
@@ -19,13 +19,13 @@
switch(damagetype) switch(damagetype)
if(BRUTE) if(BRUTE)
if(BP) if(BP)
if(BP.receive_damage(damage * hit_percent, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) if(BP.receive_damage(damage * hit_percent, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness, attack_direction = attack_direction))
update_damage_overlays() update_damage_overlays()
else //no bodypart, we deal damage with a more general method. else //no bodypart, we deal damage with a more general method.
adjustBruteLoss(damage * hit_percent) adjustBruteLoss(damage * hit_percent)
if(BURN) if(BURN)
if(BP) if(BP)
if(BP.receive_damage(0, damage * hit_percent, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) if(BP.receive_damage(0, damage * hit_percent, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness, attack_direction = attack_direction))
update_damage_overlays() update_damage_overlays()
else else
adjustFireLoss(damage * hit_percent) adjustFireLoss(damage * hit_percent)

View File

@@ -1,7 +1,7 @@
/mob/living/carbon/human/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) /mob/living/carbon/human/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null)
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, wound_bonus, bare_wound_bonus, sharpness) return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, wound_bonus, bare_wound_bonus, sharpness, attack_direction)
/mob/living/carbon/human/revive(full_heal = 0, admin_revive = 0) /mob/living/carbon/human/revive(full_heal = 0, admin_revive = 0)
if(..()) if(..())

View File

@@ -354,7 +354,8 @@
if(!affecting) if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST) affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor = run_armor_check(affecting, MELEE, armour_penetration = M.armour_penetration) var/armor = run_armor_check(affecting, MELEE, armour_penetration = M.armour_penetration)
apply_damage(damage, M.melee_damage_type, affecting, armor, wound_bonus = M.wound_bonus, bare_wound_bonus = M.bare_wound_bonus, sharpness = M.sharpness) var/attack_direction = get_dir(M, src)
apply_damage(damage, M.melee_damage_type, affecting, armor, wound_bonus = M.wound_bonus, bare_wound_bonus = M.bare_wound_bonus, sharpness = M.sharpness, attack_direction = attack_direction)
/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M) /mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M)

View File

@@ -1584,11 +1584,12 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(user.limb_destroyer) if(user.limb_destroyer)
target.dismembering_strike(user, affecting.body_zone) target.dismembering_strike(user, affecting.body_zone)
var/attack_direction = get_dir(user, target)
if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage
target.apply_damage(damage*1.5, user.dna.species.attack_type, affecting, armor_block) target.apply_damage(damage*1.5, user.dna.species.attack_type, affecting, armor_block, attack_direction = attack_direction)
log_combat(user, target, "kicked") log_combat(user, target, "kicked")
else//other attacks deal full raw damage + 1.5x in stamina damage else//other attacks deal full raw damage + 1.5x in stamina damage
target.apply_damage(damage, user.dna.species.attack_type, affecting, armor_block) target.apply_damage(damage, user.dna.species.attack_type, affecting, armor_block, attack_direction = attack_direction)
target.apply_damage(damage*1.5, STAMINA, affecting, armor_block) target.apply_damage(damage*1.5, STAMINA, affecting, armor_block)
log_combat(user, target, "punched") log_combat(user, target, "punched")
@@ -1778,7 +1779,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
H.send_item_attack_message(I, user, hit_area, affecting) H.send_item_attack_message(I, user, hit_area, affecting)
apply_damage(I.force * weakness, I.damtype, def_zone, armor_block, H, wound_bonus = Iwound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.sharpness) var/attack_direction = get_dir(user, H)
apply_damage(I.force * weakness, I.damtype, def_zone, armor_block, H, wound_bonus = Iwound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.sharpness, attack_direction = attack_direction)
if(!I.force) if(!I.force)
return FALSE //item force is zero return FALSE //item force is zero
@@ -1845,8 +1847,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
H.forcesay(GLOB.hit_appends) //forcesay checks stat already. H.forcesay(GLOB.hit_appends) //forcesay checks stat already.
return TRUE return TRUE
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) /datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null)
SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal) SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness, attack_direction) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal)
var/hit_percent = (100-(blocked+armor))/100 var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
if(!damage || hit_percent <= 0) if(!damage || hit_percent <= 0)
@@ -1866,14 +1868,14 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(BRUTE) if(BRUTE)
H.damageoverlaytemp = 20 H.damageoverlaytemp = 20
if(BP) if(BP)
if(BP.receive_damage(damage * hit_percent * brutemod * H.physiology.brute_mod, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) if(BP.receive_damage(damage * hit_percent * brutemod * H.physiology.brute_mod, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness, attack_direction = attack_direction))
H.update_damage_overlays() H.update_damage_overlays()
else//no bodypart, we deal damage with a more general method. else//no bodypart, we deal damage with a more general method.
H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod) H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod)
if(BURN) if(BURN)
H.damageoverlaytemp = 20 H.damageoverlaytemp = 20
if(BP) if(BP)
if(BP.receive_damage(0, damage * hit_percent * burnmod * H.physiology.burn_mod, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) if(BP.receive_damage(0, damage * hit_percent * burnmod * H.physiology.burn_mod, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness, attack_direction = attack_direction))
H.update_damage_overlays() H.update_damage_overlays()
else else
H.adjustFireLoss(damage * hit_percent * burnmod * H.physiology.burn_mod) H.adjustFireLoss(damage * hit_percent * burnmod * H.physiology.burn_mod)

View File

@@ -26,7 +26,7 @@
screamsound = 'yogstation/sound/voice/eggperson/egg_scream.ogg' // (Hopefully) the sound of an egg cracking screamsound = 'yogstation/sound/voice/eggperson/egg_scream.ogg' // (Hopefully) the sound of an egg cracking
species_language_holder = /datum/language_holder/egg species_language_holder = /datum/language_holder/egg
/datum/species/egg/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) /datum/species/egg/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE, attack_direction = null)
if(damagetype == BRUTE) // Dynamic brute resist based on burn damage. The more fried the egg, the harder the shell!! if(damagetype == BRUTE) // Dynamic brute resist based on burn damage. The more fried the egg, the harder the shell!!
var/x = H.getFireLoss() var/x = H.getFireLoss()
brutemod = EGG_ALPHA * x*x + EGG_BETA * x + EGG_MAXBRUTEMOD //A polynomial, to determine how much brute we take. https://www.desmos.com/calculator/dwxdxwt0rl brutemod = EGG_ALPHA * x*x + EGG_BETA * x + EGG_MAXBRUTEMOD //A polynomial, to determine how much brute we take. https://www.desmos.com/calculator/dwxdxwt0rl

View File

@@ -63,7 +63,7 @@
/datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H,amount) /datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H,amount)
. = min(20, amount) . = min(20, amount)
/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) /datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null)
. = ..() . = ..()
if(.) if(.)
regen_cooldown = world.time + REGENERATION_DELAY regen_cooldown = world.time + REGENERATION_DELAY
@@ -153,7 +153,7 @@
/datum/species/zombie/infectious/gamemode/spec_stun(mob/living/carbon/human/H,amount) /datum/species/zombie/infectious/gamemode/spec_stun(mob/living/carbon/human/H,amount)
. = 0 . = 0
/datum/species/zombie/infectious/gamemode/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) /datum/species/zombie/infectious/gamemode/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE, attack_direction = null)
if(damagetype == STAMINA) if(damagetype == STAMINA)
return return
. = ..() . = ..()

View File

@@ -8,7 +8,7 @@
Returns Returns
standard 0 if fail standard 0 if fail
*/ */
/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) /mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone) SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone)
var/hit_percent = (100-blocked)/100 var/hit_percent = (100-blocked)/100
if(!damage || (hit_percent <= 0)) if(!damage || (hit_percent <= 0))

View File

@@ -71,7 +71,8 @@
if((istype(P, /obj/item/projectile/energy/nuclear_particle)) && (getarmor(null, RAD) >= 100)) if((istype(P, /obj/item/projectile/energy/nuclear_particle)) && (getarmor(null, RAD) >= 100))
P.damage = 0 P.damage = 0
else else
apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus = P.wound_bonus, bare_wound_bonus = P.bare_wound_bonus, sharpness = P.get_sharpness()) var/attack_direction = get_dir(P.starting, src)
apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus = P.wound_bonus, bare_wound_bonus = P.bare_wound_bonus, sharpness = P.get_sharpness(), attack_direction = attack_direction)
if(P.dismemberment) if(P.dismemberment)
check_projectile_dismemberment(P, def_zone) check_projectile_dismemberment(P, def_zone)
if(P.penetrating && (P.penetration_type == 0 || P.penetration_type == 2) && P.penetrations > 0) if(P.penetrating && (P.penetration_type == 0 || P.penetration_type == 2) && P.penetrations > 0)

View File

@@ -1,5 +1,5 @@
/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) /mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null)
var/hit_percent = (100-blocked)/100 var/hit_percent = (100-blocked)/100
if(!damage || (hit_percent <= 0)) if(!damage || (hit_percent <= 0))
return 0 return 0

View File

@@ -513,6 +513,12 @@
if(zMove(DOWN, TRUE)) if(zMove(DOWN, TRUE))
to_chat(src, span_notice("You move down.")) to_chat(src, span_notice("You move down."))
/mob/abstract_move(atom/destination)
var/turf/new_turf = get_turf(destination)
if(new_turf && (istype(new_turf, /turf/cordon/secret) || is_secret_level(new_turf.z)) && !client?.holder)
return
return ..()
///Move a mob between z levels, if it's valid to move z's on this turf ///Move a mob between z levels, if it's valid to move z's on this turf
/mob/proc/zMove(dir, feedback = FALSE) /mob/proc/zMove(dir, feedback = FALSE)
if(dir != UP && dir != DOWN) if(dir != UP && dir != DOWN)

View File

@@ -208,7 +208,7 @@
//Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all. //Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all.
//Damage will not exceed max_damage using this proc //Damage will not exceed max_damage using this proc
//Cannot apply negative damage //Cannot apply negative damage
/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) // maybe separate BRUTE_SHARP and BRUTE_OTHER eventually somehow hmm /obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE, attack_direction = null) // maybe separate BRUTE_SHARP and BRUTE_OTHER eventually somehow hmm
var/hit_percent = (100-blocked)/100 var/hit_percent = (100-blocked)/100
if((!brute && !burn && !stamina) || hit_percent <= 0) if((!brute && !burn && !stamina) || hit_percent <= 0)
return FALSE return FALSE
@@ -280,7 +280,7 @@
// now we have our wounding_type and are ready to carry on with wounds and dealing the actual damage // now we have our wounding_type and are ready to carry on with wounds and dealing the actual damage
if(owner && wounding_dmg >= WOUND_MINIMUM_DAMAGE && wound_bonus != CANT_WOUND) if(owner && wounding_dmg >= WOUND_MINIMUM_DAMAGE && wound_bonus != CANT_WOUND)
check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus) check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus, attack_direction)
for(var/i in wounds) for(var/i in wounds)
var/datum/wound/iter_wound = i var/datum/wound/iter_wound = i
@@ -373,7 +373,7 @@
* * wound_bonus- The wound_bonus of an attack * * wound_bonus- The wound_bonus of an attack
* * bare_wound_bonus- The bare_wound_bonus of an attack * * bare_wound_bonus- The bare_wound_bonus of an attack
*/ */
/obj/item/bodypart/proc/check_wounding(woundtype, damage, wound_bonus, bare_wound_bonus) /obj/item/bodypart/proc/check_wounding(woundtype, damage, wound_bonus, bare_wound_bonus, attack_direction)
// note that these are fed into an exponent, so these are magnified // note that these are fed into an exponent, so these are magnified
if(HAS_TRAIT(owner, TRAIT_EASILY_WOUNDED)) if(HAS_TRAIT(owner, TRAIT_EASILY_WOUNDED))
damage *= 1.5 damage *= 1.5
@@ -396,7 +396,7 @@
if(injury_roll > WOUND_DISMEMBER_OUTRIGHT_THRESH && (get_damage() / max_damage * 50)) if(injury_roll > WOUND_DISMEMBER_OUTRIGHT_THRESH && (get_damage() / max_damage * 50))
var/datum/wound/loss/dismembering = new var/datum/wound/loss/dismembering = new
dismembering.apply_dismember(src, woundtype, outright=TRUE) dismembering.apply_dismember(src, woundtype, outright = TRUE, attack_direction = attack_direction)
return return
// quick re-check to see if bare_wound_bonus applies, for the benefit of log_wound(), see about getting the check from check_wounding_mods() somehow // quick re-check to see if bare_wound_bonus applies, for the benefit of log_wound(), see about getting the check from check_wounding_mods() somehow
@@ -426,11 +426,11 @@
var/datum/wound/new_wound var/datum/wound/new_wound
if(replaced_wound) if(replaced_wound)
new_wound = replaced_wound.replace_wound(possible_wound) new_wound = replaced_wound.replace_wound(possible_wound)
log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) // dismembering wounds are logged in the apply_wound() for loss wounds since they delete themselves immediately, these will be immediately returned log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll, attack_direction = attack_direction) // dismembering wounds are logged in the apply_wound() for loss wounds since they delete themselves immediately, these will be immediately returned
else else
new_wound = new possible_wound new_wound = new possible_wound
new_wound.apply_wound(src) new_wound.apply_wound(src)
log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll, attack_direction = attack_direction)
return new_wound return new_wound
// try forcing a specific wound, but only if there isn't already a wound of that severity or greater for that type on this bodypart // try forcing a specific wound, but only if there isn't already a wound of that severity or greater for that type on this bodypart

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

View File

@@ -16,3 +16,5 @@ revolverspin1, revolverspin2, and revolverspin3 are cut from a recording
captured by the BBC captured by the BBC
"Gunfire & Guns: Revolver, Chambers spinning.": http://bbcsfx.acropolis.org.uk/assets/07027165.wav "Gunfire & Guns: Revolver, Chambers spinning.": http://bbcsfx.acropolis.org.uk/assets/07027165.wav
bbc.co.uk - (c) copyright 2018 BBC bbc.co.uk - (c) copyright 2018 BBC
splatter.ogg adapted from https://freesound.org/people/Rocktopus/sounds/233418/