mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 20:48:56 +01:00
Moves and documents AI's definition (+ minor cleanup) (#88928)
## About The Pull Request The AI's definition is big and unorganized, which makes figuring out what does what really annoying, so I took the time to document and organize the file Some vars have been renamed to better communicate what they actually do when reading code Unused vars have been deleted: * var/chnotify * var/waypoint * var/interfaceButton ### Misc changes: The check to see if malf modules could be used was written incorrectly, and therefore all malf modules could be used without cooldown. The check has been fixed, so actions which used to have no cooldown should now AI bot summoning was supposed to have a cooldown, but it was broken and not implemented for basic bots. This has been removed instead of fixed, since I found it was an unnecessary limitation for something so simple. ## Changelog 🆑 fix: Malf modules correctly use action cooldown (affects camera repair and detonate RCDs) code: Minor (extremely minor) AI code maintenance /🆑
This commit is contained in:
+2
-2
@@ -73,8 +73,8 @@
|
||||
if(world.time <= next_move)
|
||||
return
|
||||
|
||||
if(waypoint_mode)
|
||||
waypoint_mode = 0
|
||||
if(setting_waypoint)
|
||||
setting_waypoint = FALSE
|
||||
set_waypoint(A)
|
||||
return
|
||||
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
using = new /atom/movable/screen/ai/modpc(null, src)
|
||||
using.screen_loc = ui_ai_mod_int
|
||||
static_inventory += using
|
||||
myai.interfaceButton = using
|
||||
var/atom/movable/screen/ai/modpc/tabletbutton = using
|
||||
tabletbutton.robot = myai
|
||||
|
||||
|
||||
@@ -63,12 +63,12 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
|
||||
button_icon = 'icons/mob/actions/actions_AI.dmi'
|
||||
/// The owner AI, so we don't have to typecast every time
|
||||
var/mob/living/silicon/ai/owner_AI
|
||||
/// If we have multiple uses of the same power
|
||||
var/uses
|
||||
/// Amount of uses for this action. Defining this as 0 will make this infinite-use
|
||||
var/uses = FALSE
|
||||
/// If we automatically use up uses on each activation
|
||||
var/auto_use_uses = TRUE
|
||||
/// If applicable, the time in deciseconds we have to wait before using any more modules
|
||||
var/cooldown_period
|
||||
var/cooldown_period = 0 SECONDS
|
||||
|
||||
/datum/action/innate/ai/Grant(mob/living/player)
|
||||
. = ..()
|
||||
@@ -79,16 +79,16 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
|
||||
owner_AI = owner
|
||||
|
||||
/datum/action/innate/ai/IsAvailable(feedback = FALSE)
|
||||
if(owner_AI && !COOLDOWN_FINISHED(owner_AI, malf_cooldown))
|
||||
return FALSE
|
||||
. = ..()
|
||||
if(owner_AI && owner_AI.malf_cooldown > world.time)
|
||||
return
|
||||
|
||||
/datum/action/innate/ai/Trigger(trigger_flags)
|
||||
. = ..()
|
||||
if(auto_use_uses)
|
||||
adjust_uses(-1)
|
||||
if(cooldown_period)
|
||||
owner_AI.malf_cooldown = world.time + cooldown_period
|
||||
COOLDOWN_START(owner_AI, malf_cooldown, cooldown_period)
|
||||
|
||||
/datum/action/innate/ai/proc/adjust_uses(amt, silent)
|
||||
uses += amt
|
||||
@@ -188,6 +188,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
|
||||
set_up_us_the_bomb(owner)
|
||||
|
||||
/datum/action/innate/ai/nuke_station/proc/set_up_us_the_bomb(mob/living/owner)
|
||||
//oh my GOD.
|
||||
set waitfor = FALSE
|
||||
message_admins("[key_name_admin(owner)][ADMIN_FLW(owner)] has activated AI Doomsday.")
|
||||
var/pass = prob(10) ? "******" : "hunter2"
|
||||
@@ -807,7 +808,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
|
||||
button_icon_state = "reactivate_cameras"
|
||||
uses = 30
|
||||
auto_use_uses = FALSE
|
||||
cooldown_period = 30
|
||||
cooldown_period = 3 SECONDS
|
||||
|
||||
/datum/action/innate/ai/reactivate_cameras/New()
|
||||
..()
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
var/use_visibility = TRUE
|
||||
/// List of [camera chunks][/datum/camerachunk] visible to this camera.
|
||||
/// Please don't interface with this directly. Use the [cameranet][/datum/cameranet].
|
||||
var/final/list/datum/camerachunk/visibleCameraChunks = list()
|
||||
VAR_FINAL/list/datum/camerachunk/visibleCameraChunks = list()
|
||||
/// NxN Range of a single camera chunk.
|
||||
var/static_visibility_range = 16
|
||||
|
||||
|
||||
@@ -1,108 +1,7 @@
|
||||
#define CALL_BOT_COOLDOWN 900
|
||||
|
||||
#define HOLOGRAM_CHOICE_CHARACTER "Existing Character"
|
||||
#define CHARACTER_TYPE_SELF "My Character"
|
||||
#define CHARACTER_TYPE_CREWMEMBER "Station Member"
|
||||
|
||||
/mob/living/silicon/ai
|
||||
name = "AI"
|
||||
real_name = "AI"
|
||||
icon = 'icons/mob/silicon/ai.dmi'
|
||||
icon_state = "ai"
|
||||
move_resist = MOVE_FORCE_OVERPOWERING
|
||||
density = TRUE
|
||||
status_flags = CANSTUN|CANPUSH
|
||||
combat_mode = TRUE //so we always get pushed instead of trying to swap
|
||||
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
|
||||
hud_type = /datum/hud/ai
|
||||
silicon_huds = list(DATA_HUD_MEDICAL_BASIC, DATA_HUD_SECURITY_BASIC, DATA_HUD_DIAGNOSTIC, DATA_HUD_BOT_PATH)
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
radio = /obj/item/radio/headset/silicon/ai
|
||||
can_buckle_to = FALSE
|
||||
var/battery = 200 //emergency power if the AI's APC is off
|
||||
var/list/network = list(CAMERANET_NETWORK_SS13)
|
||||
var/obj/machinery/camera/current
|
||||
var/list/connected_robots = list()
|
||||
var/aiRestorePowerRoutine = POWER_RESTORATION_OFF
|
||||
var/requires_power = POWER_REQ_ALL
|
||||
var/can_be_carded = TRUE
|
||||
var/mutable_appearance/hologram_appearance //Default is assigned when AI is created.
|
||||
var/obj/controlled_equipment //A piece of equipment, to determine whether to relaymove or use the AI eye.
|
||||
var/radio_enabled = TRUE //Determins if a carded AI can speak with its built in radio or not.
|
||||
radiomod = ";" //AIs will, by default, state their laws on the internal radio.
|
||||
///Used as a fake multitoool in tcomms machinery
|
||||
var/obj/item/multitool/aiMulti
|
||||
///Weakref to the bot the ai's commanding right now
|
||||
var/datum/weakref/bot_ref
|
||||
var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever
|
||||
|
||||
//MALFUNCTION
|
||||
var/datum/module_picker/malf_picker
|
||||
var/list/datum/ai_module/current_modules = list()
|
||||
var/can_dominate_mechs = FALSE
|
||||
var/shunted = FALSE //1 if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead
|
||||
var/obj/machinery/ai_voicechanger/ai_voicechanger = null // reference to machine that holds the voicechanger
|
||||
var/malfhacking = FALSE // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite
|
||||
/// List of hacked APCs
|
||||
var/list/hacked_apcs = list()
|
||||
var/malf_cooldown = 0 //Cooldown var for malf modules, stores a worldtime + cooldown
|
||||
|
||||
var/obj/machinery/power/apc/malfhack
|
||||
var/explosive = FALSE //does the AI explode when it dies?
|
||||
|
||||
var/camera_light_on = FALSE
|
||||
var/list/obj/machinery/camera/lit_cameras = list()
|
||||
|
||||
///The internal tool used to track players visible through cameras.
|
||||
var/datum/trackable/ai_tracking_tool
|
||||
|
||||
var/last_tablet_note_seen = null
|
||||
var/can_shunt = TRUE
|
||||
var/last_announcement = "" // For AI VOX, if enabled
|
||||
var/turf/waypoint //Holds the turf of the currently selected waypoint.
|
||||
var/waypoint_mode = FALSE //Waypoint mode is for selecting a turf via clicking.
|
||||
var/call_bot_cooldown = 0 //time of next call bot command
|
||||
var/obj/machinery/power/apc/apc_override //Ref of the AI's APC, used when the AI has no power in order to access their APC.
|
||||
var/nuking = FALSE
|
||||
var/obj/machinery/doomsday_device/doomsday_device
|
||||
|
||||
var/mob/eye/camera/ai/eyeobj
|
||||
var/sprint = 10
|
||||
var/last_moved = 0
|
||||
var/acceleration = TRUE
|
||||
|
||||
var/obj/structure/ai_core/deactivated/linked_core //For exosuit control
|
||||
var/mob/living/silicon/robot/deployed_shell = null //For shell control
|
||||
var/datum/action/innate/deploy_shell/deploy_action = new
|
||||
var/datum/action/innate/deploy_last_shell/redeploy_action = new
|
||||
var/datum/action/innate/choose_modules/modules_action
|
||||
var/chnotify = 0
|
||||
|
||||
var/multicam_on = FALSE
|
||||
var/atom/movable/screen/movable/pic_in_pic/ai/master_multicam
|
||||
var/list/multicam_screens = list()
|
||||
var/list/all_eyes = list()
|
||||
var/max_multicams = 6
|
||||
var/display_icon_override
|
||||
|
||||
var/list/cam_hotkeys = new/list(9)
|
||||
var/atom/cam_prev
|
||||
|
||||
var/datum/robot_control/robot_control
|
||||
/// Station alert datum for showing alerts UI
|
||||
var/datum/station_alert/alert_control
|
||||
///remember AI's last location
|
||||
var/atom/lastloc
|
||||
interaction_range = INFINITY
|
||||
|
||||
var/atom/movable/screen/ai/modpc/interfaceButton
|
||||
///whether its mmi is a posibrain or regular mmi when going ai mob to ai core structure
|
||||
var/posibrain_inside = TRUE
|
||||
///whether its cover is opened, so you can wirecut it for deconstruction
|
||||
var/opened = FALSE
|
||||
///whether AI is anchored or not, used for checks
|
||||
var/is_anchored = TRUE
|
||||
|
||||
/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
|
||||
. = ..()
|
||||
if(!target_ai) //If there is no player/brain inside.
|
||||
@@ -608,9 +507,7 @@
|
||||
summon_success = basic_bot.summon_bot(src, waypoint, grant_all_access = TRUE)
|
||||
else
|
||||
var/mob/living/simple_animal/bot/simple_bot = bot
|
||||
call_bot_cooldown = world.time + CALL_BOT_COOLDOWN
|
||||
summon_success = simple_bot.call_bot(src, waypoint)
|
||||
call_bot_cooldown = 0
|
||||
|
||||
var/chat_message = summon_success ? "Sending command to bot..." : "Interface error. Unit is already in use."
|
||||
to_chat(src, span_notice("[chat_message]"))
|
||||
@@ -1004,7 +901,7 @@
|
||||
|
||||
/mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc)
|
||||
malfhack = null
|
||||
malfhacking = 0
|
||||
malfhacking = FALSE
|
||||
clear_alert(ALERT_HACKING_APC)
|
||||
|
||||
if(!istype(apc) || QDELETED(apc) || apc.machine_stat & BROKEN)
|
||||
@@ -1181,8 +1078,6 @@
|
||||
return ai_voicechanger.say_name
|
||||
return
|
||||
|
||||
#undef CALL_BOT_COOLDOWN
|
||||
|
||||
#undef HOLOGRAM_CHOICE_CHARACTER
|
||||
#undef CHARACTER_TYPE_SELF
|
||||
#undef CHARACTER_TYPE_CREWMEMBER
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/mob/living/silicon/ai
|
||||
name = "AI"
|
||||
real_name = "AI"
|
||||
icon = 'icons/mob/silicon/ai.dmi'
|
||||
icon_state = "ai"
|
||||
move_resist = MOVE_FORCE_OVERPOWERING
|
||||
density = TRUE
|
||||
status_flags = CANSTUN|CANPUSH
|
||||
combat_mode = TRUE //so we always get pushed instead of trying to swap
|
||||
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
|
||||
hud_type = /datum/hud/ai
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
can_buckle_to = FALSE
|
||||
|
||||
silicon_huds = list(DATA_HUD_MEDICAL_BASIC, DATA_HUD_SECURITY_BASIC, DATA_HUD_DIAGNOSTIC, DATA_HUD_BOT_PATH)
|
||||
radio = /obj/item/radio/headset/silicon/ai
|
||||
radiomod = ";" //AIs will, by default, state their laws on the internal radio.
|
||||
|
||||
|
||||
/* INITIALIZATION */
|
||||
/// If FALSE, attempts to [obj/item/aicard][card] the AI will be rejected
|
||||
var/can_be_carded = TRUE
|
||||
/// If TRUE, the AI will explode when killed
|
||||
var/explodes_on_death = FALSE
|
||||
/// Whether its MMI is a posibrain or regular MMI, used when being [obj/structure/ai_core][deconstructed]
|
||||
var/posibrain_inside = TRUE
|
||||
|
||||
|
||||
/* STATE */
|
||||
/// If this AI can use their radio
|
||||
var/radio_enabled = TRUE
|
||||
/// Whether its cover is opened, so you can wirecut it for deconstruction
|
||||
var/opened = FALSE
|
||||
/// If the AI is currently anchored to the ground, used for checks. Distinct from [atom/movable/anchored]
|
||||
var/is_anchored = TRUE
|
||||
|
||||
/// Raw HTML containing the last page that was loaded from a held-up PDA
|
||||
VAR_FINAL/last_tablet_note_seen = null
|
||||
/// The last attempted VOX announcement. Exists so that failed VOXes can be retried easily
|
||||
VAR_FINAL/last_announcement = ""
|
||||
/// AI core icon_state selected by the AI through [verb/pick_icon]
|
||||
var/display_icon_override
|
||||
|
||||
|
||||
/* ROBOTS */
|
||||
/// List of robots currently synced to the AI
|
||||
VAR_FINAL/list/mob/living/silicon/robot/connected_robots = list()
|
||||
|
||||
|
||||
/* POWER */
|
||||
/// Reserve emergency power, consumed when the AI has no [var/power_requirement][power source]
|
||||
var/battery = 200
|
||||
/// The conditions the AI will check to see if it's powered, can be set to NONE (0) to disable requirements
|
||||
var/power_requirement = POWER_REQ_ALL
|
||||
/// Current stage of the AI's power restoration routine
|
||||
VAR_FINAL/aiRestorePowerRoutine = POWER_RESTORATION_OFF
|
||||
/// The APC the AI is powered from, set when the AI has no power in order to access their APC
|
||||
VAR_FINAL/obj/machinery/power/apc/apc_override
|
||||
|
||||
|
||||
/* CAMERA */
|
||||
/// The network that the AI is currently viewing
|
||||
var/list/network = list(CAMERANET_NETWORK_SS13)
|
||||
/// If the AI has their camera light enabled
|
||||
var/camera_light_on = FALSE
|
||||
/// List of cameras that have been illuminated by the AI's camera light
|
||||
VAR_FINAL/list/obj/machinery/camera/lit_cameras = list()
|
||||
/// List of atoms that the AI's camera can quickly jump to through keys 1-9
|
||||
VAR_FINAL/list/atom/cam_hotkeys = new/list(9)
|
||||
/// The camera's last location before jumping
|
||||
VAR_FINAL/atom/cam_prev
|
||||
|
||||
|
||||
/* CAMERA EYE */
|
||||
/// The AI's **main** eye
|
||||
VAR_FINAL/mob/eye/camera/ai/eyeobj
|
||||
/// List of [mob/eye/camera/ai][camera eyes] that the AI has created, including the [var/eyeobj][main eye]
|
||||
VAR_FINAL/list/mob/eye/camera/ai/all_eyes = list()
|
||||
/// The internal tool used to track players visible through cameras
|
||||
VAR_FINAL/datum/trackable/ai_tracking_tool
|
||||
/// The current movement speed of the camera, it's definition being the base speed. Moves 1 more tile for every 10 sprint.
|
||||
var/sprint = 10
|
||||
/// Time since the AI [proc/AIMove][last moved their camera eye], uses world.timeofday
|
||||
VAR_FINAL/last_moved = 0
|
||||
/// If the camera eye [proc/AIMove][moves progressively faster] when looking around
|
||||
var/acceleration = TRUE
|
||||
|
||||
|
||||
/* MALFUNCTION */
|
||||
/// UI for picking malfunction modules
|
||||
VAR_FINAL/datum/module_picker/malf_picker
|
||||
/// Opens the [datum/module_picker][malf module UI]
|
||||
VAR_FINAL/datum/action/innate/choose_modules/modules_action
|
||||
/// Modules that the AI has already unlocked
|
||||
VAR_FINAL/list/datum/ai_module/current_modules = list()
|
||||
/// Cooldown between malf module usages
|
||||
COOLDOWN_DECLARE(malf_cooldown)
|
||||
|
||||
/// Timer used when hacking an APC
|
||||
VAR_FINAL/malfhacking = FALSE //--NeoFite was here
|
||||
/// APC that we are currently hacking
|
||||
VAR_FINAL/obj/machinery/power/apc/malfhack
|
||||
/// APCs that the AI has already hacked
|
||||
VAR_FINAL/list/hacked_apcs = list()
|
||||
|
||||
/// If TRUE, the AI can take control over mechs
|
||||
var/can_dominate_mechs = FALSE
|
||||
/// If TRUE, the AI can shunt themselves into APCs
|
||||
var/can_shunt = TRUE
|
||||
/// TRUE if the AI is currently shunted, used to differentiate between shunted and ghosted/braindead
|
||||
VAR_FINAL/shunted = FALSE
|
||||
/// If the AI has enabled doomsday
|
||||
VAR_FINAL/nuking = FALSE
|
||||
/// Reference to the doomsday device
|
||||
VAR_FINAL/obj/machinery/doomsday_device/doomsday_device
|
||||
/// Reference to machine that holds the voicechanger
|
||||
VAR_FINAL/obj/machinery/ai_voicechanger/ai_voicechanger
|
||||
|
||||
|
||||
/* REMOTE CONTROL */
|
||||
/// Equipment that the AI is controlling remotely, to determine whether to relaymove or use the AI eye
|
||||
VAR_FINAL/obj/controlled_equipment
|
||||
/// AI core that this AI is linked to, used when put into an exosuit
|
||||
VAR_FINAL/obj/structure/ai_core/deactivated/linked_core
|
||||
/// Robot that this AI is currently using
|
||||
VAR_FINAL/mob/living/silicon/robot/deployed_shell
|
||||
/// Action to deploy to a shell from a list of options
|
||||
VAR_FINAL/datum/action/innate/deploy_shell/deploy_action = new()
|
||||
/// Action to deploy to the last shell the AI used
|
||||
VAR_FINAL/datum/action/innate/deploy_last_shell/redeploy_action = new()
|
||||
|
||||
|
||||
/* MULTICAMERA */
|
||||
/// If the AI is in multicamera mode
|
||||
VAR_FINAL/multicam_on = FALSE
|
||||
/// Maximum multicamera windows the AI can have open
|
||||
var/max_multicams = 6
|
||||
/// The multicamera window that the AI is currently using
|
||||
VAR_FINAL/atom/movable/screen/movable/pic_in_pic/ai/master_multicam
|
||||
/// All of the AI's currently open multicamera windows
|
||||
VAR_FINAL/list/atom/movable/screen/movable/pic_in_pic/ai/multicam_screens = list()
|
||||
|
||||
|
||||
/* ROBOT CONTROL */
|
||||
/// UI for robot controls
|
||||
VAR_FINAL/datum/robot_control/robot_control
|
||||
/// Weakref to the bot the AI is currently commanding
|
||||
VAR_FINAL/datum/weakref/bot_ref
|
||||
/// If TRUE, the AI will send it's [var/bot_ref][commanded bot] to the next clicked atom
|
||||
VAR_FINAL/setting_waypoint = FALSE
|
||||
|
||||
|
||||
/* HOLOGRAM */
|
||||
/// The AI eye's last location, used when answering a hologram request
|
||||
VAR_FINAL/atom/lastloc
|
||||
/// The AI's hologram appearance, can be set by a client and is assigned on AI creation
|
||||
VAR_FINAL/mutable_appearance/hologram_appearance
|
||||
/// The AI's currently used holopad
|
||||
VAR_FINAL/obj/machinery/holopad/current
|
||||
|
||||
/* UI */
|
||||
/// UI for station alerts
|
||||
VAR_FINAL/datum/station_alert/alert_control
|
||||
|
||||
/* I'M DUMB AND CAN'T SORT */
|
||||
/// Used as a fake multitool in tcomms machinery
|
||||
VAR_FINAL/obj/item/multitool/aiMulti
|
||||
/// Helper effect that creates sparks when the AI is damaged
|
||||
VAR_FINAL/datum/effect_system/spark_spread/spark_system
|
||||
@@ -36,7 +36,7 @@
|
||||
if(gibbed && drop_mmi)
|
||||
make_mmi_drop_and_transfer()
|
||||
|
||||
if(explosive)
|
||||
if(explodes_on_death)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), loc, 3, 6, 12, null, 15), 1 SECONDS)
|
||||
|
||||
SSblackbox.ReportDeath(src)
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
/mob/living/silicon/ai/proc/lacks_power()
|
||||
var/turf/T = get_turf(src)
|
||||
var/area/A = get_area(src)
|
||||
switch(requires_power)
|
||||
switch(power_requirement)
|
||||
if(NONE)
|
||||
return FALSE
|
||||
if(POWER_REQ_ALL)
|
||||
|
||||
@@ -84,9 +84,6 @@
|
||||
|
||||
switch(action)
|
||||
if("callbot") //Command a bot to move to a selected location.
|
||||
if(owner.call_bot_cooldown > world.time)
|
||||
to_chat(our_user, span_danger("Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route."))
|
||||
return
|
||||
if(isbasicbot(bot))
|
||||
var/mob/living/basic/bot/basic_bot = bot
|
||||
if(!(basic_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED))
|
||||
@@ -97,7 +94,7 @@
|
||||
return
|
||||
|
||||
owner.bot_ref = WEAKREF(bot)
|
||||
owner.waypoint_mode = TRUE
|
||||
owner.setting_waypoint = TRUE
|
||||
to_chat(our_user, span_notice("Set your waypoint by clicking on a valid location free of obstructions."))
|
||||
if("interface") //Remotely connect to a bot!
|
||||
owner.bot_ref = WEAKREF(bot)
|
||||
|
||||
@@ -5303,6 +5303,7 @@
|
||||
#include "code\modules\mob\living\silicon\ai\_preferences.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai_defense.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai_defines.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai_portrait_picker.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai_say.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\death.dm"
|
||||
|
||||
Reference in New Issue
Block a user