diff --git a/_maps/map_files/TgStation/tgstation.2.1.3.dmm b/_maps/map_files/TgStation/tgstation.2.1.3.dmm
index 3299ce24c7d..d77e656a08c 100644
--- a/_maps/map_files/TgStation/tgstation.2.1.3.dmm
+++ b/_maps/map_files/TgStation/tgstation.2.1.3.dmm
@@ -6262,7 +6262,7 @@
"cqv" = (/turf/simulated/floor/plasteel{icon_state = "dark"},/area/aisat)
"cqw" = (/obj/machinery/door/airlock/hatch{name = "Teleporter Room"; req_access_txt = "17;65"},/turf/simulated/floor/plasteel{icon_state = "dark"},/area/aisat)
"cqx" = (/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/turf/simulated/wall/r_wall,/area/tcommsat/computer)
-"cqy" = (/obj/machinery/light{dir = 1},/obj/machinery/atmospherics/components/unary/vent_scrubber{dir = 8; on = 1; scrub_N2O = 0; scrub_Toxins = 0},/turf/simulated/floor/plasteel,/area/tcommsat/computer)
+"cqy" = (/obj/machinery/light{dir = 1},/obj/machinery/atmospherics/components/unary/vent_scrubber{dir = 8; on = 1; scrub_N2O = 0; scrub_Toxins = 0},/obj/machinery/announcement_system,/turf/simulated/floor/plasteel,/area/tcommsat/computer)
"cqz" = (/obj/structure/closet/crate/freezer,/obj/item/weapon/reagent_containers/blood/empty,/obj/item/weapon/reagent_containers/blood/empty{pixel_x = -3; pixel_y = -3},/obj/item/weapon/reagent_containers/blood/AMinus,/obj/item/weapon/reagent_containers/blood/BMinus{pixel_x = -4; pixel_y = 4},/obj/item/weapon/reagent_containers/blood/BPlus{pixel_x = 1; pixel_y = 2},/obj/item/weapon/reagent_containers/blood/OMinus,/obj/item/weapon/reagent_containers/blood/OPlus{pixel_x = -2; pixel_y = -1},/obj/item/weapon/reagent_containers/blood/random,/obj/item/weapon/reagent_containers/blood/random,/obj/item/weapon/reagent_containers/blood/random,/obj/machinery/atmospherics/components/unary/vent_scrubber{dir = 8; on = 1; scrub_N2O = 0; scrub_Toxins = 0},/turf/simulated/floor/plasteel{icon_state = "white"},/area/medical/virology)
"cqA" = (/obj/machinery/camera/emp_proof{c_tag = "Singularity West"; dir = 1; network = list("Singularity")},/turf/simulated/floor/plating/airless,/area/engine/engineering)
"cqB" = (/obj/machinery/camera/emp_proof{c_tag = "Singularity East"; dir = 1; network = list("Singularity")},/turf/simulated/floor/plating/airless,/area/engine/engineering)
@@ -6587,7 +6587,7 @@
"cwI" = (/obj/structure/table,/obj/item/weapon/paper_bin{pixel_x = -3; pixel_y = 7},/obj/item/weapon/pen,/turf/simulated/floor/plasteel{icon_state = "showroomfloor"},/area/security/brig)
"cwJ" = (/obj/machinery/camera{c_tag = "Brig Evidence Storage"; dir = 1},/obj/machinery/light,/obj/structure/table,/obj/item/weapon/hand_labeler,/turf/simulated/floor/plasteel{icon_state = "showroomfloor"},/area/security/brig)
"cwK" = (/obj/structure/sign/securearea{desc = "A warning sign which reads 'RADIOACTIVE AREA'"; icon_state = "radiation"; name = "RADIOACTIVE AREA"; pixel_x = 0; pixel_y = 0},/turf/simulated/wall,/area/engine/engineering)
-
+
(1,1,1) = {"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index 5d6c83b8206..e3d1114edbf 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -390,6 +390,7 @@ var/datum/subsystem/job/SSjob
else //We ran out of spare locker spawns!
break
+
/datum/subsystem/job/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist
if(!config.load_jobs_from_txt)
return 0
diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm
index e6b7500a866..3b453e491cc 100644
--- a/code/game/gamemodes/malfunction/malfunction.dm
+++ b/code/game/gamemodes/malfunction/malfunction.dm
@@ -302,4 +302,4 @@
text += "
"
world << text
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm
index 3d97869a1ca..2af85ca36ba 100644
--- a/code/game/jobs/job/captain.dm
+++ b/code/game/jobs/job/captain.dm
@@ -89,3 +89,5 @@ Head of Personnel
//Equip ID box & telebaton
H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/ids(H.back), slot_in_backpack)
H.equip_to_slot_or_del(new /obj/item/weapon/melee/classic_baton/telescopic(H), slot_in_backpack)
+
+ announce_head(H.mind, list("Supply", "Service")) //tell underlings (suuply/service) they have a head
diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm
index e686d1e9593..fa8f10f9eff 100644
--- a/code/game/jobs/job/engineering.dm
+++ b/code/game/jobs/job/engineering.dm
@@ -41,6 +41,8 @@ Chief Engineer
//Equip telebaton
H.equip_to_slot_or_del(new /obj/item/weapon/melee/classic_baton/telescopic(H), slot_in_backpack)
+ announce_head(H.mind, list("Engineering")) //tell underlings (engineering radio) they have a head
+
/*
Station Engineer
*/
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index e77878cd7f0..fe7638427f8 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -167,4 +167,10 @@
return max(0, minimal_player_age - C.player_age)
/datum/job/proc/config_check()
- return 1
\ No newline at end of file
+ return 1
+
+/datum/job/proc/announce_head(var/datum/mind/o_mind, var/channels) //tells the given channel that the given mind is the new department head. See communications.dm for valid channels.
+ spawn(4) //to allow some initialization
+ if(announcement_systems.len)
+ var/obj/machinery/announcement_system/announcer = pick(announcement_systems)
+ announcer.announce("NEWHEAD", o_mind.name, o_mind.assigned_role, channels)
diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm
index 083aedb3ac2..7485f8442ca 100644
--- a/code/game/jobs/job/medical.dm
+++ b/code/game/jobs/job/medical.dm
@@ -35,6 +35,8 @@ Chief Medical Officer
H.equip_to_slot_or_del(new /obj/item/device/flashlight/pen(H), slot_s_store)
H.equip_to_slot_or_del(new /obj/item/weapon/melee/classic_baton/telescopic(H), slot_in_backpack)
+ announce_head(H.mind, list("Medical")) //tell underlings (medical radio) they have a head
+
/*
Medical Doctor
*/
diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm
index 518813f2990..9f571c85ea2 100644
--- a/code/game/jobs/job/science.dm
+++ b/code/game/jobs/job/science.dm
@@ -37,6 +37,8 @@ Research Director
H.equip_to_slot_or_del(new /obj/item/device/laser_pointer(H), slot_l_store)
H.equip_to_slot_or_del(new /obj/item/weapon/melee/classic_baton/telescopic(H), slot_in_backpack)
+ announce_head(H.mind, list("Science")) //tell underlings (science radio) they have a head
+
/*
Scientist
*/
diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm
index 9adfaef00ca..2d8bdefcbde 100644
--- a/code/game/jobs/job/security.dm
+++ b/code/game/jobs/job/security.dm
@@ -52,6 +52,8 @@ Head of Security
L.implanted = 1
H.sec_hud_set_implants()
+ announce_head(H.mind, list("Security")) //tell underlings (security radio) they have a head
+
/*
Warden
*/
diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
new file mode 100644
index 00000000000..e69df9c883c
--- /dev/null
+++ b/code/game/machinery/announcement_system.dm
@@ -0,0 +1,196 @@
+var/list/announcement_systems = list()
+
+/obj/machinery/announcement_system
+ density = 1
+ anchored = 1
+ name = "\improper Automated Announcement System"
+ desc = "An automated announcement system that handles minor announcements over the radio."
+ icon = 'icons/obj/machines/telecomms.dmi'
+ icon_state = "AAS_On"
+ var/obj/item/device/radio/headset/radio
+
+ verb_say = "coldly states"
+ verb_ask = "queries"
+ verb_exclaim = "alarms"
+
+ var/broken = 0
+
+ idle_power_usage = 20
+ active_power_usage = 50
+
+ var/arrival = "%PERSON has signed up as %RANK"
+ var/arrivalToggle = 1
+ var/newhead = "%PERSON, %RANK, is the department head."
+ var/newheadToggle = 1
+
+ var/greenlight = "Light_Green"
+ var/pinklight = "Light_Pink"
+ var/errorlight = "Error_Red"
+
+/obj/machinery/announcement_system/New()
+ ..()
+ announcement_systems += src
+ radio = new /obj/item/device/radio/headset/ai(src)
+
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/announcement_system(null)
+ component_parts += new /obj/item/stack/cable_coil(null, 2)
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
+ RefreshParts()
+
+ update_icon()
+
+/obj/machinery/announcement_system/update_icon()
+ if(is_operational())
+ icon_state = (panel_open ? "AAS_On_Open" : "AAS_On")
+ else
+ icon_state = (panel_open ? "AAS_Off_Open" : "AAS_Off")
+
+
+ overlays.Cut()
+ if(arrivalToggle)
+ overlays |= greenlight
+ else
+ overlays -= greenlight
+
+ if(newheadToggle)
+ overlays |= pinklight
+ else
+ overlays -= pinklight
+
+ if(broken)
+ overlays |= errorlight
+ else
+ overlays -= errorlight
+
+/obj/machinery/announcement_system/Destroy()
+ announcement_systems -= src //"OH GOD WHY ARE THERE 100,000 LISTED ANNOUNCEMENT SYSTEMS?!!"
+
+/obj/machinery/announcement_system/power_change()
+ ..()
+ update_icon()
+
+/obj/machinery/announcement_system/attackby(obj/item/P, mob/user, params)
+ if(istype(P, /obj/item/weapon/screwdriver))
+ if(!panel_open)
+ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ user << "You open the maintenance hatch of [src]."
+ panel_open = 1
+ else
+ playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
+ user << "You close the maintenance hatch of [src]."
+ panel_open = 0
+ update_icon()
+ return
+
+ if(panel_open)
+ default_deconstruction_crowbar(P)
+ if(istype(P, /obj/item/device/multitool) && broken)
+ user << "You reset [src]'s firmware."
+ broken = 0
+ update_icon()
+
+/obj/machinery/announcement_system/attack_hand(mob/user)
+ if(can_be_used_by(user))
+ Interact(user)
+
+/obj/machinery/announcement_system/proc/CompileText(str, user, rank) //replaces user-given variables with actual thingies.
+ str = replacetext(str, "%PERSON", "[user]")
+ str = replacetext(str, "%RANK", "[rank]")
+ return str
+
+/obj/machinery/announcement_system/proc/announce(message_type, user, rank, list/channels)
+ if(!is_operational())
+ return
+
+ var/message
+
+ if(message_type == "ARRIVAL" && arrivalToggle)
+ message = CompileText(arrival, user, rank)
+
+ else if(message_type == "NEWHEAD" && newheadToggle)
+ message = CompileText(newhead, user, rank)
+
+ if(channels.len == 0)
+ radio.talk_into(src, message, null, list(SPAN_ROBOT))
+ else
+ for(var/channel in channels)
+ radio.talk_into(src, message, channel, list(SPAN_ROBOT))
+
+//config stuff
+
+/obj/machinery/announcement_system/proc/Interact(mob/user)
+ if(!can_be_used_by(user))
+ return
+
+ if(broken)
+ visible_message("[src] buzzes.", "You hear a faint buzz.")
+ playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 1)
+ return
+
+
+ var/contents = "Arrival Announcement: ([(arrivalToggle ? "On" : "Off")])
\n[arrival]
\n"
+ contents += "Departmental Head Announcement: ([(newheadToggle ? "On" : "Off")])
\n[newhead]
\n"
+
+ var/datum/browser/popup = new(user, "announcement_config", "Automated Announcement Configuration", 370, 220)
+ popup.set_content(contents)
+ popup.open()
+
+/obj/machinery/announcement_system/Topic(href, href_list)
+ if(!can_be_used_by(usr) || usr.lying || usr.stat || usr.stunned)
+ return
+ if(broken)
+ visible_message("[src] buzzes.", "You hear a faint buzz.")
+ playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 1)
+ return
+
+ if(href_list["ArrivalTopic"])
+ var/NewMessage = stripped_input(usr, "Enter in the arrivals announcement configuration.", "Arrivals Announcement Config", arrival)
+ if(!in_range(src, usr) && src.loc != usr && !isAI(usr))
+ return
+ if(NewMessage)
+ arrival = NewMessage
+ else if(href_list["NewheadTopic"])
+ var/NewMessage = stripped_input(usr, "Enter in the departmental head announcement configuration.", "Head Departmental Announcement Config", newhead)
+ if(!in_range(src, usr) && src.loc != usr && !isAI(usr))
+ return
+ if(NewMessage)
+ newhead = NewMessage
+
+ else if(href_list["NewheadT-Topic"])
+ newheadToggle = !newheadToggle
+ update_icon()
+ else if(href_list["ArrivalT-Topic"])
+ arrivalToggle = !arrivalToggle
+ update_icon()
+
+ add_fingerprint(usr)
+ Interact(usr)
+ return
+
+/obj/machinery/announcement_system/attack_ai(mob/living/silicon/ai/user)
+ if(!isAI(user))
+ return
+ if(broken)
+ user << "[src]'s firmware appears to be malfunctioning!"
+ return
+ Interact(user)
+
+/obj/machinery/announcement_system/proc/act_up() //does funny breakage stuff
+ broken = 1
+ update_icon()
+
+ arrival = pick("#!@%ERR-34%2 CANNOT LOCAT@# JO# F*LE!", "CRITICAL ERROR 99.", "ERR)#: DA#AB@#E NOT F(*ND!")
+ newhead = pick("OV#RL()D: \[UNKNOWN??\] DET*#CT)D!", "ER)#R - B*@ TEXT F*O(ND!", "AAS.exe is not responding. NanoOS is searching for a solution to the problem.")
+
+/obj/machinery/announcement_system/emp_act(severity)
+ if(stat & (NOPOWER|BROKEN))
+ ..(severity)
+ return
+ act_up()
+ ..(severity)
+
+/obj/machinery/announcement_system/emag_act()
+ ..()
+ emagged = 1
+ act_up()
\ No newline at end of file
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index d351c5f7ec4..4288620d25e 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -254,6 +254,15 @@ to destroy them and players will be able to make replacements.
user << "You set the board to [names_paths[build_path]]."
req_components = list(text2path("/obj/item/weapon/vending_refill/[copytext("[build_path]", 24)]") = 3) //Never before has i used a method as horrible as this one, im so sorry
+/obj/item/weapon/circuitboard/announcement_system
+ name = "circuit board (Announcement System)"
+ build_path = /obj/machinery/announcement_system
+ board_type = "machine"
+ origin_tech = "programming=3;bluespace=2"
+ req_components = list(
+ /obj/item/stack/cable_coil = 2,
+ /obj/item/weapon/stock_parts/console_screen = 1)
+
/obj/item/weapon/circuitboard/smes
name = "circuit board (SMES)"
build_path = /obj/machinery/power/smes
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 0b5158f6085..2b4776c12b7 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -300,14 +300,11 @@
/mob/new_player/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank)
if (ticker.current_state == GAME_STATE_PLAYING)
- var/ailist[] = list()
- for (var/mob/living/silicon/ai/A in living_mob_list)
- ailist += A
- if (ailist.len)
- var/mob/living/silicon/ai/announcer = pick(ailist)
+ if(announcement_systems.len)
if(character.mind)
if((character.mind.assigned_role != "Cyborg") && (character.mind.assigned_role != character.mind.special_role))
- announcer.say("[announcer.radiomod] [character.real_name] has signed up as [rank].")
+ var/obj/machinery/announcement_system/announcer = pick(announcement_systems)
+ announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common
/mob/new_player/proc/LateChoices()
var/mills = world.time // 1/10 of a second, not real milliseconds but whatever
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index 8c0cfe30ea1..75202c87387 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -12,6 +12,16 @@
build_path = /obj/item/weapon/circuitboard/smes
category = list ("Engineering Machinery")
+/datum/design/announcement_system
+ name = "Machine Design (Automated Announcement System Board)"
+ desc = "The circuit board for an automated announcement system."
+ id = "automated_announcement"
+ req_tech = list("programming" = 3, "bluespace" = 2)
+ build_type = IMPRINTER
+ materials = list(MAT_GLASS = 1000, "sacid" = 20)
+ build_path = /obj/item/weapon/circuitboard/announcement_system
+ category = list("Subspace Telecomms")
+
/datum/design/turbine_computer
name = "Computer Design (Power Turbine Console Board)"
desc = "The circuit board for a power turbine console."
diff --git a/html/changelogs/Chiefwaffles-Obsolete_AI_2016.yml b/html/changelogs/Chiefwaffles-Obsolete_AI_2016.yml
new file mode 100644
index 00000000000..3a38249e8bb
--- /dev/null
+++ b/html/changelogs/Chiefwaffles-Obsolete_AI_2016.yml
@@ -0,0 +1,8 @@
+author: Chiefwaffles
+
+delete-after: True
+
+changes:
+ - rscadd: "After realizing how often AIs tend to 'disappear', Central Command has authorized the shipment of the Automated Announcement System to take over the AI's responsibility of announcing new arrivals."
+ - rscadd: "In addition, users are able to configure the messages to their liking. Early testing has shown that this feature may be vulnerable to malicious external elements!"
+ - rscadd: "The station's R&D software has been updated so it can now succesfully create its own prototype AAS board once the prerequisite levels have been met."
diff --git a/icons/obj/machines/telecomms.dmi b/icons/obj/machines/telecomms.dmi
index f47fcc63f87..7f5f05e3aff 100644
Binary files a/icons/obj/machines/telecomms.dmi and b/icons/obj/machines/telecomms.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 56abaadb801..3aeb99ee87f 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -381,6 +381,7 @@
#include "code\game\machinery\ai_slipper.dm"
#include "code\game\machinery\airlock_control.dm"
#include "code\game\machinery\alarm.dm"
+#include "code\game\machinery\announcement_system.dm"
#include "code\game\machinery\atmo_control.dm"
#include "code\game\machinery\autolathe.dm"
#include "code\game\machinery\Beacon.dm"