Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a #13

Closed
wants to merge 12 commits into from
8 changes: 6 additions & 2 deletions code/__DEFINES/ai/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
#define GET_TARGETING_STRATEGY(targeting_type) SSai_behaviors.targeting_strategies[targeting_type]
#define HAS_AI_CONTROLLER_TYPE(thing, type) istype(thing?.ai_controller, type)

#define AI_STATUS_ON 1
#define AI_STATUS_OFF 2
//AI controller flags
//If you add a new status, be sure to add it to the ai_controllers subsystem's ai_controllers_by_status list.
///The AI is currently active.
#define AI_STATUS_ON "ai_on"
///The AI is currently offline for any reason.
#define AI_STATUS_OFF "ai_off"

///For JPS pathing, the maximum length of a path we'll try to generate. Should be modularized depending on what we're doing later on
#define AI_MAX_PATH_LENGTH 30 // 30 is possibly overkill since by default we lose interest after 14 tiles of distance, but this gives wiggle room for weaving around obstacles
Expand Down
1 change: 0 additions & 1 deletion code/__DEFINES/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,6 @@
#define AI_ON 1
#define AI_IDLE 2
#define AI_OFF 3
#define AI_Z_OFF 4

//The range at which a mob should wake up if you spawn into the z level near it
#define MAX_SIMPLEMOB_WAKEUP_RANGE 5
Expand Down
1 change: 0 additions & 1 deletion code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)

#define FIRE_PRIORITY_PING 10
#define FIRE_PRIORITY_IDLE_NPC 10
#define FIRE_PRIORITY_SERVER_MAINT 10
#define FIRE_PRIORITY_RESEARCH 10
#define FIRE_PRIORITY_VIS 10
Expand Down
2 changes: 1 addition & 1 deletion code/_globalvars/lists/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ GLOBAL_LIST_EMPTY(human_list) //all instances of /mob/living/carbon/human and su
GLOBAL_LIST_EMPTY(ai_list)
GLOBAL_LIST_EMPTY(pai_list)
GLOBAL_LIST_EMPTY(available_ai_shells)
GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list(),list())) // One for each AI_* status define
GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list())) // One for each AI_* status define
GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs
GLOBAL_LIST_EMPTY(bots_list)
GLOBAL_LIST_EMPTY(aiEyes)
Expand Down
39 changes: 29 additions & 10 deletions code/controllers/subsystem/ai_controllers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,32 @@ SUBSYSTEM_DEF(ai_controllers)
name = "AI Controller Ticker"
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
priority = FIRE_PRIORITY_NPC
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_order = INIT_ORDER_AI_CONTROLLERS
wait = 0.5 SECONDS //Plan every half second if required, not great not terrible.
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME

///List of all ai_subtree singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_subtrees()
var/list/ai_subtrees = list()
///List of all ai controllers currently running
var/list/active_ai_controllers = list()
var/list/datum/ai_planning_subtree/ai_subtrees = list()
///Assoc List of all AI statuses and all AI controllers with that status.
var/list/ai_controllers_by_status = list(
AI_STATUS_ON = list(),
AI_STATUS_OFF = list(),
)
///Assoc List of all AI controllers and the Z level they are on, which we check when someone enters/leaves a Z level to turn them on/off.
var/list/ai_controllers_by_zlevel = list()

/datum/controller/subsystem/ai_controllers/Initialize()
setup_subtrees()
return SS_INIT_SUCCESS

/datum/controller/subsystem/ai_controllers/proc/setup_subtrees()
ai_subtrees = list()
for(var/subtree_type in subtypesof(/datum/ai_planning_subtree))
var/datum/ai_planning_subtree/subtree = new subtree_type
ai_subtrees[subtree_type] = subtree
/datum/controller/subsystem/ai_controllers/stat_entry(msg)
var/list/active_list = ai_controllers_by_status[AI_STATUS_ON]
var/list/inactive_list = ai_controllers_by_status[AI_STATUS_OFF]
msg = "Active AIs:[length(active_list)]|Inactive:[length(inactive_list)]"
return ..()

/datum/controller/subsystem/ai_controllers/fire(resumed)
for(var/datum/ai_controller/ai_controller as anything in active_ai_controllers)
for(var/datum/ai_controller/ai_controller as anything in ai_controllers_by_status[AI_STATUS_ON])
if(!COOLDOWN_FINISHED(ai_controller, failed_planning_cooldown))
continue

