From 32ffc0ae329aec438df43a6ce2b6345557a7470e Mon Sep 17 00:00:00 2001 From: Morrow Date: Fri, 13 Oct 2023 21:07:32 -0400 Subject: [PATCH] Initial --- code/controllers/subsystem/xeno_ai.dm | 6 +- .../xenomorph/abilities/general_abilities.dm | 25 +++++++++ .../abilities/runner/runner_abilities.dm | 2 +- .../carbon/xenomorph/abilities/xeno_action.dm | 23 +++++++- .../xenomorph/ai/movement/base_define.dm | 4 +- .../carbon/xenomorph/ai/movement/drone.dm | 2 +- .../carbon/xenomorph/ai/movement/linger.dm | 56 +++++++++++++++++++ .../mob/living/carbon/xenomorph/ai/xeno_ai.dm | 19 +++---- .../living/carbon/xenomorph/castes/Runner.dm | 56 +++++++++++++++++++ code/modules/mob/mob.dm | 2 +- colonialmarines.dme | 1 + 11 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 code/modules/mob/living/carbon/xenomorph/ai/movement/linger.dm diff --git a/code/controllers/subsystem/xeno_ai.dm b/code/controllers/subsystem/xeno_ai.dm index f323f90a6f..9cd608f211 100644 --- a/code/controllers/subsystem/xeno_ai.dm +++ b/code/controllers/subsystem/xeno_ai.dm @@ -9,12 +9,10 @@ SUBSYSTEM_DEF(xeno_ai) /// A list of AI mobs var/list/ai_mobs = list() - var/game_evaluation = 0 - var/ai_kill = FALSE /datum/controller/subsystem/xeno_ai/stat_entry(msg) - msg = "P:[length(ai_mobs)]|Eval:[game_evaluation]" + msg = "P:[length(ai_mobs)]" return ..() /datum/admins/proc/toggle_ai() @@ -39,7 +37,7 @@ SUBSYSTEM_DEF(xeno_ai) var/mob/living/carbon/xenomorph/M = current_run[current_run.len] current_run.len-- if(!QDELETED(M) && !M.client && M.stat != DEAD) - M.process_ai(wait * 0.1, game_evaluation) + M.process_ai(wait * 0.1) else remove_ai(M) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm index c1b1ee5f89..ce5492adc3 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm @@ -219,6 +219,31 @@ var/list/pounce_callbacks = null // Specific callbacks to invoke when a pounce lands on an atom of a specific type // (note that if a collided atom does not match any of the key types, defaults to the appropriate X_launch_collision proc) + default_ai_action = TRUE + var/prob_chance = 80 + +/datum/action/xeno_action/activable/pounce/process_ai(mob/living/carbon/xenomorph/pouncing_xeno, delta_time) + if(get_dist(pouncing_xeno, pouncing_xeno.current_target) > distance || !DT_PROB(prob_chance, delta_time)) + return + + var/turf/last_turf = pouncing_xeno.loc + var/clear = TRUE + + pouncing_xeno.add_temp_pass_flags(PASS_OVER_THROW_MOB) + + for(var/i in getline2(pouncing_xeno, pouncing_xeno.current_target, FALSE)) + var/turf/new_turf = i + if(LinkBlocked(pouncing_xeno, last_turf, new_turf, list(pouncing_xeno.current_target, pouncing_xeno))) + clear = FALSE + break + + pouncing_xeno.remove_temp_pass_flags(PASS_OVER_THROW_MOB) + + if(!clear) + return + + use_ability_async(pouncing_xeno.current_target) + /datum/action/xeno_action/activable/pounce/New() . = ..() initialize_pounce_pass_flags() 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 510f161d8a..09544304d8 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 @@ -5,7 +5,7 @@ macro_path = /datum/action/xeno_action/verb/verb_pounce action_type = XENO_ACTION_CLICK ability_primacy = XENO_PRIMARY_ACTION_1 - xeno_cooldown = 30 + xeno_cooldown = 50 plasma_cost = 0 // Config options 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 b7109f3c55..cd4df54dae 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/xeno_action.dm @@ -7,6 +7,9 @@ var/action_type = XENO_ACTION_CLICK // Determines how macros interact with this action. Defines are in xeno.dm in the defines folder. var/ability_primacy = XENO_NOT_PRIMARY_ACTION // Determines how the default ability macros handle this. + /// Whether this action gets added to AI xenos + var/default_ai_action = FALSE + // Cooldown /// Cooldown of the ability (do not use the cooldown var) /// Probably should only have the cooldown var, but that is for another rework @@ -58,6 +61,11 @@ action.update_button_icon() return TRUE +/// Used for AI xenos to prevent them from sleeping +/datum/action/xeno_action/proc/use_ability_async(atom/A) + set waitfor = FALSE + use_ability(A) + // Track statistics for this ability /datum/action/xeno_action/proc/track_xeno_ability_stats() if(!owner) @@ -74,10 +82,19 @@ if(X && !X.is_mob_incapacitated() && !X.dazed && !X.lying && !X.buckled && X.plasma_stored >= plasma_cost) return TRUE -/datum/action/xeno_action/give_to(mob/living/L) +/datum/action/xeno_action/give_to(mob/living/living_mob) ..() + if(macro_path) - add_verb(L, macro_path) + add_verb(living_mob, macro_path) + + if(!istype(living_mob, /mob/living/carbon/xenomorph)) + return + + var/mob/living/carbon/xenomorph/xeno_mob = living_mob + + if(default_ai_action) + xeno_mob.register_ai_action(src) /datum/action/xeno_action/update_button_icon() if(!button) @@ -92,7 +109,7 @@ else button.color = rgb(255,255,255,255) -/datum/action/xeno_action/proc/process_ai(mob/living/carbon/xenomorph/X, delta_time, game_evaluation) +/datum/action/xeno_action/proc/process_ai(mob/living/carbon/xenomorph/X, delta_time) SHOULD_NOT_SLEEP(TRUE) return PROCESS_KILL 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 1edefcd59d..e85952a435 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 @@ -19,7 +19,7 @@ parent = null return ..() -/datum/xeno_ai_movement/proc/ai_move_idle(delta_time, game_evaluation) +/datum/xeno_ai_movement/proc/ai_move_idle(delta_time) SHOULD_NOT_SLEEP(TRUE) var/mob/living/carbon/xenomorph/idle_xeno = parent if(idle_xeno.throwing) @@ -47,7 +47,7 @@ else home_turf = null -/datum/xeno_ai_movement/proc/ai_move_target(delta_time, game_evaluation) +/datum/xeno_ai_movement/proc/ai_move_target(delta_time) SHOULD_NOT_SLEEP(TRUE) var/mob/living/carbon/xenomorph/X = parent if(X.throwing) 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 7f8349177e..c612fdbe79 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/drone.dm @@ -9,7 +9,7 @@ blacklisted_turfs = null //drones expand the hive -/datum/xeno_ai_movement/drone/ai_move_idle(delta_time, game_evaluation) +/datum/xeno_ai_movement/drone/ai_move_idle(delta_time) var/mob/living/carbon/xenomorph/idle_xeno = parent if(idle_xeno.throwing) diff --git a/code/modules/mob/living/carbon/xenomorph/ai/movement/linger.dm b/code/modules/mob/living/carbon/xenomorph/ai/movement/linger.dm new file mode 100644 index 0000000000..7b83ce1c46 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/ai/movement/linger.dm @@ -0,0 +1,56 @@ +/datum/xeno_ai_movement/linger + + /// The turf the xeno is currently attempting to travel to + var/turf/travelling_turf + + /// Distance in turfs that the xeno will try to run to when the marine is not incapacitated or out of view + var/linger_range = 5 + + /// The deviation in turfs of linger_range, gives the bounds a + and - of upper and inner range + var/linger_deviation = 1 + + /// The cooldown for how long the xeno will wait out of view before attempting to re-engage + COOLDOWN_DECLARE(reengage_cooldown) + +/datum/xeno_ai_movement/linger/ai_move_target(delta_time) + var/mob/living/carbon/xenomorph/moving_xeno = parent + if(moving_xeno.throwing) + return + + if(moving_xeno.current_target.is_mob_incapacitated()) + return ..() + + check_for_travelling_turf_change(moving_xeno) + + if(!moving_xeno.move_to_next_turf(travelling_turf)) + travelling_turf = get_turf(moving_xeno.current_target) + return TRUE + +#define REENGAGE_COOLDOWN (2 SECONDS) +#define FIND_NEW_TRAVEL_TURF_LIMIT 5 + +/datum/xeno_ai_movement/linger/proc/check_for_travelling_turf_change(mob/living/carbon/xenomorph/moving_xeno) + if(!(moving_xeno in view(world.view, moving_xeno.current_target)) && COOLDOWN_FINISHED(src, reengage_cooldown)) + travelling_turf = get_turf(moving_xeno.current_target) + COOLDOWN_START(src, reengage_cooldown, REENGAGE_COOLDOWN) + return + + if(!travelling_turf || get_dist(travelling_turf, moving_xeno) <= 0) + for(var/i = 0 to FIND_NEW_TRAVEL_TURF_LIMIT) + travelling_turf = get_random_turf_in_range_unblocked(moving_xeno.current_target, linger_range + linger_deviation, linger_range - linger_deviation) + + if(!travelling_turf) + continue + + var/travelling_turf_dir = get_dir(moving_xeno, travelling_turf) + var/current_target_dir = get_dir(moving_xeno, moving_xeno.current_target) + + if(current_target_dir != travelling_turf_dir && current_target_dir != turn(travelling_turf_dir, 45) && current_target_dir != turn(travelling_turf_dir, -45)) + break + + if(!travelling_turf) + travelling_turf = get_turf(moving_xeno.current_target) + return + +#undef REENGAGE_COOLDOWN +#undef FIND_NEW_TRAVEL_TURF_LIMIT 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 9b7d230b55..915475d3bf 100644 --- a/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm +++ b/code/modules/mob/living/carbon/xenomorph/ai/xeno_ai.dm @@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ai_target_limbs, list( registered_ai_abilities -= XA XA.ai_unregistered(src) -/mob/living/carbon/xenomorph/proc/process_ai(delta_time, game_evaluation) +/mob/living/carbon/xenomorph/proc/process_ai(delta_time) SHOULD_NOT_SLEEP(TRUE) SHOULD_CALL_PARENT(TRUE) if(!hive || !get_turf(src)) @@ -72,10 +72,10 @@ GLOBAL_LIST_INIT(ai_target_limbs, list( a_intent = INTENT_HARM if(!current_target) - ai_move_idle(delta_time, game_evaluation) + ai_move_idle(delta_time) return TRUE - if(ai_move_target(delta_time, game_evaluation)) + if(ai_move_target(delta_time)) return TRUE for(var/x in registered_ai_abilities) @@ -87,23 +87,23 @@ GLOBAL_LIST_INIT(ai_target_limbs, list( if(XA.hidden) continue - if(XA.process_ai(src, delta_time, game_evaluation) == PROCESS_KILL) + if(XA.process_ai(src, delta_time) == PROCESS_KILL) unregister_ai_action(XA) if(get_dist(src, current_target) <= 1 && DT_PROB(XENO_SLASH, delta_time)) - INVOKE_ASYNC(src, TYPE_PROC_REF(/mob, do_click), current_target, "", list()) + INVOKE_ASYNC(src, PROC_REF(do_click), current_target, "", list()) /** Controls movement when idle. Called by process_ai */ -/mob/living/carbon/xenomorph/proc/ai_move_idle(delta_time, game_evaluation) +/mob/living/carbon/xenomorph/proc/ai_move_idle(delta_time) if(!ai_movement_handler) CRASH("No valid movement handler for [src]!") - ai_movement_handler.ai_move_idle(delta_time, game_evaluation) + ai_movement_handler.ai_move_idle(delta_time) /** Controls movement towards target. Called by process_ai */ -/mob/living/carbon/xenomorph/proc/ai_move_target(delta_time, game_evaluation) +/mob/living/carbon/xenomorph/proc/ai_move_target(delta_time) if(!ai_movement_handler) CRASH("No valid movement handler for [src]!") - return ai_movement_handler.ai_move_target(delta_time, game_evaluation) + return ai_movement_handler.ai_move_target(delta_time) /atom/proc/xeno_ai_obstacle(mob/living/carbon/xenomorph/X, direction) return INFINITY @@ -126,7 +126,6 @@ GLOBAL_LIST_INIT(ai_target_limbs, list( next_move_slowdown = 0 return TRUE - /mob/living/carbon/xenomorph/proc/set_path(list/path) current_path = path if(!path) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm index f946ec44b5..06148a7fce 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm @@ -67,12 +67,68 @@ icon_xeno = 'icons/mob/xenos/runner.dmi' icon_xenonid = 'icons/mob/xenonids/runner.dmi' + var/linger_range = 5 + var/linger_deviation = 1 + var/pull_direction /mob/living/carbon/xenomorph/runner/initialize_pass_flags(datum/pass_flags_container/PF) ..() if (PF) PF.flags_pass |= PASS_FLAGS_CRAWLER +/mob/living/carbon/xenomorph/runner/launch_towards(datum/launch_metadata/LM) + if(!current_target) + return ..() + + pull_direction = turn(get_dir(src, current_target), 180) + + if(!(pull_direction in GLOB.cardinals)) + if(abs(x - current_target.x) < abs(y - current_target.y)) + pull_direction &= (NORTH|SOUTH) + else + pull_direction &= (EAST|WEST) + return ..() + +/mob/living/carbon/xenomorph/runner/init_movement_handler() + var/datum/xeno_ai_movement/linger/linger_movement = new(src) + linger_movement.linger_range = linger_range + linger_movement.linger_deviation = linger_deviation + return linger_movement + +/mob/living/carbon/xenomorph/runner/ai_move_target(delta_time) + if(throwing) + return + + if(pulling) + if(can_move_and_apply_move_delay()) + if(!Move(get_step(loc, pull_direction), pull_direction)) + pull_direction = turn(pull_direction, pick(45, -45)) + current_path = null + return + + ..() + + if(get_dist(current_target, src) > 1) + return + + if(!current_target.is_mob_incapacitated()) + return + + if(isxeno(current_target.pulledby)) + return + + if(!DT_PROB(RUNNER_GRAB, delta_time)) + return + + INVOKE_ASYNC(src, PROC_REF(start_pulling), current_target) + swap_hand() + +/mob/living/carbon/xenomorph/runner/process_ai(delta_time) + if(get_active_hand()) + swap_hand() + zone_selected = pick(GLOB.ai_target_limbs) + return ..() + /datum/behavior_delegate/runner_base name = "Base Runner Behavior Delegate" diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 348d007ee3..85a56aefbf 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -472,7 +472,7 @@ if(istype(AM, /atom/movable/clone)) AM = AM.mstr //If AM is a clone, refer to the real target - if ( QDELETED(AM) || !usr || src==AM || !isturf(loc) || !isturf(AM.loc) ) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort! + if (QDELETED(AM) || src == AM || !isturf(loc) || !isturf(AM.loc)) //if there's no person pulling OR the person is pulling themself OR the object being pulled is inside something: abort! return if (AM.anchored || AM.throwing) diff --git a/colonialmarines.dme b/colonialmarines.dme index 87b0abd4db..4bba1126dc 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -1949,6 +1949,7 @@ s// DM Environment file for colonialmarines.dme. #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\drone.dm" +#include "code\modules\mob\living\carbon\xenomorph\ai\movement\linger.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Boiler.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Burrower.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Carrier.dm"