From f7d923757303646888bee142d0d6ab4903962167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=BA=D1=82=D0=BE?= <65656972+xDanilcusx@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:46:12 +0300 Subject: [PATCH] AI Hive Wars (#93) Co-authored-by: morrow --- code/__DEFINES/xeno_ai.dm | 5 +- .../attack_override_behavior.dm | 2 +- .../base_override_behavior.dm | 16 +++ .../build_override_behavior.dm | 34 +++-- .../capture_override_behavior.dm | 15 +-- .../hive_override_behavior.dm | 2 +- .../colonialmarines/ai/colonialmarines_ai.dm | 4 + .../abilities/crusher/crusher_abilities.dm | 21 ++-- .../abilities/crusher/crusher_powers.dm | 2 +- .../abilities/warrior/warrior_powers.dm | 1 + .../xenomorph/ai/movement/base_define.dm | 14 ++- .../carbon/xenomorph/ai/movement/drone.dm | 11 +- .../carbon/xenomorph/ai/movement/lurking.dm | 4 +- .../mob/living/carbon/xenomorph/ai/xeno_ai.dm | 77 +++++++----- .../mob/living/carbon/xenomorph/death.dm | 1 - .../carbon/xenomorph/xeno_ai_interaction.dm | 116 +++++++++++++++--- .../living/carbon/xenomorph/xeno_defines.dm | 16 +-- code/modules/mob/mob.dm | 2 +- 18 files changed, 238 insertions(+), 105 deletions(-) diff --git a/code/__DEFINES/xeno_ai.dm b/code/__DEFINES/xeno_ai.dm index 847f649fd5..a132aa28ed 100644 --- a/code/__DEFINES/xeno_ai.dm +++ b/code/__DEFINES/xeno_ai.dm @@ -23,7 +23,7 @@ PROBABILITY CALCULATIONS ARE HERE #define XENO_SLASH 80 -#define XENO_DOOR_BUILDING_CHANCE 25 +#define XENO_DOOR_BUILDING_CHANCE 40 #define PLASMA_RETREAT_PERCENTAGE 10 #define HEALTH_RETREAT_PERCENTAGE 20 @@ -91,3 +91,6 @@ PROBABILITY CALCULATIONS ARE HERE /// Special blockers for pathfinding or obstacle handling #define XENO_AI_SPECIAL_BLOCKERS list(/obj/flamer_fire, /obj/vehicle/multitile, /turf/open/space) + +// Friend-or-foe universal check +#define IS_SAME_HIVENUMBER(A,B) (A.hivenumber == B.hivenumber) diff --git a/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm index 2b2290586e..d996158840 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/attack_override_behavior.dm @@ -27,7 +27,7 @@ return FALSE processing_xeno.current_target = parent - processing_xeno.resting = FALSE + processing_xeno.set_resting(FALSE, FALSE, TRUE) if(prob(5)) processing_xeno.emote("hiss") diff --git a/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm index a5bb0728de..1dbcb353d1 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/base_override_behavior.dm @@ -36,6 +36,9 @@ GLOBAL_LIST_EMPTY(all_ai_behavior_overrides) game_master.images -= behavior_image QDEL_NULL(behavior_image) + + for(var/assigned_xeno in currently_assigned) + UnregisterSignal(assigned_xeno, COMSIG_PARENT_QDELETING) currently_assigned = null . = ..() @@ -43,6 +46,11 @@ GLOBAL_LIST_EMPTY(all_ai_behavior_overrides) /// Override this to check if we want our behavior to be valid for the checked_xeno, passes the common factor of "distance" which is the distance between the checked_xeno and src parent /datum/component/ai_behavior_override/proc/check_behavior_validity(mob/living/carbon/xenomorph/checked_xeno, distance) if(length(currently_assigned) >= max_assigned && !(checked_xeno in currently_assigned)) + remove_from_queue(checked_xeno) + return FALSE + + if(checked_xeno.stat != CONSCIOUS) + remove_from_queue(checked_xeno) return FALSE return TRUE @@ -51,6 +59,14 @@ GLOBAL_LIST_EMPTY(all_ai_behavior_overrides) /datum/component/ai_behavior_override/proc/process_override_behavior(mob/living/carbon/xenomorph/processing_xeno, delta_time) SHOULD_NOT_SLEEP(TRUE) + RegisterSignal(processing_xeno, COMSIG_PARENT_QDELETING, PROC_REF(remove_from_queue), TRUE) currently_assigned |= processing_xeno return TRUE + +/datum/component/ai_behavior_override/proc/remove_from_queue(mob/removed_xeno) + SIGNAL_HANDLER + if(currently_assigned) + currently_assigned -= removed_xeno + + UnregisterSignal(removed_xeno, COMSIG_PARENT_QDELETING) diff --git a/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm index 5430c3cd03..a5c4a03982 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/build_override_behavior.dm @@ -35,6 +35,14 @@ qdel(src) return FALSE + var/area/current_area = get_area(location) + if(!current_area.is_resin_allowed) + for(var/client/game_master in GLOB.game_masters) + to_chat(game_master, SPAN_XENOBOLDNOTICE("This area doesn't allow xenos to build here")) + + qdel(src) + return FALSE + if(distance > 10) return FALSE @@ -46,9 +54,8 @@ if(checked_xeno.get_plasma_percentage() < PLASMA_RETREAT_PERCENTAGE) var/turf/xeno_loc = get_turf(checked_xeno) - if(xeno_loc.weeds && !checked_xeno.resting) - currently_assigned -= checked_xeno - checked_xeno.lay_down() + if(xeno_loc.weeds) + checked_xeno.set_resting(TRUE, FALSE, TRUE) return FALSE @@ -59,16 +66,21 @@ if(!.) return - processing_xeno.resting = FALSE + processing_xeno.set_resting(FALSE, FALSE, TRUE) var/turf/xeno_loc = get_turf(processing_xeno) if(xeno_loc.density) return FALSE // We shouldn't stand in a wall, let's act default var/turf/parent_turf = get_turf(parent) + var/distance = get_dist(processing_xeno, parent) + + var/list/turfs_around = xeno_loc.AdjacentTurfs() + if(turfs_around && distance < 1) // We are gonna be stuck after building at our loc, let's step away + return processing_xeno.move_to_next_turf(pick(turfs_around)) var/is_diagonal = (get_dir(processing_xeno, parent_turf) in diagonals) - if(is_diagonal || get_dist(processing_xeno, parent) > 1) + if(is_diagonal || distance > 1) return processing_xeno.move_to_next_turf(parent_turf) for(var/obj/structure/blocker in parent_turf.contents) @@ -84,19 +96,23 @@ var/list/resin_types = processing_xeno.resin_build_order processing_xeno.selected_resin = locate(/datum/resin_construction/resin_turf/wall) in resin_types - var/wall_nearby + var/wall_nearby = FALSE var/blocked_turfs = 0 for(var/turf/blocked_turf in orange(1, parent_turf) - parent_turf.AdjacentTurfs()) + blocked_turfs++ + if(get_dir(blocked_turf, parent_turf) in diagonals) continue if(blocked_turf.density) wall_nearby = TRUE - blocked_turfs++ + var/obj/effect/alien/weeds/turf_weeds = blocked_turf.weeds + if(turf_weeds && turf_weeds.secreting) + wall_nearby = TRUE // Something is being constructed nearby, let's bet this is a new resin wall - if(blocked_turfs) - if(prob(XENO_DOOR_BUILDING_CHANCE) || (wall_nearby && blocked_turfs == 2)) + if(wall_nearby) + if(prob(XENO_DOOR_BUILDING_CHANCE) || (wall_nearby && blocked_turfs > 1)) processing_xeno.selected_resin = locate(/datum/resin_construction/resin_obj/door) in resin_types var/datum/action/xeno_action/activable/secrete_resin/build_action = locate() in processing_xeno.actions diff --git a/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm index 3651ca25ed..505dc6cdfa 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/capture_override_behavior.dm @@ -7,10 +7,11 @@ /datum/component/ai_behavior_override/capture/Initialize(...) . = ..() - if(!istype(parent, /mob)) - return COMPONENT_INCOMPATIBLE + if(!ishuman(parent)) + var/mob/living/carbon/human/new_parent = locate() in get_turf(parent) + if(new_parent) + new_parent.AddComponent(/datum/component/ai_behavior_override/capture) - if(isxeno(parent)) return COMPONENT_INCOMPATIBLE /datum/component/ai_behavior_override/capture/check_behavior_validity(mob/living/carbon/xenomorph/checked_xeno, distance) @@ -20,10 +21,10 @@ var/mob/parent_mob = parent - var/stat = parent_mob.stat + var/captee_stat = parent_mob.stat var/mob/pulledby = parent_mob.pulledby - if(stat == DEAD) + if(captee_stat == DEAD) qdel(src) return FALSE @@ -41,7 +42,7 @@ if(distance > 10) return FALSE - if(stat == CONSCIOUS) + if(captee_stat == CONSCIOUS) return FALSE if(isxeno(pulledby) && pulledby != checked_xeno) @@ -55,7 +56,7 @@ return processing_xeno.current_target = parent - processing_xeno.resting = FALSE + processing_xeno.set_resting(FALSE, FALSE, TRUE) if(processing_xeno.get_active_hand()) processing_xeno.swap_hand() diff --git a/code/datums/components/xeno/ai_behavior_overrides/hive_override_behavior.dm b/code/datums/components/xeno/ai_behavior_overrides/hive_override_behavior.dm index 3fb5923a71..e6c1dd8275 100644 --- a/code/datums/components/xeno/ai_behavior_overrides/hive_override_behavior.dm +++ b/code/datums/components/xeno/ai_behavior_overrides/hive_override_behavior.dm @@ -9,7 +9,7 @@ GLOBAL_LIST_EMPTY(ai_hives) max_assigned = 0 - var/hive_radius = 7 + var/hive_radius = 5 /datum/component/ai_behavior_override/hive/Initialize(...) . = ..() diff --git a/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm b/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm index d1d98b7af0..cbe4c24fab 100644 --- a/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm +++ b/code/game/gamemodes/colonialmarines/ai/colonialmarines_ai.dm @@ -43,6 +43,10 @@ . = ..() +/datum/game_mode/colonialmarines/ai/post_setup() + set_lz_resin_allowed(TRUE) + return ..() + /datum/game_mode/colonialmarines/ai/announce_bioscans() return 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 ca5f88638a..418393afd5 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 @@ -149,29 +149,29 @@ 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) + for(var/mob/living/carbon/base_checked_carbon as anything in GLOB.alive_mob_list) + var/distance_between_base_carbon_and_xeno = get_dist(processing_xeno, base_checked_carbon) - if(distance_between_base_human_and_xeno > MAXIMUM_TARGET_DISTANCE) + if(distance_between_base_carbon_and_xeno > MAXIMUM_TARGET_DISTANCE) continue - if(distance_between_base_human_and_xeno < MINIMUM_CHARGE_DISTANCE) + if(distance_between_base_carbon_and_xeno < MINIMUM_CHARGE_DISTANCE) continue - if(!processing_xeno.check_mob_target(base_checked_human)) + if(!base_checked_carbon.ai_can_target(processing_xeno)) 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)) + for(var/mob/living/carbon/secondary_checked_carbon in range(FLOCK_SCAN_RADIUS, base_checked_carbon)) + if(!secondary_checked_carbon.ai_can_target(processing_xeno)) continue secondary_count++ - secondary_x_sum += secondary_checked_human.x - secondary_y_sum += secondary_checked_human.y + secondary_x_sum += secondary_checked_carbon.x + secondary_y_sum += secondary_checked_carbon.y if(secondary_count < MIN_TARGETS_TO_CHARGE) continue @@ -305,6 +305,9 @@ if(hit_human.body_position == LYING_DOWN) continue + if(xeno.can_not_harm(hit_human)) + 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!")) 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 74c6dadb72..ca313a69ef 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 @@ -127,7 +127,7 @@ xeno_owner.create_stomp() 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)) + if(xeno_owner.can_not_harm(carbon_in_range)) continue var/distance_to_target = get_dist(carbon_in_range, xeno_owner) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm index 1ba675f5e6..e262e28b71 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/warrior/warrior_powers.dm @@ -119,6 +119,7 @@ woyer.flick_attack_overlay(carbone, "disarm") carbone.throw_atom(throw_turf, fling_distance, SPEED_VERY_FAST, woyer, TRUE) + COOLDOWN_RESET(woyer, forced_retarget_cooldown) apply_cooldown() return ..() diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm index e2c8e5cf20..172461bfce 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/base_define.dm @@ -25,25 +25,27 @@ if(idle_xeno.throwing) return - if(next_home_search < world.time && (!home_turf || !home_turf.weeds || get_dist(home_turf, idle_xeno) > max_distance_from_home)) + if(next_home_search < world.time && (!home_turf || !home_turf.weeds || !IS_SAME_HIVENUMBER(idle_xeno, home_turf.weeds) || get_dist(home_turf, idle_xeno) > max_distance_from_home)) var/turf/T = get_turf(idle_xeno.loc) next_home_search = world.time + home_search_delay - if(T.weeds) + var/obj/effect/alien/weeds/current_weeds = T.weeds + if(current_weeds && IS_SAME_HIVENUMBER(idle_xeno, current_weeds)) home_turf = T else var/shortest_distance = INFINITY for(var/i in RANGE_TURFS(home_locate_range, T)) var/turf/potential_home = i - if(potential_home.weeds && !potential_home.density && get_dist(idle_xeno, potential_home) < shortest_distance) + var/obj/effect/alien/weeds/potential_weeds = potential_home.weeds + if(potential_weeds && IS_SAME_HIVENUMBER(idle_xeno, potential_weeds) && !potential_home.density && get_dist(idle_xeno, potential_home) < shortest_distance) home_turf = potential_home shortest_distance = get_dist(idle_xeno, potential_home) - + idle_xeno.set_resting(FALSE, FALSE, TRUE) if(!home_turf) return if(idle_xeno.move_to_next_turf(home_turf, home_locate_range)) - if(get_dist(home_turf, idle_xeno) <= 0 && !idle_xeno.resting) - idle_xeno.lay_down() + if(get_dist(home_turf, idle_xeno) <= 0) + idle_xeno.set_resting(TRUE, FALSE, TRUE) else home_turf = null diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm index 086000bed4..263f354c78 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm @@ -50,11 +50,10 @@ home_turf = potential_home if(!home_turf) - if(!idle_xeno.resting) - idle_xeno.lay_down() + idle_xeno.set_resting(TRUE, FALSE, TRUE) return - idle_xeno.resting = FALSE + idle_xeno.set_resting(FALSE, FALSE, TRUE) if(home_turf == last_home_turf) blacklisted_turfs += home_turf @@ -79,13 +78,15 @@ if(checked_turf in blacklisted_turfs) return FALSE - if(checked_turf.weeds) + var/obj/effect/alien/weeds/checked_weeds = checked_turf.weeds + if(checked_weeds && IS_SAME_HIVENUMBER(checked_weeds, parent)) return FALSE if(checked_turf.is_weedable() < FULLY_WEEDABLE) return FALSE - if(locate(/obj/effect/alien/weeds/node) in range(3, checked_turf)) + var/obj/effect/alien/weeds/found_weeds = locate(/obj/effect/alien/weeds/node) in range(3, checked_turf) + if(found_weeds && IS_SAME_HIVENUMBER(found_weeds, parent)) return FALSE if(checked_turf.density) diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm index eeeb5b9ba8..3f2e46f5d0 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/lurking.dm @@ -271,7 +271,7 @@ registered_turfs += cycled_open_turf var/mob/living/carbon/human/possible_target = locate() in cycled_open_turf - if(possible_target && (!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target)) && parent.check_mob_target(possible_target)) + if(possible_target && (!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target)) && possible_target.ai_can_target(parent)) parent.current_target = possible_target /datum/xeno_ai_movement/linger/lurking/proc/unregister_turf_signals() @@ -286,7 +286,7 @@ return var/mob/living/carbon/human/possible_target = entering_atom - if(!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target) && parent.check_mob_target(possible_target)) + if(!parent.current_target || get_dist(parent, possible_target) < get_dist(parent, parent.current_target) && possible_target.ai_can_target(parent)) parent.current_target = possible_target /datum/xeno_ai_movement/linger/lurking/proc/lurking_parent_moved(atom/movable/moving_atom, atom/oldloc, direction, Forced) 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 4cac1dabe6..b10bc7f7f1 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm @@ -25,6 +25,9 @@ /// The actual cooldown declaration for forceful retargeting, reference forced_retarget_time for time in between checks COOLDOWN_DECLARE(forced_retarget_cooldown) + /// Amount of times no path found has occured + var/no_path_found_amount = 0 + /// The time interval between calculating new paths if we cannot find a path var/no_path_found_period = (2.5 SECONDS) @@ -62,28 +65,23 @@ if(!hive || !get_turf(src)) return TRUE - if(is_mob_incapacitated(TRUE)) - current_path = null - return TRUE - var/datum/component/ai_behavior_override/behavior_override = check_overrides() if(behavior_override?.process_override_behavior(src, delta_time)) return TRUE - var/stat_check = FALSE - if(istype(current_target, /mob)) - var/mob/current_target_mob = current_target - stat_check = (current_target_mob.stat != CONSCIOUS) + if(is_mob_incapacitated(TRUE)) + current_path = null + return TRUE - if(QDELETED(current_target) || stat_check || get_dist(current_target, src) > ai_range || COOLDOWN_FINISHED(src, forced_retarget_cooldown)) + if(QDELETED(current_target) || !current_target.ai_check_stat() || 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 if(current_target) - resting = FALSE + set_resting(FALSE, FALSE, TRUE) if(prob(5)) emote("hiss") @@ -139,6 +137,9 @@ return 0 return INFINITY +/atom/proc/ai_check_stat() + return TRUE // So we aren't trying to find a new target on attack override + // Called whenever an obstacle is encountered but xeno_ai_obstacle returned something else than infinite // and now it is considered a valid path. /atom/proc/xeno_ai_act(mob/living/carbon/xenomorph/X) @@ -168,10 +169,15 @@ return FALSE if(no_path_found) - COOLDOWN_START(src, no_path_found_cooldown, no_path_found_period) + + if(no_path_found_amount > 0) + COOLDOWN_START(src, no_path_found_cooldown, no_path_found_period) no_path_found = FALSE + no_path_found_amount++ return FALSE + no_path_found_amount = 0 + if((!current_path || (next_path_generation < world.time && current_target_turf != T)) && COOLDOWN_FINISHED(src, no_path_found_cooldown)) if(!XENO_CALCULATING_PATH(src) || current_target_turf != T) SSxeno_pathfinding.calculate_path(src, T, max_range, src, CALLBACK(src, PROC_REF(set_path)), list(src, current_target)) @@ -242,24 +248,28 @@ var/list/viable_targets = list() var/atom/movable/closest_target var/smallest_distance = INFINITY - for(var/mob/living/carbon/human/potential_alive_human_target as anything in GLOB.alive_human_list) - if(z != potential_alive_human_target.z) + + for(var/mob/living/carbon/potential_target as anything in GLOB.alive_mob_list) + if(!iscarbon(potential_target)) + continue + + if(z != potential_target.z) continue - if(!check_mob_target(potential_alive_human_target)) + if(!potential_target.ai_can_target(src)) continue - var/distance = get_dist(src, potential_alive_human_target) + var/distance = get_dist(src, potential_target) if(distance > ai_range) continue - viable_targets += potential_alive_human_target + viable_targets += potential_target if(smallest_distance <= distance) continue - closest_target = potential_alive_human_target + closest_target = potential_target smallest_distance = distance for(var/obj/vehicle/multitile/potential_vehicle_target as anything in GLOB.all_multi_vehicles) @@ -272,18 +282,24 @@ continue if(potential_vehicle_target.health <= 0) - var/skip_vehicle = TRUE - - var/list/interior_living_mobs = potential_vehicle_target.interior.get_passengers() - for(var/mob/living/carbon/human/human_mob in interior_living_mobs) - if(!check_mob_target(human_mob)) - continue + continue - skip_vehicle = FALSE + var/multitile_faction = potential_vehicle_target.vehicle_faction + if(hive.faction_is_ally(multitile_faction)) + continue - if(skip_vehicle) + var/skip_vehicle + var/list/interior_living_mobs = potential_vehicle_target.interior.get_passengers() + for(var/mob/living/carbon/human/human_mob in interior_living_mobs) + if(!human_mob.ai_can_target(src)) continue + skip_vehicle = FALSE + break + + if(skip_vehicle) + continue + viable_targets += potential_vehicle_target if(smallest_distance <= distance) @@ -322,17 +338,14 @@ #undef EXTRA_CHECK_DISTANCE_MULTIPLIER -/mob/living/carbon/xenomorph/proc/check_mob_target(mob/living/carbon/human/checked_human) - if(checked_human.species.flags & IS_SYNTHETIC) - return FALSE - - if(HAS_TRAIT(checked_human, TRAIT_NESTED)) +/mob/living/carbon/proc/ai_can_target(mob/living/carbon/xenomorph/ai_xeno) + if(!ai_check_stat()) return FALSE - if(can_not_harm(checked_human)) + if(ai_xeno.can_not_harm(src)) return FALSE - if(checked_human.stat != CONSCIOUS) + if(alpha <= 45 && get_dist(ai_xeno, src) > 2) return FALSE return TRUE diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm index 9084c9fb3a..e7366df07f 100644 --- a/code/modules/mob/living/carbon/xenomorph/death.dm +++ b/code/modules/mob/living/carbon/xenomorph/death.dm @@ -70,7 +70,6 @@ if(!QDELETED(Q) && Q != src && Q.hivenumber == hivenumber) hive.set_living_xeno_queen(Q) break - hive.on_queen_death() hive.handle_xeno_leader_pheromones() if(SSticker.mode) INVOKE_ASYNC(SSticker.mode, TYPE_PROC_REF(/datum/game_mode, check_queen_status), hivenumber) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm b/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm index 217f61d56f..5d36c4efde 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_ai_interaction.dm @@ -15,7 +15,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in */// - Morrow -// OBJECTS + +///////////////////////////// +// OBJECTS // +///////////////////////////// /obj/structure/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -43,7 +46,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return ..() -// MINERAL DOOR + +///////////////////////////// +// MINERAL DOOR // +///////////////////////////// /obj/structure/mineral_door/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) return DOOR_PENALTY @@ -53,11 +59,14 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return 0 /obj/structure/mineral_door/resin/xeno_ai_act(mob/living/carbon/xenomorph/acting_xeno) - if(acting_xeno.hivenumber == hivenumber) + if(IS_SAME_HIVENUMBER(acting_xeno, src)) acting_xeno.a_intent = INTENT_HELP . = ..() -/// Platforms + +///////////////////////////// +// PLATFORMS // +///////////////////////////// /obj/structure/platform/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -65,7 +74,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// Poddoors/shutters + +///////////////////////////// +// Poddoors/shutters // +///////////////////////////// /obj/structure/machinery/door/poddoor/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -82,7 +94,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// AIRLOCK + +///////////////////////////// +// AIRLOCK // +///////////////////////////// /obj/structure/machinery/door/airlock/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -93,7 +108,17 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// HUMANS + +///////////////////////////// +// MOBS // +///////////////////////////// +/mob/living/ai_check_stat() + return stat == CONSCIOUS + + +///////////////////////////// +// HUMANS // +///////////////////////////// /mob/living/carbon/human/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) if(status_flags & GODMODE) return ..() @@ -104,17 +129,61 @@ At bare minimum, make sure the relevant checks from parent types gets copied in if(status_flags & GODMODE) return + if(X.can_not_harm(src)) + return // No nibbles for friendlies + . = ..() -// XENOS +/mob/living/carbon/human/ai_can_target(mob/living/carbon/xenomorph/ai_xeno) + . = ..() + if(!.) + return FALSE + + if(species.flags & IS_SYNTHETIC) + return FALSE + + if(HAS_TRAIT(src, TRAIT_NESTED)) + return FALSE + + return TRUE + + +///////////////////////////// +// XENOS // +///////////////////////////// /mob/living/carbon/xenomorph/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) return + if(!IS_SAME_HIVENUMBER(X, src)) + return HUMAN_PENALTY + return XENO_PENALTY -// VEHICLES +/mob/living/carbon/xenomorph/xeno_ai_act(mob/living/carbon/xenomorph/X) + if(X.can_not_harm(src)) + return + + . = ..() + +/mob/living/carbon/xenomorph/ai_can_target(mob/living/carbon/xenomorph/ai_xeno) + . = ..() + if(!.) + return FALSE + + if(IS_SAME_HIVENUMBER(ai_xeno, src)) + return FALSE + + return TRUE + +/mob/living/carbon/xenomorph/ai_check_stat() + return stat != DEAD // Should slash enemy xenos, even if they are critted out + + +///////////////////////////// +// VEHICLES // +///////////////////////////// /obj/vehicle/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -122,7 +191,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return VEHICLE_PENALTY -// SENTRY + +///////////////////////////// +// SENTRY // +///////////////////////////// /obj/structure/machinery/defenses/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -130,7 +202,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return VEHICLE_PENALTY -// WINDOW FRAME + +///////////////////////////// +// WINDOW FRAME // +///////////////////////////// /obj/structure/window_frame/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) if(X.claw_type == CLAW_TYPE_VERY_SHARP || (X.claw_type >= CLAW_TYPE_SHARP && !reinforced)) return ..() @@ -143,7 +218,10 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return DOOR_PENALTY -// Avoid barricades if possible. + +///////////////////////////// +// BARRICADES // +///////////////////////////// /obj/structure/barricade/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) @@ -151,14 +229,24 @@ At bare minimum, make sure the relevant checks from parent types gets copied in return BARRICADE_PENALTY -// FIRE + +///////////////////////////// +// FIRE // +///////////////////////////// /obj/flamer_fire/xeno_ai_obstacle(mob/living/carbon/xenomorph/xeno, direction, turf/target) if(xeno.caste?.fire_immunity & (FIRE_IMMUNITY_NO_IGNITE|FIRE_IMMUNITY_NO_DAMAGE)) return 0 return FIRE_PENALTY -/// Open turfs, sometimes open turfs are passed back as obstacles due to platforms and such, generally it's fast so very slight penalty mainly for handling subtypes properly + +///////////////////////////// +// FLOOR // +///////////////////////////// +/* + Sometimes open turfs are passed back as obstacles due to platforms and such, + generally it's fast so very slight penalty mainly for handling subtypes properly +*/ /turf/open/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction, turf/target) . = ..() if(!.) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index afca63a59a..1d286adf3b 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -1034,7 +1034,7 @@ return faction_is_ally(living_mob.faction) -/datum/hive_status/proc/faction_is_ally(faction, ignore_queen_check = FALSE) +/datum/hive_status/proc/faction_is_ally(faction, ignore_queen_check = TRUE) if(faction == internal_faction) return TRUE if(!ignore_queen_check && !living_xeno_queen) @@ -1424,20 +1424,6 @@ /datum/hive_status/corrupted/renegade/faction_is_ally(faction, ignore_queen_check = TRUE) return ..() -/datum/hive_status/proc/on_queen_death() //break alliances on queen's death - if(allow_no_queen_actions || living_xeno_queen) - return - var/broken_alliances = FALSE - for(var/faction in allies) - if(!allies[faction]) - continue - change_stance(faction, FALSE) - broken_alliances = TRUE - - - if(broken_alliances) - xeno_message(SPAN_XENOANNOUNCE("With the death of the Queen, all alliances have been broken."), 3, hivenumber) - /datum/hive_status/proc/change_stance(faction, should_ally) if(faction == name) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 4b49a15a6f..3df4c8b83f 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -721,7 +721,7 @@ note dizziness decrements automatically in the mob's Life() proc. // facing verbs /mob/proc/canface() - if(client.moving) return 0 + if(client && client.moving) return 0 if(stat==2) return 0 if(anchored) return 0 if(monkeyizing) return 0