diff --git a/baystation12.dme b/baystation12.dme
index a52e66f6609..d0d51121ffd 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -882,6 +882,8 @@
#include "code\modules\awaymissions\pamphlet.dm"
#include "code\modules\awaymissions\trigger.dm"
#include "code\modules\awaymissions\zlevel.dm"
+#include "code\modules\cciaa\cciaa.dm"
+#include "code\modules\cciaa\cciaa_items.dm"
#include "code\modules\client\client defines.dm"
#include "code\modules\client\client procs.dm"
#include "code\modules\client\preferences.dm"
diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm
index 36fa534fa95..fcf2f569011 100644
--- a/code/__HELPERS/type2type.dm
+++ b/code/__HELPERS/type2type.dm
@@ -289,6 +289,7 @@ proc/tg_list2text(list/list, glue=",")
if (rights & R_SPAWN) . += "[seperator]+SPAWN"
if (rights & R_MOD) . += "[seperator]+MODERATOR"
if (rights & R_MENTOR) . += "[seperator]+MENTOR"
+ if (rights & R_CCIAA) . += "[seperator]+CCIAA"
return .
/proc/ui_style2icon(ui_style)
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index ceb65c71ab2..6c21fed7523 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -65,6 +65,7 @@ var/list/gamemode_cache = list()
var/kick_inactive = 0 //force disconnect for inactive players after this many minutes, if non-0
var/show_mods = 0
var/show_mentors = 0
+ var/show_auxiliary_roles = 0 //toggles all secondary staff members. Such as CCIAA, Devs, etcetera.
var/mods_can_tempban = 0
var/mods_can_job_tempban = 0
var/mod_tempban_max = 1440
@@ -678,6 +679,9 @@ var/list/gamemode_cache = list()
if("sql_whitelists")
config.sql_whitelists = 1
+ if("show_auxiliary_roles")
+ config.show_auxiliary_roles = 1
+
else
log_misc("Unknown setting in configuration: '[name]'")
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index c1badaaca85..bda2c114cdf 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -36,6 +36,8 @@
var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds.
var/active = 0
+ var/mob/living/admin_mob_placeholder = null
+
var/memory
var/assigned_role
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index f12d7741378..d19fc48ddf3 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -63,9 +63,11 @@
var/msg = ""
var/modmsg = ""
var/mentmsg = ""
+ var/cciaamsg = ""
var/num_mods_online = 0
var/num_admins_online = 0
var/num_mentors_online = 0
+ var/num_cciaa_online = 0
if(holder)
for(var/client/C in admins)
if(R_ADMIN & C.holder.rights || (!R_MOD & C.holder.rights && !R_MENTOR & C.holder.rights)) //Used to determine who shows up in admin rows
@@ -119,6 +121,20 @@
mentmsg += "\n"
num_mentors_online++
+ else if (R_CCIAA & C.holder.rights)
+ cciaamsg += "\t[C]"
+ if (isobserver(C.mob))
+ cciaamsg += " - Observing"
+ else if (istype(C.mob, /mob/new_player))
+ cciaamsg += " - Lobby"
+ else
+ cciaamsg += " - Playing"
+
+ if (C.is_afk())
+ cciaamsg += " (AFK)"
+ cciaamsg += "\n"
+ num_cciaa_online++
+
else
for(var/client/C in admins)
if(R_ADMIN & C.holder.rights || (!R_MOD & C.holder.rights && !R_MENTOR & C.holder.rights))
@@ -131,6 +147,9 @@
else if (R_MENTOR & C.holder.rights)
mentmsg += "\t[C] is a [C.holder.rank]\n"
num_mentors_online++
+ else if (R_CCIAA & C.holder.rights)
+ cciaamsg += "\t[C] is a [C.holder.rank]\n"
+ num_cciaa_online++
if(config.admin_irc)
src << "Adminhelps are also sent to IRC. If no admins are available in game try anyway and an admin on IRC may see it and respond."
@@ -142,4 +161,8 @@
if(config.show_mentors)
msg += "\n Current Mentors ([num_mentors_online]):\n" + mentmsg
+ if (config.show_auxiliary_roles)
+ if (num_cciaa_online)
+ msg += "\n Current CCIA Agents ([num_cciaa_online]):\n" + cciaamsg
+
src << msg
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index 877946479b3..17051d11099 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -41,7 +41,8 @@ var/list/admin_ranks = list() //list of all ranks with associated rights
if("sound","sounds") rights |= R_SOUNDS
if("spawn","create") rights |= R_SPAWN
if("mod") rights |= R_MOD
- if("mentor") rights |= R_MENTOR
+ if("mentor") rights |= R_MENTOR
+ if("cciaa") rights |= R_CCIAA
admin_ranks[rank] = rights
previous_rights = rights
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 05be2cd7a05..bd4ce2fe742 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -93,6 +93,7 @@ var/list/admin_verbs_admin = list(
/client/proc/view_chemical_reaction_logs,
/client/proc/makePAI,
/datum/admins/proc/paralyze_mob
+// /datum/admins/proc/send_admin_fax
)
var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
@@ -119,6 +120,7 @@ var/list/admin_verbs_fun = list(
/client/proc/toggle_random_events,
/client/proc/editappear,
/client/proc/roll_dices
+// /datum/admins/proc/send_admin_fax
)
var/list/admin_verbs_spawn = list(
/datum/admins/proc/spawn_fruit,
@@ -301,6 +303,17 @@ var/list/admin_verbs_mentor = list(
/client/proc/cmd_admin_subtle_message
)
+var/list/admin_verbs_cciaa = list(
+ /client/proc/cmd_admin_pm_panel, /*admin-pm list*/
+ /client/proc/spawn_duty_officer,
+ /client/proc/cmd_admin_create_centcom_report,
+ /client/proc/cmd_cciaa_say,
+ /client/proc/returntobody,
+// /client/proc/view_duty_log,
+// /datum/admins/proc/send_admin_fax,
+ /client/proc/check_fax_history
+)
+
/client/proc/add_admin_verbs()
if(holder)
verbs += admin_verbs_default
@@ -321,6 +334,7 @@ var/list/admin_verbs_mentor = list(
if(holder.rights & R_SPAWN) verbs += admin_verbs_spawn
if(holder.rights & R_MOD) verbs += admin_verbs_mod
if(holder.rights & R_MENTOR) verbs += admin_verbs_mentor
+ if(holder.rights & R_CCIAA) verbs += admin_verbs_cciaa
/client/proc/remove_admin_verbs()
verbs.Remove(
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index dbd4de64446..ef3b35b45c3 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -8,6 +8,8 @@ var/list/admin_datums = list()
var/datum/marked_datum
+ var/mob/living/original_mob = null
+
var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description
var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders.
var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel
@@ -43,7 +45,7 @@ var/list/admin_datums = list()
owner.holder = src
owner.deadmin_holder = null
owner.add_admin_verbs()
-
+
/*
checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags)
diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm
index faa4d7def5a..60fd1f37971 100644
--- a/code/modules/admin/verbs/adminsay.dm
+++ b/code/modules/admin/verbs/adminsay.dm
@@ -36,3 +36,21 @@
C << "" + create_text_tag("mod", "MOD:", C) + " [sender_name](JMP): "
feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/proc/cmd_cciaa_say(msg as text)
+ set category = "Special Verbs"
+ set name = "CCIAsay"
+ set hidden = 1
+
+ if(!check_rights(R_ADMIN|R_CCIAA)) return
+
+ msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ if(!msg) return
+
+ log_admin("CCIASAY: [key_name(src)] : [msg]")
+
+ if(check_rights((R_CCIAA|R_ADMIN),0))
+ msg = "CCIAAgent: [key_name(usr, 0, 1, 0)]: "
+ for(var/client/C in admins)
+ if(C.holder.rights & (R_ADMIN|R_CCIAA))
+ C << msg
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index d987c52260b..313de0150d9 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -29,10 +29,14 @@
//log_admin("HELP: [key_name(src)]: [msg]")
/proc/Centcomm_announce(var/msg, var/mob/Sender, var/iamessage)
+ var/msg_cciaa = "\blue CENTCOMM[iamessage ? " IA" : ""]:[key_name(Sender, 1)] (RPLY): [msg]"
msg = "\blue CENTCOMM[iamessage ? " IA" : ""]:[key_name(Sender, 1)] (PP) (VV) (SM) (JMP) (CA) (BSA) (RPLY): [msg]"
+
for(var/client/C in admins)
if(R_ADMIN & C.holder.rights)
C << msg
+ else if (R_CCIAA & C.holder.rights)
+ C << msg_cciaa
/proc/Syndicate_announce(var/msg, var/mob/Sender)
msg = "\blue ILLEGAL:[key_name(Sender, 1)] (PP) (VV) (SM) (JMP) (CA) (BSA) (RPLY): [msg]"
diff --git a/code/modules/cciaa/cciaa.dm b/code/modules/cciaa/cciaa.dm
new file mode 100644
index 00000000000..cee2524f0d4
--- /dev/null
+++ b/code/modules/cciaa/cciaa.dm
@@ -0,0 +1,323 @@
+/client/proc/spawn_duty_officer()
+ set category = "Special Verbs"
+ set name = "Spawn CCIA Agent"
+ set desc = "Spawns a CCIA Agent to... IA Agent?"
+
+ if(!check_rights(R_CCIAA)) return
+
+ if(!holder)
+ return //how did they get here?
+
+ if(!ticker)
+ alert("Wait until the game starts")
+ return
+
+ if(ticker.current_state < GAME_STATE_PLAYING)
+ src << "\red The game hasn't started yet!"
+ return
+
+ if(istype(mob, /mob/new_player))
+ src << "\red You can't be in the lobby to join as a duty officer"
+ return
+
+ if(mob.mind && mob.mind.special_role == "CCIA Agent")
+ src << "\red You are already a CCIA Agent"
+ verbs += /client/proc/returntobody
+ return
+
+ var/wasLiving = 0
+ if(istype(mob, /mob/living))
+ holder.original_mob = mob
+ wasLiving = 1
+
+ for (var/obj/effect/landmark/L in landmarks_list)
+ if(L.name == "CCIAAgent")
+ var/new_name = input(usr, "Pick a name","Name") as null|text
+ var/mob/living/carbon/human/M = new(null)
+
+ var/new_facial = input("Please select facial hair color.", "Character Generation") as color
+ if(new_facial)
+ M.r_facial = hex2num(copytext(new_facial, 2, 4))
+ M.g_facial = hex2num(copytext(new_facial, 4, 6))
+ M.b_facial = hex2num(copytext(new_facial, 6, 8))
+
+ var/new_hair = input("Please select hair color.", "Character Generation") as color
+ if(new_facial)
+ M.r_hair = hex2num(copytext(new_hair, 2, 4))
+ M.g_hair = hex2num(copytext(new_hair, 4, 6))
+ M.b_hair = hex2num(copytext(new_hair, 6, 8))
+
+ var/new_eyes = input("Please select eye color.", "Character Generation") as color
+ if(new_eyes)
+ M.r_eyes = hex2num(copytext(new_eyes, 2, 4))
+ M.g_eyes = hex2num(copytext(new_eyes, 4, 6))
+ M.b_eyes = hex2num(copytext(new_eyes, 6, 8))
+
+ var/new_tone = input("Please select skin tone level: 1-220 (1=albino, 35=caucasian, 150=black, 220='very' black)", "Character Generation") as text
+
+ if (!new_tone)
+ new_tone = 35
+ M.s_tone = max(min(round(text2num(new_tone)), 220), 1)
+ M.s_tone = -M.s_tone + 35
+
+ // hair
+ var/list/all_hairs = typesof(/datum/sprite_accessory/hair) - /datum/sprite_accessory/hair
+ var/list/hairs = list()
+
+ // loop through potential hairs
+ for(var/x in all_hairs)
+ var/datum/sprite_accessory/hair/H = new x
+ hairs.Add(H.name)
+ del(H)
+ //hair
+ var/new_hstyle = input(usr, "Select a hair style", "Grooming") as null|anything in hair_styles_list
+ if(new_hstyle)
+ M.h_style = new_hstyle
+
+ // facial hair
+ var/new_fstyle = input(usr, "Select a facial hair style", "Grooming") as null|anything in facial_hair_styles_list
+ if(new_fstyle)
+ M.f_style = new_fstyle
+
+ var/new_gender = alert(usr, "Please select gender.", "Character Generation", "Male", "Female")
+ if (new_gender)
+ if(new_gender == "Male")
+ M.gender = MALE
+ else
+ M.gender = FEMALE
+ //M.rebuild_appearance()
+ M.update_hair()
+ M.update_body()
+ M.check_dna(M)
+
+ M.real_name = new_name
+ M.name = new_name
+ M.age = input("Enter your characters age:","Num") as null|num
+ if(!M.age)
+ M.age = rand(35,50)
+ if(M.age < 33 || M.age > 60)
+ src << "\red The age you selected was not in a valid range for a Duty Officer"
+ if(M.age < 33)
+ M.age = 33
+ else
+ M.age = 60
+ src << "\red Your age has been set to [M.age]"
+
+ M.dna.ready_dna(M)
+
+ //Creates mind stuff.
+ M.mind = new
+ M.mind.current = M
+ M.mind.original = M
+ if(wasLiving)
+ M.mind.admin_mob_placeholder = mob
+ M.mind.assigned_role = "Central Command Internal Affairs Agent"
+ M.mind.special_role = "CCIA Agent"
+ M.loc = L.loc
+ M.key = key
+
+ if(wasLiving)
+ clear_cciaa_job(holder.original_mob)
+ spawn(1)
+ holder.original_mob.key = "@[key]"
+
+ M.equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(M), slot_w_uniform)
+ M.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(M), slot_shoes)
+ M.equip_to_slot_or_del(new /obj/item/clothing/gloves/white(M), slot_gloves)
+ M.equip_to_slot_or_del(new /obj/item/device/radio/headset/ert(M), slot_l_ear)
+ M.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/sechud(M), slot_glasses)
+ M.equip_to_slot_or_del(new /obj/item/clothing/head/beret/centcom/officer(M), slot_head)
+ M.equip_to_slot_or_del(new /obj/item/weapon/reagent_containers/spray/pepper(M), slot_l_store)
+ M.equip_to_slot_or_del(new /obj/item/device/taperecorder/cciaa(M), slot_r_store)
+
+ var/obj/item/weapon/storage/backpack/satchel/bag = new(M)
+ bag.name = "officer's leather satchel"
+ bag.desc = "A well cared for leather satchel for Nanotrasen officers."
+ M.equip_to_slot_or_del(bag, slot_back)
+ if(M.backbag == 1)
+ M.equip_to_slot_or_del(new /obj/item/weapon/stamp/centcomm(M.back), slot_in_backpack)
+
+ var /obj/item/weapon/storage/lockbox/lockbox = new(M)
+ lockbox.req_access = list(access_cent_captain)
+ lockbox.name = "A CCIA Agent briefcase"
+ lockbox.desc = "A smart looking briefcase with a NT logo on the side"
+ lockbox.storage_slots = 8
+ lockbox.max_storage_space = 16
+ M.equip_to_slot_or_del(lockbox, slot_l_hand)
+
+ var/obj/item/device/pda/central/pda = new(M)
+ pda.owner = M.real_name
+ pda.ownjob = "Central Command Internal Affairs Agent"
+ pda.name = "PDA-[M.real_name] ([pda.ownjob])"
+
+ M.equip_to_slot_or_del(pda, slot_belt)
+
+ M.implant_loyalty(M, 1)
+
+ var/obj/item/weapon/card/id/W = new(M)
+ W.name = "[M.real_name]'s ID Card"
+ W.icon_state = "centcom"
+ W.item_state = "id_inv"
+ W.access = get_all_accesses() + get_centcom_access("CCIAA")
+ W.assignment = "Central Command Internal Affairs Agent"
+ W.registered_name = M.real_name
+ M.equip_to_slot_or_del(W, slot_wear_id)
+
+ verbs += /client/proc/returntobody
+ break
+
+/client/proc/returntobody()
+ set name = "Return to mob"
+ set desc = "The Agent's work is done, return to your original mob"
+ set category = "Special Verbs"
+
+ if(!check_rights(0)) return
+
+ if(!mob.mind || mob.mind.special_role != "CCIA Agent")
+ verbs -= /client/proc/returntobody
+ return
+
+ if(!holder) return
+
+ var/mob/M = mob
+ var/area/A = get_area(M)
+
+ if(M.stat == DEAD)
+ if(holder.original_mob)
+ if(holder.original_mob.client)
+ if(alert(src, "There is someone else in your old body.\nWould you like to ghost instead?", "There is someone else in your old body, you will be ghosted", "Yes", "No") == "Yes")
+ M.mind.special_role = null
+ mob.ghostize(1)
+ return
+ else
+ return
+ holder.original_mob.key = key
+ holder.original_mob = null
+ return
+ M.mind.special_role = null
+ mob.ghostize(1)
+ return
+
+ if(!is_type_in_list(A,centcom_areas))
+ src << "\red You need to be back at central to do this"
+ return
+
+ if(holder.original_mob)
+ if(holder.original_mob == M)
+ verbs -= /client/proc/returntobody
+ return
+
+ if(holder.original_mob.client)
+ if(alert(src, "There is someone else in your old body.\nWould you like to ghost instead?", "There is someone else in your old body, you will be ghosted", "Yes", "No") == "Yes")
+ M.mind.special_role = null
+ mob.ghostize(0)
+ else
+ return
+ else
+ holder.original_mob.key = key
+
+ holder.original_mob = null
+ else
+ if(mob.mind.admin_mob_placeholder)
+ if(mob.mind.admin_mob_placeholder.client)
+ if(alert(src, "There is someone else in your old body.\nWould you like to ghost instead?", "There is someone else in your old body, you will be ghosted", "Yes", "No") == "Yes")
+ M.mind.special_role = null
+ mob.ghostize(0)
+ else
+ return
+ else
+ mob.mind.admin_mob_placeholder.key = key
+ M.mind.admin_mob_placeholder = null
+ else
+ M.mind.special_role = null
+ mob.ghostize(0)
+ verbs -= /client/proc/returntobody
+ del(M)
+
+/proc/clear_cciaa_job(var/mob/living/carbon/human/M)
+ spawn(9000)
+ if(!M.client)
+ var/oldjob = M.mind.assigned_role
+ job_master.FreeRole(oldjob)
+ return
+
+/datum/admins/proc/send_admin_fax(var/mob/sender = null, var/obj/machinery/photocopier/faxmachine/fax = null)
+ set name = "Send admin fax"
+ set desc = "Send a fax from Central Command"
+ set category = "Special Verbs"
+
+ if (!check_rights(R_ADMIN|R_CCIAA|R_FUN))
+ src.owner << "\red You do not have enough powers to do this."
+ return
+
+ var/target_department = null
+ if (!fax)
+ var/list/all_fax_machines = list()
+ for (var/obj/machinery/photocopier/faxmachine/F in world)
+ all_fax_machines += F
+ target_department = input(src.owner, "Please select a department to send this fax to.", "Target Fax Department", null) as null|anything in all_fax_machines
+
+ if (!fax)
+ src.owner << "\red Couldn't find the desired fax machine! Sending cancelled!"
+
+ //todo: sanitize
+ var/input = input(src.owner, "Please enter a message to reply to [sender ? key_name(sender) : "a fax"] via secure connection. NOTE: BBCode does not work, but HTML tags do! Use
for line breaks.", "Outgoing message from Centcomm", "") as message|null
+ if (!input)
+ src.owner << "\red Cancelled."
+ return
+
+ var/customname = input(src.owner, "Pick a title for the report", "Title") as text|null
+
+ // Create the reply message
+ var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( null ) //hopefully the null loc won't cause trouble for us
+ P.name = "[command_name()]- [customname]"
+ P.info = input
+ P.update_icon()
+
+ // Stamps
+ var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
+ stampoverlay.icon_state = "paper_stamp-cent"
+ if(!P.stamped)
+ P.stamped = new
+ P.stamped += /obj/item/weapon/stamp
+ P.overlays += stampoverlay
+ P.stamps += "