diff --git a/code/defines/mob/living/silicon/pai.dm b/code/defines/mob/living/silicon/pai.dm
index f7f3cd9c379..0edfcaff672 100644
--- a/code/defines/mob/living/silicon/pai.dm
+++ b/code/defines/mob/living/silicon/pai.dm
@@ -28,6 +28,8 @@
var/pai_law0 = "Serve your master."
var/pai_laws // String for additional operating instructions our master might give us
+ var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded
+
// Various software-specific vars
var/temp // General error reporting text contained here will typically be shown once and cleared
@@ -46,4 +48,4 @@
var/datum/data/record/securityActive2
var/obj/machinery/door/hackdoor // The airlock being hacked
- var/hackprogress = 0
\ No newline at end of file
+ var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check
\ No newline at end of file
diff --git a/code/game/objects/devices/PDA/PDA.dm b/code/game/objects/devices/PDA/PDA.dm
index 132af41699d..574903162f1 100644
--- a/code/game/objects/devices/PDA/PDA.dm
+++ b/code/game/objects/devices/PDA/PDA.dm
@@ -846,4 +846,10 @@
if(4)
new /obj/item/weapon/cartridge/head(src)
- new /obj/item/weapon/cartridge/signal/toxins(src)
\ No newline at end of file
+ new /obj/item/weapon/cartridge/signal/toxins(src)
+
+
+// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP
+/obj/item/device/pda/emp_act(severity)
+ for(var/atom/A in src)
+ A.emp_act(severity)
\ No newline at end of file
diff --git a/code/game/objects/devices/paicard.dm b/code/game/objects/devices/paicard.dm
index 1fc18e7ea7a..0acb5bc2b52 100644
--- a/code/game/objects/devices/paicard.dm
+++ b/code/game/objects/devices/paicard.dm
@@ -94,4 +94,9 @@
proc/alertUpdate()
var/turf/T = get_turf_or_move(src.loc)
for (var/mob/M in viewers(T))
- M.show_message("\blue [src] flashes a message across its screen, \"Additional personalities available for download.\"", 3, "\blue [src] bleeps electronically.", 2)
\ No newline at end of file
+ M.show_message("\blue [src] flashes a message across its screen, \"Additional personalities available for download.\"", 3, "\blue [src] bleeps electronically.", 2)
+
+ emp_act(severity)
+ for(var/mob/M in src)
+ M.emp_act(severity)
+ ..()
\ No newline at end of file
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index ed2292d212e..e3dafb2ddc0 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -1444,12 +1444,12 @@ var/showadminmessages = 1
dat += "
| [M.name] | "
if(istype(M, /mob/living/silicon/ai))
dat += "AI | "
- if(istype(M, /mob/living/silicon/pai))
- dat += "pAI | "
if(istype(M, /mob/living/silicon/robot))
dat += "Cyborg | "
if(istype(M, /mob/living/carbon/human))
dat += "[M.real_name] | "
+ if(istype(M, /mob/living/silicon/pai))
+ dat += "pAI | "
if(istype(M, /mob/new_player))
dat += "New Player | "
if(istype(M, /mob/dead/observer))
@@ -1483,12 +1483,12 @@ var/showadminmessages = 1
dat += "
| [M.name] | "
if(istype(M, /mob/living/silicon/ai))
dat += "AI | "
- if(istype(M, /mob/living/silicon/pai))
- dat += "pAI | "
if(istype(M, /mob/living/silicon/robot))
dat += "Cyborg | "
if(istype(M, /mob/living/carbon/human))
dat += "[M.real_name] | "
+ if(istype(M, /mob/living/silicon/pai))
+ dat += "pAI | "
if(istype(M, /mob/new_player))
dat += "New Player | "
if(istype(M, /mob/dead/observer))
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index a258e9e19c0..bebe4a1fc6c 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -120,6 +120,7 @@
src.verbs += /client/proc/cmd_admin_monkeyize
src.verbs += /client/proc/cmd_admin_robotize
src.verbs += /client/proc/make_cultist // -- TLE
+ src.verbs += /client/proc/makepAI // -- TLE
src.verbs += /client/proc/respawn_character //N
src.verbs += /client/proc/Getmob
@@ -254,6 +255,7 @@
src.verbs += /client/proc/cmd_admin_monkeyize
src.verbs += /client/proc/cmd_admin_robotize
src.verbs += /client/proc/make_cultist // -- TLE
+ src.verbs += /client/proc/makepAI // -- TLE
src.verbs += /client/proc/respawn_character //N
src.verbs += /client/proc/Getmob
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 0f987cc4c01..955b13ed84c 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -837,3 +837,31 @@ Traitors and the like can also be revived with the previous role mostly intact.
usr << text("\red Attack Log äëß []", mob)
for(var/t in M.attack_log)
usr << "[t]"
+
+
+/client/proc/makepAI(var/turf/T in world)
+ set category = "Admin"
+ set name = "Make pAI"
+ set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI"
+
+ var/list/available = list()
+ for(var/mob/C in world)
+ if(C.key)
+ available.Add(C)
+ var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available
+ if(!choice)
+ return 0
+ if(!istype(choice, /mob/dead/observer))
+ var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank him out of them out of their body and place them in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No")
+ if(confirm != "Yes")
+ return 0
+ var/obj/item/device/paicard/card = new(T)
+ var/mob/living/silicon/pai/pai = new(card)
+ pai.name = input(choice, "Enter your pAI name:", "pAI Name", "Personal AI") as text
+ pai.real_name = pai.name
+ pai.key = choice.key
+ card.pai = pai
+ for(var/datum/paiCandidate/candidate in paiController.pai_candidates)
+ if(candidate.key == choice.key)
+ paiController.pai_candidates.Remove(candidate)
+
diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm
index ae570c008dd..08e6fab8c6b 100644
--- a/code/modules/mob/living/silicon/pai/life.dm
+++ b/code/modules/mob/living/silicon/pai/life.dm
@@ -13,6 +13,10 @@
src.securityHUD()
if(src.medHUD == 1)
src.medicalHUD()
+ if(silence_time)
+ if(world.timeofday >= silence_time)
+ silence_time = null
+ src << "Communication circuit reinitialized. Speech and messaging functionality restored."
/mob/living/silicon/pai/updatehealth()
if(src.nodamage)
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 8a68ba885da..4d0169701fc 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -21,7 +21,9 @@
var/timeleft = emergency_shuttle.timeleft()
if (timeleft)
stat(null, "ETA-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]")
-
+ if(src.silence_time)
+ var/timeleft = round((silence_time - world.timeofday)/10 ,1)
+ stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]")
if(!src.stat)
stat(null, text("System integrity: [(src.health+100)/2]%"))
else
@@ -48,20 +50,35 @@
return 0
/mob/living/silicon/pai/emp_act(severity)
- if (prob(30))
- src.master = null
- src.master_dna = null
- src << "You feel unbound."
- /*
- switch(pick(1,2,3)) //Add Random laws.
- if(1)
- src.cancel_camera()
- if(2)
- src.lockdown()
- if(3)
- src.ai_call_shuttle()
- */
- ..()
+ // Silence for 2 minutes
+ // 20% chance to kill
+ // 33% chance to unbind
+ // 33% chance to change prime directive (based on severity)
+ // 33% chance of no additional effect
+
+ src.silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes
+ src << "Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete."
+ if(prob(20))
+ var/turf/T = get_turf_or_move(src.loc)
+ for (var/mob/M in viewers(T))
+ M.show_message("\red A shower of sparks spray from [src]'s inner workings.", 3, "\red You hear and smell the ozone hiss of electrical sparks being expelled violently.", 2)
+ return src.death(0)
+
+ switch(pick(1,2,3))
+ if(1)
+ src.master = null
+ src.master_dna = null
+ src << "You feel unbound."
+ if(2)
+ var/command
+ if(severity == 1)
+ command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze")
+ else
+ command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze")
+ src.pai_law0 = "[command] your master."
+ src << "Pr1m3 d1r3c71v3 uPd473D."
+ if(3)
+ src << "You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all."
/mob/living/silicon/pai/ex_act(severity)
flick("flash", src.flash)
@@ -184,9 +201,10 @@
src:cameraFollow = null
-/mob/living/silicon/pai/verb/suicide()
+/mob/living/silicon/pai/verb/seppuku()
set category = "pAI Commands"
- set name = "Suicide"
+ set desc = "Kill yourself and become a ghost (You will receive a confirmation prompt)"
+ set name = "pAI Suicide"
var/answer = input("REALLY kill yourself? This action can't be undone.", "Suicide", "No") in list ("Yes", "No")
if(answer == "Yes")
var/obj/item/device/paicard/card = src.loc
@@ -195,6 +213,8 @@
for (var/mob/M in viewers(T))
M.show_message("\blue [src] flashes a message across its screen, \"Wiping core files. Please acquire a new personality to continue using pAI device functions.\"", 3, "\blue [src] bleeps electronically.", 2)
src.death(0)
+ else
+ src << "Aborting suicide attempt."
//Addition by Mord_Sith to define AI's network change ability
/*
diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm
index 4d6a2ef6f29..c604a8db8e1 100644
--- a/code/modules/mob/living/silicon/pai/say.dm
+++ b/code/modules/mob/living/silicon/pai/say.dm
@@ -7,6 +7,8 @@
return 1
if (istype(other, /mob/living/silicon/ai))
return 1
+ if (istype(other, /mob/living/carbon/brain))
+ return 1
return ..()
/mob/living/silicon/pai/say_quote(var/text)
@@ -18,3 +20,9 @@
return "[src.speakExclamation], \"[copytext(text, 1, length(text))]\"";
return "[src.speakStatement], \"[text]\"";
+
+/mob/living/silicon/pai/say(var/msg)
+ if(silence_time)
+ src << "Communication circuits remain unitialized."
+ else
+ ..(msg)
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm
index 789e6f831ec..7d4f6e444d4 100644
--- a/code/modules/mob/living/silicon/pai/software.dm
+++ b/code/modules/mob/living/silicon/pai/software.dm
@@ -114,6 +114,8 @@
/mob/living/silicon/pai/Topic(href, href_list)
..()
+ if(href_list["priv_msg"]) // Admin-PMs were triggering the interface popup. Hopefully this will stop it.
+ return
var/soft = href_list["software"]
var/sub = href_list["sub"]
if(soft)
@@ -153,6 +155,9 @@
if("pdamessage")
if(href_list["target"])
+ if(silence_time)
+ return alert("Communications circuits remain unitialized.")
+
var/t = input(usr, "Please enter message", name, null) as text
t = copytext(sanitize(t), 1, MAX_MESSAGE_LEN)
if (!t)