diff --git a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm index f66c264fde..0027e6700b 100644 --- a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm +++ b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm @@ -42,6 +42,8 @@ #define COMSIG_XENO_STOP_MOMENTUM "xeno_stop_momentum" /// Called whenever xeno should resume charge #define COMSIG_XENO_START_CHARGING "xeno_start_charging" +/// From /datum/action/xeno_action/onclick/charger_charge/proc/stop_momentum() +#define COMSIG_XENO_STOPPED_CHARGING "xeno_stopped_charging" // Used in resin_constructions.dm // Checks whether the xeno can build a thick structure regardless of hive weeds diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm index 935f58f69c..c77dac05fc 100644 --- a/code/__DEFINES/xeno.dm +++ b/code/__DEFINES/xeno.dm @@ -662,7 +662,6 @@ #define FIRE_IMMUNITY_NO_DAMAGE (1<<0) #define FIRE_IMMUNITY_NO_IGNITE (1<<1) #define FIRE_IMMUNITY_XENO_FRENZY (1<<2) -#define FIRE_VULNERABILITY (1<<3) #define FIRE_MULTIPLIER_BASE 1 #define FIRE_MULTIPLIER_LOW 1.25 diff --git a/code/datums/ammo/bullet/shotgun.dm b/code/datums/ammo/bullet/shotgun.dm index 9504e3c1d1..d8a7248436 100644 --- a/code/datums/ammo/bullet/shotgun.dm +++ b/code/datums/ammo/bullet/shotgun.dm @@ -159,9 +159,8 @@ accurate_range = 8 max_range = 8 - damage = 90 - penetration = ARMOR_PENETRATION_TIER_4 - bonus_projectiles_amount = EXTRA_PROJECTILES_TIER_6 + damage = 60 + bonus_projectiles_amount = EXTRA_PROJECTILES_TIER_8 firing_freq_offset = SOUND_FREQ_LOW //buckshot variant only used by the masterkey shotgun attachment. @@ -196,7 +195,6 @@ accurate_range = 8 max_range = 8 damage = 90 - penetration = ARMOR_PENETRATION_TIER_4 firing_freq_offset = SOUND_FREQ_LOW /* diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 2a026e9da7..2f2735dc5f 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -85,6 +85,7 @@ melee_damage_lower = 5 melee_damage_upper = 10 var/melee_vehicle_damage = 10 + var/melee_sentry_damage_multiplier = 1 var/claw_type = CLAW_TYPE_SHARP var/burn_damage_lower = 0 var/burn_damage_upper = 0 diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm index 6405428de7..c278b8bbf1 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_abilities.dm @@ -19,6 +19,9 @@ should_destroy_objects = TRUE throw_speed = SPEED_FAST tracks_target = FALSE + + default_ai_action = FALSE + var/direct_hit_damage = 60 var/frontal_armor = 15 // Object types that dont reduce cooldown when hit @@ -44,21 +47,16 @@ var/damage = 65 - var/distance = 2 - var/effect_type_base = /datum/effects/xeno_slow/superslow - var/effect_duration = 10 + var/distance = 4 + var/windup_duration = 1.5 SECONDS -/datum/action/xeno_action/onclick/crusher_stomp/charger - name = "Crush" - action_icon_state = "stomp" - macro_path = /datum/action/xeno_action/verb/verb_crusher_charger_stomp - action_type = XENO_ACTION_CLICK - ability_primacy = XENO_PRIMARY_ACTION_3 - plasma_cost = 25 - damage = 75 - distance = 3 - xeno_cooldown = 12 SECONDS + default_ai_action = TRUE +/datum/action/xeno_action/onclick/crusher_stomp/process_ai(mob/living/carbon/xenomorph/X, delta_time) + if(!DT_PROB(ai_prob_chance, delta_time) || get_dist(X, X.current_target) >= distance - 1 || HAS_TRAIT(X, TRAIT_CHARGING) || X.action_busy) + return + + use_ability_async() /datum/action/xeno_action/onclick/crusher_shield name = "Defensive Shield" @@ -86,6 +84,14 @@ weaken_power = 0 slowdown = 8 + default_ai_action = TRUE + ai_prob_chance = 60 + +/datum/action/xeno_action/activable/fling/charger/process_ai(mob/living/carbon/xenomorph/X, delta_time) + if(!DT_PROB(ai_prob_chance, delta_time) || get_dist(X, X.current_target) > 1 || HAS_TRAIT(X, TRAIT_CHARGING) || X.action_busy) + return + + use_ability_async(X.current_target) /datum/action/xeno_action/onclick/charger_charge name = "Toggle Charging" @@ -95,11 +101,14 @@ action_type = XENO_ACTION_CLICK ability_primacy = XENO_PRIMARY_ACTION_1 + default_ai_action = TRUE + ai_prob_chance = 40 + // Config vars var/max_momentum = 8 - var/steps_to_charge = 4 - var/speed_per_momentum = XENO_SPEED_FASTMOD_TIER_5 + XENO_SPEED_FASTMOD_TIER_1//2 - var/plasma_per_step = 3 // charger has 400 plasma atm, this gives a good 100 tiles of crooshing + var/steps_to_charge = 3 + var/speed_per_momentum = XENO_SPEED_FASTMOD_TIER_5//2.2 + var/plasma_per_step = 0 // State vars var/activated = FALSE @@ -119,29 +128,120 @@ /// Dictates speed and damage dealt via collision, increased with movement var/momentum = 0 +#define MIN_TARGETS_TO_CHARGE 2 +#define FLOCK_SCAN_RADIUS 3 +#define MINIMUM_CHARGE_DISTANCE 3 +#define MAXIMUM_TARGET_DISTANCE 12 + +/datum/action/xeno_action/onclick/charger_charge/process_ai(mob/living/carbon/xenomorph/processing_xeno, delta_time) + if(!DT_PROB(ai_prob_chance, delta_time) || !isnull(charge_dir) || processing_xeno.action_busy) + return + + var/turf/xeno_turf = get_turf(processing_xeno) + + if(!xeno_turf) + return + + var/list/possible_charge_dirs = list() + + for(var/mob/living/carbon/human/base_checked_human as anything in GLOB.alive_human_list) + var/distance_between_base_human_and_xeno = get_dist(processing_xeno, base_checked_human) + + if(distance_between_base_human_and_xeno > MAXIMUM_TARGET_DISTANCE) + continue + + if(distance_between_base_human_and_xeno < MINIMUM_CHARGE_DISTANCE) + continue + + if(!processing_xeno.check_mob_target(base_checked_human)) + continue + + var/secondary_count = 0 + var/secondary_x_sum = 0 + var/secondary_y_sum = 0 + + for(var/mob/living/carbon/human/secondary_checked_human in range(FLOCK_SCAN_RADIUS, base_checked_human)) + if(!processing_xeno.check_mob_target(secondary_checked_human)) + continue + + secondary_count++ + secondary_x_sum += secondary_checked_human.x + secondary_y_sum += secondary_checked_human.y + if(secondary_count < MIN_TARGETS_TO_CHARGE) + continue -/datum/action/xeno_action/onclick/charger_charge/proc/handle_movement(mob/living/carbon/xenomorph/Xeno, atom/oldloc, dir, forced) + var/x_middle = round(secondary_x_sum / secondary_count) + var/y_middle = round(secondary_y_sum / secondary_count) + + if((abs(x_middle - processing_xeno.x) > 1) && (abs(y_middle - processing_xeno.y) > 1)) + continue + + var/turf/potential_charge_turf = locate(x_middle, y_middle, processing_xeno.z) + + var/distance_between_potential_charge_turf_and_xeno = get_dist(potential_charge_turf, processing_xeno) + + if(distance_between_potential_charge_turf_and_xeno < MINIMUM_CHARGE_DISTANCE) + continue + + var/cardinal_dir_to_potential_charge_turf = get_cardinal_dir(processing_xeno, potential_charge_turf) + + var/list/turf/turfs_to_check = getline2(xeno_turf, get_angle_target_turf(xeno_turf, cardinal_dir_to_potential_charge_turf, MINIMUM_CHARGE_DISTANCE), FALSE) + + var/blocked = FALSE + var/turf/previous_turf = xeno_turf + + for(var/turf/checked_turf in turfs_to_check) + var/list/ignore = list() + + for(var/mob/mob_blocker in checked_turf) + ignore += mob_blocker + + if(LinkBlocked(processing_xeno, previous_turf, checked_turf, ignore)) + blocked = TRUE + break + + previous_turf = checked_turf + + if(blocked) + continue + + possible_charge_dirs += cardinal_dir_to_potential_charge_turf + + if(!length(possible_charge_dirs)) + return + + charge_dir = pick(possible_charge_dirs) + + last_charge_move = world.time + use_ability_async() + +#undef MIN_TARGETS_TO_CHARGE +#undef FLOCK_SCAN_RADIUS +#undef MINIMUM_CHARGE_DISTANCE +#undef MAXIMUM_TARGET_DISTANCE + +/datum/action/xeno_action/onclick/charger_charge/proc/handle_movement(mob/living/carbon/xenomorph/xeno, atom/oldloc, dir, forced) SIGNAL_HANDLER - if(Xeno.pulling) + if(xeno.pulling) if(!momentum) steps_taken = 0 return else - Xeno.stop_pulling() + xeno.stop_pulling() - if(Xeno.is_mob_incapacitated()) + if(xeno.is_mob_incapacitated()) if(!momentum) return - var/lol = get_ranged_target_turf(Xeno, charge_dir, momentum/2) - INVOKE_ASYNC(Xeno, TYPE_PROC_REF(/atom/movable, throw_atom), lol, momentum/2, SPEED_FAST, null, TRUE) + var/lol = get_ranged_target_turf(xeno, charge_dir, momentum/2) + INVOKE_ASYNC(xeno, TYPE_PROC_REF(/atom/movable, throw_atom), lol, momentum/2, SPEED_FAST, null, TRUE) stop_momentum() return - if(!isturf(Xeno.loc)) + if(!isturf(xeno.loc)) stop_momentum() return // Don't build up charge if you move via getting propelled by something - if(Xeno.throwing) + if(xeno.throwing) stop_momentum() return @@ -156,7 +256,7 @@ if(do_stop_momentum) stop_momentum() - if(Xeno.plasma_stored <= plasma_per_step) + if(xeno.plasma_stored <= plasma_per_step) stop_momentum() return last_charge_move = world.time @@ -165,34 +265,49 @@ return if(momentum < max_momentum) momentum++ - ADD_TRAIT(Xeno, TRAIT_CHARGING, TRAIT_SOURCE_XENO_ACTION_CHARGE) - Xeno.update_icons() + ADD_TRAIT(xeno, TRAIT_CHARGING, TRAIT_SOURCE_XENO_ACTION_CHARGE) + xeno.update_icons() if(momentum == max_momentum) - Xeno.emote("roar") + xeno.emote("roar") //X.use_plasma(plasma_per_step) // take if you are in toggle charge mode if(momentum > 0) - Xeno.use_plasma(plasma_per_step) // take plasma when you have momentum + xeno.use_plasma(plasma_per_step) // take plasma when you have momentum noise_timer = noise_timer ? --noise_timer : 3 if(noise_timer == 3) - playsound(Xeno, 'sound/effects/alien_footstep_charge1.ogg', 50) + playsound(xeno, 'sound/effects/alien_footstep_charge1.ogg', 50) + + for(var/mob/living/carbon/human/Mob in range(10, xeno)) + shake_camera(Mob, 2, 1) - for(var/mob/living/carbon/human/Mob in Xeno.loc) + for(var/mob/living/carbon/human/Mob in xeno.loc) if(Mob.lying && Mob.stat != DEAD) - Xeno.visible_message(SPAN_DANGER("[Xeno] runs [Mob] over!"), + xeno.visible_message(SPAN_DANGER("[xeno] runs [Mob] over!"), SPAN_DANGER("You run [Mob] over!") ) - var/ram_dir = pick(get_perpen_dir(Xeno.dir)) + var/ram_dir = pick(get_perpen_dir(xeno.dir)) var/dist = 1 if(momentum == max_momentum) dist = momentum * 0.25 step(Mob, ram_dir, dist) - Mob.take_overall_armored_damage(momentum * 6) + Mob.take_overall_armored_damage(momentum * 8) INVOKE_ASYNC(Mob, TYPE_PROC_REF(/mob/living/carbon/human, emote),"pain") - shake_camera(Mob, 7,3) + shake_camera(Mob, 7, 3) animation_flash_color(Mob) - Xeno.recalculate_speed() + if(momentum >= 5) + for(var/mob/living/carbon/human/hit_human in orange(1, xeno)) + if(hit_human.knocked_down) + continue + + shake_camera(hit_human, 4, 2) + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(xeno_throw_human), hit_human, xeno, get_dir(xeno, hit_human), 1, FALSE) + to_chat(hit_human, SPAN_XENOHIGHDANGER("You fall backwards as [xeno] gives you a glancing blow!")) + hit_human.take_overall_armored_damage(momentum * 4) + hit_human.apply_effect(0.5, WEAKEN) + animation_flash_color(hit_human) + + xeno.recalculate_speed() /datum/action/xeno_action/onclick/charger_charge/proc/handle_dir_change(datum/source, old_dir, new_dir) SIGNAL_HANDLER @@ -217,6 +332,8 @@ Xeno.visible_message(SPAN_DANGER("[Xeno] skids to a halt!")) REMOVE_TRAIT(Xeno, TRAIT_CHARGING, TRAIT_SOURCE_XENO_ACTION_CHARGE) + SEND_SIGNAL(Xeno, COMSIG_XENO_STOPPED_CHARGING) + charge_dir = null steps_taken = 0 momentum = 0 Xeno.recalculate_speed() diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm index ce4a631e28..bfa826711b 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm @@ -96,92 +96,63 @@ ..(T) /datum/action/xeno_action/onclick/crusher_stomp/use_ability(atom/A) - var/mob/living/carbon/xenomorph/X = owner - if (!istype(X)) + var/mob/living/carbon/xenomorph/xeno_owner = owner + if (!istype(xeno_owner)) return if (!action_cooldown_check()) return - if (!X.check_state()) + if (!xeno_owner.check_state()) return if (!check_and_use_plasma_owner()) return - playsound(get_turf(X), 'sound/effects/bang.ogg', 25, 0) - X.visible_message(SPAN_XENODANGER("[X] smashes into the ground!"), SPAN_XENODANGER("You smash into the ground!")) - X.create_stomp() + apply_cooldown() - for (var/mob/living/carbon/H in get_turf(X)) - if (H.stat == DEAD || X.can_not_harm(H)) - continue + xeno_owner.frozen = TRUE + xeno_owner.anchored = TRUE + xeno_owner.update_canmove() - new effect_type_base(H, X, , , get_xeno_stun_duration(H, effect_duration)) - to_chat(H, SPAN_XENOHIGHDANGER("You are slowed as [X] knocks you off balance!")) + if (!do_after(xeno_owner, windup_duration, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE)) + xeno_owner.frozen = FALSE + xeno_owner.anchored = FALSE + xeno_owner.update_canmove() + return - if(H.mob_size < MOB_SIZE_BIG) - H.apply_effect(get_xeno_stun_duration(H, 0.2), WEAKEN) + xeno_owner.frozen = FALSE + xeno_owner.anchored = FALSE + xeno_owner.update_canmove() - H.apply_armoured_damage(get_xeno_damage_slash(H, damage), ARMOR_MELEE, BRUTE) - H.last_damage_data = create_cause_data(X.caste_type, X) + playsound(get_turf(xeno_owner), 'sound/effects/bang.ogg', 25, 0) + xeno_owner.visible_message(SPAN_XENODANGER("[xeno_owner] smashes into the ground!"), SPAN_XENODANGER("You smash into the ground!")) + xeno_owner.create_stomp() - for (var/mob/living/carbon/H in orange(distance, get_turf(X))) - if (H.stat == DEAD || X.can_not_harm(H)) + for(var/mob/living/carbon/carbon_in_range in range(distance, get_turf(xeno_owner))) + if(carbon_in_range.stat == DEAD || xeno_owner.can_not_harm(carbon_in_range)) continue - new effect_type_base(H, X, , , get_xeno_stun_duration(H, effect_duration)) - if(H.mob_size < MOB_SIZE_BIG) - H.apply_effect(get_xeno_stun_duration(H, 0.2), WEAKEN) - to_chat(H, SPAN_XENOHIGHDANGER("You are slowed as [X] knocks you off balance!")) - - apply_cooldown() - return ..() - -/datum/action/xeno_action/onclick/crusher_stomp/charger/use_ability() - var/mob/living/carbon/xenomorph/Xeno = owner - var/mob/living/carbon/Targeted - if (!istype(Xeno)) - return - - if (!action_cooldown_check()) - return + var/distance_to_target = get_dist(carbon_in_range, xeno_owner) - if (!Xeno.check_state()) - return + to_chat(carbon_in_range, SPAN_XENOHIGHDANGER("You fall as [xeno_owner] knocks you off balance!")) + shake_camera(carbon_in_range, (6 - distance_to_target), 2) - if (!check_and_use_plasma_owner()) - return + var/weaken_time = 2.5 - (distance_to_target / 2) - playsound(get_turf(Xeno), 'sound/effects/bang.ogg', 25, 0) - Xeno.visible_message(SPAN_XENODANGER("[Xeno] smashes into the ground!"), SPAN_XENODANGER("You smash into the ground!")) - Xeno.create_stomp() + carbon_in_range.apply_effect(weaken_time, WEAKEN) - for (var/mob/living/carbon/Human in get_turf(Xeno)) // MOBS ONTOP - if (Human.stat == DEAD || Xeno.can_not_harm(Human)) + if(distance_to_target == 0) + carbon_in_range.apply_armoured_damage(damage, ARMOR_MELEE, BRUTE) + carbon_in_range.last_damage_data = create_cause_data(xeno_owner.caste_type, xeno_owner) + to_chat(carbon_in_range, SPAN_XENOHIGHDANGER("You are crushed as [xeno_owner] stomps on you!")) continue - new effect_type_base(Human, Xeno, , , get_xeno_stun_duration(Human, effect_duration)) - to_chat(Human, SPAN_XENOHIGHDANGER("You are BRUTALLY crushed and stomped on by [Xeno]!!!")) - shake_camera(Human, 10, 2) - if(Human.mob_size < MOB_SIZE_BIG) - Human.apply_effect(get_xeno_stun_duration(Human, 0.2), WEAKEN) - - Human.apply_armoured_damage(get_xeno_damage_slash(Human, damage), ARMOR_MELEE, BRUTE,"chest", 3) - Human.apply_armoured_damage(15, BRUTE) // random - Human.last_damage_data = create_cause_data(Xeno.caste_type, Xeno) - Human.emote("pain") - Targeted = Human - for (var/mob/living/carbon/Human in orange(distance, get_turf(Xeno))) // MOBS AROUND - if (Human.stat == DEAD || Xeno.can_not_harm(Human)) + if(distance_to_target <= 2) + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(xeno_throw_human), carbon_in_range, xeno_owner, get_dir(xeno_owner, carbon_in_range), (6 - (distance_to_target * 2)), FALSE) + to_chat(carbon_in_range, SPAN_XENOHIGHDANGER("You are flung by [xeno_owner] from the force of its crashing weight!")) continue - if(Human.client) - shake_camera(Human, 10, 2) - if(Targeted) - to_chat(Human, SPAN_XENOHIGHDANGER("You watch as [Targeted] gets crushed by [Xeno]!")) - to_chat(Human, SPAN_XENOHIGHDANGER("You are shaken as [Xeno] quakes the earth!")) - apply_cooldown() return ..() /datum/action/xeno_action/onclick/crusher_shield/use_ability(atom/Target) @@ -275,6 +246,14 @@ button.icon_state = "template" return ..() +/datum/action/xeno_action/activable/fling/charger/check_and_use_plasma_owner(plasma_to_use) + if (!check_plasma_owner(plasma_to_use)) + return FALSE + + flick("Normal Crusher Charging", owner) + use_plasma_owner(plasma_to_use) + return TRUE + /datum/action/xeno_action/activable/tumble/use_ability(atom/Target) if(!action_cooldown_check()) return 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 781d958771..5f59f1c006 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm @@ -112,7 +112,7 @@ else button.color = rgb(255,255,255,255) -/datum/action/xeno_action/proc/process_ai(mob/living/carbon/xenomorph/X, delta_time) +/datum/action/xeno_action/proc/process_ai(mob/living/carbon/xenomorph/processing_xeno, delta_time) SHOULD_NOT_SLEEP(TRUE) return PROCESS_KILL diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/crusher.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/crusher.dm new file mode 100644 index 0000000000..efa541ed74 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/crusher.dm @@ -0,0 +1,51 @@ +/datum/xeno_ai_movement/crusher + +/datum/xeno_ai_movement/crusher/New(mob/living/carbon/xenomorph/parent) + . = ..() + + RegisterSignal(parent, COMSIG_XENO_STOPPED_CHARGING, PROC_REF(stop_charging)) + +#define MAX_CHARGE_DISTANCE 30 + +/datum/xeno_ai_movement/crusher/ai_move_target(delta_time) + var/mob/living/carbon/xenomorph/moving_xeno = parent + + if(moving_xeno.throwing) + return ..() + + var/datum/action/xeno_action/onclick/charger_charge/charge_action = locate() in moving_xeno.actions + + if(!charge_action) + return ..() + + if(!charge_action.charge_dir) + return ..() + + if(charge_action.steps_taken > MAX_CHARGE_DISTANCE) + stop_charging() + return ..() + + if(!moving_xeno.can_move_and_apply_move_delay()) + return TRUE + + var/turf/next_turf = get_step(moving_xeno, charge_action.charge_dir) + if(!moving_xeno.Move(next_turf, charge_action.charge_dir)) + stop_charging() + + return TRUE + +#undef MAX_CHARGE_DISTANCE + +/datum/xeno_ai_movement/crusher/proc/stop_charging() + SIGNAL_HANDLER + + var/datum/action/xeno_action/onclick/charger_charge/charge_action = locate() in parent.actions + + if(!charge_action) + return + + if(!charge_action.activated) + return + + INVOKE_ASYNC(charge_action, TYPE_PROC_REF(/datum/action/xeno_action/onclick/charger_charge, use_ability_wrapper)) + COOLDOWN_RESET(parent, forced_retarget_cooldown) diff --git a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm index 9e7cad9c65..3de86d6482 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm @@ -19,6 +19,12 @@ var/datum/xeno_ai_movement/ai_movement_handler + /// The time interval before this xeno should forcefully get a new target + var/forced_retarget_time = (10 SECONDS) + + /// The actual cooldown declaration for forceful retargeting, reference forced_retarget_time for time in between checks + COOLDOWN_DECLARE(forced_retarget_cooldown) + /mob/living/carbon/xenomorph/Destroy() QDEL_NULL(ai_movement_handler) return ..() @@ -68,8 +74,9 @@ var/mob/current_target_mob = current_target stat_check = (current_target_mob.stat != CONSCIOUS) - if(QDELETED(current_target) || stat_check || get_dist(current_target, src) > ai_range) + if(QDELETED(current_target) || stat_check || get_dist(current_target, src) > ai_range || COOLDOWN_FINISHED(src, forced_retarget_cooldown)) current_target = get_target(ai_range) + COOLDOWN_START(src, forced_retarget_cooldown, forced_retarget_time) if(QDELETED(src)) return TRUE @@ -77,7 +84,6 @@ resting = FALSE if(prob(5)) emote("hiss") - return TRUE a_intent = INTENT_HARM diff --git a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm index c331324770..aad8d40f3b 100644 --- a/code/modules/mob/living/carbon/xenomorph/attack_alien.dm +++ b/code/modules/mob/living/carbon/xenomorph/attack_alien.dm @@ -659,7 +659,7 @@ M.animation_attack_on(src) playsound(src, 'sound/effects/metalhit.ogg', 25, 1) - update_health(rand(M.melee_damage_lower, M.melee_damage_upper)) + update_health(rand(M.melee_damage_lower, M.melee_damage_upper) * M.melee_sentry_damage_multiplier) if(health <= 0) M.visible_message(SPAN_DANGER("[M] slices \the [src] apart!"), \ SPAN_DANGER("You slice \the [src] apart!"), null, 5, CHAT_TYPE_XENO_COMBAT) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm index 092dae00d6..2651137a80 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Boiler.dm @@ -22,7 +22,6 @@ acid_level = 3 caste_luminosity = 2 spit_types = list(/datum/ammo/xeno/boiler_gas, /datum/ammo/xeno/boiler_gas/acid) - fire_immunity = FIRE_VULNERABILITY // 3x fire damage fire_vulnerability_mult = FIRE_MULTIPLIER_DEADLY diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm index bf17025982..7eca90af85 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm @@ -4,17 +4,17 @@ melee_damage_lower = XENO_DAMAGE_TIER_5 melee_damage_upper = XENO_DAMAGE_TIER_5 - melee_vehicle_damage = XENO_DAMAGE_TIER_5 - max_health = XENO_HEALTH_TIER_10 + melee_vehicle_damage = XENO_DAMAGE_TIER_5 * 8 + max_health = XENO_HEALTH_IMMORTAL plasma_gain = XENO_PLASMA_GAIN_TIER_7 - plasma_max = XENO_PLASMA_TIER_4 + plasma_max = XENO_PLASMA_TIER_8 xeno_explosion_resistance = XENO_EXPLOSIVE_ARMOR_TIER_10 armor_deflection = XENO_ARMOR_TIER_3 evasion = XENO_EVASION_NONE - speed = XENO_SPEED_TIER_2 + speed = XENO_SPEED_TIER_4 heal_standing = 0.66 - behavior_delegate_type = /datum/behavior_delegate/crusher_base + behavior_delegate_type = /datum/behavior_delegate/crusher_charger minimum_evolve_time = 15 MINUTES @@ -25,6 +25,8 @@ evolution_allowed = FALSE deevolves_to = list(XENO_CASTE_WARRIOR) caste_desc = "A huge tanky xenomorph." + fire_intensity_resistance = 20 + fire_vulnerability_mult = 0.25 minimap_icon = "crusher" @@ -37,6 +39,7 @@ plasma_types = list(PLASMA_CHITIN) tier = 3 drag_delay = 6 //pulling a big dead xeno is hard + melee_sentry_damage_multiplier = 2 small_explosives_stun = FALSE @@ -56,7 +59,8 @@ /datum/action/xeno_action/onclick/regurgitate, /datum/action/xeno_action/watch_xeno, /datum/action/xeno_action/activable/tail_stab, - /datum/action/xeno_action/activable/pounce/crusher_charge, + /datum/action/xeno_action/activable/fling/charger, + /datum/action/xeno_action/onclick/charger_charge, /datum/action/xeno_action/onclick/crusher_stomp, /datum/action/xeno_action/onclick/crusher_shield, /datum/action/xeno_action/onclick/tacmap, @@ -64,11 +68,26 @@ claw_type = CLAW_TYPE_VERY_SHARP mutation_icon_state = CRUSHER_NORMAL - mutation_type = CRUSHER_NORMAL + mutation_type = CRUSHER_CHARGER icon_xeno = 'icons/mob/xenos/crusher.dmi' icon_xenonid = 'icons/mob/xenonids/crusher.dmi' + ai_range = 24 + forced_retarget_time = (3 SECONDS) + +/mob/living/carbon/xenomorph/crusher/init_movement_handler() + return new /datum/xeno_ai_movement/crusher(src) + +/mob/living/carbon/xenomorph/crusher/Initialize(mapload, mob/living/carbon/xenomorph/oldXeno, h_number, ai_hard_off = FALSE) + . = ..() + + playsound(src, 'sound/voice/alien_crusher_spawn.ogg', 100, FALSE, 30) + for(var/mob/current_mob as anything in get_mobs_in_z_level_range(get_turf(src), 30) - src) + var/relative_dir = get_dir(current_mob, src) + var/final_dir = dir2text(relative_dir) + to_chat(current_mob, SPAN_HIGHDANGER("You hear a terrible roar coming from [final_dir ? "the [final_dir]" : "nearby"] as the ground shakes!")) + // Refactored to handle all of crusher's interactions with object during charge. /mob/living/carbon/xenomorph/proc/handle_collision(atom/target) if(!target) @@ -205,7 +224,7 @@ if (!.) update_icons() -// Mutator delegate for base ravager +// Mutator delegate for base crusher /datum/behavior_delegate/crusher_base name = "Base Crusher Behavior Delegate" @@ -276,6 +295,6 @@ . += "Shield: [shield_total]" /datum/behavior_delegate/crusher_base/on_update_icons() - if(bound_xeno.throwing || is_charging) //Let it build up a bit so we're not changing icons every single turf + if(HAS_TRAIT(bound_xeno, TRAIT_CHARGING) && !bound_xeno.lying) bound_xeno.icon_state = "[bound_xeno.mutation_icon_state || bound_xeno.mutation_type] Crusher Charging" return TRUE diff --git a/code/modules/mob/living/carbon/xenomorph/life.dm b/code/modules/mob/living/carbon/xenomorph/life.dm index da66c61bfc..6ea3b699dc 100644 --- a/code/modules/mob/living/carbon/xenomorph/life.dm +++ b/code/modules/mob/living/carbon/xenomorph/life.dm @@ -83,10 +83,7 @@ G.die() drop_inv_item_on_ground(G) if(!caste || !(caste.fire_immunity & FIRE_IMMUNITY_NO_DAMAGE) || fire_reagent.fire_penetrating) - if(caste.fire_immunity & FIRE_VULNERABILITY && caste.fire_vulnerability_mult >= 1) - apply_damage(PASSIVE_BURN_DAM_CALC(fire_reagent.intensityfire, fire_reagent.durationfire, fire_stacks) * caste.fire_vulnerability_mult, BURN) - else - apply_damage(armor_damage_reduction(GLOB.xeno_fire, PASSIVE_BURN_DAM_CALC(fire_reagent.intensityfire, fire_reagent.durationfire, fire_stacks)), BURN) + apply_damage(armor_damage_reduction(GLOB.xeno_fire, PASSIVE_BURN_DAM_CALC(fire_reagent.intensityfire, fire_reagent.durationfire, fire_stacks) * caste.fire_vulnerability_mult), BURN) INVOKE_ASYNC(src, TYPE_PROC_REF(/mob, emote), pick("roar", "needhelp")) #undef PASSIVE_BURN_DAM_CALC diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/crusher/charger.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/crusher/charger.dm index f7daee71df..fc7be12e4e 100644 --- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/crusher/charger.dm +++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/crusher/charger.dm @@ -23,13 +23,11 @@ caste_whitelist = list(XENO_CASTE_CRUSHER) mutator_actions_to_remove = list ( /datum/action/xeno_action/activable/pounce/crusher_charge, - /datum/action/xeno_action/onclick/crusher_stomp, /datum/action/xeno_action/onclick/crusher_shield, ) mutator_actions_to_add = list( /datum/action/xeno_action/onclick/charger_charge, /datum/action/xeno_action/activable/tumble, - /datum/action/xeno_action/onclick/crusher_stomp/charger, /datum/action/xeno_action/activable/fling/charger, ) keystone = TRUE @@ -59,7 +57,9 @@ name = "Charger Crusher Behavior Delegate" var/frontal_armor = 30 - var/side_armor = 15 + var/side_armor = 20 + + var/aoe_slash_damage_reduction = 0.40 /datum/behavior_delegate/crusher_charger/add_to_xeno() RegisterSignal(bound_xeno, COMSIG_MOB_SET_FACE_DIR, PROC_REF(cancel_dir_lock)) @@ -69,6 +69,38 @@ SIGNAL_HANDLER return COMPONENT_CANCEL_SET_FACE_DIR +/datum/behavior_delegate/crusher_charger/melee_attack_additional_effects_target(mob/living/carbon/A) + + if (!isxeno_human(A)) + return + + new /datum/effects/xeno_slow(A, bound_xeno, , , 20) + + var/damage = bound_xeno.melee_damage_upper * aoe_slash_damage_reduction + + var/base_cdr_amount = 15 + var/cdr_amount = base_cdr_amount + for (var/mob/living/carbon/H in orange(1, A)) + if (H.stat == DEAD) + continue + + if(!isxeno_human(H) || bound_xeno.can_not_harm(H)) + continue + + cdr_amount += 5 + + bound_xeno.visible_message(SPAN_DANGER("[bound_xeno] slashes [H]!"), \ + SPAN_DANGER("You slash [H]!"), null, null, CHAT_TYPE_XENO_COMBAT) + + bound_xeno.flick_attack_overlay(H, "slash") + + H.last_damage_data = create_cause_data(initial(bound_xeno.name), bound_xeno) + H.apply_armoured_damage(get_xeno_damage_slash(H, damage), ARMOR_MELEE, BRUTE, bound_xeno.zone_selected) + + var/datum/action/xeno_action/onclick/crusher_stomp/cAction = get_xeno_action_by_type(bound_xeno, /datum/action/xeno_action/onclick/crusher_stomp) + if (!cAction.action_cooldown_check()) + cAction.reduce_cooldown(cdr_amount) + /datum/behavior_delegate/crusher_charger/proc/apply_directional_armor(mob/living/carbon/xenomorph/xeno, list/damagedata) SIGNAL_HANDLER var/projectile_direction = damagedata["direction"] @@ -102,7 +134,7 @@ charger_ability.stop_momentum() return - health -= CHARGER_DESTROY //Usually knocks it down. + health -= CHARGER_DESTROY * 2 //Usually knocks it down. healthcheck() if(QDELETED(src)) @@ -340,9 +372,9 @@ attack_log += text("\[[time_stamp()]\] was xeno charged by [xeno] ([xeno.ckey])") xeno.attack_log += text("\[[time_stamp()]\] xeno charged [src] ([src.ckey])") log_attack("[xeno] ([xeno.ckey]) xeno charged [src] ([src.ckey])") - var/momentum_mult = 5 + var/momentum_mult = 7 if(charger_ability.momentum == charger_ability.max_momentum) - momentum_mult = 8 + momentum_mult = 10 take_overall_armored_damage(charger_ability.momentum * momentum_mult, ARMOR_MELEE, BRUTE, 60, 13) // Giving AP because this spreads damage out and then applies armor to them apply_armoured_damage(charger_ability.momentum * momentum_mult/4, ARMOR_MELEE, BRUTE,"chest") xeno.visible_message( @@ -363,7 +395,7 @@ if(LinkBlocked(src, cur_turf, target_turf)) ram_dir = REVERSE_DIR(ram_dir) step(src, ram_dir, charger_ability.momentum * 0.5) - charger_ability.lose_momentum(CCA_MOMENTUM_LOSS_MIN) +// charger_ability.lose_momentum(CCA_MOMENTUM_LOSS_MIN) return XENO_CHARGE_TRY_MOVE // Fellow xenos @@ -401,7 +433,7 @@ apply_effect(1, WEAKEN) // brief flicker stun src.throw_atom(src.loc,1,3,xeno,TRUE) step(src, ram_dir, charger_ability.momentum * 0.5) - charger_ability.lose_momentum(CCA_MOMENTUM_LOSS_MIN) +// charger_ability.lose_momentum(CCA_MOMENTUM_LOSS_MIN) return XENO_CHARGE_TRY_MOVE charger_ability.stop_momentum() diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index eabe4a7853..d97fbaa623 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -101,7 +101,7 @@ var/caste_luminosity = 0 /// if fire_immunity is set to be vulnerable, how much will fire damage be multiplied. Defines in xeno.dm - var/fire_vulnerability_mult = 0 + var/fire_vulnerability_mult = 1 var/burrow_cooldown = 5 SECONDS var/tunnel_cooldown = 100 diff --git a/colonialmarines.dme b/colonialmarines.dme index e572416caf..629bdb46c6 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -2006,6 +2006,7 @@ #include "code\modules\mob\living\carbon\xenomorph\abilities\warrior\warrior_powers.dm" #include "code\modules\mob\living\carbon\xenomorph\ai\xeno_ai.dm" #include "code\modules\mob\living\carbon\xenomorph\ai\movement\base_define.dm" +#include "code\modules\mob\living\carbon\xenomorph\ai\movement\crusher.dm" #include "code\modules\mob\living\carbon\xenomorph\ai\movement\drone.dm" #include "code\modules\mob\living\carbon\xenomorph\ai\movement\linger.dm" #include "code\modules\mob\living\carbon\xenomorph\ai\movement\lurking.dm" diff --git a/sound/voice/alien_crusher_spawn.ogg b/sound/voice/alien_crusher_spawn.ogg new file mode 100644 index 0000000000..b93030e3ed Binary files /dev/null and b/sound/voice/alien_crusher_spawn.ogg differ