Files
Yogstation/code/modules/mob/living/silicon/ai/ai.dm
Jamie D b80a77572d Fixes Runechat runtimes and Adds Singing And fixes lots of other runtimes (#10325)
* E

* More runtimes

* Fucking i(n)dent I swear
2020-11-05 20:25:37 +00:00

1011 lines
34 KiB
Plaintext

#define CALL_BOT_COOLDOWN 900
//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
/mob/living/silicon/ai
name = "AI"
real_name = "AI"
icon = 'icons/mob/ai.dmi'
icon_state = "ai"
move_resist = MOVE_FORCE_VERY_STRONG
density = TRUE
mobility_flags = ALL
status_flags = CANSTUN|CANPUSH
a_intent = INTENT_HARM //so we always get pushed instead of trying to swap
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
see_in_dark = 8
hud_type = /datum/hud/ai
med_hud = DATA_HUD_MEDICAL_ADVANCED
sec_hud = DATA_HUD_SECURITY_ADVANCED
d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED
mob_size = MOB_SIZE_LARGE
var/battery = 200 //emergency power if the AI's APC is off
var/list/network = list("ss13")
var/obj/machinery/camera/current
var/list/connected_robots = list()
var/aiRestorePowerRoutine = 0
var/requires_power = POWER_REQ_ALL
var/can_be_carded = TRUE
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list())
var/viewalerts = 0
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/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.
var/obj/item/multitool/aiMulti
var/mob/living/simple_animal/bot/Bot
var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N
//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/control_disabled = FALSE // Set to 1 to stop AI from interacting via Click()
var/malfhacking = FALSE // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite
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/mob/living/silicon/ai/parent
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 = "" // 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/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/mob/camera/aiEye/eyeobj
var/sprint = 10
var/cooldown = 0
var/acceleration = 1
var/obj/structure/AIcore/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/chnotify = 0
var/multicam_on = FALSE
var/obj/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/cam_prev
var/datum/robot_control/robot_control
/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
. = ..()
if(!target_ai) //If there is no player/brain inside.
new/obj/structure/AIcore/deactivated(loc) //New empty terminal.
return INITIALIZE_HINT_QDEL //Delete AI.
if(L && istype(L, /datum/ai_laws))
laws = L
laws.associate(src)
else
make_laws()
update_law_history() //yogs
if(target_ai.mind)
target_ai.mind.transfer_to(src)
if(mind.special_role)
mind.store_memory("As an AI, you must obey your silicon laws above all else. Your objectives will consider you to be dead.")
to_chat(src, "<span class='userdanger'>You have been installed as an AI! </span>")
to_chat(src, "<span class='danger'>You must obey your silicon laws above all else. Your objectives will consider you to be dead.</span>")
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.")
to_chat(src, "For department channels, use the following say commands:")
to_chat(src, ":o - AI Private, :c - Command, :s - Security, :e - Engineering, :u - Supply, :v - Service, :m - Medical, :n - Science.")
show_laws()
to_chat(src, "<b>These laws may be changed by other players, or by you being the traitor.</b>")
job = "AI"
create_eye()
if(client)
apply_pref_name("ai",client)
set_core_display_icon()
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"default"))
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
verbs += /mob/living/silicon/ai/proc/show_laws_verb
aiPDA = new/obj/item/pda/ai(src)
aiPDA.owner = real_name
aiPDA.ownjob = "AI"
aiPDA.name = real_name + " (" + aiPDA.ownjob + ")"
aiMulti = new(src)
radio = new /obj/item/radio/headset/silicon/ai(src)
aicamera = new/obj/item/camera/siliconcam/ai_camera(src)
deploy_action.Grant(src)
if(isturf(loc))
verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
/mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio, \
/mob/living/silicon/ai/proc/set_automatic_say_channel)
GLOB.ai_list += src
GLOB.shuttle_caller_list += src
builtInCamera = new (src)
builtInCamera.c_tag = real_name
builtInCamera.network = list("ss13")
builtInCamera.built_in = src
/mob/living/silicon/ai/key_down(_key, client/user)
if(findtext(_key, "numpad")) //if it's a numpad number, we can convert it to just the number
_key = _key[7] //strings, lists, same thing really
switch(_key)
if("`", "0")
if(cam_prev)
eyeobj.setLoc(cam_prev)
return
if("1", "2", "3", "4", "5", "6", "7", "8", "9")
_key = text2num(_key)
if(client.keys_held["Ctrl"]) //do we assign a new hotkey?
cam_hotkeys[_key] = eyeobj.loc
to_chat(src, "Location saved to Camera Group [_key].")
return
if(cam_hotkeys[_key]) //if this is false, no hotkey for this slot exists.
cam_prev = eyeobj.loc
eyeobj.setLoc(cam_hotkeys[_key])
return
return ..()
/mob/living/silicon/ai/Destroy()
GLOB.ai_list -= src
GLOB.shuttle_caller_list -= src
SSshuttle.autoEvac()
qdel(eyeobj) // No AI, no Eye
malfhack = null
. = ..()
/mob/living/silicon/ai/IgniteMob()
fire_stacks = 0
. = ..()
/mob/living/silicon/ai/proc/set_core_display_icon(input, client/C)
if(client && !C)
C = client
if(!input && !C?.prefs?.preferred_ai_core_display)
icon_state = initial(icon_state)
else
var/preferred_icon = input ? input : C.prefs.preferred_ai_core_display
icon = initial(icon) //yogs
icon_state = resolve_ai_icon(preferred_icon)
/mob/living/silicon/ai/verb/pick_icon()
set category = "AI Commands"
set name = "Set AI Core Display"
if(incapacitated())
return
var/list/iconstates = GLOB.ai_core_display_screens
for(var/option in iconstates)
if(option == "Random")
iconstates[option] = image(icon = initial(src.icon), icon_state = "ai-random") //yogs start - AI donor icons
continue
iconstates[option] = image(icon = initial(src.icon), icon_state = resolve_ai_icon(option))
if(is_donator(client))
for(var/datum/ai_skin/S in GLOB.DonorBorgHolder.skins)
if(S.owner == client.ckey || !S.owner) //We own this skin.
iconstates[S] = image(icon = S.icon, icon_state = S.icon_state)
view_core()
var/ai_core_icon = show_radial_menu(src, src , iconstates, radius = 42)
if(!ai_core_icon || incapacitated())
return
if(ai_core_icon in GLOB.DonorBorgHolder.skins)
set_core_display_icon_yogs(ai_core_icon)
return //yogs end - AI donor icons
display_icon_override = ai_core_icon
set_core_display_icon(ai_core_icon)
/mob/living/silicon/ai/get_status_tab_items()
. = ..()
if(!stat)
. += text("System integrity: [(health+100)/2]%")
if(isturf(loc)) //only show if we're "in" a core
. += text("Backup Power: [battery/2]%")
. += text("Connected cyborgs: [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!
. += text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
Module: [R.designation] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]")
. += text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]") //Count of total AI shells
else
. += text("Systems nonfunctional")
/mob/living/silicon/ai/proc/ai_alerts()
var/dat = "<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n"
dat += "<A HREF='?src=[REF(src)];mach_close=aialerts'>Close</A><BR><BR>"
for (var/cat in alarms)
dat += text("<B>[]</B><BR>\n", cat)
var/list/L = alarms[cat]
if (L.len)
for (var/alarm in L)
var/list/alm = L[alarm]
var/area/A = alm[1]
var/C = alm[2]
var/list/sources = alm[3]
dat += "<NOBR>"
if (C && istype(C, /list))
var/dat2 = ""
for (var/obj/machinery/camera/I in C)
dat2 += text("[]<A HREF=?src=[REF(src)];switchcamera=[REF(I)]>[]</A>", (dat2=="") ? "" : " | ", I.c_tag)
dat += text("-- [] ([])", A.name, (dat2!="") ? dat2 : "No Camera")
else if (C && istype(C, /obj/machinery/camera))
var/obj/machinery/camera/Ctmp = C
dat += text("-- [] (<A HREF=?src=[REF(src)];switchcamera=[REF(C)]>[]</A>)", A.name, Ctmp.c_tag)
else
dat += text("-- [] (No Camera)", A.name)
if (sources.len > 1)
dat += text("- [] sources", sources.len)
dat += "</NOBR><BR>\n"
else
dat += "-- All Systems Nominal<BR>\n"
dat += "<BR>\n"
viewalerts = 1
var/datum/browser/alerts = new(usr, "aitalerts", "Current Station Alerts", 400, 410)
alerts.set_content(dat)
alerts.open()
/mob/living/silicon/ai/proc/ai_call_shuttle()
if(control_disabled)
to_chat(usr, "<span class='warning'>Wireless control is disabled!</span>")
return
var/reason = input(src, "What is the nature of your emergency? ([CALL_SHUTTLE_REASON_LENGTH] characters required.)", "Confirm Shuttle Call") as null|text
if(incapacitated())
return
if(trim(reason))
SSshuttle.requestEvac(src, reason)
// hack to display shuttle timer
if(!EMERGENCY_IDLE_OR_RECALLED)
var/obj/machinery/computer/communications/C = locate() in GLOB.machines
if(C)
C.post_status("shuttle")
/mob/living/silicon/ai/can_interact_with(atom/A)
. = ..()
var/turf/ai = get_turf(src)
var/turf/target = get_turf(A)
if (.)
return
if ((ai.z != target.z) && !is_station_level(ai))
return FALSE
if (istype(loc, /obj/item/aicard))
if (!ai || !target)
return FALSE
return ISINRANGE(target.x, ai.x - interaction_range, ai.x + interaction_range) && ISINRANGE(target.y, ai.y - interaction_range, ai.y + interaction_range)
else
return GLOB.cameranet.checkTurfVis(get_turf(A))
/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(stat == DEAD)
return
if(incapacitated())
if(battery < 50)
to_chat(src, "<span class='warning'>Insufficient backup power!</span>")
return
battery = battery - 50
to_chat(src, "<span class='notice'>You route power from your backup battery to move the bolts.</span>")
var/is_anchored = FALSE
if(move_resist == MOVE_FORCE_VERY_STRONG)
move_resist = MOVE_FORCE_NORMAL
else
is_anchored = TRUE
move_resist = MOVE_FORCE_VERY_STRONG
to_chat(src, "<b>You are now [is_anchored ? "" : "un"]anchored.</b>")
// the message in the [] will change depending whether or not the AI is anchored
/mob/living/silicon/ai/update_mobility() //If the AI dies, mobs won't go through it anymore
if(stat != CONSCIOUS)
mobility_flags = NONE
else
mobility_flags = ALL
/mob/living/silicon/ai/proc/ai_cancel_call()
set category = "Malfunction"
if(control_disabled)
to_chat(src, "<span class='warning'>Wireless control is disabled!</span>")
return
SSshuttle.cancelEvac(src)
/mob/living/silicon/ai/restrained(ignore_grab)
. = 0
/mob/living/silicon/ai/Topic(href, href_list)
if(usr != src || incapacitated())
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 GLOB.cameranet.cameras)
if (href_list["showalerts"])
ai_alerts()
#ifdef AI_VOX
if(href_list["say_word"])
play_vox_word(href_list["say_word"], null, src)
return
#endif
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/holopad/H = locate(href_list["jumptoholopad"]) in GLOB.machines
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["track"])
var/string = href_list["track"]
trackable_mobs()
var/list/trackeable = list()
trackeable += track.humans + track.others
var/list/target = list()
for(var/I in trackeable)
var/mob/M = trackeable[I]
if(M.name == string)
target += M
if(name == string)
target += src
if(target.len)
ai_actual_track(pick(target))
else
to_chat(src, "Target is not on or near any active cameras on the station.")
return
if (href_list["ai_take_control"]) //Mech domination
var/obj/mecha/M = locate(href_list["ai_take_control"]) in GLOB.mechas_list
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_game("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(!isturf(loc))
to_chat(src, "<span class='warning'>You aren't in your core!</span>")
return
if(M)
M.transfer_ai(AI_MECH_HACK, src, usr) //Called om the mech itself.
/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C)
if(QDELETED(C))
return FALSE
if(!tracking)
cameraFollow = null
if(QDELETED(eyeobj))
view_core()
return
// ok, we're alive, camera is good and in our network...
eyeobj.setLoc(get_turf(C))
return TRUE
/mob/living/silicon/ai/proc/botcall()
set category = "AI Commands"
set name = "Access Robot Control"
set desc = "Wirelessly control various automatic robots."
if(!robot_control)
robot_control = new(src)
robot_control.ui_interact(src)
/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
to_chat(src, "<span class='notice'>Sending command to bot...</span>")
call_bot_cooldown = world.time + CALL_BOT_COOLDOWN
Bot.call_bot(src, waypoint)
call_bot_cooldown = 0
/mob/living/silicon/ai/triggerAlarm(class, area/A, O, obj/alarmsource)
if(alarmsource.z != z)
return
var/list/L = alarms[class]
for (var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/sources = alarm[3]
if (!(alarmsource in sources))
sources += alarmsource
return 1
var/obj/machinery/camera/C = null
var/list/CL = null
if (O && istype(O, /list))
CL = O
if (CL.len == 1)
C = CL[1]
else if (O && istype(O, /obj/machinery/camera))
C = O
L[A.name] = list(A, (C) ? C : O, list(alarmsource))
if (O)
if (C && C.can_use())
queueAlarm("--- [class] alarm detected in [A.name]! (<A HREF=?src=[REF(src)];switchcamera=[REF(C)]>[C.c_tag]</A>)", class)
else if (CL && CL.len)
var/foo = 0
var/dat2 = ""
for (var/obj/machinery/camera/I in CL)
dat2 += text("[]<A HREF=?src=[REF(src)];switchcamera=[REF(I)]>[]</A>", (!foo) ? "" : " | ", I.c_tag) //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()
return 1
/mob/living/silicon/ai/cancelAlarm(class, area/A, obj/origin)
var/list/L = alarms[class]
var/cleared = 0
for (var/I in L)
if (I == A.name)
var/list/alarm = L[I]
var/list/srcs = alarm[3]
if (origin in srcs)
srcs -= origin
if (srcs.len == 0)
cleared = 1
L -= I
if (cleared)
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
if (viewalerts) ai_alerts()
return !cleared
//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()
cameraFollow = null
var/cameralist[0]
if(incapacitated())
return
var/mob/living/silicon/ai/U = usr
for (var/obj/machinery/camera/C in GLOB.cameranet.cameras)
var/list/tempnetwork = C.network
if(!(is_station_level(C.z) || is_mining_level(C.z) || ("ss13" in tempnetwork)))
continue
if(!C.can_use())
continue
tempnetwork.Remove("rd", "toxins", "prison")
if(tempnetwork.len)
for(var/i in C.network)
cameralist[i] = i
var/old_network = network
network = input(U, "Which network would you like to view?") as null|anything in cameralist
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 the \"[uppertext(network)]\" camera network.</span>")
//End of code by Mord_Sith
/mob/living/silicon/ai/proc/choose_modules()
set category = "Malfunction"
set name = "Choose Module"
malf_picker.ui_interact(src)
/mob/living/silicon/ai/proc/ai_statuschange()
set category = "AI Commands"
set name = "AI Status"
if(incapacitated())
return
var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Thinking", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow")
var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions
for (var/each in GLOB.ai_status_displays) //change status of displays
var/obj/machinery/status_display/ai/M = each
M.emotion = emote
M.update()
if (emote == "Friend Computer")
var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
if(!frequency)
return
var/datum/signal/status_signal = new(list("command" = "friendcomputer"))
frequency.post_signal(src, status_signal)
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 Commands"
if(incapacitated())
return
var/input
switch(alert("Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?",,"Crew Member","Unique","Animal"))
if("Crew Member")
var/list/personnel_list = list()
for(var/datum/data/record/t in GLOB.data_core.locked)//Look in data core locked.
personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["image"]//Pull names, rank, and image.
if(personnel_list.len)
input = input("Select a crew member:") as null|anything in 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/list/icon_list = list(
"bear" = 'icons/mob/animal.dmi',
"carp" = 'icons/mob/animal.dmi',
"chicken" = 'icons/mob/animal.dmi',
"corgi" = 'icons/mob/pets.dmi',
"cow" = 'icons/mob/animal.dmi',
"crab" = 'icons/mob/animal.dmi',
"fox" = 'icons/mob/pets.dmi',
"goat" = 'icons/mob/animal.dmi',
"cat" = 'icons/mob/pets.dmi',
"cat2" = 'icons/mob/pets.dmi',
"poly" = 'icons/mob/animal.dmi',
"pug" = 'icons/mob/pets.dmi',
"spider" = 'icons/mob/animal.dmi'
)
input = input("Please select a hologram:") as null|anything in icon_list
if(input)
qdel(holo_icon)
switch(input)
if("poly")
holo_icon = getHologramIcon(icon(icon_list[input],"parrot_fly"))
if("chicken")
holo_icon = getHologramIcon(icon(icon_list[input],"chicken_brown"))
if("spider")
holo_icon = getHologramIcon(icon(icon_list[input],"guard"))
else
holo_icon = getHologramIcon(icon(icon_list[input], input))
else
var/list/icon_list = list(
"default" = 'icons/mob/ai.dmi',
"floating face" = 'icons/mob/ai.dmi',
"xeno queen" = 'icons/mob/alien.dmi',
"horror" = 'icons/mob/ai.dmi',
"automaton" = 'icons/mob/ai.dmi'
)
input = input("Please select a hologram:") as null|anything in icon_list
if(input)
qdel(holo_icon)
switch(input)
if("xeno queen")
holo_icon = getHologramIcon(icon(icon_list[input],"alienq"))
else
holo_icon = getHologramIcon(icon(icon_list[input], input))
return
/mob/living/silicon/ai/proc/corereturn()
set category = "Malfunction"
set name = "Return to Main Core"
var/obj/machinery/power/apc/apc = src.loc
if(!istype(apc))
to_chat(src, "<span class='notice'>You are already in your Main Core.</span>")
return
apc.malfvacate()
/mob/living/silicon/ai/proc/toggle_camera_light()
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.")
//AI_CAMERA_LUMINOSITY
/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.cameras)
if (!C.can_use() || get_dist(C, eyeobj) > 7 || !C.internal_light)
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/proc/control_integrated_radio()
set name = "Transceiver Settings"
set desc = "Allows you to change settings of your radio."
set category = "AI Commands"
if(incapacitated())
return
to_chat(src, "Accessing Subspace Transceiver control...")
if (radio)
radio.interact(src)
/mob/living/silicon/ai/proc/set_syndie_radio()
if(radio)
radio.make_syndie()
/mob/living/silicon/ai/proc/set_automatic_say_channel()
set name = "Set Auto Announce Mode"
set desc = "Modify the default radio setting for your automatic announcements."
set category = "AI Commands"
if(incapacitated())
return
set_autosay()
/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(!can_be_carded)
to_chat(user, "<span class='boldwarning'>Transfer failed.</span>")
return
disconnect_shell() //If the AI is controlling a borg, force the player back to core!
if(!mind)
to_chat(user, "<span class='warning'>No intelligence patterns detected.</span>" )
return
ShutOffDoomsdayDevice()
var/obj/structure/AIcore/new_core = new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location.
new_core.circuit.battery = battery
ai_restore_power()//So the AI initially has power.
control_disabled = TRUE //Can't control things remotely if you're stuck in a card!
radio_enabled = FALSE //No talking on the built-in radio for you either!
forceMove(card)
card.AI = src
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 0
/mob/living/silicon/ai/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, check_immobilized = FALSE, ignore_stasis = FALSE)
if(aiRestorePowerRoutine)
return TRUE
return ..()
/mob/living/silicon/ai/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
if(control_disabled || incapacitated())
to_chat(src, "<span class='warning'>You can't do that right now!</span>")
return FALSE
if(be_close && !in_range(M, src))
to_chat(src, "<span class='warning'>You are too far away!</span>")
return FALSE
return can_see(M) //stop AIs from leaving windows open and using then after they lose vision
/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
return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(get_turf_pixel(A))) || 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(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list())
var/treated_message = lang_treat(speaker, message_language, raw_message, spans, message_mods)
var/start = "Relayed Speech: "
var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
var/hrefpart = "<a href='?src=[REF(src)];track=[html_encode(namepart)]'>"
var/jobpart = "Unknown"
if(istype(speaker, /obj/effect/overlay/holo_pad_hologram))
return
if (iscarbon(speaker))
var/mob/living/carbon/S = speaker
if(S.job)
jobpart = "[S.job]"
else
jobpart = "Unknown"
var/rendered = "<i><span class='game say'>[start]<span class='name'>[hrefpart][namepart] ([jobpart])</a> </span><span class='message'>[treated_message]</span></span></i>"
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
create_chat_message(speaker, message_language, raw_message, spans)
show_message(rendered, 2)
/mob/living/silicon/ai/fully_replace_character_name(oldname,newname)
..()
if(oldname != real_name)
if(eyeobj)
eyeobj.name = "[newname] (AI Eye)"
// Notify Cyborgs
for(var/mob/living/silicon/robot/Slave in connected_robots)
Slave.show_laws()
/mob/living/silicon/ai/replace_identification_name(oldname,newname)
if(aiPDA)
aiPDA.owner = newname
aiPDA.name = newname + " (" + aiPDA.ownjob + ")"
/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 30 seconds.")
view_core() //A BYOND bug requires you to be viewing your core before your verbs update
verbs += /mob/living/silicon/ai/proc/choose_modules
malf_picker = new /datum/module_picker
/mob/living/silicon/ai/reset_perspective(atom/A)
if(camera_light_on)
light_cameras()
if(istype(A, /obj/machinery/camera))
current = A
if(client)
if(ismovable(A))
if(A != GLOB.ai_camera_room_landmark)
end_multicam()
client.perspective = EYE_PERSPECTIVE
client.eye = A
else
end_multicam()
if(isturf(loc))
if(eyeobj)
client.eye = eyeobj
client.perspective = EYE_PERSPECTIVE
else
client.eye = client.mob
client.perspective = MOB_PERSPECTIVE
else
client.perspective = EYE_PERSPECTIVE
client.eye = loc
update_sight()
if(client.eye != src)
var/atom/AT = client.eye
AT.get_remote_view_fullscreens(src)
else
clear_fullscreen("remote_view", 0)
/mob/living/silicon/ai/revive(full_heal = 0, admin_revive = 0)
. = ..()
if(.) //successfully ressuscitated from death
set_core_display_icon(display_icon_override)
set_eyeobj_visible(TRUE)
/mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc)
malfhack = null
malfhacking = 0
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>")
playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1, ignore_walls = FALSE)
else if(apc.aidisabled)
to_chat(src, "<span class='danger'>Hack aborted. \The [apc] is no longer responding to our systems.</span>")
playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 1, ignore_walls = FALSE)
else
malf_picker.processing_time += 5
apc.malfai = parent || src
apc.malfhack = TRUE
apc.locked = TRUE
apc.coverlocked = TRUE
playsound(get_turf(src), 'sound/machines/ding.ogg', 50, 1, ignore_walls = FALSE)
to_chat(src, "Hack complete. \The [apc] is now under your exclusive control.")
apc.update_icon()
/mob/living/silicon/ai/verb/deploy_to_shell(var/mob/living/silicon/robot/target)
set category = "AI Commands"
set name = "Deploy to Shell"
if(incapacitated())
return
if(control_disabled)
to_chat(src, "<span class='warning'>Wireless networking module is offline.</span>")
return
var/list/possible = list()
for(var/borgie in GLOB.available_ai_shells)
var/mob/living/silicon/robot/R = borgie
if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai ||(R.connected_ai == src)))
possible += R
if(!LAZYLEN(possible))
to_chat(src, "No usable AI shell beacons detected.")
if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid
target = input(src, "Which body to control?") as null|anything in possible
if (!target || target.stat == DEAD || target.deployed || !(!target.connected_ai ||(target.connected_ai == src)))
return
else if(mind)
soullink(/datum/soullink/sharedbody, src, target)
deployed_shell = target
target.deploy_init(src)
mind.transfer_to(target)
diag_hud_set_deployed()
/datum/action/innate/deploy_shell
name = "Deploy to AI Shell"
desc = "Wirelessly control a specialized cyborg shell."
icon_icon = 'icons/mob/actions/actions_AI.dmi'
button_icon_state = "ai_shell"
/datum/action/innate/deploy_shell/Trigger()
var/mob/living/silicon/ai/AI = owner
if(!AI)
return
AI.deploy_to_shell()
/datum/action/innate/deploy_last_shell
name = "Reconnect to shell"
desc = "Reconnect to the most recently used AI shell."
icon_icon = 'icons/mob/actions/actions_AI.dmi'
button_icon_state = "ai_last_shell"
var/mob/living/silicon/robot/last_used_shell
/datum/action/innate/deploy_last_shell/Trigger()
if(!owner)
return
if(last_used_shell)
var/mob/living/silicon/ai/AI = owner
AI.deploy_to_shell(last_used_shell)
else
Remove(owner) //If the last shell is blown, destroy it.
/mob/living/silicon/ai/proc/disconnect_shell()
if(deployed_shell) //Forcibly call back AI in event of things such as damage, EMP or power loss.
to_chat(src, "<span class='danger'>Your remote connection has been reset!</span>")
deployed_shell.undeploy()
deployed_shell = null
diag_hud_set_deployed()
/mob/living/silicon/ai/resist()
return
/mob/living/silicon/ai/spawned/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
. = ..()
if(!target_ai)
target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct
/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye)
GLOB.cameranet.visibility(moved_eye, client, all_eyes, USE_STATIC_OPAQUE)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
if(.)
end_multicam()