Expand All @@ -32,3 +37,17 @@ SUBSYSTEM_DEF(ai_controllers)
ai_controller.SelectBehaviors(wait * 0.1)
if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan
COOLDOWN_START(ai_controller, failed_planning_cooldown, AI_FAILED_PLANNING_COOLDOWN)

///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list.
/datum/controller/subsystem/ai_controllers/proc/setup_subtrees()
for(var/subtree_type in subtypesof(/datum/ai_planning_subtree))
var/datum/ai_planning_subtree/subtree = new subtree_type
ai_subtrees[subtree_type] = subtree

///Called when the max Z level was changed, updating our coverage.
/datum/controller/subsystem/ai_controllers/proc/on_max_z_changed()
if (!islist(ai_controllers_by_zlevel))
ai_controllers_by_zlevel = new /list(world.maxz,0)
while (SSai_controllers.ai_controllers_by_zlevel.len < world.maxz)
SSai_controllers.ai_controllers_by_zlevel.len++
SSai_controllers.ai_controllers_by_zlevel[ai_controllers_by_zlevel.len] = list()
47 changes: 0 additions & 47 deletions code/controllers/subsystem/idlenpcpool.dm

This file was deleted.

72 changes: 56 additions & 16 deletions code/datums/ai/_ai_controller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ multiple modular subtrees with behaviors
PossessPawn(new_pawn)

/datum/ai_controller/Destroy(force)
set_ai_status(AI_STATUS_OFF)
UnpossessPawn(FALSE)
set_movement_target(type, null)
if(ai_movement.moving_controllers[src])
Expand Down Expand Up @@ -116,9 +115,14 @@ multiple modular subtrees with behaviors
pawn = new_pawn
pawn.ai_controller = src

var/turf/pawn_turf = get_turf(pawn)
if(pawn_turf)
SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] += src

SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_POSSESSED_PAWN)

reset_ai_status()
RegisterSignal(pawn, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_changed_z_level))
RegisterSignal(pawn, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_changed))
RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained))
RegisterSignal(pawn, COMSIG_QDELETING, PROC_REF(on_pawn_qdeleted))
Expand All @@ -127,25 +131,53 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/reset_ai_status()
set_ai_status(get_expected_ai_status())

/// Returns what the AI status should be based on current conditions.
/**
* Gets the AI status we expect the AI controller to be on at this current moment.
* Returns AI_STATUS_OFF if it's inhabited by a Client and shouldn't be, if it's dead and cannot act while dead, or is on a z level without clients.
* Returns AI_STATUS_ON otherwise.
*/
/datum/ai_controller/proc/get_expected_ai_status()
var/final_status = AI_STATUS_ON

if (!ismob(pawn))
return final_status
return AI_STATUS_ON

var/mob/living/mob_pawn = pawn

if(!continue_processing_when_client && mob_pawn.client)
final_status = AI_STATUS_OFF

if(ai_traits & CAN_ACT_WHILE_DEAD)
return final_status
return AI_STATUS_OFF

if(mob_pawn.stat == DEAD)
final_status = AI_STATUS_OFF
if(ai_traits & CAN_ACT_WHILE_DEAD)
return AI_STATUS_ON
return AI_STATUS_OFF

var/turf/pawn_turf = get_turf(mob_pawn)
#ifdef TESTING
if(!pawn_turf)
CRASH("AI controller [src] controlling pawn ([pawn]) is not on a turf.")
#endif
if(!length(SSmobs.clients_by_zlevel[pawn_turf.z]))
return AI_STATUS_OFF
return AI_STATUS_ON

/datum/ai_controller/proc/get_current_turf()
var/mob/living/mob_pawn = pawn
var/turf/pawn_turf = get_turf(mob_pawn)
to_chat(world, "[pawn_turf]")

return final_status
///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is.
/datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
SIGNAL_HANDLER
var/mob/mob_pawn = pawn
if((mob_pawn?.client && !continue_processing_when_client))
return
if(old_turf)
SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src
if(new_turf)
SSai_controllers.ai_controllers_by_zlevel[new_turf.z] += src
var/new_level_clients = SSmobs.clients_by_zlevel[new_turf.z].len
if(new_level_clients)
set_ai_status(AI_STATUS_ON)
else
set_ai_status(AI_STATUS_OFF)

