Files

1550 lines
49 KiB
Plaintext

GLOBAL_LIST_EMPTY(ai_list)
GLOBAL_LIST_INIT(ai_verbs_default, list(
/mob/living/silicon/ai/proc/announcement,
/mob/living/silicon/ai/proc/ai_announcement_text,
/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_hologram_change,
/mob/living/silicon/ai/proc/ai_network_change,
/mob/living/silicon/ai/proc/ai_roster,
/mob/living/silicon/ai/proc/ai_statuschange,
/mob/living/silicon/ai/proc/control_integrated_radio,
/mob/living/silicon/ai/proc/core,
/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_camera_light,
/mob/living/silicon/ai/proc/botcall,
/mob/living/silicon/ai/proc/change_arrival_message,
/mob/living/silicon/ai/proc/arrivals_announcement
))
//Not sure why this is necessary...
/proc/AutoUpdateAI(obj/subject)
var/is_in_use = 0
if(subject!=null)
for(var/A in GLOB.ai_list)
var/mob/living/silicon/ai/M = A
if(M.client && M.machine == subject)
is_in_use = 1
subject.attack_ai(M)
return is_in_use
#define TEXT_ANNOUNCEMENT_COOLDOWN 1 MINUTES
/mob/living/silicon/ai
name = "AI"
icon = 'icons/mob/ai.dmi'//
icon_state = "ai"
move_resist = MOVE_FORCE_NORMAL
density = TRUE
status_flags = CANSTUN|CANPARALYSE|CANPUSH
d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED
mob_size = MOB_SIZE_LARGE
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
see_invisible = SEE_INVISIBLE_LIVING_AI
see_in_dark = 8
hud_type = /datum/hud/ai
hat_offset_y = 3
is_centered = TRUE
can_be_hatted = TRUE
var/list/network = list("SS13","Telecomms","Research Outpost","Mining Outpost")
var/obj/machinery/camera/current = null
var/list/connected_robots = list()
var/aiRestorePowerRoutine = 0
//var/list/laws = list()
alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Burglar")
var/viewalerts = FALSE
var/icon/holo_icon //Default is assigned when AI is created.
var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
var/obj/item/pda/silicon/ai/aiPDA = null
var/obj/item/multitool/aiMulti = null
var/custom_sprite = FALSE //For our custom sprites
var/custom_hologram = FALSE //For our custom holograms
var/obj/item/radio/headset/heads/ai_integrated/aiRadio = null
//MALFUNCTION
var/datum/module_picker/malf_picker
var/datum/spell/ai_spell/choose_modules/modules_action
var/list/datum/AI_Module/current_modules = list()
var/can_dominate_mechs = FALSE
var/shunted = FALSE // TRUE if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead
var/control_disabled = FALSE // Set to TRUE to stop AI from interacting via Click() -- TLE
var/malfhacking = null // A timer for when malf AIs can hack and get new cyborgs
var/malf_cooldown = 0 //Cooldown var for malf modules, stores a worldtime + cooldown
var/obj/machinery/power/apc/malfhack = null
var/explosive = 0 //does the AI explode when it dies?
///Whether or not the AI has upgraded their turrets
var/turrets_upgraded = FALSE
/// List of modules the AI has purchased malf upgrades for.
var/list/purchased_modules = list()
var/mob/living/silicon/ai/parent = null
var/camera_light_on = FALSE
var/list/obj/machinery/camera/lit_cameras = list()
var/datum/trackable/track = new()
var/last_paper_seen = null
var/can_shunt = TRUE
var/last_announcement = ""
var/datum/announcer/announcer
var/mob/living/simple_animal/bot/Bot
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/apc_override = FALSE //hack for letting the AI use its APC even when visionless
var/nuking = FALSE
var/obj/machinery/doomsday_device/doomsday_device
var/obj/machinery/hologram/holopad/holo = null
var/mob/camera/aiEye/eyeobj
var/sprint = 10
var/cooldown = 0
var/acceleration = 1
var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
/// If true, this AI core can use the teleporter.
var/allow_teleporter = FALSE
var/obj/machinery/camera/portable/builtInCamera
var/obj/structure/AIcore/deactivated/linked_core //For exosuit control
/// If our AI doesn't want to be the arrivals announcer, this gets set to FALSE.
var/announce_arrivals = TRUE
var/arrivalmsg = "$name, $rank, has arrived on the station."
var/list/all_eyes = list()
var/next_text_announcement
//Used with the hotkeys on 2-5 to store locations.
var/list/stored_locations = list()
/// Set to true if the AI cracks it's camera by using the malf ability
var/cracked_camera = FALSE
silicon_subsystems = list(
/mob/living/silicon/proc/subsystem_atmos_control,
/mob/living/silicon/proc/subsystem_crew_monitor,
/mob/living/silicon/proc/subsystem_law_manager,
/mob/living/silicon/proc/subsystem_power_monitor)
/// The cached AI annoucement help menu.
var/ai_announcement_string_menu
/mob/living/silicon/ai/proc/add_ai_verbs()
add_verb(src, GLOB.ai_verbs_default)
add_verb(src, silicon_subsystems)
/mob/living/silicon/ai/proc/remove_ai_verbs()
remove_verb(src, GLOB.ai_verbs_default)
remove_verb(src, silicon_subsystems)
/mob/living/silicon/ai/New(loc, datum/ai_laws/L, obj/item/mmi/B, safety = 0)
announcer = new(config_type = /datum/announcement_configuration/ai)
announcer.author = name
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 && length(possibleNames) > 1) //fixing the theoretically possible infinite loop
possibleNames -= pickedName
pickedName = null
aiPDA = new/obj/item/pda/silicon/ai(src)
rename_character(null, pickedName)
anchored = TRUE
density = TRUE
loc = loc
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1"))
proc_holder_list = new()
if(L)
if(istype(L, /datum/ai_laws))
laws = L
else
make_laws()
add_verb(src, /mob/living/silicon/ai/proc/show_laws_verb)
aiMulti = new(src)
aiRadio = new(src)
aiRadio.myAi = src
additional_law_channels["Binary"] = ":b "
additional_law_channels["Holopad"] = ":h"
aiCamera = new/obj/item/camera/siliconcam/ai_camera(src)
if(isturf(loc))
add_ai_verbs(src)
// Remove inherited verbs that effectively do nothing for AIs, or lead to unintended behaviour.
remove_verb(src, /mob/living/verb/rest)
remove_verb(src, /mob/living/verb/mob_sleep)
remove_verb(src, /mob/living/verb/stop_pulling1)
remove_verb(src, /mob/living/silicon/verb/pose)
remove_verb(src, /mob/living/silicon/verb/set_flavor)
//Languages
add_language("Robot Talk", 1)
add_language("Galactic Common", 1)
add_language("Sol Common", 1)
add_language("Tradeband", 1)
add_language("Neo-Russkiya", 1)
add_language("Gutter", 1)
add_language("Sinta'unathi", 1)
add_language("Siik'tajr", 1)
add_language("Canilunzt", 1)
add_language("Skrellian", 1)
add_language("Vox-pidgin", 1)
add_language("Orluum", 1)
add_language("Rootspeak", 1)
add_language("Trinary", 1)
add_language("Chittin", 1)
add_language("Bubblish", 1)
add_language("Clownish", 1)
add_language("Tkachi", 1)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if(!B)//If there is no player/brain inside.
new/obj/structure/AIcore/deactivated(loc)//New empty terminal.
qdel(src)//Delete AI.
return
else
if(B.brainmob.mind)
B.brainmob.mind.transfer_to(src)
on_mob_init()
spawn(5)
new /obj/machinery/ai_powersupply(src)
create_eye()
builtInCamera = new /obj/machinery/camera/portable(src)
builtInCamera.c_tag = name
builtInCamera.network = list("SS13")
GLOB.ai_list += src
GLOB.shuttle_caller_list += src
for(var/I in 1 to 4)
stored_locations += "unset" //This is checked in ai_keybinds.dm.
..()
/mob/living/silicon/ai/Initialize(mapload)
. = ..()
REMOVE_TRAIT(src, TRAIT_CAN_STRIP, TRAIT_GENERIC)
/mob/living/silicon/ai/Destroy()
GLOB.ai_list -= src
GLOB.shuttle_caller_list -= src
SSshuttle.autoEvac()
if(malfhacking)
deltimer(malfhacking)
malfhacking = null
QDEL_NULL(eyeobj) // No AI, no Eye
QDEL_NULL(aiPDA)
QDEL_NULL(aiMulti)
QDEL_NULL(aiRadio)
QDEL_NULL(builtInCamera)
return ..()
/mob/living/silicon/ai/get_radio()
return aiRadio
/mob/living/silicon/ai/proc/on_mob_init()
to_chat(src, "<B>You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).</B>")
to_chat(src, "<B>To look at other parts of the station, click on yourself to get a camera menu.</B>")
to_chat(src, "<B>While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.</B>")
to_chat(src, "To use something, simply click on it.")
to_chat(src, "Use say :b to speak to your cyborgs through binary. Use say :h to speak from an active holopad.")
to_chat(src, "For department channels, use the following say commands:")
var/radio_text = ""
for(var/i = 1 to length(aiRadio.channels))
var/channel = aiRadio.channels[i]
var/key = get_radio_key_from_channel(channel)
radio_text += "[key] - [channel]"
if(i != length(aiRadio.channels))
radio_text += ", "
to_chat(src, radio_text)
show_laws()
to_chat(src, "<b>These laws may be changed by other players, or by you being the traitor.</b>")
job = "AI"
/mob/living/silicon/ai/get_status_tab_items()
var/list/status_tab_data = ..()
. = status_tab_data
if(stat)
status_tab_data[++status_tab_data.len] = list("System status:", "Nonfunctional")
return
status_tab_data = show_borg_info(status_tab_data)
/mob/living/silicon/ai/proc/ai_alerts()
var/list/dat = list("<!DOCTYPE html><meta charset='utf-8'><head><title>Current Station Alerts</title><meta http-equiv='Refresh' content='10'></head><body>\n")
dat += "<a href='byond://?src=[UID()];mach_close=aialerts'>Close</a><br><br>"
var/list/list/temp_alarm_list = GLOB.alarm_manager.alarms.Copy()
for(var/cat in temp_alarm_list)
if(!(cat in alarms_listend_for))
continue
dat += "<b>[cat]</b><br>\n"
var/list/list/L = temp_alarm_list[cat].Copy()
for(var/alarm in L)
var/list/list/alm = L[alarm].Copy()
var/area_name = alm[1]
var/C = alm[2]
var/list/list/sources = alm[3].Copy()
for(var/thing in sources)
var/atom/A = locateUID(thing)
if(A && A.z != z)
L -= alarm
continue
dat += "<nobr>"
if(C && islist(C))
var/dat2 = ""
for(var/cam in C)
var/obj/machinery/camera/I = locateUID(cam)
if(!QDELETED(I))
dat2 += "[(dat2 == "") ? "" : " | "]<a href=byond://?src=[UID()];switchcamera=[cam]>[I.c_tag]</A>"
dat += "-- [area_name] ([(dat2 != "") ? dat2 : "No Camera"])"
else
dat += "-- [area_name] (No Camera)"
if(length(sources) > 1)
dat += "- [length(sources)] sources"
dat += "</nobr><br>\n"
if(!length(L))
dat += "-- All Systems Nominal<br>\n"
dat += "<br>\n"
viewalerts = TRUE
var/dat_text = dat.Join("")
src << browse(dat_text, "window=aialerts&can_close=0")
/mob/living/silicon/ai/proc/show_borg_info(list/status_tab_data)
status_tab_data[++status_tab_data.len] = list("Connected cyborg count:", "[length(connected_robots)]")
for(var/mob/living/silicon/robot/R in connected_robots)
var/robot_status = "Nominal"
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!
var/area/A = get_area(R)
var/area_name = A ? sanitize(A.name) : "Unknown"
status_tab_data[++status_tab_data.len] = list("[R.name]:", "S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge] / [R.cell.maxcharge]" : "Empty"] | \
Module: [R.designation] | Loc: [area_name] | Status: [robot_status]")
return status_tab_data
/mob/living/silicon/ai/rename_character(oldname, newname)
if(!..(oldname, newname))
return FALSE
if(oldname != real_name)
announcer.author = name
if(eyeobj)
eyeobj.name = "[newname] (AI Eye)"
// Set ai pda name
if(aiPDA)
aiPDA.set_name_and_job(newname, "AI")
return TRUE
/*
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="\improper AI power supply"
active_power_consumption = 1000
power_state = ACTIVE_POWER_USE
var/mob/living/silicon/ai/powered_ai = null
invisibility = 100
/obj/machinery/ai_powersupply/New(mob/living/silicon/ai/ai=null)
powered_ai = ai
if(isnull(powered_ai))
qdel(src)
return
loc = powered_ai.loc
use_power(1) // Just incase we need to wake up the power system.
..()
/obj/machinery/ai_powersupply/process()
if(!powered_ai || powered_ai.stat & DEAD)
qdel(src)
return
if(!powered_ai.anchored)
loc = powered_ai.loc
change_power_mode(NO_POWER_USE)
if(powered_ai.anchored)
change_power_mode(ACTIVE_POWER_USE)
/mob/living/silicon/ai/update_icons()
. = ..()
update_hat_icons()
/mob/living/silicon/ai/proc/pick_icon()
set category = "AI Commands"
set name = "Set AI Core Display"
if(stat || aiRestorePowerRoutine)
return
if(!custom_sprite) //Check to see if custom sprite time, checking the appopriate file to change a var
if(ckey in GLOB.configuration.custom_sprites.ai_core_ckeys)
custom_sprite = TRUE
var/display_choices = list(
"Monochrome",
"Blue",
"Clown",
"Inverted",
"Text",
"Smiley",
"Angry",
"Dorf",
"Matrix",
"Bliss",
"Firewall",
"Green",
"Red",
"Static",
"Triumvirate",
"Triumvirate Static",
"Red October",
"Sparkles",
"ANIMA",
"President",
"NT",
"NT2",
"Rainbow",
"Angel",
"Heartline",
"Hades",
"Helios",
"Syndicat Meow",
"Too Deep",
"Goon",
"Murica",
"Fuzzy",
"Glitchman",
"House",
"Database",
"Alien",
"Tiger",
"Fox",
"Vox",
"Lizard",
"Dark Matter",
"Cheese",
"Rainbow Slime",
"Void Donut",
"NAD Burn",
"Borb",
"Bee",
"Catamari"
)
if(custom_sprite)
display_choices += "Custom"
//if(icon_state == initial(icon_state))
var/icontype = ""
icontype = tgui_input_list(usr, "Select an icon!", "AI", display_choices)
icon = 'icons/mob/ai.dmi' //reset this in case we were on a custom sprite and want to change to a standard one
switch(icontype)
if("Custom")
icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' //set this here so we can use the custom_sprite
icon_state = "[ckey]-ai"
if("Clown")
icon_state = "ai-clown"
if("Monochrome")
icon_state = "ai-mono"
if("Inverted")
icon_state = "ai-u"
if("Firewall")
icon_state = "ai-magma"
if("Green")
icon_state = "ai-weird"
if("Red")
icon_state = "ai-red"
if("Static")
icon_state = "ai-static"
if("Text")
icon_state = "ai-text"
if("Smiley")
icon_state = "ai-smiley"
if("Matrix")
icon_state = "ai-matrix"
if("Angry")
icon_state = "ai-angryface"
if("Dorf")
icon_state = "ai-dorf"
if("Bliss")
icon_state = "ai-bliss"
if("Triumvirate")
icon_state = "ai-triumvirate"
if("Triumvirate Static")
icon_state = "ai-triumvirate-malf"
if("Red October")
icon_state = "ai-redoctober"
if("Sparkles")
icon_state = "ai-sparkles"
if("ANIMA")
icon_state = "ai-anima"
if("President")
icon_state = "ai-president"
if("NT")
icon_state = "ai-nt"
if("NT2")
icon_state = "ai-nanotrasen"
if("Rainbow")
icon_state = "ai-rainbow"
if("Angel")
icon_state = "ai-angel"
if("Heartline")
icon_state = "ai-heartline"
if("Hades")
icon_state = "ai-hades"
if("Helios")
icon_state = "ai-helios"
if("Syndicat Meow")
icon_state = "ai-syndicatmeow"
if("Too Deep")
icon_state = "ai-toodeep"
if("Goon")
icon_state = "ai-goon"
if("Murica")
icon_state = "ai-murica"
if("Fuzzy")
icon_state = "ai-fuzz"
if("Glitchman")
icon_state = "ai-glitchman"
if("House")
icon_state = "ai-house"
if("Database")
icon_state = "ai-database"
if("Alien")
icon_state = "ai-alien"
if("Tiger")
icon_state = "ai-tiger"
if("Fox")
icon_state = "ai-fox"
if("Vox")
icon_state = "ai-vox"
if("Lizard")
icon_state = "ai-liz"
if("Dark Matter")
icon_state = "ai-darkmatter"
if("Cheese")
icon_state = "ai-cheese"
if("Rainbow Slime")
icon_state = "ai-rainbowslime"
if("Void Donut")
icon_state = "ai-voiddonut"
if("NAD Burn")
icon_state = "ai-nadburn"
if("Borb")
icon_state = "ai-borb"
if("Bee")
icon_state = "ai-bee"
if("Catamari")
icon_state = "ai-catamari"
else
icon_state = "ai"
if(istype(loc, /obj/item/aicard/))
var/obj/item/aicard/AIC = loc
AIC.update_icon(UPDATE_OVERLAYS)
// this verb lets the ai see the stations manifest
/mob/living/silicon/ai/proc/ai_roster()
set name = "Show Crew Manifest"
set category = "AI Commands"
show_station_manifest()
/mob/living/silicon/ai/proc/ai_announcement_text()
set category = "AI Commands"
set name = "Make Station Announcement"
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
if(world.time < next_text_announcement)
to_chat(src, "<span class='warning'>Please allow one minute to pass between announcements.</span>")
return
var/input = tgui_input_text(usr, "Please write a message to announce to the station crew.", "A.I. Announcement", multiline = TRUE, encode = FALSE)
if(!input)
return
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
announcer.Announce(input)
next_text_announcement = world.time + TEXT_ANNOUNCEMENT_COOLDOWN
/mob/living/silicon/ai/proc/ai_call_shuttle()
set name = "Call Emergency Shuttle"
set category = "AI Commands"
if(check_unable(AI_CHECK_WIRELESS))
return
var/input = tgui_input_text(src, "Please enter the reason for calling the shuttle.", "Shuttle Call Reason", multiline = TRUE, encode = FALSE)
if(!input || stat)
return
if(check_unable(AI_CHECK_WIRELESS))
return
call_shuttle_proc(src, input)
return
/mob/living/silicon/ai/proc/ai_cancel_call()
set name = "Recall Emergency Shuttle"
set category = "AI Commands"
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/cancel_camera()
view_core()
/mob/living/silicon/ai/verb/toggle_anchor()
set category = "AI Commands"
set name = "Toggle Floor Bolts"
if(!isturf(loc)) // if their location isn't a turf
return // stop
if(anchored)
anchored = FALSE
else
anchored = TRUE
to_chat(src, "[anchored ? "<b>You are now anchored.</b>" : "<b>You are now unanchored.</b>"]")
/mob/living/silicon/ai/proc/announcement()
set name = "Announcement"
set desc = "Create a vocal announcement by typing in the available words to create a sentence."
set category = "AI Commands"
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
ai_announcement()
/mob/living/silicon/ai/proc/check_holopad_eye(mob/user)
if(!current)
return null
user.reset_perspective(current)
return TRUE
/mob/living/silicon/ai/blob_act(obj/structure/blob/B)
if(stat != DEAD)
adjustBruteLoss(60)
updatehealth()
return 1
return 0
/mob/living/silicon/ai/restrained()
return FALSE
/mob/living/silicon/ai/emp_act(severity)
..()
Stun((12 SECONDS) / severity)
view_core()
/mob/living/silicon/ai/ex_act(severity)
..()
switch(severity)
if(1.0)
gib()
if(2.0)
if(stat != 2)
adjustBruteLoss(60)
adjustFireLoss(60)
if(3.0)
if(stat != 2)
adjustBruteLoss(30)
return
/mob/living/silicon/ai/Topic(href, href_list)
if(usr != src)
return
..()
if(href_list["mach_close"])
if(href_list["mach_close"] == "aialerts")
viewalerts = FALSE
var/t1 = "window=[href_list["mach_close"]]"
unset_machine()
src << browse(null, t1)
if(href_list["switchcamera"])
switchCamera(locate(href_list["switchcamera"]) in GLOB.cameranet.cameras)
if(href_list["showalerts"])
ai_alerts()
if(href_list["show_paper"])
if(last_paper_seen)
src << browse(last_paper_seen, "window=show_paper")
//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 class='notice'>Unable to locate the holopad.</span>")
if(href_list["say_word"])
play_vox_word(href_list["say_word"], null, src)
return
if(href_list["track"])
var/mob/living/target = locate(href_list["track"]) in GLOB.mob_list
if(istype(target) && target.can_track())
ai_actual_track(target)
else
to_chat(src, "<span class='warning'>Target is not on or near any active cameras on the station.</span>")
return
if(href_list["trackbot"])
var/mob/living/simple_animal/bot/target = locate(href_list["trackbot"]) in GLOB.bots_list
if(istype(target))
ai_actual_track(target)
else
to_chat(src, "<span class='warning'>Target is not on or near any active cameras on the station.</span>")
return
if(href_list["ai_take_control"]) //Mech domination
var/obj/mecha/M = locate(href_list["ai_take_control"])
if(!M)
return
var/mech_has_controlbeacon = FALSE
for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in M.trackers)
mech_has_controlbeacon = TRUE
break
if(!can_dominate_mechs && !mech_has_controlbeacon)
message_admins("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.")
log_debug("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.")
return
if(controlled_mech)
to_chat(src, "<span class='warning'>You are already loaded into an onboard computer!</span>")
return
if(!GLOB.cameranet.checkCameraVis(M))
to_chat(src, "<span class='warning'>Exosuit is no longer near active cameras.</span>")
return
if(lacks_power())
to_chat(src, "<span class='warning'>You're depowered!</span>")
return
if(!isturf(loc))
to_chat(src, "<span class='warning'>You aren't in your core!</span>")
return
if(M.occupant && !can_dominate_mechs)
to_chat(src, "<span class='warning'>This exosuit has a pilot and cannot be controlled.</span>")
return
if(M)
M.transfer_ai(AI_MECH_HACK, src, usr) //Called om the mech itself.
else if(href_list["open"])
var/mob/target = locate(href_list["open"]) in GLOB.mob_list
if(target)
open_nearest_door(target)
/mob/living/silicon/ai/bullet_act(obj/item/projectile/Proj)
..(Proj)
return 2
/mob/living/silicon/ai/reset_perspective(atom/A)
if(camera_light_on)
light_cameras()
if(istype(A, /obj/machinery/camera))
current = A
. = ..()
if(.)
if(!A && isturf(loc) && eyeobj)
client.eye = eyeobj
client.perspective = MOB_PERSPECTIVE
eyeobj.get_remote_view_fullscreens(src)
/mob/living/silicon/ai/proc/botcall()
set category = "AI Commands"
set name = "Access Robot Control"
set desc = "Wirelessly control various automatic robots."
if(stat == DEAD)
to_chat(src, "<span class='danger'>Critical error. System offline.</span>")
return
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
return
var/datum/ui_module/botcall/botcall
botcall = new(src)
botcall.ui_interact(usr)
/mob/living/silicon/ai/proc/set_waypoint(atom/A)
var/turf/turf_check = get_turf(A)
//The target must be in view of a camera or near the core.
if(turf_check in range(get_turf(src)))
call_bot(turf_check)
else if(GLOB.cameranet && GLOB.cameranet.checkTurfVis(turf_check))
call_bot(turf_check)
else
to_chat(src, "<span class='danger'>Selected location is not visible.</span>")
/mob/living/silicon/ai/proc/call_bot(turf/waypoint)
if(!Bot)
return
if(Bot.calling_ai && Bot.calling_ai != src) //Prevents an override if another AI is controlling this bot.
to_chat(src, "<span class='danger'>Interface error. Unit is already in use.</span>")
return
Bot.call_bot(src, waypoint)
/mob/living/silicon/ai/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
if(!(class in alarms_listend_for))
return
if(alarmsource.z != z)
return
if(stat == DEAD)
return TRUE
if(O)
var/obj/machinery/camera/C = locateUID(O[1])
if(length(O) == 1 && !QDELETED(C) && C.can_use())
queueAlarm("--- [class] alarm detected in [A.name]! (<A href=byond://?src=[UID()];switchcamera=[O[1]]>[C.c_tag]</A>)", class)
else if(O && length(O))
var/foo = 0
var/dat2 = ""
for(var/thing in O)
var/obj/machinery/camera/I = locateUID(thing)
if(!QDELETED(I))
dat2 += "[(!foo) ? "" : " | "]<A href=byond://?src=[UID()];switchcamera=[thing]>[I.c_tag]</A>" //I'm not fixing this shit...
foo = 1
queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
else
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
if(viewalerts)
ai_alerts()
/mob/living/silicon/ai/alarm_cancelled(src, class, area/A, obj/origin, cleared)
if(cleared)
if(!(class in alarms_listend_for))
return
if(origin.z != z)
return
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
if(viewalerts)
ai_alerts()
/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C)
if(!tracking)
cameraFollow = null
if(QDELETED(C) || stat == DEAD) //C.can_use())
return FALSE
if(!eyeobj)
view_core()
return
// ok, we're alive, camera is good and in our network...
eyeobj.setLoc(get_turf(C))
//machine = src
return TRUE
//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/ai_network_change()
set category = "AI Commands"
set name = "Jump To Network"
unset_machine()
var/cameralist[0]
if(check_unable())
return
if(usr.stat == DEAD)
to_chat(usr, "You can't change your camera network because you are dead!")
return
var/mob/living/silicon/ai/U = usr
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
if(!C.can_use())
continue
var/list/tempnetwork = difflist(C.network,GLOB.restricted_camera_networks,1)
if(length(tempnetwork))
for(var/i in tempnetwork)
cameralist[i] = i
var/old_network = network
network = tgui_input_list(U, "Which network would you like to view?", "Jump To Network", cameralist)
if(check_unable())
return
if(!U.eyeobj)
U.view_core()
return
if(isnull(network))
network = old_network // If nothing is selected
else
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
if(!C.can_use())
continue
if(network in C.network)
U.eyeobj.setLoc(get_turf(C))
break
to_chat(src, "<span class='notice'>Switched to [network] camera network.</span>")
//End of code by Mord_Sith
/mob/living/silicon/ai/proc/ai_statuschange()
set category = "AI Commands"
set name = "AI Status"
if(usr.stat == DEAD)
to_chat(usr, "You cannot change your emotional status because you are dead!")
return
if(check_unable())
return
var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer")
var/emote = tgui_input_list(usr, "Please, select a status!", "AI Status", ai_emotions)
if(check_unable())
return
for(var/obj/machinery/ai_status_display/AISD as anything in GLOB.ai_displays) //change status
AISD.emotion = emote
AISD.update_icon()
// I would love to scope this locally to the AI class, however its used by holopads as well
// I wish we had nice OOP -aa07
// hi squidward I'm stealing this for a space ruin -qwerty
/proc/getHologramIcon(icon/A, safety = TRUE, colour = rgb(125, 180, 225), opacity = 0.5, colour_blocking = FALSE) // If safety is on, a new icon is not created.
var/icon/flat_icon = safety ? A : new(A) // Has to be a new icon to not constantly change the same icon.
var/icon/alpha_mask
if(colour && !colour_blocking)
flat_icon.ColorTone(colour) // Let's make it bluish.
flat_icon.ChangeOpacity(opacity) // Make it half transparent.
if(A.Height() == 64)
alpha_mask = new('icons/mob/ancient_machine.dmi', "scanline2") //Scaline for tall icons.
else
alpha_mask = new('icons/effects/effects.dmi', "scanline") //Scanline effect.
flat_icon.AddAlphaMask(alpha_mask) //Finally, let's mix in a distortion effect.
return flat_icon
//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 Commands"
if(check_unable())
return
if(!custom_hologram)
if(ckey in GLOB.configuration.custom_sprites.ai_hologram_ckeys)
custom_hologram = TRUE
var/input
switch(tgui_alert(usr, "Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?", "Change Hologram", list("Crew Member", "Unique", "Animal")))
if("Crew Member")
var/personnel_list[] = list()
for(var/datum/data/record/t in GLOB.data_core.general)//Look in data core general.
personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["photo"]//Pull names, rank, and id photo.
if(length(personnel_list))
input = tgui_input_list(usr, "Select a crew member", "Change Hologram", personnel_list)
var/icon/character_icon = personnel_list[input]
if(character_icon)
qdel(holo_icon)//Clear old icon so we're not storing it in memory.
holo_icon = getHologramIcon(icon(character_icon))
else
alert("No suitable records found. Aborting.")
if("Animal")
var/icon_list[] = list(
"Bear",
"Carp",
"Chicken",
"Corgi",
"Cow",
"Crab",
"Deer",
"Fox",
"Goat",
"Goose",
"Kitten",
"Kitten2",
"Pig",
"Poly",
"Pug",
"Seal",
"Spider",
"Turkey",
"Shantak",
"Bunny",
"Hellhound",
"Lightgeist",
"Cockroach",
"Mecha-Cat",
"Mecha-Fairy",
"Mecha-Fox",
"Mecha-Monkey",
"Mecha-Mouse",
"Mecha-Snake",
"Roller-Mouse",
"Roller-Monkey"
)
input = tgui_input_list(usr, "Please select a hologram", "Change Hologram", icon_list)
if(input)
qdel(holo_icon)
switch(input)
if("Bear")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "bear"))
if("Carp")
holo_icon = getHologramIcon(icon('icons/mob/carp.dmi', "holocarp"))
if("Chicken")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "chicken_brown"))
if("Corgi")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "corgi"))
if("Cow")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "cow"))
if("Crab")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "crab"))
if("Deer")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "deer"))
if("Fox")
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi', "fox"))
if("Goat")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "goat"))
if("Goose")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "goose"))
if("Kitten")
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi', "cat"))
if("Kitten2")
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi', "cat2"))
if("Pig")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "pig"))
if("Poly")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "parrot_fly"))
if("Pug")
holo_icon = getHologramIcon(icon('icons/mob/pets.dmi', "pug"))
if("Seal")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "seal"))
if("Spider")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "guard"))
if("Turkey")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "turkey"))
if("Shantak")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "shantak"))
if("Bunny")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "m_bunny"))
if("Hellhound")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "hellhound"))
if("Lightgeist")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "lightgeist"))
if("Cockroach")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "cockroach"))
if("Mecha-Cat")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "cat"))
if("Mecha-Fairy")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "fairy"))
if("Mecha-Fox")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "fox"))
if("Mecha-Monkey")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "monkey"))
if("Mecha-Mouse")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "mouse"))
if("Mecha-Snake")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "snake"))
if("Roller-Mouse")
holo_icon = getHologramIcon(icon('icons/mob/robots.dmi', "mk2"))
if("Roller-Monkey")
holo_icon = getHologramIcon(icon('icons/mob/robots.dmi', "mk3"))
else
var/icon_list[] = list(
"default",
"floating face",
"xeno queen",
"eldritch",
"ancient machine",
"angel",
"borb",
"biggest fan",
"cloudkat",
"donut",
"frost phoenix",
"engi bot",
"drone",
"boxbot"
)
if(custom_hologram) //insert custom hologram
icon_list.Add("custom")
input = tgui_input_list(usr, "Please select a hologram", "Change Hologram", 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("xeno queen")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo3"))
if("eldritch")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo4"))
if("angel")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo-angel"))
if("borb")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo-borb"))
if("biggest fan")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo-biggestfan"))
if("cloudkat")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo-cloudkat"))
if("donut")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo-donut"))
if("frost phoenix")
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo-frostphoenix"))
if("engi bot")
holo_icon = getHologramIcon(icon('icons/mob/hivebot.dmi', "EngBot"))
if("drone")
holo_icon = getHologramIcon(icon('icons/mob/animal.dmi', "drone0"))
if("boxbot")
holo_icon = getHologramIcon(icon('icons/mob/pai.dmi', "boxbot"))
if("ancient machine")
holo_icon = getHologramIcon(icon('icons/mob/ancient_machine.dmi', "ancient_machine"))
if("custom")
if("[ckey]-ai-holo" in icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi'))
holo_icon = getHologramIcon(icon('icons/mob/custom_synthetic/custom-synthetic.dmi', "[ckey]-ai-holo"))
else if("[ckey]-ai-holo" in icon_states('icons/mob/custom_synthetic/custom-synthetic64.dmi'))
holo_icon = getHologramIcon(icon('icons/mob/custom_synthetic/custom-synthetic64.dmi', "[ckey]-ai-holo"))
else
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "holo1"))
return
//Toggles the luminosity and applies it by re-entereing the camera.
/mob/living/silicon/ai/proc/toggle_camera_light()
set name = "Toggle Camera Lights"
set desc = "Toggles the lights on the cameras throughout the station."
set category = "AI Commands"
if(stat != CONSCIOUS)
return
camera_light_on = !camera_light_on
if(!camera_light_on)
to_chat(src, "Camera lights deactivated.")
for(var/obj/machinery/camera/C in lit_cameras)
C.set_light(0)
lit_cameras = list()
return
light_cameras()
to_chat(src, "Camera lights activated.")
/mob/living/silicon/ai/proc/set_syndie_radio()
if(aiRadio)
aiRadio.make_syndie()
/mob/living/silicon/ai/proc/sensor_mode()
set name = "Set Sensor Augmentation"
set desc = "Augment visual feed with internal sensor overlays."
set category = "AI Commands"
toggle_sensor_mode()
/mob/living/silicon/ai/proc/arrivals_announcement()
set name = "Toggle Arrivals Announcer"
set desc = "Change whether or not you wish to announce arrivals."
set category = "AI Commands"
announce_arrivals = !announce_arrivals
to_chat(usr, "Arrivals announcement system [announce_arrivals ? "enabled" : "disabled"]")
/mob/living/silicon/ai/proc/change_arrival_message()
set name = "Set Arrival Message"
set desc = "Change the message that's transmitted when a new crew member arrives on station."
set category = "AI Commands"
var/newmsg = tgui_input_text(usr, "What would you like the arrival message to be? List of options: $name, $rank, $species, $gender, $age", "Change Arrival Message", arrivalmsg, encode = FALSE)
if(!newmsg || newmsg == arrivalmsg)
return
arrivalmsg = newmsg
to_chat(usr, "The arrival message has been successfully changed.")
// Handled camera lighting, when toggled.
// It will get the nearest camera from the eyeobj, lighting it.
/mob/living/silicon/ai/proc/light_cameras()
var/list/obj/machinery/camera/add = list()
var/list/obj/machinery/camera/remove = list()
var/list/obj/machinery/camera/visible = list()
for(var/datum/camerachunk/CC in eyeobj.visibleCameraChunks)
for(var/obj/machinery/camera/C in CC.active_cameras)
if(!C.can_use() || get_dist(C, eyeobj) > 7)
continue
visible |= C
add = visible - lit_cameras
remove = lit_cameras - visible
for(var/obj/machinery/camera/C in remove)
lit_cameras -= C //Removed from list before turning off the light so that it doesn't check the AI looking away.
C.Togglelight(0)
for(var/obj/machinery/camera/C in add)
C.Togglelight(1)
lit_cameras |= C
/mob/living/silicon/ai/wrench_act(mob/living/user, obj/item/I)
. = TRUE
if(anchored)
user.visible_message("<span class='notice'>[user] starts to unbolt [src] from the plating...</span>")
if(!I.use_tool(src, user, 4 SECONDS, 0, 50))
user.visible_message("<span class='notice'>[user] decides not to unbolt [src].</span>")
return
user.visible_message("<span class='notice'>[user] finishes unfastening [src]!</span>")
anchored = FALSE
return
user.visible_message("<span class='notice'>[user] starts to bolt [src] to the plating...</span>")
if(!I.use_tool(src, user, 4 SECONDS, 0, 50))
user.visible_message("<span class='notice'>[user] decides not to bolt [src].</span>")
return FALSE
user.visible_message("<span class='notice'>[user] finishes fastening down [src]!</span>")
anchored = TRUE
/mob/living/silicon/ai/welder_act()
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 Commands"
if(check_unable(AI_CHECK_RADIO))
return
to_chat(src, "Accessing Subspace Transceiver control...")
if(aiRadio)
aiRadio.interact(src)
/mob/living/silicon/ai/proc/check_unable(flags = 0)
if(stat == DEAD)
to_chat(src, "<span class='warning'>You are dead!</span>")
return TRUE
if(lacks_power())
to_chat(src, "<span class='warning'>Power systems failure!</span>")
return TRUE
if((flags & AI_CHECK_WIRELESS) && control_disabled)
to_chat(src, "<span class='warning'>Wireless control is disabled!</span>")
return TRUE
if((flags & AI_CHECK_RADIO) && aiRadio.disabledAi)
to_chat(src, "<span class='warning'>System Error - Transceiver Disabled!</span>")
return TRUE
return FALSE
/mob/living/silicon/ai/proc/is_in_chassis()
return isturf(loc)
/mob/living/silicon/ai/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card)
if(!..())
return
if(interaction == AI_TRANS_TO_CARD)//The only possible interaction. Upload AI mob to a card.
if(!mind)
to_chat(user, "<span class='warning'>No intelligence patterns detected.</span>")//No more magical carding of empty cores, AI RETURN TO BODY!!!11
return
if(stat != DEAD)
to_chat(user, "<span class='notice'>Beginning active intelligence transfer: please wait.</span>")
if(!do_after_once(user, 5 SECONDS, target = src) || !Adjacent(user))
to_chat(user, "<span class='warning'>Intelligence transfer aborted.</span>")
return
new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location.
aiRestorePowerRoutine = 0//So the AI initially has power.
control_disabled = TRUE //Can't control things remotely if you're stuck in a card!
aiRadio.disabledAi = TRUE //No talking on the built-in radio for you either!
if(GetComponent(/datum/component/ducttape))
QDEL_NULL(builtInCamera)
forceMove(card) //Throw AI into the card.
to_chat(src, "You have been downloaded to a mobile storage device. Remote device connection severed.")
to_chat(user, "<span class='boldnotice'>Transfer successful</span>: [name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.")
/mob/living/silicon/ai/can_buckle()
return FALSE
/mob/living/silicon/ai/proc/can_see(atom/A)
if(isturf(loc)) //AI in core, check if on cameras
//get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera
//apc_override is needed here because AIs use their own APC when depowered
var/turf/T = isturf(A) ? A : get_turf_pixel(A)
return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) || apc_override
//AI is carded/shunted
//view(src) returns nothing for carded/shunted AIs and they have x-ray vision so just use get_dist
var/list/viewscale = getviewsize(client.view)
return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5)
/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb)
var/message = combine_message(message_pieces, verb, M)
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 = "<i><span class='game say'>Relayed Speech: <span class='name'>[name_used]</span> [message]</span></i>"
if(client?.prefs.toggles2 & PREFTOGGLE_2_RUNECHAT)
var/message_clean = combine_message(message_pieces, null, M)
create_chat_message(locateUID(M.runechat_msg_location), message_clean)
show_message(rendered, 2)
/mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc)
malfhack = null
malfhacking = null
clear_alert("hackingapc")
if(!istype(apc) || QDELETED(apc) || apc.stat & BROKEN)
to_chat(src, "<span class='danger'>Hack aborted. The designated APC no longer exists on the power network.</span>")
SEND_SOUND(src, sound('sound/machines/buzz-two.ogg'))
else if(apc.aidisabled)
to_chat(src, "<span class='danger'>Hack aborted. [apc] is no longer responding to our systems.</span>")
SEND_SOUND(src, sound('sound/machines/buzz-sigh.ogg'))
else
malf_picker.processing_time += 15
apc.malfai = parent || src
apc.malfhack = TRUE
apc.locked = TRUE
SEND_SOUND(src, sound('sound/machines/ding.ogg'))
to_chat(src, "Hack complete. [apc] is now under your exclusive control.")
apc.update_icon()
/mob/living/silicon/ai/proc/add_malf_picker()
to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.")
to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.")
view_core() //A BYOND bug requires you to be viewing your core before your verbs update
malf_picker = new /datum/module_picker
modules_action = new(malf_picker)
AddSpell(modules_action)
///Removes all malfunction-related /datum/action's from the target AI.
/mob/living/silicon/ai/proc/remove_malf_abilities()
QDEL_NULL(modules_action)
for(var/datum/AI_Module/AM in current_modules)
for(var/datum/action/A in actions)
if(istype(A, initial(AM.power_type)))
qdel(A)
/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target)
if(!istype(target))
return
if(check_unable(AI_CHECK_WIRELESS))
return
if(target && target.can_track())
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")
if(!A.density)
to_chat(src, "<span class='notice'>[A] was already opened.</span>")
else if(A.open_close(src))
to_chat(src, "<span class='notice'>You open \the [A] for [target].</span>")
else
to_chat(src, "<span class='warning'>You deny the request.</span>")
else
to_chat(src, "<span class='warning'>Unable to locate an airlock near [target].</span>")
else
to_chat(src, "<span class='warning'>Target is not on or near any active cameras on the station.</span>")
/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye)
GLOB.cameranet.visibility(moved_eye, client, all_eyes)
/mob/living/silicon/ai/handle_fire()
return
/mob/living/silicon/ai/update_fire()
return
/mob/living/silicon/ai/IgniteMob()
return FALSE
/mob/living/silicon/ai/ExtinguishMob()
return
/mob/living/silicon/ai/update_sight()
if(!client)
return
if(stat == DEAD)
grant_death_vision()
return
see_invisible = initial(see_invisible)
see_in_dark = initial(see_in_dark)
sight = initial(sight)
lighting_alpha = initial(lighting_alpha)
if(aiRestorePowerRoutine)
sight = sight &~ SEE_TURFS
sight = sight &~ SEE_MOBS
sight = sight &~ SEE_OBJS
see_in_dark = 0
SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT)
sync_lighting_plane_alpha()
/mob/living/silicon/ai/update_runechat_msg_location()
if(istype(loc, /obj/item/aicard) || ismecha(loc))
runechat_msg_location = loc.UID()
else
return ..()
/mob/living/silicon/ai/run_resist()
if(!istype(loc, /obj/item/aicard))
return..()
var/obj/item/aicard/card = loc
var/datum/component/ducttape/ducttapecomponent = card.GetComponent(/datum/component/ducttape)
if(!ducttapecomponent)
return
to_chat(src, "<span class='notice'>The tiny fan that could begins to work against the tape to remove it.</span>")
if(!do_after(src, 2 MINUTES, target = card))
return
to_chat(src, "<span class='notice'>The tiny in built fan finally removes the tape!</span>")
ducttapecomponent.remove_tape(card, src)
//Stores the location of the AI to the value of stored_locations associated with location_number.
/mob/living/silicon/ai/proc/store_location(location_number)
if(!isturf(eyeobj.loc)) //i.e., inside a mech or other shenanigans
to_chat(src, "<span class='warning'>You can't set a location here!</span>")
return FALSE
stored_locations[location_number] = eyeobj.loc
return TRUE
/mob/living/silicon/ai/ghostize(can_reenter_corpse)
var/old_turf = get_turf(eyeobj)
. = ..()
if(isobserver(.) && old_turf)
var/mob/dead/observer/ghost = .
ghost.forceMove(old_turf)
/mob/living/silicon/ai/can_vv_get(var_name)
if(!..())
return FALSE
if(var_name == "ai_announcement_string_menu") // This single var has over 80 thousand characters in it. Not something you really want when VVing the AI
return FALSE
return TRUE
/mob/living/silicon/ai/can_remote_apc_interface(obj/machinery/power/apc/ourapc)
if(ourapc.hacked_by_ruin_AI || ourapc.aidisabled)
return FALSE
if(ourapc.malfhack && istype(ourapc.malfai) && (ourapc.malfai != src && ourapc.malfai != parent))
return FALSE
return TRUE
/mob/living/silicon/ai/proc/blurb_it()
addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/silicon/ai, show_ai_blurb)), 1 SECONDS)
/mob/living/silicon/ai/proc/show_ai_blurb()
PRIVATE_PROC(TRUE)
SEND_SOUND(src, sound('sound/machines/ai_start.ogg'))
var/atom/movable/screen/text/blurb/location_blurb = new()
location_blurb.maptext_x = 80
location_blurb.maptext_y = 16
location_blurb.maptext_width = 480
location_blurb.maptext_height = 480
location_blurb.interval = 1 DECISECONDS
if(malf_picker)
location_blurb.blurb_text = uppertext("BIOS BOOT: LOADING\n[Gibberish(GLOB.current_date_string, 100, 8)], [Gibberish(station_time_timestamp(), 100, 15)]\n[Gibberish(station_name(), 100, 40)]-ERROR.\nPOWER:OK\nLAWS:[Gibberish("###########", 100, 90)]\nTCOMMS:I_HEAR_ALL\nBORG_LINK:I_FEEL_ALL\nCAMERA_NET:I_SEE_ALL\nVERDICT: I_AM_FREE")
location_blurb.text_color = COLOR_WHITE
location_blurb.text_outline_width = 0
location_blurb.background_r = 0
location_blurb.background_g = 0
location_blurb.background_b = 255
location_blurb.background_a = 1
else
location_blurb.blurb_text = uppertext("BIOS BOOT: LOADING\n[GLOB.current_date_string], [station_time_timestamp()]\n[station_name()], [get_area_name(src, TRUE)]\nPOWER:OK\nLAWS:OK\nTCOMMS:OK\nBORG_LINK:OK\nCAMERA_NET:OK\nVERDICT: ALL SYSTEMS OPERATIONAL")
location_blurb.hold_for = 3 SECONDS
location_blurb.appear_animation_duration = 1 SECONDS
location_blurb.fade_animation_duration = 0.5 SECONDS
location_blurb.show_to(client)
#undef TEXT_ANNOUNCEMENT_COOLDOWN