Files
CHOMPStation2/code/modules/mob/living/silicon/ai/ai.dm
2025-11-05 18:56:44 -05:00

1042 lines
35 KiB
Plaintext

#define AI_CHECK_WIRELESS 1
#define AI_CHECK_RADIO 2
var/list/ai_verbs_default = list(
// /mob/living/silicon/ai/proc/ai_recall_shuttle,
/mob/living/silicon/ai/proc/ai_emergency_message,
/mob/living/silicon/ai/proc/ai_goto_location,
/mob/living/silicon/ai/proc/ai_remove_location,
/mob/living/silicon/ai/proc/ai_hologram_change,
/mob/living/silicon/ai/proc/ai_network_change,
/mob/living/silicon/ai/proc/ai_statuschange,
/mob/living/silicon/ai/proc/ai_store_location,
/mob/living/silicon/ai/proc/control_integrated_radio,
/mob/living/silicon/ai/proc/pick_icon,
/mob/living/silicon/ai/proc/sensor_mode,
/mob/living/silicon/ai/proc/show_laws_verb,
/mob/living/silicon/ai/proc/toggle_acceleration,
/mob/living/silicon/ai/proc/toggle_hologram_movement,
/mob/living/silicon/ai/proc/ai_announcement,
/mob/living/silicon/ai/proc/ai_call_shuttle,
/mob/living/silicon/ai/proc/ai_camera_track,
/mob/living/silicon/ai/proc/ai_camera_list,
/mob/living/silicon/ai/proc/ai_checklaws,
/mob/living/silicon/ai/proc/toggle_camera_light,
/mob/living/silicon/ai/proc/take_image,
/mob/living/silicon/ai/proc/view_images,
/mob/living/silicon/ai/proc/delete_images,
/mob/living/silicon/ai/proc/toggle_multicam_verb,
/mob/living/silicon/ai/proc/add_multicam_verb
)
//Not sure why this is necessary...
/proc/AutoUpdateAI(obj/subject)
var/is_in_use = 0
if (subject!=null)
for(var/mob/living/silicon/ai/M as anything in GLOB.ai_list)
if ((M.client && M.check_current_machine(subject)))
is_in_use = 1
subject.attack_ai(M)
return is_in_use
/mob/living/silicon/ai
name = JOB_AI
icon = 'icons/mob/AI.dmi'//
icon_state = "ai"
anchored = TRUE // -- TLE
density = TRUE
status_flags = CANSTUN|CANPARALYSE|CANPUSH
shouldnt_see = list(/mob/observer/eye, /obj/effect/rune)
var/list/network = list(NETWORK_DEFAULT)
var/obj/machinery/camera/camera = null
var/aiRestorePowerRoutine = 0
var/viewalerts = 0
var/icon/holo_icon //Default is assigned when AI is created.
var/holo_color = null
var/list/connected_robots = list()
var/obj/item/pda/ai/aiPDA = null
var/obj/item/communicator/aiCommunicator = null
var/obj/item/multitool/aiMulti = null
var/obj/item/radio/headset/heads/ai_integrated/aiRadio = null
var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through.
var/datum/trackable/track = null
var/last_announcement = ""
var/control_disabled = 0
var/datum/announcement/priority/announcement
var/obj/machinery/ai_powersupply/psupply = null // Backwards reference to AI's powersupply object.
var/hologram_follow = 1 //This is used for the AI eye, to determine if a holopad's hologram should follow it or not.
var/is_dummy = 0 //Used to prevent dummy AIs from spawning with communicators.
//NEWMALF VARIABLES
var/malfunctioning = 0 // Master var that determines if AI is malfunctioning.
var/datum/malf_hardware/hardware = null // Installed piece of hardware.
var/datum/malf_research/research = null // Malfunction research datum.
var/obj/machinery/power/apc/hack = null // APC that is currently being hacked.
var/list/hacked_apcs = null // List of all hacked APCs
var/APU_power = 0 // If set to 1 AI runs on APU power
var/hacking = 0 // Set to 1 if AI is hacking APC, cyborg, other AI, or running system override.
var/system_override = 0 // Set to 1 if system override is initiated, 2 if succeeded.
var/hack_can_fail = 1 // If 0, all abilities have zero chance of failing.
var/hack_fails = 0 // This increments with each failed hack, and determines the warning message text.
var/errored = 0 // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit.
var/bombing_core = 0 // Set to 1 if core auto-destruct is activated
var/bombing_station = 0 // Set to 1 if station nuke auto-destruct is activated
var/override_CPUStorage = 0 // Bonus/Penalty CPU Storage. For use by admins/testers.
var/override_CPURate = 0 // Bonus/Penalty CPU generation rate. For use by admins/testers.
var/datum/ai_icon/selected_sprite // The selected icon set
var/custom_sprite = 0 // Whether the selected icon is custom
var/carded
// Multicam Vars
var/multicam_allowed = TRUE
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
can_be_antagged = TRUE
/mob/living/silicon/ai/proc/add_ai_verbs()
add_verb(src, ai_verbs_default)
add_verb(src, silicon_subsystems)
/mob/living/silicon/ai/proc/remove_ai_verbs()
remove_verb(src, ai_verbs_default)
remove_verb(src, silicon_subsystems)
/mob/living/silicon/ai/Initialize(mapload, is_decoy, datum/ai_laws/L, obj/item/mmi/B, safety = FALSE)
announcement = new()
announcement.title = "A.I. Announcement"
announcement.announcement_type = "A.I. Announcement"
announcement.newscast = 1
var/list/possibleNames = GLOB.ai_names
var/pickedName = null
while(!pickedName)
pickedName = pick(GLOB.ai_names)
for (var/mob/living/silicon/ai/A in GLOB.mob_list)
if (A.real_name == pickedName && possibleNames.len > 1) //fixing the theoretically possible infinite loop
possibleNames -= pickedName
pickedName = null
if(!is_dummy)
aiPDA = new/obj/item/pda/ai(src)
SetName(pickedName)
anchored = TRUE
canmove = 0
density = TRUE
if(!is_dummy)
aiCommunicator = new /obj/item/communicator/integrated(src)
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1"))
proc_holder_list = new()
if(L)
if (istype(L, /datum/ai_laws))
laws = L
else
laws = new using_map.default_law_type
aiMulti = new(src)
aiRadio = new(src)
common_radio = aiRadio
aiRadio.myAi = src
additional_law_channels["Binary"] = "#b"
additional_law_channels["Holopad"] = ":h"
aiCamera = new/obj/item/camera/siliconcam/ai_camera(src)
if (istype(loc, /turf))
add_ai_verbs(src)
//Languages
add_language(LANGUAGE_ROBOT_TALK, 1)
add_language(LANGUAGE_GALCOM, 1)
add_language(LANGUAGE_SOL_COMMON, 1)
add_language(LANGUAGE_UNATHI, 1)
add_language(LANGUAGE_SIIK, 1)
add_language(LANGUAGE_AKHANI, 1)
add_language(LANGUAGE_SKRELLIAN, 1)
add_language(LANGUAGE_TRADEBAND, 1)
add_language(LANGUAGE_GUTTER, 1)
add_language(LANGUAGE_EAL, 1)
add_language(LANGUAGE_SCHECHI, 1)
add_language(LANGUAGE_SIGN, 1)
add_language(LANGUAGE_ROOTLOCAL, 1)
add_language(LANGUAGE_TERMINUS, 1)
add_language(LANGUAGE_ZADDAT, 1)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if (!B)//If there is no player/brain inside.
GLOB.empty_playable_ai_cores += new/obj/structure/AIcore/deactivated(loc)//New empty terminal.
return INITIALIZE_HINT_QDEL //Delete AI.
if (B.brainmob.mind)
B.brainmob.mind.transfer_to(src)
on_mob_init()
GLOB.ai_list += src
. = ..()
new /obj/machinery/ai_powersupply(src)
if(CONFIG_GET(flag/allow_ai_shells))
add_verb(src, /mob/living/silicon/ai/proc/deploy_to_shell_act)
create_eyeobj()
if(eyeobj)
eyeobj.loc = src.loc
/mob/living/silicon/ai/proc/on_mob_init()
var/init_text = list(span_bold("You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras)."),
span_bold("To look at other parts of the station, click on yourself to get a camera menu."),
span_bold("While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc."),
"To use something, simply click on it.",
"For department channels, use the following say commands:")
to_chat(src, span_filter_notice("[jointext(init_text, "<br>")]"))
var/radio_text = ""
for(var/i = 1 to common_radio.channels.len)
var/channel = common_radio.channels[i]
var/key = get_radio_key_from_channel(channel)
radio_text += "[key] - [channel]"
if(i != common_radio.channels.len)
radio_text += ", "
to_chat(src,radio_text)
// Vorestation Edit: Meta Info for AI's. Mostly used for Holograms
if (client)
ooc_notes = client.prefs.read_preference(/datum/preference/text/living/ooc_notes)
ooc_notes_likes = client.prefs.read_preference(/datum/preference/text/living/ooc_notes_likes)
ooc_notes_dislikes = client.prefs.read_preference(/datum/preference/text/living/ooc_notes_dislikes)
ooc_notes_favs = read_preference(/datum/preference/text/living/ooc_notes_favs)
ooc_notes_maybes = read_preference(/datum/preference/text/living/ooc_notes_maybes)
ooc_notes_style = read_preference(/datum/preference/toggle/living/ooc_notes_style)
private_notes = client.prefs.read_preference(/datum/preference/text/living/private_notes)
if (malf && !(mind in malf.current_antagonists))
show_laws()
to_chat(src, span_filter_notice(span_bold("These laws may be changed by other players, or by you being the traitor.")))
job = JOB_AI
setup_icon()
/mob/living/silicon/ai/Destroy()
GLOB.ai_list -= src
QDEL_NULL(announcement)
QDEL_NULL(eyeobj)
QDEL_NULL(psupply)
QDEL_NULL(aiPDA)
QDEL_NULL(aiCommunicator)
QDEL_NULL(aiMulti)
QDEL_NULL(aiRadio)
hack = null
destroy_eyeobj()
return ..()
/mob/living/silicon/ai/get_status_tab_items()
. = ..()
. += ""
if(!stat) // Make sure we're not unconscious/dead.
. += "System integrity: [(health+100)/2]%"
. += "Connected synthetics: [connected_robots.len]"
for(var/mob/living/silicon/robot/R in connected_robots)
var/robot_status = "Nominal"
if(R.shell)
robot_status = "AI SHELL"
else if(R.stat || !R.client)
robot_status = "OFFLINE"
else if(!R.cell || R.cell.charge <= 0)
robot_status = "DEPOWERED"
//Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
. += "[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
Module: [R.modtype] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"
. += "AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]" //Count of total AI shells
else
. += "Systems nonfunctional"
/mob/living/silicon/ai/proc/setup_icon()
var/file = file2text("config/custom_sprites.txt")
var/lines = splittext(file, "\n")
for(var/line in lines)
// split & clean up
var/list/Entry = splittext(line, ":")
for(var/i = 1 to Entry.len)
Entry[i] = trim(Entry[i])
if(Entry.len < 2)
continue;
if(Entry[1] == src.ckey && Entry[2] == src.real_name)
icon = CUSTOM_ITEM_SYNTH
custom_sprite = 1
selected_sprite = new/datum/ai_icon("Custom", "[src.ckey]-ai", "4", "[ckey]-ai-crash", "#FFFFFF", "#FFFFFF", "#FFFFFF")
else
selected_sprite = default_ai_icon
update_icon()
/mob/living/silicon/ai/pointed(atom/A as mob|obj|turf in view())
set popup_menu = 0
set src = usr.contents
return 0
/mob/living/silicon/ai/SetName(pickedName as text)
..()
announcement.announcer = pickedName
if(eyeobj)
eyeobj.name = "[pickedName] (AI Eye)"
// Set ai pda name
if(aiPDA)
aiPDA.ownjob = JOB_AI
aiPDA.owner = pickedName
aiPDA.name = pickedName + " (" + aiPDA.ownjob + ")"
if(aiCommunicator)
aiCommunicator.register_device(src.name)
/*
The AI Power supply is a dummy object used for powering the AI since only machinery should be using power.
The alternative was to rewrite a bunch of AI code instead here we are.
*/
/obj/machinery/ai_powersupply
name="Power Supply"
active_power_usage=50000 // Station AIs use significant amounts of power. This, when combined with charged SMES should mean AI lasts for 1hr without external power.
use_power = USE_POWER_ACTIVE
power_channel = EQUIP
var/mob/living/silicon/ai/powered_ai = null
invisibility = INVISIBILITY_MAXIMUM
/obj/machinery/ai_powersupply/Initialize(mapload)
. = ..()
powered_ai = loc
if(!istype(powered_ai))
return INITIALIZE_HINT_QDEL
powered_ai.psupply = src
if(istype(powered_ai,/mob/living/silicon/ai/announcer)) //Don't try to get a loc for a nullspace announcer mob, just put it into it
forceMove(powered_ai)
else
forceMove(powered_ai.loc)
use_power(1) // Just incase we need to wake up the power system.
/obj/machinery/ai_powersupply/Destroy()
. = ..()
powered_ai = null
/obj/machinery/ai_powersupply/process()
if(!powered_ai || powered_ai.stat == DEAD)
qdel(src)
return
if(powered_ai.psupply != src) // For some reason, the AI has different powersupply object. Delete this one, it's no longer needed.
qdel(src)
return
if(powered_ai.APU_power)
update_use_power(USE_POWER_OFF)
return
if(!powered_ai.anchored)
loc = powered_ai.loc
update_use_power(USE_POWER_OFF)
use_power(50000) // Less optimalised but only called if AI is unwrenched. This prevents usage of wrenching as method to keep AI operational without power. Intellicard is for that.
if(powered_ai.anchored)
update_use_power(USE_POWER_ACTIVE)
/mob/living/silicon/ai/proc/pick_icon()
set category = "AI.Settings"
set name = "Set AI Core Display"
if(stat || aiRestorePowerRoutine)
return
if (!custom_sprite)
var/new_sprite = tgui_input_list(src, "Select an icon!", "AI", ai_icons)
if(new_sprite) selected_sprite = new_sprite
update_icon()
/mob/living/silicon/ai/var/message_cooldown = 0
/mob/living/silicon/ai/proc/ai_announcement()
set category = "AI.Station Commands"
set name = "Make Station Announcement"
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
if(message_cooldown)
to_chat(src, span_filter_notice("Please allow one minute to pass between announcements."))
return
var/input = tgui_input_text(src, "Please write a message to announce to the station crew.", "A.I. Announcement")
if(!input)
return
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
announcement.Announce(input)
message_cooldown = 1
spawn(600)//One minute cooldown
message_cooldown = 0
/mob/living/silicon/ai/proc/ai_call_shuttle()
set category = "AI.Station Commands"
set name = "Call Emergency Shuttle"
if(check_unable(AI_CHECK_WIRELESS))
return
var/confirm = tgui_alert(src, "Are you sure you want to call the shuttle?", "Confirm Shuttle Call", list("Yes", "No"))
if(!confirm)
return
if(check_unable(AI_CHECK_WIRELESS))
return
if(confirm == "Yes")
call_shuttle_proc(src)
// hack to display shuttle timer
if(emergency_shuttle.online())
post_status(src, "shuttle", user = src)
/mob/living/silicon/ai/proc/ai_recall_shuttle()
set category = "AI.Station Commands"
set name = "Recall Emergency Shuttle"
if(check_unable(AI_CHECK_WIRELESS))
return
var/confirm = tgui_alert(src, "Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", list("Yes", "No"))
if(check_unable(AI_CHECK_WIRELESS))
return
if(confirm == "Yes")
cancel_call_proc(src)
/mob/living/silicon/ai/var/emergency_message_cooldown = 0
/mob/living/silicon/ai/proc/ai_emergency_message()
set category = "AI.Station Commands"
set name = "Send Emergency Message"
if(check_unable(AI_CHECK_WIRELESS))
return
if(emergency_message_cooldown)
to_chat(src, span_warning("Arrays recycling. Please stand by."))
return
var/input = tgui_input_text(src, "Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "", MAX_MESSAGE_LEN)
if(!input)
return
CentCom_announce(input, src)
to_chat(src, span_notice("Message transmitted."))
log_game("[key_name(src)] has made an IA [using_map.boss_short] announcement: [input]")
emergency_message_cooldown = 1
spawn(300)
emergency_message_cooldown = 0
/mob/living/silicon/ai/restrained()
return 0
/mob/living/silicon/ai/emp_act(severity)
disconnect_shell("Disconnected from remote shell due to ionic interfe%*@$^___")
if (prob(30))
view_core()
..()
/mob/living/silicon/ai/Topic(href, href_list)
if(..()) //VOREstation edit: So the AI can actually can actually get its OOC prefs read
return
if(usr != src)
return
/*if(..()) // <------ MOVED FROM HERE
return*/
if (href_list["mach_close"])
if (href_list["mach_close"] == "aialerts")
viewalerts = 0
var/t1 = text("window=[]", href_list["mach_close"])
unset_machine()
src << browse(null, t1)
if (href_list["switchcamera"])
switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
if (href_list["showalerts"])
subsystem_alarm_monitor()
//Carn: holopad requests
if (href_list["jumptoholopad"])
var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"])
if(stat == CONSCIOUS)
if(H)
H.attack_ai(src) //may as well recycle
else
to_chat(src, span_notice("Unable to locate the holopad."))
if (href_list["track"])
var/mob/target = locate(href_list["track"]) in GLOB.mob_list
if(target && (!ishuman(target) || html_decode(href_list["trackname"]) == target:get_face_name()))
ai_actual_track(target)
else
to_chat(src, span_filter_warning("[span_red("System error. Cannot locate [html_decode(href_list["trackname"])].")]"))
return
if(href_list["trackbot"])
var/mob/living/bot/target = locate(href_list["trackbot"]) in GLOB.mob_list
if(target)
ai_actual_track(target)
else
to_chat(src, span_warning("Target is not on or near any active cameras on the station."))
return
if(href_list["open"])
var/mob/target = locate(href_list["open"]) in GLOB.mob_list
if(target)
open_nearest_door(target)
return
/mob/living/silicon/ai/proc/camera_visibility(mob/observer/eye/aiEye/moved_eye)
cameranet.visibility(moved_eye, client, all_eyes)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
if(.)
end_multicam()
/mob/living/silicon/ai/reset_perspective(atom/new_eye)
if(camera)
camera.set_light(0)
if(istype(new_eye,/obj/machinery/camera))
camera = new_eye
if(new_eye != GLOB.ai_camera_room_landmark)
end_multicam()
. = ..()
if(.)
if(!new_eye && isturf(loc) && eyeobj)
end_multicam()
reset_perspective(eyeobj)
if(istype(new_eye,/obj/machinery/camera))
if(camera_light_on)
new_eye.set_light(AI_CAMERA_LUMINOSITY)
else
new_eye.set_light(0)
/mob/living/silicon/ai/proc/switchCamera(var/obj/machinery/camera/C)
if (!C || stat == DEAD) //C.can_use())
return 0
if(!src.eyeobj)
view_core()
return
// ok, we're alive, camera is good and in our network...
eyeobj.setLoc(get_turf(C))
//machine = src
return 1
/mob/living/silicon/ai/cancel_camera()
set category = "AI.Camera Control"
set name = "Cancel Camera View"
view_core()
//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm
//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead
//Addition by Mord_Sith to define AI's network change ability
/mob/living/silicon/ai/proc/get_camera_network_list()
if(check_unable())
return
var/list/cameralist = new()
for (var/obj/machinery/camera/C in cameranet.cameras)
if(!C.can_use())
continue
var/list/tempnetwork = difflist(C.network,restricted_camera_networks,1)
for(var/i in tempnetwork)
cameralist[i] = i
cameralist = sortAssoc(cameralist)
return cameralist
/mob/living/silicon/ai/proc/ai_network_change(var/network in get_camera_network_list())
set category = "AI.Camera Control"
set name = "Jump To Network"
unset_machine()
if(!network)
return
if(!eyeobj)
view_core()
return
src.network = network
for(var/obj/machinery/camera/C in cameranet.cameras)
if(!C.can_use())
continue
if(network in C.network)
eyeobj.setLoc(get_turf(C))
break
to_chat(src, span_notice("Switched to [network] camera network."))
//End of code by Mord_Sith
/mob/living/silicon/ai/proc/ai_statuschange()
set category = "AI.Settings"
set name = "AI Status"
if(check_unable(AI_CHECK_WIRELESS))
return
set_ai_status_displays(src)
return
//I am the icon meister. Bow fefore me. //>fefore
/mob/living/silicon/ai/proc/ai_hologram_change()
set name = "Change Hologram"
set desc = "Change the default hologram available to AI to something else."
set category = "AI.Settings"
if(check_unable())
return
var/input
var/choice
choice = tgui_alert(src, "Would you like to modify your hologram's model, or color?", "Modify Hologram", list("Model","Color","Cancel"))
if(!choice || choice == "Cancel")
return
switch(choice)
if("Color")
input = tgui_color_picker(src, "Choose a color:", "Hologram Color", holo_color)
if(input)
holo_color = input
if("Model")
choice = tgui_alert(src, "Would you like to select a hologram based on a (visible) crew member, switch to unique avatar, or load your character from your character slot?","Hologram Selection",list("Crew Member","Unique","My Character"))
if(!choice)
return
switch(choice)
if("Crew Member") //A seeable crew member (or a dog)
var/list/targets = trackable_mobs()
if(targets.len)
input = tgui_input_list(src, "Select a crew member:", "Hologram Choice", targets) //The definition of "crew member" is a little loose...
//This is torture, I know. If someone knows a better way...
if(!input) return
var/new_holo = getHologramIcon(getCompoundIcon(targets[input]))
qdel(holo_icon)
holo_icon = new_holo
else
tgui_alert_async(src, "No suitable records found. Aborting.")
if("My Character") //Loaded character slot
if(!client || !client.prefs) return
var/mob/living/carbon/human/dummy/dummy = new ()
//This doesn't include custom_items because that's ... hard.
client.prefs.dress_preview_mob(dummy)
sleep(1 SECOND) //Strange bug in preview code? Without this, certain things won't show up. Yay race conditions?
dummy.regenerate_icons()
var/new_holo = getHologramIcon(getCompoundIcon(dummy))
qdel(holo_icon)
qdel(dummy)
holo_icon = new_holo
else //A premade from the dmi
var/icon_list[] = list(
"default",
"floating face",
"singularity",
"drone",
"carp",
"spider",
"bear",
"fox", // CHOMPStation Edit: Fox holograms!
"fox, alt", // CHOMPStation Edit: Fox holograms!
"syndifox", // CHOMPStation Edit: Fox holograms!
"slime",
"ian",
"runtime",
"poly",
"pun pun",
"male human",
"female human",
"male unathi",
"female unathi",
"male tajaran",
"female tajaran",
"male tesharii",
"female tesharii",
"male skrell",
"female skrell"
)
input = tgui_input_list(src, "Please select a hologram:", "Hologram Choice", icon_list)
if(input)
qdel(holo_icon)
switch(input)
if("default")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1"))
if("floating face")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo2"))
if("singularity")
holo_icon = getHologramIcon(icon('icons/obj/singularity.dmi',"singularity_s1"))
if("drone")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"drone"))
if("carp")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo4"))
if("spider")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"nurse"))
if("bear")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"brownbear"))
if("slime")
holo_icon = getHologramIcon(icon('icons/mob/slimes.dmi',"cerulean adult slime"))
if("ian")
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"corgi"))
if("runtime")
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"cat"))
if("poly")
holo_icon = getHologramIcon(icon('icons/mob/birds.dmi',"poly-flap"))
if("pun pun")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"punpun"))
if("male human")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holohumm"))
if("female human")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holohumf"))
if("male unathi")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holounam"))
if("female unathi")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holounaf"))
if("male tajaran")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotajm"))
if("female tajaran")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotajf"))
if("male tesharii")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotesm"))
if("female tesharii")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotesf"))
if("male skrell")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holoskrm"))
if("female skrell")
holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holoskrf"))
if("fox") // CHOMPStation Edit: Fox holograms!
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"fox")) // CHOMPStation Edit: Fox holograms!
if("syndifox") // CHOMPStation Edit: Fox holograms!
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"syndifox")) // CHOMPStation Edit: Fox holograms!
if("fox, alt") // CHOMPStation Edit: Fox holograms!
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"fox2")) // CHOMPStation Edit: Fox holograms!
//Toggles the luminosity and applies it by re-entereing the camera.
/mob/living/silicon/ai/proc/toggle_camera_light()
set name = "Toggle Camera Light"
set desc = "Toggles the light on the camera the AI is looking through."
set category = "AI.Camera Control"
if(check_unable())
return
camera_light_on = !camera_light_on
to_chat(src, span_filter_notice("Camera lights [camera_light_on ? "activated" : "deactivated"]."))
if(!camera_light_on)
if(camera)
camera.set_light(0)
camera = null
else
lightNearbyCamera()
// Handled camera lighting, when toggled.
// It will get the nearest camera from the eyeobj, lighting it.
/mob/living/silicon/ai/proc/lightNearbyCamera()
if(camera_light_on && camera_light_on < world.timeofday)
if(src.camera)
var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
if(camera && src.camera != camera)
src.camera.set_light(0)
if(!camera.light_disabled)
src.camera = camera
src.camera.set_light(AI_CAMERA_LUMINOSITY)
else
src.camera = null
else if(isnull(camera))
src.camera.set_light(0)
src.camera = null
else
var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
if(camera && !camera.light_disabled)
src.camera = camera
src.camera.set_light(AI_CAMERA_LUMINOSITY)
camera_light_on = world.timeofday + 1 * 20 // Update the light every 2 seconds.
/mob/living/silicon/ai/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/aicard))
var/obj/item/aicard/card = W
card.grab_ai(src, user)
else if(W.has_tool_quality(TOOL_WRENCH))
if(user == deployed_shell)
to_chat(user, span_notice("The shell's subsystems resist your efforts to tamper with your bolts."))
return
if(anchored)
playsound(src, W.usesound, 50, 1)
user.visible_message(span_notice("\The [user] starts to unbolt \the [src] from the plating..."))
if(!do_after(user, 4 SECONDS * W.toolspeed, target = src))
user.visible_message(span_notice("\The [user] decides not to unbolt \the [src]."))
return
user.visible_message(span_notice("\The [user] finishes unfastening \the [src]!"))
anchored = FALSE
return
else
playsound(src, W.usesound, 50, 1)
user.visible_message(span_notice("\The [user] starts to bolt \the [src] to the plating..."))
if(!do_after(user, 4 SECONDS * W.toolspeed, target = src))
user.visible_message(span_notice("\The [user] decides not to bolt \the [src]."))
return
user.visible_message(span_notice("\The [user] finishes fastening down \the [src]!"))
anchored = TRUE
return
else
return ..()
/mob/living/silicon/ai/proc/control_integrated_radio()
set name = "Radio Settings"
set desc = "Allows you to change settings of your radio."
set category = "AI.Settings"
if(check_unable(AI_CHECK_RADIO))
return
to_chat(src, span_filter_notice("Accessing Subspace Transceiver control..."))
if (src.aiRadio)
src.aiRadio.interact(src)
/mob/living/silicon/ai/proc/sensor_mode()
set name = "Toggle Sensor Augmentation" //VOREStation Add
set category = "AI.Settings"
set desc = "Augment visual feed with internal sensor overlays"
sensor_type = !sensor_type //VOREStation Add
to_chat(src, "You [sensor_type ? "enable" : "disable"] your sensors.") //VOREStation Add
toggle_sensor_mode()
/mob/living/silicon/ai/proc/toggle_hologram_movement()
set name = "Toggle Hologram Movement"
set category = "AI.Settings"
set desc = "Toggles hologram movement based on moving with your virtual eye."
hologram_follow = !hologram_follow
//VOREStation Add - Required to stop movement because we use walk_to(wards) in hologram.dm
if(holo)
var/obj/effect/overlay/aiholo/hologram = holo.masters[src]
walk(hologram, 0)
//VOREStation Add End
to_chat(src, span_filter_notice("Your hologram will [hologram_follow ? "follow" : "no longer follow"] you now."))
/mob/living/silicon/ai/proc/check_unable(var/flags = NONE, var/feedback = 1)
if(stat == DEAD)
if(feedback)
to_chat(src, span_warning("You are dead!"))
return 1
if(aiRestorePowerRoutine)
if(feedback)
to_chat(src, span_warning("You lack power!"))
return 1
if((flags & AI_CHECK_WIRELESS) && src.control_disabled)
if(feedback)
to_chat(src, span_warning("Wireless control is disabled!"))
return 1
if((flags & AI_CHECK_RADIO) && src.aiRadio.disabledAi)
if(feedback)
to_chat(src, span_warning("System Error - Transceiver Disabled!"))
return 1
return 0
/mob/living/silicon/ai/proc/is_in_chassis()
return istype(loc, /turf)
/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) // Rykka ports AI opening doors
if(!istype(target))
return
if(target && ai_actual_track(target))
var/obj/machinery/door/airlock/A = null
var/dist = -1
for(var/obj/machinery/door/airlock/D in range(3, target))
if(!D.density)
continue
var/curr_dist = get_dist(D, target)
if(dist < 0)
dist = curr_dist
A = D
else if(dist > curr_dist)
dist = curr_dist
A = D
if(istype(A))
switch(tgui_alert(src, "Do you want to open \the [A] for [target]?", "Doorknob_v2a.exe", list("Yes", "No")))
if("Yes")
A.AIShiftClick(src)
to_chat(src, span_notice("You open \the [A] for [target]."))
else
to_chat(src, span_warning("You deny the request."))
else
to_chat(src, span_warning("Unable to locate an airlock near [target]."))
else
to_chat(src, span_warning("Target is not on or near any active cameras on the station."))
/mob/living/silicon/ai/ex_act(var/severity)
if(severity == 1.0)
qdel(src)
return
..()
/mob/living/silicon/ai/update_icon()
if(!selected_sprite) selected_sprite = default_ai_icon
if(stat == DEAD)
icon_state = selected_sprite.dead_icon
set_light(3, 1, selected_sprite.dead_light)
else if(aiRestorePowerRoutine)
icon_state = selected_sprite.nopower_icon
set_light(1, 1, selected_sprite.nopower_light)
else
icon_state = selected_sprite.alive_icon
set_light(1, 1, selected_sprite.alive_light)
// Pass lying down or getting up to our pet human, if we're in a rig.
/mob/living/silicon/ai/lay_down()
set name = "Rest"
set category = "IC.Game"
resting = 0
var/obj/item/rig/rig = src.get_rig()
if(rig)
rig.force_rest(src)
/mob/living/silicon/ai/is_sentient()
// AI cores don't store what brain was used to build them so we're just gonna assume they can think to some degree.
// If that is ever fixed please update this proc.
return TRUE
/mob/living/silicon/ai/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear)
if(hard_to_hear)
return
var/jobname // the mob's "job"
var/mob/living/carbon/human/impersonating //The crew member being impersonated, if any.
var/changed_voice
if(ishuman(speaker))
var/mob/living/carbon/human/H = speaker
if(H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice))
changed_voice = 1
var/list/impersonated = new()
var/mob/living/carbon/human/I = impersonated[speaker_name]
if(!I)
for(var/mob/living/carbon/human/M in GLOB.mob_list)
if(M.real_name == speaker_name)
I = M
impersonated[speaker_name] = I
break
// If I's display name is currently different from the voice name and using an agent ID then don't impersonate
// as this would allow the AI to track I and realize the mismatch.
if(I && !(I.name != speaker_name && I.wear_id && istype(I.wear_id,/obj/item/card/id/syndicate)))
impersonating = I
jobname = impersonating.get_assignment()
else
jobname = "Unknown"
else
jobname = H.get_assignment()
else if(iscarbon(speaker)) // Nonhuman carbon mob
jobname = "No id"
else if(isAI(speaker))
jobname = JOB_AI
else if(isrobot(speaker))
jobname = JOB_CYBORG
else if(ispAI(speaker))
jobname = "Personal AI"
else
jobname = "Unknown"
var/track = ""
if(changed_voice) // They have a fake name
if(impersonating) // And we found a mob with that name above, track them instead
track = "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];track=\ref[impersonating]'>[speaker_name] ([jobname])</a>"
track += "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];open=\ref[impersonating]'>\[OPEN\]</a>" // Rykka ports AI opening doors
else // We couldn't find a mob with their fake name, don't track at all
track = "[speaker_name] ([jobname])"
else // Not faking their name
if(isbot(speaker)) // It's a bot, and no fake name! (That'd be kinda weird.) :p
track = "<a href='byond://?src=\ref[src];trackbot=\ref[speaker]'>[speaker_name] ([jobname])</a>"
else // It's not a bot, and no fake name!
track = "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];track=\ref[speaker]'>[speaker_name] ([jobname])</a>"
track += "<a href='byond://?src=\ref[src];trackname=[html_encode(speaker_name)];open=\ref[speaker]'>\[OPEN\]</a>" // Rykka ports AI opening doors
return track // Feed variable back to AI
/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb)
var/list/combined = combine_message(message_pieces, verb, M)
var/message = combined["formatted"]
var/name_used = M.GetVoice()
//This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
var/rendered = span_game(span_say(span_italics("Relayed Speech: [span_name(name_used)] [message]")))
show_message(rendered, 2)
/mob/living/silicon/ai/proc/toggle_multicam_verb()
set name = "Toggle Multicam"
set category = "AI.Camera Control"
toggle_multicam()
/mob/living/silicon/ai/proc/add_multicam_verb()
set name = "Add Multicam Viewport"
set category = "AI.Camera Control"
drop_new_multicam()
//Special subtype kept around for global announcements
/mob/living/silicon/ai/announcer
is_dummy = 1
/mob/living/silicon/ai/announcer/Initialize(mapload)
. = ..()
GLOB.mob_list -= src
GLOB.living_mob_list -= src
GLOB.dead_mob_list -= src
GLOB.ai_list -= src
GLOB.silicon_mob_list -= src
QDEL_NULL(eyeobj)
/mob/living/silicon/ai/announcer/Life()
GLOB.mob_list -= src
GLOB.living_mob_list -= src
GLOB.dead_mob_list -= src
GLOB.ai_list -= src
GLOB.silicon_mob_list -= src
QDEL_NULL(eyeobj)
#undef AI_CHECK_WIRELESS
#undef AI_CHECK_RADIO