///Abstract proc for initializing the pawn to the new controller
/datum/ai_controller/proc/TryPossessPawn(atom/new_pawn)
Expand All @@ -156,9 +188,15 @@ multiple modular subtrees with behaviors
if(isnull(pawn))
return // instantiated without an applicable pawn, fine

UnregisterSignal(pawn, list(COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE, COMSIG_QDELETING))
set_ai_status(AI_STATUS_OFF)
UnregisterSignal(pawn, list(COMSIG_MOVABLE_Z_CHANGED, COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE, COMSIG_QDELETING))
if(ai_movement.moving_controllers[src])
ai_movement.stop_moving_towards(src)
var/turf/pawn_turf = get_turf(pawn)
if(pawn_turf)
SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] -= src
if(ai_status)
SSai_controllers.ai_controllers_by_status[ai_status] -= src
pawn.ai_controller = null
pawn = null
if(destroy)
Expand Down Expand Up @@ -269,15 +307,17 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/set_ai_status(new_ai_status)
if(ai_status == new_ai_status)
return FALSE //no change


//remove old status, if we've got one
if(ai_status)
SSai_controllers.ai_controllers_by_status[ai_status] -= src
ai_status = new_ai_status
SSai_controllers.ai_controllers_by_status[new_ai_status] += src
switch(ai_status)
if(AI_STATUS_ON)
SSai_controllers.active_ai_controllers += src
START_PROCESSING(SSai_behaviors, src)
if(AI_STATUS_OFF)
STOP_PROCESSING(SSai_behaviors, src)
SSai_controllers.active_ai_controllers -= src
CancelActions()

/datum/ai_controller/proc/PauseAi(time)
Expand Down
2 changes: 1 addition & 1 deletion code/game/world.dm
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ GLOBAL_VAR(restart_counter)
/world/proc/incrementMaxZ()
maxz++
SSmobs.MaxZChanged()
SSidlenpcpool.MaxZChanged()
SSai_controllers.on_max_z_changed()

/world/proc/change_fps(new_value = 20)
if(new_value <= 0)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/clothing/shoes/cowboy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
icon_state = "cowboy_brown"
armor_type = /datum/armor/shoes_cowboy
custom_price = PAYCHECK_CREW
var/max_occupants = 4
can_be_tied = FALSE
var/max_occupants = 4
/// Do these boots have spur sounds?
var/has_spurs = FALSE
/// The jingle jangle jingle of our spurs
Expand Down
24 changes: 14 additions & 10 deletions code/modules/mob/dead/dead.dm
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,20 @@ INITIALIZE_IMMEDIATE(/mob/dead)

#undef SERVER_HOPPER_TRAIT

/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister
if (registered_z != new_z)
if (registered_z)
SSmobs.dead_players_by_zlevel[registered_z] -= src
if (client)
if (new_z)
SSmobs.dead_players_by_zlevel[new_z] += src
registered_z = new_z
else
registered_z = null
/**
* updates the Z level for dead players
* If they don't have a new z, we'll keep the old one, preventing bugs from ghosting and re-entering, among others
*/
/mob/dead/proc/update_z(new_z)
if(registered_z == new_z)
return
if(registered_z)
SSmobs.dead_players_by_zlevel[registered_z] -= src
if(isnull(client))
registered_z = null
return
registered_z = new_z
SSmobs.dead_players_by_zlevel[new_z] += src

/mob/dead/Login()
. = ..()
Expand Down
4 changes: 0 additions & 4 deletions code/modules/mob/dead/observer/login.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
preferred_form = client.prefs.read_preference(/datum/preference/choiced/ghost_form)
ghost_orbit = client.prefs.read_preference(/datum/preference/choiced/ghost_orbit)

var/turf/T = get_turf(src)
if (isturf(T))
update_z(T.z)

update_icon(ALL, preferred_form)
updateghostimages()
client.set_right_click_menu_mode(FALSE)
Expand Down
1 change: 0 additions & 1 deletion code/modules/mob/dead/observer/logout.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/mob/dead/observer/Logout()
update_z(null)
if (client)
client.images -= (GLOB.ghost_images_default+GLOB.ghost_images_simple)

Expand Down
Loading
Loading