mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-12 11:13:16 +00:00
Refactor AI into two subsystems and clean up targeting some
This commit is contained in:
@@ -74,6 +74,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
|
|||||||
#define INIT_ORDER_XENOARCH -20
|
#define INIT_ORDER_XENOARCH -20
|
||||||
#define INIT_ORDER_CIRCUIT -21
|
#define INIT_ORDER_CIRCUIT -21
|
||||||
#define INIT_ORDER_AI -22
|
#define INIT_ORDER_AI -22
|
||||||
|
#define INIT_ORDER_AI_FAST -23
|
||||||
#define INIT_ORDER_GAME_MASTER -24
|
#define INIT_ORDER_GAME_MASTER -24
|
||||||
#define INIT_ORDER_TICKER -50
|
#define INIT_ORDER_TICKER -50
|
||||||
#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init.
|
#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ SUBSYSTEM_DEF(ai)
|
|||||||
name = "AI"
|
name = "AI"
|
||||||
init_order = INIT_ORDER_AI
|
init_order = INIT_ORDER_AI
|
||||||
priority = FIRE_PRIORITY_AI
|
priority = FIRE_PRIORITY_AI
|
||||||
wait = 5 // This gets run twice a second, however this is technically two loops in one, with the second loop being run every four iterations.
|
wait = 2 SECONDS
|
||||||
flags = SS_NO_INIT|SS_TICKER
|
flags = SS_NO_INIT|SS_TICKER
|
||||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||||
|
|
||||||
@@ -22,15 +22,11 @@ SUBSYSTEM_DEF(ai)
|
|||||||
var/list/currentrun = src.currentrun
|
var/list/currentrun = src.currentrun
|
||||||
|
|
||||||
while(currentrun.len)
|
while(currentrun.len)
|
||||||
// var/mob/living/L = currentrun[currentrun.len]
|
|
||||||
var/datum/ai_holder/A = currentrun[currentrun.len]
|
var/datum/ai_holder/A = currentrun[currentrun.len]
|
||||||
--currentrun.len
|
--currentrun.len
|
||||||
if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick
|
if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick
|
||||||
continue
|
continue
|
||||||
if(times_fired % 4 == 0 && A.holder.stat != DEAD)
|
|
||||||
A.handle_strategicals()
|
A.handle_strategicals()
|
||||||
if(A.holder.stat != DEAD) // The /TG/ version checks stat twice, presumably in-case processing somehow got the mob killed in that instant.
|
|
||||||
A.handle_tactics()
|
|
||||||
|
|
||||||
if(MC_TICK_CHECK)
|
if(MC_TICK_CHECK)
|
||||||
return
|
return
|
||||||
|
|||||||
32
code/controllers/subsystems/aifast.dm
Normal file
32
code/controllers/subsystems/aifast.dm
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
SUBSYSTEM_DEF(aifast)
|
||||||
|
name = "AI (Fast)"
|
||||||
|
init_order = INIT_ORDER_AI_FAST
|
||||||
|
priority = FIRE_PRIORITY_AI
|
||||||
|
wait = 5 // This gets run twice a second, but shouldn't do much unless mobs are in combat
|
||||||
|
flags = SS_NO_INIT|SS_TICKER
|
||||||
|
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||||
|
|
||||||
|
var/list/processing = list()
|
||||||
|
var/list/currentrun = list()
|
||||||
|
|
||||||
|
/datum/controller/subsystem/aifast/stat_entry(msg_prefix)
|
||||||
|
var/list/msg = list(msg_prefix)
|
||||||
|
msg += "P:[processing.len]"
|
||||||
|
..(msg.Join())
|
||||||
|
|
||||||
|
/datum/controller/subsystem/aifast/fire(resumed = 0)
|
||||||
|
if (!resumed)
|
||||||
|
src.currentrun = processing.Copy()
|
||||||
|
|
||||||
|
//cache for sanic speed (lists are references anyways)
|
||||||
|
var/list/currentrun = src.currentrun
|
||||||
|
|
||||||
|
while(currentrun.len)
|
||||||
|
var/datum/ai_holder/A = currentrun[currentrun.len]
|
||||||
|
--currentrun.len
|
||||||
|
if(!A || QDELETED(A) || A.busy) // Doesn't exist or won't exist soon or not doing it this tick
|
||||||
|
continue
|
||||||
|
A.handle_tactics()
|
||||||
|
|
||||||
|
if(MC_TICK_CHECK)
|
||||||
|
return
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
// This is a datum-based artificial intelligence for simple mobs (and possibly others) to use.
|
// This is a datum-based artificial intelligence for simple mobs (and possibly others) to use.
|
||||||
// The neat thing with having this here instead of on the mob is that it is independant of Life(), and that different mobs
|
// The neat thing with having this here instead of on the mob is that it is independant of Life(), and that different mobs
|
||||||
// can use a more or less complex AI by giving it a different datum.
|
// can use a more or less complex AI by giving it a different datum.
|
||||||
|
#define AI_NO_PROCESS 0
|
||||||
|
#define AI_PROCESSING (1<<0)
|
||||||
|
#define AI_FASTPROCESSING (1<<1)
|
||||||
|
|
||||||
|
#define START_AIPROCESSING(Datum) if (!(Datum.process_flags & AI_PROCESSING)) {Datum.process_flags |= AI_PROCESSING;SSai.processing += Datum}
|
||||||
|
#define STOP_AIPROCESSING(Datum) Datum.process_flags &= ~AI_PROCESSING;SSai.processing -= Datum
|
||||||
|
#define START_AIFASTPROCESSING(Datum) if (!(Datum.process_flags & AI_FASTPROCESSING)) {Datum.process_flags |= AI_FASTPROCESSING;SSaifast.processing += Datum}
|
||||||
|
#define STOP_AIFASTPROCESSING(Datum) Datum.process_flags &= ~AI_FASTPROCESSING;SSaifast.processing -= Datum
|
||||||
|
|
||||||
/mob/living
|
/mob/living
|
||||||
var/datum/ai_holder/ai_holder = null
|
var/datum/ai_holder/ai_holder = null
|
||||||
@@ -27,7 +35,20 @@
|
|||||||
var/busy = FALSE // If true, the ticker will skip processing this mob until this is false. Good for if you need the
|
var/busy = FALSE // If true, the ticker will skip processing this mob until this is false. Good for if you need the
|
||||||
// mob to stay still (e.g. delayed attacking). If you need the mob to be inactive for an extended period of time,
|
// mob to stay still (e.g. delayed attacking). If you need the mob to be inactive for an extended period of time,
|
||||||
// consider sleeping the AI instead.
|
// consider sleeping the AI instead.
|
||||||
|
var/process_flags = 0 // Where we're processing, see flag defines.
|
||||||
|
var/list/static/fastprocess_stances = list(
|
||||||
|
STANCE_ALERT,
|
||||||
|
STANCE_APPROACH,
|
||||||
|
STANCE_FIGHT,
|
||||||
|
STANCE_BLINDFIGHT,
|
||||||
|
STANCE_REPOSITION,
|
||||||
|
STANCE_MOVE,
|
||||||
|
STANCE_FOLLOW,
|
||||||
|
STANCE_FLEE
|
||||||
|
)
|
||||||
|
var/list/static/noprocess_stances = list(
|
||||||
|
STANCE_SLEEP
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
/datum/ai_holder/hostile
|
/datum/ai_holder/hostile
|
||||||
@@ -40,16 +61,34 @@
|
|||||||
/datum/ai_holder/New(var/new_holder)
|
/datum/ai_holder/New(var/new_holder)
|
||||||
ASSERT(new_holder)
|
ASSERT(new_holder)
|
||||||
holder = new_holder
|
holder = new_holder
|
||||||
SSai.processing += src
|
|
||||||
home_turf = get_turf(holder)
|
home_turf = get_turf(holder)
|
||||||
|
manage_processing(AI_PROCESSING)
|
||||||
|
GLOB.stat_set_event.register(holder, src, .proc/holder_stat_change)
|
||||||
..()
|
..()
|
||||||
|
|
||||||
/datum/ai_holder/Destroy()
|
/datum/ai_holder/Destroy()
|
||||||
holder = null
|
holder = null
|
||||||
SSai.processing -= src // We might've already been asleep and removed, but byond won't care if we do this again and it saves a conditional.
|
manage_processing(AI_NO_PROCESS)
|
||||||
home_turf = null
|
home_turf = null
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
|
/datum/ai_holder/proc/manage_processing(var/desired)
|
||||||
|
if(desired & AI_PROCESSING)
|
||||||
|
START_AIPROCESSING(src)
|
||||||
|
else
|
||||||
|
STOP_AIPROCESSING(src)
|
||||||
|
|
||||||
|
if(desired & AI_FASTPROCESSING)
|
||||||
|
START_AIFASTPROCESSING(src)
|
||||||
|
else
|
||||||
|
STOP_AIFASTPROCESSING(src)
|
||||||
|
|
||||||
|
/datum/ai_holder/proc/holder_stat_change(var/mob, old_stat, new_stat)
|
||||||
|
if(old_stat >= DEAD && new_stat <= DEAD) //Revived
|
||||||
|
manage_processing(AI_PROCESSING)
|
||||||
|
else if(old_stat <= DEAD && new_stat >= DEAD) //Killed
|
||||||
|
manage_processing(AI_NO_PROCESS)
|
||||||
|
|
||||||
/datum/ai_holder/proc/update_stance_hud()
|
/datum/ai_holder/proc/update_stance_hud()
|
||||||
var/image/stanceimage = holder.grab_hud(LIFE_HUD)
|
var/image/stanceimage = holder.grab_hud(LIFE_HUD)
|
||||||
stanceimage.icon_state = "ais_[stance]"
|
stanceimage.icon_state = "ais_[stance]"
|
||||||
@@ -78,7 +117,6 @@
|
|||||||
return
|
return
|
||||||
forget_everything() // If we ever wake up, its really unlikely that our current memory will be of use.
|
forget_everything() // If we ever wake up, its really unlikely that our current memory will be of use.
|
||||||
set_stance(STANCE_SLEEP)
|
set_stance(STANCE_SLEEP)
|
||||||
SSai.processing -= src
|
|
||||||
update_paused_hud()
|
update_paused_hud()
|
||||||
|
|
||||||
// Reverses the above proc.
|
// Reverses the above proc.
|
||||||
@@ -89,7 +127,6 @@
|
|||||||
if(!should_wake())
|
if(!should_wake())
|
||||||
return
|
return
|
||||||
set_stance(STANCE_IDLE)
|
set_stance(STANCE_IDLE)
|
||||||
SSai.processing += src
|
|
||||||
update_paused_hud()
|
update_paused_hud()
|
||||||
|
|
||||||
/datum/ai_holder/proc/should_wake()
|
/datum/ai_holder/proc/should_wake()
|
||||||
@@ -122,12 +159,23 @@
|
|||||||
|
|
||||||
// For setting the stance WITHOUT processing it
|
// For setting the stance WITHOUT processing it
|
||||||
/datum/ai_holder/proc/set_stance(var/new_stance)
|
/datum/ai_holder/proc/set_stance(var/new_stance)
|
||||||
|
if(stance == new_stance)
|
||||||
|
ai_log("set_stance() : Ignoring change stance to same stance request.", AI_LOG_INFO)
|
||||||
|
return
|
||||||
|
|
||||||
ai_log("set_stance() : Setting stance from [stance] to [new_stance].", AI_LOG_INFO)
|
ai_log("set_stance() : Setting stance from [stance] to [new_stance].", AI_LOG_INFO)
|
||||||
stance = new_stance
|
stance = new_stance
|
||||||
if(stance_coloring) // For debugging or really weird mobs.
|
if(stance_coloring) // For debugging or really weird mobs.
|
||||||
stance_color()
|
stance_color()
|
||||||
update_stance_hud()
|
update_stance_hud()
|
||||||
|
|
||||||
|
if(new_stance in fastprocess_stances) //Becoming fast
|
||||||
|
manage_processing(AI_PROCESSING|AI_FASTPROCESSING)
|
||||||
|
else if(new_stance in noprocess_stances)
|
||||||
|
manage_processing(AI_NO_PROCESS) //Becoming off
|
||||||
|
else
|
||||||
|
manage_processing(AI_PROCESSING) //Becoming slow
|
||||||
|
|
||||||
// This is called every half a second.
|
// This is called every half a second.
|
||||||
/datum/ai_holder/proc/handle_stance_tactical()
|
/datum/ai_holder/proc/handle_stance_tactical()
|
||||||
ai_log("========= Fast Process Beginning ==========", AI_LOG_TRACE) // This is to make it easier visually to disinguish between 'blocks' of what a tick did.
|
ai_log("========= Fast Process Beginning ==========", AI_LOG_TRACE) // This is to make it easier visually to disinguish between 'blocks' of what a tick did.
|
||||||
@@ -167,19 +215,6 @@
|
|||||||
return
|
return
|
||||||
|
|
||||||
switch(stance)
|
switch(stance)
|
||||||
if(STANCE_IDLE)
|
|
||||||
if(should_go_home())
|
|
||||||
ai_log("handle_stance_tactical() : STANCE_IDLE, going to go home.", AI_LOG_TRACE)
|
|
||||||
go_home()
|
|
||||||
|
|
||||||
else if(should_follow_leader())
|
|
||||||
ai_log("handle_stance_tactical() : STANCE_IDLE, going to follow leader.", AI_LOG_TRACE)
|
|
||||||
set_stance(STANCE_FOLLOW)
|
|
||||||
|
|
||||||
else if(should_wander())
|
|
||||||
ai_log("handle_stance_tactical() : STANCE_IDLE, going to wander randomly.", AI_LOG_TRACE)
|
|
||||||
handle_wander_movement()
|
|
||||||
|
|
||||||
if(STANCE_ALERT)
|
if(STANCE_ALERT)
|
||||||
ai_log("handle_stance_tactical() : STANCE_ALERT, going to threaten_target().", AI_LOG_TRACE)
|
ai_log("handle_stance_tactical() : STANCE_ALERT, going to threaten_target().", AI_LOG_TRACE)
|
||||||
threaten_target()
|
threaten_target()
|
||||||
@@ -241,9 +276,23 @@
|
|||||||
if(STANCE_IDLE)
|
if(STANCE_IDLE)
|
||||||
if(speak_chance) // In the long loop since otherwise it wont shut up.
|
if(speak_chance) // In the long loop since otherwise it wont shut up.
|
||||||
handle_idle_speaking()
|
handle_idle_speaking()
|
||||||
|
|
||||||
if(hostile)
|
if(hostile)
|
||||||
ai_log("handle_stance_strategical() : STANCE_IDLE, going to find_target().", AI_LOG_TRACE)
|
ai_log("handle_stance_strategical() : STANCE_IDLE, going to find_target().", AI_LOG_TRACE)
|
||||||
find_target()
|
find_target()
|
||||||
|
|
||||||
|
if(should_go_home())
|
||||||
|
ai_log("handle_stance_tactical() : STANCE_IDLE, going to go home.", AI_LOG_TRACE)
|
||||||
|
go_home()
|
||||||
|
|
||||||
|
else if(should_follow_leader())
|
||||||
|
ai_log("handle_stance_tactical() : STANCE_IDLE, going to follow leader.", AI_LOG_TRACE)
|
||||||
|
set_stance(STANCE_FOLLOW)
|
||||||
|
|
||||||
|
else if(should_wander())
|
||||||
|
ai_log("handle_stance_tactical() : STANCE_IDLE, going to wander randomly.", AI_LOG_TRACE)
|
||||||
|
handle_wander_movement()
|
||||||
|
|
||||||
if(STANCE_APPROACH)
|
if(STANCE_APPROACH)
|
||||||
if(target)
|
if(target)
|
||||||
ai_log("handle_stance_strategical() : STANCE_APPROACH, going to calculate_path([target]).", AI_LOG_TRACE)
|
ai_log("handle_stance_strategical() : STANCE_APPROACH, going to calculate_path([target]).", AI_LOG_TRACE)
|
||||||
@@ -292,3 +341,6 @@
|
|||||||
/mob/living/proc/taunt(atom/movable/taunter, force_target_switch = FALSE)
|
/mob/living/proc/taunt(atom/movable/taunter, force_target_switch = FALSE)
|
||||||
if(ai_holder)
|
if(ai_holder)
|
||||||
ai_holder.receive_taunt(taunter, force_target_switch)
|
ai_holder.receive_taunt(taunter, force_target_switch)
|
||||||
|
|
||||||
|
#undef AI_PROCESSING
|
||||||
|
#undef AI_FASTPROCESSING
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
// If our holder is able to do anything.
|
// If our holder is able to do anything.
|
||||||
/datum/ai_holder/proc/can_act()
|
/datum/ai_holder/proc/can_act()
|
||||||
if(!holder) // Holder missing.
|
if(!holder) // Holder missing.
|
||||||
SSai.processing -= src
|
manage_processing(AI_NO_PROCESS)
|
||||||
return FALSE
|
return FALSE
|
||||||
if(holder.stat) // Dead or unconscious.
|
if(holder.stat) // Dead or unconscious.
|
||||||
ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE)
|
ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
ai_log("lose_follow() : Exited.", AI_LOG_DEBUG)
|
ai_log("lose_follow() : Exited.", AI_LOG_DEBUG)
|
||||||
|
|
||||||
/datum/ai_holder/proc/should_follow_leader()
|
/datum/ai_holder/proc/should_follow_leader()
|
||||||
if(!leader)
|
if(!leader || target)
|
||||||
return FALSE
|
return FALSE
|
||||||
if(follow_until_time && world.time > follow_until_time)
|
if(follow_until_time && world.time > follow_until_time)
|
||||||
lose_follow()
|
lose_follow()
|
||||||
|
|||||||
@@ -43,6 +43,8 @@
|
|||||||
ai_log("walk_to_destination() : Exiting.",AI_LOG_TRACE)
|
ai_log("walk_to_destination() : Exiting.",AI_LOG_TRACE)
|
||||||
|
|
||||||
/datum/ai_holder/proc/should_go_home()
|
/datum/ai_holder/proc/should_go_home()
|
||||||
|
if(stance != STANCE_IDLE)
|
||||||
|
return FALSE
|
||||||
if(!returns_home || !home_turf)
|
if(!returns_home || !home_turf)
|
||||||
return FALSE
|
return FALSE
|
||||||
if(get_dist(holder, home_turf) > max_home_distance)
|
if(get_dist(holder, home_turf) > max_home_distance)
|
||||||
@@ -139,7 +141,7 @@
|
|||||||
return MOVEMENT_ON_COOLDOWN
|
return MOVEMENT_ON_COOLDOWN
|
||||||
|
|
||||||
/datum/ai_holder/proc/should_wander()
|
/datum/ai_holder/proc/should_wander()
|
||||||
return wander && !leader
|
return (stance == STANCE_IDLE) && wander && !leader
|
||||||
|
|
||||||
// Wanders randomly in cardinal directions.
|
// Wanders randomly in cardinal directions.
|
||||||
/datum/ai_holder/proc/handle_wander_movement()
|
/datum/ai_holder/proc/handle_wander_movement()
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
|
|
||||||
// Step 1, find out what we can see.
|
// Step 1, find out what we can see.
|
||||||
/datum/ai_holder/proc/list_targets()
|
/datum/ai_holder/proc/list_targets()
|
||||||
. = hearers(vision_range, holder) - holder // Remove ourselves to prevent suicidal decisions. ~ SRC is the ai_holder.
|
. = ohearers(vision_range, holder)
|
||||||
. -= dview_mob // Not the dview mob either, nerd.
|
. -= dview_mob // Not the dview mob!
|
||||||
|
|
||||||
var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/blob))
|
var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/structure/blob))
|
||||||
|
|
||||||
@@ -43,13 +43,8 @@
|
|||||||
if(!has_targets_list)
|
if(!has_targets_list)
|
||||||
possible_targets = list_targets()
|
possible_targets = list_targets()
|
||||||
for(var/possible_target in possible_targets)
|
for(var/possible_target in possible_targets)
|
||||||
var/atom/A = possible_target
|
if(can_attack(possible_target)) // Can we attack it?
|
||||||
if(found(A)) // In case people want to override this.
|
. += possible_target
|
||||||
. = list(A)
|
|
||||||
break
|
|
||||||
if(can_attack(A)) // Can we attack it?
|
|
||||||
. += A
|
|
||||||
continue
|
|
||||||
|
|
||||||
var/new_target = pick_target(.)
|
var/new_target = pick_target(.)
|
||||||
give_target(new_target)
|
give_target(new_target)
|
||||||
@@ -57,7 +52,7 @@
|
|||||||
|
|
||||||
// Step 3, pick among the possible, attackable targets.
|
// Step 3, pick among the possible, attackable targets.
|
||||||
/datum/ai_holder/proc/pick_target(list/targets)
|
/datum/ai_holder/proc/pick_target(list/targets)
|
||||||
if(target != null) // If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets.
|
if(target) // If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets.
|
||||||
targets = target_filter_distance(targets)
|
targets = target_filter_distance(targets)
|
||||||
else
|
else
|
||||||
targets = target_filter_closest(targets)
|
targets = target_filter_closest(targets)
|
||||||
@@ -88,32 +83,31 @@
|
|||||||
|
|
||||||
// Filters return one or more 'preferred' targets.
|
// Filters return one or more 'preferred' targets.
|
||||||
|
|
||||||
// This one is for closest targets.
|
// This one is for targets closer than our current one.
|
||||||
/datum/ai_holder/proc/target_filter_distance(list/targets)
|
/datum/ai_holder/proc/target_filter_distance(list/targets)
|
||||||
|
var/target_dist = get_dist(holder, target)
|
||||||
|
var/list/better_targets = list()
|
||||||
for(var/possible_target in targets)
|
for(var/possible_target in targets)
|
||||||
var/atom/A = possible_target
|
var/atom/A = possible_target
|
||||||
var/target_dist = get_dist(holder, target)
|
|
||||||
var/possible_target_distance = get_dist(holder, A)
|
var/possible_target_distance = get_dist(holder, A)
|
||||||
if(target_dist < possible_target_distance)
|
if(possible_target_distance < target_dist)
|
||||||
targets -= A
|
better_targets += A
|
||||||
return targets
|
return better_targets
|
||||||
|
|
||||||
|
// Returns the closest target and anything tied with it for distance
|
||||||
/datum/ai_holder/proc/target_filter_closest(list/targets)
|
/datum/ai_holder/proc/target_filter_closest(list/targets)
|
||||||
var/lowest_distance = -1
|
var/lowest_distance = 1e6 //fakely far
|
||||||
var/list/sorted_targets = list()
|
var/list/closest_targets = list()
|
||||||
for(var/possible_target in targets)
|
for(var/possible_target in targets)
|
||||||
var/atom/A = possible_target
|
var/atom/A = possible_target
|
||||||
var/current_distance = get_dist(holder, A)
|
var/current_distance = get_dist(holder, A)
|
||||||
if(lowest_distance == -1)
|
if(current_distance < lowest_distance)
|
||||||
|
closest_targets.Cut()
|
||||||
lowest_distance = current_distance
|
lowest_distance = current_distance
|
||||||
sorted_targets += A
|
closest_targets += A
|
||||||
else if(current_distance < lowest_distance)
|
|
||||||
targets.Cut()
|
|
||||||
lowest_distance = current_distance
|
|
||||||
sorted_targets += A
|
|
||||||
else if(current_distance == lowest_distance)
|
else if(current_distance == lowest_distance)
|
||||||
sorted_targets += A
|
closest_targets += A
|
||||||
return sorted_targets
|
return closest_targets
|
||||||
|
|
||||||
/datum/ai_holder/proc/can_attack(atom/movable/the_target, var/vision_required = TRUE)
|
/datum/ai_holder/proc/can_attack(atom/movable/the_target, var/vision_required = TRUE)
|
||||||
if(!can_see_target(the_target) && vision_required)
|
if(!can_see_target(the_target) && vision_required)
|
||||||
@@ -163,11 +157,6 @@
|
|||||||
return TRUE
|
return TRUE
|
||||||
// return FALSE
|
// return FALSE
|
||||||
|
|
||||||
// Override this for special targeting criteria.
|
|
||||||
// If it returns true, the mob will always select it as the target.
|
|
||||||
/datum/ai_holder/proc/found(atom/movable/the_target)
|
|
||||||
return FALSE
|
|
||||||
|
|
||||||
// 'Soft' loss of target. They may still exist, we still have some info about them maybe.
|
// 'Soft' loss of target. They may still exist, we still have some info about them maybe.
|
||||||
/datum/ai_holder/proc/lose_target()
|
/datum/ai_holder/proc/lose_target()
|
||||||
ai_log("lose_target() : Entering.", AI_LOG_TRACE)
|
ai_log("lose_target() : Entering.", AI_LOG_TRACE)
|
||||||
|
|||||||
@@ -256,14 +256,10 @@
|
|||||||
|
|
||||||
for(var/possible_target in possible_targets)
|
for(var/possible_target in possible_targets)
|
||||||
var/atom/A = possible_target
|
var/atom/A = possible_target
|
||||||
if(found(A))
|
|
||||||
. = list(A)
|
|
||||||
break
|
|
||||||
if(istype(A, /mob/living) && !can_pick_mobs)
|
if(istype(A, /mob/living) && !can_pick_mobs)
|
||||||
continue
|
continue
|
||||||
if(can_attack(A)) // Can we attack it?
|
if(can_attack(A)) // Can we attack it?
|
||||||
. += A
|
. += A
|
||||||
continue
|
|
||||||
|
|
||||||
for(var/obj/item/I in .)
|
for(var/obj/item/I in .)
|
||||||
last_search = world.time
|
last_search = world.time
|
||||||
|
|||||||
@@ -225,6 +225,7 @@
|
|||||||
#include "code\controllers\verbs.dm"
|
#include "code\controllers\verbs.dm"
|
||||||
#include "code\controllers\observer_listener\atom\observer.dm"
|
#include "code\controllers\observer_listener\atom\observer.dm"
|
||||||
#include "code\controllers\subsystems\ai.dm"
|
#include "code\controllers\subsystems\ai.dm"
|
||||||
|
#include "code\controllers\subsystems\aifast.dm"
|
||||||
#include "code\controllers\subsystems\air.dm"
|
#include "code\controllers\subsystems\air.dm"
|
||||||
#include "code\controllers\subsystems\airflow.dm"
|
#include "code\controllers\subsystems\airflow.dm"
|
||||||
#include "code\controllers\subsystems\alarm.dm"
|
#include "code\controllers\subsystems\alarm.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user