diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 3df635790a5e2..660e7a23b1444 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -267,6 +267,11 @@ #define TRAIT_ABILITY_BURROWED "t_ability_burrowed" /// Xenos with this trait can toggle long sight while resting. #define TRAIT_ABILITY_SIGHT_IGNORE_REST "t_ability_sight_ignore_rest" + + +// -- specific ability traits -- +#define TRAIT_ABILITY_ASSASSINATE "t_ability_assassinate" +#define TRAIT_ABILITY_INVIS "t_ability_invis" /// Used by shielder to check stance. #define TRAIT_ABILITY_ENCLOSED_PLATES "t_ability_enclosed_plates" /// Used by shielder for reflective plates. diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_abilities.dm index d4fb390f14f92..99f8a67f17234 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_abilities.dm @@ -19,10 +19,12 @@ action_type = XENO_ACTION_CLICK plasma_cost = 20 - var/duration = 30 SECONDS // 30 seconds base var/invis_timer_id = TIMER_ID_NULL var/alpha_amount = 25 var/speed_buff = 0.20 + var/invis_recharge_time = 20 SECONDS + var/invis_start_time = -1 // Special value for when we're not invisible + var/invis_duration = 30 SECONDS // tightly coupled 'buff next slash' action /datum/action/xeno_action/onclick/lurker_assassinate @@ -35,6 +37,8 @@ plasma_cost = 20 var/buff_duration = 50 + var/buffed_slash_damage_ratio = 1.2 + var/slash_slow_duration = 35 // VAMP LURKER ABILITIES diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm index 9bd5e35cb8e34..4ff9876731d2f 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm @@ -71,28 +71,18 @@ /datum/behavior_delegate/lurker_base name = "Base Lurker Behavior Delegate" - // Config - var/invis_recharge_time = 20 SECONDS - var/invis_start_time = -1 // Special value for when we're not invisible - var/invis_duration = 30 SECONDS // so we can display how long the lurker is invisible to it - var/buffed_slash_damage_ratio = 1.2 - var/slash_slow_duration = 35 - - // State - var/next_slash_buffed = FALSE - /datum/behavior_delegate/lurker_base/melee_attack_modify_damage(original_damage, mob/living/carbon/target_carbon) - if (!isxeno_human(target_carbon)) + if(!isxeno_human(target_carbon)) return original_damage - if (next_slash_buffed) + if(HAS_TRAIT(bound_xeno, TRAIT_ABILITY_ASSASSINATE)) to_chat(bound_xeno, SPAN_XENOHIGHDANGER("We significantly strengthen our attack, slowing [target_carbon]!")) to_chat(target_carbon, SPAN_XENOHIGHDANGER("You feel a sharp pain as [bound_xeno] slashes you, slowing you down!")) - original_damage *= buffed_slash_damage_ratio - target_carbon.set_effect(get_xeno_stun_duration(target_carbon, 3), SUPERSLOW) - next_slash_buffed = FALSE var/datum/action/xeno_action/onclick/lurker_assassinate/ability = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_assassinate) - if (ability) + original_damage *= ability.buffed_slash_damage_ratio + target_carbon.set_effect(get_xeno_stun_duration(target_carbon, 3), SUPERSLOW) + REMOVE_TRAIT(bound_xeno, TRAIT_ABILITY_ASSASSINATE, TRAIT_SOURCE_ABILITY("assassinate")) + if(ability) ability.button.icon_state = "template_xeno" return original_damage @@ -103,15 +93,19 @@ if(!isxeno_human(target_carbon)) return - if(next_slash_buffed && target_carbon) + if(HAS_TRAIT(bound_xeno, TRAIT_ABILITY_ASSASSINATE) && target_carbon) return INTENT_HARM /datum/behavior_delegate/lurker_base/melee_attack_additional_effects_target(mob/living/carbon/target_carbon) - if (!isxeno_human(target_carbon)) + if(!isxeno_human(target_carbon)) + return + + var/datum/action/xeno_action/onclick/lurker_assassinate/lurker_ass = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_assassinate) + if(!lurker_ass) return - if (HAS_TRAIT(target_carbon, TRAIT_FLOORED)) - new /datum/effects/xeno_slow(target_carbon, bound_xeno, null, null, get_xeno_stun_duration(target_carbon, slash_slow_duration)) + if(HAS_TRAIT(target_carbon, TRAIT_FLOORED)) + new /datum/effects/xeno_slow(target_carbon, bound_xeno, null, null, get_xeno_stun_duration(target_carbon, lurker_ass.slash_slow_duration)) return @@ -119,69 +113,46 @@ ..() var/datum/action/xeno_action/onclick/lurker_invisibility/lurker_invis_action = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_invisibility) - if (lurker_invis_action) + if(lurker_invis_action) lurker_invis_action.invisibility_off() // Full cooldown -/datum/behavior_delegate/lurker_base/proc/decloak_handler(mob/source) - SIGNAL_HANDLER - var/datum/action/xeno_action/onclick/lurker_invisibility/lurker_invis_action = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_invisibility) - if(istype(lurker_invis_action)) - lurker_invis_action.invisibility_off(0.5) // Partial refund of remaining time - -/// Implementation for enabling invisibility. -/datum/behavior_delegate/lurker_base/proc/on_invisibility() - var/datum/action/xeno_action/activable/pounce/lurker/lurker_pounce_action = get_action(bound_xeno, /datum/action/xeno_action/activable/pounce/lurker) - if(lurker_pounce_action) - lurker_pounce_action.knockdown = TRUE // pounce knocks down - lurker_pounce_action.freeze_self = TRUE - ADD_TRAIT(bound_xeno, TRAIT_CLOAKED, TRAIT_SOURCE_ABILITY("cloak")) - RegisterSignal(bound_xeno, COMSIG_MOB_EFFECT_CLOAK_CANCEL, PROC_REF(decloak_handler)) - bound_xeno.stealth = TRUE - invis_start_time = world.time - -/// Implementation for disabling invisibility. -/datum/behavior_delegate/lurker_base/proc/on_invisibility_off() - var/datum/action/xeno_action/activable/pounce/lurker/lurker_pounce_action = get_action(bound_xeno, /datum/action/xeno_action/activable/pounce/lurker) - if(lurker_pounce_action) - lurker_pounce_action.knockdown = FALSE // pounce no longer knocks down - lurker_pounce_action.freeze_self = FALSE - bound_xeno.stealth = FALSE - REMOVE_TRAIT(bound_xeno, TRAIT_CLOAKED, TRAIT_SOURCE_ABILITY("cloak")) - UnregisterSignal(bound_xeno, COMSIG_MOB_EFFECT_CLOAK_CANCEL) - invis_start_time = -1 - /datum/behavior_delegate/lurker_base/append_to_stat() . = list() + var/datum/action/xeno_action/onclick/lurker_invisibility/lurker_inv = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_invisibility) + // Invisible - if(invis_start_time != -1) - var/time_left = (invis_duration-(world.time - invis_start_time)) / 10 + if(lurker_inv.invis_start_time != -1) + var/time_left = (lurker_inv.invis_duration-(world.time - lurker_inv.invis_start_time)) / 10 . += "Invisibility Remaining: [time_left] second\s." return - var/datum/action/xeno_action/onclick/lurker_invisibility/lurker_invisibility_action = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_invisibility) - if(!lurker_invisibility_action) + if(!lurker_inv) return if(!bound_xeno.client?.prefs.show_cooldown_messages) return // Recharged - if(lurker_invisibility_action.cooldown_timer_id == TIMER_ID_NULL) + if(lurker_inv.cooldown_timer_id == TIMER_ID_NULL) . += "Invisibility Recharge: Ready." return // Recharging - var/time_left = timeleft(lurker_invisibility_action.cooldown_timer_id) / 10 + var/time_left = timeleft(lurker_inv.cooldown_timer_id) / 10 . += "Invisibility Recharge: [time_left] second\s." + /datum/behavior_delegate/lurker_base/on_collide(atom/movable/movable_atom) . = ..() if(!ishuman(movable_atom)) return - if(!bound_xeno || !bound_xeno.stealth) + if(!bound_xeno) + return + + if(!HAS_TRAIT(bound_xeno, TRAIT_ABILITY_INVIS)) return var/datum/action/xeno_action/onclick/lurker_invisibility/lurker_invisibility_action = get_action(bound_xeno, /datum/action/xeno_action/onclick/lurker_invisibility) @@ -195,7 +166,6 @@ to_chat(bound_xeno, SPAN_XENOHIGHDANGER("We bumped into someone and lost our invisibility!")) lurker_invisibility_action.invisibility_off(0.5) // partial refund of remaining time - /datum/action/xeno_action/activable/pounce/lurker/additional_effects(mob/living/living_mob) var/mob/living/carbon/xenomorph/xeno = owner if(!istype(xeno)) @@ -234,14 +204,12 @@ if(!istype(xeno)) return - if(!action_cooldown_check()) - return - if(!check_and_use_plasma_owner()) - return + + XENO_ACTION_CHECK_USE_PLASMA(xeno) xeno.deselect_timer = world.time + 5 // Half a second to prevent double clicks - if(xeno.stealth) + if(HAS_TRAIT(xeno, TRAIT_ABILITY_INVIS)) invisibility_off(0.9) // Near full refund of remaining time return ..() @@ -252,14 +220,25 @@ xeno.speed_modifier -= speed_buff xeno.recalculate_speed() - var/datum/behavior_delegate/lurker_base/behavior = xeno.behavior_delegate - behavior.on_invisibility() + on_invisibility() // if we go off early, this also works fine. - invis_timer_id = addtimer(CALLBACK(src, PROC_REF(invisibility_off)), duration, TIMER_STOPPABLE) + invis_timer_id = addtimer(CALLBACK(src, PROC_REF(invisibility_off)), invis_duration, TIMER_STOPPABLE) return ..() +/// Implementation for enabling invisibility. +/datum/action/xeno_action/onclick/lurker_invisibility/proc/on_invisibility() + var/mob/living/carbon/xenomorph/xeno = owner + var/datum/action/xeno_action/activable/pounce/lurker/lurker_pounce_action = get_action(xeno, /datum/action/xeno_action/activable/pounce/lurker) + if(lurker_pounce_action) + lurker_pounce_action.knockdown = TRUE // pounce knocks down + lurker_pounce_action.freeze_self = TRUE + ADD_TRAIT(xeno, TRAIT_CLOAKED, TRAIT_SOURCE_ABILITY("cloak")) + ADD_TRAIT(xeno, TRAIT_ABILITY_INVIS, TRAIT_SOURCE_ABILITY("invis")) + RegisterSignal(xeno, COMSIG_MOB_EFFECT_CLOAK_CANCEL, PROC_REF(decloak_handler)) + invis_start_time = world.time + /// Implementation for disabling invisibility. /// (refund_multiplier) indicates how much cooldown to refund based on time remaining /// 0 indicates full cooldown; 0.5 indicates 50% of remaining time is refunded @@ -268,7 +247,11 @@ if(!istype(xeno)) return - if(owner.alpha == initial(owner.alpha) && !xeno.stealth) + + if(owner.alpha == initial(owner.alpha)) + return + + if(!HAS_TRAIT(xeno, TRAIT_ABILITY_INVIS)) return if(invis_timer_id != TIMER_ID_NULL) @@ -283,18 +266,25 @@ xeno.speed_modifier += speed_buff xeno.recalculate_speed() - var/datum/behavior_delegate/lurker_base/behavior = xeno.behavior_delegate - if(!istype(behavior)) - CRASH("lurker_base behavior_delegate missing/invalid for [xeno]!") - - var/recharge_time = behavior.invis_recharge_time - if(behavior.invis_start_time > 0) // Sanity + if(invis_start_time > 0) // Sanity refund_multiplier = clamp(refund_multiplier, 0, 1) - var/remaining = 1 - (world.time - behavior.invis_start_time) / behavior.invis_duration - recharge_time = behavior.invis_recharge_time - remaining * refund_multiplier * behavior.invis_recharge_time - apply_cooldown_override(recharge_time) + var/remaining = 1 - (world.time - invis_start_time) / invis_duration + invis_recharge_time = invis_recharge_time - remaining * refund_multiplier * invis_recharge_time - behavior.on_invisibility_off() + apply_cooldown_override(invis_recharge_time) + + var/datum/action/xeno_action/activable/pounce/lurker/lurker_pounce_action = get_action(xeno, /datum/action/xeno_action/activable/pounce/lurker) + if(lurker_pounce_action) + lurker_pounce_action.knockdown = FALSE // pounce no longer knocks down + lurker_pounce_action.freeze_self = FALSE + REMOVE_TRAIT(xeno, TRAIT_CLOAKED, TRAIT_SOURCE_ABILITY("cloak")) + REMOVE_TRAIT(xeno, TRAIT_ABILITY_INVIS, TRAIT_SOURCE_ABILITY("invis")) + UnregisterSignal(xeno, COMSIG_MOB_EFFECT_CLOAK_CANCEL) + invis_start_time = -1 + +/datum/action/xeno_action/onclick/lurker_invisibility/proc/decloak_handler(mob/source) + SIGNAL_HANDLER + invisibility_off(0.5) // Partial refund of remaining time /datum/action/xeno_action/onclick/lurker_invisibility/ability_cooldown_over() if(owner.client?.prefs.show_cooldown_messages) @@ -304,18 +294,12 @@ /datum/action/xeno_action/onclick/lurker_assassinate/use_ability(atom/targeted_atom) var/mob/living/carbon/xenomorph/xeno = owner - if (!istype(xeno)) - return - - if (!action_cooldown_check()) + if(!istype(xeno)) return - if (!check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(xeno) - var/datum/behavior_delegate/lurker_base/behavior = xeno.behavior_delegate - if (istype(behavior)) - behavior.next_slash_buffed = TRUE + ADD_TRAIT(xeno, TRAIT_ABILITY_ASSASSINATE, TRAIT_SOURCE_ABILITY("assassinate")) to_chat(xeno, SPAN_XENOHIGHDANGER("Our next slash will deal increased damage!")) @@ -327,13 +311,11 @@ /datum/action/xeno_action/onclick/lurker_assassinate/proc/unbuff_slash() var/mob/living/carbon/xenomorph/xeno = owner - if (!istype(xeno)) + if(!istype(xeno)) + return + + if(!HAS_TRAIT(xeno, TRAIT_ABILITY_ASSASSINATE)) return - var/datum/behavior_delegate/lurker_base/behavior = xeno.behavior_delegate - if (istype(behavior)) - // In case slash has already landed - if (!behavior.next_slash_buffed) - return - behavior.next_slash_buffed = FALSE + REMOVE_TRAIT(xeno, TRAIT_ABILITY_ASSASSINATE, TRAIT_SOURCE_ABILITY("assassinate")) to_chat(xeno, SPAN_XENODANGER("We have waited too long, our slash will no longer deal increased damage!"))