diff --git a/baystation12.dme b/baystation12.dme
index 1cbd9ee410c..82badb93616 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -978,8 +978,8 @@
#include "code\modules\client\preferences_toggles.dm"
#include "code\modules\client\ui_style.dm"
#include "code\modules\client\preference_setup\preference_setup.dm"
-#include "code\modules\client\preference_setup\antagonism\01_basic.dm"
-#include "code\modules\client\preference_setup\antagonism\02_candidacy.dm"
+#include "code\modules\client\preference_setup\antagonism\01_candidacy.dm"
+#include "code\modules\client\preference_setup\antagonism\02_setup.dm"
#include "code\modules\client\preference_setup\general\01_basic.dm"
#include "code\modules\client\preference_setup\general\02_language.dm"
#include "code\modules\client\preference_setup\general\03_body.dm"
diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm
index d117d0df5c7..94288982684 100644
--- a/code/__defines/gamemode.dm
+++ b/code/__defines/gamemode.dm
@@ -9,44 +9,9 @@
#define SEC_LEVEL_RED 2
#define SEC_LEVEL_DELTA 3
-#define BE_TRAITOR 0x1
-#define BE_OPERATIVE 0x2
-#define BE_CHANGELING 0x4
-#define BE_WIZARD 0x8
-#define BE_MALF 0x10
-#define BE_REV 0x20
-#define BE_ALIEN 0x40
-#define BE_AI 0x80
-#define BE_CULTIST 0x100
-#define BE_MONKEY 0x200
-#define BE_NINJA 0x400
-#define BE_RAIDER 0x800
-#define BE_PLANT 0x1000
-#define BE_MUTINEER 0x2000
-#define BE_PAI 0x4000
-#define BE_LOYALIST 0x8000
-
-var/list/be_special_flags = list(
- "Traitor" = BE_TRAITOR,
- "Operative" = BE_OPERATIVE,
- "Changeling" = BE_CHANGELING,
- "Wizard" = BE_WIZARD,
- "Malf AI" = BE_MALF,
- "Revolutionary" = BE_REV,
- "Loyalist" = BE_LOYALIST,
- "Xenomorph" = BE_ALIEN,
- "Positronic Brain" = BE_AI,
- "Cultist" = BE_CULTIST,
- "Monkey" = BE_MONKEY,
- "Ninja" = BE_NINJA,
- "Raider" = BE_RAIDER,
- "Diona" = BE_PLANT,
- "Mutineer" = BE_MUTINEER,
- "pAI" = BE_PAI
-)
-
-#define IS_MODE_COMPILED(MODE) (ispath(text2path("/datum/game_mode/"+(MODE))))
-
+#define BE_PLANT "BE_PLANT"
+#define BE_SYNTH "BE_SYNTH"
+#define BE_PAI "BE_PAI"
// Antagonist datum flags.
#define ANTAG_OVERRIDE_JOB 0x1 // Assigned job is set to MODE when spawning.
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index cf955a022c7..2a4dc372d92 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -330,12 +330,11 @@ proc/isInSight(var/atom/A, var/atom/B)
// Same as above but for alien candidates.
/proc/get_alien_candidates()
-
var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn
var/i = 0
while(candidates.len <= 0 && i < 5)
for(var/mob/dead/observer/G in player_list)
- if(G.client.prefs.be_special & BE_ALIEN)
+ if(MODE_XENOMORPH in G.client.prefs.be_special_role)
if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i) // the most active players are more likely to become an alien
if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
candidates += G.key
diff --git a/code/game/antagonist/alien/xenomorph.dm b/code/game/antagonist/alien/xenomorph.dm
index 36f5216045a..2eb8d2d23fc 100644
--- a/code/game/antagonist/alien/xenomorph.dm
+++ b/code/game/antagonist/alien/xenomorph.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/xenos/xenomorphs
/datum/antagonist/xenos
id = MODE_XENOMORPH
- role_type = BE_ALIEN
role_text = "Xenomorph"
role_text_plural = "Xenomorphs"
mob_path = /mob/living/carbon/alien/larva
diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm
index 32f40ad985d..2dd934d1f04 100644
--- a/code/game/antagonist/antagonist.dm
+++ b/code/game/antagonist/antagonist.dm
@@ -14,7 +14,7 @@
// Role data.
var/id = "traitor" // Unique datum identifier.
- var/role_type = BE_TRAITOR // Preferences option for this role.
+ var/role_type // Preferences option for this role. Defaults to the id if unset
var/role_text = "Traitor" // special_role text.
var/role_text_plural = "Traitors" // As above but plural.
@@ -72,6 +72,9 @@
/datum/antagonist/New()
..()
+ if(!role_type)
+ role_type = id
+
cur_max = hard_cap
get_starting_locations()
if(!role_text_plural)
diff --git a/code/game/antagonist/outsider/actors.dm b/code/game/antagonist/outsider/actors.dm
index 7d5deb39e76..d9e8d422700 100644
--- a/code/game/antagonist/outsider/actors.dm
+++ b/code/game/antagonist/outsider/actors.dm
@@ -2,8 +2,7 @@ var/datum/antagonist/actor/actor
/datum/antagonist/actor
id = MODE_ACTOR
- bantype = "operative"
- role_type = BE_OPERATIVE
+ bantype = "actor"
role_text = "NanoTrasen Actor"
role_text_plural = "NanoTrasen Actors"
welcome_text = "You've been hired to entertain people through the power of television!"
diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm
index ca0b39aaefe..2abdb5db3d4 100644
--- a/code/game/antagonist/outsider/deathsquad.dm
+++ b/code/game/antagonist/outsider/deathsquad.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/deathsquad/deathsquad
/datum/antagonist/deathsquad
id = MODE_DEATHSQUAD
- role_type = BE_OPERATIVE
role_text = "Death Commando"
role_text_plural = "Death Commandos"
welcome_text = "You work in the service of corporate Asset Protection, answering directly to the Board of Directors."
diff --git a/code/game/antagonist/outsider/ert.dm b/code/game/antagonist/outsider/ert.dm
index e8517dbf4e8..5408a8a889d 100644
--- a/code/game/antagonist/outsider/ert.dm
+++ b/code/game/antagonist/outsider/ert.dm
@@ -3,7 +3,6 @@ var/datum/antagonist/ert/ert
/datum/antagonist/ert
id = MODE_ERT
bantype = "Emergency Response Team"
- role_type = BE_OPERATIVE
role_text = "Emergency Responder"
role_text_plural = "Emergency Responders"
welcome_text = "As member of the Emergency Response Team, you answer only to your leader and company officials."
@@ -41,6 +40,6 @@ var/datum/antagonist/ert/ert
player.equip_to_slot_or_del(new /obj/item/clothing/shoes/swat(src), slot_shoes)
player.equip_to_slot_or_del(new /obj/item/clothing/gloves/swat(src), slot_gloves)
player.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses(src), slot_glasses)
-
+
create_id(role_text, player)
return 1
diff --git a/code/game/antagonist/outsider/mercenary.dm b/code/game/antagonist/outsider/mercenary.dm
index e2c44aef70f..82415de808a 100644
--- a/code/game/antagonist/outsider/mercenary.dm
+++ b/code/game/antagonist/outsider/mercenary.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/mercenary/mercs
/datum/antagonist/mercenary
id = MODE_MERCENARY
- role_type = BE_OPERATIVE
role_text = "Mercenary"
bantype = "operative"
antag_indicator = "synd"
diff --git a/code/game/antagonist/outsider/ninja.dm b/code/game/antagonist/outsider/ninja.dm
index 09dc15b5949..eeead17be7f 100644
--- a/code/game/antagonist/outsider/ninja.dm
+++ b/code/game/antagonist/outsider/ninja.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/ninja/ninjas
/datum/antagonist/ninja
id = MODE_NINJA
- role_type = BE_NINJA
role_text = "Ninja"
role_text_plural = "Ninja"
bantype = "ninja"
diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm
index 8ab342bbf24..26648a34f04 100644
--- a/code/game/antagonist/outsider/raider.dm
+++ b/code/game/antagonist/outsider/raider.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/raider/raiders
/datum/antagonist/raider
id = MODE_RAIDER
- role_type = BE_RAIDER
role_text = "Raider"
role_text_plural = "Raiders"
bantype = "raider"
diff --git a/code/game/antagonist/outsider/wizard.dm b/code/game/antagonist/outsider/wizard.dm
index f6741cafa07..292277b1a27 100644
--- a/code/game/antagonist/outsider/wizard.dm
+++ b/code/game/antagonist/outsider/wizard.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/wizard/wizards
/datum/antagonist/wizard
id = MODE_WIZARD
- role_type = BE_WIZARD
role_text = "Space Wizard"
role_text_plural = "Space Wizards"
bantype = "wizard"
diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm
index dad7ed81a26..2fe9581cbda 100644
--- a/code/game/antagonist/station/changeling.dm
+++ b/code/game/antagonist/station/changeling.dm
@@ -1,6 +1,5 @@
/datum/antagonist/changeling
id = MODE_CHANGELING
- role_type = BE_CHANGELING
role_text = "Changeling"
role_text_plural = "Changelings"
bantype = "changeling"
diff --git a/code/game/antagonist/station/cultist.dm b/code/game/antagonist/station/cultist.dm
index aba2115d152..cc6314c7519 100644
--- a/code/game/antagonist/station/cultist.dm
+++ b/code/game/antagonist/station/cultist.dm
@@ -13,7 +13,6 @@ var/datum/antagonist/cultist/cult
bantype = "cultist"
restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Head of Security", "Captain")
protected_jobs = list("Security Officer", "Warden", "Detective")
- role_type = BE_CULTIST
feedback_tag = "cult_objective"
antag_indicator = "cult"
welcome_text = "You have a talisman in your possession; one that will help you start the cult on this station. Use it well and remember - there are others."
diff --git a/code/game/antagonist/station/loyalist.dm b/code/game/antagonist/station/loyalist.dm
index fddf0c37960..f8b5ec49946 100644
--- a/code/game/antagonist/station/loyalist.dm
+++ b/code/game/antagonist/station/loyalist.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/loyalists/loyalists
/datum/antagonist/loyalists
id = MODE_LOYALIST
- role_type = BE_LOYALIST
role_text = "Head Loyalist"
role_text_plural = "Loyalists"
bantype = "loyalist"
diff --git a/code/game/antagonist/station/revolutionary.dm b/code/game/antagonist/station/revolutionary.dm
index 96e37529915..ca0011f8d72 100644
--- a/code/game/antagonist/station/revolutionary.dm
+++ b/code/game/antagonist/station/revolutionary.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/revolutionary/revs
/datum/antagonist/revolutionary
id = MODE_REVOLUTIONARY
- role_type = BE_REV
role_text = "Head Revolutionary"
role_text_plural = "Revolutionaries"
bantype = "revolutionary"
diff --git a/code/game/antagonist/station/rogue_ai.dm b/code/game/antagonist/station/rogue_ai.dm
index 4240708cc22..ea99bf14afb 100644
--- a/code/game/antagonist/station/rogue_ai.dm
+++ b/code/game/antagonist/station/rogue_ai.dm
@@ -2,7 +2,6 @@ var/datum/antagonist/rogue_ai/malf
/datum/antagonist/rogue_ai
id = MODE_MALFUNCTION
- role_type = BE_MALF
role_text = "Rampant AI"
role_text_plural = "Rampant AIs"
mob_path = /mob/living/silicon/ai
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 83085fdda99..2a3c361bc99 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -435,7 +435,7 @@ var/global/list/additional_antag_types = list()
continue
if(istype(player, /mob/new_player))
continue
- if(!role || (player.client.prefs.be_special & role))
+ if(!role || (role in player.client.prefs.be_special_role))
log_debug("[player.key] had [antag_id] enabled, so we are drafting them.")
candidates |= player.mind
else
@@ -446,7 +446,7 @@ var/global/list/additional_antag_types = list()
// Get a list of all the people who want to be the antagonist for this round
for(var/mob/new_player/player in players)
- if(!role || (player.client.prefs.be_special & role))
+ if(!role || (role in player.client.prefs.be_special_role))
log_debug("[player.key] had [antag_id] enabled, so we are drafting them.")
candidates += player.mind
players -= player
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 7bf949e217f..53b6627f076 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -91,7 +91,7 @@ var/global/datum/controller/occupations/job_master
if(job.minimum_character_age && (player.client.prefs.age < job.minimum_character_age))
Debug("FOC character not old enough, Player: [player]")
continue
- if(flag && (!player.client.prefs.be_special & flag))
+ if(flag && !(flag in player.client.prefs.be_special_role))
Debug("FOC flag failed, Player: [player], Flag: [flag], ")
continue
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm
index 3cbacf9e501..613e9c7241a 100644
--- a/code/game/jobs/whitelist.dm
+++ b/code/game/jobs/whitelist.dm
@@ -52,6 +52,8 @@ var/list/whitelist = list()
//todo: admin aliens
/proc/is_alien_whitelisted(mob/M, var/species)
+ if(!M || !species)
+ return 0
if(!config.usealienwhitelist)
return 1
if(istype(species,/datum/species) || istype(species,/datum/language))
diff --git a/code/game/objects/structures/alien/egg.dm b/code/game/objects/structures/alien/egg.dm
index c16c1a182e8..5d32f9685f6 100644
--- a/code/game/objects/structures/alien/egg.dm
+++ b/code/game/objects/structures/alien/egg.dm
@@ -30,7 +30,7 @@
progress++
if(progress >= MAX_PROGRESS)
for(var/mob/M in dead_mob_list)
- if(istype(M,/mob/dead) && M.client && M.client.prefs && (M.client.prefs.be_special & BE_ALIEN))
+ if(istype(M,/mob/dead) && M.client && M.client.prefs && (MODE_XENOMORPH in M.client.prefs.be_special_role))
M << "An alien is ready to hatch! ([ghost_follow_link(src, M)]) (spawn)"
processing_objects -= src
update_icon()
diff --git a/code/modules/client/preference_setup/antagonism/01_candidacy.dm b/code/modules/client/preference_setup/antagonism/01_candidacy.dm
new file mode 100644
index 00000000000..449a5ceb747
--- /dev/null
+++ b/code/modules/client/preference_setup/antagonism/01_candidacy.dm
@@ -0,0 +1,84 @@
+var/list/private_valid_special_roles
+
+/datum/category_item/player_setup_item/antagonism/candidacy
+ name = "Candidacy"
+ sort_order = 1
+
+/datum/category_item/player_setup_item/antagonism/candidacy/load_character(var/savefile/S)
+ S["be_special"] >> pref.be_special_role
+
+/datum/category_item/player_setup_item/antagonism/candidacy/save_character(var/savefile/S)
+ S["be_special"] << pref.be_special_role
+
+/datum/category_item/player_setup_item/antagonism/candidacy/sanitize_character()
+ if(!istype(pref.be_special_role))
+ pref.be_special_role = list()
+
+ for(var/role in pref.be_special_role)
+ if(!(role in valid_special_roles()))
+ pref.be_special_role -= role
+
+/datum/category_item/player_setup_item/antagonism/candidacy/content(var/mob/user)
+ . += "Special Role Availability:
"
+ . += "
"
+ for(var/antag_type in all_antag_types)
+ var/datum/antagonist/antag = all_antag_types[antag_type]
+ . += "| [antag.role_text]: | "
+ if(jobban_isbanned(preference_mob(), antag.bantype))
+ . += "\[BANNED\] "
+ else if(antag.role_type in pref.be_special_role)
+ . += "Yes / No"
+ else
+ . += "Yes / No"
+ . += " |
"
+
+ var/list/ghost_traps = get_ghost_traps()
+ for(var/ghost_trap_key in ghost_traps)
+ var/datum/ghosttrap/ghost_trap = ghost_traps[ghost_trap_key]
+ if(!ghost_trap.list_as_special_role)
+ continue
+
+ . += "| [(ghost_trap.ghost_trap_role)]: | "
+ if(banned_from_ghost_role(preference_mob(), ghost_trap))
+ . += "\[BANNED\] "
+ else if(ghost_trap.pref_check in pref.be_special_role)
+ . += "Yes / No"
+ else
+ . += "Yes / No"
+ . += " |
"
+ . += "
"
+
+/datum/category_item/player_setup_item/proc/banned_from_ghost_role(var/mob, var/datum/ghosttrap/ghost_trap)
+ for(var/ban_type in ghost_trap.ban_checks)
+ if(jobban_isbanned(mob, ban_type))
+ return 1
+ return 0
+
+/datum/category_item/player_setup_item/antagonism/candidacy/OnTopic(var/href,var/list/href_list, var/mob/user)
+ if(href_list["add_special"])
+ if(!(href_list["add_special"] in valid_special_roles()))
+ return TOPIC_HANDLED
+ pref.be_special_role |= href_list["add_special"]
+ return TOPIC_REFRESH
+
+ if(href_list["del_special"])
+ pref.be_special_role -= href_list["del_special"]
+ return TOPIC_REFRESH
+
+ return ..()
+
+/datum/category_item/player_setup_item/antagonism/candidacy/proc/valid_special_roles()
+ if(!(private_valid_special_roles && private_valid_special_roles.len))
+ private_valid_special_roles = list()
+ for(var/antag_type in all_antag_types)
+ var/datum/antagonist/antag = all_antag_types[antag_type]
+ private_valid_special_roles += antag.role_type
+
+ var/list/ghost_traps = get_ghost_traps()
+ for(var/ghost_trap_key in ghost_traps)
+ var/datum/ghosttrap/ghost_trap = ghost_traps[ghost_trap_key]
+ if(!ghost_trap.list_as_special_role)
+ continue
+ private_valid_special_roles += ghost_trap.pref_check
+
+ return private_valid_special_roles
diff --git a/code/modules/client/preference_setup/antagonism/02_candidacy.dm b/code/modules/client/preference_setup/antagonism/02_candidacy.dm
deleted file mode 100644
index 82d44027afe..00000000000
--- a/code/modules/client/preference_setup/antagonism/02_candidacy.dm
+++ /dev/null
@@ -1,54 +0,0 @@
-var/global/list/special_roles = list( //keep synced with the defines BE_* in setup.dm --rastaf
-//some autodetection here.
-// TODO: Update to new antagonist system.
- "traitor" = IS_MODE_COMPILED("traitor"), // 0
- "operative" = IS_MODE_COMPILED("nuclear"), // 1
- "changeling" = IS_MODE_COMPILED("changeling"), // 2
- "wizard" = IS_MODE_COMPILED("wizard"), // 3
- "malf AI" = IS_MODE_COMPILED("malfunction"), // 4
- "revolutionary" = IS_MODE_COMPILED("revolution"), // 5
- "alien candidate" = 1, //always show // 6
- "positronic brain" = 1, // 7
- "cultist" = IS_MODE_COMPILED("cult"), // 8
- "infested monkey" = IS_MODE_COMPILED("monkey"), // 9
- "ninja" = "true", // 10
- "raider" = IS_MODE_COMPILED("heist"), // 11
- "diona" = 1, // 12
- "loyalist" = IS_MODE_COMPILED("revolution"), // 13
- "pAI candidate" = 1, // -- TLE // 14
-)
-
-/datum/category_item/player_setup_item/antagonism/candidacy
- name = "Candidacy"
- sort_order = 2
-
-/datum/category_item/player_setup_item/antagonism/candidacy/load_character(var/savefile/S)
- S["be_special"] >> pref.be_special
-
-/datum/category_item/player_setup_item/antagonism/candidacy/save_character(var/savefile/S)
- S["be_special"] << pref.be_special
-
-/datum/category_item/player_setup_item/antagonism/candidacy/sanitize_character()
- pref.be_special = sanitize_integer(pref.be_special, 0, 65535, initial(pref.be_special))
-
-/datum/category_item/player_setup_item/antagonism/candidacy/content(var/mob/user)
- if(jobban_isbanned(user, "Syndicate"))
- . += "You are banned from antagonist roles."
- pref.be_special = 0
- else
- var/n = 0
- for (var/i in special_roles)
- if(special_roles[i]) //if mode is available on the server
- if(jobban_isbanned(user, i) || (i == "positronic brain" && jobban_isbanned(user, "AI") && jobban_isbanned(user, "Cyborg")) || (i == "pAI candidate" && jobban_isbanned(user, "pAI")))
- . += "Be [i]: \[BANNED]
"
- else
- . += "Be [i]: [pref.be_special&(1<
"
- n++
-
-/datum/category_item/player_setup_item/antagonism/candidacy/OnTopic(var/href,var/list/href_list, var/mob/user)
- if(href_list["be_special"])
- var/num = text2num(href_list["be_special"])
- pref.be_special ^= (1<> pref.uplinklocation
@@ -16,9 +16,9 @@ var/global/list/uplink_locations = list("PDA", "Headset", "None")
pref.uplinklocation = sanitize_inlist(pref.uplinklocation, uplink_locations, initial(pref.uplinklocation))
/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user)
- . +="Uplink Type : [pref.uplinklocation]"
- . +="
"
- . +="Exploitable information:
"
+ . +="Antag Setup:
"
+ . +="Uplink Type: [pref.uplinklocation]
"
+ . +="Exploitable information:
"
if(jobban_isbanned(user, "Records"))
. += "You are banned from using character records.
"
else
diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm
index 13df4a05e01..90a15851dc4 100644
--- a/code/modules/client/preference_setup/preference_setup.dm
+++ b/code/modules/client/preference_setup/preference_setup.dm
@@ -19,7 +19,7 @@
category_item_type = /datum/category_item/player_setup_item/occupation
/datum/category_group/player_setup_category/appearance_preferences
- name = "Antagonism"
+ name = "Roles"
sort_order = 4
category_item_type = /datum/category_item/player_setup_item/antagonism
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index a5a037ee811..6c5eaeb0544 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -17,7 +17,7 @@ datum/preferences
//game-preferences
var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
var/ooccolor = "#010000" //Whatever this is set to acts as 'reset' color and is thus unusable as an actual custom color
- var/be_special = 0 //Special role selection
+ var/list/be_special_role = list() //Special role selection
var/UI_style = "Midnight"
var/toggles = TOGGLES_DEFAULT
var/UI_style_color = "#ffffff"
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 09ee55b1736..b2d0958dbd5 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -145,15 +145,3 @@
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1)
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2)
feedback_add_details("admin_verb","TAmbi") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-//be special
-/client/verb/toggle_be_special(role in be_special_flags)
- set name = "Toggle SpecialRole Candidacy"
- set category = "Preferences"
- set desc = "Toggles which special roles you would like to be a candidate for, during events."
- var/role_flag = be_special_flags[role]
- if(!role_flag) return
- prefs.be_special ^= role_flag
- prefs.save_preferences()
- src << "You will [(prefs.be_special & role_flag) ? "now" : "no longer"] be considered for [role] events (where possible)."
- feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/ghosttrap/trap.dm b/code/modules/ghosttrap/trap.dm
index c6afe7ac987..92eb1d0f1c5 100644
--- a/code/modules/ghosttrap/trap.dm
+++ b/code/modules/ghosttrap/trap.dm
@@ -3,12 +3,17 @@
var/list/ghost_traps
-proc/get_ghost_trap(var/trap_key)
+/proc/get_ghost_trap(var/trap_key)
if(!ghost_traps)
populate_ghost_traps()
return ghost_traps[trap_key]
-proc/populate_ghost_traps()
+/proc/get_ghost_traps()
+ if(!ghost_traps)
+ populate_ghost_traps()
+ return ghost_traps
+
+/proc/populate_ghost_traps()
ghost_traps = list()
for(var/traptype in typesof(/datum/ghosttrap))
var/datum/ghosttrap/G = new traptype
@@ -18,14 +23,20 @@ proc/populate_ghost_traps()
var/object = "positronic brain"
var/minutes_since_death = 0 // If non-zero the ghost must have been dead for this many minutes to be allowed to spawn
var/list/ban_checks = list("AI","Cyborg")
- var/pref_check = BE_AI
+ var/pref_check = BE_SYNTH
var/ghost_trap_message = "They are occupying a positronic brain now."
var/ghost_trap_role = "Positronic Brain"
+ var/can_set_own_name = TRUE
+ var/list_as_special_role = TRUE // If true, this entry will be listed as a special role in the character setup
+
+ var/list/request_timeouts
+
+/datum/ghosttrap/New()
+ request_timeouts = list()
+ ..()
// Check for bans, proper atom types, etc.
-/datum/ghosttrap/proc/assess_candidate(var/mob/dead/observer/candidate)
- if(!istype(candidate) || !candidate.client || !candidate.ckey)
- return 0
+/datum/ghosttrap/proc/assess_candidate(var/mob/dead/observer/candidate, var/mob/target)
if(!candidate.MayRespawn(1, minutes_since_death))
return 0
if(islist(ban_checks))
@@ -36,9 +47,13 @@ proc/populate_ghost_traps()
return 1
// Print a message to all ghosts with the right prefs/lack of bans.
-/datum/ghosttrap/proc/request_player(var/mob/target, var/request_string)
- if(!target)
- return
+/datum/ghosttrap/proc/request_player(var/mob/target, var/request_string, var/request_timeout)
+ if(request_timeout)
+ request_timeouts[target] = world.time + request_timeout
+ target.destruction.register(src, /datum/ghosttrap/proc/target_destroyed)
+ else
+ request_timeouts -= target
+
for(var/mob/dead/observer/O in player_list)
if(!O.MayRespawn())
continue
@@ -46,10 +61,13 @@ proc/populate_ghost_traps()
for(var/bantype in ban_checks)
if(jobban_isbanned(O, "[bantype]"))
continue
- if(pref_check && !(O.client.prefs.be_special & pref_check))
+ if(pref_check && !(pref_check in O.client.prefs.be_special_role))
continue
if(O.client)
- O << "[request_string]Click here if you wish to play as this option."
+ O << "[request_string] (Occupy) ([ghost_follow_link(target, O)])"
+
+/datum/ghosttrap/proc/target_destroyed(var/destroyed_target)
+ request_timeouts -= destroyed_target
// Handles a response to request_player().
/datum/ghosttrap/Topic(href, href_list)
@@ -60,7 +78,15 @@ proc/populate_ghost_traps()
var/mob/target = locate(href_list["target"]) // So much BYOND magic.
if(!target || !candidate)
return
- if(candidate == usr && assess_candidate(candidate) && !target.ckey)
+ if(candidate != usr)
+ return
+ if(request_timeouts[target] && world.time > request_timeouts[target])
+ candidate << "This occupation request is no longer valid."
+ return
+ if(target.key)
+ candidate << "The target is already occupied."
+ return
+ if(assess_candidate(candidate, target))
transfer_personality(candidate,target)
return 1
@@ -93,12 +119,17 @@ proc/populate_ghost_traps()
// Allows people to set their own name. May or may not need to be removed for posibrains if people are dumbasses.
/datum/ghosttrap/proc/set_new_name(var/mob/target)
+ if(!can_set_own_name)
+ return
+
var/newname = sanitizeSafe(input(target,"Enter a name, or leave blank for the default name.", "Name change","") as text, MAX_NAME_LEN)
if (newname != "")
target.real_name = newname
target.name = target.real_name
-// Doona pods and walking mushrooms.
+/***********************************
+* Diona pods and walking mushrooms *
+***********************************/
/datum/ghosttrap/plant
object = "living plant"
ban_checks = list("Dionaea")
@@ -111,4 +142,47 @@ proc/populate_ghost_traps()
// This is a hack, replace with some kind of species blurb proc.
if(istype(target,/mob/living/carbon/alien/diona))
target << "You are \a [target], one of a race of drifting interstellar plantlike creatures that sometimes share their seeds with human traders."
- target << "Too much darkness will send you into shock and starve you, but light will help you heal."
\ No newline at end of file
+ target << "Too much darkness will send you into shock and starve you, but light will help you heal."
+
+/*****************
+* Cortical Borer *
+*****************/
+/datum/ghosttrap/borer
+ object = "cortical borer"
+ ban_checks = list("Borer")
+ pref_check = MODE_BORER
+ ghost_trap_message = "They are occupying a borer now."
+ ghost_trap_role = "Cortical Borer"
+ can_set_own_name = FALSE
+ list_as_special_role = FALSE
+
+/datum/ghosttrap/borer/welcome_candidate(var/mob/target)
+ target << "You are a cortical borer! You are a brain slug that worms its way \
+ into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \
+ your host and your eventual spawn safe and warm."
+ target << "You can speak to your victim with say, to other borers with say [target.get_language_prefix()]x, and use your Abilities tab to access powers."
+
+/********************
+* Maintenance Drone *
+*********************/
+/datum/ghosttrap/drone
+ object = "maintenance drone"
+ pref_check = BE_PAI
+ ghost_trap_message = "They are occupying a maintenance drone now."
+ ghost_trap_role = "Maintenance Drone"
+ can_set_own_name = FALSE
+ list_as_special_role = FALSE
+
+/datum/ghosttrap/drone/New()
+ minutes_since_death = DRONE_SPAWN_DELAY
+ ..()
+
+datum/ghosttrap/drone/assess_candidate(var/mob/dead/observer/candidate, var/mob/target)
+ . = ..()
+ if(. && !target.can_be_possessed_by(candidate))
+ return 0
+
+datum/ghosttrap/drone/transfer_personality(var/mob/candidate, var/mob/living/silicon/robot/drone/drone)
+ if(!assess_candidate(candidate))
+ return 0
+ drone.transfer_personality(candidate.client)
diff --git a/code/modules/hydroponics/seed_mobs.dm b/code/modules/hydroponics/seed_mobs.dm
index 88462acb47a..3c2ee048a7f 100644
--- a/code/modules/hydroponics/seed_mobs.dm
+++ b/code/modules/hydroponics/seed_mobs.dm
@@ -4,7 +4,7 @@
if(!host || !istype(host)) return
var/datum/ghosttrap/plant/P = get_ghost_trap("living plant")
- P.request_player(host, "Someone is harvesting [display_name]. ")
+ P.request_player(host, "Someone is harvesting \a [display_name].")
spawn(75)
if(!host.ckey && !host.client)
diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm
index 5bb14920823..5350ec6403f 100644
--- a/code/modules/mob/living/carbon/brain/posibrain.dm
+++ b/code/modules/mob/living/carbon/brain/posibrain.dm
@@ -16,63 +16,14 @@
/obj/item/device/mmi/digital/posibrain/attack_self(mob/user as mob)
if(brainmob && !brainmob.key && searching == 0)
//Start the process of searching for a new user.
- user << "\blue You carefully locate the manual activation switch and start the positronic brain's boot process."
+ user << "You carefully locate the manual activation switch and start the positronic brain's boot process."
icon_state = "posibrain-searching"
src.searching = 1
- src.request_player()
+ var/datum/ghosttrap/G = get_ghost_trap("positronic brain")
+ G.request_player(brainmob, "Someone is requesting a personality for a positronic brain.", 60 SECONDS)
spawn(600) reset_search()
-/obj/item/device/mmi/digital/posibrain/proc/request_player()
- for(var/mob/dead/observer/O in player_list)
- if(!O.MayRespawn())
- continue
- if(jobban_isbanned(O, "AI") && jobban_isbanned(O, "Cyborg"))
- continue
- if(O.client)
- if(O.client.prefs.be_special & BE_AI)
- question(O.client)
-
-/obj/item/device/mmi/digital/posibrain/proc/question(var/client/C)
- spawn(0)
- if(!C) return
- var/response = alert(C, "Someone is requesting a personality for a positronic brain. Would you like to play as one?", "Positronic brain request", "Yes", "No", "Never for this round")
- if(response == "Yes")
- response = alert(C, "Are you sure you want to play as a positronic brain?", "Positronic brain request", "Yes", "No")
- if(!C || brainmob.key || 0 == searching) return //handle logouts that happen whilst the alert is waiting for a response, and responses issued after a brain has been located.
- if(response == "Yes")
- transfer_personality(C.mob)
- else if (response == "Never for this round")
- C.prefs.be_special ^= BE_AI
-
-
-/obj/item/device/mmi/digital/posibrain/transfer_identity(var/mob/living/carbon/H)
- ..()
- if(brainmob.mind)
- brainmob.mind.assigned_role = "Positronic Brain"
- brainmob << "You feel slightly disoriented. That's normal when you're just a metal cube."
- icon_state = "posibrain-occupied"
- return
-
-/obj/item/device/mmi/digital/posibrain/proc/transfer_personality(var/mob/candidate)
- announce_ghost_joinleave(candidate, 0, "They are occupying a positronic brain now.")
- src.searching = 0
- src.brainmob.mind = candidate.mind
- src.brainmob.ckey = candidate.ckey
- src.brainmob.mind.reset()
- src.name = "positronic brain ([src.brainmob.name])"
- src.brainmob << "You are a positronic brain, brought into existence on [station_name()]."
- src.brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI."
- src.brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm."
- src.brainmob << "Use say [candidate.get_language_prefix()]b to speak to other artificial intelligences."
- src.brainmob.mind.assigned_role = "Positronic Brain"
-
- var/turf/T = get_turf_or_move(src.loc)
- for (var/mob/M in viewers(T))
- M.show_message("\blue The positronic brain chimes quietly.")
- icon_state = "posibrain-occupied"
-
/obj/item/device/mmi/digital/posibrain/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer.
-
if(src.brainmob && src.brainmob.key) return
src.searching = 0
@@ -80,7 +31,19 @@
var/turf/T = get_turf_or_move(src.loc)
for (var/mob/M in viewers(T))
- M.show_message("\blue The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?")
+ M.show_message("The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?")
+
+/obj/item/device/mmi/digital/posibrain/attack_ghost(var/mob/dead/observer/user)
+ if(!searching || (src.brainmob && src.brainmob.key))
+ return
+
+ var/datum/ghosttrap/G = get_ghost_trap("positronic brain")
+ if(!G.assess_candidate(user))
+ return
+ var/response = alert(user, "Are you sure you wish to possess this [src]?", "Possess [src]", "Yes", "No")
+ if(response == "Yes")
+ G.transfer_personality(user, brainmob)
+ return
/obj/item/device/mmi/digital/posibrain/examine(mob/user)
if(!..(user))
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 3099c654375..ffe29a519d5 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -827,8 +827,11 @@ default behaviour is:
if(deaf >= 0)
ear_deaf = deaf
-/mob/living/proc/can_be_possessed_by(var/mob/dead/observer/possessor)
- if(!istype(possessor))
+/mob/proc/can_be_possessed_by(var/mob/dead/observer/possessor)
+ return istype(possessor) && possessor.client
+
+/mob/living/can_be_possessed_by(var/mob/dead/observer/possessor)
+ if(!..())
return 0
if(!possession_candidate)
possessor << "That animal cannot be possessed."
diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm
index 91a10bf3e50..1bae59372f7 100644
--- a/code/modules/mob/living/silicon/pai/recruit.dm
+++ b/code/modules/mob/living/silicon/pai/recruit.dm
@@ -357,7 +357,7 @@ var/datum/paiController/paiController // Global handler for pAI candidates
else
asked.Remove(O.key)
if(O.client)
- if(O.client.prefs.be_special & BE_PAI)
+ if(BE_PAI in O.client.prefs.be_special_role)
question(O.client)
/datum/paiController/proc/question(var/client/C)
@@ -370,4 +370,4 @@ var/datum/paiController/paiController // Global handler for pAI candidates
if(response == "Yes")
recruitWindow(C.mob)
else if (response == "Never for this round")
- C.prefs.be_special ^= BE_PAI
+ C.prefs.be_special_role -= BE_PAI
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 9346b63c16a..af4d59c3db2 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -60,11 +60,14 @@ var/list/mob_hat_cache = list()
holder_type = /obj/item/weapon/holder/drone
/mob/living/silicon/robot/drone/can_be_possessed_by(var/mob/dead/observer/possessor)
- if(!istype(possessor))
+ if(!istype(possessor) || !possessor.client || !possessor.ckey)
return 0
if(!config.allow_drone_spawn)
src << "Playing as drones is not currently permitted."
return 0
+ if(too_many_active_drones())
+ src << "The maximum number of active drones has been reached.."
+ return 0
if(jobban_isbanned(possessor,"Cyborg"))
usr << "You are banned from playing synthetics and cannot spawn as a drone."
return 0
@@ -161,7 +164,7 @@ var/list/mob_hat_cache = list()
if(hat)
return
hat = new_hat
- new_hat.loc = src
+ new_hat.forceMove(src)
updateicon()
//Drones cannot be upgraded with borg modules so we need to catch some items before they get used in ..().
@@ -196,12 +199,7 @@ var/list/mob_hat_cache = list()
return
user.visible_message("\The [user] swipes \his ID card through \the [src], attempting to reboot it.", ">You swipe your ID card through \the [src], attempting to reboot it.")
- var/drones = 0
- for(var/mob/living/silicon/robot/drone/D in world)
- if(D.key && D.client)
- drones++
- if(drones < config.max_maint_drones)
- request_player()
+ request_player()
return
else
@@ -304,28 +302,13 @@ var/list/mob_hat_cache = list()
//Reboot procs.
/mob/living/silicon/robot/drone/proc/request_player()
- for(var/mob/dead/observer/O in player_list)
- if(jobban_isbanned(O, "Cyborg"))
- continue
- if(O.client)
- if(O.client.prefs.be_special & BE_PAI)
- question(O.client)
-
-/mob/living/silicon/robot/drone/proc/question(var/client/C)
- spawn(0)
- if(!C || jobban_isbanned(C,"Cyborg")) return
- var/response = alert(C, "Someone is attempting to reboot a maintenance drone. Would you like to play as one?", "Maintenance drone reboot", "Yes", "No", "Never for this round")
- if(!C || ckey)
- return
- if(response == "Yes")
- transfer_personality(C)
- else if (response == "Never for this round")
- C.prefs.be_special ^= BE_PAI
+ if(too_many_active_drones())
+ return
+ var/datum/ghosttrap/G = get_ghost_trap("maintenance drone")
+ G.request_player(src, "Someone is attempting to reboot a maintenance drone.", 30 SECONDS)
/mob/living/silicon/robot/drone/proc/transfer_personality(var/client/player)
-
if(!player) return
-
src.ckey = player.ckey
if(player.mob && player.mob.mind)
@@ -361,3 +344,10 @@ var/list/mob_hat_cache = list()
/mob/living/silicon/robot/drone/construction/updatename()
real_name = "construction drone ([rand(100,999)])"
name = real_name
+
+/proc/too_many_active_drones()
+ var/drones = 0
+ for(var/mob/living/silicon/robot/drone/D in mob_list)
+ if(D.key && D.client)
+ drones++
+ return drones >= config.max_maint_drones
diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm
index d8606524e21..7836493722d 100644
--- a/code/modules/mob/living/simple_animal/borer/borer.dm
+++ b/code/modules/mob/living/simple_animal/borer/borer.dm
@@ -173,41 +173,8 @@
//Procs for grabbing players.
/mob/living/simple_animal/borer/proc/request_player()
- for(var/mob/dead/observer/O in player_list)
- if(jobban_isbanned(O, "Borer"))
- continue
- if(O.client)
- if(O.client.prefs.be_special & BE_ALIEN)
- question(O.client)
-
-/mob/living/simple_animal/borer/proc/question(var/client/C)
- spawn(0)
- if(!C) return
- var/response = alert(C, "A cortical borer needs a player. Are you interested?", "Cortical borer request", "Yes", "No", "Never for this round")
- if(!C || ckey)
- return
- if(response == "Yes")
- transfer_personality(C)
- else if (response == "Never for this round")
- C.prefs.be_special ^= BE_ALIEN
-
-/mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
-
- if(!candidate || !candidate.mob || !candidate.mob.mind)
- return
-
- src.mind = candidate.mob.mind
- candidate.mob.mind.current = src
- src.ckey = candidate.ckey
-
- if(src.mind)
- src.mind.assigned_role = "Cortical Borer"
- src.mind.special_role = "Cortical Borer"
-
- src << "You are a cortical borer! You are a brain slug that worms its way \
- into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \
- your host and your eventual spawn safe and warm."
- src << "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers."
+ var/datum/ghosttrap/G = get_ghost_trap("cortical borer")
+ G.request_player(src, "A cortical borer needs a player.")
/mob/living/simple_animal/borer/cannot_use_vents()
- return
\ No newline at end of file
+ return