|
|
|
@@ -1,5 +1,7 @@
|
|
|
|
|
#define AI_CHECK_WIRELESS 1
|
|
|
|
|
#define AI_CHECK_RADIO 2
|
|
|
|
|
#define AI_ANNOUNCEMENT_COOLDOWN 1 MINUTE
|
|
|
|
|
#define AI_EMERGENCY_MESSAGE_COOLDOWN 30 SECONDS
|
|
|
|
|
|
|
|
|
|
GLOBAL_LIST_INIT_TYPED(ai_list, /mob/living/silicon/ai, list())
|
|
|
|
|
|
|
|
|
@@ -55,19 +57,23 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
var/carded
|
|
|
|
|
|
|
|
|
|
// Holopad and holograms
|
|
|
|
|
var/mob/holo_icon // an abstract mob used to store icon info
|
|
|
|
|
var/hologram_follow = TRUE //This is used for the AI eye, to determine if a holopad's hologram should follow it or not
|
|
|
|
|
/// An abstract mob used to store icon info
|
|
|
|
|
var/mob/holo_icon
|
|
|
|
|
/// This is used for the AI eye, to determine if a holopad's hologram should follow it or not
|
|
|
|
|
var/hologram_follow = TRUE
|
|
|
|
|
|
|
|
|
|
// Equipment
|
|
|
|
|
/// What camera network the AI can view
|
|
|
|
|
var/list/network = list("Station")
|
|
|
|
|
var/obj/machinery/camera/camera
|
|
|
|
|
var/list/cameraRecords = list() //For storing what is shown to the cameras
|
|
|
|
|
/// For storing what is shown to the cameras
|
|
|
|
|
var/list/cameraRecords = list()
|
|
|
|
|
var/obj/item/device/multitool/ai_multi
|
|
|
|
|
var/obj/item/device/radio/headset/heads/ai_integrated/ai_radio
|
|
|
|
|
var/datum/announcement/priority/announcement
|
|
|
|
|
var/obj/machinery/ai_powersupply/psupply
|
|
|
|
|
|
|
|
|
|
// Borgs
|
|
|
|
|
/// Borgs linked to AI
|
|
|
|
|
var/list/connected_robots = list()
|
|
|
|
|
|
|
|
|
|
// Damage variables
|
|
|
|
@@ -77,35 +83,59 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
|
|
|
|
|
// Misc variables
|
|
|
|
|
var/last_announcement = ""
|
|
|
|
|
/// Uses `world.time` to track when the last announcement was made. Used in `ai_announcement()`
|
|
|
|
|
var/last_announcement_time
|
|
|
|
|
/// Uses `world.time` to track when the last emergency message was sent. Used in `ai_emergency_message()`
|
|
|
|
|
var/last_emergency_message_time
|
|
|
|
|
/// If TRUE, AI is on backup power (restricts most actions)
|
|
|
|
|
var/ai_restore_power_routine = FALSE
|
|
|
|
|
var/view_alerts = FALSE
|
|
|
|
|
var/camera_light_on = FALSE //Defines if the AI toggled the light on the camera it's looking through.
|
|
|
|
|
/// Defines if the AI toggled the light on the camera it's looking through.
|
|
|
|
|
var/camera_light_on = FALSE
|
|
|
|
|
var/datum/trackable/track
|
|
|
|
|
var/control_disabled = FALSE
|
|
|
|
|
var/multitool_mode = FALSE
|
|
|
|
|
|
|
|
|
|
// Malf variables
|
|
|
|
|
var/malfunctioning = FALSE // Master var that determines if AI is malfunctioning.
|
|
|
|
|
var/datum/malf_hardware/hardware // Installed piece of hardware.
|
|
|
|
|
var/datum/malf_research/research // Malfunction research datum.
|
|
|
|
|
var/obj/machinery/power/apc/hack // APC that is currently being hacked.
|
|
|
|
|
var/list/hacked_apcs = list() // List of all hacked APCs
|
|
|
|
|
var/APU_power = FALSE // If set to 1 AI runs on APU power
|
|
|
|
|
var/hacking = FALSE // Set to 1 if AI is hacking APC, cyborg, other AI, or running system override.
|
|
|
|
|
var/system_override = FALSE // Set to 1 if system override is initiated, 2 if succeeded.
|
|
|
|
|
var/synthetic_takeover = FALSE // 1 is started, 2 is complete.
|
|
|
|
|
var/hack_can_fail = TRUE // If 0, all abilities have zero chance of failing.
|
|
|
|
|
var/hack_fails = 0 // This increments with each failed hack, and determines the warning message text.
|
|
|
|
|
var/errored = FALSE // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit.
|
|
|
|
|
var/bombing_core = FALSE // Set to 1 if core auto-destruct is activated
|
|
|
|
|
var/bombing_station = FALSE // Set to 1 if station nuke auto-destruct is activated
|
|
|
|
|
var/bombing_time = 1200 // How much time is remaining for the nuke
|
|
|
|
|
var/override_CPUStorage = 0 // Bonus/Penalty CPU Storage. For use by admins/testers.
|
|
|
|
|
var/override_CPURate = 0 // Bonus/Penalty CPU generation rate. For use by admins/testers.
|
|
|
|
|
/// Master var that determines if AI is malfunctioning.
|
|
|
|
|
var/malfunctioning = FALSE
|
|
|
|
|
/// Installed piece of Malfunction hardware.
|
|
|
|
|
var/datum/malf_hardware/hardware
|
|
|
|
|
/// Malfunction research datum.
|
|
|
|
|
var/datum/malf_research/research
|
|
|
|
|
/// APC that is currently being hacked.
|
|
|
|
|
var/obj/machinery/power/apc/hack
|
|
|
|
|
/// List of all hacked APCs
|
|
|
|
|
var/list/hacked_apcs = list()
|
|
|
|
|
/// If set to TRUE AI runs on APU power
|
|
|
|
|
var/APU_power = FALSE
|
|
|
|
|
/// Set to TRUE if AI is hacking APC, cyborg, other AI, or running system override.
|
|
|
|
|
var/hacking = FALSE
|
|
|
|
|
/// Set to 1 if system override is initiated, 2 if succeeded.
|
|
|
|
|
var/system_override = 0
|
|
|
|
|
/// Set to 1 if synthetic takeover has started, 2 if it is complete.
|
|
|
|
|
var/synthetic_takeover = FALSE
|
|
|
|
|
/// If FALSE, all abilities have zero chance of failing.
|
|
|
|
|
var/hack_can_fail = TRUE
|
|
|
|
|
/// This increments with each failed hack, and determines the warning message text.
|
|
|
|
|
var/hack_fails = 0
|
|
|
|
|
/// Set to TRUE if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit.
|
|
|
|
|
var/errored = FALSE
|
|
|
|
|
/// Set to TRUE if core auto-destruct is activated
|
|
|
|
|
var/bombing_core = FALSE
|
|
|
|
|
/// Set to TRUE if station nuke auto-destruct is activated
|
|
|
|
|
var/bombing_station = FALSE
|
|
|
|
|
/// How much time is remaining for the nuke
|
|
|
|
|
var/bombing_time = 1200
|
|
|
|
|
/// Bonus/Penalty CPU Storage. For use by admins/testers.
|
|
|
|
|
var/override_CPUStorage = 0
|
|
|
|
|
/// Bonus/Penalty CPU generation rate. For use by admins/testers.
|
|
|
|
|
var/override_CPURate = 0
|
|
|
|
|
|
|
|
|
|
// Sprites
|
|
|
|
|
var/datum/ai_icon/selected_sprite // The selected icon set
|
|
|
|
|
var/custom_sprite = FALSE // Whether the selected icon is custom
|
|
|
|
|
/// The selected icon set
|
|
|
|
|
var/datum/ai_icon/selected_sprite
|
|
|
|
|
/// Whether the selected icon is custom
|
|
|
|
|
var/custom_sprite = FALSE
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/proc/add_ai_verbs()
|
|
|
|
|
add_verb(src, GLOB.ai_verbs_default)
|
|
|
|
@@ -293,6 +323,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
set src = usr.contents
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
/// Proc override for AI SetName()
|
|
|
|
|
/mob/living/silicon/ai/SetName(pickedName as text)
|
|
|
|
|
..()
|
|
|
|
|
announcement.announcer = pickedName
|
|
|
|
@@ -311,9 +342,9 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
setup_icon() //this is because the ai custom name is related to the ai name, so, we just call the setup icon after someone named their ai
|
|
|
|
|
SSrecords.reset_manifest()
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
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.
|
|
|
|
|
/**
|
|
|
|
|
* The AI Power supply is a dummy object used for powering the AI since only machinery should be using power.
|
|
|
|
|
* The alternative was to rewrite a bunch of AI code instead here we are.
|
|
|
|
|
*/
|
|
|
|
|
/obj/machinery/ai_powersupply
|
|
|
|
|
name = "power supply"
|
|
|
|
@@ -393,16 +424,16 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
|
|
|
|
|
if (!custom_sprite)
|
|
|
|
|
var/new_sprite = tgui_input_list(src, "Select an icon!", "AI", GLOB.ai_icons, selected_sprite)
|
|
|
|
|
if(new_sprite) selected_sprite = new_sprite
|
|
|
|
|
if(new_sprite) selected_sprite = GLOB.ai_icons[new_sprite]
|
|
|
|
|
update_icon()
|
|
|
|
|
|
|
|
|
|
// this verb lets the ai see the stations manifest
|
|
|
|
|
/// This verb lets the ai see the stations manifest
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_roster()
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
|
set name = "Show Crew Manifest"
|
|
|
|
|
SSrecords.open_manifest_tgui(usr)
|
|
|
|
|
|
|
|
|
|
//AI Examine code
|
|
|
|
|
/// AI Examine code
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_examine(atom/A as mob|obj|turf in view(src.eyeobj))
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
|
set name = "Examine"
|
|
|
|
@@ -414,29 +445,33 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
face_atom(A)
|
|
|
|
|
A.examine(src)
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/var/message_cooldown = 0
|
|
|
|
|
/**
|
|
|
|
|
* AI verb to make announcements
|
|
|
|
|
*
|
|
|
|
|
* `last_announcement_time` is compared with `world.time` and `AI_ANNOUNCEMENT_COOLDOWN` to prevent announcement spam
|
|
|
|
|
*/
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_announcement()
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
|
set name = "Make Station Announcement"
|
|
|
|
|
set name = "Make Ship-wide Announcement"
|
|
|
|
|
|
|
|
|
|
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
|
|
|
|
|
return
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
if(message_cooldown)
|
|
|
|
|
to_chat(src, "Please allow one minute to pass between announcements.")
|
|
|
|
|
return
|
|
|
|
|
if((world.time - last_announcement_time) < AI_ANNOUNCEMENT_COOLDOWN)
|
|
|
|
|
to_chat(src, SPAN_WARNING("You need to wait <b>[DisplayTimeText(AI_ANNOUNCEMENT_COOLDOWN - (world.time - last_announcement_time))]</b> before you can make another announcement."))
|
|
|
|
|
return FALSE
|
|
|
|
|
var/input = tgui_input_text(usr, "Please write a message to announce to the [station_name(TRUE)] crew.", "A.I. Announcement", multiline = TRUE)
|
|
|
|
|
if(!input)
|
|
|
|
|
return
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
|
|
|
|
|
return
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
announcement.Announce(input)
|
|
|
|
|
message_cooldown = 1
|
|
|
|
|
spawn(600)//One minute cooldown
|
|
|
|
|
message_cooldown = 0
|
|
|
|
|
last_announcement_time = world.time
|
|
|
|
|
return TRUE
|
|
|
|
|
|
|
|
|
|
/// Allows AI to call evacuation
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_call_shuttle()
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
|
set name = "Call Evacuation"
|
|
|
|
@@ -454,6 +489,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
|
|
|
|
|
post_display_status("shuttle")
|
|
|
|
|
|
|
|
|
|
/// Allows AI to cancel evacuation
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_recall_shuttle()
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
|
set name = "Cancel Evacuation"
|
|
|
|
@@ -468,28 +504,32 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
if(confirm == "Yes")
|
|
|
|
|
cancel_call_proc(src)
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/var/emergency_message_cooldown = 0
|
|
|
|
|
/**
|
|
|
|
|
* AI verb to send emergency message to Centcom (admins)
|
|
|
|
|
*
|
|
|
|
|
* `last_emergency_message_time` is compared with `world.time` and `AI_EMERGENCY_MESSAGE_COOLDOWN` to reduce message spam
|
|
|
|
|
*/
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_emergency_message()
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
|
set name = "Send Emergency Message"
|
|
|
|
|
|
|
|
|
|
if(check_unable(AI_CHECK_WIRELESS))
|
|
|
|
|
return
|
|
|
|
|
return FALSE
|
|
|
|
|
if(!is_relay_online())
|
|
|
|
|
to_chat(usr, SPAN_WARNING("No Emergency Bluespace Relay detected. Unable to transmit message."))
|
|
|
|
|
return
|
|
|
|
|
if(emergency_message_cooldown)
|
|
|
|
|
to_chat(usr, SPAN_WARNING("Arrays recycling. Please stand by."))
|
|
|
|
|
return
|
|
|
|
|
return FALSE
|
|
|
|
|
if((world.time - last_emergency_message_time) < AI_EMERGENCY_MESSAGE_COOLDOWN)
|
|
|
|
|
to_chat(src, SPAN_WARNING("Arrays are recycling. You need to wait <b>[DisplayTimeText(AI_EMERGENCY_MESSAGE_COOLDOWN - (world.time - last_emergency_message_time))]</b> before you can send another message."))
|
|
|
|
|
return FALSE
|
|
|
|
|
var/input = sanitize(input(usr, "Please choose a message to transmit to [SSatlas.current_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", ""))
|
|
|
|
|
if(!input)
|
|
|
|
|
return
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
Centcomm_announce(input, usr)
|
|
|
|
|
to_chat(usr, SPAN_NOTICE("Message transmitted."))
|
|
|
|
|
log_say("[key_name(usr)] has made an AI [SSatlas.current_map.boss_short] announcement: [input]")
|
|
|
|
|
emergency_message_cooldown = 1
|
|
|
|
|
spawn(300)
|
|
|
|
|
emergency_message_cooldown = 0
|
|
|
|
|
log_say("[key_name(usr)] has sent a message to [SSatlas.current_map.boss_short]: [input]")
|
|
|
|
|
last_emergency_message_time = world.time
|
|
|
|
|
return TRUE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/check_eye(var/mob/user as mob)
|
|
|
|
@@ -498,7 +538,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/restrained()
|
|
|
|
|
return 0
|
|
|
|
|
return FALSE
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/emp_act(severity)
|
|
|
|
|
. = ..()
|
|
|
|
@@ -594,9 +634,10 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
//src.cameraFollow = null
|
|
|
|
|
src.view_core()
|
|
|
|
|
|
|
|
|
|
//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm
|
|
|
|
|
//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead
|
|
|
|
|
//Addition by Mord_Sith to define AI's network change ability
|
|
|
|
|
/** Replaces `/mob/living/silicon/ai/verb/change_network()` in ai.dm & camera.dm
|
|
|
|
|
* Adds in `/mob/living/silicon/ai/proc/ai_network_change()` instead
|
|
|
|
|
* Addition by Mord_Sith to define AI's network change ability
|
|
|
|
|
*/
|
|
|
|
|
/mob/living/silicon/ai/proc/get_camera_network_list()
|
|
|
|
|
if(check_unable())
|
|
|
|
|
return
|
|
|
|
@@ -633,7 +674,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
eyeobj.setLoc(get_turf(C))
|
|
|
|
|
break
|
|
|
|
|
to_chat(src, SPAN_NOTICE("Switched to [network] camera network."))
|
|
|
|
|
//End of code by Mord_Sith
|
|
|
|
|
|
|
|
|
|
/mob/living/silicon/ai/proc/ai_statuschange()
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
@@ -695,7 +735,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
holo_icon.set_invisibility(0)
|
|
|
|
|
holo_icon.icon = I
|
|
|
|
|
|
|
|
|
|
//Toggles the luminosity and applies it by re-entereing the camera.
|
|
|
|
|
/// Toggles the luminosity and applies it by re-entereing the camera.
|
|
|
|
|
/mob/living/silicon/ai/proc/toggle_camera_light()
|
|
|
|
|
set name = "Toggle Camera Light"
|
|
|
|
|
set desc = "Toggles the light on the camera the AI is looking through."
|
|
|
|
@@ -715,9 +755,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Handled camera lighting, when toggled.
|
|
|
|
|
// It will get the nearest camera from the eyeobj, lighting it.
|
|
|
|
|
|
|
|
|
|
/// Handles camera lighting, when toggled, it will get the nearest camera from the `eyeobj`, lighting it.
|
|
|
|
|
/mob/living/silicon/ai/proc/lightNearbyCamera()
|
|
|
|
|
if(camera_light_on && camera_light_on < world.timeofday)
|
|
|
|
|
if(src.camera)
|
|
|
|
@@ -795,6 +833,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
to_chat(src, "Radio keys: [radio_keys]")
|
|
|
|
|
src.ai_radio.interact(src)
|
|
|
|
|
|
|
|
|
|
/// Verb to call `toggle_sensor_mode()` from `silicon.dm`
|
|
|
|
|
/mob/living/silicon/ai/proc/sensor_mode()
|
|
|
|
|
set name = "Set Sensor Augmentation"
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
@@ -829,6 +868,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
else
|
|
|
|
|
SSvirtualreality.mind_transfer(src, choice)
|
|
|
|
|
|
|
|
|
|
/// AI verb to toggle `hologram_follow`
|
|
|
|
|
/mob/living/silicon/ai/proc/toggle_hologram_movement()
|
|
|
|
|
set name = "Toggle Hologram Movement"
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
@@ -837,6 +877,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
hologram_follow = !hologram_follow
|
|
|
|
|
to_chat(usr, "<span class='info'>Your hologram will now [hologram_follow ? "follow" : "no longer follow"] you.</span>")
|
|
|
|
|
|
|
|
|
|
/// Checks if AI is able to perform certain actions by checking if dead, depowered, disconnected, etc.
|
|
|
|
|
/mob/living/silicon/ai/proc/check_unable(var/flags = 0, var/feedback = 1)
|
|
|
|
|
if(stat == DEAD)
|
|
|
|
|
if(feedback) to_chat(src, SPAN_WARNING("You are dead!"))
|
|
|
|
@@ -858,6 +899,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
/mob/living/silicon/ai/proc/is_in_chassis()
|
|
|
|
|
return istype(loc, /turf)
|
|
|
|
|
|
|
|
|
|
/// AI verb to toggle `multitool_mode`
|
|
|
|
|
/mob/living/silicon/ai/proc/multitool_mode()
|
|
|
|
|
set name = "Toggle Multitool Mode"
|
|
|
|
|
set category = "AI Commands"
|
|
|
|
@@ -878,7 +920,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
icon_state = selected_sprite.alive_icon
|
|
|
|
|
set_light(1, 1, selected_sprite.alive_light)
|
|
|
|
|
|
|
|
|
|
// Pass lying down or getting up to our pet human, if we're in a rig.
|
|
|
|
|
/// Pass lying down or getting up to our pet human, if we're in a rig.
|
|
|
|
|
/mob/living/silicon/ai/lay_down()
|
|
|
|
|
set name = "Rest"
|
|
|
|
|
set category = "IC"
|
|
|
|
@@ -904,8 +946,11 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
|
|
|
|
cameraRecords += list(s)
|
|
|
|
|
return cameraRecords.len
|
|
|
|
|
|
|
|
|
|
/// Checks if AI is on backup power by checking`ai_restore_power_routine`. Returns TRUE if `ai_restore_power_routine` is FALSE
|
|
|
|
|
/mob/living/silicon/ai/proc/has_power()
|
|
|
|
|
return (ai_restore_power_routine == 0)
|
|
|
|
|
return (ai_restore_power_routine == FALSE)
|
|
|
|
|
|
|
|
|
|
#undef AI_CHECK_WIRELESS
|
|
|
|
|
#undef AI_CHECK_RADIO
|
|
|
|
|
#undef AI_ANNOUNCEMENT_COOLDOWN
|
|
|
|
|
#undef AI_EMERGENCY_MESSAGE_COOLDOWN
|
|
|
|
|