#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" icon = 'icons/mob/ai.dmi' icon_state = "ai" move_resist = MOVE_FORCE_OVERPOWERING 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 deathsound = 'sound/voice/scream/android_scream.ogg' hud_type = /datum/hud/ai med_hud = DATA_HUD_MEDICAL_BASIC sec_hud = DATA_HUD_SECURITY_BASIC d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED mob_size = MOB_SIZE_LARGE has_field_of_vision = FALSE //Vision through cameras. 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//Female 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/pda/ai/aiPDA 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/datum/action/innate/custom_holoform/custom_holoform = 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/emote_display = "Neutral" //text string of the current emote we set for the status displays, to prevent logins resetting it. /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. ADD_TRAIT(src, TRAIT_NO_TELEPORT, src) if(L && istype(L, /datum/ai_laws)) laws = L laws.associate(src) else make_laws() 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, "You have been installed as an AI! ") to_chat(src, "You must obey your silicon laws above all else. Your objectives will consider you to be dead.") to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).") to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.") to_chat(src, "While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.") 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, "These laws may be changed by other players, or by you being the traitor.") job = "AI" create_eye() apply_pref_name("ai") set_core_display_icon() holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"female")) spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src) spark_system.attach(src) add_verb(src, /mob/living/silicon/ai/proc/show_laws_verb) aiPDA = new/obj/item/pda/ai(src) aiPDA.owner = name aiPDA.ownjob = "AI" aiPDA.name = 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) custom_holoform.Grant(src) if(isturf(loc)) add_verb(src, list(/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.network = list("ss13") /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_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 = src.icon, icon_state = "ai-random") continue iconstates[option] = image(icon = src.icon, icon_state = resolve_ai_icon(option)) view_core() var/ai_core_icon = show_radial_menu(src, src , iconstates, radius = 42) if(!ai_core_icon || incapacitated()) return display_icon_override = ai_core_icon set_core_display_icon(ai_core_icon) /mob/living/silicon/ai/get_status_tab_items() . = ..() if(stat != CONSCIOUS) . += text("Systems nonfunctional") return . += text("System integrity: [(health + 100) * 0.5]%") . += text("Connected cyborgs: [length(connected_robots)]") for(var/r in connected_robots) var/mob/living/silicon/robot/connected_robot = r var/robot_status = "Nominal" if(connected_robot.shell) robot_status = "AI SHELL" else if(connected_robot.stat != CONSCIOUS || !connected_robot.client) robot_status = "OFFLINE" else if(!connected_robot.cell || connected_robot.cell.charge <= 0) robot_status = "DEPOWERED" //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies! . += text("[connected_robot.name] | S.Integrity: [connected_robot.health]% | Cell: [connected_robot.cell ? "[connected_robot.cell.charge]/[connected_robot.cell.maxcharge]" : "Empty"] | \ Module: [connected_robot.designation] | Loc: [get_area_name(connected_robot, TRUE)] | Status: [robot_status]") . += text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]") //Count of total AI shells /mob/living/silicon/ai/proc/ai_alerts() var/dat = "Current Station Alerts\n" dat += "Close

