diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index ca8bfd87804..12f1834c045 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -802,6 +802,9 @@ #define COMSIG_XENOABILITY_REGENERATE_SKIN "xenoability_regenerate_skin" #define COMSIG_XENOABILITY_CENTRIFUGAL_FORCE "xenoability_centrifugal_force" +#define COMSIG_XENOABILITY_STEELCREST_HEADBUTT "xenoability_steelcrest_bodyswap_headbutt" +#define COMSIG_XENOABILITY_STEELCREST_SOAK "xenoability_steelcrest_soak" + #define COMSIG_XENOABILITY_EMIT_NEUROGAS "xenoability_emit_neurogas" #define COMSIG_XENOABILITY_SELECT_REAGENT "xenoability_select_reagent" #define COMSIG_XENOABILITY_RADIAL_SELECT_REAGENT "xenoability_radial_select_reagent" diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index 12a9a9eae0d..a8e4d99fb85 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -30,6 +30,7 @@ #define MOVESPEED_ID_XENO_CHARGE "XENO_CHARGE_MODIFIER" #define MOVESPEED_ID_ENHANCEMENT "ENHANCEMENT" #define MOVESPEED_ID_CRESTDEFENSE "CRESTDEFENSE" +#define MOVESPEED_ID_FORTIFY "FORTIFY" #define MOVESPEED_ID_WARRIOR_AGILITY "WARRIOR_AGILITY" #define MOVESPEED_ID_FRENZY_AURA "FRENZY_AURA" #define MOVESPEED_ID_XENO_HEMODILE "XENO_HEMODILE" diff --git a/code/datums/keybinding/xeno.dm b/code/datums/keybinding/xeno.dm index cf6a86e5dc7..b7d4c6b452b 100644 --- a/code/datums/keybinding/xeno.dm +++ b/code/datums/keybinding/xeno.dm @@ -378,6 +378,20 @@ keybind_signal = COMSIG_XENOABILITY_FORTIFY hotkey_keys = list("Space") +/datum/keybinding/xeno/headbutt + name = "headbutt" + full_name = "Steel Crest: Headbutt" + description = "Headbutts into the designated target." + keybind_signal = COMSIG_XENOABILITY_STEELCREST_HEADBUTT + hotkey_keys = list("F") + +/datum/keybinding/xeno/soak + name = "soak" + full_name = "Steel Crest: Soak" + description = "Healing after taking damage" + keybind_signal = COMSIG_XENOABILITY_STEELCREST_SOAK + hotkey_keys = list("E") + /datum/keybinding/xeno/regenerate_skin name = "regenerate_skin" full_name = "Defender: Regenerate Skin" diff --git a/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm index 5edf369cf18..7c31dcfef05 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/defender/abilities_defender.dm @@ -15,32 +15,32 @@ /datum/action/ability/xeno_action/tail_sweep/can_use_action(silent, override_flags) . = ..() - var/mob/living/carbon/xenomorph/X = owner - if(X.crest_defense && X.plasma_stored < (ability_cost * 2)) - to_chat(X, span_xenowarning("We don't have enough plasma, we need [(ability_cost * 2) - X.plasma_stored] more plasma!")) + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(xeno_owner.crest_defense && xeno_owner.plasma_stored < (ability_cost * 2)) + to_chat(xeno_owner, span_xenowarning("We don't have enough plasma, we need [(ability_cost * 2) - xeno_owner.plasma_stored] more plasma!")) return FALSE /datum/action/ability/xeno_action/tail_sweep/action_activate() - var/mob/living/carbon/xenomorph/X = owner + var/mob/living/carbon/xenomorph/xeno_owner = owner GLOB.round_statistics.defender_tail_sweeps++ SSblackbox.record_feedback(FEEDBACK_TALLY, "round_statistics", 1, "defender_tail_sweeps") - X.visible_message(span_xenowarning("\The [X] sweeps its tail in a wide circle!"), \ + xeno_owner.visible_message(span_xenowarning("\The [xeno_owner] sweeps its tail in a wide circle!"), \ span_xenowarning("We sweep our tail in a wide circle!")) - X.add_filter("defender_tail_sweep", 2, gauss_blur_filter(1)) //Add cool SFX - X.spin(4, 1) - X.enable_throw_parry(0.6 SECONDS) - playsound(X,pick('sound/effects/alien/tail_swipe1.ogg','sound/effects/alien/tail_swipe2.ogg','sound/effects/alien/tail_swipe3.ogg'), 25, 1) //Sound effects + xeno_owner.add_filter("defender_tail_sweep", 2, gauss_blur_filter(1)) //Add cool SFX + xeno_owner.spin(4, 1) + xeno_owner.enable_throw_parry(0.6 SECONDS) + playsound(xeno_owner,pick('sound/effects/alien/tail_swipe1.ogg','sound/effects/alien/tail_swipe2.ogg','sound/effects/alien/tail_swipe3.ogg'), 25, 1) //Sound effects var/sweep_range = 1 - var/list/L = orange(sweep_range, X) // Not actually the fruit + var/list/L = orange(sweep_range, xeno_owner) // Not actually the fruit for(var/obj/item/explosive/grenade/G in L) - G.knockback(X, 6, 2) + G.knockback(xeno_owner, 6, 2) for(var/obj/machinery/deployable/mounted/sentry/sentry in L) - var/damage = X.xeno_caste.melee_damage + var/damage = xeno_owner.xeno_caste.melee_damage sentry.take_damage(damage, BRUTE, MELEE) sentry.knock_down() @@ -49,11 +49,11 @@ continue H.add_filter("defender_tail_sweep", 2, gauss_blur_filter(1)) //Add cool SFX; motion blur addtimer(CALLBACK(H, TYPE_PROC_REF(/atom, remove_filter), "defender_tail_sweep"), 0.5 SECONDS) //Remove cool SFX - var/damage = X.xeno_caste.melee_damage + var/damage = xeno_owner.xeno_caste.melee_damage var/affecting = H.get_limb(ran_zone(null, 0)) if(!affecting) //Still nothing?? affecting = H.get_limb("chest") //Gotta have a torso?! - H.knockback(X, sweep_range, 4) + H.knockback(xeno_owner, sweep_range, 4) H.apply_damage(damage, BRUTE, affecting, MELEE) H.apply_damage(damage, STAMINA, updating_health = TRUE) H.Paralyze(0.5 SECONDS) //trip and go @@ -61,18 +61,18 @@ SSblackbox.record_feedback(FEEDBACK_TALLY, "round_statistics", 1, "defender_tail_sweep_hits") shake_camera(H, 2, 1) - to_chat(H, span_xenowarning("We are struck by \the [X]'s tail sweep!")) + to_chat(H, span_xenowarning("We are struck by \the [xeno_owner]'s tail sweep!")) playsound(H,'sound/weapons/alien_claw_block.ogg', 50, 1) - addtimer(CALLBACK(X, TYPE_PROC_REF(/atom, remove_filter), "defender_tail_sweep"), 0.5 SECONDS) //Remove cool SFX + addtimer(CALLBACK(xeno_owner, TYPE_PROC_REF(/atom, remove_filter), "defender_tail_sweep"), 0.5 SECONDS) //Remove cool SFX succeed_activate() - if(X.crest_defense) - X.use_plasma(ability_cost) + if(xeno_owner.crest_defense) + xeno_owner.use_plasma(ability_cost) add_cooldown() /datum/action/ability/xeno_action/tail_sweep/on_cooldown_finish() - var/mob/living/carbon/xenomorph/X = owner - to_chat(X, span_notice("We gather enough strength to tail sweep again.")) + var/mob/living/carbon/xenomorph/xeno_owner = owner + to_chat(xeno_owner, span_notice("We gather enough strength to tail sweep again.")) owner.playsound_local(owner, 'sound/effects/alien/newlarva.ogg', 25, 0, 1) return ..() @@ -179,67 +179,67 @@ /datum/action/ability/xeno_action/toggle_crest_defense/give_action() . = ..() - var/mob/living/carbon/xenomorph/defender/X = owner - last_crest_bonus = X.xeno_caste.crest_defense_armor + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner + last_crest_bonus = xeno_owner.xeno_caste.crest_defense_armor /datum/action/ability/xeno_action/toggle_crest_defense/on_xeno_upgrade() - var/mob/living/carbon/xenomorph/X = owner - if(X.crest_defense) - X.soft_armor = X.soft_armor.modifyAllRatings(-last_crest_bonus) - last_crest_bonus = X.xeno_caste.crest_defense_armor - X.soft_armor = X.soft_armor.modifyAllRatings(last_crest_bonus) - X.add_movespeed_modifier(MOVESPEED_ID_CRESTDEFENSE, TRUE, 0, NONE, TRUE, X.xeno_caste.crest_defense_slowdown) + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(xeno_owner.crest_defense) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(-last_crest_bonus) + last_crest_bonus = xeno_owner.xeno_caste.crest_defense_armor + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(last_crest_bonus) + xeno_owner.add_movespeed_modifier(MOVESPEED_ID_CRESTDEFENSE, TRUE, 0, NONE, TRUE, xeno_owner.xeno_caste.crest_defense_slowdown) else - last_crest_bonus = X.xeno_caste.crest_defense_armor + last_crest_bonus = xeno_owner.xeno_caste.crest_defense_armor /datum/action/ability/xeno_action/toggle_crest_defense/on_cooldown_finish() - var/mob/living/carbon/xenomorph/defender/X = owner - to_chat(X, span_notice("We can [X.crest_defense ? "raise" : "lower"] our crest.")) + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner + to_chat(xeno_owner, span_notice("We can [xeno_owner.crest_defense ? "raise" : "lower"] our crest.")) return ..() /datum/action/ability/xeno_action/toggle_crest_defense/action_activate() - var/mob/living/carbon/xenomorph/defender/X = owner + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner - if(X.crest_defense) + if(xeno_owner.crest_defense) set_crest_defense(FALSE) add_cooldown() return succeed_activate() - var/was_fortified = X.fortify - if(X.fortify) - var/datum/action/ability/xeno_action/fortify/FT = X.actions_by_path[/datum/action/ability/xeno_action/fortify] + var/was_fortified = xeno_owner.fortify + if(xeno_owner.fortify) + var/datum/action/ability/xeno_action/fortify/FT = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/fortify] if(FT.cooldown_timer) - to_chat(X, span_xenowarning("We cannot yet untuck ourselves!")) + to_chat(xeno_owner, span_xenowarning("We cannot yet untuck ourselves!")) return fail_activate() FT.set_fortify(FALSE, TRUE) FT.add_cooldown() - to_chat(X, span_xenowarning("We carefully untuck, keeping our crest lowered.")) + to_chat(xeno_owner, span_xenowarning("We carefully untuck, keeping our crest lowered.")) set_crest_defense(TRUE, was_fortified) add_cooldown() return succeed_activate() /datum/action/ability/xeno_action/toggle_crest_defense/proc/set_crest_defense(on, silent = FALSE) - var/mob/living/carbon/xenomorph/defender/X = owner + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner if(on) if(!silent) - to_chat(X, span_xenowarning("We tuck ourselves into a defensive stance.")) + to_chat(xeno_owner, span_xenowarning("We tuck ourselves into a defensive stance.")) GLOB.round_statistics.defender_crest_lowerings++ SSblackbox.record_feedback(FEEDBACK_TALLY, "round_statistics", 1, "defender_crest_lowerings") - ADD_TRAIT(X, TRAIT_STAGGERIMMUNE, CREST_DEFENSE_TRAIT) //Can now endure impacts/damages that would make lesser xenos flinch - X.soft_armor = X.soft_armor.modifyAllRatings(last_crest_bonus) - X.add_movespeed_modifier(MOVESPEED_ID_CRESTDEFENSE, TRUE, 0, NONE, TRUE, X.xeno_caste.crest_defense_slowdown) + ADD_TRAIT(xeno_owner, TRAIT_STAGGERIMMUNE, CREST_DEFENSE_TRAIT) //Can now endure impacts/damages that would make lesser xenos flinch + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(last_crest_bonus) + xeno_owner.add_movespeed_modifier(MOVESPEED_ID_CRESTDEFENSE, TRUE, 0, NONE, TRUE, xeno_owner.xeno_caste.crest_defense_slowdown) else if(!silent) - to_chat(X, span_xenowarning("We raise our crest.")) + to_chat(xeno_owner, span_xenowarning("We raise our crest.")) GLOB.round_statistics.defender_crest_raises++ SSblackbox.record_feedback(FEEDBACK_TALLY, "round_statistics", 1, "defender_crest_raises") - REMOVE_TRAIT(X, TRAIT_STAGGERIMMUNE, CREST_DEFENSE_TRAIT) - X.soft_armor = X.soft_armor.modifyAllRatings(-last_crest_bonus) - X.remove_movespeed_modifier(MOVESPEED_ID_CRESTDEFENSE) + REMOVE_TRAIT(xeno_owner, TRAIT_STAGGERIMMUNE, CREST_DEFENSE_TRAIT) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(-last_crest_bonus) + xeno_owner.remove_movespeed_modifier(MOVESPEED_ID_CRESTDEFENSE) - X.crest_defense = on - X.update_icons() + xeno_owner.crest_defense = on + xeno_owner.update_icons() // *************************************** // *********** Fortify @@ -254,79 +254,86 @@ KEYBINDING_NORMAL = COMSIG_XENOABILITY_FORTIFY, ) var/last_fortify_bonus = 0 + var/move_on_fortifed = FALSE /datum/action/ability/xeno_action/fortify/give_action() . = ..() - var/mob/living/carbon/xenomorph/defender/X = owner - last_fortify_bonus = X.xeno_caste.fortify_armor + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner + last_fortify_bonus = xeno_owner.xeno_caste.fortify_armor /datum/action/ability/xeno_action/fortify/on_xeno_upgrade() - var/mob/living/carbon/xenomorph/X = owner - if(X.fortify) - X.soft_armor = X.soft_armor.modifyAllRatings(-last_fortify_bonus) - X.soft_armor = X.soft_armor.modifyRating(BOMB = -last_fortify_bonus) + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(xeno_owner.fortify) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(-last_fortify_bonus) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyRating(BOMB = -last_fortify_bonus) - last_fortify_bonus = X.xeno_caste.fortify_armor + last_fortify_bonus = xeno_owner.xeno_caste.fortify_armor - X.soft_armor = X.soft_armor.modifyAllRatings(last_fortify_bonus) - X.soft_armor = X.soft_armor.modifyRating(BOMB = last_fortify_bonus) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(last_fortify_bonus) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyRating(BOMB = last_fortify_bonus) else - last_fortify_bonus = X.xeno_caste.fortify_armor + last_fortify_bonus = xeno_owner.xeno_caste.fortify_armor /datum/action/ability/xeno_action/fortify/on_cooldown_finish() - var/mob/living/carbon/xenomorph/X = owner - to_chat(X, span_notice("We can [X.fortify ? "stand up" : "fortify"] again.")) + var/mob/living/carbon/xenomorph/xeno_owner = owner + to_chat(xeno_owner, span_notice("We can [xeno_owner.fortify ? "stand up" : "fortify"] again.")) return ..() /datum/action/ability/xeno_action/fortify/action_activate() - var/mob/living/carbon/xenomorph/defender/X = owner + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner - if(X.fortify) + if(xeno_owner.fortify) set_fortify(FALSE) add_cooldown() return succeed_activate() - var/was_crested = X.crest_defense - if(X.crest_defense) - var/datum/action/ability/xeno_action/toggle_crest_defense/CD = X.actions_by_path[/datum/action/ability/xeno_action/toggle_crest_defense] + var/was_crested = xeno_owner.crest_defense + if(xeno_owner.crest_defense) + var/datum/action/ability/xeno_action/toggle_crest_defense/CD = xeno_owner.actions_by_path[/datum/action/ability/xeno_action/toggle_crest_defense] if(CD.cooldown_timer) - to_chat(X, span_xenowarning("We cannot yet transition to a defensive stance!")) + to_chat(xeno_owner, span_xenowarning("We cannot yet transition to a defensive stance!")) return fail_activate() CD.set_crest_defense(FALSE, TRUE) CD.add_cooldown() - to_chat(X, span_xenowarning("We tuck our lowered crest into ourselves.")) + to_chat(xeno_owner, span_xenowarning("We tuck our lowered crest into ourselves.")) - var/datum/action/ability/activable/xeno/charge/forward_charge/combo_cooldown = X.actions_by_path[/datum/action/ability/activable/xeno/charge/forward_charge] - combo_cooldown.add_cooldown(cooldown_duration) + var/datum/action/ability/activable/xeno/charge/forward_charge/combo_cooldown = xeno_owner.actions_by_path[/datum/action/ability/activable/xeno/charge/forward_charge] + combo_cooldown?.add_cooldown(cooldown_duration) set_fortify(TRUE, was_crested) add_cooldown() return succeed_activate() /datum/action/ability/xeno_action/fortify/proc/set_fortify(on, silent = FALSE) - var/mob/living/carbon/xenomorph/defender/X = owner + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner GLOB.round_statistics.defender_fortifiy_toggles++ SSblackbox.record_feedback(FEEDBACK_TALLY, "round_statistics", 1, "defender_fortifiy_toggles") if(on) - ADD_TRAIT(X, TRAIT_IMMOBILE, FORTIFY_TRAIT) - ADD_TRAIT(X, TRAIT_STOPS_TANK_COLLISION, FORTIFY_TRAIT) + if(move_on_fortifed) + xeno_owner.add_movespeed_modifier(MOVESPEED_ID_FORTIFY, TRUE, 0, NONE, TRUE, 5) + else + ADD_TRAIT(xeno_owner, TRAIT_IMMOBILE, FORTIFY_TRAIT) + ADD_TRAIT(xeno_owner, TRAIT_STOPS_TANK_COLLISION, FORTIFY_TRAIT) if(!silent) - to_chat(X, span_xenowarning("We tuck ourselves into a defensive stance.")) - X.soft_armor = X.soft_armor.modifyAllRatings(last_fortify_bonus) - X.soft_armor = X.soft_armor.modifyRating(BOMB = last_fortify_bonus) //double bomb bonus for explosion immunity + to_chat(xeno_owner, span_xenowarning("We tuck ourselves into a defensive stance.")) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(last_fortify_bonus) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyRating(BOMB = last_fortify_bonus) //double bomb bonus for explosion immunity owner.drop_all_held_items() // drop items (hugger/jelly) else if(!silent) - to_chat(X, span_xenowarning("We resume our normal stance.")) - X.soft_armor = X.soft_armor.modifyAllRatings(-last_fortify_bonus) - X.soft_armor = X.soft_armor.modifyRating(BOMB = -last_fortify_bonus) - REMOVE_TRAIT(X, TRAIT_IMMOBILE, FORTIFY_TRAIT) - REMOVE_TRAIT(X, TRAIT_STOPS_TANK_COLLISION, FORTIFY_TRAIT) - - X.fortify = on - X.anchored = on - playsound(X.loc, 'sound/effects/stonedoor_openclose.ogg', 30, TRUE) - X.update_icons() + to_chat(xeno_owner, span_xenowarning("We resume our normal stance.")) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyAllRatings(-last_fortify_bonus) + xeno_owner.soft_armor = xeno_owner.soft_armor.modifyRating(BOMB = -last_fortify_bonus) + if(move_on_fortifed) + xeno_owner.remove_movespeed_modifier(MOVESPEED_ID_FORTIFY) + else + REMOVE_TRAIT(xeno_owner, TRAIT_IMMOBILE, FORTIFY_TRAIT) + REMOVE_TRAIT(xeno_owner, TRAIT_STOPS_TANK_COLLISION, FORTIFY_TRAIT) + + xeno_owner.fortify = on + xeno_owner.anchored = on + playsound(xeno_owner.loc, 'sound/effects/stonedoor_openclose.ogg', 30, TRUE) + xeno_owner.update_icons() // *************************************** // *********** Regenerate Skin @@ -344,27 +351,27 @@ ) /datum/action/ability/xeno_action/regenerate_skin/on_cooldown_finish() - var/mob/living/carbon/xenomorph/X = owner - to_chat(X, span_notice("We feel we are ready to shred our skin and grow another.")) + var/mob/living/carbon/xenomorph/xeno_owner = owner + to_chat(xeno_owner, span_notice("We feel we are ready to shred our skin and grow another.")) return ..() /datum/action/ability/xeno_action/regenerate_skin/action_activate() - var/mob/living/carbon/xenomorph/defender/X = owner + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner if(!can_use_action(TRUE)) return fail_activate() - if(X.on_fire) - to_chat(X, span_xenowarning("We can't use that while on fire.")) + if(xeno_owner.on_fire) + to_chat(xeno_owner, span_xenowarning("We can't use that while on fire.")) return fail_activate() - X.emote("roar") - X.visible_message(span_warning("The skin on \the [X] shreds and a new layer can be seen in it's place!"), + xeno_owner.emote("roar") + xeno_owner.visible_message(span_warning("The skin on \the [xeno_owner] shreds and a new layer can be seen in it's place!"), span_notice("We shed our skin, showing the fresh new layer underneath!")) - X.do_jitter_animation(1000) - X.set_sunder(0) - X.heal_overall_damage(50, 50, updating_health = TRUE) //RUTGMC EDIT + xeno_owner.do_jitter_animation(1000) + xeno_owner.set_sunder(0) + xeno_owner.heal_overall_damage(50, 50, updating_health = TRUE) add_cooldown() return succeed_activate() @@ -392,9 +399,9 @@ if(spin_loop_timer) return TRUE . = ..() - var/mob/living/carbon/xenomorph/X = owner - if(X.crest_defense && X.plasma_stored < (ability_cost * 2)) - to_chat(X, span_xenowarning("We don't have enough plasma, we need [(ability_cost * 2) - X.plasma_stored] more plasma!")) + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(xeno_owner.crest_defense && xeno_owner.plasma_stored < (ability_cost * 2)) + to_chat(xeno_owner, span_xenowarning("We don't have enough plasma, we need [(ability_cost * 2) - xeno_owner.plasma_stored] more plasma!")) return FALSE /datum/action/ability/xeno_action/centrifugal_force/action_activate() @@ -416,43 +423,43 @@ /// runs a spin, then starts the timer for a new spin if needed /datum/action/ability/xeno_action/centrifugal_force/proc/do_spin() spin_loop_timer = null - var/mob/living/carbon/xenomorph/X = owner - X.spin(4, 1) - X.enable_throw_parry(0.6 SECONDS) - playsound(X, pick('sound/effects/alien/tail_swipe1.ogg','sound/effects/alien/tail_swipe2.ogg','sound/effects/alien/tail_swipe3.ogg'), 25, 1) //Sound effects + var/mob/living/carbon/xenomorph/xeno_owner = owner + xeno_owner.spin(4, 1) + xeno_owner.enable_throw_parry(0.6 SECONDS) + playsound(xeno_owner, pick('sound/effects/alien/tail_swipe1.ogg','sound/effects/alien/tail_swipe2.ogg','sound/effects/alien/tail_swipe3.ogg'), 25, 1) //Sound effects - for(var/obj/item/explosive/grenade/G in orange (1, X)) - G.knockback(X, 6, 2) + for(var/obj/item/explosive/grenade/G in orange (1, xeno_owner)) + G.knockback(xeno_owner, 6, 2) - for(var/obj/machinery/deployable/mounted/sentry/sentry in orange (1, X)) - var/damage = X.xeno_caste.melee_damage + for(var/obj/machinery/deployable/mounted/sentry/sentry in orange (1, xeno_owner)) + var/damage = xeno_owner.xeno_caste.melee_damage sentry.take_damage(damage, BRUTE, MELEE) sentry.knock_down() - for(var/mob/living/carbon/human/slapped in orange(1, X)) + for(var/mob/living/carbon/human/slapped in orange(1, xeno_owner)) if(slapped.stat == DEAD) continue slapped.add_filter("defender_tail_sweep", 2, gauss_blur_filter(1)) //Add cool SFX; motion blur addtimer(CALLBACK(slapped, TYPE_PROC_REF(/atom, remove_filter), "defender_tail_sweep"), 0.5 SECONDS) //Remove cool SFX - var/damage = X.xeno_caste.melee_damage * 0.5 + var/damage = xeno_owner.xeno_caste.melee_damage * 0.5 var/affecting = slapped.get_limb(ran_zone(null, 0)) if(!affecting) affecting = slapped.get_limb("chest") - slapped.knockback(X, 1, 4) + slapped.knockback(xeno_owner, 1, 4) slapped.apply_damage(damage, BRUTE, affecting, MELEE) slapped.apply_damage(damage, STAMINA, updating_health = TRUE) slapped.Paralyze(0.3 SECONDS) shake_camera(slapped, 2, 1) - to_chat(slapped, span_xenowarning("We are struck by \the [X]'s flying tail!")) + to_chat(slapped, span_xenowarning("We are struck by \the [xeno_owner]'s flying tail!")) playsound(slapped, 'sound/weapons/alien_claw_block.ogg', 50, 1) - succeed_activate(X.crest_defense ? ability_cost * 2 : ability_cost) + succeed_activate(xeno_owner.crest_defense ? ability_cost * 2 : ability_cost) if(step_tick) - step(X, pick(GLOB.alldirs)) + step(xeno_owner, pick(GLOB.alldirs)) step_tick = !step_tick - if(can_use_action(X, ABILITY_IGNORE_COOLDOWN)) + if(can_use_action(xeno_owner, ABILITY_IGNORE_COOLDOWN)) spin_loop_timer = addtimer(CALLBACK(src, PROC_REF(do_spin)), 5, TIMER_STOPPABLE) return stop_spin() @@ -464,3 +471,152 @@ deltimer(spin_loop_timer) spin_loop_timer = null UnregisterSignal(owner, list(SIGNAL_ADDTRAIT(TRAIT_FLOORED), SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED), SIGNAL_ADDTRAIT(TRAIT_IMMOBILE))) + +// *************************************** +// *********** Headbutt +// *************************************** + +/datum/action/ability/activable/xeno/headbutt + name = "Headbutt" + action_icon_state = "headbutt" + desc = "Headbutts into the designated target" + cooldown_duration = 10 SECONDS + ability_cost = 35 + use_state_flags = ABILITY_USE_FORTIFIED|ABILITY_USE_CRESTED // yea + keybinding_signals = list( + KEYBINDING_NORMAL = COMSIG_XENOABILITY_STEELCREST_HEADBUTT, + ) + target_flags = ABILITY_MOB_TARGET + var/base_damage = 30 + +/datum/action/ability/activable/xeno/headbutt/on_cooldown_finish() + to_chat(owner, span_notice("We gather enough strength to headbutt again.")) + return ..() + +/datum/action/ability/activable/xeno/headbutt/can_use_ability(atom/target, silent = FALSE, override_flags) + . = ..() + if(!.) + return FALSE + if(QDELETED(target)) + return FALSE + if(!ishuman(target)) + return FALSE + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner + var/max_dist = 2 - (xeno_owner.crest_defense) + if(!line_of_sight(owner, target, max_dist)) + if(!silent) + to_chat(owner, span_warning("We must get closer to headbutt")) + return FALSE + if(ishuman(target)) + var/mob/living/carbon/human/victim = target + if(isnestedhost(victim)) + return FALSE + if(!CHECK_BITFIELD(use_state_flags|override_flags, ABILITY_IGNORE_DEAD_TARGET) && victim.stat == DEAD) + return FALSE + +/datum/action/ability/activable/xeno/headbutt/use_ability(atom/target) + var/mob/living/carbon/xenomorph/defender/xeno_owner = owner + var/mob/living/victim = target + + SSblackbox.record_feedback(FEEDBACK_TALLY, "round_statistics", 1, "headbutts") + + var/headbutt_distance = 1 + (xeno_owner.crest_defense * 2) + (xeno_owner.fortify * 2) + var/headbutt_damage = base_damage - (xeno_owner.crest_defense * 10) + + if(!xeno_owner.crest_defense) + add_cooldown() + xeno_owner.throw_at(get_step_towards(victim, xeno_owner), 4, 3, owner) + if(!xeno_owner.Adjacent(victim)) + succeed_activate() + return + + owner.visible_message(span_xenowarning("[owner] rams [victim] with its armored crest!"), \ + span_xenowarning("We ram [victim] with our armored crest!")) + + victim.apply_damage(headbutt_damage, BRUTE, BODY_ZONE_CHEST, MELEE) + + xeno_owner.do_attack_animation(victim) + + var/facing = get_dir(xeno_owner, victim) + var/turf/T = victim.loc + var/turf/temp + for(var/x in 1 to headbutt_distance) + temp = get_step(T, facing) + if(!temp) + break + T = temp + victim.throw_at(T, headbutt_distance, 1, owner, TRUE) + + playsound(victim,'sound/weapons/alien_claw_block.ogg', 75, 1) + + succeed_activate() + add_cooldown() + +/datum/action/ability/xeno_action/soak + name = "soak" + action_icon_state = "soak" + desc = "When activated tracks damaged taken for 6 seconds, once the amount of damage reaches 140, the Defender is healed by 75 and the Tail Slam cooldown is reset. If the damage threshold is not reached, nothing happens." + cooldown_duration = 17 SECONDS + ability_cost = 35 + use_state_flags = ABILITY_USE_FORTIFIED|ABILITY_USE_CRESTED // yea + keybinding_signals = list( + KEYBINDING_NORMAL = COMSIG_XENOABILITY_STEELCREST_SOAK, + ) + /// Requires 140 damage taken within 6 seconds to activate the ability + var/damage_threshold = 140 + /// Heal + var/heal_amount = 80 + /// Sunder heal + var/heal_sunder_amount = 25 + /// Initially zero, gets damage added when the ability is activated + var/damage_accumulated = 0 + +/datum/action/ability/xeno_action/soak/action_activate(atom/target) + var/mob/living/carbon/xenomorph/xeno_owner = owner + + RegisterSignal(xeno_owner, COMSIG_XENOMORPH_TAKING_DAMAGE, PROC_REF(damage_accumulate)) + addtimer(CALLBACK(src, PROC_REF(stop_accumulating)), 6 SECONDS) + + xeno_owner.balloon_alert(xeno_owner, "begins to tank incoming damage!") + + to_chat(xeno_owner, span_xenonotice("We begin to tank incoming damage!")) + + xeno_owner.add_filter("steelcrest_enraging", 1, list("type" = "outline", "color" = "#421313", "size" = 1)) + + succeed_activate() + add_cooldown() + +/datum/action/ability/xeno_action/soak/proc/damage_accumulate(datum/source, damage) + SIGNAL_HANDLER + + damage_accumulated += damage + + if(damage_accumulated >= damage_threshold) + addtimer(CALLBACK(src, PROC_REF(enraged), owner), 0.1 SECONDS) //CM use timer, so i do + UnregisterSignal(owner, COMSIG_XENOMORPH_TAKING_DAMAGE) // Two Unregistersignal because if the enrage proc doesnt happen, then it needs to stop counting + +/datum/action/ability/xeno_action/soak/proc/stop_accumulating() + UnregisterSignal(owner, COMSIG_XENOMORPH_TAKING_DAMAGE) + + damage_accumulated = 0 + to_chat(owner, span_xenonotice("We stop taking incoming damage.")) + owner.remove_filter("steelcrest_enraging") + +/datum/action/ability/xeno_action/soak/proc/enraged() + owner.remove_filter("steelcrest_enraging") + owner.add_filter("steelcrest_enraged", 1, list("type" = "outline", "color" = "#ad1313", "size" = 1)) + + owner.visible_message(span_xenowarning("[owner] gets enraged after being damaged enough!"), \ + span_xenowarning("We feel enraged after taking in oncoming damage! Our tail slam's cooldown is reset and we heal!")) + + var/mob/living/carbon/xenomorph/enraged_mob = owner + HEAL_XENO_DAMAGE(enraged_mob, heal_amount, FALSE) + enraged_mob.adjust_sunder(-heal_sunder_amount) + + addtimer(CALLBACK(src, PROC_REF(remove_enrage), owner), 3 SECONDS) + +/datum/action/ability/xeno_action/soak/proc/remove_enrage() + owner.remove_filter("steelcrest_enraged") + +/datum/action/ability/xeno_action/fortify/steel_crest + move_on_fortifed = TRUE diff --git a/code/modules/mob/living/carbon/xenomorph/castes/defender/castedatum_defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/defender/castedatum_defender.dm index 167214217df..d03e76c13cf 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/defender/castedatum_defender.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/defender/castedatum_defender.dm @@ -53,7 +53,10 @@ /datum/action/ability/xeno_action/tail_sweep, ) -/datum/xeno_caste/defender/ancient +/datum/xeno_caste/defender/normal + upgrade = XENO_UPGRADE_NORMAL + +/datum/xeno_caste/defender/steel_crest/normal upgrade = XENO_UPGRADE_NORMAL /datum/xeno_caste/defender/primordial @@ -72,3 +75,35 @@ /datum/action/ability/xeno_action/tail_sweep, /datum/action/ability/xeno_action/centrifugal_force, ) + +/datum/xeno_caste/defender/steel_crest + caste_type_path = /mob/living/carbon/xenomorph/defender/steel_crest + base_caste_type_path = /mob/living/carbon/xenomorph/defender + upgrade_name = "" + caste_name = "Defender" + display_name = "Steel crest" + upgrade = XENO_UPGRADE_BASETYPE + caste_desc = "An alien with an armored crest. It looks very tough." + + // *** Speed *** // + speed = -0.4 + + // *** Defender Abilities *** // + crest_defense_armor = 30 + crest_defense_slowdown = 0.8 + fortify_armor = 40 + + actions = list( + /datum/action/ability/xeno_action/xeno_resting, + /datum/action/ability/xeno_action/watch_xeno, + /datum/action/ability/activable/xeno/psydrain, + /datum/action/ability/xeno_action/fortify/steel_crest, + /datum/action/ability/xeno_action/toggle_crest_defense, + /datum/action/ability/activable/xeno/headbutt, + /datum/action/ability/xeno_action/soak, + ) + +/datum/xeno_caste/defender/steel_crest/primordial + upgrade_name = "Primordial" + upgrade = XENO_UPGRADE_PRIMO + caste_desc = "Alien with an incredibly tough and armored head crest able to endure even the strongest hits." diff --git a/code/modules/mob/living/carbon/xenomorph/castes/defender/defender.dm b/code/modules/mob/living/carbon/xenomorph/castes/defender/defender.dm index fbd230aff73..bba27badc16 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/defender/defender.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/defender/defender.dm @@ -43,9 +43,13 @@ if(isnull(.)) return if(. == CONSCIOUS && fortify) //No longer conscious. - var/datum/action/ability/xeno_action/fortify/FT = actions_by_path[/datum/action/ability/xeno_action/fortify] - FT.set_fortify(FALSE) //Fortify prevents dragging due to the anchor component. - + //Fortify prevents dragging due to the anchor component. // TODO: Unshitcode me + if(actions_by_path[/datum/action/ability/xeno_action/fortify]) + var/datum/action/ability/xeno_action/fortify/FT = actions_by_path[/datum/action/ability/xeno_action/fortify] + FT.set_fortify(FALSE) + else if(actions_by_path[/datum/action/ability/xeno_action/fortify/steel_crest]) + var/datum/action/ability/xeno_action/fortify/FT = actions_by_path[/datum/action/ability/xeno_action/fortify/steel_crest] + FT.set_fortify(FALSE) // *************************************** // *********** Mob overrides @@ -54,3 +58,24 @@ /mob/living/carbon/xenomorph/defender/Initialize(mapload) . = ..() AddComponent(/datum/component/throw_parry) + +// *************************************** +// *********** Steel crest +// *************************************** + +/mob/living/carbon/xenomorph/defender/steel_crest + icon = 'icons/Xeno/castes/defender/steel_crest.dmi' + caste_base_type = /datum/xeno_caste/defender/steel_crest + +// *************************************** +// *********** Front Armor +// *************************************** + +/mob/living/carbon/xenomorph/defender/steel_crest/projectile_hit(obj/projectile/proj, cardinal_move, uncrossing) + if(SEND_SIGNAL(src, COMSIG_XENO_PROJECTILE_HIT, proj, cardinal_move, uncrossing) & COMPONENT_PROJECTILE_DODGE) + return FALSE + if(proj.ammo.flags_ammo_behavior & AMMO_SKIPS_ALIENS) + return FALSE + if((cardinal_move & REVERSE_DIR(dir))) + proj.damage -= proj.damage * (0.2 * get_sunder()) + return ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fe0aebcd4cf..7fd5a5060d6 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -323,6 +323,8 @@ return FALSE if(buckled || now_pushing) return + if(anchored) + return if(isliving(A)) var/mob/living/L = A diff --git a/icons/Xeno/actions.dmi b/icons/Xeno/actions.dmi index 92e37923d6f..d1dd26d3cbc 100644 Binary files a/icons/Xeno/actions.dmi and b/icons/Xeno/actions.dmi differ diff --git a/icons/Xeno/castes/defender/steel_crest.dmi b/icons/Xeno/castes/defender/steel_crest.dmi new file mode 100644 index 00000000000..414a578f107 Binary files /dev/null and b/icons/Xeno/castes/defender/steel_crest.dmi differ