diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 3df635790a5e2..3fa1c13355149 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -258,7 +258,7 @@ /// If the mob won't drop items held in face slot when downed #define TRAIT_IRON_TEETH "t_iron_teeth" -// -- ability traits -- +// -- general ability traits -- /// Xenos with this trait cannot have plasma transferred to them #define TRAIT_ABILITY_NO_PLASMA_TRANSFER "t_ability_no_plasma_transfer" /// Shows that the xeno queen is on ovi @@ -267,6 +267,15 @@ #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_ASSASINATE "t_ability_assasinate" +#define TRAIT_ABILITY_INVIS "t_ability_invis" +#define TRAIT_ABILITY_DODGE "t_ability_dodge" +#define TRAIT_ABILITY_RED_TAG "t_ability_red_tag" +#define TRAIT_ABILITY_YELLOW_TAG "t_ability_yellow_tag" +#define TRAIT_ABILITY_SHED_SPIKES "t_ability_shed_spikes" /// 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/Evolution.dm b/code/modules/mob/living/carbon/xenomorph/Evolution.dm index ff5348756c8ae..77f57f8208e04 100644 --- a/code/modules/mob/living/carbon/xenomorph/Evolution.dm +++ b/code/modules/mob/living/carbon/xenomorph/Evolution.dm @@ -285,7 +285,7 @@ GLOBAL_LIST_EMPTY(deevolved_ckeys) to_chat(src, SPAN_WARNING("We must be at full health to evolve.")) return FALSE - if(fortify || crest_defense || stealth || HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES) || HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) + if(fortify || crest_defense || HAS_TRAIT(src, TRAIT_ABILITY_INVIS) || HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES) || HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) to_chat(src, SPAN_WARNING("We cannot evolve while in this stance.")) return FALSE @@ -326,7 +326,7 @@ GLOBAL_LIST_EMPTY(deevolved_ckeys) if(tier == 0 || tier == 4) to_chat(src, SPAN_XENOWARNING("We can't transmute.")) return - if(fortify || crest_defense || stealth) + if(fortify || crest_defense || HAS_TRAIT(src, TRAIT_ABILITY_INVIS)) to_chat(src, SPAN_XENOWARNING("We can't transmute while in this stance.")) return if(lock_evolve) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 51e9bc990dfe9..5948c8fcb11bd 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -295,7 +295,6 @@ var/tunnel = FALSE /// for check on lurker invisibility - var/stealth = FALSE var/fortify = FALSE var/crest_defense = FALSE /// For drones/hivelords. Extends the maximum build range they have @@ -827,7 +826,7 @@ return TRUE playsound(puller.loc, 'sound/weapons/pierce.ogg', 25, 1) puller.visible_message(SPAN_WARNING("[puller] tried to pull [src] but instead gets a tail swipe to the head!")) - if(stealth) + if(HAS_TRAIT(src, TRAIT_ABILITY_INVIS)) puller.apply_effect(caste.tacklestrength_min, WEAKEN) return FALSE puller.apply_effect(rand(caste.tacklestrength_min,caste.tacklestrength_max), WEAKEN) 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/abilities/praetorian/praetorian_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_abilities.dm index 5ffeccb1a44e5..7239cc7510384 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/praetorian/praetorian_abilities.dm @@ -172,6 +172,9 @@ action_type = XENO_ACTION_CLICK ability_primacy = XENO_TAIL_STAB + var/blunt_damage = 8 // How much damage Harpoon Tail on DISARM mode do. (pierces armor) + var/tail_mode = null // Harpoon Tail mode, used only to display in status. + /datum/action/xeno_action/activable/prae_impale name = "Impale" action_icon_state = "prae_impale" @@ -197,6 +200,11 @@ var/speed_buff_amount = 0.5 var/afterimage_interval = 1 DECISECONDS + var/dodge_start_time = -1 // Used to countdown DANCER_DODGE_TIME. + var/refund_multiplier = 2.0 // How much refund we want to get back? 1.0 is 1s used to 1s cooldown, 2.0 is 1s used 2s cooldown. + var/recharge_time = null // Used in calculation, finalized number will be displayed as cooldown. + var/safe_click_cooldown = 0 // Cooldown after activation to prevent accidental double click. + /datum/action/xeno_action/activable/prae_tail_trip name = "Tail Trip" action_icon_state = "prae_tail_trip" diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm index c676fff618263..e730b656c8db3 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/ravager/ravager_abilities.dm @@ -149,4 +149,7 @@ var/ammo_type = /datum/ammo/xeno/bone_chips/spread var/shrapnel_amount = 40 + var/shard_lock_duration = 15 SECONDS + var/shard_lock_speed_mod = 0.45 + diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_abilities.dm index 528dc9767a139..158cf0a43da44 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/runner/runner_abilities.dm @@ -38,6 +38,7 @@ macro_path = /datum/action/xeno_action/verb/verb_acider_acid ability_primacy = XENO_PRIMARY_ACTION_1 action_type = XENO_ACTION_CLICK + var/acid_cost = 100 /datum/action/xeno_action/activable/acider_for_the_hive @@ -46,4 +47,19 @@ macro_path = /datum/action/xeno_action/verb/verb_acider_sacrifice action_type = XENO_ACTION_CLICK ability_primacy = XENO_PRIMARY_ACTION_2 + var/minimal_acid = 200 + var/caboom_timer = 20 + var/caboom_left = 20 + var/caboom_loop = 1 + + var/caboom_left_display = 20 SECONDS + + var/caboom_trigger + var/caboom_last_proc + var/list/caboom_sound = list('sound/effects/runner_charging_1.ogg','sound/effects/runner_charging_2.ogg') + + var/caboom_acid_ratio = 200 + var/caboom_burn_damage_ratio = 5 + var/caboom_burn_range_ratio = 100 + var/caboom_struct_acid_type = /obj/effect/xenomorph/acid diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm index d2cd1e6615566..855b4ac468ded 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm @@ -13,12 +13,16 @@ var/cooldown_message = null var/no_cooldown_msg = FALSE - var/cooldown_timer_id = TIMER_ID_NULL // holds our timer ID + var/cooldown_timer_id = TIMER_ID_NULL // holds cooldown timer ID // Track state so we can effectively REDUCE xeno_cooldown by removing and replacing timers. var/current_cooldown_start_time = 0 var/current_cooldown_duration = 0 + var/duration_timer_id = TIMER_ID_NULL // holds duration timer ID + var/current_duration_start_time = 0 + var/current_duration_duration = 0 + // Charging /// When set, an ability has to be charged up by being the active ability before it can be used var/charge_time = null @@ -43,6 +47,9 @@ . = ..() /datum/action/xeno_action/process(delta_time) + if(duration_timer_id != TIMER_ID_NULL) + return update_duration_visual() + return update_cooldown_visual() /datum/action/xeno_action/proc/remove_charge() @@ -472,13 +479,39 @@ if(action_start_message) to_chat(owner, SPAN_NOTICE(action_start_message)) +///Use to display duration time of active ability. +/datum/action/xeno_action/proc/start_duration_display(duration) + if(duration_timer_id != TIMER_ID_NULL) + deltimer(duration_timer_id) + + duration_timer_id = addtimer(CALLBACK(src, PROC_REF(end_duration_display)), duration, TIMER_STOPPABLE) + + current_duration_start_time = world.time + current_duration_duration = duration + + START_PROCESSING(SSfasteffects, src) + +///Use to stop display duration time of active ability. +/datum/action/xeno_action/proc/end_duration_display() + duration_timer_id = TIMER_ID_NULL + current_duration_start_time = 0 + current_duration_duration = 0 + + update_button_icon() + +/datum/action/xeno_action/proc/update_duration_visual() + var/time_left = max(current_duration_start_time + current_duration_duration - world.time, 0) + if(!owner || time_left <= 0 || duration_timer_id == TIMER_ID_NULL) + button.set_maptext() + return PROCESS_KILL + button.set_maptext(SMALL_FONTS(7, round(time_left/10, 0.1)), 4, 4) + /datum/action/xeno_action/proc/update_cooldown_visual() var/time_left = max(current_cooldown_start_time + current_cooldown_duration - world.time, 0) if(!owner || time_left <= 0 || cooldown_timer_id == TIMER_ID_NULL) button.set_maptext() return PROCESS_KILL - else - button.set_maptext(SMALL_FONTS(7, round(time_left/10, 0.1)), 4, 4) + button.set_maptext(SMALL_FONTS_COLOR(7, round(time_left/10, 0.1), "#fffb00"), 4, 4) /datum/action/xeno_action/proc/on_select(mob/user) return diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm b/code/modules/mob/living/carbon/xenomorph/castes/Lurker.dm index 9bd5e35cb8e34..7aa37a275919b 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_ASSASINATE)) 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_ASSASINATE, TRAIT_SOURCE_ABILITY("assasinate")) + 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_ASSASINATE) && 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,19 @@ ..() 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() - - // Invisible - if(invis_start_time != -1) - var/time_left = (invis_duration-(world.time - 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) - return - - if(!bound_xeno.client?.prefs.show_cooldown_messages) - return - - // Recharged - if(lurker_invisibility_action.cooldown_timer_id == TIMER_ID_NULL) - . += "Invisibility Recharge: Ready." - return - - // Recharging - var/time_left = timeleft(lurker_invisibility_action.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 +139,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 +177,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 +193,26 @@ 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 + start_duration_display(invis_duration) + /// 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 +221,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 +240,26 @@ 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 + + apply_cooldown_override(invis_recharge_time) + end_duration_display() + + 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 - behavior.on_invisibility_off() +/datum/action/xeno_action/onclick/lurker_invisibility/proc/decloak_handler() + 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 +269,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_ASSASINATE, TRAIT_SOURCE_ABILITY("assasinate")) to_chat(xeno, SPAN_XENOHIGHDANGER("Our next slash will deal increased damage!")) @@ -327,13 +286,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_ASSASINATE)) 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_ASSASINATE, TRAIT_SOURCE_ABILITY("assasinate")) to_chat(xeno, SPAN_XENODANGER("We have waited too long, our slash will no longer deal increased damage!")) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm index a6fc83204cda6..d7cd73a9507aa 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Ravager.dm @@ -254,7 +254,7 @@ color += num2text(alpha, 2, 16) xeno.add_filter("empower_rage", 1, list("type" = "outline", "color" = color, "size" = 3)) - addtimer(CALLBACK(xeno, PROC_REF(weaken_superbuff), xeno, behavior), 5 SECONDS) + addtimer(CALLBACK(xeno, PROC_REF(weaken_superbuff), xeno), 5 SECONDS) /datum/action/xeno_action/onclick/empower/proc/weaken_superbuff(mob/living/carbon/xenomorph/xeno, datum/behavior_delegate/ravager_base/behavior) @@ -264,7 +264,7 @@ color += num2text(alpha, 2, 16) xeno.add_filter("empower_rage", 1, list("type" = "outline", "color" = color, "size" = 3)) - addtimer(CALLBACK(xeno, PROC_REF(remove_superbuff), xeno, behavior), 1.5 SECONDS) + addtimer(CALLBACK(xeno, PROC_REF(remove_superbuff), xeno), 1.5 SECONDS) /datum/action/xeno_action/onclick/empower/proc/remove_superbuff(mob/living/carbon/xenomorph/xeno, datum/behavior_delegate/ravager_base/behavior) behavior.empower_targets = 0 diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm index 4fe0425c410f0..1f0561f2df4b2 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Spitter.dm @@ -68,21 +68,17 @@ skull = /obj/item/skull/spitter pelt = /obj/item/pelt/spitter -/datum/action/xeno_action/onclick/charge_spit/use_ability(atom/A) +/datum/action/xeno_action/onclick/charge_spit/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/zenomorf = owner - if (!action_cooldown_check()) + if(!istype(zenomorf)) return - if (!istype(zenomorf) || !zenomorf.check_state()) - return - - if (buffs_active) + if(buffs_active) to_chat(zenomorf, SPAN_XENOHIGHDANGER("We cannot stack this!")) return - if (!check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(zenomorf) to_chat(zenomorf, SPAN_XENOHIGHDANGER("We accumulate acid in your glands. Our next spit will be stronger but shorter-ranged.")) to_chat(zenomorf, SPAN_XENOWARNING("Additionally, we are slightly faster and more armored for a small amount of time.")) @@ -94,6 +90,7 @@ zenomorf.armor_modifier += armor_buff_amount zenomorf.recalculate_speed() zenomorf.recalculate_armor() + start_duration_display(duration) /// Though the ability's other buffs are supposed to last for its duration, it's only supposed to enhance one spit. RegisterSignal(zenomorf, COMSIG_XENO_POST_SPIT, PROC_REF(disable_spatter)) @@ -111,11 +108,12 @@ zenomorf.balloon_alert(zenomorf, "our spits are back to normal", text_color = "#93ec78") zenomorf.ammo = GLOB.ammo_list[/datum/ammo/xeno/acid] // el codigo de mierda es mi ciudad UnregisterSignal(zenomorf, COMSIG_XENO_POST_SPIT) + end_duration_display() /datum/action/xeno_action/onclick/charge_spit/proc/remove_effects() var/mob/living/carbon/xenomorph/zenomorf = owner - if (!istype(zenomorf)) + if(!istype(zenomorf)) return zenomorf.speed_modifier += speed_buff_amount @@ -126,7 +124,7 @@ disable_spatter() buffs_active = FALSE -/datum/action/xeno_action/activable/tail_stab/spitter/use_ability(atom/A) +/datum/action/xeno_action/activable/tail_stab/spitter/use_ability(atom/target_atom) var/target = ..() if(iscarbon(target)) var/mob/living/carbon/carbon_target = target diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/defender/steel_crest.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/defender/steel_crest.dm index 50957326b920d..7f2082db0aac7 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/defender/steel_crest.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/defender/steel_crest.dm @@ -22,7 +22,6 @@ defender.recalculate_stats() - // Steel crest override /datum/action/xeno_action/activable/fortify/steel_crest/apply_modifiers(mob/living/carbon/xenomorph/xeno, fortify_state) if(fortify_state) @@ -37,20 +36,14 @@ xeno.damage_modifier += XENO_DAMAGE_MOD_SMALL -/datum/action/xeno_action/onclick/soak/use_ability(atom/A) +/datum/action/xeno_action/onclick/soak/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/steelcrest = owner - if (!action_cooldown_check()) - return - - if (!steelcrest.check_state()) - return - - if(!check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(steelcrest) RegisterSignal(steelcrest, COMSIG_MOB_TAKE_DAMAGE, PROC_REF(damage_accumulate)) addtimer(CALLBACK(src, PROC_REF(stop_accumulating)), 6 SECONDS) + start_duration_display(6 SECONDS) steelcrest.balloon_alert(steelcrest, "begins to tank incoming damage!") @@ -74,15 +67,18 @@ /datum/action/xeno_action/onclick/soak/proc/stop_accumulating() UnregisterSignal(owner, COMSIG_MOB_TAKE_DAMAGE) + end_duration_display() damage_accumulated = 0 to_chat(owner, SPAN_XENONOTICE("We stop taking incoming damage.")) owner.remove_filter("steelcrest_enraging") /datum/action/xeno_action/onclick/soak/proc/enraged() + end_duration_display() 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!")) + owner.visible_message(SPAN_XENOWARNING("[owner] gets enraged and their carapace start to rapidly mend!"), SPAN_XENOWARNING("We feel enraged after taking in oncoming damage! Our tail slam's cooldown is reset and our carapace start to rapidly mend!")) + owner.flick_heal_overlay(3 SECONDS, "#00B800") var/mob/living/carbon/xenomorph/enraged_mob = owner enraged_mob.gain_health(75) // pretty reasonable amount of health recovered @@ -90,7 +86,7 @@ // Check actions list for tail slam and reset it's cooldown if it's there var/datum/action/xeno_action/activable/tail_stab/slam/slam_action = locate() in owner.actions - if (slam_action && !slam_action.action_cooldown_check()) + if(slam_action && !slam_action.action_cooldown_check()) slam_action.end_cooldown() diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/dancer.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/dancer.dm index 616418396fadc..de13352bc2e4f 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/dancer.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/dancer.dm @@ -45,28 +45,6 @@ /// How much time is left on timer. (used for status) var/time_left = null - /// Check for slashed target that had yellow tag - var/spread_slash_triggered = FALSE - - /// How many targets got yellow tag. - var/spread_count = 0 - - /// How much damage Harpoon Tail on DISARM mode do. (pierces armor) - var/blunt_damage = 8 - /// Harpoon Tail mode, used only to display in status. - var/tail_mode = null - - /// Is Dodge ability active? - var/dodge_activated = FALSE - /// Used to countdown DANCER_DODGE_TIME. - var/dodge_start_time = -1 - /// How much refund we want to get back? 1.0 is 1s used to 1s cooldown, 2.0 is 1s used 2s cooldown. - var/refund_multiplier = 2.0 - /// Used in calculation, finalized number will be displayed as cooldown. - var/recharge_time = null - /// Cooldown after activation to prevent accidental double click. - var/safe_click_cooldown = 0 - /// Timer to prevent dancer from spreading yellow tags. var/last_dancer_spread_time = 0 @@ -74,23 +52,27 @@ . = list() . += "Guaranteed Dodge every [bound_xeno.dodge_threshold] bullet\s." . += "Yellow Tag Spread Delay: 5 seconds." - intent_detection() - . += "Tail Lance Intent: [tail_mode]" - if(tail_mode == "Blunt") - . += "Damage: [blunt_damage] AP" + + var/datum/action/xeno_action/activable/tail_stab/harpoon_tail/harpoon_action = get_action(bound_xeno, /datum/action/xeno_action/activable/tail_stab/harpoon_tail) + harpoon_action.intent_detection() + . += "Tail Lance Intent: [harpoon_action.tail_mode]" + if(harpoon_action.tail_mode == "Blunt") + . += "Damage: [harpoon_action.blunt_damage] AP" . += "Cooldown: 3 seconds." - if(dodge_start_time != -1) - time_left = (DANCER_DODGE_TIME - (world.time - dodge_start_time)) / 10 + + var/datum/action/xeno_action/onclick/prae_dodge/dodge_action = get_action(bound_xeno, /datum/action/xeno_action/onclick/prae_dodge) + if(dodge_action.dodge_start_time != -1) + time_left = (DANCER_DODGE_TIME - (world.time - dodge_action.dodge_start_time)) / 10 . += "Dodge Remaining: [time_left] second\s." return /datum/behavior_delegate/praetorian_dancer/melee_attack_additional_effects_self() ..() - if(!spread_slash_triggered) + if(!HAS_TRAIT(bound_xeno, TRAIT_ABILITY_YELLOW_TAG)) return - spread_slash_triggered = FALSE + REMOVE_TRAIT(bound_xeno, TRAIT_ABILITY_YELLOW_TAG, TRAIT_SOURCE_ABILITY("yellow_tag")) var/datum/action/xeno_action/activable/prae_impale/impale_action = get_action(bound_xeno, /datum/action/xeno_action/activable/prae_impale) if(!impale_action.action_cooldown_check()) @@ -121,30 +103,30 @@ break if(consumed_spread) - spread_slash_triggered = TRUE + ADD_TRAIT(bound_xeno, TRAIT_ABILITY_YELLOW_TAG, TRAIT_SOURCE_ABILITY("yellow_tag")) if(target_carbon.health <= 0) try_spread_tags_from(target_carbon) -/datum/behavior_delegate/praetorian_dancer/proc/try_spread_tags_from(mob/living/carbon/human/source) - if(!ishuman(source)) +/datum/behavior_delegate/praetorian_dancer/proc/try_spread_tags_from(mob/living/carbon/human/target_human) + if(!ishuman(target_human)) return - var/turf/origin = get_turf(source) + var/turf/origin = get_turf(target_human) if(!origin) return if(world.time < last_dancer_spread_time + DANCER_YELLOW_TAG_SPREAD_DURATION) return - if(world.time < source.last_target_spread_time + DANCER_YELLOW_TAG_SPREAD_CD) + if(world.time < target_human.last_target_spread_time + DANCER_YELLOW_TAG_SPREAD_CD) return - source.last_target_spread_time = world.time + target_human.last_target_spread_time = world.time var/spread_count = 0 for(var/mob/living/carbon/human/human_target in view(DANCER_YELLOW_TAG_SPREAD_DIST)) - if(human_target == source) + if(human_target == target_human) continue if(human_target.stat == DEAD || human_target.stat == UNCONSCIOUS) continue @@ -164,49 +146,47 @@ if(spread_count >= 0) to_chat(bound_xeno, SPAN_XENOHIGHDANGER("Fear spreads among the prey, their weakness fuels your instincts to strike them down!")) -/datum/behavior_delegate/praetorian_dancer/proc/intent_detection() - if(bound_xeno && bound_xeno.a_intent == INTENT_DISARM) - tail_mode = "Blunt" - else - tail_mode = "Normal" -/datum/action/xeno_action/activable/tail_stab/harpoon_tail/ability_act(mob/living/carbon/xenomorph/xeno, mob/living/carbon/target, obj/limb/limb) - if(!istype(xeno) || !istype(target)) - return - var/datum/behavior_delegate/praetorian_dancer/behavior = xeno.behavior_delegate - if(!istype(behavior)) +/datum/action/xeno_action/activable/tail_stab/harpoon_tail/ability_act(mob/living/carbon/xenomorph/xeno, mob/living/carbon/target_carbon, obj/limb/limb) + if(!istype(xeno) || !istype(target_carbon)) return if(xeno.a_intent == INTENT_DISARM) - target.last_damage_data = create_cause_data(initial(xeno.caste_type), xeno) + target_carbon.last_damage_data = create_cause_data(initial(xeno.caste_type), xeno) xeno.visible_message( - SPAN_XENOWARNING("[xeno] smash [target] with flat side of its tail!"), - SPAN_XENOWARNING("We smash [target] with flat side of our tail!") + SPAN_XENOWARNING("[xeno] smash [target_carbon] with flat side of its tail!"), + SPAN_XENOWARNING("We smash [target_carbon] with flat side of our tail!") ) - xeno.animation_attack_on(target) - xeno.flick_attack_overlay(target, "slam") + xeno.animation_attack_on(target_carbon) + xeno.flick_attack_overlay(target_carbon, "slam") if(xeno.behavior_delegate) - xeno.behavior_delegate.melee_attack_additional_effects_target(target) + xeno.behavior_delegate.melee_attack_additional_effects_target(target_carbon) - playsound(target, "punch", 25, TRUE) - target.apply_damage(behavior.blunt_damage, BRUTE, "chest") + playsound(target_carbon, "punch", 25, TRUE) + target_carbon.apply_damage(blunt_damage, BRUTE, "chest") apply_cooldown(cooldown_modifier = 0.3) update_button_icon() - return target + return target_carbon return ..() +/datum/action/xeno_action/activable/tail_stab/harpoon_tail/proc/intent_detection() + var/mob/living/carbon/xenomorph/xeno = owner + if(xeno && xeno.a_intent == INTENT_DISARM) + tail_mode = "Blunt" + else + tail_mode = "Normal" + + + + /datum/action/xeno_action/activable/prae_impale/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/dancer_user = owner - if(!action_cooldown_check()) - return - - if(!dancer_user.check_state()) - return + XENO_ACTION_CHECK(dancer_user) if(!ismob(target_atom)) apply_cooldown_override(impale_click_miss_cooldown) @@ -240,19 +220,18 @@ to_chat(dancer_user, SPAN_WARNING("We can't attack through [atom_in_turf]!")) return - if(!check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(dancer_user) apply_cooldown() - var/buffed = FALSE + REMOVE_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG, TRAIT_SOURCE_ABILITY("red_tag")) for(var/datum/effects/dancer_tag/spread/tag_spread in target_carbon.effects_list) - buffed = TRUE + ADD_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG, TRAIT_SOURCE_ABILITY("red_tag")) qdel(tag_spread) apply_cooldown_override() break for(var/datum/effects/dancer_tag/normal/dancer_tag_effect in target_carbon.effects_list) - buffed = TRUE + ADD_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG, TRAIT_SOURCE_ABILITY("red_tag")) qdel(dancer_tag_effect) break @@ -264,60 +243,50 @@ dancer_user.face_atom(target_atom) var/damage = get_xeno_damage_slash(target_carbon, rand(dancer_user.melee_damage_lower, dancer_user.melee_damage_upper)) - + var/buffed = HAS_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG) dancer_user.visible_message(SPAN_DANGER("\The [dancer_user] violently slices [target_atom] with its tail[buffed?" twice":""]!"), SPAN_DANGER("We slice [target_atom] with our tail[buffed?" twice":""]!")) - var/list/attack_data = list( - "attacker" = dancer_user, - "target" = target_carbon, - "damage" = damage - ) - impale_strike(attack_data) + impale_strike(dancer_user, target_carbon, damage) if(buffed) dancer_user.emote("roar") // Feedback for the player that we got the magic double impale - addtimer(CALLBACK(src, PROC_REF(impale_strike), attack_data), 4 DECISECONDS) + addtimer(CALLBACK(src, PROC_REF(impale_strike), dancer_user, target_carbon, damage), 4 DECISECONDS) return ..() -/datum/action/xeno_action/activable/prae_impale/proc/impale_strike(list/attack_data) - var/mob/living/carbon/xenomorph/attacker = attack_data["attacker"] - var/mob/living/carbon/target = attack_data["target"] - var/damage = attack_data["damage"] - if(!attacker || !target || target.stat == DEAD || QDELETED(attacker) || QDELETED(target)) + +/datum/action/xeno_action/activable/prae_impale/proc/impale_strike(mob/living/carbon/xenomorph/dancer_user, mob/living/carbon/target_carbon, damage) + if(!dancer_user || !target_carbon || target_carbon.stat == DEAD || QDELETED(dancer_user) || QDELETED(target_carbon)) return - attacker.animation_attack_on(target) - attacker.flick_attack_overlay(target, "tail") + dancer_user.animation_attack_on(target_carbon) + dancer_user.flick_attack_overlay(target_carbon, "tail") - target.last_damage_data = create_cause_data(initial(attacker.caste_type), attacker) - target.apply_armoured_damage(damage, ARMOR_MELEE, BRUTE, "chest", 10) - playsound(target, 'sound/weapons/alien_tail_attack.ogg', 30, TRUE) + target_carbon.last_damage_data = create_cause_data(initial(dancer_user.caste_type), dancer_user) + target_carbon.apply_armoured_damage(damage, ARMOR_MELEE, BRUTE, "chest", 10) + playsound(target_carbon, 'sound/weapons/alien_tail_attack.ogg', 30, TRUE) -/datum/action/xeno_action/onclick/prae_dodge/use_ability(atom/target) - var/mob/living/carbon/xenomorph/dodge_user = owner - if(!istype(dodge_user) || !dodge_user.check_state()) - return - var/datum/behavior_delegate/praetorian_dancer/behavior = dodge_user.behavior_delegate - if(!istype(behavior)) + +/datum/action/xeno_action/onclick/prae_dodge/use_ability(atom/target_atom) + var/mob/living/carbon/xenomorph/dodge_user = owner + if(!istype(dodge_user)) return - if(behavior.dodge_activated) + if(HAS_TRAIT(dodge_user, TRAIT_ABILITY_DODGE)) remove_effects() return - if(!action_cooldown_check()) - return + XENO_ACTION_CHECK(dodge_user) if(!check_and_use_plasma_owner(200)) return - behavior.dodge_activated = TRUE - behavior.dodge_start_time = world.time - behavior.safe_click_cooldown = world.time + 1 SECONDS + ADD_TRAIT(dodge_user, TRAIT_ABILITY_DODGE, TRAIT_SOURCE_ABILITY("dodge")) + dodge_start_time = world.time + safe_click_cooldown = world.time + 1 SECONDS button.icon_state = "template_active" dodge_user.speed_modifier -= speed_buff_amount dodge_user.dodge_threshold -= 3 @@ -325,6 +294,8 @@ dodge_user.recalculate_speed() dodge_user.balloon_alert(dodge_user, "we start our evasive stance!", text_color = "#7d32bb", delay = 1 SECONDS) + start_duration_display(duration) + INVOKE_ASYNC(src, PROC_REF(create_afterimage_sequence), dodge_user, duration) if(dodge_timer != TIMER_ID_NULL) @@ -339,18 +310,14 @@ if(!istype(dodge_remove)) return - var/datum/behavior_delegate/praetorian_dancer/behavior = dodge_remove.behavior_delegate - if(!istype(behavior)) - return - - if(!behavior.dodge_activated) + if(!HAS_TRAIT(dodge_remove, TRAIT_ABILITY_DODGE)) return - if(world.time < behavior.safe_click_cooldown) + if(world.time < safe_click_cooldown) to_chat(dodge_remove, SPAN_XENOWARNING("We need a moment before breaking our evasive stance!")) return - behavior.dodge_activated = FALSE + REMOVE_TRAIT(dodge_remove, TRAIT_ABILITY_DODGE, TRAIT_SOURCE_ABILITY("dodge")) button.icon_state = "template_xeno" dodge_remove.speed_modifier += speed_buff_amount dodge_remove.dodge_threshold += 3 @@ -359,16 +326,18 @@ dodge_remove.reset_position_to_initial() dodge_remove.balloon_alert(dodge_remove, "our evasive stance fades", text_color = "#bb5d32", delay = 1 SECONDS) + end_duration_display() + if(dodge_timer != TIMER_ID_NULL) deltimer(dodge_timer) dodge_timer = TIMER_ID_NULL - if(behavior.dodge_start_time > 0) - var/used_ratio = round((world.time - behavior.dodge_start_time) / duration, 0.1) - behavior.recharge_time = max(DANCER_DODGE_TIME * used_ratio * behavior.refund_multiplier, 5 SECONDS) + if(dodge_start_time > 0) + var/used_ratio = round((world.time - dodge_start_time) / duration, 0.1) + recharge_time = max(DANCER_DODGE_TIME * used_ratio * refund_multiplier, 5 SECONDS) - behavior.dodge_start_time = -1 - apply_cooldown_override(behavior.recharge_time) + dodge_start_time = -1 + apply_cooldown_override(recharge_time) /datum/action/xeno_action/onclick/prae_dodge/proc/create_afterimage_sequence(mob/living/carbon/xenomorph/dodge_user, duration) if(!dodge_user || !dodge_user.loc) @@ -388,8 +357,7 @@ return var/mob/living/carbon/xenomorph/dodge_user = state.owner - var/datum/behavior_delegate/praetorian_dancer/behavior = dodge_user.behavior_delegate - if(!istype(behavior) || !behavior.dodge_activated) + if(!HAS_TRAIT(dodge_user, TRAIT_ABILITY_DODGE)) return var/turf/current_position = get_turf(dodge_user.loc) @@ -444,6 +412,8 @@ addtimer(CALLBACK(afterimage, TYPE_PROC_REF(/obj/effect/overlay/afterimage, fade_out_afterimage))) + + /obj/effect/overlay/afterimage/proc/fade_out_afterimage() if(!src) return @@ -484,14 +454,15 @@ var/fade_max_steps = 3 var/fade_delay = 1 DECISECONDS + + /datum/action/xeno_action/activable/prae_tail_trip/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/dancer_user = owner - if(!action_cooldown_check()) + if(!istype(dancer_user)) return - if(!istype(dancer_user) || !dancer_user.check_state()) - return + XENO_ACTION_CHECK(dancer_user) if(!ismob(target_atom)) apply_cooldown_override(tail_click_miss_cooldown) @@ -508,9 +479,7 @@ to_chat(dancer_user, SPAN_XENOWARNING("[target_atom] is dead, why would we want to attack it?")) return - if(!check_and_use_plasma_owner()) - return - + XENO_ACTION_CHECK_USE_PLASMA(dancer_user) if(ishuman(target_carbon)) var/mob/living/carbon/human/target_human = target_carbon @@ -537,43 +506,43 @@ dancer_user.face_atom(target_carbon) dancer_user.flick_attack_overlay(target_carbon, "disarm") - var/buffed = FALSE + REMOVE_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG, TRAIT_SOURCE_ABILITY("red_tag")) var/datum/effects/dancer_tag/normal/dancer_tag_effect = locate() in target_carbon.effects_list var/datum/effects/dancer_tag/spread/tag_spread = locate() in target_carbon.effects_list if(tag_spread) - buffed = TRUE + ADD_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG, TRAIT_SOURCE_ABILITY("red_tag")) qdel(tag_spread) apply_cooldown_override() if(dancer_tag_effect) - buffed = TRUE + ADD_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG, TRAIT_SOURCE_ABILITY("red_tag")) qdel(dancer_tag_effect) - if(!buffed) + if(!HAS_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG)) new /datum/effects/xeno_slow(target_carbon, dancer_user, null, null, get_xeno_stun_duration(target_carbon, slow_duration)) var/stun_duration = stun_duration_default var/daze_duration = 0 - if(buffed) + if(HAS_TRAIT(dancer_user, TRAIT_ABILITY_RED_TAG)) stun_duration = stun_duration_buffed daze_duration = daze_duration_buffed var/xeno_smashed = FALSE if(isxeno(target_carbon)) - var/mob/living/carbon/xenomorph/Xeno = target_carbon - if(Xeno.mob_size >= MOB_SIZE_BIG) + var/mob/living/carbon/xenomorph/target_xeno = target_carbon + if(target_xeno.mob_size >= MOB_SIZE_BIG) xeno_smashed = TRUE - shake_camera(Xeno, 10, 1) - dancer_user.visible_message(SPAN_XENODANGER("[dancer_user] smashes [Xeno] with it's tail!"), SPAN_XENODANGER("We smash [Xeno] with your tail!")) - to_chat(Xeno, SPAN_XENOHIGHDANGER("You feel dizzy as [dancer_user] smashes you with their tail!")) - dancer_user.animation_attack_on(Xeno) + shake_camera(target_xeno, 10, 1) + dancer_user.visible_message(SPAN_XENODANGER("[dancer_user] smashes [target_xeno] with it's tail!"), SPAN_XENODANGER("We smash [target_xeno] with your tail!")) + to_chat(target_xeno, SPAN_XENOHIGHDANGER("You feel dizzy as [dancer_user] smashes you with their tail!")) + dancer_user.animation_attack_on(target_xeno) if(!xeno_smashed) - if (stun_duration > 0) + if(stun_duration > 0) target_carbon.apply_effect(stun_duration, WEAKEN) dancer_user.visible_message(SPAN_XENODANGER("[dancer_user] trips [target_atom] with it's tail!"), SPAN_XENODANGER("We trip [target_atom] with our tail!")) dancer_user.spin_circle() diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/oppressor.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/oppressor.dm index d3843ebbef424..2405b7121453c 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/oppressor.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/praetorian/oppressor.dm @@ -51,17 +51,10 @@ /datum/action/xeno_action/activable/tail_stab/tail_seize/use_ability(atom/targetted_atom) var/mob/living/carbon/xenomorph/stabbing_xeno = owner - if(!action_cooldown_check()) + if(world.time <= stabbing_xeno.next_move) return FALSE - if(!stabbing_xeno.check_state()) - return FALSE - - if (world.time <= stabbing_xeno.next_move) - return FALSE - - if(!check_and_use_plasma_owner()) - return FALSE + XENO_ACTION_CHECK_USE_PLASMA(stabbing_xeno) stabbing_xeno.visible_message(SPAN_XENODANGER("\The [stabbing_xeno] uncoils and wildly throws out its tail!"), SPAN_XENODANGER("We uncoil our tail wildly in front of us!")) @@ -79,25 +72,21 @@ xeno_attack_delay(stabbing_xeno) return ..() -/datum/action/xeno_action/activable/prae_abduct/use_ability(atom/atom) +/datum/action/xeno_action/activable/prae_abduct/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/abduct_user = owner - if(!atom || atom.layer >= FLY_LAYER || !isturf(abduct_user.loc)) + if(!target_atom || target_atom.layer >= FLY_LAYER || !isturf(abduct_user.loc)) return - if(!action_cooldown_check() || abduct_user.action_busy) + if(abduct_user.action_busy) return - if(!abduct_user.check_state()) - return - - if(!check_plasma_owner()) - return + XENO_ACTION_CHECK(abduct_user) // Build our turflist var/list/turf/turflist = list() var/list/telegraph_atom_list = list() - var/facing = get_dir(abduct_user, atom) + var/facing = get_dir(abduct_user, target_atom) var/turf/turf = abduct_user.loc var/turf/temp = abduct_user.loc for(var/distance in 0 to max_distance) @@ -158,7 +147,7 @@ break turflist += turf - facing = get_dir(turf, atom) + facing = get_dir(turf, target_atom) telegraph_atom_list += new /obj/effect/xenomorph/xeno_telegraph/abduct_hook(turf, windup) if(allow_one_more_step) @@ -168,17 +157,19 @@ to_chat(abduct_user, SPAN_XENOWARNING("We don't have any room to do our abduction!")) return - abduct_user.visible_message(SPAN_XENODANGER("\The [abduct_user]'s segmented tail starts coiling..."), SPAN_XENODANGER("We begin coiling our tail, aiming towards \the [atom]...")) + abduct_user.visible_message(SPAN_XENODANGER("\The [abduct_user]'s segmented tail starts coiling..."), SPAN_XENODANGER("We begin coiling our tail, aiming towards \the [target_atom]...")) abduct_user.emote("roar") var/throw_target_turf = get_step(abduct_user, facing) ADD_TRAIT(abduct_user, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Abduct")) + start_duration_display(windup) if(!do_after(abduct_user, windup, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE, numticks = 1)) + end_duration_display(windup) to_chat(abduct_user, SPAN_XENOWARNING("You relax your tail.")) apply_cooldown() - for (var/obj/effect/xenomorph/xeno_telegraph/xenotelegraph in telegraph_atom_list) + for(var/obj/effect/xenomorph/xeno_telegraph/xenotelegraph in telegraph_atom_list) telegraph_atom_list -= xenotelegraph qdel(xenotelegraph) @@ -186,27 +177,32 @@ return - if(!check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(abduct_user) REMOVE_TRAIT(abduct_user, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Abduct")) playsound(get_turf(abduct_user), 'sound/effects/bang.ogg', 25, 0) - abduct_user.visible_message(SPAN_XENODANGER("\The [abduct_user] suddenly uncoils its tail, firing it towards [atom]!"), SPAN_XENODANGER("We uncoil our tail, sending it out towards \the [atom]!")) + abduct_user.visible_message(SPAN_XENODANGER("\The [abduct_user] suddenly uncoils its tail, firing it towards [target_atom]!"), SPAN_XENODANGER("We uncoil our tail, sending it out towards \the [target_atom]!")) var/list/targets = list() for(var/turf/target_turf in turflist) for(var/mob/living/carbon/target in target_turf) if(!isxeno_human(target) || abduct_user.can_not_harm(target) || target.is_dead() || target.is_mob_incapacitated(TRUE) || target.mob_size >= MOB_SIZE_BIG) continue - targets += target - if(LAZYLEN(targets) == 1) - abduct_user.balloon_alert(abduct_user, "slowed one target", text_color = "#51a16c") - else if(LAZYLEN(targets) == 2) - abduct_user.balloon_alert(abduct_user, "rooted two targets", text_color = "#51a16c") - else if(LAZYLEN(targets) >= 3) - abduct_user.balloon_alert(abduct_user, "stunned [LAZYLEN(targets)] targets", text_color = "#51a16c") + + var/target_count = LAZYLEN(targets) + var/captured_message + + if(target_count == 1) + captured_message = "slowed one target" + else if(target_count == 2) + captured_message = "rooted two targets" + else if(target_count >= 3) + captured_message = "stunned [target_count] targets" + + if(captured_message) + abduct_user.balloon_alert(abduct_user, captured_message, text_color = "#51a16c") apply_cooldown() @@ -231,7 +227,6 @@ target.apply_effect(get_xeno_stun_duration(target, 1.3), WEAKEN) to_chat(target, SPAN_XENOHIGHDANGER("You are slammed into the other victims of [abduct_user]!")) - shake_camera(target, 10, 1) var/obj/effect/beam/tail_beam = abduct_user.beam(target, "oppressor_tail", 'icons/effects/beam.dmi', 0.5 SECONDS, 8) @@ -251,18 +246,12 @@ /datum/action/xeno_action/activable/oppressor_punch/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/oppressor_user = owner - if (!action_cooldown_check()) - return - - if (!isxeno_human(target_atom) || oppressor_user.can_not_harm(target_atom)) - return - - if (!oppressor_user.check_state()) + if(!isxeno_human(target_atom) || oppressor_user.can_not_harm(target_atom)) return var/mob/living/carbon/target_carbon = target_atom - if (!oppressor_user.Adjacent(target_carbon)) + if(!oppressor_user.Adjacent(target_carbon)) return if(target_carbon.stat == DEAD) @@ -270,11 +259,10 @@ var/obj/limb/target_limb = target_carbon.get_limb(check_zone(oppressor_user.zone_selected)) - if (ishuman(target_carbon) && (!target_limb || (target_limb.status & LIMB_DESTROYED))) + if(ishuman(target_carbon) && (!target_limb || (target_limb.status & LIMB_DESTROYED))) target_limb = target_carbon.get_limb("chest") - if (!check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(oppressor_user) target_carbon.last_damage_data = create_cause_data(oppressor_user.caste_type, oppressor_user) @@ -287,11 +275,11 @@ oppressor_user.animation_attack_on(target_carbon) oppressor_user.flick_attack_overlay(target_carbon, "punch") - if (!(target_carbon.mobility_flags & MOBILITY_MOVE) || !(target_carbon.mobility_flags & MOBILITY_STAND) || target_carbon.slowed) + if(!(target_carbon.mobility_flags & MOBILITY_MOVE) || !(target_carbon.mobility_flags & MOBILITY_STAND) || target_carbon.slowed) target_carbon.apply_damage(get_xeno_damage_slash(target_carbon, damage), BRUTE, target_limb? target_limb.name : "chest") ADD_TRAIT(target_carbon, TRAIT_IMMOBILIZED, TRAIT_SOURCE_ABILITY("Oppressor Punch")) - if (ishuman(target_carbon)) + if(ishuman(target_carbon)) var/mob/living/carbon/human/human_to_update = target_carbon human_to_update.update_xeno_hostile_hud() @@ -313,17 +301,16 @@ apply_cooldown() return ..() -/datum/action/xeno_action/activable/tail_lash/use_ability(atom/atoms) +/datum/action/xeno_action/activable/tail_lash/use_ability(atom/target_atom) var/mob/living/carbon/xenomorph/lash_user = owner - if (!istype(lash_user) || !lash_user.check_state() || !action_cooldown_check()) + if(!istype(lash_user) || !target_atom) return - if(!atoms || atoms.layer >= FLY_LAYER || !isturf(lash_user.loc)) + if(target_atom.layer >= FLY_LAYER || !isturf(lash_user.loc)) return - if (!check_plasma_owner()) - return + XENO_ACTION_CHECK(lash_user) // Transient turf list var/list/target_turfs = list() @@ -332,7 +319,7 @@ // Code to get a 2x3 area of turfs var/turf/root = get_turf(lash_user) - var/facing = Get_Compass_Dir(lash_user, atoms) + var/facing = Get_Compass_Dir(lash_user, target_atom) var/turf/infront = get_step(root, facing) var/turf/left = get_step(root, turn(facing, 90)) var/turf/right = get_step(root, turn(facing, -90)) @@ -345,17 +332,17 @@ temp_turfs += infront_right for(var/turf/turfs_to_check in temp_turfs) - if (!istype(turfs_to_check)) + if(!istype(turfs_to_check)) continue - if (turfs_to_check.density) + if(turfs_to_check.density) continue target_turfs += turfs_to_check telegraph_atom_list += new /obj/effect/xenomorph/xeno_telegraph/lash(turfs_to_check, windup) var/turf/next_turf = get_step(turfs_to_check, facing) - if (!istype(next_turf) || next_turf.density) + if(!istype(next_turf) || next_turf.density) continue target_turfs += next_turf @@ -373,8 +360,7 @@ qdel(tail_telegraph) return - if(!action_cooldown_check() || !check_and_use_plasma_owner()) - return + XENO_ACTION_CHECK_USE_PLASMA(lash_user) apply_cooldown() @@ -382,9 +368,9 @@ lash_user.spin_circle() lash_user.emote("tail") - for (var/turf/targets_in_turf in target_turfs) - for (var/mob/living/carbon/possible_targets in targets_in_turf) - if (possible_targets.stat == DEAD) + for(var/turf/targets_in_turf in target_turfs) + for(var/mob/living/carbon/possible_targets in targets_in_turf) + if(possible_targets.stat == DEAD) continue if(!isxeno_human(possible_targets) || lash_user.can_not_harm(possible_targets)) diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm index a4ad535fad070..3c870c0a5393c 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/ravager/hedgehog.dm @@ -34,12 +34,9 @@ var/shards_per_projectile = 10 var/shards_per_slash = 15 var/armor_buff_per_fifty_shards = 2.50 - var/shard_lock_duration = 150 - var/shard_lock_speed_mod = 0.45 // Shard state var/shards = 0 - var/shards_locked = FALSE //are we locked at 0 shards? // Armor buff state var/times_armor_buffed = 0 @@ -49,46 +46,21 @@ . += "Bone Shards: [shards]/[max_shards]" . += "Shards Armor Bonus: [times_armor_buffed*armor_buff_per_fifty_shards]" -/datum/behavior_delegate/ravager_hedgehog/proc/lock_shards() - - if (!bound_xeno) - return - - to_chat(bound_xeno, SPAN_XENODANGER("You have shed your spikes and cannot gain any more for [shard_lock_duration/10] seconds!")) - - bound_xeno.speed_modifier -= shard_lock_speed_mod - bound_xeno.recalculate_speed() - - shards = 0 - shards_locked = TRUE - addtimer(CALLBACK(src, PROC_REF(unlock_shards)), shard_lock_duration) - -/datum/behavior_delegate/ravager_hedgehog/proc/unlock_shards() - - if (!bound_xeno) - return - - to_chat(bound_xeno, SPAN_XENODANGER("You feel your ability to gather shards return!")) - - bound_xeno.speed_modifier += shard_lock_speed_mod - bound_xeno.recalculate_speed() - shards_locked = FALSE - // Return true if we have enough shards, false otherwise /datum/behavior_delegate/ravager_hedgehog/proc/check_shards(amount) - if (!amount) + if(!amount) return FALSE else return (shards >= amount) /datum/behavior_delegate/ravager_hedgehog/proc/use_shards(amount) - if (!amount) + if(!amount) return shards = max(0, shards - amount) /datum/behavior_delegate/ravager_hedgehog/on_life() - if (!shards_locked) + if(!HAS_TRAIT(bound_xeno, TRAIT_ABILITY_SHED_SPIKES)) shards = min(max_shards, shards + shard_gain_onlife) var/armor_buff_count = shards/50 //0-6 @@ -110,7 +82,6 @@ bound_xeno.small_explosives_stun = TRUE bound_xeno.remove_filter("hedge_unstunnable", 1, list("type" = "outline", "color" = "#421313", "size" = 1)) - return @@ -119,27 +90,74 @@ holder.overlays.Cut() /datum/behavior_delegate/ravager_hedgehog/on_hitby_projectile() - if (!shards_locked) + if(!HAS_TRAIT(bound_xeno, TRAIT_ABILITY_SHED_SPIKES)) shards = min(max_shards, shards + shards_per_projectile) return /datum/behavior_delegate/ravager_hedgehog/melee_attack_additional_effects_self() - if (!shards_locked) + if(!HAS_TRAIT(bound_xeno, TRAIT_ABILITY_SHED_SPIKES)) shards = min(max_shards, shards + shards_per_slash) return +/datum/behavior_delegate/ravager_hedgehog/override_intent(mob/living/carbon/target_carbon) + . = ..() -/datum/action/xeno_action/onclick/spike_shield/use_ability(atom/target) - var/mob/living/carbon/xenomorph/xeno = owner + // If the ravager fails RNG, they perform an accidental slash instead! + if(bound_xeno.a_intent == INTENT_DISARM && prob(25)) // 1/4 chance + if(bound_xeno.claw_restrained()) + bound_xeno.animation_attack_on(target_carbon) + bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] almost slashes [target_carbon]!"), + SPAN_XENONOTICE("We feel the strongest urge to destroy [target_carbon], but the Queen holds us back!")) + return XENO_ATTACK_ACTION - if (!action_cooldown_check()) - return + if(bound_xeno.can_not_harm(target_carbon, check_hive_flags=FALSE)) // We manually check hive_flags later + bound_xeno.animation_attack_on(bound_xeno) + bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), + SPAN_XENONOTICE("We nibble [bound_xeno]")) + return XENO_ATTACK_ACTION - if (!xeno.check_state()) - return + if(bound_xeno.behavior_delegate && bound_xeno.behavior_delegate.handle_slash(bound_xeno)) + return XENO_NO_DELAY_ACTION + + if(target_carbon.stat == DEAD) + to_chat(bound_xeno, SPAN_WARNING("We raise our claws to attack [target_carbon]!- but... they're already dead.")) + return XENO_NO_DELAY_ACTION + + if(bound_xeno.caste && !bound_xeno.caste.is_intelligent) + var/embryo_allied = FALSE + if(target_carbon.status_flags & XENO_HOST) + for(var/obj/item/alien_embryo/embryo in target_carbon) + if(HIVE_ALLIED_TO_HIVE(bound_xeno.hivenumber, embryo.hivenumber)) + embryo_allied = TRUE + break + + if(embryo_allied) + if(HAS_TRAIT(bound_xeno, TRAIT_NESTED)) + bound_xeno.animation_attack_on(target_carbon) + bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), + SPAN_XENONOTICE("ATTACK!!!! Oh- [target_carbon] has a sister inside...")) + return XENO_NO_DELAY_ACTION + if(!HAS_FLAG(bound_xeno.hive.hive_flags, XENO_SLASH_INFECTED)) + bound_xeno.animation_attack_on(target_carbon) + bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), + SPAN_XENONOTICE("ATTACK!!!! Oh- [target_carbon] has a sister inside...")) + return XENO_ATTACK_ACTION + if(!HAS_FLAG(bound_xeno.hive.hive_flags, XENO_SLASH_NORMAL)) + bound_xeno.animation_attack_on(target_carbon) + bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), + SPAN_XENONOTICE("ATTACK!!!! Wait- we're not allowed to attack hosts anymore...")) + return XENO_ATTACK_ACTION + bound_xeno.visible_message(SPAN_DANGER("[bound_xeno] fumbles stupidly for a moment, then slashes [target_carbon]!"), null, null, CHAT_TYPE_XENO_COMBAT) + bound_xeno.visible_message(SPAN_HIGHDANGER("Your oversized claws and small mind get in the way of restraining, slashing [target_carbon]!"), null, null, CHAT_TYPE_XENO_COMBAT) + return INTENT_HARM + +/datum/action/xeno_action/onclick/spike_shield/use_ability(atom/target_atom) + var/mob/living/carbon/xenomorph/xeno = owner + + XENO_ACTION_CHECK(xeno) var/datum/behavior_delegate/ravager_hedgehog/behavior = xeno.behavior_delegate - if (!behavior.check_shards(shard_cost)) + if(!behavior.check_shards(shard_cost)) to_chat(xeno, SPAN_DANGER("Not enough shards! We need [shard_cost - behavior.shards] more!")) return behavior.use_shards(shard_cost) @@ -148,7 +166,7 @@ // Add our shield var/datum/xeno_shield/hedgehog_shield/shield = xeno.add_xeno_shield(shield_amount, XENO_SHIELD_SOURCE_HEDGE_RAV, /datum/xeno_shield/hedgehog_shield) - if (shield) + if(shield) shield.owner = xeno shield.shrapnel_amount = shield_shrapnel_amount xeno.overlay_shields() @@ -162,7 +180,7 @@ return ..() /datum/action/xeno_action/onclick/spike_shield/action_cooldown_check() - if (shield_active) // If active shield, return FALSE so that this action does not get carried out + if(shield_active) // If active shield, return FALSE so that this action does not get carried out return FALSE else if (cooldown_timer_id == TIMER_ID_NULL) var/mob/living/carbon/xenomorph/xeno = owner @@ -173,14 +191,14 @@ /datum/action/xeno_action/onclick/spike_shield/proc/remove_shield() var/mob/living/carbon/xenomorph/xeno = owner - if (!shield_active) + if(!shield_active) return shield_active = FALSE button.icon_state = "template_xeno" - for (var/datum/xeno_shield/shield in xeno.xeno_shields) - if (shield.shield_source == XENO_SHIELD_SOURCE_HEDGE_RAV) + for(var/datum/xeno_shield/shield in xeno.xeno_shields) + if(shield.shield_source == XENO_SHIELD_SOURCE_HEDGE_RAV) shield.on_removal() qdel(shield) break @@ -192,28 +210,27 @@ /datum/action/xeno_action/activable/rav_spikes/use_ability(atom/affected_atom) var/mob/living/carbon/xenomorph/xeno = owner - if (!action_cooldown_check()) + if(!affected_atom || affected_atom.layer >= FLY_LAYER || !isturf(xeno.loc)) return - if(!affected_atom || affected_atom.layer >= FLY_LAYER || !isturf(xeno.loc) || !xeno.check_state()) - return + XENO_ACTION_CHECK(xeno) var/datum/behavior_delegate/ravager_hedgehog/behavior = xeno.behavior_delegate - if (!behavior.check_shards(shard_cost)) + if(!behavior.check_shards(shard_cost)) to_chat(xeno, SPAN_DANGER("Not enough shards! We need [shard_cost - behavior.shards] more!")) return behavior.use_shards(shard_cost) xeno.visible_message(SPAN_XENOWARNING("[xeno] fires their spikes at [affected_atom]!"), SPAN_XENOWARNING("We fire our spikes at [affected_atom]!")) - var/turf/target = locate(affected_atom.x, affected_atom.y, affected_atom.z) + var/turf/target_turf = locate(affected_atom.x, affected_atom.y, affected_atom.z) var/obj/projectile/projectile = new /obj/projectile(xeno.loc, create_cause_data(initial(xeno.caste_type), xeno)) var/datum/ammo/ammo_datum = GLOB.ammo_list[ammo_type] projectile.generate_bullet(ammo_datum) - projectile.fire_at(target, xeno, xeno, ammo_datum.max_range, ammo_datum.shell_speed) + projectile.fire_at(target_turf, xeno, xeno, ammo_datum.max_range, ammo_datum.shell_speed) playsound(xeno, 'sound/effects/spike_spray.ogg', 25, 1) apply_cooldown() @@ -222,7 +239,7 @@ /datum/action/xeno_action/activable/rav_spikes/action_cooldown_check() if(!owner) return FALSE - if (cooldown_timer_id == TIMER_ID_NULL) + if(cooldown_timer_id == TIMER_ID_NULL) var/mob/living/carbon/xenomorph/xeno = owner if(!istype(xeno)) return FALSE @@ -234,18 +251,14 @@ /datum/action/xeno_action/onclick/spike_shed/use_ability(atom/affected_atom) var/mob/living/carbon/xenomorph/xeno = owner - if (!action_cooldown_check()) - return - - if (!xeno.check_state()) - return + XENO_ACTION_CHECK(xeno) var/datum/behavior_delegate/ravager_hedgehog/behavior = xeno.behavior_delegate - if (!behavior.check_shards(shard_cost)) + if(!behavior.check_shards(shard_cost)) to_chat(xeno, SPAN_DANGER("Not enough shards! We need [shard_cost - behavior.shards] more!")) return behavior.use_shards(shard_cost) - behavior.lock_shards() + lock_shards() xeno.visible_message(SPAN_XENOWARNING("[xeno] sheds their spikes, firing them in all directions!"), SPAN_XENOWARNING("We shed our spikes, firing them in all directions!!")) xeno.spin_circle() @@ -256,61 +269,36 @@ return ..() /datum/action/xeno_action/onclick/spike_shed/action_cooldown_check() - if (cooldown_timer_id == TIMER_ID_NULL) + if(cooldown_timer_id == TIMER_ID_NULL) var/mob/living/carbon/xenomorph/xeno = owner var/datum/behavior_delegate/ravager_hedgehog/behavior = xeno.behavior_delegate return behavior.check_shards(shard_cost) else return FALSE -/datum/behavior_delegate/ravager_hedgehog/override_intent(mob/living/carbon/target_carbon) - . = ..() +/datum/action/xeno_action/onclick/spike_shed/proc/lock_shards() + var/mob/living/carbon/xenomorph/xeno = owner + if(!xeno) + return - // If the ravager fails RNG, they perform an accidental slash instead! - if(bound_xeno.a_intent == INTENT_DISARM && prob(25)) // 1/4 chance - if(bound_xeno.claw_restrained()) - bound_xeno.animation_attack_on(target_carbon) - bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] almost slashes [target_carbon]!"), - SPAN_XENONOTICE("We feel the strongest urge to destroy [target_carbon], but the Queen holds us back!")) - return XENO_ATTACK_ACTION + to_chat(xeno, SPAN_XENODANGER("You have shed your spikes and cannot gain any more for [shard_lock_duration/10] seconds!")) - if(bound_xeno.can_not_harm(target_carbon, check_hive_flags=FALSE)) // We manually check hive_flags later - bound_xeno.animation_attack_on(bound_xeno) - bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), - SPAN_XENONOTICE("We nibble [bound_xeno]")) - return XENO_ATTACK_ACTION + xeno.speed_modifier -= shard_lock_speed_mod + xeno.recalculate_speed() - if(bound_xeno.behavior_delegate && bound_xeno.behavior_delegate.handle_slash(bound_xeno)) - return XENO_NO_DELAY_ACTION + var/datum/behavior_delegate/ravager_hedgehog/behavior = xeno.behavior_delegate + behavior.shards = 0 + start_duration_display(shard_lock_duration) + ADD_TRAIT(xeno, TRAIT_ABILITY_SHED_SPIKES, TRAIT_SOURCE_ABILITY("shed_spikes")) + addtimer(CALLBACK(src, PROC_REF(unlock_shards)), shard_lock_duration) - if(target_carbon.stat == DEAD) - to_chat(bound_xeno, SPAN_WARNING("We raise our claws to attack [target_carbon]!- but... they're already dead.")) - return XENO_NO_DELAY_ACTION +/datum/action/xeno_action/onclick/spike_shed/proc/unlock_shards() + var/mob/living/carbon/xenomorph/xeno = owner + if(!xeno) + return - if(bound_xeno.caste && !bound_xeno.caste.is_intelligent) - var/embryo_allied = FALSE - if(target_carbon.status_flags & XENO_HOST) - for(var/obj/item/alien_embryo/embryo in target_carbon) - if(HIVE_ALLIED_TO_HIVE(bound_xeno.hivenumber, embryo.hivenumber)) - embryo_allied = TRUE - break + to_chat(xeno, SPAN_XENODANGER("You feel your ability to gather shards return!")) - if(embryo_allied) - if(HAS_TRAIT(bound_xeno, TRAIT_NESTED)) - bound_xeno.animation_attack_on(target_carbon) - bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), - SPAN_XENONOTICE("ATTACK!!!! Oh- [target_carbon] has a sister inside...")) - return XENO_NO_DELAY_ACTION - if(!HAS_FLAG(bound_xeno.hive.hive_flags, XENO_SLASH_INFECTED)) - bound_xeno.animation_attack_on(target_carbon) - bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), - SPAN_XENONOTICE("ATTACK!!!! Oh- [target_carbon] has a sister inside...")) - return XENO_ATTACK_ACTION - if(!HAS_FLAG(bound_xeno.hive.hive_flags, XENO_SLASH_NORMAL)) - bound_xeno.animation_attack_on(target_carbon) - bound_xeno.visible_message(SPAN_NOTICE("[bound_xeno] nibbles [target_carbon]"), - SPAN_XENONOTICE("ATTACK!!!! Wait- we're not allowed to attack hosts anymore...")) - return XENO_ATTACK_ACTION - bound_xeno.visible_message(SPAN_DANGER("[bound_xeno] fumbles stupidly for a moment, then slashes [target_carbon]!"), null, null, CHAT_TYPE_XENO_COMBAT) - bound_xeno.visible_message(SPAN_HIGHDANGER("Your oversized claws and small mind get in the way of restraining, slashing [target_carbon]!"), null, null, CHAT_TYPE_XENO_COMBAT) - return INTENT_HARM + xeno.speed_modifier += shard_lock_speed_mod + xeno.recalculate_speed() + REMOVE_TRAIT(xeno, TRAIT_ABILITY_SHED_SPIKES, TRAIT_SOURCE_ABILITY("shed_spikes")) diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/runner/acider.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/runner/acider.dm index 448b5f426e2f0..25238812e2cb8 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/runner/acider.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/runner/acider.dm @@ -26,12 +26,7 @@ /datum/behavior_delegate/runner_acider var/acid_amount = 0 - var/caboom_left = 20 - var/caboom_trigger - var/caboom_last_proc - var/max_acid = 1000 - var/caboom_timer = 20 var/acid_slash_regen_lying = 8 var/acid_slash_regen_standing = 14 var/acid_passive_regen = 1 @@ -50,14 +45,6 @@ /// How much acid is required to fill a trap var/fill_acid_cost = 75 - var/list/caboom_sound = list('sound/effects/runner_charging_1.ogg','sound/effects/runner_charging_2.ogg') - var/caboom_loop = 1 - - var/caboom_acid_ratio = 200 - var/caboom_burn_damage_ratio = 5 - var/caboom_burn_range_ratio = 100 - var/caboom_struct_acid_type = /obj/effect/xenomorph/acid - var/drool_overlay_active = FALSE var/mutable_appearance/drool_applied_icon var/mutable_appearance/acid_overlays_icon @@ -81,8 +68,6 @@ if(acid_amount >= acid_gen_cap) . += "Passive acid generation cap ([acid_gen_cap]) reached" . += "Battle acid generation: [combat_gen_active ? "Active" : "Inactive"]" - if(caboom_trigger) - . += "FOR THE HIVE!: in [caboom_left] seconds" /datum/behavior_delegate/runner_acider/melee_attack_additional_effects_target(mob/living/carbon/target_mob) if(ishuman(target_mob)) //Will acid be applied to the mob @@ -112,6 +97,7 @@ drool_overlay_active = TRUE //turns the overlay on /datum/behavior_delegate/runner_acider/on_life() + var/datum/action/xeno_action/activable/acider_for_the_hive/acider_fth = get_action(bound_xeno, /datum/action/xeno_action/activable/acider_for_the_hive) if(acid_amount < acid_gen_cap) modify_acid(acid_passive_regen) if(combat_gen_active) @@ -120,19 +106,19 @@ return if(bound_xeno.stat == DEAD) return - if(caboom_trigger) - var/wt = world.time - if(caboom_last_proc) - caboom_left -= (wt - caboom_last_proc)/10 - caboom_last_proc = wt - var/amplitude = 50 + 50 * (caboom_timer - caboom_left) / caboom_timer - playsound(bound_xeno, caboom_sound[caboom_loop], amplitude, FALSE, 10) - caboom_loop++ - if(caboom_loop > length(caboom_sound)) - caboom_loop = 1 - if(caboom_left <= 0) - caboom_trigger = FALSE - do_caboom() + if(acider_fth.caboom_trigger) + var/world_time = world.time + if(acider_fth.caboom_last_proc) + acider_fth.caboom_left -= (world_time - acider_fth.caboom_last_proc)/10 + acider_fth.caboom_last_proc = world_time + var/amplitude = 50 + 50 * (acider_fth.caboom_timer - acider_fth.caboom_left) / acider_fth.caboom_timer + playsound(bound_xeno, acider_fth.caboom_sound[acider_fth.caboom_loop], amplitude, FALSE, 10) + acider_fth.caboom_loop++ + if(acider_fth.caboom_loop > length(acider_fth.caboom_sound)) + acider_fth.caboom_loop = 1 + if(acider_fth.caboom_left <= 0) + acider_fth.caboom_trigger = FALSE + acider_fth.do_caboom() return var/image/holder = bound_xeno.hud_list[SPECIAL_HUD] @@ -144,70 +130,11 @@ if(acid_amount >= acid_gen_cap) holder.overlays += image('icons/mob/hud/hud.dmi', "cap[percentage_acid_cap]") -/datum/behavior_delegate/runner_acider/handle_death(mob/M) +/datum/behavior_delegate/runner_acider/handle_death(mob/target_mob) var/image/holder = bound_xeno.hud_list[SPECIAL_HUD] holder.overlays.Cut() STOP_PROCESSING(SSfasteffects, src) -/datum/behavior_delegate/runner_acider/proc/do_caboom() - if(!bound_xeno) - return - var/acid_range = acid_amount / caboom_acid_ratio - var/max_burn_damage = acid_amount / caboom_burn_damage_ratio - var/burn_range = acid_amount / caboom_burn_range_ratio - - for(var/barricades in dview(acid_range, bound_xeno)) - if(istype(barricades, /obj/structure/barricade)) - new caboom_struct_acid_type(get_turf(barricades), barricades) - continue - if(istype(barricades, /mob)) - new /datum/effects/acid(barricades, bound_xeno, initial(bound_xeno.caste_type)) - continue - var/x = bound_xeno.x - var/y = bound_xeno.y - FOR_DVIEW(var/mob/living/target_living, burn_range, bound_xeno, HIDE_INVISIBLE_OBSERVER) - if (!isxeno_human(target_living) || bound_xeno.can_not_harm(target_living)) - continue - var/dist = 0 - // such cheap, much fast - var/dx = abs(target_living.x - x) - var/dy = abs(target_living.y - y) - if(dx>=dy) - dist = (0.934*dx) + (0.427*dy) - else - dist = (0.427*dx) + (0.934*dy) - var/damage = floor((burn_range - dist) * max_burn_damage / burn_range) - if(isxeno(target_living)) - damage *= XVX_ACID_DAMAGEMULT - - target_living.apply_damage(damage, BURN) - FOR_DVIEW_END - FOR_DVIEW(var/turf/T, acid_range, bound_xeno, HIDE_INVISIBLE_OBSERVER) - new /obj/effect/particle_effect/smoke/acid_runner_harmless(T) - FOR_DVIEW_END - playsound(bound_xeno, 'sound/effects/blobattack.ogg', 75) - if(bound_xeno.client && bound_xeno.hive) - var/datum/hive_status/hive_status = bound_xeno.hive - var/turf/spawning_turf = get_turf(bound_xeno) - if(!hive_status.hive_location) - addtimer(CALLBACK(bound_xeno.hive, TYPE_PROC_REF(/datum/hive_status, respawn_on_turf), bound_xeno.client, spawning_turf), 0.5 SECONDS) - else - addtimer(CALLBACK(bound_xeno.hive, TYPE_PROC_REF(/datum/hive_status, free_respawn), bound_xeno.client), 5 SECONDS) - bound_xeno.gib(create_cause_data("internal acid rupture", src)) - -/mob/living/carbon/xenomorph/runner/ventcrawl_carry() - var/datum/behavior_delegate/runner_acider/behavior_delegates = behavior_delegate - if(istype(behavior_delegates) && behavior_delegates.caboom_trigger) - to_chat(src, SPAN_XENOWARNING("You cannot ventcrawl when you are about to explode!")) - return FALSE - return ..() - -/mob/living/carbon/xenomorph/runner/get_examine_text(mob/user) - . = ..() - var/datum/behavior_delegate/runner_acider/behavior = behavior_delegate - if(istype(behavior) && isxeno(user)) - . += "it has [SPAN_GREEN(behavior.acid_amount)] acid!" - /datum/behavior_delegate/runner_acider/proc/combat_gen_end() //This proc is triggerd once the combat acid timer runs out. combat_gen_active = FALSE //turns combat acid off drool_overlay_active = FALSE @@ -252,9 +179,9 @@ var/perc_index = 0 if(acid_amount > max_acid * 0.8) perc_index = 3 - else if (acid_amount > max_acid * 0.5) + else if(acid_amount > max_acid * 0.5) perc_index = 2 - else if (acid_amount > max_acid * 0.2) + else if(acid_amount > max_acid * 0.2) perc_index = 1 if(perc_index && bound_runner.stat != DEAD) @@ -276,7 +203,7 @@ to_chat(xeno, SPAN_XENOHIGHDANGER("Can only melt barricades and items!")) return var/datum/behavior_delegate/runner_acider/behavior_delegate = xeno.behavior_delegate - if (!istype(behavior_delegate)) + if(!istype(behavior_delegate)) return if(behavior_delegate.acid_amount < acid_cost) to_chat(xeno, SPAN_XENOHIGHDANGER("Not enough acid stored!")) @@ -288,7 +215,7 @@ return ..() -/datum/action/xeno_action/activable/acider_for_the_hive/use_ability(atom/affected_atom) +/datum/action/xeno_action/activable/acider_for_the_hive/use_ability() var/mob/living/carbon/xenomorph/xeno = owner if(!istype(xeno)) @@ -303,17 +230,13 @@ to_chat(xeno, SPAN_XENOWARNING("We can't activate this here!")) return - if(!xeno.check_state()) - return - - if(!action_cooldown_check()) - return + XENO_ACTION_CHECK(xeno) var/datum/behavior_delegate/runner_acider/behavior_delegate = xeno.behavior_delegate if(!istype(behavior_delegate)) return - if(behavior_delegate.caboom_trigger) + if(caboom_trigger) cancel_ability() return @@ -328,12 +251,12 @@ xeno.set_light_color("#22FF22") xeno.set_light_range(3) - behavior_delegate.caboom_trigger = TRUE - behavior_delegate.caboom_left = behavior_delegate.caboom_timer - behavior_delegate.caboom_last_proc = 0 - xeno.set_effect(behavior_delegate.caboom_timer*2, SUPERSLOW) + caboom_trigger = TRUE + caboom_left = caboom_timer + caboom_last_proc = 0 + xeno.set_effect(caboom_timer*2, SUPERSLOW) - START_PROCESSING(SSfasteffects, src) + start_duration_display(caboom_left_display) xeno.say(";FOR THE HIVE!!!") return ..() @@ -346,35 +269,85 @@ if(!istype(behavior_delegate)) return - behavior_delegate.caboom_trigger = FALSE + caboom_trigger = FALSE xeno.color = null xeno.set_light_range(0) behavior_delegate.modify_acid(-behavior_delegate.max_acid / 4) // Done this way rather than setting to 0 in case something else slowed us // -Original amount set - (time exploding + timer inaccuracy) * how much gets removed per tick / 2 - xeno.adjust_effect(behavior_delegate.caboom_timer * -2 - (behavior_delegate.caboom_timer - behavior_delegate.caboom_left + 2) * xeno.life_slow_reduction * 0.5, SUPERSLOW) + xeno.adjust_effect(caboom_timer * -2 - (caboom_timer - caboom_left + 2) * xeno.life_slow_reduction * 0.5, SUPERSLOW) + end_duration_display() to_chat(xeno, SPAN_XENOWARNING("We remove all our explosive acid before it combusted.")) - STOP_PROCESSING(SSfasteffects, src) - button.set_maptext() +/datum/action/xeno_action/activable/acider_for_the_hive/proc/do_caboom() + var/mob/living/carbon/xenomorph/xeno = owner + if(!istype(xeno)) + return -/datum/action/xeno_action/activable/acider_for_the_hive/process(delta_time) - return update_caboom_maptext() + var/datum/behavior_delegate/runner_acider/behavior_delegate = xeno.behavior_delegate + if(!istype(behavior_delegate)) + return -/datum/action/xeno_action/activable/acider_for_the_hive/proc/update_caboom_maptext() - var/mob/living/carbon/xenomorph/xeno = owner - var/datum/behavior_delegate/runner_acider/delegate = xeno.behavior_delegate - if(!istype(delegate) || !delegate.caboom_trigger || delegate.caboom_left <= 0) - button.set_maptext() - return PROCESS_KILL + var/acid_range = behavior_delegate.acid_amount / caboom_acid_ratio + var/max_burn_damage = behavior_delegate.acid_amount / caboom_burn_damage_ratio + var/burn_range = behavior_delegate.acid_amount / caboom_burn_range_ratio - button.set_maptext(SMALL_FONTS_COLOR(7, delegate.caboom_left, "#e69d00"), 19, 2) - return + for(var/barricades in dview(acid_range, xeno)) + if(istype(barricades, /obj/structure/barricade)) + new caboom_struct_acid_type(get_turf(barricades), barricades) + continue + if(istype(barricades, /mob)) + new /datum/effects/acid(barricades, xeno, initial(xeno.caste_type)) + continue + var/x = xeno.x + var/y = xeno.y + FOR_DVIEW(var/mob/living/target_living, burn_range, xeno, HIDE_INVISIBLE_OBSERVER) + if (!isxeno_human(target_living) || xeno.can_not_harm(target_living)) + continue + var/dist = 0 + // such cheap, much fast + var/dx = abs(target_living.x - x) + var/dy = abs(target_living.y - y) + if(dx>=dy) + dist = (0.934*dx) + (0.427*dy) + else + dist = (0.427*dx) + (0.934*dy) + var/damage = floor((burn_range - dist) * max_burn_damage / burn_range) + if(isxeno(target_living)) + damage *= XVX_ACID_DAMAGEMULT + + target_living.apply_damage(damage, BURN) + FOR_DVIEW_END + FOR_DVIEW(var/turf/target_turf, acid_range, xeno, HIDE_INVISIBLE_OBSERVER) + new /obj/effect/particle_effect/smoke/acid_runner_harmless(target_turf) + FOR_DVIEW_END + playsound(xeno, 'sound/effects/blobattack.ogg', 75) + if(xeno.client && xeno.hive) + var/datum/hive_status/hive_status = xeno.hive + var/turf/spawning_turf = get_turf(xeno) + if(!hive_status.hive_location) + addtimer(CALLBACK(xeno.hive, TYPE_PROC_REF(/datum/hive_status, respawn_on_turf), xeno.client, spawning_turf), 0.5 SECONDS) + else + addtimer(CALLBACK(xeno.hive, TYPE_PROC_REF(/datum/hive_status, free_respawn), xeno.client), 5 SECONDS) + xeno.gib(create_cause_data("internal acid rupture", src)) +/mob/living/carbon/xenomorph/runner/get_examine_text(mob/user) + . = ..() + var/datum/behavior_delegate/runner_acider/behavior = behavior_delegate + if(istype(behavior) && isxeno(user)) + . += "it has [SPAN_GREEN(behavior.acid_amount)] acid!" + +/mob/living/carbon/xenomorph/runner/ventcrawl_carry() + var/datum/action/xeno_action/activable/acider_for_the_hive/acider_fth = get_action(src, /datum/action/xeno_action/activable/acider_for_the_hive) + if(acider_fth.caboom_trigger) + to_chat(src, SPAN_XENOWARNING("You cannot ventcrawl when you are about to explode!")) + return FALSE + return ..() + /mob/living/carbon/xenomorph/runner/corrosive_acid(atom/affected_atom, acid_type, plasma_cost) if(!istype(strain, /datum/xeno_strain/acider)) return ..() diff --git a/code/modules/mob/living/carbon/xenomorph/strains/xeno_strain.dm b/code/modules/mob/living/carbon/xenomorph/strains/xeno_strain.dm index 8b73f42710425..85c8fc1e5ae82 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/xeno_strain.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/xeno_strain.dm @@ -171,7 +171,7 @@ to_chat(src, SPAN_WARNING("We must be at full health to take a strain.")) return FALSE - if(fortify || crest_defense || stealth || HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES) || HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) + if(fortify || crest_defense || HAS_TRAIT(src, TRAIT_ABILITY_INVIS) || HAS_TRAIT(src, TRAIT_ABILITY_ENCLOSED_PLATES) || HAS_TRAIT(src, TRAIT_ABILITY_REFLECTIVE_PLATES)) to_chat(src, SPAN_WARNING("We cannot take a strain while in this stance.")) return FALSE