Mob attackedby / check_block refactor, plus some minor cleanup of attack_x procs (#79563)

## About The Pull Request

- Deletes `spec_attacked_by`
- Elements simple/basic mob attack threshold
- There was a skeleton mob that "mimics armor" but didn't use the actual
mimicing armor thing we have, so I changed that.
- Moves `check_shields` down to the living level, renames it to
`check_block`
 - Martial art blocking is now signalized (only CQC uses it anyways) 
- Cleaned up a bit of `attack_x` procs, but not a lot. Should have an
entire PR dedicated to this .... mess.
 - Deprecates `/obj/item/melee`

## Why It's Good For The Game

Second verse, same as the first. 

- Less bad species related procs. 
- Largely brings a lot of code in line, making combat more consistent
across types.
- Makes it a lot easier to add new code relating to blocking or taking
damage.

## Changelog

🆑 Melbert
refactor: Refactored another large chuck of attack code, primarily
involving melee item attacks and non-human mob attacks. Report if you
see anything weird
fix: Pacifists clicking on simple robots or silicons no longer causes
sparks
fix: Blocked thrown batons are now properly... blocked
/🆑
This commit is contained in:
MrMelbert
2023-11-17 19:57:51 -07:00
committed by GitHub
parent e4751e9aa2
commit 365e50bbce
41 changed files with 520 additions and 527 deletions
+10 -1
View File
@@ -132,13 +132,22 @@ DEFINE_BITFIELD(status_flags, list(
//slowdown when crawling
#define CRAWLING_ADD_SLOWDOWN 4
//Attack types for checking shields/hit reactions
//Attack types for checking block reactions
/// Attack was made with a melee weapon
#define MELEE_ATTACK 1
/// Attack is a punch or kick.
/// Mob attacks are not classified as unarmed (currently).
#define UNARMED_ATTACK 2
/// A projectile is hitting us.
#define PROJECTILE_ATTACK 3
/// A thrown item is hitting us.
#define THROWN_PROJECTILE_ATTACK 4
/// We're being tackled or leaped at.
#define LEAP_ATTACK 5
/// Used in check block to get what mob is attacking the blocker.
#define GET_ASSAILANT(weapon) (get(weapon, /mob/living))
//attack visual effects
#define ATTACK_EFFECT_PUNCH "punch"
#define ATTACK_EFFECT_KICK "kick"
@@ -133,9 +133,6 @@
#define COMSIG_HUMAN_CORETEMP_CHANGE "human_coretemp_change"
///from /datum/species/handle_fire. Called when the human is set on fire and burning clothes and stuff
#define COMSIG_HUMAN_BURNING "human_burning"
///from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration, damage_type)
#define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields"
#define SHIELD_BLOCK (1<<0)
///from /mob/living/carbon/human/proc/force_say(): ()
#define COMSIG_HUMAN_FORCESAY "human_forcesay"
@@ -222,6 +222,10 @@
/// From /datum/ai/behavior/climb_tree/perform() : (mob/living/basic/living_pawn)
#define COMSIG_LIVING_CLIMB_TREE "living_climb_tree"
///from /mob/living/proc/check_block(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration, damage_type)
#define COMSIG_LIVING_CHECK_BLOCK "living_check_block"
#define SUCCESSFUL_BLOCK (1<<0)
/// Sent on a mob from /datum/component/mob_chain when component is attached with it as the "front" : (mob/living/basic/tail)
#define COMSIG_MOB_GAINED_CHAIN_TAIL "living_gained_chain_tail"
/// Sent on a mob from /datum/component/mob_chain when component is detached from it as the "front" : (mob/living/basic/tail)
+143 -21
View File
@@ -257,32 +257,154 @@
CRASH("areas are NOT supposed to have attacked_by() called on them!")
/mob/living/attacked_by(obj/item/attacking_item, mob/living/user)
send_item_attack_message(attacking_item, user)
if(!attacking_item.force)
return FALSE
var/targeting = check_zone(user.zone_selected)
if(user != src)
var/zone_hit_chance = 80
if(body_position == LYING_DOWN)
zone_hit_chance += 10
targeting = get_random_valid_zone(targeting, zone_hit_chance)
var/targeting_human_readable = parse_zone(targeting)
send_item_attack_message(attacking_item, user, targeting_human_readable, targeting)
var/armor_block = min(run_armor_check(
def_zone = targeting,
attack_flag = MELEE,
absorb_text = span_notice("Your armor has protected your [targeting_human_readable]!"),
soften_text = span_warning("Your armor has softened a hit to your [targeting_human_readable]!"),
armour_penetration = attacking_item.armour_penetration,
weak_against_armour = attacking_item.weak_against_armour,
), ARMOR_MAX_BLOCK)
var/damage = attacking_item.force
if(mob_biotypes & MOB_ROBOTIC)
damage *= attacking_item.demolition_mod
apply_damage(damage, attacking_item.damtype, attacking_item = attacking_item)
if(attacking_item.damtype == BRUTE && prob(33))
var/wounding = attacking_item.wound_bonus
if((attacking_item.item_flags & SURGICAL_TOOL) && !user.combat_mode && body_position == LYING_DOWN && (LAZYLEN(surgeries) > 0))
wounding = CANT_WOUND
if(user != src)
// This doesn't factor in armor, or most damage modifiers (physiology). Your mileage may vary
if(check_block(attacking_item, damage, "the [attacking_item.name]", MELEE_ATTACK, attacking_item.armour_penetration, attacking_item.damtype))
return FALSE
SEND_SIGNAL(attacking_item, COMSIG_ITEM_ATTACK_ZONE, src, user, targeting)
if(damage <= 0)
return FALSE
if(ishuman(src) || client) // istype(src) is kinda bad, but it's to avoid spamming the blackbox
SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[attacking_item.force]", "[attacking_item.type]"))
SSblackbox.record_feedback("tally", "zone_targeted", 1, targeting_human_readable)
var/damage_done = apply_damage(
damage = damage,
damagetype = attacking_item.damtype,
def_zone = targeting,
blocked = armor_block,
wound_bonus = wounding,
bare_wound_bonus = attacking_item.bare_wound_bonus,
sharpness = attacking_item.get_sharpness(),
attack_direction = get_dir(user, src),
attacking_item = attacking_item,
)
attack_effects(damage_done, targeting, armor_block, attacking_item, user)
return TRUE
/**
* Called when we take damage, used to cause effects such as a blood splatter.
*
* Return TRUE if an effect was done, FALSE otherwise.
*/
/mob/living/proc/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
if(damage_done > 0 && attacking_item.damtype == BRUTE && prob(25 + damage_done * 2))
attacking_item.add_mob_blood(src)
var/turf/location = get_turf(src)
add_splatter_floor(location)
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
return TRUE //successful attack
add_splatter_floor(get_turf(src))
if(get_dist(attacker, src) <= 1)
attacker.add_mob_blood(src)
return TRUE
/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
if(!attack_threshold_check(I.force, I.damtype, MELEE, FALSE))
playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), TRUE, -1)
else
return ..()
return FALSE
/mob/living/basic/attacked_by(obj/item/I, mob/living/user)
if(!attack_threshold_check(I.force, I.damtype, MELEE, FALSE))
playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), TRUE, -1)
else
return ..()
/mob/living/silicon/robot/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
if(damage_done > 0 && attacking_item.damtype != STAMINA && stat != DEAD)
spark_system.start()
. = TRUE
return ..() || .
/mob/living/silicon/ai/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
if(damage_done > 0 && attacking_item.damtype != STAMINA && stat != DEAD)
spark_system.start()
. = TRUE
return ..() || .
/mob/living/carbon/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
var/obj/item/bodypart/hit_bodypart = get_bodypart(hit_zone) || bodyparts[1]
if(!hit_bodypart.can_bleed())
return FALSE
return ..()
/mob/living/carbon/human/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
. = ..()
switch(hit_zone)
if(BODY_ZONE_HEAD)
if(.)
if(wear_mask)
wear_mask.add_mob_blood(src)
update_worn_mask()
if(head)
head.add_mob_blood(src)
update_worn_head()
if(glasses && prob(33))
glasses.add_mob_blood(src)
update_worn_glasses()
if(!attacking_item.get_sharpness() && armor_block < 50)
if(prob(damage_done))
adjustOrganLoss(ORGAN_SLOT_BRAIN, 20)
if(stat == CONSCIOUS)
visible_message(
span_danger("[src] is knocked senseless!"),
span_userdanger("You're knocked senseless!"),
)
set_confusion_if_lower(20 SECONDS)
adjust_eye_blur(20 SECONDS)
if(prob(10))
gain_trauma(/datum/brain_trauma/mild/concussion)
else
adjustOrganLoss(ORGAN_SLOT_BRAIN, damage_done * 0.2)
// rev deconversion through blunt trauma.
// this can be signalized to the rev datum
if(mind && stat == CONSCIOUS && src != attacker && prob(damage_done + ((100 - health) * 0.5)))
var/datum/antagonist/rev/rev = mind.has_antag_datum(/datum/antagonist/rev)
rev?.remove_revolutionary(attacker)
if(BODY_ZONE_CHEST)
if(.)
if(wear_suit)
wear_suit.add_mob_blood(src)
update_worn_oversuit()
if(w_uniform)
w_uniform.add_mob_blood(src)
update_worn_undersuit()
if(stat == CONSCIOUS && !attacking_item.get_sharpness() && armor_block < 50)
if(prob(damage_done))
visible_message(
span_danger("[src] is knocked down!"),
span_userdanger("You're knocked down!"),
)
apply_effect(6 SECONDS, EFFECT_KNOCKDOWN, armor_block)
// Triggers force say events
if(damage_done > 10 || (damage_done >= 5 && prob(33)))
force_say()
/**
* Last proc in the [/obj/item/proc/melee_attack_chain].
@@ -335,7 +457,7 @@
else
return clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area, obj/item/bodypart/hit_bodypart)
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area, def_zone)
if(!I.force && !length(I.attack_verb_simple) && !length(I.attack_verb_continuous))
return
var/message_verb_continuous = length(I.attack_verb_continuous) ? "[pick(I.attack_verb_continuous)]" : "attacks"
+3
View File
@@ -169,6 +169,9 @@
/mob/living/proc/resolve_right_click_attack(atom/target, list/modifiers)
return target.attack_animal_secondary(src, modifiers)
/**
* Called when a simple animal is unarmed attacking / clicking on this atom.
*/
/atom/proc/attack_animal(mob/user, list/modifiers)
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_ANIMAL, user)
+1 -1
View File
@@ -213,7 +213,7 @@
var/mob/living/living_target = target
if(ishuman(living_target))
var/mob/living/carbon/human/human_target = living_target
if(human_target.check_shields(source, 0, "the [source.name]", attack_type = LEAP_ATTACK) && living_source)
if(human_target.check_block(source, 0, "the [source.name]", attack_type = LEAP_ATTACK) && living_source)
living_source.Stun(recoil_duration, ignore_canstun = TRUE)
return
+51
View File
@@ -0,0 +1,51 @@
/// Applied to living mobs.
/// Adds a force threshold for which attacks will be blocked entirely.
/// IE, if they are hit with an attack that deals less than X damage, the attack does nothing.
/datum/element/damage_threshold
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// Incoming attacks beneath this threshold, inclusive, will be blocked entirely
var/force_threshold = -1
/datum/element/damage_threshold/Attach(datum/target, threshold)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
if(!isnum(threshold) || threshold <= 0)
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(check_block))
force_threshold = threshold
/datum/element/damage_threshold/Detach(datum/source, ...)
. = ..()
UnregisterSignal(source, COMSIG_LIVING_CHECK_BLOCK)
/datum/element/damage_threshold/proc/check_block(
mob/living/source,
atom/hitby,
damage,
attack_text,
attack_type,
armour_penetration,
damage_type,
attack_flag,
)
SIGNAL_HANDLER
if(damage <= 0) // Already handled
return NONE
if(damage <= force_threshold)
var/obj/item/item_hitting = hitby
var/tap_vol = istype(item_hitting) ? item_hitting.get_clamped_volume() : 50
source.visible_message(
span_warning("[src] looks unharmed!"),
span_warning("[attack_text] deals no damage to you!"),
span_hear("You hear a thud."),
COMBAT_MESSAGE_RANGE,
)
playsound(source, 'sound/weapons/tap.ogg', tap_vol, TRUE, -1)
return SUCCESSFUL_BLOCK
return NONE
-1
View File
@@ -5,7 +5,6 @@
var/max_streak_length = 6
var/current_target
var/datum/martial_art/base // The permanent style. This will be null unless the martial art is temporary
var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
var/help_verb
var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts
var/smashes_tables = FALSE //If the martial art smashes tables when performing table slams and head smashes
+29 -2
View File
@@ -8,18 +8,20 @@
name = "CQC"
id = MARTIALART_CQC
help_verb = /mob/living/proc/CQC_help
block_chance = 75
smashes_tables = TRUE
display_combos = TRUE
var/old_grab_state = null
var/mob/restraining_mob
/// Probability of successfully blocking attacks while on throw mode
var/block_chance = 75
/datum/martial_art/cqc/teach(mob/living/cqc_user, make_temporary)
. = ..()
RegisterSignal(cqc_user, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
RegisterSignal(cqc_user, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(check_block))
/datum/martial_art/cqc/on_remove(mob/living/cqc_user)
UnregisterSignal(cqc_user, COMSIG_ATOM_ATTACKBY)
UnregisterSignal(cqc_user, list(COMSIG_ATOM_ATTACKBY, COMSIG_LIVING_CHECK_BLOCK))
. = ..()
///Signal from getting attacked with an item, for a special interaction with touch spells
@@ -41,6 +43,31 @@
INVOKE_ASYNC(touch_spell, TYPE_PROC_REF(/datum/action/cooldown/spell/touch, do_hand_hit), touch_weapon, attacker, attacker)
return COMPONENT_NO_AFTERATTACK
/datum/martial_art/cqc/proc/check_block(mob/living/cqc_user, atom/movable/hitby, damage, attack_text, attack_type, ...)
SIGNAL_HANDLER
if(!can_use(cqc_user) || !cqc_user.throw_mode || cqc_user.incapacitated(IGNORE_GRAB))
return NONE
if(attack_type == PROJECTILE_ATTACK)
return NONE
if(!prob(block_chance))
return NONE
var/mob/living/attacker = GET_ASSAILANT(hitby)
if(istype(attacker) && cqc_user.Adjacent(attacker))
cqc_user.visible_message(
span_danger("[cqc_user] blocks [attack_text] and twists [attacker]'s arm behind [attacker.p_their()] back!"),
span_userdanger("You block [attack_text]!"),
)
attacker.Stun(4 SECONDS)
else
cqc_user.visible_message(
span_danger("[cqc_user] blocks [attack_text]!"),
span_userdanger("You block [attack_text]!"),
)
return SUCCESSFUL_BLOCK
/datum/martial_art/cqc/reset_streak(mob/living/new_target)
if(new_target && new_target != restraining_mob)
restraining_mob = null
+3 -6
View File
@@ -175,13 +175,10 @@
target.visible_message(desc["visible"], desc["local"])
/obj/item/melee/baton/proc/check_parried(mob/living/carbon/human/human_target, mob/living/user)
if(!ishuman(human_target))
return
if (human_target.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
if (human_target.check_block(src, 0, "[user]'s [name]", MELEE_ATTACK))
playsound(human_target, 'sound/weapons/genhit.ogg', 50, TRUE)
return TRUE
if(check_martial_counter(human_target, user))
return TRUE
return FALSE
/obj/item/melee/baton/proc/finalize_baton_attack(mob/living/target, mob/living/user, modifiers, in_attack_chain = TRUE)
if(!in_attack_chain && HAS_TRAIT_FROM(target, TRAIT_IWASBATONED, REF(user)))
@@ -632,7 +629,7 @@
/obj/item/melee/baton/security/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(active && prob(throw_stun_chance) && isliving(hit_atom))
if(!. && active && prob(throw_stun_chance) && isliving(hit_atom))
finalize_baton_attack(hit_atom, thrownby?.resolve(), in_attack_chain = FALSE)
/obj/item/melee/baton/security/emp_act(severity)
+1 -8
View File
@@ -1,14 +1,7 @@
// Deprecated, you do not need to use this type for melee weapons.
/obj/item/melee
item_flags = NEEDS_PERMIT
/obj/item/melee/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user)
if(target.check_block())
target.visible_message(span_danger("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!"),
span_userdanger("You block the attack!"))
user.Stun(40)
return TRUE
/obj/item/melee/chainofcommand
name = "chain of command"
desc = "A tool used by great men to placate the frothing masses."
@@ -28,7 +28,7 @@
return
if(ishuman(attacked_mob))
var/mob/living/carbon/human/human = attacked_mob
if(human.check_shields(src, 0, "[attacked_mob]'s [name]", MELEE_ATTACK))
if(human.check_block(src, 0, "[attacked_mob]'s [name]", MELEE_ATTACK))
playsound(attacked_mob, 'sound/weapons/genhit.ogg', 50, TRUE)
return FALSE
if(iscyborg(user))
@@ -117,10 +117,10 @@
var/riposte_ready = TRUE
/datum/heretic_knowledge/blade_dance/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
RegisterSignal(user, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(on_shield_reaction))
RegisterSignal(user, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(on_shield_reaction))
/datum/heretic_knowledge/blade_dance/on_lose(mob/user, datum/antagonist/heretic/our_heretic)
UnregisterSignal(user, COMSIG_HUMAN_CHECK_SHIELDS)
UnregisterSignal(user, COMSIG_LIVING_CHECK_BLOCK)
/datum/heretic_knowledge/blade_dance/proc/on_shield_reaction(
mob/living/carbon/human/source,
@@ -133,7 +133,7 @@
return ..()
/datum/status_effect/protective_blades/on_apply()
RegisterSignal(owner, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(on_shield_reaction))
RegisterSignal(owner, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(on_shield_reaction))
for(var/blade_num in 1 to max_num_blades)
var/time_until_created = (blade_num - 1) * time_between_initial_blades
if(time_until_created <= 0)
@@ -144,7 +144,7 @@
return TRUE
/datum/status_effect/protective_blades/on_remove()
UnregisterSignal(owner, COMSIG_HUMAN_CHECK_SHIELDS)
UnregisterSignal(owner, COMSIG_LIVING_CHECK_BLOCK)
QDEL_LIST(blades)
return ..()
@@ -160,7 +160,7 @@
RegisterSignal(blade, COMSIG_QDELETING, PROC_REF(remove_blade))
playsound(get_turf(owner), 'sound/items/unsheath.ogg', 33, TRUE)
/// Signal proc for [COMSIG_HUMAN_CHECK_SHIELDS].
/// Signal proc for [COMSIG_LIVING_CHECK_BLOCK].
/// If we have a blade in our list, consume it and block the incoming attack (shield it)
/datum/status_effect/protective_blades/proc/on_shield_reaction(
mob/living/carbon/human/source,
@@ -194,7 +194,7 @@
addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_BEING_BLADE_SHIELDED, type), 1)
return SHIELD_BLOCK
return SUCCESSFUL_BLOCK
/// Remove deleted blades from our blades list properly.
/datum/status_effect/protective_blades/proc/remove_blade(obj/effect/floating_blade/to_remove)
-2
View File
@@ -43,8 +43,6 @@
/// 1 for full damage, 0 for none, -1 for 1:1 heal from that source.
var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
///Minimum force required to deal any damage.
var/force_threshold = 0
///Verbs used for speaking e.g. "Says" or "Chitters". This can be elementized
var/list/speak_emote = list()
+50 -54
View File
@@ -11,40 +11,58 @@
var/shove_dir = get_dir(user, src)
if(!Move(get_step(src, shove_dir), shove_dir))
log_combat(user, src, "shoved", "failing to move it")
user.visible_message(span_danger("[user.name] [response_disarm_continuous] [src]!"),
span_danger("You [response_disarm_simple] [src]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, list(src))
user.visible_message(
span_danger("[user.name] [response_disarm_continuous] [src]!"),
span_danger("You [response_disarm_simple] [src]!"),
span_hear("You hear aggressive shuffling!"),
COMBAT_MESSAGE_RANGE,
list(src),
)
to_chat(src, span_userdanger("You're shoved by [user.name]!"))
return TRUE
log_combat(user, src, "shoved", "pushing it")
user.visible_message(span_danger("[user.name] [response_disarm_continuous] [src], pushing [p_them()]!"),
span_danger("You [response_disarm_simple] [src], pushing [p_them()]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, list(src))
user.visible_message(
span_danger("[user.name] [response_disarm_continuous] [src], pushing [p_them()]!"),
span_danger("You [response_disarm_simple] [src], pushing [p_them()]!"),
span_hear("You hear aggressive shuffling!"),
COMBAT_MESSAGE_RANGE,
list(src),
)
to_chat(src, span_userdanger("You're pushed by [user.name]!"))
return TRUE
if(!user.combat_mode)
if (stat == DEAD)
return
visible_message(span_notice("[user] [response_help_continuous] [src]."), \
span_notice("[user] [response_help_continuous] you."), null, null, user)
to_chat(user, span_notice("You [response_help_simple] [src]."))
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
else
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to hurt [src]!"))
return
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
visible_message(span_danger("[user] [response_harm_continuous] [src]!"),\
span_userdanger("[user] [response_harm_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You [response_harm_simple] [src]!"))
playsound(loc, attacked_sound, 25, TRUE, -1)
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
var/damage = (basic_mob_flags & IMMUNE_TO_FISTS) ? 0 : rand(active_arm.unarmed_damage_low, active_arm.unarmed_damage_high)
attack_threshold_check(damage)
log_combat(user, src, "attacked")
updatehealth()
if (stat != DEAD)
visible_message(
span_notice("[user] [response_help_continuous] [src]."),
span_notice("[user] [response_help_continuous] you."),
ignored_mobs = user,
)
to_chat(user, span_notice("You [response_help_simple] [src]."))
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
return TRUE
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to hurt [src]!"))
return TRUE
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
var/damage = (basic_mob_flags & IMMUNE_TO_FISTS) ? 0 : rand(active_arm.unarmed_damage_low, active_arm.unarmed_damage_high)
if(check_block(user, damage, "[user]'s punch", UNARMED_ATTACK, 0, BRUTE))
return
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
visible_message(
span_danger("[user] [response_harm_continuous] [src]!"),
span_userdanger("[user] [response_harm_continuous] you!"),
vision_distance = COMBAT_MESSAGE_RANGE,
ignored_mobs = user,
)
to_chat(user, span_danger("You [response_harm_simple] [src]!"))
playsound(loc, attacked_sound, 25, TRUE, -1)
apply_damage(damage)
log_combat(user, src, "attacked")
updatehealth()
return TRUE
/mob/living/basic/attack_hulk(mob/living/carbon/human/user)
. = ..()
if(!.)
@@ -58,9 +76,8 @@
/mob/living/basic/attack_paw(mob/living/carbon/human/user, list/modifiers)
if(..()) //successful monkey bite.
if(stat != DEAD)
var/damage = rand(1, 3)
attack_threshold_check(damage)
return 1
return apply_damage(rand(1, 3))
if (!user.combat_mode)
if (health > 0)
visible_message(span_notice("[user.name] [response_help_continuous] [src]."), \
@@ -85,29 +102,22 @@
span_userdanger("You're slashed at by [user]!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You slash at [src]!"))
playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
attack_threshold_check(damage)
apply_damage(damage)
log_combat(user, src, "attacked")
/mob/living/basic/attack_larva(mob/living/carbon/alien/larva/attacking_larva, list/modifiers)
. = ..()
if(. && stat != DEAD) //successful larva bite
var/damage = rand(attacking_larva.melee_damage_lower, attacking_larva.melee_damage_upper)
. = attack_threshold_check(damage)
if(.)
attacking_larva.amount_grown = min(attacking_larva.amount_grown + damage, attacking_larva.max_grown)
/mob/living/basic/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(.)
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
return attack_threshold_check(damage, user.melee_damage_type)
var/damage_done = apply_damage(rand(attacking_larva.melee_damage_lower, attacking_larva.melee_damage_upper), BRUTE)
if(damage_done > 0)
attacking_larva.amount_grown = min(attacking_larva.amount_grown + damage_done, attacking_larva.max_grown)
/mob/living/basic/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
var/damage = rand(15, 25)
if(M.is_adult)
damage = rand(20, 35)
return attack_threshold_check(damage)
return apply_damage(damage, M.melee_damage_type)
/mob/living/basic/attack_drone(mob/living/basic/drone/attacking_drone)
if(attacking_drone.combat_mode) //No kicking dogs even as a rogue drone. Use a weapon.
@@ -119,20 +129,6 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return ..()
/mob/living/basic/proc/attack_threshold_check(damage, damagetype = BRUTE, armorcheck = MELEE, actuallydamage = TRUE)
var/temp_damage = damage
if(!damage_coeff[damagetype])
temp_damage = 0
else
temp_damage *= damage_coeff[damagetype]
if(temp_damage >= 0 && temp_damage <= force_threshold)
visible_message(span_warning("[src] looks unharmed!"))
return FALSE
else
if(actuallydamage)
apply_damage(damage, damagetype, blocked = getarmor(null, armorcheck))
return TRUE
/mob/living/basic/check_projectile_armor(def_zone, obj/projectile/impacting_projectile, is_silent)
return 0
@@ -22,7 +22,6 @@
verb_ask = "demands"
verb_exclaim = "roars"
verb_yell = "bellows"
force_threshold = 10
pressure_resistance = 50
mob_size = MOB_SIZE_LARGE
hud_type = /datum/hud/living/blobbernaut
@@ -32,6 +31,7 @@
/mob/living/basic/blob_minion/blobbernaut/Initialize(mapload)
. = ..()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBBERNAUT, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
AddElement(/datum/element/damage_threshold, 10)
/mob/living/basic/blob_minion/blobbernaut/death(gibbed)
flick("blobbernaut_death", src)
+1 -2
View File
@@ -370,7 +370,6 @@
speed = 1
melee_damage_lower = 10
melee_damage_upper = 15
force_threshold = 10 //lots of fat to cushion blows.
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 2, STAMINA = 0, OXY = 1)
attack_verb_continuous = "slams"
attack_verb_simple = "slam"
@@ -395,7 +394,7 @@
AddElement(/datum/element/swabable, CELL_LINE_TABLE_GLUTTON, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheesiehonkers, /obj/item/food/cornchips), tame_chance = 30, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed)))
AddElement(/datum/element/damage_threshold, 10) //lots of fat to cushion blows.
/mob/living/basic/clown/mutant/glutton/attacked_by(obj/item/item, mob/living/user)
if(!check_edible(item))
@@ -17,7 +17,6 @@
attack_sound = 'sound/weapons/punch3.ogg'
status_flags = NONE
mob_size = MOB_SIZE_LARGE
force_threshold = 10
construct_spells = list(
/datum/action/cooldown/spell/basic_projectile/juggernaut,
/datum/action/cooldown/spell/forcewall/cult,
@@ -27,6 +26,10 @@
smashes_walls = TRUE
/mob/living/basic/construct/juggernaut/Initialize(mapload)
. = ..()
AddElement(/datum/element/damage_threshold, 10)
/// Hostile NPC version. Pretty dumb, just attacks whoever is near.
/mob/living/basic/construct/juggernaut/hostile
ai_controller = /datum/ai_controller/basic_controller/juggernaut
@@ -62,7 +62,7 @@
/datum/action/cooldown/mob_cooldown/charge/basic_charge/guardian/hit_target(atom/movable/source, mob/living/target, damage_dealt)
if(ishuman(target))
var/mob/living/carbon/human/hit_human = target
if(hit_human.check_shields(src, charge_damage, name, attack_type = LEAP_ATTACK))
if(hit_human.check_block(src, charge_damage, name, attack_type = LEAP_ATTACK))
return
. = ..()
var/mob/living/hit_mob = target
@@ -86,7 +86,7 @@
maxHealth = 150
health = 150
speed = 2
force_threshold = 10 //trying to simulate actually having armor
damage_coeff = list(BRUTE = 0.5, BURN = 0.5, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) //trying to simulate actually having armor
obj_damage = 50
melee_damage_lower = 25
melee_damage_upper = 30
@@ -75,10 +75,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
real_name = name
/mob/living/carbon/alien/adult/proc/grab(mob/living/carbon/human/target)
if(target.check_block())
target.visible_message(span_warning("[target] blocks [src]'s grab!"), \
span_userdanger("You block [src]'s grab!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, src)
to_chat(src, span_warning("Your grab at [target] was blocked!"))
if(target.check_block(src, 0, "[target]'s grab"))
return FALSE
target.grabbedby(src)
return TRUE
@@ -74,7 +74,7 @@
var/blocked = FALSE
if(ishuman(hit_atom))
var/mob/living/carbon/human/H = hit_atom
if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
if(H.check_block(src, 0, "the [name]", attack_type = LEAP_ATTACK))
blocked = TRUE
if(!blocked)
L.visible_message(span_danger("[src] pounces on [L]!"), span_userdanger("[src] pounces on you!"))
@@ -67,25 +67,6 @@ In all, this is a lot like the monkey code. /N
var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(user.zone_selected))
apply_damage(rand(1, 3), BRUTE, affecting)
/mob/living/carbon/alien/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(.)
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
switch(user.melee_damage_type)
if(BRUTE)
adjustBruteLoss(damage)
if(BURN)
adjustFireLoss(damage)
if(TOX)
adjustToxLoss(damage)
if(OXY)
adjustOxyLoss(damage)
if(CLONE)
adjustCloneLoss(damage)
if(STAMINA)
adjustStaminaLoss(damage)
/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(..()) //successful slime attack
var/damage = rand(5, 35)
@@ -90,46 +90,13 @@
return TRUE
return ..()
/mob/living/carbon/attacked_by(obj/item/I, mob/living/user)
var/obj/item/bodypart/affecting
if(user == src)
affecting = get_bodypart(check_zone(user.zone_selected)) //we're self-mutilating! yay!
else
var/zone_hit_chance = 80
if(body_position == LYING_DOWN) // half as likely to hit a different zone if they're on the ground
zone_hit_chance += 10
affecting = get_bodypart(get_random_valid_zone(user.zone_selected, zone_hit_chance))
if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest)
affecting = bodyparts[1]
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
send_item_attack_message(I, user, affecting.plaintext_zone, affecting)
if(I.force)
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.get_sharpness(), attack_direction = attack_direction, attacking_item = I)
if(I.damtype == BRUTE && affecting.can_bleed())
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
add_splatter_floor(location)
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
if(affecting.body_zone == BODY_ZONE_HEAD)
if(wear_mask)
wear_mask.add_mob_blood(src)
update_worn_mask()
if(wear_neck)
wear_neck.add_mob_blood(src)
update_worn_neck()
if(head)
head.add_mob_blood(src)
update_worn_head()
return TRUE //successful attack
/mob/living/carbon/send_item_attack_message(obj/item/I, mob/living/user, hit_area, obj/item/bodypart/hit_bodypart)
/mob/living/carbon/send_item_attack_message(obj/item/I, mob/living/user, hit_area, def_zone)
if(!I.force && !length(I.attack_verb_simple) && !length(I.attack_verb_continuous))
return
var/obj/item/bodypart/hit_bodypart = get_bodypart(def_zone)
if(isnull(hit_bodypart)) // ??
return ..()
var/message_verb_continuous = length(I.attack_verb_continuous) ? "[pick(I.attack_verb_continuous)]" : "attacks"
var/message_verb_simple = length(I.attack_verb_simple) ? "[pick(I.attack_verb_simple)]" : "attack"
@@ -257,7 +224,18 @@
updatehealth()
return 1
/mob/living/carbon/proc/dismembering_strike(mob/living/attacker, dam_zone)
/**
* Really weird proc that attempts to dismebmer the passed zone if it is at max damage
* Unless the attacker is an NPC, in which case it disregards the zone and picks a random one
*
* Cannot dismember heads
*
* Returns a falsy value (null) on success, and a truthy value (the hit zone) on failure
*/
/mob/living/proc/dismembering_strike(mob/living/attacker, dam_zone)
return dam_zone
/mob/living/carbon/dismembering_strike(mob/living/attacker, dam_zone)
if(!attacker.limb_destroyer)
return dam_zone
var/obj/item/bodypart/affecting
@@ -265,18 +243,17 @@
affecting = get_bodypart(get_random_valid_zone(dam_zone))
else
var/list/things_to_ruin = shuffle(bodyparts.Copy())
for(var/B in things_to_ruin)
var/obj/item/bodypart/bodypart = B
for(var/obj/item/bodypart/bodypart as anything in things_to_ruin)
if(bodypart.body_zone == BODY_ZONE_HEAD || bodypart.body_zone == BODY_ZONE_CHEST)
continue
if(!affecting || ((affecting.get_damage() / affecting.max_damage) < (bodypart.get_damage() / bodypart.max_damage)))
affecting = bodypart
if(affecting)
dam_zone = affecting.body_zone
if(affecting.get_damage() >= affecting.max_damage)
affecting.dismember()
if(affecting.get_damage() >= affecting.max_damage && affecting.dismember())
return null
return affecting.body_zone
return dam_zone
/**
@@ -1121,11 +1121,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
user.do_cpr(target)
/datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
if(target.check_block())
target.visible_message(span_warning("[target] blocks [user]'s grab!"), \
span_userdanger("You block [user]'s grab!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_warning("Your grab at [target] was blocked!"))
return FALSE
if(attacker_style?.grab_act(user, target) == MARTIAL_ATTACK_SUCCESS)
return TRUE
target.grabbedby(user)
@@ -1136,11 +1131,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(HAS_TRAIT(user, TRAIT_PACIFISM) && !attacker_style?.pacifist_style)
to_chat(user, span_warning("You don't want to harm [target]!"))
return FALSE
if(target.check_block())
target.visible_message(span_warning("[target] blocks [user]'s attack!"), \
span_userdanger("You block [user]'s attack!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_warning("Your attack at [target] was blocked!"))
return FALSE
if(attacker_style?.harm_act(user,target) == MARTIAL_ATTACK_SUCCESS)
return TRUE
else
@@ -1231,11 +1221,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
log_combat(user, target, "got a stun punch with their previous punch")
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
if(target.check_block())
target.visible_message(span_warning("[user]'s shove is blocked by [target]!"), \
span_danger("You block [user]'s shove!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_warning("Your shove at [target] was blocked!"))
return FALSE
if(attacker_style?.disarm_act(user,target) == MARTIAL_ATTACK_SUCCESS)
return TRUE
if(user.body_position != STANDING_UP)
@@ -1256,7 +1241,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return
if(owner.mind)
attacker_style = owner.mind.martial_art
if((owner != target) && target.check_shields(owner, 0, owner.name, attack_type = UNARMED_ATTACK))
if((owner != target) && target.check_block(owner, 0, owner.name, attack_type = UNARMED_ATTACK))
log_combat(owner, target, "attempted to touch")
target.visible_message(span_warning("[owner] attempts to touch [target]!"), \
span_danger("[owner] attempts to touch you!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, owner)
@@ -1273,115 +1258,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
else
help(owner, target, attacker_style)
/datum/species/proc/spec_attacked_by(obj/item/weapon, mob/living/user, obj/item/bodypart/affecting, mob/living/carbon/human/human)
// Allows you to put in item-specific reactions based on species
if(user != human)
if(human.check_shields(weapon, weapon.force, "the [weapon.name]", MELEE_ATTACK, weapon.armour_penetration, weapon.damtype))
return FALSE
if(human.check_block())
human.visible_message(span_warning("[human] blocks [weapon]!"), \
span_userdanger("You block [weapon]!"))
return FALSE
affecting ||= human.bodyparts[1] //Something went wrong. Maybe the limb is missing?
var/hit_area = affecting.plaintext_zone
var/armor_block = min(human.run_armor_check(
def_zone = affecting,
attack_flag = MELEE,
absorb_text = span_notice("Your armor has protected your [hit_area]!"),
soften_text = span_warning("Your armor has softened a hit to your [hit_area]!"),
armour_penetration = weapon.armour_penetration,
weak_against_armour = weapon.weak_against_armour,
), ARMOR_MAX_BLOCK) //cap damage reduction at 90%
var/modified_wound_bonus = weapon.wound_bonus
// this way, you can't wound with a surgical tool on help intent if they have a surgery active and are lying down, so a misclick with a circular saw on the wrong limb doesn't bleed them dry (they still get hit tho)
if((weapon.item_flags & SURGICAL_TOOL) && !user.combat_mode && human.body_position == LYING_DOWN && (LAZYLEN(human.surgeries) > 0))
modified_wound_bonus = CANT_WOUND
human.send_item_attack_message(weapon, user, hit_area, affecting)
var/damage_dealt = human.apply_damage(
damage = weapon.force,
damagetype = weapon.damtype,
def_zone = affecting,
blocked = armor_block,
wound_bonus = modified_wound_bonus,
bare_wound_bonus = weapon.bare_wound_bonus,
sharpness = weapon.get_sharpness(),
attack_direction = get_dir(user, human),
attacking_item = weapon,
)
if(damage_dealt <= 0)
return FALSE //item force is zero
var/bloody = FALSE
if(weapon.damtype != BRUTE)
return TRUE
if(!(prob(25 + (weapon.force * 2))))
return TRUE
if(affecting.can_bleed())
weapon.add_mob_blood(human) //Make the weapon bloody, not the person.
if(prob(weapon.force * 2)) //blood spatter!
bloody = TRUE
var/turf/location = human.loc
if(istype(location))
human.add_splatter_floor(location)
if(get_dist(user, human) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(human)
switch(hit_area)
if(BODY_ZONE_HEAD)
if(!weapon.get_sharpness() && armor_block < 50)
if(prob(weapon.force))
human.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20)
if(human.stat == CONSCIOUS)
human.visible_message(span_danger("[human] is knocked senseless!"), \
span_userdanger("You're knocked senseless!"))
human.set_confusion_if_lower(20 SECONDS)
human.adjust_eye_blur(20 SECONDS)
if(prob(10))
human.gain_trauma(/datum/brain_trauma/mild/concussion)
else
human.adjustOrganLoss(ORGAN_SLOT_BRAIN, weapon.force * 0.2)
if(human.mind && human.stat == CONSCIOUS && human != user && prob(weapon.force + ((100 - human.health) * 0.5))) // rev deconversion through blunt trauma.
var/datum/antagonist/rev/rev = human.mind.has_antag_datum(/datum/antagonist/rev)
if(rev)
rev.remove_revolutionary(user)
if(bloody) //Apply blood
if(human.wear_mask)
human.wear_mask.add_mob_blood(human)
human.update_worn_mask()
if(human.head)
human.head.add_mob_blood(human)
human.update_worn_head()
if(human.glasses && prob(33))
human.glasses.add_mob_blood(human)
human.update_worn_glasses()
if(BODY_ZONE_CHEST)
if(human.stat == CONSCIOUS && !weapon.get_sharpness() && armor_block < 50)
if(prob(weapon.force))
human.visible_message(span_danger("[human] is knocked down!"), \
span_userdanger("You're knocked down!"))
human.apply_effect(60, EFFECT_KNOCKDOWN, armor_block)
if(bloody)
if(human.wear_suit)
human.wear_suit.add_mob_blood(human)
human.update_worn_oversuit()
if(human.w_uniform)
human.w_uniform.add_mob_blood(human)
human.update_worn_undersuit()
/// Triggers force say events
if(weapon.force > 10 || (weapon.force >= 5 && prob(33)))
human.force_say(user)
return TRUE
//////////////////////////
// ENVIRONMENT HANDLERS //
//////////////////////////
@@ -68,7 +68,7 @@
return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
if(check_shields(bullet, bullet.damage, "the [bullet.name]", PROJECTILE_ATTACK, bullet.armour_penetration, bullet.damage_type))
if(check_block(bullet, bullet.damage, "the [bullet.name]", PROJECTILE_ATTACK, bullet.armour_penetration, bullet.damage_type))
bullet.on_hit(src, 100, def_zone, piercing_hit)
return BULLET_ACT_HIT
@@ -87,94 +87,39 @@
return TRUE
return FALSE
/mob/living/carbon/human/proc/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE)
var/block_chance_modifier = round(damage / -3)
for(var/obj/item/I in held_items)
if(!isclothing(I))
var/final_block_chance = I.block_chance - (clamp((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type, damage_type))
return TRUE
if(wear_suit)
var/final_block_chance = wear_suit.block_chance - (clamp((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier
if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type, damage_type))
return TRUE
if(w_uniform)
var/final_block_chance = w_uniform.block_chance - (clamp((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier
if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type, damage_type))
return TRUE
if(wear_neck)
var/final_block_chance = wear_neck.block_chance - (clamp((armour_penetration-wear_neck.armour_penetration)/2,0,100)) + block_chance_modifier
if(wear_neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type, damage_type))
return TRUE
if(head)
var/final_block_chance = head.block_chance - (clamp((armour_penetration-head.armour_penetration)/2,0,100)) + block_chance_modifier
if(head.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type, damage_type))
return TRUE
if(SEND_SIGNAL(src, COMSIG_HUMAN_CHECK_SHIELDS, AM, damage, attack_text, attack_type, armour_penetration, damage_type) & SHIELD_BLOCK)
/mob/living/carbon/human/check_block(atom/hit_by, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE)
. = ..()
if(.)
return TRUE
return FALSE
/mob/living/carbon/human/proc/check_block()
if(mind)
if(mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && throw_mode && !incapacitated(IGNORE_GRAB))
var/block_chance_modifier = round(damage / -3)
for(var/obj/item/worn_thing in get_equipped_items(include_pockets = FALSE) + held_items)
// Things that are supposed to be worn, being held = cannot block
if(isclothing(worn_thing))
if(worn_thing in held_items)
continue
// Things that are supposed to be held, being worn = cannot block
else
if(!(worn_thing in held_items))
continue
var/final_block_chance = worn_thing.block_chance - (clamp((armour_penetration - worn_thing.armour_penetration) / 2, 0, 100)) + block_chance_modifier
if(worn_thing.hit_reaction(src, hit_by, attack_text, final_block_chance, damage, attack_type, damage_type))
return TRUE
return FALSE
/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
var/obj/item/I
var/damage_type = BRUTE
var/throwpower = 30
if(isitem(AM))
I = AM
if(I.thrownby == WEAKREF(src)) //No throwing stuff at yourself to trigger hit reactions
return ..()
throwpower = I.throwforce
damage_type = I.damtype
if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK, 0, damage_type))
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
return ..()
/mob/living/carbon/human/grippedby(mob/living/user, instant = FALSE)
if(w_uniform)
w_uniform.add_fingerprint(user)
..()
/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user)
if(!I || !user)
return FALSE
var/obj/item/bodypart/affecting
if(user == src)
affecting = get_bodypart(check_zone(user.zone_selected)) //stabbing yourself always hits the right target
else
var/zone_hit_chance = 80
if(body_position == LYING_DOWN) // half as likely to hit a different zone if they're on the ground
zone_hit_chance += 10
affecting = get_bodypart(get_random_valid_zone(user.zone_selected, zone_hit_chance))
var/target_area = parse_zone(check_zone(user.zone_selected)) //our intended target
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[I.force]", "[I.type]"))
SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area)
// the attacked_by code varies among species
return dna.species.spec_attacked_by(I, user, affecting, src)
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user)
. = ..()
if(!.)
return
var/hulk_verb = pick("smash","pummel")
if(check_shields(user, 15, "the [hulk_verb]ing", attack_type = UNARMED_ATTACK))
return
if(check_block()) //everybody is kung fu fighting
if(check_block(user, 15, "the [hulk_verb]ing", attack_type = UNARMED_ATTACK))
return
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
playsound(loc, active_arm.unarmed_attack_sound, 25, TRUE, -1)
@@ -234,17 +179,12 @@
var/damage = HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) ? monkey_mouth.unarmed_damage_high : rand(monkey_mouth.unarmed_damage_low, monkey_mouth.unarmed_damage_high)
if(!damage)
return FALSE
if(check_shields(user, damage, "the [user.name]"))
if(check_block(user, damage, "the [user.name]"))
return FALSE
apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, MELEE))
return TRUE
/mob/living/carbon/human/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
if(check_shields(user, 0, "the [user.name]"))
visible_message(span_danger("[user] attempts to touch [src]!"), \
span_danger("[user] attempts to touch you!"), span_hear("You hear a swoosh!"), null, user)
to_chat(user, span_warning("You attempt to touch [src]!"))
return FALSE
. = ..()
if(!.)
return
@@ -306,7 +246,7 @@
var/damage = rand(L.melee_damage_lower, L.melee_damage_upper)
if(!damage)
return
if(check_shields(L, damage, "the [L.name]"))
if(check_block(L, damage, "the [L.name]"))
return FALSE
if(stat != DEAD)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
@@ -314,22 +254,6 @@
var/armor_block = run_armor_check(affecting, MELEE)
apply_damage(damage, BRUTE, affecting, armor_block)
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(!.)
return
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
if(check_shields(user, damage, "the [user.name]", MELEE_ATTACK, user.armour_penetration, user.melee_damage_type))
return FALSE
var/dam_zone = dismembering_strike(user, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
return TRUE
var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(dam_zone))
var/armor = run_armor_check(affecting, MELEE, armour_penetration = user.armour_penetration)
var/attack_direction = get_dir(user, src)
apply_damage(damage, user.melee_damage_type, affecting, armor, wound_bonus = user.wound_bonus, bare_wound_bonus = user.bare_wound_bonus, sharpness = user.sharpness, attack_direction = attack_direction)
/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
. = ..()
if(!.) // slime attack failed
@@ -342,7 +266,7 @@
damage += rand(5, 10)
wound_mod = -90 // 35^1.4=145, 145-90=55
if(check_shields(M, damage, "the [M.name]"))
if(check_block(M, damage, "the [M.name]"))
return FALSE
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
+119 -48
View File
@@ -159,35 +159,49 @@
SEND_SOUND(src, sound('sound/misc/ui_toggleoffcombat.ogg', volume = 25)) //Slightly modified version of the above
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(isitem(AM))
var/obj/item/thrown_item = AM
var/zone = get_random_valid_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
var/nosell_hit = SEND_SIGNAL(thrown_item, COMSIG_MOVABLE_IMPACT_ZONE, src, zone, blocked, throwingdatum) // TODO: find a better way to handle hitpush and skipcatch for humans
if(nosell_hit)
if(!isitem(AM))
// Filled with made up numbers for non-items.
if(check_block(AM, 30, "\the [AM.name]", THROWN_PROJECTILE_ATTACK, 0, BRUTE))
hitpush = FALSE
skipcatch = TRUE
hitpush = FALSE
if(blocked)
return TRUE
var/mob/thrown_by = thrown_item.thrownby?.resolve()
if(thrown_by)
log_combat(thrown_by, src, "threw and hit", thrown_item)
if(nosell_hit)
return ..()
visible_message(span_danger("[src] is hit by [thrown_item]!"), \
span_userdanger("You're hit by [thrown_item]!"))
if(!thrown_item.throwforce)
return
var/armor = run_armor_check(zone, MELEE, "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", thrown_item.armour_penetration, "", FALSE, thrown_item.weak_against_armour)
apply_damage(thrown_item.throwforce, thrown_item.damtype, zone, armor, sharpness = thrown_item.get_sharpness(), wound_bonus = (nosell_hit * CANT_WOUND))
if(QDELETED(src)) //Damage can delete the mob.
return
if(body_position == LYING_DOWN) // physics says it's significantly harder to push someone by constantly chucking random furniture at them if they are down on the floor.
hitpush = FALSE
blocked = TRUE
else
playsound(loc, 'sound/weapons/genhit.ogg', 50, TRUE, -1) //Item sounds are handled in the item itself
return ..()
playsound(loc, 'sound/weapons/genhit.ogg', 50, TRUE, -1) //Item sounds are handled in the item itself
var/obj/item/thrown_item = AM
if(thrown_item.thrownby == WEAKREF(src)) //No throwing stuff at yourself to trigger hit reactions
return ..()
if(check_block(AM, thrown_item.throwforce, "\the [thrown_item.name]", THROWN_PROJECTILE_ATTACK, 0, thrown_item.damtype))
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
var/zone = get_random_valid_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
var/nosell_hit = SEND_SIGNAL(thrown_item, COMSIG_MOVABLE_IMPACT_ZONE, src, zone, blocked, throwingdatum) // TODO: find a better way to handle hitpush and skipcatch for humans
if(nosell_hit)
skipcatch = TRUE
hitpush = FALSE
if(blocked)
return TRUE
var/mob/thrown_by = thrown_item.thrownby?.resolve()
if(thrown_by)
log_combat(thrown_by, src, "threw and hit", thrown_item)
if(nosell_hit)
return ..()
visible_message(span_danger("[src] is hit by [thrown_item]!"), \
span_userdanger("You're hit by [thrown_item]!"))
if(!thrown_item.throwforce)
return
var/armor = run_armor_check(zone, MELEE, "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", thrown_item.armour_penetration, "", FALSE, thrown_item.weak_against_armour)
apply_damage(thrown_item.throwforce, thrown_item.damtype, zone, armor, sharpness = thrown_item.get_sharpness(), wound_bonus = (nosell_hit * CANT_WOUND))
if(QDELETED(src)) //Damage can delete the mob.
return
if(body_position == LYING_DOWN) // physics says it's significantly harder to push someone by constantly chucking random furniture at them if they are down on the floor.
hitpush = FALSE
return ..()
/mob/living/fire_act()
@@ -272,10 +286,6 @@
/mob/living/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
if(!SSticker.HasRoundStarted())
to_chat(M, "You cannot attack people before the game has started.")
return
if(M.buckled)
if(M in buckled_mobs)
M.Feedstop()
@@ -285,6 +295,9 @@
to_chat(M, span_warning("You don't want to hurt anyone!"))
return FALSE
if(check_block(src, M.melee_damage_upper, "[M]'s glomp", MELEE_ATTACK, M.armour_penetration, M.melee_damage_type))
return FALSE
if (stat != DEAD)
log_combat(M, src, "attacked")
M.do_attack_animation(src)
@@ -293,30 +306,68 @@
to_chat(M, span_danger("You glomp [src]!"))
return TRUE
return FALSE
/mob/living/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
user.face_atom(src)
if(.)
return FALSE // looks wrong, but if the attack chain was cancelled we don't propogate it up to children calls. Yeah it's cringe.
if(user.melee_damage_upper == 0)
if(user != src)
visible_message(span_notice("\The [user] [user.friendly_verb_continuous] [src]!"), \
span_notice("\The [user] [user.friendly_verb_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
visible_message(
span_notice("[user] [user.friendly_verb_continuous] [src]!"),
span_notice("[user] [user.friendly_verb_continuous] you!"),
vision_distance = COMBAT_MESSAGE_RANGE,
ignored_mobs = user,
)
to_chat(user, span_notice("You [user.friendly_verb_simple] [src]!"))
return FALSE
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to hurt anyone!"))
return FALSE
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
if(check_block(user, damage, "[user]'s [user.attack_verb_simple]", MELEE_ATTACK/*or UNARMED_ATTACK?*/, user.armour_penetration, user.melee_damage_type))
return FALSE
if(user.attack_sound)
playsound(loc, user.attack_sound, 50, TRUE, TRUE)
playsound(src, user.attack_sound, 50, TRUE, TRUE)
user.do_attack_animation(src)
visible_message(span_danger("\The [user] [user.attack_verb_continuous] [src]!"), \
span_userdanger("\The [user] [user.attack_verb_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
visible_message(
span_danger("[user] [user.attack_verb_continuous] [src]!"),
span_userdanger("[user] [user.attack_verb_continuous] you!"),
null,
COMBAT_MESSAGE_RANGE,
user,
)
var/dam_zone = dismembering_strike(user, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
return FALSE
var/armor_block = run_armor_check(user.zone_selected, MELEE, armour_penetration = user.armour_penetration)
to_chat(user, span_danger("You [user.attack_verb_simple] [src]!"))
log_combat(user, src, "attacked")
return TRUE
var/damage_done = apply_damage(
damage = damage,
damagetype = user.melee_damage_type,
def_zone = user.zone_selected,
blocked = armor_block,
wound_bonus = user.wound_bonus,
bare_wound_bonus = user.bare_wound_bonus,
sharpness = user.sharpness,
attack_direction = get_dir(user, src),
)
return damage_done
/mob/living/attack_hand(mob/living/carbon/human/user, list/modifiers)
. = ..()
if(.)
return TRUE
for(var/datum/surgery/operations as anything in surgeries)
if(user.combat_mode)
@@ -330,6 +381,8 @@
if (martial_result != MARTIAL_ATTACK_INVALID)
return martial_result
return FALSE
/mob/living/attack_paw(mob/living/carbon/human/user, list/modifiers)
var/martial_result = user.apply_martial_art(src, modifiers)
if (martial_result != MARTIAL_ATTACK_INVALID)
@@ -350,6 +403,10 @@
if(user.is_muzzled() || user.is_mouth_covered(ITEM_SLOT_MASK))
to_chat(user, span_warning("You can't bite with your mouth covered!"))
return FALSE
if(check_block(user, 1, "[user]'s bite", UNARMED_ATTACK, 0, BRUTE))
return FALSE
user.do_attack_animation(src, ATTACK_EFFECT_BITE)
if (HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) || prob(75))
log_combat(user, src, "attacked")
@@ -369,7 +426,10 @@
if(L.combat_mode)
if(HAS_TRAIT(L, TRAIT_PACIFISM))
to_chat(L, span_warning("You don't want to hurt anyone!"))
return
return FALSE
if(check_block(L, 1, "[L]'s bite", UNARMED_ATTACK, 0, BRUTE))
return FALSE
L.do_attack_animation(src)
if(prob(90))
@@ -383,29 +443,34 @@
visible_message(span_danger("[L.name]'s bite misses [src]!"), \
span_danger("You avoid [L.name]'s bite!"), span_hear("You hear the sound of jaws snapping shut!"), COMBAT_MESSAGE_RANGE, L)
to_chat(L, span_warning("Your bite misses [src]!"))
else
visible_message(span_notice("[L.name] rubs its head against [src]."), \
span_notice("[L.name] rubs its head against you."), null, null, L)
to_chat(L, span_notice("You rub your head against [src]."))
return FALSE
return FALSE
visible_message(span_notice("[L.name] rubs its head against [src]."), \
span_notice("[L.name] rubs its head against you."), null, null, L)
to_chat(L, span_notice("You rub your head against [src]."))
return FALSE
/mob/living/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_ALIEN, user, modifiers)
if(LAZYACCESS(modifiers, RIGHT_CLICK))
if(check_block(user, 0, "[user]'s tackle", MELEE_ATTACK, 0, BRUTE))
return FALSE
user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
return TRUE
if(user.combat_mode)
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to hurt anyone!"))
return FALSE
if(check_block(user, user.melee_damage_upper, "[user]'s slash", MELEE_ATTACK, 0, BRUTE))
return FALSE
user.do_attack_animation(src)
return TRUE
else
visible_message(span_notice("[user] caresses [src] with its scythe-like arm."), \
span_notice("[user] caresses you with its scythe-like arm."), null, null, user)
to_chat(user, span_notice("You caress [src] with your scythe-like arm."))
return FALSE
visible_message(span_notice("[user] caresses [src] with its scythe-like arm."), \
span_notice("[user] caresses you with its scythe-like arm."), null, null, user)
to_chat(user, span_notice("You caress [src] with your scythe-like arm."))
return FALSE
/mob/living/attack_hulk(mob/living/carbon/human/user)
..()
@@ -567,3 +632,9 @@
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + GET_ANGLE_OF_INCIDENCE(face_angle, (ricocheting_projectile.Angle + 180)))
ricocheting_projectile.set_angle(new_angle_s)
return TRUE
/mob/living/proc/check_block(atom/hit_by, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE)
if(SEND_SIGNAL(src, COMSIG_LIVING_CHECK_BLOCK, hit_by, damage, attack_text, attack_type, armour_penetration, damage_type) & SUCCESSFUL_BLOCK)
return TRUE
return FALSE
@@ -7,15 +7,8 @@
return
MOD.install(laws, user) //Proc includes a success mesage so we don't need another one
return
if(W.force && W.damtype != STAMINA && stat != DEAD && !QDELETED(src)) //only sparks if real damage is dealt.
spark_system.start()
return ..()
/mob/living/silicon/ai/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
if(!SSticker.HasRoundStarted())
to_chat(user, "You cannot attack people before the game has started.")
return
..()
return ..()
/mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
return //immune to slimes
@@ -184,8 +184,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
modularInterface.inserted_disk = floppy
return
if(W.force && W.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt.
spark_system.start()
return ..()
/mob/living/silicon/robot/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
@@ -27,20 +27,17 @@
/mob/living/silicon/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(.)
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
if(prob(damage))
for(var/mob/living/buckled in buckled_mobs)
buckled.Paralyze(20)
unbuckle_mob(buckled)
buckled.visible_message(span_danger("[buckled] is knocked off of [src] by [user]!"), \
span_userdanger("You're knocked off of [src] by [user]!"), null, null, user)
to_chat(user, span_danger("You knock [buckled] off of [src]!"))
switch(user.melee_damage_type)
if(BRUTE)
adjustBruteLoss(damage)
if(BURN)
adjustFireLoss(damage)
var/damage_received = .
if(prob(damage_received))
for(var/mob/living/buckled in buckled_mobs)
buckled.Paralyze(2 SECONDS)
unbuckle_mob(buckled)
buckled.visible_message(
span_danger("[buckled] is knocked off of [src] by [user]!"),
span_userdanger("You're knocked off of [src] by [user]!"),
ignored_mobs = user,
)
to_chat(user, span_danger("You knock [buckled] off of [src]!"))
/mob/living/silicon/attack_paw(mob/living/user, list/modifiers)
return attack_hand(user, modifiers)
@@ -79,6 +76,15 @@
to_chat(user, span_notice("You pet [src]."))
user.add_mood_event("pet_borg", /datum/mood_event/pet_borg)
/mob/living/silicon/check_block(atom/hitby, damage, attack_text, attack_type, armour_penetration, damage_type, attack_flag)
. = ..()
if(.)
return TRUE
if(damage_type == BRUTE && attack_type == UNARMED_ATTACK && attack_flag == MELEE && damage <= 10)
playsound(src, 'sound/effects/bang.ogg', 10, TRUE)
visible_message(span_danger("[attack_text] doesn't leave a dent on [src]!"), vision_distance = COMBAT_MESSAGE_RANGE)
return TRUE
return FALSE
/mob/living/silicon/attack_drone(mob/living/basic/drone/user)
if(user.combat_mode)
@@ -32,12 +32,14 @@
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to hurt [src]!"))
return
if(check_block(user, harm_intent_damage, "[user]'s punch", UNARMED_ATTACK, 0, BRUTE))
return
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
visible_message(span_danger("[user] [response_harm_continuous] [src]!"),\
span_userdanger("[user] [response_harm_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You [response_harm_simple] [src]!"))
playsound(loc, attacked_sound, 25, TRUE, -1)
attack_threshold_check(harm_intent_damage)
apply_damage(harm_intent_damage)
log_combat(user, src, "attacked")
updatehealth()
return TRUE
@@ -55,9 +57,7 @@
/mob/living/simple_animal/attack_paw(mob/living/carbon/human/user, list/modifiers)
if(..()) //successful monkey bite.
if(stat != DEAD)
var/damage = rand(1, 3)
attack_threshold_check(damage)
return 1
return apply_damage(rand(1, 3))
if (!user.combat_mode)
if (health > 0)
visible_message(span_notice("[user.name] [response_help_continuous] [src]."), \
@@ -80,30 +80,23 @@
span_userdanger("You're slashed at by [user]!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You slash at [src]!"))
playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
attack_threshold_check(damage)
apply_damage(damage)
log_combat(user, src, "attacked")
return 1
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
. = ..()
if(. && stat != DEAD) //successful larva bite
var/damage = rand(5, 10)
. = attack_threshold_check(damage)
if(.)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
/mob/living/simple_animal/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(.)
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
return attack_threshold_check(damage, user.melee_damage_type)
var/damage_done = apply_damage(rand(L.melee_damage_lower, L.melee_damage_upper), BRUTE)
if(damage_done > 0)
L.amount_grown = min(L.amount_grown + damage_done, L.max_grown)
/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
if(..()) //successful slime attack
var/damage = rand(15, 25)
if(user.is_adult)
damage = rand(20, 35)
return attack_threshold_check(damage)
return apply_damage(damage, user.melee_damage_type)
/mob/living/simple_animal/attack_drone(mob/living/basic/drone/user)
if(user.combat_mode) //No kicking dogs even as a rogue drone. Use a weapon.
@@ -115,21 +108,6 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
return ..()
/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE, armorcheck = MELEE, actuallydamage = TRUE)
var/temp_damage = damage
if(!damage_coeff[damagetype])
temp_damage = 0
else
temp_damage *= damage_coeff[damagetype]
if(temp_damage >= 0 && temp_damage <= force_threshold)
visible_message(span_warning("[src] looks unharmed!"))
return FALSE
else
if(actuallydamage)
apply_damage(damage, damagetype, null, getarmor(null, armorcheck))
return TRUE
/mob/living/simple_animal/ex_act(severity, target, origin)
. = ..()
if(!. || QDELETED(src))
@@ -496,11 +496,11 @@
return
return ..()
/mob/living/simple_animal/bot/attacked_by(obj/item/I, mob/living/user)
. = ..()
if (!.)
return
do_sparks(5, TRUE, src)
/mob/living/simple_animal/bot/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
if(damage_done > 0 && attacking_item.damtype != STAMINA && stat != DEAD)
do_sparks(5, TRUE, src)
. = TRUE
return ..() || .
/mob/living/simple_animal/bot/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit = FALSE)
. = ..()
@@ -520,7 +520,7 @@
return ..()
/mob/living/simple_animal/bot/secbot/attack_alien(mob/living/carbon/alien/user, list/modifiers)
..()
. = ..()
if(!isalien(target))
target = user
mode = BOT_HUNT
@@ -326,20 +326,16 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
/mob/living/simple_animal/parrot/attack_paw(mob/living/carbon/human/user, list/modifiers)
return attack_hand(user, modifiers)
/mob/living/simple_animal/parrot/attack_alien(mob/living/carbon/alien/user, list/modifiers)
return attack_hand(user, modifiers)
//Simple animals
/mob/living/simple_animal/parrot/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..() //goodbye immortal parrots
if(client)
return
if(parrot_state == PARROT_PERCH)
parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched
if(user.melee_damage_upper > 0 && !stat)
if(. > 0 && stat == CONSCIOUS)
set_parrot_interest(user)
parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless
icon_state = icon_living
@@ -51,8 +51,6 @@
///Harm-intent verb in present simple tense.
var/response_harm_simple = "hit"
var/harm_intent_damage = 3
///Minimum force required to deal any damage.
var/force_threshold = 0
///Maximum amount of stamina damage the mob can be inflicted with total
var/max_staminaloss = 200
///How much stamina the mob recovers per second
@@ -342,7 +342,6 @@
if(.)
attacked += 10
/mob/living/simple_animal/slime/attack_paw(mob/living/carbon/human/user, list/modifiers)
if(..()) //successful monkey bite.
attacked += 10
+3 -3
View File
@@ -120,13 +120,13 @@
/obj/item/mod/module/energy_shield/on_suit_activation()
mod.AddComponent(/datum/component/shielded, max_charges = max_charges, recharge_start_delay = recharge_start_delay, charge_increment_delay = charge_increment_delay, \
charge_recovery = charge_recovery, lose_multiple_charges = lose_multiple_charges, recharge_path = recharge_path, starting_charges = charges, shield_icon_file = shield_icon_file, shield_icon = shield_icon)
RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(shield_reaction))
RegisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(shield_reaction))
/obj/item/mod/module/energy_shield/on_suit_deactivation(deleting = FALSE)
var/datum/component/shielded/shield = mod.GetComponent(/datum/component/shielded)
charges = shield.current_charges
qdel(shield)
UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS)
UnregisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK)
/obj/item/mod/module/energy_shield/proc/shield_reaction(mob/living/carbon/human/owner,
atom/movable/hitby,
@@ -140,7 +140,7 @@
if(SEND_SIGNAL(mod, COMSIG_ITEM_HIT_REACT, owner, hitby, attack_text, 0, damage, attack_type, damage_type) & COMPONENT_HIT_REACTION_BLOCK)
drain_power(use_power_cost)
return SHIELD_BLOCK
return SUCCESSFUL_BLOCK
return NONE
/obj/item/mod/module/energy_shield/wizard
+3 -3
View File
@@ -67,10 +67,10 @@
overlay_state_use = "module_pepper_used"
/obj/item/mod/module/pepper_shoulders/on_suit_activation()
RegisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS, PROC_REF(on_check_shields))
RegisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(on_check_block))
/obj/item/mod/module/pepper_shoulders/on_suit_deactivation(deleting = FALSE)
UnregisterSignal(mod.wearer, COMSIG_HUMAN_CHECK_SHIELDS)
UnregisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK)
/obj/item/mod/module/pepper_shoulders/on_use()
. = ..()
@@ -84,7 +84,7 @@
smoke.start(log = TRUE)
QDEL_NULL(capsaicin_holder) // Reagents have a ref to their holder which has a ref to them. No leaks please.
/obj/item/mod/module/pepper_shoulders/proc/on_check_shields()
/obj/item/mod/module/pepper_shoulders/proc/on_check_block()
SIGNAL_HANDLER
if(!COOLDOWN_FINISHED(src, cooldown_timer))
@@ -430,7 +430,7 @@
if(ishuman(target))
var/mob/living/carbon/human/human_target = target
if(human_target.check_shields(source, punch_damage, "[source]'s' [picked_hit_type]"))
if(human_target.check_block(source, punch_damage, "[source]'s' [picked_hit_type]"))
source.do_attack_animation(target)
playsound(living_target.loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
log_combat(source, target, "attempted to [picked_hit_type]", "muscle implant")
+1
View File
@@ -1347,6 +1347,7 @@
#include "code\datums\elements\cult_halo.dm"
#include "code\datums\elements\curse_announcement.dm"
#include "code\datums\elements\cursed.dm"
#include "code\datums\elements\damage_threshold.dm"
#include "code\datums\elements\dangerous_surgical_removal.dm"
#include "code\datums\elements\death_drops.dm"
#include "code\datums\elements\death_explosion.dm"