" for (var/cat in alarms) dat += text("[]
\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 += "" if (C && istype(C, /list)) var/dat2 = "" for (var/obj/machinery/camera/I in C) dat2 += text("[][]", (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.name, Ctmp.c_tag) else dat += text("-- [] (No Camera)", A.name) if (sources.len > 1) dat += text("- [] sources", sources.len) dat += "
\n" else dat += "-- All Systems Nominal
\n" dat += "
\n" viewalerts = 1 src << browse(dat, "window=aialerts&can_close=0") /mob/living/silicon/ai/proc/ai_call_shuttle() if(control_disabled) to_chat(usr, "Wireless control is disabled!") 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(!target) return if ((ai.z != target.z) && !is_station_level(ai.z)) return FALSE if (istype(loc, /obj/item/aicard)) var/turf/T0 = get_turf(src) var/turf/T1 = get_turf(A) if (!T0 || ! T1) return FALSE return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.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(incapacitated()) return var/is_anchored = FALSE if(move_resist == MOVE_FORCE_OVERPOWERING) move_resist = MOVE_FORCE_NORMAL REMOVE_TRAIT(src, TRAIT_NO_TELEPORT, src) else is_anchored = TRUE move_resist = MOVE_FORCE_OVERPOWERING ADD_TRAIT(src, TRAIT_NO_TELEPORT, src) to_chat(src, "You are now [is_anchored ? "" : "un"]anchored.") // the message in the [] will change depending whether or not the AI is anchored // AIs are immobile /mob/living/silicon/ai/update_mobility() mobility_flags = ALL return ALL /mob/living/silicon/ai/proc/ai_cancel_call() set category = "Malfunction" if(control_disabled) to_chat(src, "Wireless control is disabled!") 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"]) if(H) H.attack_ai(src) //may as well recycle else to_chat(src, "Unable to locate the holopad.") 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["callbot"]) //Command a bot to move to a selected location. if(call_bot_cooldown > world.time) to_chat(src, "Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route.") return Bot = locate(href_list["callbot"]) in GLOB.alive_mob_list if(!Bot || Bot.remote_disabled || src.control_disabled) return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off. waypoint_mode = 1 to_chat(src, "Set your waypoint by clicking on a valid location free of obstructions.") return if(href_list["interface"]) //Remotely connect to a bot! Bot = locate(href_list["interface"]) in GLOB.alive_mob_list if(!Bot || Bot.remote_disabled || src.control_disabled) return Bot.attack_ai(src) if(href_list["botrefresh"]) //Refreshes the bot control panel. botcall() return if (href_list["ai_take_control"]) //Mech domination var/obj/mecha/M = locate(href_list["ai_take_control"]) if(controlled_mech) to_chat(src, "You are already loaded into an onboard computer!") return if(!GLOB.cameranet.checkCameraVis(M)) to_chat(src, "Exosuit is no longer near active cameras.") return if(!isturf(loc)) to_chat(src, "You aren't in your core!") 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(incapacitated()) return if(control_disabled) to_chat(src, "Wireless control is disabled.") return var/turf/ai_current_turf = get_turf(src) var/ai_Zlevel = ai_current_turf.z var/d d += "Query network status
" d += "" for (Bot in GLOB.alive_mob_list) if(Bot.z == ai_Zlevel && !Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected! var/bot_mode = Bot.get_mode() d += "" //If the bot is on, it will display the bot's current mode status. If the bot is not mode, it will just report "Idle". "Inactive if it is not on at all. d += "" d += "" d += "" d += "" d += "" d = format_text(d) var/datum/browser/popup = new(src, "botcall", "Remote Robot Control", 700, 400) popup.set_content(d) popup.open() /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, "Selected location is not visible.") /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, "Interface error. Unit is already in use.") return to_chat(src, "Sending command to bot...") 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]! ([C.c_tag])", class) else if (CL && CL.len) var/foo = 0 var/dat2 = "" for (var/obj/machinery/camera/I in CL) dat2 += text("[][]", (!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, "Switched to the \"[uppertext(network)]\" camera network.") //End of code by Mord_Sith /mob/living/silicon/ai/proc/choose_modules() set category = "Malfunction" set name = "Choose Module" malf_picker.use(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/n_emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions if(!n_emote) return emote_display = n_emote for (var/each in GLOB.ai_status_displays) //change status of displays var/obj/machinery/status_display/ai/M = each M.emotion = emote_display M.update() if (emote_display == "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( "female" = 'icons/mob/ai.dmi', "male" = 'icons/mob/ai.dmi', "floating face" = 'icons/mob/ai.dmi', "green face" = 'icons/mob/ai.dmi', "xeno queen" = 'icons/mob/alien.dmi', "horror" = 'icons/mob/ai.dmi', "creature" = 'icons/mob/ai.dmi', "custom" ) input = input("Please select a hologram:") as null|anything in icon_list if(input) qdel(holo_icon) switch(input) if("custom") if(client?.prefs?.custom_holoform_icon) holo_icon = client.prefs.get_filtered_holoform(HOLOFORM_FILTER_AI) else holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "female")) if("xeno queen") holo_icon = getHologramIcon(icon(icon_list[input],"alienq")) else holo_icon = getHologramIcon(icon(icon_list[input], input)) /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, "You are already in your Main Core.") 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, "Transfer failed.") return disconnect_shell() //If the AI is controlling a borg, force the player back to core! if(!mind) to_chat(user, "No intelligence patterns detected." ) return ShutOffDoomsdayDevice() new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location. ai_restore_power()//So the AI initially has power. control_disabled = 1//Can't control things remotely if you're stuck in a card! radio_enabled = 0 //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, "Transfer successful: [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, ignore_grab) 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, "You can't do that right now!") return FALSE if(be_close && !in_range(M, src)) to_chat(src, "You are too far away!") 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, message_mode) var/treated_message = lang_treat(speaker, message_language, raw_message, spans, message_mode) var/start = "Relayed Speech: " var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]" var/hrefpart = "" var/jobpart = "Unknown" if (iscarbon(speaker)) var/mob/living/carbon/S = speaker if(S.job) jobpart = "[S.job]" var/rendered = "[start][hrefpart][namepart] ([jobpart])[treated_message]" if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker))) create_chat_message(speaker, message_language, raw_message, spans, message_mode) show_message(rendered, MSG_AUDIBLE) /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 60 seconds.") view_core() //A BYOND bug requires you to be viewing your core before your verbs update add_verb(src, /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) return 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) SEND_SIGNAL(src, COMSIG_MOB_RESET_PERSPECTIVE, A) return TRUE /mob/living/silicon/ai/revive(full_heal = 0, admin_revive = 0) . = ..() if(.) //successfully ressuscitated from death set_eyeobj_visible(TRUE) set_core_display_icon(display_icon_override) /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, "Hack aborted. The designated APC no longer exists on the power network.") playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, TRUE, ignore_walls = FALSE) else if(apc.aidisabled) to_chat(src, "Hack aborted. \The [apc] is no longer responding to our systems.") playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, TRUE, ignore_walls = FALSE) else malf_picker.processing_time += 10 apc.malfai = parent || src apc.malfhack = TRUE apc.locked = TRUE apc.coverlocked = TRUE playsound(get_turf(src), 'sound/machines/ding.ogg', 50, TRUE, 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, "Wireless networking module is offline.") 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, "Your remote connection has been reset!") deployed_shell.undeploy() diag_hud_set_deployed() /mob/living/silicon/ai/do_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() /mob/living/silicon/ai/verb/ai_cryo() set name = "AI Cryogenic Stasis" set desc = "Puts the current AI personality into cryogenic stasis, freeing the space for another." set category = "AI Commands" if(incapacitated()) return switch(alert("Would you like to enter cryo? This will ghost you. Remember to AHELP before cryoing out of important roles, even with no admins online.",,"Yes.","No.")) if("Yes.") src.ghostize(FALSE, penalize = TRUE) var/announce_rank = "Artificial Intelligence," if(GLOB.announcement_systems.len) // Sends an announcement the AI has cryoed. var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) announcer.announce("CRYOSTORAGE", src.real_name, announce_rank, list()) new /obj/structure/AIcore/latejoin_inactive(loc) if(src.mind) //Handle job slot/tater cleanup. if(src.mind.assigned_role == "AI") SSjob.FreeRole("AI") src.mind.special_role = null qdel(src) else return /mob/living/silicon/ai/emote(act, m_type=1, message = null, intentional = FALSE) if(current && eyeobj) return eyeobj.emote(act, m_type, message, intentional, forced = TRUE) return ..() /mob/living/silicon/ai/zMove(dir, feedback = FALSE) . = eyeobj.zMove(dir, feedback)

Name

Status

Location

Control

[Bot.hacked ? "(!)" : ""] [Bot.name] ([Bot.model])[bot_mode][get_area_name(Bot, TRUE)]InterfaceCall