Skip to content

Commit

Permalink
Attack logs for xeno tackling (#6061)
Browse files Browse the repository at this point in the history
# About the pull request

Makes Xenomorphs tackling/disarming people show in attack logs.
Additionally, this also changes `mob/living/carbon/xenomorph/M` to
`mob/living/carbon/xenomorph/attacking_xeno` in the proc's parameters,
and merges two behavior_delegate checks that were right next to each
other.

It's being logged with `log_attack()` rather than the
`msg_admin_attack()` used by human disarming since a xeno tackling
someone is a normal part of the game loop, and not something that could
potentially need admin attention.

# Explain why it's good for the game

"Normal part of the game" or not, this is the sort of thing which should
be in attack logs.

# Testing Photographs and Procedure
<details>
<summary>Screenshots & Videos</summary>

**Xenomorph combat logs:**

![dreamseeker_egsPasjsdj](https://github.com/cmss13-devs/cmss13/assets/57483089/05482a63-045c-437c-a676-82a07d151d26)

**Human combat logs:**

![dreamseeker_UZFD8dNiYt](https://github.com/cmss13-devs/cmss13/assets/57483089/552397e5-c303-4753-aa4b-64845794fb1a)

`attack.log`:
```
[2024-03-31 21:48:27.610] ATTACK: sabreml/(Young Drone (XX-653)) unsuccessfully tackled @SabreML/(Sean Agg) in Open grounds (event P) (45,82,2).
[2024-03-31 21:48:28.684] ATTACK: sabreml/(Young Drone (XX-653)) unsuccessfully tackled @SabreML/(Sean Agg) in Open grounds (event P) (45,82,2).
[2024-03-31 21:48:29.700] ATTACK: sabreml/(Young Drone (XX-653)) successfully tackled @SabreML/(Sean Agg) in Open grounds (event P) (45,82,2).
```

</details>


# Changelog
:cl:
admin: Added attack logging for Xeno tackling.
/:cl:
  • Loading branch information
SabreML committed Apr 6, 2024
1 parent 681199e commit eb5c82c
Showing 1 changed file with 79 additions and 76 deletions.
155 changes: 79 additions & 76 deletions code/modules/mob/living/carbon/xenomorph/attack_alien.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,110 +7,110 @@
* In that case, the first argument is always the attacker. For attack_alien, it should always be Xenomorph sub-types
*/


/mob/living/carbon/human/attack_alien(mob/living/carbon/xenomorph/M, dam_bonus)
if(M.fortify || HAS_TRAIT(M, TRAIT_ABILITY_BURROWED))
// this proc could use refactoring at some point
/mob/living/carbon/human/attack_alien(mob/living/carbon/xenomorph/attacking_xeno, dam_bonus)
if(attacking_xeno.fortify || HAS_TRAIT(attacking_xeno, TRAIT_ABILITY_BURROWED))
return XENO_NO_DELAY_ACTION

var/intent = M.a_intent
var/intent = attacking_xeno.a_intent

if(M.behavior_delegate)
intent = M.behavior_delegate.override_intent(src)
if(attacking_xeno.behavior_delegate)
intent = attacking_xeno.behavior_delegate.override_intent(src)

//Reviewing the four primary intents
switch(intent)

if(INTENT_HELP)
if(on_fire)
extinguish_mob(M)
extinguish_mob(attacking_xeno)
else
M.visible_message(SPAN_NOTICE("[M] caresses [src] with its claws."), \
attacking_xeno.visible_message(SPAN_NOTICE("[attacking_xeno] caresses [src] with its claws."), \
SPAN_NOTICE("We caress [src] with our claws."), null, 5, CHAT_TYPE_XENO_FLUFF)

if(INTENT_GRAB)
if(M == src || anchored || buckled)
if(attacking_xeno == src || anchored || buckled)
return XENO_NO_DELAY_ACTION

if(check_shields(0, M.name)) // Blocking check
M.visible_message(SPAN_DANGER("[M]'s grab is blocked by [src]'s shield!"), \
if(check_shields(0, attacking_xeno.name)) // Blocking check
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno]'s grab is blocked by [src]'s shield!"), \
SPAN_DANGER("Our grab was blocked by [src]'s shield!"), null, 5, CHAT_TYPE_XENO_COMBAT)
playsound(loc, 'sound/weapons/alien_claw_block.ogg', 25, 1) //Feedback
return XENO_ATTACK_ACTION

if(Adjacent(M)) //Logic!
M.start_pulling(src)
if(Adjacent(attacking_xeno)) //Logic!
attacking_xeno.start_pulling(src)

if(INTENT_HARM)
if(M.can_not_harm(src))
M.animation_attack_on(src)
M.visible_message(SPAN_NOTICE("[M] nibbles [src]"), \
if(attacking_xeno.can_not_harm(src))
attacking_xeno.animation_attack_on(src)
attacking_xeno.visible_message(SPAN_NOTICE("[attacking_xeno] nibbles [src]"), \
SPAN_XENONOTICE("We nibble [src]"))
return XENO_ATTACK_ACTION

if(M.behavior_delegate && M.behavior_delegate.handle_slash(src))
if(attacking_xeno.behavior_delegate && attacking_xeno.behavior_delegate.handle_slash(src))
return XENO_NO_DELAY_ACTION

if(stat == DEAD)
to_chat(M, SPAN_WARNING("[src] is dead, why would we want to touch it?"))
to_chat(attacking_xeno, SPAN_WARNING("[src] is dead, why would we want to touch it?"))
return XENO_NO_DELAY_ACTION

if(M.caste && !M.caste.is_intelligent)
if(attacking_xeno.caste && !attacking_xeno.caste.is_intelligent)
if(HAS_TRAIT(src, TRAIT_NESTED) && (status_flags & XENO_HOST))
for(var/obj/item/alien_embryo/embryo in src)
if(HIVE_ALLIED_TO_HIVE(M.hivenumber, embryo.hivenumber))
to_chat(M, SPAN_WARNING("We should not harm this host! It has a sister inside."))
if(HIVE_ALLIED_TO_HIVE(attacking_xeno.hivenumber, embryo.hivenumber))
to_chat(attacking_xeno, SPAN_WARNING("We should not harm this host! It has a sister inside."))
return XENO_NO_DELAY_ACTION

if(check_shields(0, M.name)) // Blocking check
M.visible_message(SPAN_DANGER("[M]'s slash is blocked by [src]'s shield!"), \
if(check_shields(0, attacking_xeno.name)) // Blocking check
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno]'s slash is blocked by [src]'s shield!"), \
SPAN_DANGER("Our slash is blocked by [src]'s shield!"), null, 5, CHAT_TYPE_XENO_COMBAT)
playsound(loc, 'sound/weapons/alien_claw_block.ogg', 25, 1) //Feedback
return XENO_ATTACK_ACTION

//From this point, we are certain a full attack will go out. Calculate damage and modifiers
M.track_slashes(M.caste_type) //Adds to slash stat.
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + dam_bonus
attacking_xeno.track_slashes(attacking_xeno.caste_type) //Adds to slash stat.
var/damage = rand(attacking_xeno.melee_damage_lower, attacking_xeno.melee_damage_upper) + dam_bonus
var/acid_damage = 0
if(M.burn_damage_lower)
acid_damage = rand(M.burn_damage_lower, M.burn_damage_upper)
if(attacking_xeno.burn_damage_lower)
acid_damage = rand(attacking_xeno.burn_damage_lower, attacking_xeno.burn_damage_upper)

//Frenzy auras stack in a way, then the raw value is multipled by two to get the additive modifier
if(M.frenzy_aura > 0)
damage += (M.frenzy_aura * FRENZY_DAMAGE_MULTIPLIER)
if(attacking_xeno.frenzy_aura > 0)
damage += (attacking_xeno.frenzy_aura * FRENZY_DAMAGE_MULTIPLIER)
if(acid_damage)
acid_damage += (M.frenzy_aura * FRENZY_DAMAGE_MULTIPLIER)
acid_damage += (attacking_xeno.frenzy_aura * FRENZY_DAMAGE_MULTIPLIER)

M.animation_attack_on(src)
attacking_xeno.animation_attack_on(src)

//Somehow we will deal no damage on this attack
if(!damage)
playsound(M.loc, 'sound/weapons/alien_claw_swipe.ogg', 25, 1)
M.animation_attack_on(src)
M.visible_message(SPAN_DANGER("[M] lunges at [src]!"), \
playsound(attacking_xeno.loc, 'sound/weapons/alien_claw_swipe.ogg', 25, 1)
attacking_xeno.animation_attack_on(src)
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno] lunges at [src]!"), \
SPAN_DANGER("We lunge at [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
return XENO_ATTACK_ACTION

M.flick_attack_overlay(src, "slash")
attacking_xeno.flick_attack_overlay(src, "slash")
var/obj/limb/affecting
affecting = get_limb(rand_zone(M.zone_selected, 70))
affecting = get_limb(rand_zone(attacking_xeno.zone_selected, 70))
if(!affecting) //No organ, just get a random one
affecting = get_limb(rand_zone(null, 0))
if(!affecting) //Still nothing??
affecting = get_limb("chest") //Gotta have a torso?!

var/armor_block = getarmor(affecting, ARMOR_MELEE)

if(wear_mask && check_zone(M.zone_selected) == "head")
if(wear_mask && check_zone(attacking_xeno.zone_selected) == "head")
if(istype(wear_mask, /obj/item/clothing/mask/gas/yautja))
var/knock_chance = 1
if(M.frenzy_aura > 0)
knock_chance += 2 * M.frenzy_aura
if(M.caste && M.caste.is_intelligent)
if(attacking_xeno.frenzy_aura > 0)
knock_chance += 2 * attacking_xeno.frenzy_aura
if(attacking_xeno.caste && attacking_xeno.caste.is_intelligent)
knock_chance += 2
knock_chance += min(round(damage * 0.25), 10) //Maximum of 15% chance.
if(prob(knock_chance))
playsound(loc, "alien_claw_metal", 25, 1)
M.visible_message(SPAN_DANGER("[M] smashes off [src]'s [wear_mask.name]!"), \
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno] smashes off [src]'s [wear_mask.name]!"), \
SPAN_DANGER("We smash off [src]'s [wear_mask.name]!"), null, 5)
drop_inv_item_on_ground(wear_mask)
if(isyautja(src))
Expand All @@ -121,42 +121,39 @@

var/n_damage = armor_damage_reduction(GLOB.marine_melee, damage, armor_block)

if(M.behavior_delegate)
n_damage = M.behavior_delegate.melee_attack_modify_damage(n_damage, src)
if(attacking_xeno.behavior_delegate)
n_damage = attacking_xeno.behavior_delegate.melee_attack_modify_damage(n_damage, src)
attacking_xeno.behavior_delegate.melee_attack_additional_effects_target(src)
attacking_xeno.behavior_delegate.melee_attack_additional_effects_self()

if(M.behavior_delegate)
var/datum/behavior_delegate/MD = M.behavior_delegate
MD.melee_attack_additional_effects_target(src)
MD.melee_attack_additional_effects_self()

var/slash_noise = M.slash_sound
var/slash_noise = attacking_xeno.slash_sound
var/list/slashdata = list("n_damage" = n_damage, "slash_noise" = slash_noise)
SEND_SIGNAL(src, COMSIG_HUMAN_XENO_ATTACK, slashdata, M)
SEND_SIGNAL(src, COMSIG_HUMAN_XENO_ATTACK, slashdata, attacking_xeno)
var/f_damage = slashdata["n_damage"]
slash_noise = slashdata["slash_noise"]

//The normal attack proceeds
playsound(loc, slash_noise, 25, TRUE)
M.visible_message(SPAN_DANGER("[M] [M.slashes_verb] [src]!"), \
SPAN_DANGER("We [M.slash_verb] [src]!"), null, null, CHAT_TYPE_XENO_COMBAT)
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno] [attacking_xeno.slashes_verb] [src]!"), \
SPAN_DANGER("We [attacking_xeno.slash_verb] [src]!"), null, null, CHAT_TYPE_XENO_COMBAT)

handle_blood_splatter(get_dir(M.loc, src.loc))
handle_blood_splatter(get_dir(attacking_xeno.loc, src.loc))

last_damage_data = create_cause_data(initial(M.name), M)
last_damage_data = create_cause_data(initial(attacking_xeno.name), attacking_xeno)

//Logging, including anti-rulebreak logging
if(status_flags & XENO_HOST && stat != DEAD)
if(HAS_TRAIT(src, TRAIT_NESTED)) //Host was buckled to nest while infected, this is a rule break
attack_log += text("\[[time_stamp()]\] <font color='orange'><B>was [M.slash_verb]ed by [key_name(M)] while they were infected and nested</B></font>")
M.attack_log += text("\[[time_stamp()]\] <font color='red'><B>[M.slash_verb]ed [key_name(src)] while they were infected and nested</B></font>")
message_admins("[key_name(M)] [M.slash_verb]ed [key_name(src)] while they were infected and nested.") //This is a blatant rulebreak, so warn the admins
attack_log += text("\[[time_stamp()]\] <font color='orange'><B>was [attacking_xeno.slash_verb]ed by [key_name(attacking_xeno)] while they were infected and nested</B></font>")
attacking_xeno.attack_log += text("\[[time_stamp()]\] <font color='red'><B>[attacking_xeno.slash_verb]ed [key_name(src)] while they were infected and nested</B></font>")
message_admins("[key_name(attacking_xeno)] [attacking_xeno.slash_verb]ed [key_name(src)] while they were infected and nested.") //This is a blatant rulebreak, so warn the admins
else //Host might be rogue, needs further investigation
attack_log += text("\[[time_stamp()]\] <font color='orange'>was [M.slash_verb]ed by [key_name(M)] while they were infected</font>")
M.attack_log += text("\[[time_stamp()]\] <font color='red'>[M.slash_verb]ed [key_name(src)] while they were infected</font>")
attack_log += text("\[[time_stamp()]\] <font color='orange'>was [attacking_xeno.slash_verb]ed by [key_name(attacking_xeno)] while they were infected</font>")
attacking_xeno.attack_log += text("\[[time_stamp()]\] <font color='red'>[attacking_xeno.slash_verb]ed [key_name(src)] while they were infected</font>")
else //Normal xenomorph friendship with benefits
attack_log += text("\[[time_stamp()]\] <font color='orange'>was [M.slash_verb]ed by [key_name(M)]</font>")
M.attack_log += text("\[[time_stamp()]\] <font color='red'>[M.slash_verb]ed [key_name(src)]</font>")
log_attack("[key_name(M)] [M.slash_verb]ed [key_name(src)]")
attack_log += text("\[[time_stamp()]\] <font color='orange'>was [attacking_xeno.slash_verb]ed by [key_name(attacking_xeno)]</font>")
attacking_xeno.attack_log += text("\[[time_stamp()]\] <font color='red'>[attacking_xeno.slash_verb]ed [key_name(src)]</font>")
log_attack("[key_name(attacking_xeno)] [attacking_xeno.slash_verb]ed [key_name(src)]")

//nice messages so people know that armor works
if(f_damage <= 0.34*damage)
Expand All @@ -176,23 +173,22 @@
to_chat(src, SPAN_WARNING("Your armor softens the acid!"))
apply_damage(n_acid_damage, BURN, affecting) //Burn damage

SEND_SIGNAL(M, COMSIG_HUMAN_ALIEN_ATTACK, src)
SEND_SIGNAL(attacking_xeno, COMSIG_HUMAN_ALIEN_ATTACK, src)

updatehealth()

if(INTENT_DISARM)

if(M.legcuffed && isyautja(src))
to_chat(M, SPAN_XENODANGER("We don't have the dexterity to tackle the headhunter with that thing on our leg!"))
if(attacking_xeno.legcuffed && isyautja(src))
to_chat(attacking_xeno, SPAN_XENODANGER("We don't have the dexterity to tackle the headhunter with that thing on our leg!"))
return XENO_NO_DELAY_ACTION

M.animation_attack_on(src)
if(check_shields(0, M.name)) // Blocking check
M.visible_message(SPAN_DANGER("[M]'s tackle is blocked by [src]'s shield!"), \
attacking_xeno.animation_attack_on(src)
if(check_shields(0, attacking_xeno.name)) // Blocking check
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno]'s tackle is blocked by [src]'s shield!"), \
SPAN_DANGER("We tackle is blocked by [src]'s shield!"), null, 5, CHAT_TYPE_XENO_COMBAT)
playsound(loc, 'sound/weapons/alien_claw_block.ogg', 25, 1) //Feedback
return XENO_ATTACK_ACTION
M.flick_attack_overlay(src, "disarm")
attacking_xeno.flick_attack_overlay(src, "disarm")

var/tackle_mult = 1
var/tackle_min_offset = 0
Expand All @@ -202,22 +198,29 @@
tackle_min_offset += 2
tackle_max_offset += 2

if(M.attempt_tackle(src, tackle_mult, tackle_min_offset, tackle_max_offset))
var/knocked_down
if(attacking_xeno.attempt_tackle(src, tackle_mult, tackle_min_offset, tackle_max_offset))
playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1)
var/strength = rand(M.tacklestrength_min, M.tacklestrength_max)
var/strength = rand(attacking_xeno.tacklestrength_min, attacking_xeno.tacklestrength_max)
Stun(strength)
KnockDown(strength) // Purely for knockdown visuals. All the heavy lifting is done by Stun
M.visible_message(SPAN_DANGER("[M] tackles down [src]!"), \
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno] tackles down [src]!"), \
SPAN_DANGER("We tackle down [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
SEND_SIGNAL(src, COMSIG_MOB_TACKLED_DOWN, M)
SEND_SIGNAL(src, COMSIG_MOB_TACKLED_DOWN, attacking_xeno)
knocked_down = TRUE
else
playsound(loc, 'sound/weapons/alien_claw_swipe.ogg', 25, 1)
if (body_position == LYING_DOWN)
M.visible_message(SPAN_DANGER("[M] tries to tackle [src], but they are already down!"), \
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno] tries to tackle [src], but they are already down!"), \
SPAN_DANGER("We try to tackle [src], but they are already down!"), null, 5, CHAT_TYPE_XENO_COMBAT)
else
M.visible_message(SPAN_DANGER("[M] tries to tackle [src]"), \
attacking_xeno.visible_message(SPAN_DANGER("[attacking_xeno] tries to tackle [src]"), \
SPAN_DANGER("We try to tackle [src]"), null, 5, CHAT_TYPE_XENO_COMBAT)
knocked_down = FALSE

attacking_xeno.attack_log += "\[[time_stamp()]\] <font color='red'>[knocked_down ? "S" : "Uns"]uccessfully tackled [key_name(src)]</font>"
attack_log += "\[[time_stamp()]\] <font color='orange'>Has been [knocked_down ? "" : "un"]successfully tackled by [key_name(attacking_xeno)]</font>"
log_attack("[key_name(attacking_xeno)] [knocked_down ? "" : "un"]successfully tackled [key_name(src)] in [get_area(src)] ([loc.x],[loc.y],[loc.z]).")
return XENO_ATTACK_ACTION


Expand Down

0 comments on commit eb5c82c

Please sign in to comment.