diff --git a/SQL/beestation_schema.sql b/SQL/beestation_schema.sql
index 632d928b17a..ea6af3a19df 100644
--- a/SQL/beestation_schema.sql
+++ b/SQL/beestation_schema.sql
@@ -122,6 +122,7 @@ CREATE TABLE IF NOT EXISTS `SS13_characters` (
`general_record` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci',
`security_record` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci',
`medical_record` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci',
+ `role_preferences` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_general_ci',
PRIMARY KEY (`slot`, `ckey`) USING BTREE
) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB;
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 6620df43f48..93f62654284 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -76,6 +76,11 @@
#define IS_HERETIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic))
#define IS_HERETIC_MONSTER(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic_monster))
+#define FACTION_SYNDICATE "Syndicate"
+#define FACTION_BLOB "Blob"
+#define FACTION_ALIEN "Xenomorph"
+#define FACTION_WIZARD "Wizard"
+
#define PATH_SIDE "Side"
#define PATH_ASH "Ash"
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 003ccf3e3db..7fc49bbf376 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -6,7 +6,7 @@
#define PREFTOGGLE_SOUND_LOBBY (1<<3)
#define PREFTOGGLE_MEMBER_PUBLIC (1<<4)
#define PREFTOGGLE_INTENT_STYLE (1<<5)
-#define PREFTOGGLE_MIDROUND_ANTAG (1<<6)
+//#define PREFTOGGLE_MIDROUND_ANTAG (1<<6)
#define PREFTOGGLE_SOUND_INSTRUMENTS (1<<7)
#define PREFTOGGLE_SOUND_SHIP_AMBIENCE (1<<8)
#define PREFTOGGLE_SOUND_PRAYERS (1<<9)
@@ -27,7 +27,7 @@
#define PREFTOGGLE_RUNECHAT_NONMOBS (1<<22)
#define PREFTOGGLE_RUNECHAT_EMOTES (1<<23)
-#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_MIDROUND_ANTAG|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES)
+#define TOGGLES_DEFAULT (PREFTOGGLE_SOUND_ADMINHELP|PREFTOGGLE_SOUND_MIDI|PREFTOGGLE_SOUND_AMBIENCE|PREFTOGGLE_SOUND_LOBBY|PREFTOGGLE_MEMBER_PUBLIC|PREFTOGGLE_INTENT_STYLE|PREFTOGGLE_SOUND_INSTRUMENTS|PREFTOGGLE_SOUND_SHIP_AMBIENCE|PREFTOGGLE_SOUND_PRAYERS|PREFTOGGLE_SOUND_ANNOUNCEMENTS|PREFTOGGLE_OUTLINE_ENABLED|PREFTOGGLE_RUNECHAT_GLOBAL|PREFTOGGLE_RUNECHAT_NONMOBS|PREFTOGGLE_RUNECHAT_EMOTES)
// You CANNOT go above 1<<23 in BYOND due to integer limits
// Please add subsequent ones as PREFTOGGLE_2_[name]
@@ -183,7 +183,7 @@
#define PREFERENCE_TAG_PDA_COLOUR "23"
#define PREFERENCE_TAG_KEYBINDS "24"
#define PREFERENCE_TAG_PURCHASED_GEAR "25"
-#define PREFERENCE_TAG_BE_SPECIAL "26"
+#define PREFERENCE_TAG_ROLE_PREFERENCES "26"
#define PREFERENCE_TAG_PREFERRED_SYNDIE_ROLE "27" //NSV13 - syndicate crew role
// True value of max save slots (3 is default, 8 is byond member, +1 to either if you have the extra slot loadout entry). Potential max is 9
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index c5ba51036ad..c1da00441f8 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -4,21 +4,21 @@
+// Banning snowflake - global antag ban. Does not include ghost roles that aren't antagonists or forced antagonists
+#define BAN_ROLE_ALL_ANTAGONISTS "All Antagonists"
+
//These are synced with the Database, if you change the values of the defines
//then you MUST update the database!
-#define ROLE_SYNDICATE "Syndicate"
#define ROLE_TRAITOR "Traitor"
-#define ROLE_OPERATIVE "Operative"
+#define ROLE_OPERATIVE "Nuclear Operative"
#define ROLE_CHANGELING "Changeling"
#define ROLE_WIZARD "Wizard"
-#define ROLE_MALF "Malf AI"
+//#define ROLE_MALF "Malf AI" // Currently under traitor datum, so we can't have this separate.
#define ROLE_INCURSION "Incursion Team"
#define ROLE_EXCOMM "Excommunicated Syndicate Agent"
#define ROLE_REV "Revolutionary"
#define ROLE_REV_HEAD "Head Revolutionary"
-#define ROLE_REV_SUCCESSFUL "Victorious Revolutionary"
#define ROLE_ALIEN "Xenomorph"
-#define ROLE_PAI "pAI"
#define ROLE_CULTIST "Cultist"
#define ROLE_SERVANT_OF_RATVAR "Servant of Ratvar"
#define ROLE_HERETIC "Heretic"
@@ -28,66 +28,222 @@
#define ROLE_REVENANT "Revenant"
#define ROLE_DEVIL "Devil"
#define ROLE_BROTHER "Blood Brother"
-#define ROLE_BRAINWASHED "Brainwashed Victim"
#define ROLE_OVERTHROW "Syndicate Mutineer"
#define ROLE_HIVE "Hivemind Host"
-#define ROLE_HIVE_VESSEL "Awakened Vessel"
#define ROLE_OBSESSED "Obsessed"
-#define ROLE_SENTIENCE "Sentience Potion Spawn"
-#define ROLE_MIND_TRANSFER "Mind Transfer Potion"
-#define ROLE_POSIBRAIN "Posibrain"
-#define ROLE_DRONE "Drone"
-#define ROLE_DEATHSQUAD "Deathsquad"
-#define ROLE_LAVALAND "Lavaland"
+#define ROLE_SPACE_DRAGON "Space Dragon"
#define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent"
#define ROLE_GANG "Gangster"
#define ROLE_HOLOPARASITE "Holoparasite"
#define ROLE_TERATOMA "Teratoma"
-
+#define ROLE_MORPH "Morph"
+#define ROLE_NIGHTMARE "Nightmare"
+#define ROLE_SPACE_PIRATE "Space Pirate"
+#define ROLE_FUGITIVE "Fugitive"
+#define ROLE_FUGITIVE_HUNTER "Fugitive Hunter"
#define ROLE_SYNDI_CREW "Syndicate crew" //Nsv13 - added pvp role
#define ROLE_BLOODLING "Bloodling" //Nsv13 - Bloodling
#define ROLE_GHOSTSHIP "Ghost Ship" //NSV13 - Playable "NPC" ships
-#define ROLE_EXPERIMENTAL_CLONE "Experimental Clone"
+#define ROLE_SLAUGHTER_DEMON "Slaughter Demon"
+#define ROLE_CONTRACTOR_SUPPORT_UNIT "Contractor Support Unit"
+#define ROLE_PYRO_SLIME "Pyroclastic Anomaly Slime"
-//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
-//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
-//(in game days played) to play that role
-GLOBAL_LIST_INIT(special_roles, list(
- ROLE_TRAITOR = /datum/game_mode/traitor,
- ROLE_BROTHER = /datum/game_mode/traitor/bros,
- ROLE_INCURSION = /datum/game_mode/incursion,
- ROLE_EXCOMM = /datum/game_mode/incursion,
- ROLE_OPERATIVE = /datum/game_mode/nuclear,
- ROLE_CHANGELING = /datum/game_mode/changeling,
- ROLE_WIZARD = /datum/game_mode/wizard,
- ROLE_MALF,
- ROLE_REV = /datum/game_mode/revolution,
+/// Roles that are antagonists, roundstart or not, and have passes to do.. antagonistry
+GLOBAL_LIST_INIT(antagonist_bannable_roles, list(
+ ROLE_TRAITOR,
+ ROLE_OPERATIVE,
+ ROLE_CHANGELING,
+ ROLE_WIZARD,
+// ROLE_MALF,
+ ROLE_INCURSION,
+ ROLE_EXCOMM,
+ ROLE_REV,
+ ROLE_REV_HEAD,
ROLE_ALIEN,
- ROLE_PAI,
- ROLE_CULTIST = /datum/game_mode/cult,
- ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockcult,
+ ROLE_CULTIST,
+ ROLE_SERVANT_OF_RATVAR,
+ ROLE_HERETIC,
ROLE_BLOB,
ROLE_NINJA,
- ROLE_OBSESSED,
- ROLE_REVENANT,
ROLE_ABDUCTOR,
- ROLE_DEVIL = /datum/game_mode/devil,
- ROLE_OVERTHROW = /datum/game_mode/overthrow,
- ROLE_HIVE = /datum/game_mode/hivemind,
- ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs,
- ROLE_SENTIENCE,
- ROLE_GANG = /datum/game_mode/gang,
+ ROLE_REVENANT,
+ ROLE_DEVIL,
+ ROLE_BROTHER,
+ ROLE_OVERTHROW,
+ ROLE_HIVE,
+ ROLE_OBSESSED,
+ ROLE_SPACE_DRAGON,
+ ROLE_INTERNAL_AFFAIRS,
+ ROLE_GANG,
ROLE_HOLOPARASITE,
- ROLE_HERETIC = /datum/game_mode/heretics,
- ROLE_SYNDI_CREW = /datum/game_mode/pvp, //NSV13
- ROLE_BLOODLING = /datum/game_mode/bloodling, //NSV13
+ ROLE_MORPH,
+ ROLE_NIGHTMARE,
+ ROLE_SPACE_PIRATE,
+ ROLE_SYNDI_CREW, //NSV13
+ ROLE_BLOODLING, //NSV13
ROLE_GHOSTSHIP, //NSV13
- ROLE_TERATOMA
+ ROLE_TERATOMA,
+ ROLE_FUGITIVE,
+ ROLE_FUGITIVE_HUNTER,
+ ROLE_SLAUGHTER_DEMON,
+ ROLE_CONTRACTOR_SUPPORT_UNIT,
))
//nsv13 - pvp + bloodling modes added here
+#define BAN_ROLE_FORCED_ANTAGONISTS "Forced Antagonists"
+
+#define ROLE_BRAINWASHED "Brainwashed Victim"
+#define ROLE_HYPNOTIZED "Hypnotized Victim"
+#define ROLE_HIVE_VESSEL "Awakened Vessel"
+
+/// Forced antagonist roles
+GLOBAL_LIST_INIT(forced_bannable_roles, list(
+ ROLE_BRAINWASHED,
+ ROLE_HYPNOTIZED,
+ ROLE_HIVE_VESSEL,
+))
+
+#define BAN_ROLE_ALL_GHOST "Non-Antagonist Ghost Roles"
+
+#define ROLE_PAI "pAI"
+#define ROLE_POSIBRAIN "Posibrain"
+#define ROLE_DRONE "Drone"
+#define ROLE_SENTIENCE "Sentience Potion Spawn"
+#define ROLE_EXPERIMENTAL_CLONE "Experimental Clone"
+#define ROLE_LAVALAND_ELITE "Lavaland Elite"
+#define ROLE_SPECTRAL_BLADE "Spectral Blade"
+#define ROLE_ASHWALKER "Ashwalker"
+#define ROLE_LIFEBRINGER "Lifebringer"
+#define ROLE_FREE_GOLEM "Free Golem"
+#define ROLE_HERMIT "Hermit"
+#define ROLE_TRANSLOCATED_VET "Translocated Vet"
+#define ROLE_LAVALAND_ESCAPED_PRISONER "Lavaland Escaped Prisoner"
+#define ROLE_BEACH_BUM "Beach Bum"
+#define ROLE_HOTEL_STAFF "Hotel Staff"
+#define ROLE_LAVALAND_SYNDICATE "Lavaland Syndicate"
+#define ROLE_DEMONIC_FRIEND "Demonic Friend"
+#define ROLE_ANCIENT_CREW "Ancient Crew"
+#define ROLE_SKELETAL_REMAINS "Skeletal Remains"
+#define ROLE_SENTIENT_ANIMAL "Sentient Animal"
+#define ROLE_HOLY_SUMMONED "Holy Summoned"
+#define ROLE_SURVIVALIST "Exploration Survivalist"
+#define ROLE_EXPLORATION_VIP "Exploration VIP"
+#define ROLE_SENTIENT_XENOARTIFACT "Sentient Xenoartifiact"
+
+/// Any ghost role that is not really an antagonist or doesn't antagonize (lavaland, sentience potion, etc)
+GLOBAL_LIST_INIT(ghost_role_bannable_roles, list(
+ ROLE_PAI,
+ ROLE_POSIBRAIN,
+ ROLE_DRONE,
+ ROLE_SENTIENCE,
+ ROLE_EXPERIMENTAL_CLONE,
+ ROLE_LAVALAND_ELITE,
+ ROLE_SPECTRAL_BLADE,
+ ROLE_ASHWALKER,
+ ROLE_LIFEBRINGER,
+ ROLE_FREE_GOLEM,
+ ROLE_HERMIT,
+ ROLE_TRANSLOCATED_VET,
+ ROLE_LAVALAND_ESCAPED_PRISONER,
+ ROLE_BEACH_BUM,
+ ROLE_HOTEL_STAFF,
+ ROLE_LAVALAND_SYNDICATE,
+ ROLE_DEMONIC_FRIEND,
+ ROLE_ANCIENT_CREW,
+ ROLE_SKELETAL_REMAINS,
+ ROLE_SENTIENT_ANIMAL,
+ ROLE_HOLY_SUMMONED,
+))
+
+#define ROLE_IMAGINARY_FRIEND "Imaginary Friend"
+#define ROLE_SPLIT_PERSONALITY "Split Personality"
+#define ROLE_MIND_TRANSFER "Mind Transfer Potion"
+#define ROLE_ERT "Emergency Response Team"
+
+/// Other roles that don't really fit any of the above, and probably shouldn't be banned with the others as a group
+/// Little to no impact on anything
+GLOBAL_LIST_INIT(other_bannable_roles, list(
+ ROLE_IMAGINARY_FRIEND,
+ ROLE_SPLIT_PERSONALITY,
+ ROLE_MIND_TRANSFER,
+ ROLE_ERT,
+))
+
+/// Do not ban this role. Oh my god. Please.
+#define UNBANNABLE_ANTAGONIST "Unbannable"
+
+/client/proc/role_preference_enabled(role_preference_key)
+ if(!ispath(role_preference_key, /datum/role_preference))
+ CRASH("Invalid role_preference_key [role_preference_key] passed to role_preference_enabled!")
+ if(!src.prefs)
+ return FALSE
+ var/list/source = src.prefs.role_preferences
+ var/datum/role_preference/pref = role_preference_key
+ if(initial(pref.per_character))
+ source = src.prefs.active_character.role_preferences_character
+ var/role_preference_value = source["[role_preference_key]"]
+ if(isnum(role_preference_value) && !role_preference_value) // explicitly disabled and not null
+ return FALSE
+ return TRUE
+
+/// If the client given is fit for a given role based on the arguments passed
+/// banning_key: ROLE_X used for this role - to check if the player is banned.
+/// role_preference_key: The /datum/role_preference typepath to check if the player has the role enabled and would like to receive the poll.
+/// poll_ignore_key: The POLL_IGNORE_X define for this role, used for temporarily disabling ghost polls for high volume roles.
+/// req_hours: The amount of living hours required to receive this role.
+/// feedback: if we should send a to_chat
+/client/proc/should_include_for_role(banning_key = BAN_ROLE_ALL_ANTAGONISTS, role_preference_key = null, poll_ignore_key = null, req_hours = 0, feedback = FALSE)
+ if(QDELETED(src) || (poll_ignore_key && GLOB.poll_ignore[poll_ignore_key] && (src.ckey in GLOB.poll_ignore[poll_ignore_key])))
+ return FALSE
+ if(role_preference_key)
+ if(!ispath(role_preference_key, /datum/role_preference))
+ CRASH("Invalid role_preference_key [role_preference_key] passed to should_include_for_role!")
+ if(!src.role_preference_enabled(role_preference_key))
+ return FALSE
+ if(banning_key)
+ if(is_banned_from(src.ckey, banning_key))
+ if(feedback)
+ to_chat(src, "You are banned from this role! ")
+ return FALSE
+ if(req_hours) //minimum living hour count
+ if((src.get_exp_living(TRUE)/60) < req_hours)
+ if(feedback)
+ to_chat(src, "You do not have enough living hours to take this role ([req_hours]hrs required)! ")
+ return FALSE
+ return TRUE
+
+/client/proc/can_take_ghost_spawner(banning_key = BAN_ROLE_ALL_ANTAGONISTS, use_cooldown = TRUE, is_ghost_role = FALSE, is_admin_spawned = FALSE)
+ if(!istype(src))
+ return FALSE
+ if(is_ghost_role && !(GLOB.ghost_role_flags & GHOSTROLE_SPAWNER) && !is_admin_spawned)
+ to_chat(src, "An admin has temporarily disabled non-admin ghost roles! ")
+ return FALSE
+ if(!src.should_include_for_role(
+ banning_key = banning_key,
+ feedback = TRUE
+ ))
+ return FALSE
+ if(use_cooldown && src.next_ghost_role_tick > world.time)
+ to_chat(src, "You have died recently, you must wait [(src.next_ghost_role_tick - world.time)/10] seconds until you can use a ghost spawner. ")
+ return FALSE
+ return TRUE
+
//Job defines for what happens when you fail to qualify for any job during job selection
#define BEOVERFLOW 1
#define BERANDOMJOB 2
#define RETURNTOLOBBY 3
+
+#define ROLE_PREFERENCE_CATEGORY_ANAGONIST "Antagonists"
+#define ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING "Midrounds (Living)"
+#define ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST "Midrounds (Ghost Poll)"
+
+GLOBAL_LIST_INIT(role_preference_entries, init_role_preference_entries())
+
+/proc/init_role_preference_entries()
+ var/list/output = list()
+ for (var/datum/role_preference/preference_type as anything in subtypesof(/datum/role_preference))
+ if (initial(preference_type.abstract_type) == preference_type)
+ continue
+ output[preference_type] = new preference_type
+ return output
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index dae5cf88e73..89c70e94cc6 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -425,7 +425,7 @@
else
candidates -= M
-/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0)
+/proc/pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time = 300, ignore_category = null, flashwindow = TRUE, req_hours = 0)
var/list/candidates = list()
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
return candidates
@@ -433,31 +433,30 @@
for(var/mob/dead/observer/G in GLOB.player_list)
candidates += G
- return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates, req_hours)
+ return pollCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category, flashwindow, candidates, req_hours)
-/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null, req_hours = 0)
+/proc/pollCandidates(Question, banning_key, role_preference_key = null, poll_time = 300, poll_ignore_key = null, flashwindow = TRUE, list/group = null, req_hours = 0)
var/time_passed = world.time
if (!Question)
Question = "Would you like to be a special role?"
+ if(isnull(poll_ignore_key)) // FALSE will not put one, no matter what
+ if(role_preference_key)
+ poll_ignore_key = "role_[role_preference_key]"
+ else if(banning_key)
+ poll_ignore_key = "ban_[role_preference_key]"
var/list/result = list()
for(var/m in group)
var/mob/M = m
- if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && (M.ckey in GLOB.poll_ignore[ignore_category])))
+ if(QDELETED(M) || !M.key || !M.client)
continue
- if(be_special_flag)
- if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special))
- continue
- if(gametypeCheck)
- if(!gametypeCheck.age_check(M.client))
- continue
- if(jobbanType)
- if(QDELETED(M) || is_banned_from(M.ckey, list(jobbanType, ROLE_SYNDICATE)))
- continue
- if(req_hours) //minimum living hour count
- if((M.client.get_exp_living(TRUE)/60) < req_hours)
- continue
-
- showCandidatePollWindow(M, poll_time, Question, result, ignore_category, time_passed, flashwindow)
+ if(!M.client.should_include_for_role(
+ banning_key = banning_key,
+ role_preference_key = role_preference_key,
+ poll_ignore_key = poll_ignore_key,
+ req_hours = req_hours
+ ))
+ continue
+ showCandidatePollWindow(M, poll_time, Question, result, poll_ignore_key, time_passed, flashwindow)
sleep(poll_time)
//Check all our candidates, to make sure they didn't log off or get deleted during the wait period.
@@ -469,14 +468,14 @@
return result
-/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null)
- var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
+/proc/pollCandidatesForMob(Question, jobbanType, role_preference_key, poll_time = 300, mob/M, ignore_category = null)
+ var/list/L = pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category)
if(QDELETED(M) || !M.loc)
return list()
return L
-/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null)
- var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
+/proc/pollCandidatesForMobs(Question, jobbanType, role_preference_key, poll_time = 300, list/mobs, ignore_category = null)
+ var/list/L = pollGhostCandidates(Question, jobbanType, role_preference_key, poll_time, ignore_category)
var/i=1
for(var/v in mobs)
var/atom/A = v
diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm
index 6f15e22d270..1cca65374b0 100644
--- a/code/_globalvars/lists/poll_ignore.dm
+++ b/code/_globalvars/lists/poll_ignore.dm
@@ -1,57 +1,42 @@
//Each lists stores ckeys for "Never for this round" option category
-#define POLL_IGNORE_SENTIENCE_POTION "sentience_potion"
-#define POLL_IGNORE_POSSESSED_BLADE "possessed_blade"
#define POLL_IGNORE_ALIEN_LARVA "alien_larva"
-#define POLL_IGNORE_SYNDICATE "syndicate"
-#define POLL_IGNORE_HOLOPARASITE "holoparasite"
#define POLL_IGNORE_POSIBRAIN "posibrain"
-#define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade"
-#define POLL_IGNORE_CONSTRUCT "construct"
#define POLL_IGNORE_SPIDER "spider"
#define POLL_IGNORE_ASHWALKER "ashwalker"
+#define POLL_IGNORE_BLOB_HELPER "blob_helper"
+#define POLL_IGNORE_CLOCKWORK_HELPER "clockwork_helper"
+#define POLL_IGNORE_CULT_SHADE "cult_shade"
#define POLL_IGNORE_GOLEM "golem"
-#define POLL_IGNORE_SWARMER "swarmer"
#define POLL_IGNORE_DRONE "drone"
-#define POLL_IGNORE_FUGITIVE "fugitive"
-#define POLL_IGNORE_DEFECTIVECLONE "defective_clone"
-#define POLL_IGNORE_PYROSLIME "slime"
+#define POLL_IGNORE_SWARMER "swarmer"
+#define POLL_IGNORE_SPECTRAL_BLADE "spectral_blade"
#define POLL_IGNORE_SHADE "shade"
-#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend"
+#define POLL_IGNORE_FUGITIVE "fugitive"
#define POLL_IGNORE_SPLITPERSONALITY "split_personality"
-#define POLL_IGNORE_CONTRACTOR_SUPPORT "contractor_support"
-#define POLL_IGNORE_CLOCKWORK "clockwork"
#define POLL_IGNORE_GHOSTSHIP "ghost ships" //NSV13
-#define POLL_IGNORE_EXPERIMENTAL_CLONE "experimental_clone"
+#define POLL_IGNORE_WIZARD_HELPER "wizard_helper"
-GLOBAL_LIST_INIT(poll_ignore_desc, list(
- POLL_IGNORE_SENTIENCE_POTION = "Sentience potion",
- POLL_IGNORE_POSSESSED_BLADE = "Possessed blade",
- POLL_IGNORE_ALIEN_LARVA = "Xenomorph larva",
- POLL_IGNORE_SYNDICATE = "Syndicate",
- POLL_IGNORE_HOLOPARASITE = "Holoparasite",
- POLL_IGNORE_POSIBRAIN = "Positronic brain",
- POLL_IGNORE_SPECTRAL_BLADE = "Spectral blade",
- POLL_IGNORE_CONSTRUCT = "Construct",
- POLL_IGNORE_SPIDER = "Spiders",
- POLL_IGNORE_ASHWALKER = "Ashwalker eggs",
- POLL_IGNORE_GOLEM = "Golems",
- POLL_IGNORE_SWARMER = "Swarmer shells",
- POLL_IGNORE_DRONE = "Drone shells",
- POLL_IGNORE_FUGITIVE = "Fugitive Hunter",
- POLL_IGNORE_DEFECTIVECLONE = "Defective clone",
- POLL_IGNORE_PYROSLIME = "Slime",
- POLL_IGNORE_SHADE = "Shade",
- POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend",
- POLL_IGNORE_SPLITPERSONALITY = "Split Personality",
- POLL_IGNORE_CONTRACTOR_SUPPORT = "Contractor Support Unit",
- POLL_IGNORE_GHOSTSHIP = "Ghost Ship", //NSV13
- POLL_IGNORE_EXPERIMENTAL_CLONE = "Experimental clone"
+GLOBAL_LIST_INIT(poll_ignore_list, list(
+ POLL_IGNORE_ALIEN_LARVA,
+ POLL_IGNORE_ASHWALKER,
+ POLL_IGNORE_BLOB_HELPER,
+ POLL_IGNORE_CLOCKWORK_HELPER,
+ POLL_IGNORE_CULT_SHADE,
+ POLL_IGNORE_GOLEM,
+ POLL_IGNORE_DRONE,
+ POLL_IGNORE_POSIBRAIN,
+ POLL_IGNORE_SPECTRAL_BLADE,
+ POLL_IGNORE_SHADE,
+ POLL_IGNORE_SPIDER,
+ POLL_IGNORE_GHOSTSHIP, //NSV13
+ POLL_IGNORE_WIZARD_HELPER,
))
+
GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore())
/proc/init_poll_ignore()
. = list()
- for (var/k in GLOB.poll_ignore_desc)
+ for (var/k in GLOB.poll_ignore_list)
.[k] = list()
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index bd62bf9d98e..41a29447708 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -137,8 +137,8 @@ SUBSYSTEM_DEF(job)
return FALSE
job.current_positions = max(0, job.current_positions - 1)
-/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag)
- JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
+/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level)
+ JobDebug("Running FOC, Job: [job], Level: [level]")
var/list/candidates = list()
for(var/mob/dead/new_player/player in unassigned)
if(QDELETED(player) || is_banned_from(player.ckey, job.title))
@@ -150,9 +150,6 @@ SUBSYSTEM_DEF(job)
if(job.required_playtime_remaining(player.client))
JobDebug("FOC player not enough xp, Player: [player]")
continue
- if(flag && (!(flag in player.client.prefs.be_special)))
- JobDebug("FOC flag failed, Player: [player], Flag: [flag], ")
- continue
if(player.mind && (job.title in player.mind.restricted_roles))
JobDebug("FOC incompatible with antagonist role, Player: [player]")
continue
diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm
index d942dda58bd..4fc3090979d 100644
--- a/code/controllers/subsystem/pai.dm
+++ b/code/controllers/subsystem/pai.dm
@@ -149,8 +149,10 @@ SUBSYSTEM_DEF(pai)
for(var/mob/dead/observer/G in GLOB.player_list)
if(!G.key || !G.client)
continue
- if(!(ROLE_PAI in G.client.prefs.be_special))
- continue
+ //NSV13 - Disabled until Beemerge, I don't know what to put here without porting from another PR so WEH
+ //if(!(ROLE_PAI in G.client.prefs.be_special))
+ // continue
+ //NSV13 - Stop
to_chat(G, "[user] is requesting a pAI personality! Use the pAI button to submit yourself as one. ")
addtimer(CALLBACK(src, PROC_REF(spam_again)), spam_delay)
var/list/available = list()
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index b77bd799465..ad30ac75041 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -46,7 +46,7 @@
if(owner.stat == DEAD || !owner.mind)
qdel(src)
return
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_IMAGINARY_FRIEND, null, 7.5 SECONDS, friend)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
friend.key = C.key
@@ -193,7 +193,7 @@
hearers += client
if(owner_chat_map)
hearers += owner.client
-
+
var/rendered = "[name] [say_quote(message)] "
var/dead_rendered = "[name] (Imaginary friend of [owner]) [say_quote(message)] "
diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm
index b2ebc6c1899..c5a8a5fd4fd 100644
--- a/code/datums/brain_damage/split_personality.dm
+++ b/code/datums/brain_damage/split_personality.dm
@@ -31,7 +31,7 @@
if(owner.stat == DEAD || !owner.mind)
qdel(src)
return
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_SPLIT_PERSONALITY, null, 7.5 SECONDS, stranger_backseat)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
stranger_backseat.key = C.key
@@ -196,7 +196,7 @@
/datum/brain_trauma/severe/split_personality/brainwashing/get_ghost()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", null, null, null, 75, stranger_backseat)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", ROLE_TRAITOR, null, 7.5 SECONDS, stranger_backseat, ignore_category = FALSE)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
stranger_backseat.key = C.key
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index b60bedb1a3e..218d0255411 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -80,10 +80,11 @@
/datum/disease/transformation/proc/replace_banned_player(var/mob/living/new_mob) // This can run well after the mob has been transferred, so need a handle on the new mob to kill it if needed.
set waitfor = FALSE
+ affected_mob.playable_bantype = bantype
affected_mob.ghostize(TRUE,SENTIENCE_FORCE)
to_chat(affected_mob, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, bantype, 50, affected_mob)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [affected_mob.name]?", bantype, null, 7.5 SECONDS, affected_mob, ignore_category = FALSE)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbanned player.")
@@ -108,7 +109,7 @@
stage5 = list("Your skin feels as if it's about to burst off! ")
new_form = /mob/living/silicon/robot
infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_ROBOTIC)
- bantype = "Cyborg"
+ bantype = JOB_NAME_CYBORG
/datum/disease/transformation/robot/stage_act()
..()
diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm
index 6059dba2a14..2ba9c586359 100644
--- a/code/game/gamemodes/brother/traitor_bro.dm
+++ b/code/game/gamemodes/brother/traitor_bro.dm
@@ -27,7 +27,7 @@
if(CONFIG_GET(flag/protect_heads_from_antagonist))
restricted_jobs += GLOB.command_positions
- var/list/datum/mind/possible_brothers = get_players_for_role(ROLE_BROTHER)
+ var/list/datum/mind/possible_brothers = get_players_for_role(/datum/antagonist/brother, /datum/role_preference/antagonist/blood_brother)
var/num_teams = team_amount
var/bsc = CONFIG_GET(number/brother_scaling_coeff)
@@ -40,7 +40,7 @@
var/datum/team/brother_team/team = new
var/team_size = prob(10) ? min(3, possible_brothers.len) : 2
for(var/k = 1 to team_size)
- var/datum/mind/bro = antag_pick(possible_brothers, ROLE_BROTHER)
+ var/datum/mind/bro = antag_pick(possible_brothers, /datum/role_preference/antagonist/blood_brother)
possible_brothers -= bro
antag_candidates -= bro
team.add_member(bro)
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 884e9025c74..49b9cdcf0c9 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -10,7 +10,8 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
name = "changeling"
config_tag = "changeling"
report_type = "changeling"
- antag_flag = ROLE_CHANGELING
+ role_preference = /datum/role_preference/antagonist/changeling
+ antag_datum = /datum/antagonist/changeling
false_report_weight = 10
restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG)
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
@@ -52,7 +53,7 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
for(var/i = 0, i < num_changelings, i++)
if(!antag_candidates.len)
break
- var/datum/mind/changeling = antag_pick(antag_candidates, ROLE_CHANGELING)
+ var/datum/mind/changeling = antag_pick(antag_candidates, /datum/role_preference/antagonist/changeling)
antag_candidates -= changeling
changelings += changeling
changeling.special_role = ROLE_CHANGELING
@@ -75,12 +76,14 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
if(changelings.len >= changelingcap) //Caps number of latejoin antagonists
return
if(changelings.len <= (changelingcap - 2) || prob(100 - (csc * 2)))
- if(ROLE_CHANGELING in character.client.prefs.be_special)
- if(!is_banned_from(character.ckey, list(ROLE_CHANGELING, ROLE_SYNDICATE)) && !QDELETED(character))
- if(age_check(character.client))
- if(!(character.job in restricted_jobs))
- character.mind.make_Changeling()
- changelings += character.mind
+ if(!QDELETED(character) && character.client?.should_include_for_role(
+ banning_key = initial(antag_datum.banning_key),
+ role_preference_key = role_preference,
+ req_hours = initial(antag_datum.required_living_playtime)
+ ))
+ if(!(character.job in restricted_jobs))
+ character.mind.make_Changeling()
+ changelings += character.mind
/datum/game_mode/changeling/generate_report()
return "The Gorlex Marauders have announced the successful raid and destruction of Central Command containment ship #S-[rand(1111, 9999)]. This ship housed only a single prisoner - \
diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm
index 95057675495..6912af56cbf 100644
--- a/code/game/gamemodes/changeling/traitor_chan.dm
+++ b/code/game/gamemodes/changeling/traitor_chan.dm
@@ -22,7 +22,7 @@
/datum/game_mode/traitor/changeling/can_start()
if(!..())
return 0
- possible_changelings = get_players_for_role(ROLE_CHANGELING)
+ possible_changelings = get_players_for_role(/datum/antagonist/changeling, /datum/role_preference/antagonist/changeling)
if(possible_changelings.len < required_enemies)
return 0
return 1
@@ -37,7 +37,7 @@
if(CONFIG_GET(flag/protect_heads_from_antagonist))
restricted_jobs += GLOB.command_positions
- var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING)
+ var/list/datum/mind/possible_changelings = get_players_for_role(/datum/antagonist/changeling, /datum/role_preference/antagonist/changeling)
var/num_changelings = 1
@@ -51,7 +51,7 @@
for(var/j = 0, j < num_changelings, j++)
if(!possible_changelings.len)
break
- var/datum/mind/changeling = antag_pick(possible_changelings, ROLE_CHANGELING)
+ var/datum/mind/changeling = antag_pick(possible_changelings, /datum/role_preference/antagonist/changeling)
antag_candidates -= changeling
possible_changelings -= changeling
changeling.special_role = ROLE_CHANGELING
@@ -72,13 +72,16 @@
if(changelings.len >= changelingcap) //Caps number of latejoin antagonists
..()
return
+ var/datum/antagonist/aux_antag_datum = /datum/antagonist/changeling
if(changelings.len <= (changelingcap - 2) || prob(100 / (csc * 4)))
- if(ROLE_CHANGELING in character.client.prefs.be_special)
- if(!is_banned_from(character.ckey, list(ROLE_CHANGELING, ROLE_SYNDICATE)) && !QDELETED(character))
- if(age_check(character.client))
- if(!(character.job in restricted_jobs))
- character.mind.make_Changeling()
- changelings += character.mind
+ if(!QDELETED(character) && character.client.should_include_for_role(
+ banning_key = initial(aux_antag_datum.banning_key),
+ role_preference_key = /datum/role_preference/antagonist/changeling,
+ req_hours = initial(aux_antag_datum.required_living_playtime)
+ ))
+ if(!(character.job in restricted_jobs))
+ character.mind.make_Changeling()
+ changelings += character.mind
if(QDELETED(character))
return
..()
diff --git a/code/game/gamemodes/clock_cult/clockcult.dm b/code/game/gamemodes/clock_cult/clockcult.dm
index b31dd9e379c..af4f68b3bc8 100644
--- a/code/game/gamemodes/clock_cult/clockcult.dm
+++ b/code/game/gamemodes/clock_cult/clockcult.dm
@@ -32,8 +32,8 @@ GLOBAL_VAR(clockcult_eminence)
required_players = 24
required_enemies = 4
recommended_enemies = 4
- antag_flag = ROLE_SERVANT_OF_RATVAR
- enemy_minimum_age = 14
+ role_preference = /datum/role_preference/antagonist/clock_cultist
+ antag_datum = /datum/antagonist/servant_of_ratvar
title_icon = "clockcult"
announce_span = "danger"
@@ -58,7 +58,7 @@ GLOBAL_VAR(clockcult_eminence)
for(var/i in 1 to clock_cultists)
if(!antag_candidates.len)
break
- var/datum/mind/clockie = antag_pick(antag_candidates, ROLE_SERVANT_OF_RATVAR)
+ var/datum/mind/clockie = antag_pick(antag_candidates, /datum/role_preference/antagonist/clock_cultist)
//In case antag_pick breaks
if(!clockie)
continue
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 7b2498f9263..1280a629674 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -36,14 +36,14 @@
name = "cult"
config_tag = "cult"
report_type = "cult"
- antag_flag = ROLE_CULTIST
+ role_preference = /datum/role_preference/antagonist/blood_cultist
+ antag_datum = /datum/antagonist/cult
false_report_weight = 1
restricted_jobs = list(JOB_NAME_CHAPLAIN,JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL)
protected_jobs = list()
required_players = 29
required_enemies = 4
recommended_enemies = 4
- enemy_minimum_age = 14
announce_span = "cult"
announce_text = "Some crew members are trying to start a cult to Nar'Sie!\n\
@@ -84,7 +84,7 @@
for(var/cultists_number = 1 to recommended_enemies)
if(!antag_candidates.len)
break
- var/datum/mind/cultist = antag_pick(antag_candidates, ROLE_CULTIST)
+ var/datum/mind/cultist = antag_pick(antag_candidates, /datum/role_preference/antagonist/blood_cultist)
antag_candidates -= cultist
if(!cultist)
cultists_number--
diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm
index b7d0bbe7766..95b7ab5db67 100644
--- a/code/game/gamemodes/devil/devil_game_mode.dm
+++ b/code/game/gamemodes/devil/devil_game_mode.dm
@@ -2,14 +2,14 @@
name = "devil"
config_tag = "devil"
report_type = "devil"
- antag_flag = ROLE_DEVIL
+ role_preference = /datum/role_preference/antagonist/devil
+ antag_datum = /datum/antagonist/devil
false_report_weight = 1
protected_jobs = list(JOB_NAME_LAWYER, JOB_NAME_CURATOR, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE)
required_players = 0
required_enemies = 1
recommended_enemies = 4
reroll_friendly = 1
- enemy_minimum_age = 0
title_icon = "devil"
allowed_special = list(/datum/special_role/traitor)
@@ -42,7 +42,7 @@
for(var/j = 0, j < num_devils, j++)
if (!antag_candidates.len)
break
- var/datum/mind/devil = antag_pick(antag_candidates, ROLE_DEVIL)
+ var/datum/mind/devil = antag_pick(antag_candidates, /datum/role_preference/antagonist/devil)
devils += devil
devil.special_role = traitor_name
devil.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 8e07f35c244..de004e48a54 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -699,15 +699,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
log_game("DYNAMIC: [src] passed lowpop_lowimpact requirement: ([living_antags_count] antags of [living_players_count] players - [antag_percent]%)")
return FALSE
-/// Checks if client age is age or older.
-/datum/game_mode/dynamic/proc/check_age(client/C, age)
- enemy_minimum_age = age
- if(get_remaining_days(C) == 0)
- enemy_minimum_age = initial(enemy_minimum_age)
- return TRUE // Available in 0 days = available right now = player is old enough to play.
- enemy_minimum_age = initial(enemy_minimum_age)
- return FALSE
-
/datum/game_mode/dynamic/make_antag_chance(mob/living/carbon/human/newPlayer)
if (GLOB.dynamic_forced_extended)
return
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
index 3179f32c974..54d038e1447 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
@@ -18,12 +18,10 @@
var/list/mob/candidates = list()
/// List of players that were selected for this rule
var/list/datum/mind/assigned = list()
- /// Preferences flag such as ROLE_WIZARD that need to be turned on for players to be antag
- var/antag_flag = null
+ /// The /datum/role_preference typepath used for this ruleset.
+ var/role_preference = null
/// The antagonist datum that is assigned to the mobs mind on ruleset execution.
var/datum/antagonist/antag_datum = null
- /// The required minimum account age for this ruleset.
- var/minimum_required_age = 7
/// If set, and config flag protect_roles_from_antagonist is false, then the rule will not pick players from these roles.
var/list/protected_roles = list()
/// If set, rule will deny candidates from those roles always.
@@ -55,8 +53,6 @@
var/list/requirements = list(40,30,20,10,10,10,10,10,10,10)
/// Reference to the mode, use this instead of SSticker.mode.
var/datum/game_mode/dynamic/mode = null
- /// If a role is to be considered another for the purpose of banning.
- var/antag_flag_override = null
/// If a ruleset type which is in this list has been executed, then the ruleset will not be executed.
var/list/blocking_rules = list()
/// The minimum amount of players required for the rule to be considered.
@@ -213,19 +209,19 @@
var/client/client = GET_CLIENT(P)
if (!client || !P.mind) // Are they connected?
candidates.Remove(P)
- else if(!mode.check_age(client, minimum_required_age))
+ continue
+
+ if(!client.should_include_for_role(
+ banning_key = initial(antag_datum.banning_key),
+ role_preference_key = role_preference,
+ req_hours = initial(antag_datum.required_living_playtime)
+ ))
candidates.Remove(P)
continue
+
if(P.mind.special_role) // We really don't want to give antag to an antag.
candidates.Remove(P)
- else if(antag_flag_override)
- if(!(antag_flag_override in client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
- candidates.Remove(P)
- continue
- else
- if(!(antag_flag in client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE)))
- candidates.Remove(P)
- continue
+ continue
/// Do your checks if the ruleset is ready to be executed here.
/// Should ignore certain checks if forced is TRUE
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index 375d1163e56..74cf8577ef0 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -9,21 +9,15 @@
if (!P.client || !P.mind || !P.mind.assigned_role) // Are they connected?
candidates.Remove(P)
continue
- if(!mode.check_age(P.client, minimum_required_age))
- candidates.Remove(P)
- continue
- if(antag_flag_override)
- if(!(antag_flag_override in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
- candidates.Remove(P)
- continue
- else
- if(!(antag_flag in P.client.prefs.be_special) || is_banned_from(P.ckey, list(antag_flag, ROLE_SYNDICATE)))
- candidates.Remove(P)
- continue
if (P.mind.assigned_role in restricted_roles) // Does their job allow for it?
candidates.Remove(P)
- continue
- if ((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
+ else if(length(exclusive_roles) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
+ candidates.Remove(P)
+ else if(!P.client.should_include_for_role(
+ banning_key = initial(antag_datum.banning_key),
+ role_preference_key = role_preference,
+ req_hours = initial(antag_datum.required_living_playtime)
+ ))
candidates.Remove(P)
continue
@@ -53,7 +47,7 @@
/datum/dynamic_ruleset/latejoin/execute()
var/mob/M = pick(candidates)
assigned += M.mind
- M.mind.special_role = antag_flag
+ M.mind.special_role = initial(antag_datum.banning_key)
M.mind.add_antag_datum(antag_datum)
return TRUE
@@ -66,7 +60,7 @@
/datum/dynamic_ruleset/latejoin/infiltrator
name = "Syndicate Infiltrator"
antag_datum = /datum/antagonist/traitor
- antag_flag = ROLE_TRAITOR
+ role_preference = /datum/role_preference/antagonist/traitor
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL)
restricted_roles = list(JOB_NAME_AI,JOB_NAME_CYBORG)
required_candidates = 1
@@ -92,8 +86,7 @@
name = "Provocateur"
persistent = TRUE
antag_datum = /datum/antagonist/rev/head
- antag_flag = ROLE_REV_HEAD
- antag_flag_override = ROLE_REV
+ role_preference = /datum/role_preference/antagonist/revolutionary
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_MASTERATARMS) //NSV13 - added MAA
enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER,JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_WARDEN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
@@ -125,7 +118,7 @@
var/mob/M = pick(candidates) // This should contain a single player, but in case.
if(check_eligible(M.mind)) // Didnt die/run off z-level/get implanted since leaving shuttle.
assigned += M.mind
- M.mind.special_role = antag_flag
+ M.mind.special_role = ROLE_REV_HEAD
revolution = new()
var/datum/antagonist/rev/head/new_head = new()
new_head.give_flash = TRUE
@@ -168,7 +161,7 @@
/datum/dynamic_ruleset/latejoin/heretic_smuggler
name = "Heretic Smuggler"
antag_datum = /datum/antagonist/heretic
- antag_flag = ROLE_HERETIC
+ role_preference = /datum/role_preference/antagonist/heretic
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_AI,JOB_NAME_CYBORG)
required_candidates = 1
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 9aa65b5b481..0c6c3579dd2 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -51,17 +51,14 @@
if (!M.client) // Are they connected?
trimmed_list.Remove(M)
continue
- if(!mode.check_age(M.client, minimum_required_age))
+ if(!M.client.should_include_for_role(
+ banning_key = initial(antag_datum.banning_key),
+ role_preference_key = role_preference,
+ poll_ignore_key = role_preference,
+ req_hours = initial(antag_datum.required_living_playtime)
+ ))
trimmed_list.Remove(M)
continue
- if(antag_flag_override)
- if(!(antag_flag_override in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag_override, ROLE_SYNDICATE)))
- trimmed_list.Remove(M)
- continue
- else
- if(!(antag_flag in M.client.prefs.be_special) || is_banned_from(M.ckey, list(antag_flag, ROLE_SYNDICATE)))
- trimmed_list.Remove(M)
- continue
if (M.mind)
if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
trimmed_list.Remove(M)
@@ -114,7 +111,7 @@
message_admins("Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
log_game("DYNAMIC: Polling [possible_volunteers.len] players to apply for the [name] ruleset.")
- candidates = pollGhostCandidates("The mode is looking for volunteers to become [antag_flag] for [name]", antag_flag, SSticker.mode, antag_flag_override ? antag_flag_override : antag_flag, poll_time = 300)
+ candidates = pollGhostCandidates("The mode is looking for volunteers to become [initial(antag_datum.name)] for [name]", initial(antag_datum.banning_key), role_preference, poll_time = 300)
if(!length(candidates))
message_admins("The ruleset [name] received no applications.")
@@ -170,7 +167,7 @@
var/datum/antagonist/new_role = new antag_datum()
setup_role(new_role)
new_character.mind.add_antag_datum(new_role)
- new_character.mind.special_role = antag_flag
+ new_character.mind.special_role = new_role.banning_key
/datum/dynamic_ruleset/midround/from_ghosts/proc/setup_role(datum/antagonist/new_role)
return
@@ -185,7 +182,7 @@
name = "Syndicate Sleeper Agent"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/traitor
- antag_flag = ROLE_TRAITOR
+ role_preference = /datum/role_preference/midround_living/traitor
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_CYBORG, JOB_NAME_AI, "Positronic Brain")
required_candidates = 1
@@ -239,7 +236,7 @@
name = "Malfunctioning AI"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/traitor
- antag_flag = ROLE_MALF
+ role_preference = /datum/role_preference/midround_living/malfunctioning_ai
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_SCIENTIST, JOB_NAME_CHEMIST, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFENGINEER)
exclusive_roles = list(JOB_NAME_AI)
required_enemies = list(4,4,4,4,4,4,2,2,2,0)
@@ -274,7 +271,7 @@
var/mob/living/silicon/ai/M = pick_n_take(candidates)
assigned += M.mind
var/datum/antagonist/traitor/AI = new
- M.mind.special_role = antag_flag
+ M.mind.special_role = "Malf AI"
M.mind.add_antag_datum(AI)
if(prob(ion_announce))
priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", ANNOUNCER_IONSTORM)
@@ -294,7 +291,7 @@
name = "Wizard"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/wizard
- antag_flag = ROLE_WIZARD
+ role_preference = /datum/role_preference/midround_ghost/wizard
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_RESEARCHDIRECTOR) //RD doesn't believe in magic
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
@@ -325,7 +322,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nuclear
name = "Nuclear Assault"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
- antag_flag = ROLE_OPERATIVE
+ role_preference = /datum/role_preference/midround_ghost/nuclear_operative
antag_datum = /datum/antagonist/nukeop
enemy_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(3,3,3,3,3,2,1,1,0,0)
@@ -351,8 +348,8 @@
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index)
- new_character.mind.special_role = "Nuclear Operative"
- new_character.mind.assigned_role = "Nuclear Operative"
+ new_character.mind.special_role = ROLE_OPERATIVE
+ new_character.mind.assigned_role = ROLE_OPERATIVE
if (index == 1) // Our first guy is the leader
var/datum/antagonist/nukeop/leader/new_role = new
nuke_team = new_role.nuke_team
@@ -370,7 +367,7 @@
name = "Blob"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/blob
- antag_flag = ROLE_BLOB
+ role_preference = /datum/role_preference/midround_ghost/blob
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
@@ -394,7 +391,7 @@
name = "Alien Infestation"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
antag_datum = /datum/antagonist/xeno
- antag_flag = ROLE_ALIEN
+ role_preference = /datum/role_preference/midround_ghost/xenomorph
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,2,1,1,1,1,0,0,0)
required_candidates = 1
@@ -442,8 +439,7 @@
name = "Nightmare"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/nightmare
- antag_flag = "Nightmare"
- antag_flag_override = ROLE_ALIEN
+ role_preference = /datum/role_preference/midround_ghost/space_dragon
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
@@ -490,8 +486,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/abductors
name = "Abductors"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
- antag_flag = "Abductor"
- antag_flag_override = ROLE_ABDUCTOR
+ role_preference = /datum/role_preference/midround_ghost/abductor
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 2
@@ -529,8 +524,7 @@
name = "Revenant"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/revenant
- antag_flag = "Revenant"
- antag_flag_override = ROLE_REVENANT
+ role_preference = /datum/role_preference/midround_ghost/revenant
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
@@ -581,7 +575,7 @@
/datum/dynamic_ruleset/midround/pirates
name = "Space Pirates"
midround_ruleset_style = MIDROUND_RULESET_STYLE_HEAVY
- antag_flag = "Space Pirates"
+ role_preference = /datum/role_preference/midround_ghost/space_pirate
required_type = /mob/dead/observer
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
@@ -608,7 +602,7 @@
name = "Obsessed"
midround_ruleset_style = MIDROUND_RULESET_STYLE_LIGHT
antag_datum = /datum/antagonist/obsessed
- antag_flag = ROLE_OBSESSED
+ role_preference = /datum/role_preference/midround_living/obsessed
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, "Positronic Brain")
enemy_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_DETECTIVE, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
@@ -625,7 +619,6 @@
!candidate.getorgan(/obj/item/organ/brain) \
|| candidate.mind.has_antag_datum(/datum/antagonist/obsessed) \
|| candidate.stat == DEAD \
- || !(ROLE_OBSESSED in candidate.client?.prefs?.be_special) \
|| !SSjob.GetJob(candidate.mind.assigned_role) \
|| (candidate.mind.assigned_role in GLOB.nonhuman_positions) \
)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index 569319b8cf8..fe384067216 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -7,9 +7,8 @@
/datum/dynamic_ruleset/roundstart/traitor
name = "Traitors"
- antag_flag = ROLE_TRAITOR
+ role_preference = /datum/role_preference/antagonist/traitor
antag_datum = /datum/antagonist/traitor
- minimum_required_age = 0
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_CYBORG)
required_candidates = 1
@@ -41,8 +40,8 @@
/datum/dynamic_ruleset/roundstart/traitorbro
name = "Blood Brothers"
- antag_flag = ROLE_BROTHER
- antag_datum = /datum/antagonist/brother/
+ role_preference = /datum/role_preference/antagonist/blood_brother
+ antag_datum = /datum/antagonist/brother
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG)
required_candidates = 2
@@ -66,7 +65,7 @@
var/mob/bro = pick_n_take(candidates)
assigned += bro.mind
team.add_member(bro.mind)
- bro.mind.special_role = "brother"
+ bro.mind.special_role = ROLE_BROTHER
bro.mind.restricted_roles = restricted_roles
pre_brother_teams += team
return TRUE
@@ -89,7 +88,7 @@
/datum/dynamic_ruleset/roundstart/changeling
name = "Changelings"
- antag_flag = ROLE_CHANGELING
+ role_preference = /datum/role_preference/antagonist/changeling
antag_datum = /datum/antagonist/changeling
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG)
@@ -126,7 +125,7 @@
/datum/dynamic_ruleset/roundstart/heretics
name = "Heretics"
- antag_flag = ROLE_HERETIC
+ role_preference = /datum/role_preference/antagonist/heretic
antag_datum = /datum/antagonist/heretic
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG)
@@ -170,10 +169,9 @@
// Dynamic is a wonderful thing that adds wizards to every round and then adds even more wizards during the round.
/datum/dynamic_ruleset/roundstart/wizard
name = "Wizard"
- antag_flag = ROLE_WIZARD
+ role_preference = /datum/role_preference/antagonist/wizard
antag_datum = /datum/antagonist/wizard
flags = HIGH_IMPACT_RULESET | NO_OTHER_ROUNDSTARTS_RULESET
- minimum_required_age = 14
restricted_roles = list(JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted
required_candidates = 1
weight = 2
@@ -214,9 +212,8 @@
/datum/dynamic_ruleset/roundstart/bloodcult
name = "Blood Cult"
- antag_flag = ROLE_CULTIST
+ role_preference = /datum/role_preference/antagonist/blood_cultist
antag_datum = /datum/antagonist/cult
- minimum_required_age = 14
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL)
required_candidates = 2
weight = 3
@@ -269,10 +266,9 @@
/datum/dynamic_ruleset/roundstart/nuclear
name = "Nuclear Emergency"
- antag_flag = ROLE_OPERATIVE
+ role_preference = /datum/role_preference/antagonist/nuclear_operative
antag_datum = /datum/antagonist/nukeop
var/datum/antagonist/antag_leader_datum = /datum/antagonist/nukeop/leader
- minimum_required_age = 14
restricted_roles = list(JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN) // Just to be sure that a nukie getting picked won't ever imply a Captain or HoS not getting drafted
required_candidates = 5
weight = 3
@@ -295,8 +291,8 @@
break
var/mob/M = pick_n_take(candidates)
assigned += M.mind
- M.mind.assigned_role = "Nuclear Operative"
- M.mind.special_role = "Nuclear Operative"
+ M.mind.assigned_role = ROLE_OPERATIVE
+ M.mind.special_role = ROLE_OPERATIVE
return TRUE
/datum/dynamic_ruleset/roundstart/nuclear/execute()
@@ -354,10 +350,8 @@
/datum/dynamic_ruleset/roundstart/revs
name = "Revolution"
persistent = TRUE
- antag_flag = ROLE_REV_HEAD
- antag_flag_override = ROLE_REV
+ role_preference = /datum/role_preference/antagonist/revolutionary
antag_datum = /datum/antagonist/rev/head
- minimum_required_age = 14
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFENGINEER, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_MASTERATARMS) //NSV13 - added MAA
required_candidates = 3
weight = 3
@@ -383,7 +377,7 @@
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.restricted_roles = restricted_roles
- M.mind.special_role = antag_flag
+ M.mind.special_role = ROLE_REV_HEAD
return TRUE
/datum/dynamic_ruleset/roundstart/revs/execute()
@@ -437,7 +431,6 @@
/datum/dynamic_ruleset/roundstart/extended
name = "Extended"
- antag_flag = null
antag_datum = null
restricted_roles = list()
required_candidates = 0
@@ -486,7 +479,7 @@
/datum/dynamic_ruleset/roundstart/devil
name = "Devil"
- antag_flag = ROLE_DEVIL
+ role_preference = /datum/role_preference/antagonist/devil
antag_datum = /datum/antagonist/devil
restricted_roles = list(JOB_NAME_LAWYER, JOB_NAME_CURATOR, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE)
required_candidates = 1
@@ -574,7 +567,7 @@
/datum/dynamic_ruleset/roundstart/clockcult
name = "Clockwork Cult"
- antag_flag = ROLE_SERVANT_OF_RATVAR
+ role_preference = /datum/role_preference/antagonist/clock_cultist
antag_datum = /datum/antagonist/servant_of_ratvar
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG, JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_CHAPLAIN, JOB_NAME_HEADOFPERSONNEL)
required_candidates = 4
@@ -636,7 +629,7 @@
/datum/dynamic_ruleset/roundstart/incursion
name = "Incursion"
- antag_flag = ROLE_INCURSION
+ role_preference = /datum/role_preference/antagonist/incursionist
antag_datum = /datum/antagonist/incursion
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER)
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG)
@@ -688,7 +681,7 @@
/datum/dynamic_ruleset/roundstart/hivemind
name = "Assimilation"
- antag_flag = ROLE_HIVE
+ role_preference = /datum/role_preference/antagonist/hivemind_host
antag_datum = /datum/antagonist/hivemind
protected_roles = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_roles = list(JOB_NAME_AI, JOB_NAME_CYBORG)
diff --git a/code/game/gamemodes/dynamic/dynamic_simulations.dm b/code/game/gamemodes/dynamic/dynamic_simulations.dm
index 4b48955ce75..acba5d1cc42 100644
--- a/code/game/gamemodes/dynamic/dynamic_simulations.dm
+++ b/code/game/gamemodes/dynamic/dynamic_simulations.dm
@@ -29,11 +29,6 @@
var/datum/client_interface/mock_client = new
var/datum/preferences/prefs = new
- var/list/be_special = list()
- for (var/special_role in GLOB.special_roles)
- be_special += special_role
-
- prefs.be_special = be_special
mock_client.prefs = prefs
mock_new_player.mock_client = mock_client
diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md
index 7faa5c2d870..9af317301d8 100644
--- a/code/game/gamemodes/dynamic/readme.md
+++ b/code/game/gamemodes/dynamic/readme.md
@@ -144,7 +144,6 @@ Rulesets have the following variables notable to developers and those interested
- Traitor: `antag_cap = list("denominator" = 24)`. This means that for every 24 players, 1 traitor will be added (assuming no scaling).
- Nuclear Emergency: `antag_cap = list("denominator" = 18, "offset" = 1)`. For every 18 players, 1 nuke op will be added. Starts at 1, meaning at 30 players, 3 nuke ops will be created, rather than 2.
- Revolution: `antag_cap = 3`. There will always be 3 rev-heads, no matter what.
-- `minimum_required_age` - The minimum age in order to apply for the ruleset.
- `weight` - How likely this ruleset is to be picked. A higher weight results in a higher chance of drafting.
- `cost` - The initial cost of the ruleset. This cost is taken from either the roundstart or midround budget, depending on the ruleset.
- `scaling_cost` - Cost for every *additional* application of this ruleset.
diff --git a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm
index 307b1be0893..c169e11ae73 100644
--- a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm
+++ b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm
@@ -2,7 +2,8 @@
name = "heresy"
config_tag = "heresy"
report_type = "heresy"
- antag_flag = ROLE_HERETIC
+ role_preference = /datum/role_preference/antagonist/heretic
+ antag_datum = /datum/antagonist/heretic
false_report_weight = 5
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG)
@@ -10,7 +11,6 @@
required_enemies = 1
recommended_enemies = 4
reroll_friendly = 1
- enemy_minimum_age = 0
allowed_special = list(/datum/special_role/traitor/higher_chance)
@@ -44,7 +44,7 @@
for(var/i in 1 to num_ecult)
if(!antag_candidates.len)
break
- var/datum/mind/cultie = antag_pick(antag_candidates, ROLE_HERETIC)
+ var/datum/mind/cultie = antag_pick(antag_candidates, /datum/role_preference/antagonist/heretic)
antag_candidates -= cultie
cultie.special_role = ROLE_HERETIC
cultie.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 03300874083..80576b5813e 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -28,13 +28,15 @@
var/maximum_players = -1 // -1 is no maximum, positive numbers limit the selection of a mode on overstaffed stations
var/required_enemies = 0
var/recommended_enemies = 0
- var/antag_flag = null //preferences flag such as BE_WIZARD that need to be turned on for players to be antag
+ /// The role preference used to poll players.
+ var/role_preference
+ /// The antag datum typepath primarily spawned by this gamemode. Used for checking the banning key and required playtime.
+ var/datum/antagonist/antag_datum
var/mob/living/living_antag_player = null
var/datum/game_mode/replacementmode = null
var/round_converted = 0 //0: round not converted, 1: round going to convert, 2: round converted
var/reroll_friendly //During mode conversion only these are in the running
var/continuous_sanity_checked //Catches some cases where config options could be used to suggest that modes without antagonists should end when all antagonists die
- var/enemy_minimum_age = 7 //How many days must players have been playing before they can play this antagonist
var/list/allowed_special = list() //Special roles that can spawn (Add things like /datum/antagonist/special/undercover for them to be able to spawn during this gamemode)
var/list/active_specials = list() //Special roles that have spawned, and can now spawn late
@@ -87,7 +89,7 @@
return 1
/datum/game_mode/proc/setup_antag_candidates()
- antag_candidates = get_players_for_role(antag_flag)
+ antag_candidates = get_players_for_role(antag_datum, role_preference)
///Attempts to select players for special roles the mode might have.
/datum/game_mode/proc/pre_setup()
@@ -121,11 +123,11 @@
if(candidates.len == 0)
return //No more candidates, end the selection process, and active specials at this time will be handled by latejoins or not included
var/mob/person
- if(special.special_role_flag)
- person = antag_pick(candidates, special.special_role_flag)
+ if(special.use_antag_rep)
+ person = antag_pick(candidates, special.preference_type)
else
person = pick_n_take(candidates)
- if(is_banned_from(person.ckey, special.preference_type))
+ if(is_banned_from(person.ckey, special.banning_key))
continue
if(!person)
continue
@@ -205,7 +207,7 @@
continue
if(!is_special_type(M, subantag.attached_antag_datum))
continue
- if(is_banned_from(M.ckey, list(subantag.preference_type)))
+ if(is_banned_from(M.ckey, subantag.banning_key))
continue
count++
if(count >= subantag.max_amount)
@@ -256,7 +258,7 @@
var/list/antag_candidates = list()
for(var/mob/living/carbon/human/H in living_crew)
- if(H.client && (H.client.prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG) && !is_centcom_level(H.z))
+ if(H.client && !is_centcom_level(H.z))
antag_candidates += H
if(!antag_candidates)
@@ -429,7 +431,9 @@
// The odds become:
// Player A: 150 / 250 = 0.6 = 60%
// Player B: 100 / 250 = 0.4 = 40%
-/datum/game_mode/proc/antag_pick(list/datum/candidates, role)
+/// The role_preference argument is optional, but candidates will not use their PERSONAL antag rep if the preference is disabled, rather only using the "base" antag rep.
+/// This is mainly used in the situation where someone is drafted for a ruleset despite having the preference disabled (a feature of gamemodes) - we don't want to spend their rep.
+/datum/game_mode/proc/antag_pick(list/datum/candidates, role_preference = null)
if(!CONFIG_GET(flag/use_antag_rep)) // || candidates.len <= 1)
return pick(candidates)
@@ -452,7 +456,10 @@
if(!player)
candidates -= mind
continue
- total_tickets += min(((role in player.client.prefs.be_special) ? SSpersistence.antag_rep[p_ckey] : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
+ var/role_enabled = TRUE
+ if(role_preference && player.client)
+ role_enabled = player.client.role_preference_enabled(role_preference)
+ total_tickets += min((role_enabled ? SSpersistence.antag_rep[p_ckey] : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
var/antag_select = rand(1,total_tickets)
var/current = 1
@@ -461,9 +468,11 @@
p_ckey = ckey(mind.key)
var/mob/dead/new_player/player = get_mob_by_ckey(p_ckey)
p_rep = SSpersistence.antag_rep[p_ckey]
-
+ var/role_enabled = TRUE
+ if(role_preference && player.client)
+ role_enabled = player.client.role_preference_enabled(role_preference)
var/previous = current
- var/spend = min(((role in player.client.prefs.be_special) ? p_rep : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
+ var/spend = min((role_enabled ? p_rep : 0) + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
current += spend
if(antag_select >= previous && antag_select <= (current-1))
@@ -474,7 +483,9 @@
WARNING("Something has gone terribly wrong. /datum/game_mode/proc/antag_pick failed to select a candidate. Falling back to pick()")
return pick(candidates)
-/datum/game_mode/proc/get_players_for_role(role)
+/datum/game_mode/proc/get_players_for_role(datum/antagonist/antag_datum, role_preference = null)
+ var/banning_key = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.banning_key) : null
+ var/req_hours = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.required_living_playtime) : 0
var/list/players = list()
var/list/candidates = list()
var/list/drafted = list()
@@ -490,11 +501,13 @@
players = shuffle(players)
for(var/mob/dead/new_player/player in players)
- if(player.client && player.ready == PLAYER_READY_TO_PLAY)
- if(role in player.client.prefs.be_special)
- if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player))
- if(age_check(player.client)) //Must be older than the minimum age
- candidates += player.mind // Get a list of all the people who want to be the antagonist for this round
+ if(!QDELETED(player) && player.client && player.ready == PLAYER_READY_TO_PLAY)
+ if(player.client.should_include_for_role(
+ banning_key = banning_key,
+ role_preference_key = role_preference,
+ req_hours = req_hours
+ ))
+ candidates += player.mind // Get a list of all the people who want to be the antagonist for this round
if(restricted_jobs)
for(var/datum/mind/player in candidates)
@@ -504,10 +517,14 @@
if(candidates.len < recommended_enemies)
for(var/mob/dead/new_player/player in players)
- if(player.client && player.ready == PLAYER_READY_TO_PLAY)
- if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one
- if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player))
- drafted += player.mind
+ if(!QDELETED(player) && player.client && player.ready == PLAYER_READY_TO_PLAY)
+ // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one but are otherwise eligible
+ if(player.client.should_include_for_role(
+ banning_key = banning_key,
+ role_preference_key = null,
+ req_hours = req_hours
+ ) && !player.client.role_preference_enabled(role_preference))
+ drafted += player.mind
if(restricted_jobs)
for(var/datum/mind/player in drafted) // Remove people who can't be an antagonist
@@ -550,15 +567,15 @@
// Less if there are not enough valid players in the game entirely to make recommended_enemies.
-/datum/game_mode/proc/get_alive_non_antagonsist_players_for_role(role, list/restricted_roles)
+/datum/game_mode/proc/get_alive_non_antagonsist_players_for_role(datum/antagonist/antag_datum, role_preference, list/restricted_roles)
+ var/banning_key = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.banning_key) : null
+ var/req_hours = ispath(antag_datum, /datum/antagonist) ? initial(antag_datum.required_living_playtime) : 0
var/list/candidates = list()
for(var/mob/living/carbon/human/player in GLOB.player_list)
- if(player.client && is_station_level(player.z))
- if(role in player.client.prefs.be_special)
- if(!is_banned_from(player.ckey, list(role, ROLE_SYNDICATE)) && !QDELETED(player))
- if(age_check(player.client) && !player.mind.special_role) //Must be older than the minimum age
- candidates += player.mind // Get a list of all the people who want to be the antagonist for this round
+ if(!QDELETED(player) && player.client && is_station_level(player.z) && !player.mind.special_role)
+ if(player.client.should_include_for_role(banning_key = banning_key, role_preference_key = role_preference, req_hours = req_hours))
+ candidates += player.mind // Get a list of all the people who want to be the antagonist for this round // Get a list of all the people who want to be the antagonist for this round
var/restricted_list = length(restricted_roles) ? restricted_roles : restricted_jobs
if(restricted_list)
@@ -728,25 +745,6 @@
for (var/C in GLOB.admins)
to_chat(C, msg.Join())
-//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
-/datum/game_mode/proc/age_check(client/C)
- if(get_remaining_days(C) == 0)
- return 1 //Available in 0 days = available right now = player is old enough to play.
- return 0
-
-
-/datum/game_mode/proc/get_remaining_days(client/C)
- if(!C)
- return 0
- if(!CONFIG_GET(flag/use_age_restriction_for_jobs))
- return 0
- if(!isnum_safe(C.player_age))
- return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced
- if(!isnum_safe(enemy_minimum_age))
- return 0
-
- return max(0, enemy_minimum_age - C.player_age)
-
/datum/game_mode/proc/remove_antag_for_borging(datum/mind/newborgie)
SSticker.mode.remove_cultist(newborgie, 0, 0)
remove_servant_of_ratvar(newborgie)
diff --git a/code/game/gamemodes/gangs/gangs.dm b/code/game/gamemodes/gangs/gangs.dm
index 7d1187ef1d9..f0e9536cdae 100644
--- a/code/game/gamemodes/gangs/gangs.dm
+++ b/code/game/gamemodes/gangs/gangs.dm
@@ -5,12 +5,12 @@ GLOBAL_LIST_EMPTY(gangs)
/datum/game_mode/gang
name = "gang war"
config_tag = "gang"
- antag_flag = ROLE_GANG
+ role_preference = /datum/role_preference/antagonist/gangster
+ antag_datum = /datum/antagonist/gang
restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY)
required_players = 15 //NSV13 - down from 30
required_enemies = 1 //NSV13 - down from 2
recommended_enemies = 2 //NSV13 - down from 3
- enemy_minimum_age = 14
announce_span = "danger"
announce_text = "A violent turf war has erupted on the station!\n\
diff --git a/code/game/gamemodes/hivemind/hivemind.dm b/code/game/gamemodes/hivemind/hivemind.dm
index 4fa0b6095c0..90bf74f93cc 100644
--- a/code/game/gamemodes/hivemind/hivemind.dm
+++ b/code/game/gamemodes/hivemind/hivemind.dm
@@ -4,7 +4,8 @@ GLOBAL_LIST_EMPTY(hivehosts)
name = "assimilation"
config_tag = "hivemind"
report_type = "hivemind"
- antag_flag = ROLE_HIVE
+ role_preference = /datum/role_preference/antagonist/hivemind_host
+ antag_datum = /datum/antagonist/hivemind
false_report_weight = 5
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG)
@@ -12,7 +13,6 @@ GLOBAL_LIST_EMPTY(hivehosts)
required_enemies = 3
recommended_enemies = 3
reroll_friendly = 1
- enemy_minimum_age = 0
announce_span = "danger"
announce_text = "The hosts of several psionic hiveminds have infiltrated the station and are looking to assimilate the crew!\n\
@@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(hivehosts)
for(var/j = 0, j < num_hosts, j++)
if (!antag_candidates.len)
break
- var/datum/mind/host = antag_pick(antag_candidates, ROLE_HIVE)
+ var/datum/mind/host = antag_pick(antag_candidates, /datum/role_preference/antagonist/hivemind_host)
hosts += host
host.special_role = ROLE_HIVE
host.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/incursion/incursion.dm b/code/game/gamemodes/incursion/incursion.dm
index adf8979f516..c5324e52111 100644
--- a/code/game/gamemodes/incursion/incursion.dm
+++ b/code/game/gamemodes/incursion/incursion.dm
@@ -7,9 +7,9 @@
config_tag = "incursion"
restricted_jobs = list(JOB_NAME_AI, JOB_NAME_CYBORG)
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_MASTERATARMS) //NSV13 - added MAA
- antag_flag = ROLE_INCURSION
+ role_preference = /datum/role_preference/antagonist/incursionist
+ antag_datum = /datum/antagonist/incursion
false_report_weight = 10
- enemy_minimum_age = 0
announce_span = "danger"
announce_text = "A large force of syndicate operatives have infiltrated the ranks of the station and wish to take it by force!\n\
@@ -30,8 +30,6 @@
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
restricted_jobs += JOB_NAME_ASSISTANT
- var/list/datum/mind/possible_traitors = get_players_for_role(ROLE_INCURSION)
-
var/datum/team/incursion/team = new
var/cost_base = CONFIG_GET(number/incursion_cost_base)
var/cost_increment = CONFIG_GET(number/incursion_cost_increment)
@@ -41,11 +39,10 @@
team_size = CLAMP(team_size, CONFIG_GET(number/incursion_count_min), CONFIG_GET(number/incursion_count_max))
for(var/k = 1 to team_size)
- var/datum/mind/incursion = antag_pick(possible_traitors, ROLE_INCURSION)
+ var/datum/mind/incursion = antag_pick(antag_candidates, /datum/role_preference/antagonist/incursionist)
if(!incursion)
message_admins("Ran out of people to put in an incursion team, wanted [team_size] but only got [k-1]")
break
- possible_traitors -= incursion
antag_candidates -= incursion
team.add_member(incursion)
incursion.special_role = ROLE_INCURSION
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 281f3507fe3..9d2880be163 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -8,8 +8,8 @@
required_players = 30 // 30 players - 3 players to be the nuke ops = 27 players remaining
required_enemies = 2
recommended_enemies = 5
- antag_flag = ROLE_OPERATIVE
- enemy_minimum_age = 14
+ role_preference = /datum/role_preference/antagonist/nuclear_operative
+ antag_datum = /datum/antagonist/nukeop
announce_span = "danger"
announce_text = "Syndicate forces are approaching the station in an attempt to destroy it!\n\
@@ -31,7 +31,7 @@
var/n_agents = min(round(num_players() / 10), antag_candidates.len, agents_possible)
if(n_agents >= required_enemies)
for(var/i = 0, i < n_agents, ++i)
- var/datum/mind/new_op = antag_pick(antag_candidates, ROLE_OPERATIVE)
+ var/datum/mind/new_op = antag_pick(antag_candidates, /datum/role_preference/antagonist/nuclear_operative)
pre_nukeops += new_op
new_op.assigned_role = "Nuclear Operative"
new_op.special_role = "Nuclear Operative"
@@ -160,7 +160,7 @@
E.implant(H)
var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(H)
W.implant(H)
- H.faction |= ROLE_SYNDICATE
+ H.faction |= FACTION_SYNDICATE
H.update_icons()
/datum/outfit/syndicate/full
diff --git a/code/game/gamemodes/overthrow/overthrow.dm b/code/game/gamemodes/overthrow/overthrow.dm
index 0783c862f5e..9fd0cd3863e 100644
--- a/code/game/gamemodes/overthrow/overthrow.dm
+++ b/code/game/gamemodes/overthrow/overthrow.dm
@@ -3,7 +3,8 @@
name = "overthrow"
config_tag = "overthrow"
report_type = "overthrow"
- antag_flag = ROLE_OVERTHROW
+ role_preference = /datum/role_preference/antagonist/traitor // use traitor role pref
+ antag_datum = /datum/antagonist/overthrow
restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_MASTERATARMS) //NSV13 - added MAA
required_players = 20 // the core idea is of a swift, bloodless coup, so it shouldn't be as chaotic as revs.
required_enemies = 2 // minimum two teams, otherwise it's just nerfed revs.
@@ -28,7 +29,7 @@
for (var/i in 1 to sleeping_agents)
if (!antag_candidates.len)
break
- var/datum/mind/sleeping_agent = antag_pick(antag_candidates, ROLE_OVERTHROW)
+ var/datum/mind/sleeping_agent = antag_pick(antag_candidates, /datum/role_preference/antagonist/traitor)
antag_candidates -= sleeping_agent
initial_agents += sleeping_agent
sleeping_agent.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 0ff58d3c82b..a635784064a 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -11,14 +11,14 @@
name = "revolution"
config_tag = "revolution"
report_type = "revolution"
- antag_flag = ROLE_REV
+ role_preference = /datum/role_preference/antagonist/revolutionary
+ antag_datum = /datum/antagonist/rev/head
false_report_weight = 10
restricted_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_AI, JOB_NAME_CYBORG,JOB_NAME_CAPTAIN, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_HEADOFSECURITY, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_MASTERATARMS) //NSV13 - added MAA
required_jobs = list(list(JOB_NAME_CAPTAIN=1),list(JOB_NAME_HEADOFPERSONNEL=1),list(JOB_NAME_HEADOFSECURITY=1),list(JOB_NAME_CHIEFENGINEER=1),list(JOB_NAME_RESEARCHDIRECTOR=1),list(JOB_NAME_CHIEFMEDICALOFFICER=1),list(JOB_NAME_MASTERATARMS=1)) //Any head present //NSV13 - added MAA
required_players = 30
required_enemies = 2
recommended_enemies = 3
- enemy_minimum_age = 14
title_icon = "revolution"
announce_span = "danger"
@@ -55,7 +55,7 @@
for (var/i=1 to max_headrevs)
if (antag_candidates.len==0)
break
- var/datum/mind/lenin = antag_pick(antag_candidates, ROLE_REV_HEAD)
+ var/datum/mind/lenin = antag_pick(antag_candidates, /datum/role_preference/antagonist/revolutionary)
antag_candidates -= lenin
headrev_candidates += lenin
lenin.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm
index 7e3db6e86c4..b5b6f9bbecc 100644
--- a/code/game/gamemodes/traitor/double_agents.dm
+++ b/code/game/gamemodes/traitor/double_agents.dm
@@ -12,11 +12,11 @@
recommended_enemies = 8
reroll_friendly = 0
traitor_name = "Nanotrasen Internal Affairs Agent"
- antag_flag = ROLE_INTERNAL_AFFAIRS
+ antag_datum = /datum/antagonist/traitor/internal_affairs
+ role_preference = /datum/role_preference/antagonist/internal_affairs
traitors_possible = 10 //hard limit on traitors if scaling is turned off
num_modifier = 4 // Four additional traitors
- antag_datum = /datum/antagonist/traitor/internal_affairs
announce_text = "There are Nanotrasen Internal Affairs Agents trying to kill each other!\n\
IAA : Eliminate your targets and protect yourself!\n\
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index b87f81f8570..30a6cea5124 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -9,7 +9,8 @@
name = "traitor"
config_tag = "traitor"
report_type = "traitor"
- antag_flag = ROLE_TRAITOR
+ role_preference = /datum/role_preference/antagonist/traitor
+ antag_datum = /datum/antagonist/traitor
false_report_weight = 20 //Reports of traitors are pretty common.
restricted_jobs = list(JOB_NAME_CYBORG)//They are part of the AI if he is traitor so are they, they use to get double chances
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_PILOT, JOB_NAME_MASTERATARMS) //NSV13 added pilots and master at arms
@@ -17,7 +18,6 @@
required_enemies = 1
recommended_enemies = 4
reroll_friendly = 1
- enemy_minimum_age = 0
announce_span = "danger"
announce_text = "There are Syndicate agents on the station!\n\
@@ -29,7 +29,6 @@
var/list/datum/mind/pre_traitors = list()
var/traitors_possible = 4 //hard limit on traitors if scaling is turned off
var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
- var/antag_datum = /datum/antagonist/traitor //what type of antag to create
var/traitors_required = TRUE //Will allow no traitors
@@ -55,7 +54,7 @@
for(var/j = 0, j < num_traitors, j++)
if (!antag_candidates.len)
break
- var/datum/mind/traitor = antag_pick(antag_candidates, ROLE_TRAITOR)
+ var/datum/mind/traitor = antag_pick(antag_candidates, /datum/role_preference/antagonist/traitor)
pre_traitors += traitor
traitor.special_role = traitor_name
traitor.restricted_roles = restricted_jobs
@@ -90,11 +89,13 @@
if((SSticker.mode.traitors.len + pre_traitors.len) >= traitorcap) //Upper cap for number of latejoin antagonists
return
if((SSticker.mode.traitors.len + pre_traitors.len) <= (traitorcap - 2) || prob(100 / (tsc * 2)))
- if(antag_flag in character.client.prefs.be_special)
- if(!is_banned_from(character.ckey, list(ROLE_TRAITOR, ROLE_SYNDICATE)) && !QDELETED(character))
- if(age_check(character.client))
- if(!(character.job in restricted_jobs))
- add_latejoin_traitor(character.mind)
+ if(!QDELETED(character) && character.client?.should_include_for_role(
+ banning_key = initial(antag_datum.banning_key),
+ role_preference_key = role_preference,
+ req_hours = initial(antag_datum.required_living_playtime),
+ ))
+ if(!(character.job in restricted_jobs))
+ add_latejoin_traitor(character.mind)
/datum/game_mode/traitor/proc/add_latejoin_traitor(datum/mind/character)
var/datum/antagonist/traitor/new_antag = new antag_datum()
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index f72b5041322..f58b8b6c09d 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -6,12 +6,12 @@
name = "wizard"
config_tag = "wizard"
report_type = "wizard"
- antag_flag = ROLE_WIZARD
+ role_preference = /datum/role_preference/antagonist/wizard
+ antag_datum = /datum/antagonist/wizard
false_report_weight = 10
required_players = 20
required_enemies = 1
recommended_enemies = 1
- enemy_minimum_age = 14
round_ends_with_antag_death = 1
announce_span = "danger"
announce_text = "There is a space wizard attacking the station!\n\
@@ -22,7 +22,7 @@
title_icon = "wizard"
/datum/game_mode/wizard/pre_setup()
- var/datum/mind/wizard = antag_pick(antag_candidates, ROLE_WIZARD)
+ var/datum/mind/wizard = antag_pick(antag_candidates, /datum/role_preference/antagonist/wizard)
wizards += wizard
wizard.assigned_role = ROLE_WIZARD
wizard.special_role = ROLE_WIZARD
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index f3e600f570d..f3b490f4993 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -297,7 +297,7 @@
/obj/machinery/clonepod/proc/offer_to_ghost(mob/living/carbon/H)
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", ROLE_EXPERIMENTAL_CLONE, null, null, 300, H, POLL_IGNORE_EXPERIMENTAL_CLONE)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [H.real_name]'s experimental clone?", ROLE_EXPERIMENTAL_CLONE, null, 30 SECONDS, H)
if(length(candidates))
var/mob/dead/observer/C = pick(candidates)
H.key = C.key
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 9830d6e5454..e60327d47be 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -424,7 +424,7 @@
if(iscyborg(sillycone))
var/mob/living/silicon/robot/sillyconerobot = A
- if((ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE)
+ if((FACTION_SYNDICATE in faction) && sillyconerobot.emagged == TRUE)
continue
else if(iscarbon(A))
@@ -686,7 +686,7 @@
stun_projectile_sound = 'sound/weapons/gunshot.ogg'
icon_state = "syndie_off"
base_icon_state = "syndie"
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
desc = "A ballistic machine gun auto-turret."
/obj/machinery/porta_turret/syndicate/ComponentInitialize()
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 68dcf131774..0a932d2a8b0 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -329,7 +329,7 @@
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
S.Evolve()
S.flavor_text = FLAVOR_TEXT_EVIL
- S.set_playable()
+ S.set_playable(ROLE_PYRO_SLIME)
/////////////////////
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index 36845c8446d..3779a8f4450 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -518,7 +518,7 @@
possessed = TRUE
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_SPECTRAL_BLADE, null, 10 SECONDS, ignore_category = POLL_IGNORE_SPECTRAL_BLADE)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index d3156ca1370..244549a5f29 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -18,6 +18,7 @@
Estimated time of last contact: Deployment, 5000 millennia ago."
assignedrole = "Lifebringer"
use_cooldown = TRUE
+ banType = ROLE_LIFEBRINGER
/obj/effect/mob_spawn/human/seed_vault/special(mob/living/new_spawn)
var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \
@@ -54,6 +55,7 @@
assignedrole = "Ash Walker"
var/datum/team/ashwalkers/team
use_cooldown = TRUE
+ banType = ROLE_ASHWALKER
/obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn)
to_chat(new_spawn, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Don't leave your nest undefended, protect it with your life. Glory to the Necropolis! ")
@@ -78,40 +80,6 @@
head = /obj/item/clothing/head/helmet/gladiator
uniform = /obj/item/clothing/under/costume/gladiator/ash_walker
-
-//Timeless prisons: Spawns in Wish Granter prisons in lavaland. Ghosts become age-old users of the Wish Granter and are advised to seek repentance for their past.
-/obj/effect/mob_spawn/human/exile
- name = "timeless prison"
- desc = "Although this stasis pod looks medicinal, it seems as though it's meant to preserve something for a very long time."
- mob_name = "a penitent exile"
- icon = 'icons/obj/machines/sleeper.dmi'
- icon_state = "sleeper"
- roundstart = FALSE
- death = FALSE
- mob_species = /datum/species/shadow
- short_desc = "You are cursed."
- flavour_text = "Years ago, you sacrificed the lives of your trusted friends and the humanity of yourself to reach the Wish Granter. Though you \
- did so, it has come at a cost: your very body rejects the light, dooming you to wander endlessly in this horrible wasteland."
- assignedrole = "Exile"
- use_cooldown = TRUE
-
-/obj/effect/mob_spawn/human/exile/Destroy()
- new/obj/structure/fluff/empty_sleeper(get_turf(src))
- return ..()
-
-/obj/effect/mob_spawn/human/exile/special(mob/living/new_spawn)
- new_spawn.fully_replace_character_name(null,"Wish Granter's Victim ([rand(1,999)])")
- var/wish = rand(1,4)
- switch(wish)
- if(1)
- to_chat(new_spawn, "You wished to kill, and kill you did. You've lost track of how many, but the spark of excitement that murder once held has winked out. You feel only regret. ")
- if(2)
- to_chat(new_spawn, "You wished for unending wealth, but no amount of money was worth this existence. Maybe charity might redeem your soul? ")
- if(3)
- to_chat(new_spawn, "You wished for power. Little good it did you, cast out of the light. You are the [gender == MALE ? "king" : "queen"] of a hell that holds no subjects. You feel only remorse. ")
- if(4)
- to_chat(new_spawn, "You wished for immortality, even as your friends lay dying behind you. No matter how many times you cast yourself into the lava, you awaken in this room again within a few days. There is no escape. ")
-
//Golem shells: Spawns in Free Golem ships in lavaland. Ghosts become mineral golems and are advised to spread personal freedom.
/obj/effect/mob_spawn/human/golem
name = "inert free golem shell"
@@ -132,6 +100,7 @@
flavour_text = "In his infinite and divine wisdom, he set your clan free to \
travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \
golems, so that no golem may ever be forced to serve again."
+ banType = ROLE_FREE_GOLEM
/obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null)
if(species) //spawners list uses object name to register so this goes before ..()
@@ -206,6 +175,7 @@
can_transfer = FALSE
mob_species = /datum/species/golem/adamantine
use_cooldown = TRUE //Only the roundstart free golems are
+ banType = ROLE_FREE_GOLEM
//Malfunctioning cryostasis sleepers: Spawns in makeshift shelters in lavaland. Ghosts become hermits with knowledge of how they got to where they are now.
/obj/effect/mob_spawn/human/hermit
@@ -224,6 +194,7 @@
the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... "
assignedrole = "Hermit"
use_cooldown = TRUE
+ banType = ROLE_HERMIT
/obj/effect/mob_spawn/human/hermit/Initialize(mapload)
. = ..()
@@ -266,6 +237,7 @@
all you did was apply bruise packs. Why is this place full of advanced medical equipment? And what are those screams you hear? The world outside is desolate - tormented with fire and brimstone. But you took an oath. \
You have to save these people! You might not have a fancy cloning machine like a real hospital, but surely there must be some way to save these people with the tools you have. Right?"
assignedrole = "Translocated Vet"
+ banType = ROLE_TRANSLOCATED_VET
/obj/effect/mob_spawn/human/doctor/alive/lavaland/Destroy()
var/obj/structure/fluff/empty_sleeper/S = new(drop_location())
@@ -287,6 +259,7 @@
flavour_text = "Good. It seems as though your ship crashed. You remember that you were convicted of "
assignedrole = "Escaped Prisoner"
use_cooldown = TRUE
+ banType = ROLE_LAVALAND_ESCAPED_PRISONER
/obj/effect/mob_spawn/human/prisoner_transport/special(mob/living/L)
L.fully_replace_character_name(null,"NTP #LL-0[rand(111,999)]") //Nanotrasen Prisoner #Lavaland-(numbers)
@@ -327,6 +300,7 @@
important_info = "DON'T leave the hotel"
assignedrole = "Hotel Staff"
use_cooldown = TRUE
+ banType = ROLE_HOTEL_STAFF
/datum/outfit/hotelstaff
name = "Hotel Staff"
@@ -374,6 +348,7 @@
var/obj/effect/proc_holder/spell/targeted/summon_friend/spell
var/datum/mind/owner
assignedrole = "SuperFriend"
+ banType = ROLE_DEMONIC_FRIEND
/obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell)
. = ..()
@@ -414,77 +389,6 @@
implants = list(/obj/item/implant/mindshield) //No revolutionaries, he's MY friend.
id = /obj/item/card/id
-/obj/effect/mob_spawn/human/syndicate
- name = "Syndicate Operative"
- roundstart = FALSE
- death = FALSE
- icon = 'icons/obj/machines/sleeper.dmi'
- icon_state = "sleeper_s"
- outfit = /datum/outfit/syndicate_empty
- assignedrole = "Space Syndicate" //I know this is really dumb, but Syndicate operative is nuke ops
-
-/datum/outfit/syndicate_empty
- name = "Syndicate Operative Empty"
- uniform = /obj/item/clothing/under/syndicate
- shoes = /obj/item/clothing/shoes/combat
- gloves = /obj/item/clothing/gloves/combat
- ears = /obj/item/radio/headset/syndicate/alt
- back = /obj/item/storage/backpack
- implants = list(/obj/item/implant/weapons_auth)
- id = /obj/item/card/id/syndicate
-
-/datum/outfit/syndicate_empty/post_equip(mob/living/carbon/human/H)
- H.faction |= ROLE_SYNDICATE
-
-/obj/effect/mob_spawn/human/syndicate/battlecruiser
- name = "Syndicate Battlecruiser Ship Operative"
- short_desc = "You are a crewmember aboard the syndicate flagship: the SBC Starfury."
- flavour_text = "Your job is to follow your captain's orders, maintain the ship, and keep the engine running. If you are not familiar with how the supermatter engine functions: do not attempt to start it."
- important_info = "The armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives."
- outfit = /datum/outfit/syndicate_empty/SBC
-
-/datum/outfit/syndicate_empty/SBC
- name = "Syndicate Battlecruiser Ship Operative"
- l_pocket = /obj/item/gun/ballistic/automatic/pistol
- r_pocket = /obj/item/kitchen/knife/combat/survival
- belt = /obj/item/storage/belt/military/assault
-
-/obj/effect/mob_spawn/human/syndicate/battlecruiser/assault
- name = "Syndicate Battlecruiser Assault Operative"
- short_desc = "You are an assault operative aboard the syndicate flagship: the SBC Starfury."
- flavour_text = "Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with."
- important_info = "Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!"
- outfit = /datum/outfit/syndicate_empty/SBC/assault
-
-/datum/outfit/syndicate_empty/SBC/assault
- name = "Syndicate Battlecruiser Assault Operative"
- uniform = /obj/item/clothing/under/syndicate/combat
- l_pocket = /obj/item/ammo_box/magazine/m10mm
- r_pocket = /obj/item/kitchen/knife/combat/survival
- belt = /obj/item/storage/belt/military
- suit = /obj/item/clothing/suit/armor/vest
- suit_store = /obj/item/gun/ballistic/automatic/pistol
- back = /obj/item/storage/backpack/security
- mask = /obj/item/clothing/mask/gas/syndicate
-
-/obj/effect/mob_spawn/human/syndicate/battlecruiser/captain
- name = "Syndicate Battlecruiser Captain"
- short_desc = "You are the captain aboard the syndicate flagship: the SBC Starfury."
- flavour_text = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal."
- important_info = "As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough."
- outfit = /datum/outfit/syndicate_empty/SBC/assault/captain
- id_access_list = list(150,151)
-
-/datum/outfit/syndicate_empty/SBC/assault/captain
- name = "Syndicate Battlecruiser Captain"
- l_pocket = /obj/item/melee/transforming/energy/sword/saber/red
- r_pocket = /obj/item/melee/classic_baton/police/telescopic
- suit = /obj/item/clothing/suit/armor/vest/capcarapace/syndicate
- suit_store = /obj/item/gun/ballistic/revolver/mateba
- back = /obj/item/storage/backpack/satchel/leather
- head = /obj/item/clothing/head/HoS/syndicate
- mask = /obj/item/clothing/mask/cigarette/cigar/havana
- glasses = /obj/item/clothing/glasses/thermal/eyepatch
//Ancient cryogenic sleepers. Players become NT crewmen from a hundred year old space station, now on the verge of collapse.
/obj/effect/mob_spawn/human/oldsec
@@ -509,6 +413,7 @@
l_pocket = /obj/item/assembly/flash/handheld
assignedrole = "Ancient Crew"
use_cooldown = TRUE
+ banType = ROLE_ANCIENT_CREW
/obj/effect/mob_spawn/human/oldsec/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
@@ -536,6 +441,7 @@
l_pocket = /obj/item/tank/internals/emergency_oxygen
assignedrole = "Ancient Crew"
use_cooldown = TRUE
+ banType = ROLE_ANCIENT_CREW
/obj/effect/mob_spawn/human/oldeng/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
@@ -562,6 +468,7 @@
l_pocket = /obj/item/stack/medical/bruise_pack
assignedrole = "Ancient Crew"
use_cooldown = TRUE
+ banType = ROLE_ANCIENT_CREW
/obj/effect/mob_spawn/human/oldsci/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
@@ -584,6 +491,7 @@
short_desc = "You are a space pirate."
flavour_text = "The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot."
assignedrole = "Space Pirate"
+ is_antagonist = TRUE
var/rank = "Mate"
/obj/effect/mob_spawn/human/pirate/special(mob/living/new_spawn)
diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm
index 0b41fcf8d49..8f7870e7b30 100644
--- a/code/game/objects/structures/spawner.dm
+++ b/code/game/objects/structures/spawner.dm
@@ -31,7 +31,7 @@
icon_state = "syndbeacon"
spawn_text = "warps in from"
mob_types = list(/mob/living/simple_animal/hostile/syndicate/ranged)
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
/obj/structure/spawner/skeleton
name = "bone pit"
diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm
index 3dc29f2d1cf..5b4f5087d99 100644
--- a/code/modules/admin/antag_panel.dm
+++ b/code/modules/admin/antag_panel.dm
@@ -165,11 +165,19 @@ GLOBAL_VAR(antag_prototypes)
continue
pref_source = prototype
break
- if(pref_source.job_rank)
- if(!is_banned_from(src.key, pref_source.job_rank))
- antag_header_parts += pref_source.enabled_in_preferences(src) ? "Enabled in Prefs" : "Disabled in Prefs"
- else
+ if(pref_source.banning_key)
+ if(is_banned_from(src.key, pref_source.banning_key))
antag_header_parts += "\[BANNED\] "
+ else if(current.client)
+ var/list/related_preferences = list()
+ for(var/datum/role_preference/role_pref_type as anything in GLOB.role_preference_entries)
+ if(initial(role_pref_type.antag_datum) == pref_source.type)
+ related_preferences += role_pref_type
+ if(length(related_preferences) == 1)
+ antag_header_parts += current.client.role_preference_enabled(related_preferences[1]) ? "Enabled in Prefs" : "Disabled in Prefs"
+ else if(length(related_preferences) >= 1)
+ for(var/datum/role_preference/preftype as anything in related_preferences)
+ antag_header_parts += "[initial(preftype.name)]: [current.client.role_preference_enabled(preftype) ? "Enabled Pref" : "Disabled Pref"]"
//Traitor : None | Traitor | IAA
// Command1 | Command2 | Command3
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index ca395efabda..33308c17d44 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -53,7 +53,7 @@
bodies += M
var/question = "Would you like to be [group_name]?"
- var/list/candidates = pollCandidatesForMobs(question, ROLE_PAI, null, FALSE, 100, bodies)
+ var/list/candidates = pollCandidatesForMobs(question, ROLE_SENTIENCE, null, 10 SECONDS, bodies)
while(LAZYLEN(candidates) && LAZYLEN(bodies))
var/mob/dead/observer/C = pick_n_take(candidates)
var/mob/living/body = pick_n_take(bodies)
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index 56759be507a..f05165e3a7a 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -690,7 +690,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new)
continue
if((L in GLOB.player_list) || L.mind || (L.flags_1 & HOLOGRAM_1))
continue
- L.set_playable()
+ L.set_playable(ROLE_SENTIENT_ANIMAL)
if("flipmovement")
if(!check_rights(R_FUN))
@@ -789,7 +789,7 @@ GLOBAL_DATUM_INIT(admin_secrets, /datum/admin_secrets, new)
var/list/candidates = list()
if (prefs["offerghosts"]["value"] == "Yes")
- candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR)
+ candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), BAN_ROLE_ALL_ANTAGONISTS, ignore_category = FALSE)
if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"])
message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]")
diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm
index 9b0efda4aec..255360d94de 100644
--- a/code/modules/admin/sql_ban_system.dm
+++ b/code/modules/admin/sql_ban_system.dm
@@ -1,20 +1,36 @@
#define MAX_ADMINBANS_PER_ADMIN 1
#define MAX_ADMINBANS_PER_HEADMIN 3
+/// Process global ban types
+/proc/check_role_ban(ban_cache, role)
+ if(role in GLOB.antagonist_bannable_roles)
+ if((BAN_ROLE_ALL_ANTAGONISTS in ban_cache) || ("Syndicate" in ban_cache)) // Legacy "Syndicate" ban
+ return TRUE
+ if(role in GLOB.forced_bannable_roles)
+ if(BAN_ROLE_FORCED_ANTAGONISTS in ban_cache)
+ return TRUE
+ if(role in GLOB.ghost_role_bannable_roles)
+ if((BAN_ROLE_ALL_GHOST in ban_cache) || ("Lavaland" in ban_cache)) // Legacy "Lavaland" ban
+ return TRUE
+ return role in ban_cache
+
//checks client ban cache or DB ban table if ckey is banned from one or more roles
//doesn't return any details, use only for if statements
/proc/is_banned_from(player_ckey, list/roles)
- if(!player_ckey)
+ if(!player_ckey || isnull(roles) || (islist(roles) && !length(roles)))
return
+ player_ckey = ckey(player_ckey)
var/client/C = GLOB.directory[player_ckey]
if(C)
if(!C.ban_cache)
build_ban_cache(C)
if(islist(roles))
for(var/R in roles)
- if(R in C.ban_cache)
+ if(!R)
+ continue
+ if(check_role_ban(C.ban_cache, R))
return TRUE //they're banned from at least one role, no need to keep checking
- else if(roles in C.ban_cache)
+ else if(check_role_ban(C.ban_cache, roles))
return TRUE
else
var/values = list(
@@ -325,16 +341,14 @@
"}
break_counter++
output += ""
- var/list/long_job_lists = list(("Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK),
- "Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE),
- "Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB,
- ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST, ROLE_HERETIC,
- ROLE_DEVIL, ROLE_INTERNAL_AFFAIRS, ROLE_MALF,
- ROLE_NINJA, ROLE_OPERATIVE,
- ROLE_SERVANT_OF_RATVAR,
- ROLE_OVERTHROW, ROLE_REV, ROLE_REVENANT,
- ROLE_REV_HEAD, ROLE_SYNDICATE,
- ROLE_TRAITOR, ROLE_WIZARD, ROLE_HIVE, ROLE_GANG, ROLE_TERATOMA)) //ROLE_REV_HEAD is excluded from this because rev jobbans are handled by ROLE_REV
+ var/list/long_job_lists = list(
+ "Civilian" = GLOB.civilian_positions | JOB_NAME_GIMMICK,
+ "Antagonist Positions" = list(BAN_ROLE_ALL_ANTAGONISTS) + GLOB.antagonist_bannable_roles,
+ "Forced Antagonist Positions" = list(BAN_ROLE_FORCED_ANTAGONISTS) + GLOB.forced_bannable_roles,
+ "Ghost Roles" = list(BAN_ROLE_ALL_GHOST) + GLOB.ghost_role_bannable_roles,
+ "Other" = GLOB.other_bannable_roles,
+ )
+
for(var/department in long_job_lists)
output += "
[department]"
break_counter = 0
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index bcf50f3238b..b91367cca0b 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -27,10 +27,10 @@
popup.set_content(dat)
popup.open()
-/datum/admins/proc/isReadytoRumble(mob/living/carbon/human/applicant, targetrole, onstation = TRUE, conscious = TRUE)
+/datum/admins/proc/isReadytoRumble(mob/living/carbon/human/applicant, targetrole, preference, onstation = TRUE, conscious = TRUE)
if(applicant.mind.special_role)
return FALSE
- if(!(targetrole in applicant.client.prefs.be_special))
+ if(!applicant.client?.should_include_for_role(targetrole, preference))
return FALSE
if(onstation)
var/turf/T = get_turf(applicant)
@@ -40,7 +40,7 @@
return FALSE
if(!considered_alive(applicant.mind) || considered_afk(applicant.mind)) //makes sure the player isn't a zombie, brain, or just afk all together
return FALSE
- return !is_banned_from(applicant.ckey, list(targetrole, ROLE_SYNDICATE))
+ return TRUE
/datum/admins/proc/makeTraitors(maxCount = 3)
@@ -59,10 +59,9 @@
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
- if(isReadytoRumble(applicant, ROLE_TRAITOR))
- if(temp.age_check(applicant.client))
- if(!(applicant.job in temp.restricted_jobs))
- candidates += applicant
+ if(isReadytoRumble(applicant, ROLE_TRAITOR, /datum/role_preference/midround_living/traitor))
+ if(!(applicant.job in temp.restricted_jobs))
+ candidates += applicant
if(candidates.len)
var/numTraitors = min(candidates.len, maxCount)
@@ -94,10 +93,9 @@
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
- if(isReadytoRumble(applicant, ROLE_CHANGELING))
- if(temp.age_check(applicant.client))
- if(!(applicant.job in temp.restricted_jobs))
- candidates += applicant
+ if(isReadytoRumble(applicant, ROLE_CHANGELING, /datum/role_preference/antagonist/changeling))
+ if(!(applicant.job in temp.restricted_jobs))
+ candidates += applicant
if(candidates.len)
var/numChangelings = min(candidates.len, maxCount)
@@ -124,10 +122,9 @@
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
- if(isReadytoRumble(applicant, ROLE_REV))
- if(temp.age_check(applicant.client))
- if(!(applicant.job in temp.restricted_jobs))
- candidates += applicant
+ if(isReadytoRumble(applicant, ROLE_REV_HEAD, /datum/role_preference/antagonist/revolutionary))
+ if(!(applicant.job in temp.restricted_jobs))
+ candidates += applicant
if(candidates.len)
var/numRevs = min(candidates.len, maxCount)
@@ -142,7 +139,7 @@
/datum/admins/proc/makeWizard()
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", ROLE_WIZARD, null)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Federation 'diplomat'?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, ignore_category = POLL_IGNORE_WIZARD_HELPER)
var/mob/dead/observer/selected = pick_n_take(candidates)
@@ -166,10 +163,9 @@
var/mob/living/carbon/human/H = null
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
- if(isReadytoRumble(applicant, ROLE_CULTIST))
- if(temp.age_check(applicant.client))
- if(!(applicant.job in temp.restricted_jobs))
- candidates += applicant
+ if(isReadytoRumble(applicant, ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist))
+ if(!(applicant.job in temp.restricted_jobs))
+ candidates += applicant
if(candidates.len)
var/numCultists = min(candidates.len, maxCount)
@@ -186,8 +182,7 @@
/datum/admins/proc/makeNukeTeam(maxCount = 5)
- var/datum/game_mode/nuclear/temp = new
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, temp)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative)
var/list/mob/dead/observer/chosen = list()
var/mob/dead/observer/theghost = null
@@ -351,7 +346,7 @@
ertemplate.enforce_human = prefs["enforce_human"]["value"] == "Yes" ? TRUE : FALSE
ertemplate.opendoors = prefs["open_armory"]["value"] == "Yes" ? TRUE : FALSE
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", "deathsquad", null, req_hours = 50)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", ROLE_ERT, req_hours = 50)
var/teamSpawned = FALSE
if(candidates.len > 0)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 6c949a6a165..6cdb4f7c102 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -300,7 +300,7 @@
for(var/mob/M in GLOB.player_list)
if(M.stat != DEAD)
continue //we are not dead!
- if(!(ROLE_ALIEN in M.client.prefs.be_special))
+ if(!M.client?.should_include_for_role(ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph))
continue //we don't want to be an alium
if(M.client.is_afk())
continue //we are afk
@@ -474,7 +474,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.forceMove(pick(GLOB.wizardstart))
var/datum/antagonist/wizard/A = new_character.mind.has_antag_datum(/datum/antagonist/wizard,TRUE)
A.equip_wizard()
- if(ROLE_SYNDICATE)
+ if(ROLE_OPERATIVE)
new_character.forceMove(pick(GLOB.nukeop_start))
var/datum/antagonist/nukeop/N = new_character.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
N.equip_op()
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index a7cc7e8c0d6..4da2f23239f 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -12,7 +12,10 @@ GLOBAL_LIST(admin_antag_list)
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
var/delete_on_mind_deletion = TRUE
- var/job_rank
+ /// The ROLE_X key used for this antagonist.
+ var/banning_key
+ /// Required living playtime to be included in the rolling for this antagonist
+ var/required_living_playtime = 0
var/give_objectives = TRUE //Should the default objectives be generated?
var/replace_banned = TRUE //Should replace jobbanned player with ghosts if granted.
var/list/objectives = list()
@@ -114,12 +117,12 @@ GLOBAL_LIST(admin_antag_list)
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
return FALSE
- . = (is_banned_from(M.ckey, list(ROLE_SYNDICATE, job_rank)) || QDELETED(M))
+ . = (is_banned_from(M.ckey, banning_key) || QDELETED(M))
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", job_rank, null, job_rank, 50, owner.current)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", banning_key, null, 7.5 SECONDS, owner.current, ignore_category = FALSE)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
@@ -127,6 +130,7 @@ GLOBAL_LIST(admin_antag_list)
owner.current.ghostize(FALSE)
owner.current.key = C.key
else
+ owner.current.playable_bantype = banning_key
owner.current.ghostize(FALSE,SENTIENCE_FORCE)
///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
@@ -221,14 +225,6 @@ GLOBAL_LIST(admin_antag_list)
/datum/antagonist/proc/antag_panel_data()
return ""
-/datum/antagonist/proc/enabled_in_preferences(datum/mind/M)
- if(job_rank)
- if(M.current && M.current.client && (job_rank in M.current.client.prefs.be_special))
- return TRUE
- else
- return FALSE
- return TRUE
-
// List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/datum/antagonist/proc/get_admin_commands()
. = list()
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index d17f6c8e2e9..eff42e0d677 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -58,7 +58,7 @@
if(used)
to_chat(H, "You already used this contract!")
return
- var/list/candidates = pollCandidatesForMob("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, null, ROLE_WIZARD, 150, src)
+ var/list/candidates = pollGhostCandidates("Do you want to play as a wizard's [href_list["school"]] apprentice?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 15 SECONDS, ignore_category = POLL_IGNORE_WIZARD_HELPER)
if(LAZYLEN(candidates))
if(QDELETED(src))
return
@@ -123,7 +123,7 @@
return
to_chat(user, "
You activate [src] and wait for confirmation. ")
- var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE)
+ var/list/nuke_candidates = pollGhostCandidates("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative, 15 SECONDS)
if(LAZYLEN(nuke_candidates))
if(QDELETED(src) || !check_usability(user))
return
@@ -243,7 +243,7 @@
return
if(used)
return
- var/list/candidates = pollCandidatesForMob("Do you want to play as a [initial(demon_type.name)]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, src)
+ var/list/candidates = pollGhostCandidates("Do you want to play as a [initial(demon_type.name)]?", ROLE_SLAUGHTER_DEMON, null, 10 SECONDS, ignore_category = FALSE)
if(LAZYLEN(candidates))
if(used || QDELETED(src))
return
@@ -295,7 +295,7 @@
return
to_chat(user, "
You activate [src] and wait for confirmation. ")
- var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", ROLE_GANG, null, ROLE_GANG, 150)
+ var/list/candidates = pollGhostCandidates("Do you want to play as a gangster reinforcements?", ROLE_GANG, /datum/role_preference/antagonist/gangster, 15 SECONDS)
if(LAZYLEN(candidates))
if(QDELETED(src) || !check_usability(user))
return
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 4973e420256..054c9d137bf 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -4,7 +4,7 @@
name = "Abductor"
roundend_category = "abductors"
antagpanel_category = "Abductor"
- job_rank = ROLE_ABDUCTOR
+ banning_key = ROLE_ABDUCTOR
show_in_antagpanel = FALSE //should only show subtypes
show_to_ghosts = TRUE
var/datum/team/abductor_team/team
@@ -179,6 +179,7 @@
name = "Abductee"
roundend_category = "abductees"
antagpanel_category = "Abductee"
+ banning_key = UNBANNABLE_ANTAGONIST
/datum/antagonist/abductee/on_gain()
give_objective()
diff --git a/code/modules/antagonists/ashwalker/ashwalker.dm b/code/modules/antagonists/ashwalker/ashwalker.dm
index f7a35eb0a31..5f767a84800 100644
--- a/code/modules/antagonists/ashwalker/ashwalker.dm
+++ b/code/modules/antagonists/ashwalker/ashwalker.dm
@@ -4,7 +4,7 @@
/datum/antagonist/ashwalker
name = "Ash Walker"
- job_rank = ROLE_LAVALAND
+ banning_key = ROLE_ASHWALKER
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
prevent_roundtype_conversion = FALSE
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm
index 9c58e95630b..802b38735b6 100644
--- a/code/modules/antagonists/blob/blob.dm
+++ b/code/modules/antagonists/blob/blob.dm
@@ -3,7 +3,7 @@
roundend_category = "blobs"
antagpanel_category = "Blob"
show_to_ghosts = TRUE
- job_rank = ROLE_BLOB
+ banning_key = ROLE_BLOB
var/datum/action/innate/blobpop/pop_action
var/starting_points_human_blob = 60
diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm
index 33bc9acd69a..f67e4d2aeca 100644
--- a/code/modules/antagonists/blob/blob_mobs.dm
+++ b/code/modules/antagonists/blob/blob_mobs.dm
@@ -7,7 +7,7 @@
/mob/living/simple_animal/hostile/blob
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB
- faction = list(ROLE_BLOB)
+ faction = list(FACTION_BLOB)
bubble_icon = "blob"
speak_emote = null //so we use verb_yell/verb_say/etc
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
@@ -166,7 +166,7 @@
update_icons()
visible_message("
The corpse of [H.name] suddenly rises! ")
if(!key)
- set_playable()
+ set_playable(ROLE_BLOB)
/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed)
// On death, create a small smoke of harmful gas (s-Acid)
diff --git a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm
index 82528d95828..7714ab6944c 100644
--- a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm
+++ b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm
@@ -31,7 +31,7 @@
var/obj/effect/temp_visual/explosion/fast/E = new /obj/effect/temp_visual/explosion/fast(get_turf(M))
E.alpha = 150
for(var/mob/living/L in ohearers(1, get_turf(M)))
- if(ROLE_BLOB in L.faction) //no friendly fire
+ if(FACTION_BLOB in L.faction) //no friendly fire
continue
var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
L.apply_damage(0.4*aoe_volume, BRUTE)
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index 2c57a7da6dd..b2139d3cbac 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
layer = FLY_LAYER
pass_flags = PASSBLOB
- faction = list(ROLE_BLOB)
+ faction = list(FACTION_BLOB)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
hud_type = /datum/hud/blob_overmind
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
@@ -145,7 +145,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
if(!(Ablob.area_flags & BLOBS_ALLOWED))
continue
- if(!(ROLE_BLOB in L.faction))
+ if(!(FACTION_BLOB in L.faction))
playsound(L, 'sound/effects/splat.ogg', 50, 1)
L.death()
new/mob/living/simple_animal/hostile/blob/blobspore(T)
diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm
index 706abeef12a..1040673e8b8 100644
--- a/code/modules/antagonists/blob/powers.dm
+++ b/code/modules/antagonists/blob/powers.dm
@@ -16,13 +16,13 @@
if(!placement_override)
if(!pop_override)
for(var/mob/living/M in range(7, src))
- if(ROLE_BLOB in M.faction)
+ if(FACTION_BLOB in M.faction)
continue
if(M.client)
to_chat(src, "
There is someone too close to place your blob core! ")
return 0
for(var/mob/living/M in hearers(13, src))
- if(ROLE_BLOB in M.faction)
+ if(FACTION_BLOB in M.faction)
continue
if(M.client)
to_chat(src, "
Someone could see your blob core from here! ")
@@ -172,7 +172,7 @@
B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory.
to_chat(src, "
You attempt to produce a blobbernaut. ")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blobstrain.name] blobbernaut?", ROLE_BLOB, /datum/role_preference/midround_ghost/blob, 7.5 SECONDS, ignore_category = POLL_IGNORE_BLOB_HELPER) //players must answer rapidly
if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now.
B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health
B.obj_integrity = min(B.obj_integrity, B.max_integrity)
@@ -268,7 +268,7 @@
if(can_buy(BLOB_SPREAD_COST))
var/attacksuccess = FALSE
for(var/mob/living/L in T)
- if(ROLE_BLOB in L.faction) //no friendly/dead fire
+ if(FACTION_BLOB in L.faction) //no friendly/dead fire
continue
if(L.stat != DEAD)
attacksuccess = TRUE
diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm
index dad188eaaaf..20dfcbb3aef 100644
--- a/code/modules/antagonists/blob/structures/_blob.dm
+++ b/code/modules/antagonists/blob/structures/_blob.dm
@@ -253,7 +253,7 @@
/obj/structure/blob/attack_animal(mob/living/simple_animal/M)
- if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut
+ if(FACTION_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut
return
..()
diff --git a/code/modules/antagonists/blood_contract/blood_contract.dm b/code/modules/antagonists/blood_contract/blood_contract.dm
index f2faecdd458..c1b79bc13b1 100644
--- a/code/modules/antagonists/blood_contract/blood_contract.dm
+++ b/code/modules/antagonists/blood_contract/blood_contract.dm
@@ -4,6 +4,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
var/duration = 2 MINUTES
+ banning_key = UNBANNABLE_ANTAGONIST
/datum/antagonist/blood_contract/on_gain()
. = ..()
@@ -37,6 +38,7 @@
for(var/mob/living/carbon/human/P in GLOB.player_list)
if(P == H)
continue
+ log_game("[key_name(P)] was selected to kill [key_name(H)] by blood contract") // holy shit why is there no antag datum. I'm doing a huge refactor so I don't have time for one but I had to add this log here
to_chat(P, "
You have an overwhelming desire to kill [H]. [H.p_theyve(TRUE)] been marked red! Whoever [H.p_they()] [H.p_were()], friend or foe, go kill [H.p_them()]! ")
var/obj/item/I = new /obj/item/kitchen/knife/butcher(get_turf(P))
diff --git a/code/modules/antagonists/brainwashing/brainwashing.dm b/code/modules/antagonists/brainwashing/brainwashing.dm
index 57d082ec819..269e40ee32b 100644
--- a/code/modules/antagonists/brainwashing/brainwashing.dm
+++ b/code/modules/antagonists/brainwashing/brainwashing.dm
@@ -28,7 +28,7 @@
/datum/antagonist/brainwashed
name = "Brainwashed Victim"
- job_rank = ROLE_BRAINWASHED
+ banning_key = ROLE_BRAINWASHED
roundend_category = "brainwashed victims"
show_in_antagpanel = TRUE
antagpanel_category = "Other"
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index efdea659704..b48cf60aa7f 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -1,8 +1,8 @@
/datum/antagonist/brother
name = "Brother"
antagpanel_category = "Brother"
- job_rank = ROLE_BROTHER
- var/special_role = ROLE_BROTHER
+ banning_key = ROLE_BROTHER
+ required_living_playtime = 4
hijack_speed = 0.5
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
@@ -22,14 +22,14 @@
objectives += team.objectives
for(var/datum/objective/O in team.objectives)
log_objective(owner, O.explanation_text)
- owner.special_role = special_role
+ owner.special_role = ROLE_BROTHER
finalize_brother()
return ..()
/datum/antagonist/brother/on_removal()
SSticker.mode.brothers -= owner
if(owner.current)
- to_chat(owner.current,"
You are no longer the [special_role]! ")
+ to_chat(owner.current,"
You are no longer the Blood Brother! ")
owner.special_role = null
return ..()
@@ -56,7 +56,7 @@
/datum/antagonist/brother/greet()
var/brother_text = get_brother_names()
- to_chat(owner.current, "
You are the [owner.special_role] of [brother_text]. ")
+ to_chat(owner.current, "
You are the Blood Brother of [brother_text]. ")
to_chat(owner.current, "The Syndicate only accepts those that have proven themselves. Prove yourself and prove your [team.member_name]s by completing your objectives together! You and your team are outfitted with communication implants allowing for direct, encrypted communication.")
owner.announce_objectives()
give_meeting_area()
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 957db20573c..53c3a42b637 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -6,7 +6,8 @@
name = "Changeling"
roundend_category = "changelings"
antagpanel_category = "Changeling"
- job_rank = ROLE_CHANGELING
+ banning_key = ROLE_CHANGELING
+ required_living_playtime = 4
antag_moodlet = /datum/mood_event/focused
hijack_speed = 0.5
var/you_are_greet = TRUE
diff --git a/code/modules/antagonists/changeling/powers/teratoma.dm b/code/modules/antagonists/changeling/powers/teratoma.dm
index 482ac09834b..f0a89f41d6f 100644
--- a/code/modules/antagonists/changeling/powers/teratoma.dm
+++ b/code/modules/antagonists/changeling/powers/teratoma.dm
@@ -23,7 +23,7 @@
var/datum/antagonist/changeling/c = user.mind.has_antag_datum(/datum/antagonist/changeling)
c.chem_charges -= chemical_cost //I'm taking your chemicals hostage!
var/turf/A = get_turf(user)
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", ROLE_TERATOMA, null, ROLE_TERATOMA, 5 SECONDS) //players must answer rapidly
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a living teratoma?", ROLE_TERATOMA, null, 7.5 SECONDS) //players must answer rapidly
if(!LAZYLEN(candidates)) //if we got at least one candidate, they're teratoma now
to_chat(usr, "
You fail at creating a tumor. Perhaps you should try again later? ")
c.chem_charges += chemical_cost //If it fails we want to refund the chemicals
diff --git a/code/modules/antagonists/changeling/teratoma.dm b/code/modules/antagonists/changeling/teratoma.dm
index e304ae4383c..3e5a4c51aaf 100644
--- a/code/modules/antagonists/changeling/teratoma.dm
+++ b/code/modules/antagonists/changeling/teratoma.dm
@@ -2,7 +2,7 @@
name = "Teratoma"
roundend_category = "other"
antagpanel_category = "Changeling"
- job_rank = ROLE_TERATOMA
+ banning_key = ROLE_TERATOMA
/datum/antagonist/teratoma/on_gain()
owner.special_role = "Teratoma"
@@ -45,7 +45,6 @@
name = "Maintenance Teratoma"
roundend_category = "other"
antagpanel_category = "Changeling"
- job_rank = ROLE_TERATOMA
/datum/antagonist/teratoma/hugbox/greet()
..()
diff --git a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm
index 8d233a2d20d..d1be9760a77 100644
--- a/code/modules/antagonists/clock_cult/mobs/cogscarab.dm
+++ b/code/modules/antagonists/clock_cult/mobs/cogscarab.dm
@@ -65,11 +65,6 @@ GLOBAL_LIST_INIT(cogscarabs, list())
/obj/effect/mob_spawn/drone/cogscarab/attack_ghost(mob/user)
if(is_banned_from(user.ckey, ROLE_SERVANT_OF_RATVAR) || QDELETED(src) || QDELETED(user))
return
- if(CONFIG_GET(flag/use_age_restriction_for_jobs))
- if(!isnum(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention
- if(user.client.player_age < 14)
- to_chat(user, "
You're too new to play as a drone! Please try again in [14 - user.client.player_age] days. ")
- return
if(!SSticker.mode)
to_chat(user, "Can't become a cogscarab before the game has started.")
return
diff --git a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm
index e4ce19d5f04..0d80bd2aa47 100644
--- a/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm
+++ b/code/modules/antagonists/clock_cult/scriptures/sigil_of_vitality.dm
@@ -53,7 +53,7 @@
if(M.mind)
M.mind.grab_ghost(TRUE)
else
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, null, ROLE_SERVANT_OF_RATVAR, 50, M)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [M.name], an inactive clock cultist?", ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 7.5 SECONDS, M)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(M)]) to replace an AFK player.")
diff --git a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm
index ef41ed2bb61..d665cbeaee7 100644
--- a/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm
+++ b/code/modules/antagonists/clock_cult/scriptures/summon_marauder.dm
@@ -17,7 +17,7 @@
var/mob/dead/observer/selected
/datum/clockcult/scripture/marauder/invoke()
- candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", ROLE_SERVANT_OF_RATVAR, null, null, 100, POLL_IGNORE_CLOCKWORK)
+ candidates = pollGhostCandidates("Would you like to play as a clockwork marauder?", ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 10 SECONDS, POLL_IGNORE_CLOCKWORK_HELPER)
if(LAZYLEN(candidates))
selected = pick(candidates)
if(!selected)
diff --git a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm
index 470215b2696..d904a53fe91 100644
--- a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm
+++ b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm
@@ -7,7 +7,8 @@
roundend_category = "clock cultists"
antagpanel_category = "Clockcult"
antag_moodlet = /datum/mood_event/cult
- job_rank = ROLE_SERVANT_OF_RATVAR
+ banning_key = ROLE_SERVANT_OF_RATVAR
+ required_living_playtime = 4
//The class of the servant
var/datum/action/innate/clockcult/transmit/transmit_spell
diff --git a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm
index 666acf43f25..0a04a5f1316 100644
--- a/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm
+++ b/code/modules/antagonists/clock_cult/structure/eminence_beacon.dm
@@ -35,7 +35,7 @@
vote_active = FALSE
used = TRUE
if(!eminence)
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", ROLE_SERVANT_OF_RATVAR, null, null, 100, POLL_IGNORE_PYROSLIME)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eminence?", ROLE_SERVANT_OF_RATVAR, /datum/role_preference/antagonist/clock_cultist, 10 SECONDS)
if(LAZYLEN(candidates))
eminence = pick(candidates)
else
diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm
index 6537250de14..63b43371dad 100644
--- a/code/modules/antagonists/creep/creep.dm
+++ b/code/modules/antagonists/creep/creep.dm
@@ -2,7 +2,7 @@
name = "Obsessed"
show_in_antagpanel = TRUE
antagpanel_category = "Other"
- job_rank = ROLE_OBSESSED
+ banning_key = ROLE_OBSESSED
show_name_in_check_antagonists = TRUE
roundend_category = "obsessed"
count_against_dynamic_roll_chance = FALSE
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index 75ff9fe6815..0f8cf5cefed 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -8,7 +8,8 @@
var/datum/action/innate/cult/comm/communion = new
var/datum/action/innate/cult/mastervote/vote = new
var/datum/action/innate/cult/blood_magic/magic = new
- job_rank = ROLE_CULTIST
+ banning_key = ROLE_CULTIST
+ required_living_playtime = 4
var/ignore_implant = FALSE
var/give_equipment = FALSE
var/datum/team/cult/cult_team
diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm
index adb892f2e73..e28c1220f3a 100644
--- a/code/modules/antagonists/cult/cult_comms.dm
+++ b/code/modules/antagonists/cult/cult_comms.dm
@@ -108,7 +108,7 @@
if(B.current && B.current != Nominee && !B.current.incapacitated())
SEND_SOUND(B.current, 'sound/magic/exit_blood.ogg')
asked_cultists += B.current
- var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 300, group = asked_cultists)
+ var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 30 SECONDS, group = asked_cultists)
if(QDELETED(Nominee) || Nominee.incapacitated())
team.cult_vote_called = FALSE
for(var/datum/mind/B in team.members)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index fef483b3a3e..4f5d58cf7c7 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -603,7 +603,7 @@ structure_check() searches for nearby cultist structures required for the invoca
mob_to_revive.grab_ghost()
if(!mob_to_revive.client || mob_to_revive.client.is_afk())
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, null, ROLE_CULTIST, 50, mob_to_revive)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [mob_to_revive.name], an inactive blood cultist?", ROLE_CULTIST, /datum/role_preference/antagonist/blood_cultist, 7.5 SECONDS, mob_to_revive)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.")
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
index 15bc64418e0..8b548791769 100644
--- a/code/modules/antagonists/devil/devil.dm
+++ b/code/modules/antagonists/devil/devil.dm
@@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
name = "Devil"
roundend_category = "devils"
antagpanel_category = "Devil"
- job_rank = ROLE_DEVIL
+ banning_key = ROLE_DEVIL
//Don't delete upon mind destruction, otherwise soul re-selling will break.
delete_on_mind_deletion = FALSE
show_to_ghosts = TRUE
diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm
index bbe9b3978c7..0b8f25b0a25 100644
--- a/code/modules/antagonists/devil/imp/imp.dm
+++ b/code/modules/antagonists/devil/imp/imp.dm
@@ -64,6 +64,7 @@
name = "Imp"
antagpanel_category = "Devil"
show_in_roundend = FALSE
+ banning_key = ROLE_DEVIL
/datum/antagonist/imp/on_gain()
. = ..()
diff --git a/code/modules/antagonists/devil/sintouched/sintouched.dm b/code/modules/antagonists/devil/sintouched/sintouched.dm
index 29c3c77fd6d..014fdfcba30 100644
--- a/code/modules/antagonists/devil/sintouched/sintouched.dm
+++ b/code/modules/antagonists/devil/sintouched/sintouched.dm
@@ -10,6 +10,7 @@
name = "sintouched"
roundend_category = "sintouched"
antagpanel_category = "Devil"
+ banning_key = UNBANNABLE_ANTAGONIST
var/sin
var/static/list/sins = list(SIN_ACEDIA,SIN_GLUTTONY,SIN_GREED,SIN_SLOTH,SIN_WRATH,SIN_ENVY,SIN_PRIDE)
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
index bd98f6e4d07..b752da58ec1 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm
@@ -3,7 +3,8 @@
roundend_category = "Heretics"
antagpanel_category = "Heretic"
antag_moodlet = /datum/mood_event/heretics
- job_rank = ROLE_HERETIC
+ banning_key = ROLE_HERETIC
+ required_living_playtime = 4
var/antag_hud_type = ANTAG_HUD_HERETIC // someone make all the other antags conform to this too lol
var/antag_hud_name = "heretic"
hijack_speed = 0.5
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm
index 7ecba7e1df1..4a48677f763 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm
@@ -4,7 +4,7 @@
roundend_category = "Heretics"
antagpanel_category = "Heretic Beast"
antag_moodlet = /datum/mood_event/heretics
- job_rank = ROLE_HERETIC
+ banning_key = ROLE_HERETIC
var/antag_hud_type = ANTAG_HUD_HERETIC
var/antag_hud_name = "heretic_beast"
var/datum/antagonist/heretic/master
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 458280d6e0e..65f0e45db0d 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -22,6 +22,7 @@
show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
count_against_dynamic_roll_chance = FALSE
+ banning_key = ROLE_ERT
/datum/antagonist/ert/on_gain()
if(random_names)
diff --git a/code/modules/antagonists/fugitive/fugitive.dm b/code/modules/antagonists/fugitive/fugitive.dm
index 164253f3f1f..b543d1752ef 100644
--- a/code/modules/antagonists/fugitive/fugitive.dm
+++ b/code/modules/antagonists/fugitive/fugitive.dm
@@ -2,6 +2,7 @@
/datum/antagonist/fugitive
name = "Fugitive"
roundend_category = "Fugitive"
+ banning_key = ROLE_FUGITIVE
silent = TRUE //greet called by the event
show_in_antagpanel = FALSE
prevent_roundtype_conversion = FALSE
diff --git a/code/modules/antagonists/fugitive/hunter.dm b/code/modules/antagonists/fugitive/hunter.dm
index aba80479982..e5dc82de09f 100644
--- a/code/modules/antagonists/fugitive/hunter.dm
+++ b/code/modules/antagonists/fugitive/hunter.dm
@@ -2,6 +2,7 @@
/datum/antagonist/fugitive_hunter
name = "Fugitive Hunter"
roundend_category = "Fugitive"
+ banning_key = ROLE_FUGITIVE_HUNTER
silent = TRUE //greet called by the spawn
show_in_antagpanel = FALSE
prevent_roundtype_conversion = FALSE
diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm
index 37377021b71..479ffb27b90 100644
--- a/code/modules/antagonists/gang/gang.dm
+++ b/code/modules/antagonists/gang/gang.dm
@@ -2,7 +2,7 @@
name = "Gangster"
roundend_category = "gangsters"
can_coexist_with_others = FALSE
- job_rank = ROLE_GANG
+ banning_key = ROLE_GANG
antagpanel_category = "Gang"
var/hud_type = "gangster"
var/message_name = "Gangster"
diff --git a/code/modules/antagonists/greentext/greentext.dm b/code/modules/antagonists/greentext/greentext.dm
index a40ab260538..3409e710410 100644
--- a/code/modules/antagonists/greentext/greentext.dm
+++ b/code/modules/antagonists/greentext/greentext.dm
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE //Not that it will be there for long
count_against_dynamic_roll_chance = FALSE
+ banning_key = UNBANNABLE_ANTAGONIST
/datum/antagonist/greentext/proc/forge_objectives()
var/datum/objective/O = new /datum/objective("Succeed")
diff --git a/code/modules/antagonists/guardian/guardian.dm b/code/modules/antagonists/guardian/guardian.dm
index 192f8675362..3d46c14c325 100644
--- a/code/modules/antagonists/guardian/guardian.dm
+++ b/code/modules/antagonists/guardian/guardian.dm
@@ -5,6 +5,7 @@
show_in_antagpanel = FALSE
var/datum/guardian_stats/stats
var/datum/mind/summoner
+ banning_key = ROLE_HOLOPARASITE
/datum/antagonist/guardian/roundend_report()
var/list/parts = list()
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index c79a4845af6..5892dec8abf 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -5,6 +5,7 @@
show_name_in_check_antagonists = TRUE
can_elimination_hijack = ELIMINATION_ENABLED
count_against_dynamic_roll_chance = FALSE
+ banning_key = BAN_ROLE_ALL_ANTAGONISTS
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
diff --git a/code/modules/antagonists/hivemind/hivemind.dm b/code/modules/antagonists/hivemind/hivemind.dm
index 0fceed869ef..fa4d0033ee2 100644
--- a/code/modules/antagonists/hivemind/hivemind.dm
+++ b/code/modules/antagonists/hivemind/hivemind.dm
@@ -2,7 +2,8 @@
name = "Hivemind Host"
roundend_category = "hiveminds"
antagpanel_category = "Hivemind Host"
- job_rank = ROLE_HIVE
+ banning_key = ROLE_HIVE
+ required_living_playtime = 4
antag_moodlet = /datum/mood_event/hivehost
var/special_role = ROLE_HIVE
var/list/hivemembers = list()
@@ -156,7 +157,7 @@
/datum/antagonist/hivemind/on_gain()
- owner.special_role = special_role
+ owner.special_role = ROLE_HIVE
GLOB.hivehosts += src
generate_flavour()
create_actions()
diff --git a/code/modules/antagonists/hivemind/vessel.dm b/code/modules/antagonists/hivemind/vessel.dm
index 2fad52867b8..393417f79d7 100644
--- a/code/modules/antagonists/hivemind/vessel.dm
+++ b/code/modules/antagonists/hivemind/vessel.dm
@@ -2,7 +2,7 @@
/datum/antagonist/hivevessel
name = "Awoken Vessel"
- job_rank = ROLE_BRAINWASHED
+ banning_key = ROLE_HIVE_VESSEL
roundend_category = "awoken vessels"
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
@@ -36,7 +36,7 @@
mind.remove_antag_datum(/datum/antagonist/brainwashed)
/datum/antagonist/hivevessel/on_gain()
- owner.special_role = special_role
+ owner.special_role = ROLE_HIVE_VESSEL
owner.AddSpell(fist)
..()
diff --git a/code/modules/antagonists/incursion/incursion.dm b/code/modules/antagonists/incursion/incursion.dm
index d9ccbb95d0e..5358e20ac0e 100644
--- a/code/modules/antagonists/incursion/incursion.dm
+++ b/code/modules/antagonists/incursion/incursion.dm
@@ -1,8 +1,8 @@
/datum/antagonist/incursion
name = "Syndicate Incursion Member"
antagpanel_category = "Incursion"
- job_rank = ROLE_INCURSION
- var/special_role = ROLE_INCURSION
+ banning_key = ROLE_INCURSION
+ required_living_playtime = 4
var/datum/team/incursion/team
antag_moodlet = /datum/mood_event/focused
hijack_speed = 0.5
@@ -22,7 +22,7 @@
for(var/datum/objective/O in team.objectives)
objectives += O
log_objective(owner, O.explanation_text)
- owner.special_role = special_role
+ owner.special_role = ROLE_INCURSION
finalize_incursion()
return ..()
@@ -198,15 +198,15 @@
/datum/team/incursion/proc/generate_traitor_kill_objective(list/restricted_jobs)
//Spawn someone as a traitor
- var/list/datum/mind/people = SSticker.mode.get_alive_non_antagonsist_players_for_role(ROLE_EXCOMM, restricted_jobs)
+ var/list/datum/mind/people = SSticker.mode.get_alive_non_antagonsist_players_for_role(/datum/antagonist/traitor, /datum/role_preference/antagonist/excommunicate, restricted_jobs)
if(!LAZYLEN(people))
log_game("Not enough players for incursion role. [LAZYLEN(people)]")
return
- var/datum/mind/target = SSticker.mode.antag_pick(people, ROLE_EXCOMM)
+ var/datum/mind/target = SSticker.mode.antag_pick(people, /datum/role_preference/antagonist/excommunicate)
if(!target)
log_game("No mind selected.")
return
- target.make_Traitor()
+ target.add_antag_datum(/datum/antagonist/traitor/excommunicate)
to_chat(target, "
You have been declared an ex-communicate of the syndicate and are being hunted down. ")
to_chat(target, "
You have stolen syndicate objective documents, complete the objectives to throw off the syndicate and sabotage their efforts. ")
target.store_memory("You have been declared an ex-communicate of the syndicate and are being hunted down by a group of traitors. Be careful!")
diff --git a/code/modules/antagonists/magic_servant/servant.dm b/code/modules/antagonists/magic_servant/servant.dm
index 3277991446c..9717e869475 100644
--- a/code/modules/antagonists/magic_servant/servant.dm
+++ b/code/modules/antagonists/magic_servant/servant.dm
@@ -3,6 +3,7 @@
show_in_roundend = FALSE
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ banning_key = ROLE_WIZARD
/datum/antagonist/magic_servant/proc/setup_master(mob/M)
var/datum/objective/O = new("Serve [M.real_name].")
diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm
index 7aa8db699f0..e88a891f1b1 100644
--- a/code/modules/antagonists/morph/morph.dm
+++ b/code/modules/antagonists/morph/morph.dm
@@ -276,7 +276,7 @@
role_name = "morphling"
/datum/round_event/ghost_role/morph/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
+ var/list/candidates = get_candidates(ROLE_MORPH, /datum/role_preference/midround_ghost/morph)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/antagonists/morph/morph_antag.dm b/code/modules/antagonists/morph/morph_antag.dm
index 7b0491e5de5..c648a24fdea 100644
--- a/code/modules/antagonists/morph/morph_antag.dm
+++ b/code/modules/antagonists/morph/morph_antag.dm
@@ -2,5 +2,6 @@
name = "Morph"
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
+ banning_key = ROLE_MORPH
//It does nothing! (Besides tracking)
diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm
index 1739e249510..59ddb46b6f4 100644
--- a/code/modules/antagonists/nightmare/nightmare.dm
+++ b/code/modules/antagonists/nightmare/nightmare.dm
@@ -1,5 +1,6 @@
/datum/antagonist/nightmare
name = "Nightmare"
+ banning_key = ROLE_NIGHTMARE
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm
index 03b0f9042c8..b697e6684bd 100644
--- a/code/modules/antagonists/ninja/ninja.dm
+++ b/code/modules/antagonists/ninja/ninja.dm
@@ -1,7 +1,7 @@
/datum/antagonist/ninja
name = "Ninja"
antagpanel_category = "Ninja"
- job_rank = ROLE_NINJA
+ banning_key = ROLE_NINJA
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 52accf3923d..8126e12ff1c 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -2,7 +2,8 @@
name = "Nuclear Operative"
roundend_category = "syndicate operatives" //just in case
antagpanel_category = "NukeOp"
- job_rank = ROLE_OPERATIVE
+ banning_key = ROLE_OPERATIVE
+ required_living_playtime = 8
antag_moodlet = /datum/mood_event/focused
show_to_ghosts = TRUE
hijack_speed = 2 //If you can't take out the station, take the shuttle instead.
@@ -144,7 +145,7 @@
nuke_team = new_team
/datum/antagonist/nukeop/admin_add(datum/mind/new_owner,mob/admin)
- new_owner.assigned_role = ROLE_SYNDICATE
+ new_owner.assigned_role = ROLE_OPERATIVE
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has nuke op'ed [key_name_admin(new_owner)].")
log_admin("[key_name(admin)] has nuke op'ed [key_name(new_owner)].")
diff --git a/code/modules/antagonists/official/official.dm b/code/modules/antagonists/official/official.dm
index a35ef429734..d1d73bc79b7 100644
--- a/code/modules/antagonists/official/official.dm
+++ b/code/modules/antagonists/official/official.dm
@@ -6,6 +6,7 @@
var/datum/objective/mission
var/datum/team/ert/ert_team
show_to_ghosts = TRUE
+ banning_key = ROLE_ERT
/datum/antagonist/official/greet()
to_chat(owner, "
You are a CentCom Official. ")
diff --git a/code/modules/antagonists/overthrow/overthrow.dm b/code/modules/antagonists/overthrow/overthrow.dm
index eeb25929891..da118ce87b4 100644
--- a/code/modules/antagonists/overthrow/overthrow.dm
+++ b/code/modules/antagonists/overthrow/overthrow.dm
@@ -9,7 +9,7 @@
name = "Syndicate mutineer"
roundend_category = "syndicate mutineers"
antagpanel_category = "Syndicate Mutineers"
- job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings
+ banning_key = ROLE_OVERTHROW
var/datum/team/overthrow/team
var/static/list/possible_useful_items
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index b8fbd18c70e..5804e64f448 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -1,6 +1,6 @@
/datum/antagonist/pirate
name = "Space Pirate"
- job_rank = ROLE_TRAITOR
+ banning_key = ROLE_SPACE_PIRATE
roundend_category = "space pirates"
antagpanel_category = "Pirate"
show_to_ghosts = TRUE
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 803d8fd916c..02370743481 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -438,7 +438,7 @@
break
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
- var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, null, ROLE_REVENANT, 50, revenant)
+ var/list/candidates = pollCandidatesForMob("Do you want to be [revenant.name] (reforming)?", ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant, 7.5 SECONDS, revenant)
if(!LAZYLEN(candidates))
qdel(revenant)
message_admins("No candidates were found for the new revenant. Oh well!")
diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm
index 169d23d25c0..2c9aa4d8a87 100644
--- a/code/modules/antagonists/revenant/revenant_antag.dm
+++ b/code/modules/antagonists/revenant/revenant_antag.dm
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ banning_key = ROLE_REVENANT
/datum/antagonist/revenant/greet()
owner.announce_objectives()
diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm
index 10ee621a9b8..54368fd8721 100644
--- a/code/modules/antagonists/revenant/revenant_spawn_event.dm
+++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm
@@ -26,7 +26,7 @@
message_admins("Event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.")
return WAITING_FOR_SOMETHING
- var/list/candidates = get_candidates(ROLE_REVENANT, null, ROLE_REVENANT)
+ var/list/candidates = get_candidates(ROLE_REVENANT, /datum/role_preference/midround_ghost/revenant)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index d5dce9e21ef..17591952189 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -7,7 +7,7 @@
name = "Revolutionary"
roundend_category = "revolutionaries" // if by some miracle revolutionaries without revolution happen
antagpanel_category = "Revolution"
- job_rank = ROLE_REV
+ banning_key = ROLE_REV
antag_moodlet = /datum/mood_event/revolution
var/hud_type = "rev"
var/datum/team/revolution/rev_team
@@ -159,6 +159,8 @@
/datum/antagonist/rev/head
name = "Head Revolutionary"
hud_type = "rev_head"
+ banning_key = ROLE_REV_HEAD
+ required_living_playtime = 4
var/remove_clumsy = FALSE
var/give_flash = FALSE
var/give_hud = TRUE
@@ -289,6 +291,7 @@
/datum/antagonist/revolution_enemy
name = "Enemy of the Revolution"
show_in_antagpanel = FALSE
+ banning_key = UNBANNABLE_ANTAGONIST
/datum/antagonist/revolution_enemy/on_gain()
owner.special_role = "revolution enemy"
@@ -342,7 +345,7 @@
var/list/datum/mind/nonhuman_promotable = list()
for(var/datum/mind/khrushchev in non_heads)
if(khrushchev.current && !khrushchev.current.incapacitated() && !khrushchev.current.restrained() && khrushchev.current.client && khrushchev.current.stat != DEAD)
- if(ROLE_REV in khrushchev.current.client.prefs.be_special)
+ if(khrushchev.current.client.should_include_for_role(ROLE_REV_HEAD, /datum/role_preference/antagonist/revolutionary))
if(ishuman(khrushchev.current))
promotable += khrushchev
else
diff --git a/code/modules/antagonists/role_preference/_role_preference.dm b/code/modules/antagonists/role_preference/_role_preference.dm
new file mode 100644
index 00000000000..710ce5d2430
--- /dev/null
+++ b/code/modules/antagonists/role_preference/_role_preference.dm
@@ -0,0 +1,27 @@
+/datum/role_preference
+ var/name
+ /// What heading to display this entry under in the preferences menu. Use ROLE_PREFERENCE_CATEGORY defines.
+ var/category
+ /// The Antagonist datum typepath for this entry, if there is one. Used to get data about the role for display (bans etc)
+ var/datum/antagonist/antag_datum
+ /// The base abstract path for this subtype.
+ var/abstract_type = /datum/role_preference
+ /// If this preference can vary between characters.
+ var/per_character = FALSE
+
+/// Includes latejoin and roundstart antagonists
+/datum/role_preference/antagonist
+ category = ROLE_PREFERENCE_CATEGORY_ANAGONIST
+ abstract_type = /datum/role_preference/antagonist
+ per_character = TRUE
+
+/// Includes autotraitor and gamemode midround assignments - being forced into an antagonist during a round (does not apply to conversion antags).
+/datum/role_preference/midround_living
+ category = ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING
+ abstract_type = /datum/role_preference/midround_living
+ per_character = TRUE
+
+/// Includes anything polled from ghosts that does antagonist stuff
+/datum/role_preference/midround_ghost
+ category = ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST
+ abstract_type = /datum/role_preference/midround_ghost
diff --git a/code/modules/antagonists/role_preference/role_antagonists.dm b/code/modules/antagonists/role_preference/role_antagonists.dm
new file mode 100644
index 00000000000..b952e5ad233
--- /dev/null
+++ b/code/modules/antagonists/role_preference/role_antagonists.dm
@@ -0,0 +1,43 @@
+/datum/role_preference/antagonist/blood_brother
+ name = "Blood Brother"
+ antag_datum = /datum/antagonist/brother
+
+/datum/role_preference/antagonist/blood_cultist
+ name = "Blood Cultist"
+ antag_datum = /datum/antagonist/cult
+
+/datum/role_preference/antagonist/clock_cultist
+ name = "Clock Cultist"
+ antag_datum = /datum/antagonist/servant_of_ratvar
+
+/datum/role_preference/antagonist/devil
+ name = "Devil"
+ antag_datum = /datum/antagonist/devil
+
+/datum/role_preference/antagonist/revolutionary
+ name = "Head Revolutionary"
+ antag_datum = /datum/antagonist/rev/head
+
+/datum/role_preference/antagonist/heretic
+ name = "Heretic"
+ antag_datum = /datum/antagonist/heretic
+
+/datum/role_preference/antagonist/hivemind_host
+ name = "Hivemind Host"
+ antag_datum = /datum/antagonist/hivemind
+
+/datum/role_preference/antagonist/incursionist
+ name = "Incursionist"
+ antag_datum = /datum/antagonist/incursion
+
+/datum/role_preference/antagonist/excommunicate
+ name = "Excommunicated Syndicate Agent"
+ antag_datum = /datum/antagonist/traitor/excommunicate
+
+/datum/role_preference/antagonist/gangster
+ name = "Gangster"
+ antag_datum = /datum/antagonist/gang
+
+/datum/role_preference/antagonist/internal_affairs
+ name = "Internal Affairs Agent"
+ antag_datum = /datum/antagonist/traitor/internal_affairs
diff --git a/code/modules/antagonists/role_preference/role_changeling.dm b/code/modules/antagonists/role_preference/role_changeling.dm
new file mode 100644
index 00000000000..cf0399b0bcf
--- /dev/null
+++ b/code/modules/antagonists/role_preference/role_changeling.dm
@@ -0,0 +1,3 @@
+/datum/role_preference/antagonist/changeling
+ name = "Changeling"
+ antag_datum = /datum/antagonist/changeling
diff --git a/code/modules/antagonists/role_preference/role_midrounds.dm b/code/modules/antagonists/role_preference/role_midrounds.dm
new file mode 100644
index 00000000000..77a4c08d798
--- /dev/null
+++ b/code/modules/antagonists/role_preference/role_midrounds.dm
@@ -0,0 +1,69 @@
+/datum/role_preference/midround_ghost/blob
+ name = "Blob"
+ antag_datum = /datum/antagonist/blob
+
+/datum/role_preference/midround_ghost/xenomorph
+ name = "Xenomorph"
+ antag_datum = /datum/antagonist/xeno
+
+/datum/role_preference/midround_ghost/nightmare
+ name = "Nightmare"
+ antag_datum = /datum/antagonist/nightmare
+
+/datum/role_preference/midround_ghost/space_dragon
+ name = "Space Dragon"
+ antag_datum = /datum/antagonist/space_dragon
+
+/datum/role_preference/midround_ghost/abductor
+ name = "Abductor"
+ antag_datum = /datum/antagonist/abductor
+
+/datum/role_preference/midround_ghost/space_pirate
+ name = "Space Pirate"
+ antag_datum = /datum/antagonist/pirate
+
+/datum/role_preference/midround_ghost/revenant
+ name = "Revenant"
+ antag_datum = /datum/antagonist/revenant
+
+/* NSV13 - Disabled because we don't have this ported yet.
+/datum/role_preference/midround_ghost/spider
+ name = "Spider"
+ antag_datum = /datum/antagonist/spider
+
+/datum/role_preference/midround_ghost/swarmer
+ name = "Swarmer"
+ antag_datum = /datum/antagonist/swarmer
+*/
+
+/datum/role_preference/midround_ghost/morph
+ name = "Morph"
+ antag_datum = /datum/antagonist/morph
+
+/datum/role_preference/midround_ghost/fugitive
+ name = "Fugitive"
+ antag_datum = /datum/antagonist/fugitive
+
+/datum/role_preference/midround_ghost/fugitive_hunter
+ name = "Fugitive Hunter"
+ antag_datum = /datum/antagonist/fugitive_hunter
+
+/datum/role_preference/midround_ghost/slaughter_demon
+ name = "Slaughter Demon"
+ antag_datum = /datum/antagonist/slaughter
+
+/datum/role_preference/midround_ghost/devil
+ name = "Devil (Midround)"
+ antag_datum = /datum/antagonist/devil
+
+/datum/role_preference/midround_ghost/ninja
+ name = "Ninja"
+ antag_datum = /datum/antagonist/ninja
+
+/datum/role_preference/midround_living/malfunctioning_ai
+ name = "Malfunctioning AI"
+ antag_datum = /datum/antagonist/traitor
+
+/datum/role_preference/midround_living/obsessed
+ name = "Obsessed"
+ antag_datum = /datum/antagonist/obsessed
diff --git a/code/modules/antagonists/role_preference/role_operative.dm b/code/modules/antagonists/role_preference/role_operative.dm
new file mode 100644
index 00000000000..6a4fe7a5b9e
--- /dev/null
+++ b/code/modules/antagonists/role_preference/role_operative.dm
@@ -0,0 +1,7 @@
+/datum/role_preference/antagonist/nuclear_operative
+ name = "Nuclear Operative"
+ antag_datum = /datum/antagonist/nukeop
+
+/datum/role_preference/midround_ghost/nuclear_operative
+ name = "Nuclear Operative (Midround)"
+ antag_datum = /datum/antagonist/nukeop
diff --git a/code/modules/antagonists/role_preference/role_traitor.dm b/code/modules/antagonists/role_preference/role_traitor.dm
new file mode 100644
index 00000000000..46d11945a14
--- /dev/null
+++ b/code/modules/antagonists/role_preference/role_traitor.dm
@@ -0,0 +1,7 @@
+/datum/role_preference/antagonist/traitor
+ name = "Traitor"
+ antag_datum = /datum/antagonist/traitor
+
+/datum/role_preference/midround_living/traitor
+ name = "Traitor (Sleeper Agent)"
+ antag_datum = /datum/antagonist/traitor
diff --git a/code/modules/antagonists/role_preference/role_wizard.dm b/code/modules/antagonists/role_preference/role_wizard.dm
new file mode 100644
index 00000000000..31681493a58
--- /dev/null
+++ b/code/modules/antagonists/role_preference/role_wizard.dm
@@ -0,0 +1,7 @@
+/datum/role_preference/antagonist/wizard
+ name = "Wizard"
+ antag_datum = /datum/antagonist/wizard
+
+/datum/role_preference/midround_ghost/wizard
+ name = "Wizard (Midround)"
+ antag_datum = /datum/antagonist/wizard
diff --git a/code/modules/antagonists/roundstart_special/special_antagonist.dm b/code/modules/antagonists/roundstart_special/special_antagonist.dm
index 7d39652e9bf..603623e4830 100644
--- a/code/modules/antagonists/roundstart_special/special_antagonist.dm
+++ b/code/modules/antagonists/roundstart_special/special_antagonist.dm
@@ -24,8 +24,10 @@
var/max_occurrences = 1
var/holidayID = ""
//Preferences
- var/preference_type = ROLE_TRAITOR
- var/special_role_flag = null //Will use antag rep if enabled
+ var/preference_type = null
+ /// If we should use antag rep. Do note that having a preference_type enables checking during gamemode execution.
+ var/use_antag_rep = TRUE
+ var/banning_key = ROLE_TRAITOR
/datum/special_role/proc/setup()
if(CONFIG_GET(flag/protect_roles_from_antagonist))
@@ -46,6 +48,7 @@
E.antagonist_datum = attached_antag_datum
E.antag_name = role_name
E.preference_type = preference_type
+ E.banning_key = banning_key
E.protected_jobs = restricted_jobs
E.typepath = /datum/round_event/create_special_antag
E.weight = weight
@@ -72,8 +75,8 @@
//The datum associated with the role
/datum/antagonist/special
- name = "Role that should not be accessable in game."
- job_rank = ROLE_SYNDICATE
+ name = "Role that should not be accessible in game."
+ banning_key = BAN_ROLE_ALL_ANTAGONISTS
show_in_antagpanel = FALSE
show_name_in_check_antagonists = FALSE
prevent_roundtype_conversion = FALSE
diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm
index fb1d304f8bb..563e89059cb 100644
--- a/code/modules/antagonists/santa/santa.dm
+++ b/code/modules/antagonists/santa/santa.dm
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
+ banning_key = UNBANNABLE_ANTAGONIST
/datum/antagonist/santa/on_gain()
. = ..()
diff --git a/code/modules/antagonists/separatist/separatist.dm b/code/modules/antagonists/separatist/separatist.dm
index 20a6d84bdec..cc9a84836e6 100644
--- a/code/modules/antagonists/separatist/separatist.dm
+++ b/code/modules/antagonists/separatist/separatist.dm
@@ -6,6 +6,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
var/datum/team/nation/nation
+ banning_key = BAN_ROLE_ALL_ANTAGONISTS
/datum/antagonist/separatist/create_team(datum/team/nation/new_team)
if(!new_team)
diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/slaughter/slaughter_antag.dm
index 9f80462f578..557b2ec5c49 100644
--- a/code/modules/antagonists/slaughter/slaughter_antag.dm
+++ b/code/modules/antagonists/slaughter/slaughter_antag.dm
@@ -3,7 +3,7 @@
show_name_in_check_antagonists = TRUE
var/objective_verb = "Kill"
var/datum/mind/summoner
- job_rank = ROLE_ALIEN
+ banning_key = ROLE_SLAUGHTER_DEMON
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm
index 8ad463763ce..9c2fdaf3d13 100644
--- a/code/modules/antagonists/slaughter/slaughterevent.dm
+++ b/code/modules/antagonists/slaughter/slaughterevent.dm
@@ -14,7 +14,7 @@
role_name = "slaughter demon"
/datum/round_event/ghost_role/slaughter/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
+ var/list/candidates = get_candidates(ROLE_SLAUGHTER_DEMON, null)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm
index 5cc275660d3..68ad2195fab 100644
--- a/code/modules/antagonists/space_dragon/space_dragon.dm
+++ b/code/modules/antagonists/space_dragon/space_dragon.dm
@@ -1,5 +1,6 @@
/datum/antagonist/space_dragon
name = "Space Dragon"
+ banning_key = ROLE_SPACE_DRAGON
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm
index 4c36b4e1354..819d8c07fd0 100644
--- a/code/modules/antagonists/survivalist/survivalist.dm
+++ b/code/modules/antagonists/survivalist/survivalist.dm
@@ -2,6 +2,7 @@
name = "Survivalist"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ banning_key = ROLE_SURVIVALIST
var/greet_message = ""
/datum/antagonist/survivalist/proc/forge_objectives()
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 506f2695465..02396b7388b 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -5,7 +5,8 @@
name = "Traitor"
roundend_category = "traitors"
antagpanel_category = "Traitor"
- job_rank = ROLE_TRAITOR
+ banning_key = ROLE_TRAITOR
+ required_living_playtime = 4
antag_moodlet = /datum/mood_event/focused
hijack_speed = 0.5 //10 seconds per hijack stage by default
var/special_role = ROLE_TRAITOR
@@ -435,3 +436,8 @@
/datum/antagonist/traitor/is_gamemode_hero()
return SSticker.mode.name == "traitor"
+
+/datum/antagonist/traitor/excommunicate
+ name = "Excommunicate Traitor"
+ banning_key = ROLE_EXCOMM
+ special_role = ROLE_EXCOMM
diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm
index 7827bf9f394..b97d5ad70c9 100644
--- a/code/modules/antagonists/traitor/equipment/contractor.dm
+++ b/code/modules/antagonists/traitor/equipment/contractor.dm
@@ -165,7 +165,7 @@
if (.)
to_chat(user, "
The uplink vibrates quietly, connecting to nearby agents... ")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_CONTRACTOR_SUPPORT_UNIT, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/modules/antagonists/traitor/traitor_spawner.dm b/code/modules/antagonists/traitor/traitor_spawner.dm
index 7d42fa8fc4f..f3c31485d74 100644
--- a/code/modules/antagonists/traitor/traitor_spawner.dm
+++ b/code/modules/antagonists/traitor/traitor_spawner.dm
@@ -13,9 +13,9 @@
allowAntagTargets = TRUE
latejoin_allowed = TRUE
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN)
-
- special_role_flag = ROLE_TRAITOR
- role_name = ROLE_TRAITOR
+ role_name = "Traitor"
+ preference_type = /datum/role_preference/antagonist/traitor
+ use_antag_rep = TRUE
var/traitors_possible = 4 //hard limit on traitors if scaling is turned off
diff --git a/code/modules/antagonists/valentines/heartbreaker.dm b/code/modules/antagonists/valentines/heartbreaker.dm
index 39ffdfa1496..51e80761140 100644
--- a/code/modules/antagonists/valentines/heartbreaker.dm
+++ b/code/modules/antagonists/valentines/heartbreaker.dm
@@ -3,6 +3,7 @@
roundend_category = "valentines"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ banning_key = BAN_ROLE_ALL_ANTAGONISTS
/datum/antagonist/heartbreaker/proc/forge_objectives()
diff --git a/code/modules/antagonists/valentines/valentine.dm b/code/modules/antagonists/valentines/valentine.dm
index df695fadce5..cc1f81ed1bf 100644
--- a/code/modules/antagonists/valentines/valentine.dm
+++ b/code/modules/antagonists/valentines/valentine.dm
@@ -5,6 +5,7 @@
prevent_roundtype_conversion = FALSE
var/datum/mind/date
count_against_dynamic_roll_chance = FALSE
+ banning_key = UNBANNABLE_ANTAGONIST
/datum/antagonist/valentine/proc/forge_objectives()
var/datum/objective/protect/protect_objective = new /datum/objective/protect
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 497bf49b00d..960d49bdc46 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_elimination_hijack = ELIMINATION_ENABLED
+ banning_key = BAN_ROLE_ALL_ANTAGONISTS
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/elimination/highlander/elimination_objective = new
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index af5087b5c84..6909768dca2 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -338,7 +338,7 @@
break
if(!chosen_ghost) //Failing that, we grab a ghost
- var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, 50, POLL_IGNORE_SHADE)
+ var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", ROLE_CULTIST, null, 5 SECONDS, ignore_category = POLL_IGNORE_CULT_SHADE)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index b08b67b13c7..46cbfb3c857 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -2,7 +2,8 @@
name = "Space Wizard"
roundend_category = "wizards/witches"
antagpanel_category = "Wizard"
- job_rank = ROLE_WIZARD
+ banning_key = ROLE_WIZARD
+ required_living_playtime = 8
antag_moodlet = /datum/mood_event/focused
hijack_speed = 0.5
var/strip = TRUE //strip before equipping
@@ -182,12 +183,12 @@
/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard
- M.faction |= ROLE_WIZARD
+ M.faction |= FACTION_WIZARD
/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_removed(M)
- M.faction -= ROLE_WIZARD
+ M.faction -= FACTION_WIZARD
/datum/antagonist/wizard/get_admin_commands()
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index 7845ec7d225..dfabb8366e4 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -11,7 +11,7 @@
/datum/antagonist/xeno
name = "Xenomorph"
- job_rank = ROLE_ALIEN
+ banning_key = ROLE_ALIEN
show_in_antagpanel = FALSE
prevent_roundtype_conversion = FALSE
show_to_ghosts = TRUE
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index 45d5cc86506..281f873cc73 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -28,9 +28,11 @@
var/mob_color //Change the mob's color
var/assignedrole
var/show_flavour = TRUE
- var/banType = ROLE_LAVALAND
+ var/banType
var/ghost_usable = TRUE
var/use_cooldown = FALSE
+ /// If this should ignore admins disabling ghost roles (like lavaland roles), since it's actually an antagonist.
+ var/is_antagonist = FALSE
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/attack_ghost(mob/user)
@@ -42,6 +44,10 @@
if(!uses)
to_chat(user, "
This spawner is out of charges! ")
return
+ if(!SSticker.HasRoundStarted())
+ return
+ if(!user?.client?.can_take_ghost_spawner(banType, use_cooldown, is_ghost_role = !is_antagonist, is_admin_spawned = flags_1 & ADMIN_SPAWNED_1))
+ return
if(is_banned_from(user.key, banType))
to_chat(user, "
You are jobanned! ")
return
@@ -402,6 +408,17 @@
id_job = JOB_NAME_BARTENDER
use_cooldown = TRUE
+/obj/effect/mob_spawn/human/bartender/alive/beach
+ assignedrole = "Beach Bartender"
+ banType = ROLE_BEACH_BUM
+ outfit = /datum/outfit/spacebartender/beach
+
+/datum/outfit/spacebartender/beach/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ ..()
+ if(visualsOnly)
+ return
+ H.dna.add_mutation(STONER)
+
/datum/outfit/spacebartender
name = "Space Bartender"
uniform = /obj/item/clothing/under/rank/civilian/bartender
@@ -434,6 +451,7 @@
flavour_text = "Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?"
assignedrole = "Beach Bum"
use_cooldown = TRUE
+ banType = ROLE_BEACH_BUM
/obj/effect/mob_spawn/human/beach/alive/lifeguard
short_desc = "You're a spunky lifeguard!"
@@ -512,29 +530,6 @@
back = /obj/item/storage/backpack/security
id = /obj/item/card/id/job/security_officer
-
-/obj/effect/mob_spawn/human/commander/alive
- death = FALSE
- roundstart = FALSE
- mob_name = "\improper Nanotrasen Commander"
- name = "sleeper"
- icon = 'icons/obj/machines/sleeper.dmi'
- icon_state = "sleeper"
- short_desc = "You are a Nanotrasen Commander!"
- use_cooldown = TRUE
-
-/obj/effect/mob_spawn/human/nanotrasensoldier/alive
- death = FALSE
- roundstart = FALSE
- mob_name = "Private Security Officer"
- name = "sleeper"
- icon = 'icons/obj/machines/sleeper.dmi'
- icon_state = "sleeper"
- faction = "nanotrasenprivate"
- short_desc = "You are a Nanotrasen Private Security Officer!"
- use_cooldown = TRUE
-
-
/////////////////Spooky Undead//////////////////////
/obj/effect/mob_spawn/human/skeleton
@@ -549,30 +544,16 @@
icon = 'icons/effects/blood.dmi'
icon_state = "remains"
short_desc = "By unknown powers, your skeletal remains have been reanimated!"
- flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path."
+ flavour_text = "Walk this mortal plane and terrorize all living adventurers who dare cross your path."
assignedrole = "Skeleton"
use_cooldown = TRUE
+ banType = ROLE_SKELETAL_REMAINS
/obj/effect/mob_spawn/human/skeleton/alive/equip(mob/living/carbon/human/H)
var/obj/item/implant/exile/implant = new/obj/item/implant/exile(H)
implant.implant(H)
H.set_species(/datum/species/skeleton)
-/obj/effect/mob_spawn/human/zombie
- name = "rotting corpse"
- mob_name = "zombie"
- mob_species = /datum/species/zombie
- assignedrole = "Zombie"
-
-/obj/effect/mob_spawn/human/zombie/alive
- death = FALSE
- roundstart = FALSE
- icon = 'icons/effects/blood.dmi'
- icon_state = "remains"
- short_desc = "By unknown powers, your rotting remains have been resurrected!"
- flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path."
- use_cooldown = TRUE
-
/obj/effect/mob_spawn/human/abductor
name = "abductor"
mob_name = "alien"
@@ -584,25 +565,6 @@
uniform = /obj/item/clothing/under/color/grey
shoes = /obj/item/clothing/shoes/combat
-
-//For ghost bar.
-/obj/effect/mob_spawn/human/alive/space_bar_patron
- name = "Bar cryogenics"
- mob_name = "Bar patron"
- random = TRUE
- permanent = TRUE
- uses = -1
- outfit = /datum/outfit/spacebartender
- assignedrole = "Space Bar Patron"
-
-//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user)
- var/despawn = alert("Return to cryosleep? (Warning, Your mob will be deleted!)",,"Yes","No")
- if(despawn != "Yes" || !loc || !Adjacent(user))
- return
- user.visible_message("
[user.name] climbs back into cryosleep... ")
- qdel(user)
-
/datum/outfit/cryobartender
name = "Cryogenic Bartender"
uniform = /obj/item/clothing/under/rank/civilian/bartender
diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm
index 724aa39b410..f62053e356d 100644
--- a/code/modules/awaymissions/mission_code/Academy.dm
+++ b/code/modules/awaymissions/mission_code/Academy.dm
@@ -90,7 +90,7 @@
var/mob/living/current_wizard = null
var/next_check = 0
var/cooldown = 600
- var/faction = ROLE_WIZARD
+ var/faction = FACTION_WIZARD
var/braindead_check = 0
/obj/structure/academy_wizard_spawner/New()
@@ -125,7 +125,7 @@
if(!current_wizard)
return
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, null, ROLE_WIZARD, 50, current_wizard)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 10 SECONDS, current_wizard)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
@@ -133,6 +133,7 @@
current_wizard.ghostize(FALSE) // on the off chance braindead defender gets back in
current_wizard.key = C.key
else
+ current_wizard.playable_bantype = ROLE_WIZARD
current_wizard.ghostize(FALSE,SENTIENCE_FORCE)
/obj/structure/academy_wizard_spawner/proc/summon_wizard()
@@ -313,7 +314,7 @@
A.setup_master(user)
servant_mind.transfer_to(H)
- var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, null, ROLE_WIZARD, 50, H)
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [user.real_name] Servant?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, 10 SECONDS, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm
index 12c18edec71..1eabeb89289 100644
--- a/code/modules/awaymissions/mission_code/snowdin.dm
+++ b/code/modules/awaymissions/mission_code/snowdin.dm
@@ -577,7 +577,7 @@
icon_state = "sleeper"
roundstart = FALSE
death = FALSE
- faction = ROLE_SYNDICATE
+ faction = FACTION_SYNDICATE
outfit = /datum/outfit/snowsyndie
short_desc = "You are a syndicate operative recently awoken from cryostasis in an underground outpost."
flavour_text = "You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 401e4131f8b..1acd467413c 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -24,10 +24,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/tip_delay = 500 //tip delay in milliseconds
//Antag preferences
- var/list/be_special = list() //Special role selection
- var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more
- //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were,
- //autocorrected this round, not that you'd need to check that.
+ var/list/role_preferences = list() //Special role selection
var/UI_style = null
var/outline_color = COLOR_BLUE_GRAY
@@ -135,11 +132,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/list/dat = list("
")
dat += "Character Settings "
+ dat += "Antagonist Preferences "
dat += "Game Preferences "
var/shop_name = "[CONFIG_GET(string/metacurrency_name)] Shop"
dat += "[shop_name] "
dat += "OOC Preferences "
- dat += "Roleplay Settings " //NSV13 - Roleplay Tab
+ dat += "Roleplay Settings " //NSV13 - Roleplay Tab
dat += " "
@@ -598,7 +596,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if (1) // Game Preferences
- dat += "
"
+ dat += ""
- for (var/i in GLOB.special_roles)
- if(is_banned_from(user.ckey, i))
- dat += "Be [capitalize(i)]: BANNED "
+ if(4) // antagonist preferences window
+ dat += ""
+ var/name
+ var/unspaced_slots = 0
+ for(var/datum/character_save/CS as anything in character_saves)
+ unspaced_slots++
+ if(unspaced_slots > 4)
+ dat += " "
+ unspaced_slots = 0
+ name = CS.real_name
+ if(!name)
+ name = "Character [CS.slot_number]"
+ if(CS.slot_locked)
+ dat += "[name] (Locked) "
else
- var/days_remaining = null
- if(ispath(GLOB.special_roles[i]) && CONFIG_GET(flag/use_age_restriction_for_jobs)) //If it's a game mode antag, check if the player meets the minimum age
- var/mode_path = GLOB.special_roles[i]
- var/datum/game_mode/temp_mode = new mode_path
- days_remaining = temp_mode.get_remaining_days(user.client)
-
- if(days_remaining)
- dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS] "
- else
- dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"] "
- dat += " "
- dat += "Midround Antagonist: [(toggles & PREFTOGGLE_MIDROUND_ANTAG) ? "Enabled" : "Disabled"] "
+ dat += "[name] "
+ dat += " "
+ dat += ""
+ //
+ dat += ""
+ // --------------------------------------------
+ // warning pannel
+ var/ban_antagonists = is_banned_from(parent.ckey, BAN_ROLE_ALL_ANTAGONISTS)
+ var/ban_forced_antagonists = is_banned_from(parent.ckey, BAN_ROLE_FORCED_ANTAGONISTS)
+ var/ban_ghost = is_banned_from(parent.ckey, BAN_ROLE_ALL_GHOST)
+ if(ban_antagonists || ban_forced_antagonists || ban_ghost)
+ dat += "Notification "
+ if(ban_antagonists)
+ dat += "You are banned from all antagonist roles. \
+ Show Info "
+ if(ban_forced_antagonists)
+ dat += "You are banned from all forced antagonist roles (such as brainwashing). \
+ Show Info "
+ if(ban_ghost)
+ dat += "You are banned from all non-antagonist ghost roles. \
+ Show Info "
+ // --------------------------------------------
+ // Antagonist roles
+ dat += "Antagonists "
+ for (var/typepath in GLOB.role_preference_entries)
+ var/datum/role_preference/pref = GLOB.role_preference_entries[typepath]
+ if(pref.category != ROLE_PREFERENCE_CATEGORY_ANAGONIST)
+ continue
+ var/ban_key = initial(pref.antag_datum.banning_key)
+ if(is_banned_from(parent.ckey, ban_key))
+ dat += "[pref.name]: BANNED "
+ else
+ dat += "[pref.name] \
+ - Character: [parent.role_preference_enabled(typepath) ? "Enabled" : "Disabled"] \
+ - Global: Enable \
+ Disable "
+ dat += " "
+ // left box closed
+
+ //
+ // --------------------------------------------
+ // Midround antagonists + ghostspawn roles
+ dat += ""
+ dat += "Midrounds (Living) "
+ for (var/typepath in GLOB.role_preference_entries)
+ var/datum/role_preference/pref = GLOB.role_preference_entries[typepath]
+ if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_LIVING)
+ continue
+ var/ban_key = initial(pref.antag_datum.banning_key)
+ if(is_banned_from(parent.ckey, ban_key))
+ dat += "[pref.name]: BANNED "
+ else
+ dat += "[pref.name] \
+ - Character: [parent.role_preference_enabled(typepath) ? "Enabled" : "Disabled"] \
+ - Global: Enable \
+ Disable "
+ dat += "Midrounds (Ghost) "
+ for (var/typepath in GLOB.role_preference_entries)
+ var/datum/role_preference/pref = GLOB.role_preference_entries[typepath]
+ if(pref.category != ROLE_PREFERENCE_CATEGORY_MIDROUND_GHOST)
+ continue
+ var/ban_key = initial(pref.antag_datum.banning_key)
+ if(is_banned_from(parent.ckey, ban_key))
+ dat += "[pref.name]: BANNED "
+ else
+ dat += "[pref.name]: [parent.role_preference_enabled(typepath) ? "Enabled" : "Disabled"] "
+ dat += " "
+ // right box closed
- dat += " " // i hate myself for this
- dat += "Customize Keybinds "
- dat += "
"
+ dat += "
"
if(2) //Loadout
var/list/type_blacklist = list()
@@ -890,7 +956,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += ""
dat += ""
- if(4) //NSV13 - Roleplay Tab - Start
+ if(5)
+ dat += "
Character Settings " //NSV13 - Roleplay Tab - Start
dat += "
"
var/name
var/unspaced_slots = 0
@@ -2005,12 +2072,33 @@ GLOBAL_LIST_EMPTY(preferences_datums)
toggles ^= PREFTOGGLE_DEADMIN_POSITION_SILICON
- if("be_special")
- var/be_special_type = href_list["be_special_type"]
- if(be_special_type in be_special)
- be_special -= be_special_type
- else
- be_special += be_special_type
+ if("role_preferences")
+ var/role_preference_type = href_list["role_preference_type"]
+ var/role_preference_path = text2path(role_preference_type)
+ var/datum/role_preference/role_pref = GLOB.role_preference_entries[role_preference_path]
+ if(istype(role_pref))
+ var/list/prefsource = role_pref.per_character ? active_character.role_preferences_character : role_preferences
+ var/current = prefsource["[role_preference_type]"]
+ if(isnum(current))
+ prefsource["[role_preference_type]"] = !current
+ else // not set, we assume it's on, so turn it off.
+ prefsource["[role_preference_type]"] = FALSE
+
+ if("role_preferences_enableall")
+ var/role_preference_type = href_list["role_preference_type"]
+ var/role_preference_path = text2path(role_preference_type)
+ var/datum/role_preference/role_pref = GLOB.role_preference_entries[role_preference_path]
+ if(istype(role_pref) && role_pref.per_character)
+ for(var/datum/character_save/CS in character_saves)
+ CS.role_preferences_character["[role_preference_type]"] = TRUE
+
+ if("role_preferences_disableall")
+ var/role_preference_type = href_list["role_preference_type"]
+ var/role_preference_path = text2path(role_preference_type)
+ var/datum/role_preference/role_pref = GLOB.role_preference_entries[role_preference_path]
+ if(istype(role_pref) && role_pref.per_character)
+ for(var/datum/character_save/CS in character_saves)
+ CS.role_preferences_character["[role_preference_type]"] = FALSE
if("name")
active_character.be_random_name = !active_character.be_random_name
@@ -2052,9 +2140,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("pull_requests")
chat_toggles ^= CHAT_PULLR
- if("allow_midround_antag")
- toggles ^= PREFTOGGLE_MIDROUND_ANTAG
-
if("parallaxup")
parallax = WRAP(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1)
if (parent && parent.mob && parent.mob.hud_used)
diff --git a/code/modules/client/preferences2/character_save.dm b/code/modules/client/preferences2/character_save.dm
index 59fc38cad52..2647311ca59 100644
--- a/code/modules/client/preferences2/character_save.dm
+++ b/code/modules/client/preferences2/character_save.dm
@@ -71,6 +71,7 @@
var/list/equipped_gear = list()
var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants
var/uplink_spawn_loc = UPLINK_PDA
+ var/list/role_preferences_character = list()
//Nsv13 squads - we CM now
var/preferred_squad = "Able"
//NSV13 - Pilots
@@ -181,6 +182,11 @@
SAFE_READ_QUERY(39, medical_record)
//NSV13 - Stop
+ // Role prefs
+ var/role_preferences_character_tmp
+ SAFE_READ_QUERY(40, role_preferences_character_tmp) //NSV13 - Moved from 32 to 40 due to Roleplaying stuff
+ role_preferences_character = json_decode(role_preferences_character_tmp)
+
//Sanitize. Please dont put query reads below this point. Please.
@@ -261,6 +267,14 @@
job_preferences -= j
all_quirks = SANITIZE_LIST(all_quirks)
+ role_preferences_character = SANITIZE_LIST(role_preferences_character)
+ // Remove any invalid entries
+ for(var/preference in role_preferences_character)
+ var/path = text2path(preference)
+ var/datum/role_preference/entry = GLOB.role_preference_entries[path]
+ if(istype(entry) && entry.per_character)
+ continue
+ role_preferences_character -= preference
//NSV13 - Roleplay Stuff - Start
flavor_text = html_decode(strip_html(flavor_text))
@@ -373,7 +387,8 @@
silicon_flavor_text,
general_record,
security_record,
- medical_record
+ medical_record,
+ role_preferences
) VALUES (
:slot,
:ckey,
@@ -414,7 +429,8 @@
:silicon_flavor_text,
:general_record,
:security_record,
- :medical_record
+ :medical_record,
+ :role_preferences
)
"}, list(
// Now for the above but in a fucking monsterous list
@@ -457,7 +473,8 @@
"silicon_flavor_text" = silicon_flavor_text,
"general_record" = general_record,
"security_record" = security_record,
- "medical_record" = medical_record
+ "medical_record" = medical_record,
+ "role_preferences" = json_encode(role_preferences_character)
))
if(!insert_query.warn_execute())
diff --git a/code/modules/client/preferences2/preferences2.dm b/code/modules/client/preferences2/preferences2.dm
index a57181f360e..fe6967c60c5 100644
--- a/code/modules/client/preferences2/preferences2.dm
+++ b/code/modules/client/preferences2/preferences2.dm
@@ -84,7 +84,7 @@
READPREF_JSONDEC(ignoring, PREFERENCE_TAG_IGNORING)
READPREF_JSONDEC(key_bindings, PREFERENCE_TAG_KEYBINDS)
READPREF_JSONDEC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR)
- READPREF_JSONDEC(be_special, PREFERENCE_TAG_BE_SPECIAL)
+ READPREF_JSONDEC(role_preferences, PREFERENCE_TAG_ROLE_PREFERENCES)
//Sanitize
asaycolor = sanitize_ooccolor(sanitize_hexcolor(asaycolor, 6, TRUE, initial(asaycolor)))
@@ -104,7 +104,14 @@
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION)
ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION)
- be_special = SANITIZE_LIST(be_special)
+ role_preferences = SANITIZE_LIST(role_preferences)
+ // Remove any invalid entries
+ for(var/preference in role_preferences)
+ var/path = text2path(preference)
+ var/datum/role_preference/entry = GLOB.role_preference_entries[path]
+ if(istype(entry) && !entry.per_character)
+ continue
+ role_preferences -= preference
pda_theme = sanitize_inlist(pda_theme, GLOB.ntos_device_themes_default_content, initial(pda_theme))
pda_color = sanitize_hexcolor(pda_color, 6, TRUE, initial(pda_color))
@@ -166,7 +173,7 @@
PREP_WRITEPREF_JSONENC(ignoring, PREFERENCE_TAG_IGNORING)
PREP_WRITEPREF_JSONENC(key_bindings, PREFERENCE_TAG_KEYBINDS)
PREP_WRITEPREF_JSONENC(purchased_gear, PREFERENCE_TAG_PURCHASED_GEAR)
- PREP_WRITEPREF_JSONENC(be_special, PREFERENCE_TAG_BE_SPECIAL)
+ PREP_WRITEPREF_JSONENC(role_preferences, PREFERENCE_TAG_ROLE_PREFERENCES)
// QuerySelect can execute many queries at once. That name is dumb but w/e
SSdbcore.QuerySelect(write_queries, TRUE, TRUE)
@@ -220,7 +227,8 @@
silicon_flavor_text,
general_record,
security_record,
- medical_record
+ medical_record,
+ role_preferences
FROM [format_table_name("characters")] WHERE
ckey=:ckey
"}, list("ckey" = parent.ckey))
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index 6ca7eb5fad4..56f84d923e5 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -78,15 +78,6 @@
prefs.save_preferences()
SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Arrivalrattle", "[!(prefs.toggles & PREFTOGGLE_DISABLE_ARRIVALRATTLE) ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, maybe you should rethink where your life went so wrong.
-/client/verb/togglemidroundantag()
- set name = "Toggle Midround Antagonist"
- set category = "Preferences"
- set desc = "Midround Antagonist"
- prefs.toggles ^= PREFTOGGLE_MIDROUND_ANTAG
- prefs.save_preferences()
- to_chat(usr, "You will [(prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.")
- SSblackbox.record_feedback("nested tally", "preferences_verb", 1, list("Toggle Midround Antag", "[prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/client/verb/toggletitlemusic()
set name = "Hear/Silence Lobby Music"
set category = "Preferences"
diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm
index fa02a1aa264..bfd90cf8825 100644
--- a/code/modules/clothing/outfits/vr.dm
+++ b/code/modules/clothing/outfits/vr.dm
@@ -33,7 +33,7 @@
W.implant(H)
var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(H)
E.implant(H)
- H.faction |= ROLE_SYNDICATE
+ H.faction |= FACTION_SYNDICATE
H.update_icons()
/obj/item/paper/fluff/vr/fluke_ops
diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm
index b089c96b5f5..e521dc6d02d 100755
--- a/code/modules/events/abductor.dm
+++ b/code/modules/events/abductor.dm
@@ -15,7 +15,7 @@
fakeable = FALSE //Nothing to fake here
/datum/round_event/ghost_role/abductor/spawn_role()
- var/list/mob/dead/observer/candidates = get_candidates(ROLE_ABDUCTOR, null, ROLE_ABDUCTOR)
+ var/list/mob/dead/observer/candidates = get_candidates(ROLE_ABDUCTOR, /datum/role_preference/midround_ghost/abductor)
if(candidates.len < 2)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index 3ff8a552a02..b3579514588 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -62,7 +62,7 @@
message_admins("An event attempted to spawn an alien but no suitable vents were found. Shutting down.")
return MAP_ERROR
- var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
+ var/list/candidates = get_candidates(ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index 662cab5c991..0869a6f35ee 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -22,7 +22,7 @@
/datum/round_event/ghost_role/blob/spawn_role()
if(!GLOB.blobstart.len)
return MAP_ERROR
- var/list/candidates = get_candidates(ROLE_BLOB, null, ROLE_BLOB)
+ var/list/candidates = get_candidates(ROLE_BLOB, /datum/role_preference/midround_ghost/blob)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
var/mob/dead/observer/new_blob = pick(candidates)
diff --git a/code/modules/events/creep_awakening.dm b/code/modules/events/creep_awakening.dm
index 1b5643bc53f..b45b45ada95 100644
--- a/code/modules/events/creep_awakening.dm
+++ b/code/modules/events/creep_awakening.dm
@@ -10,7 +10,7 @@
/datum/round_event/obsessed/start()
for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list))
- if(!H.client || !(ROLE_OBSESSED in H.client.prefs.be_special))
+ if(!H.client?.should_include_for_role(ROLE_OBSESSED, /datum/role_preference/midround_living/obsessed))
continue
if(H.stat == DEAD)
continue
diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm
index 9073171c98c..7000d2b3366 100644
--- a/code/modules/events/devil.dm
+++ b/code/modules/events/devil.dm
@@ -19,7 +19,7 @@
return MAP_ERROR
//selecting a candidate player
- var/list/candidates = get_candidates(ROLE_DEVIL, null, ROLE_DEVIL)
+ var/list/candidates = get_candidates(ROLE_DEVIL, /datum/role_preference/midround_ghost/devil)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm
index 29b3e3149a3..5dd61663786 100644
--- a/code/modules/events/fugitive_spawning.dm
+++ b/code/modules/events/fugitive_spawning.dm
@@ -22,7 +22,7 @@
return MAP_ERROR
var/turf/landing_turf = pick(possible_spawns)
var/list/possible_backstories = list()
- var/list/candidates = get_candidates(ROLE_TRAITOR, null, ROLE_TRAITOR)
+ var/list/candidates = get_candidates(ROLE_TRAITOR, /datum/role_preference/midround_ghost/fugitive)
if(candidates.len >= 1) //solo refugees
if(prob(30))
possible_backstories.Add("waldo") //less common as it comes with magicks and is kind of immershun shattering
diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm
index 67ef4002e73..70546de1fa9 100644
--- a/code/modules/events/ghost_role.dm
+++ b/code/modules/events/ghost_role.dm
@@ -55,14 +55,14 @@
// players could be found, and just runtime if anything else happens
return TRUE
-/datum/round_event/ghost_role/proc/get_candidates(jobban, gametypecheck, be_special)
+/datum/round_event/ghost_role/proc/get_candidates(banning_key, role_preference, poll_ignore = null)
// Returns a list of candidates in priority order, with candidates from
// `priority_candidates` first, and ghost roles randomly shuffled and
// appended after
var/list/mob/dead/observer/regular_candidates
// don't get their hopes up
if(priority_candidates.len < minimum_required)
- regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", jobban, gametypecheck, be_special)
+ regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", banning_key, role_preference, ignore_category = poll_ignore)
else
regular_candidates = list()
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index 6a75c1cf380..f887ab485d7 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -86,7 +86,7 @@
priority_announce("Santa is coming to town!", "Unknown Transmission", SSstation.announcer.get_rand_alert_sound())
/datum/round_event/santa/start()
- var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time=150)
+ var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time = 15 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
santa = new /mob/living/carbon/human(pick(GLOB.blobstart))
diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm
index f0311378d3f..03ef2578f3b 100644
--- a/code/modules/events/nightmare.dm
+++ b/code/modules/events/nightmare.dm
@@ -12,7 +12,7 @@
fakeable = FALSE
/datum/round_event/ghost_role/nightmare/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
+ var/list/candidates = get_candidates(ROLE_NIGHTMARE, /datum/role_preference/midround_ghost/nightmare)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm
index e655d8b0e33..bea4dad09d1 100644
--- a/code/modules/events/operative.dm
+++ b/code/modules/events/operative.dm
@@ -11,7 +11,7 @@
fakeable = FALSE
/datum/round_event/ghost_role/operative/spawn_role()
- var/list/candidates = get_candidates(ROLE_OPERATIVE, null, ROLE_OPERATIVE)
+ var/list/candidates = get_candidates(ROLE_OPERATIVE, /datum/role_preference/midround_ghost/nuclear_operative)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index a4764953150..6a6b5e564ff 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -65,7 +65,7 @@ GLOBAL_VAR_INIT(pirates_spawned, FALSE)
if(!skip_answer_check && threat?.answered == PIRATE_RESPONSE_PAY)
return
- var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_TRAITOR)
+ var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_SPACE_PIRATE, /datum/role_preference/midround_ghost/space_pirate, 15 SECONDS)
shuffle_inplace(candidates)
var/datum/map_template/shuttle/pirate/default/ship = new
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index 06cfa5e5843..7139a65377a 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
/datum/round_event/ghost_role/sentience/spawn_role()
var/list/mob/dead/observer/candidates
- candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
+ candidates = get_candidates(ROLE_SENTIENT_ANIMAL, null)
// find our chosen mob to breathe life into
// Mobs have to be simple animals, mindless, on station, and NOT holograms.
diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm
index 731cda46c4a..95e7f509cfd 100644
--- a/code/modules/events/space_dragon.dm
+++ b/code/modules/events/space_dragon.dm
@@ -17,7 +17,7 @@
priority_announce("It appears a lifeform with magical traces is approaching [station_name()], please stand-by.", "Lifesign Alert", SSstation.announcer.get_rand_alert_sound())
/datum/round_event/ghost_role/space_dragon/spawn_role()
- var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN)
+ var/list/candidates = get_candidates(ROLE_SPACE_DRAGON, /datum/role_preference/midround_ghost/space_dragon)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/events/special_antag_event.dm b/code/modules/events/special_antag_event.dm
index 6fbe7b39e73..50b1932d2eb 100644
--- a/code/modules/events/special_antag_event.dm
+++ b/code/modules/events/special_antag_event.dm
@@ -3,9 +3,10 @@
typepath = /datum/round_event/create_special_antag
auto_add = FALSE
//Antagonist data
- var/antagonist_datum = /datum/antagonist/special
+ var/datum/antagonist/antagonist_datum = /datum/antagonist/special
var/antag_name //The datum of the antag E.G. /datum/antagonist/special/undercover
- var/preference_type = ROLE_TRAITOR
+ var/preference_type = /datum/role_preference/antagonist/traitor
+ var/banning_key = BAN_ROLE_ALL_ANTAGONISTS
var/protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CAPTAIN, JOB_NAME_MASTERATARMS) //NSV13 - MPs, XO, MAA
/datum/round_event_control/spawn_special_antagonist/runEvent()
@@ -13,6 +14,7 @@
E.antag_datum = antagonist_datum
E.role_name = antag_name
E.preference_type = preference_type
+ E.banning_key = banning_key
E.protected_jobs = protected_jobs
E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
E.control = src
@@ -34,15 +36,16 @@
/datum/round_event/create_special_antag
fakeable = FALSE
var/role_name
- var/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover
- var/preference_type = ROLE_TRAITOR
+ var/datum/antagonist/antag_datum //The datum of the antag E.G. /datum/antagonist/special/undercover
+ var/banning_key = BAN_ROLE_ALL_ANTAGONISTS
+ var/preference_type = /datum/role_preference/antagonist/traitor
var/protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_DETECTIVE, JOB_NAME_HEADOFSECURITY, JOB_NAME_HEADOFPERSONNEL, JOB_NAME_CHIEFMEDICALOFFICER, JOB_NAME_CHIEFENGINEER, JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_CAPTAIN, JOB_NAME_MASTERATARMS) //NSV13 - XO, MAA, MP
/datum/round_event/create_special_antag/start()
for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list))
- if(!H.client || !(preference_type in H.client.prefs.be_special) || !(H.client.prefs.toggles & PREFTOGGLE_MIDROUND_ANTAG))
+ if(!H.client)
continue
- if(is_banned_from(H, list(preference_type)))
+ if(!H.client.should_include_for_role(initial(antag_datum.banning_key), preference_type))
continue
if(H.stat == DEAD)
continue
diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm
index 1c8ef95baae..f69d962ce0c 100644
--- a/code/modules/events/wizard/imposter.dm
+++ b/code/modules/events/wizard/imposter.dm
@@ -10,7 +10,7 @@
if(!ishuman(M.current))
continue
var/mob/living/carbon/human/W = M.current
- var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", ROLE_WIZARD)
+ var/list/candidates = pollGhostCandidates("Would you like to be an imposter wizard?", ROLE_WIZARD, /datum/role_preference/midround_ghost/wizard, ignore_category = POLL_IGNORE_WIZARD_HELPER)
if(!candidates)
return //Sad Trombone
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm
index 81e5588b5f2..afe3167fe08 100644
--- a/code/modules/food_and_drinks/food/snacks_meat.dm
+++ b/code/modules/food_and_drinks/food/snacks_meat.dm
@@ -178,7 +178,7 @@
qdel(src)
/obj/item/reagent_containers/food/snacks/monkeycube/syndicate
- faction = list("neutral", ROLE_SYNDICATE)
+ faction = list("neutral", FACTION_SYNDICATE)
/obj/item/reagent_containers/food/snacks/monkeycube/gorilla
name = "gorilla cube"
diff --git a/code/modules/guardian/guardian.dm b/code/modules/guardian/guardian.dm
index ba180d60701..3de93224483 100644
--- a/code/modules/guardian/guardian.dm
+++ b/code/modules/guardian/guardian.dm
@@ -581,7 +581,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
/mob/living/simple_animal/hostile/guardian/proc/ResetMe()
set waitfor = FALSE
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", ROLE_HOLOPARASITE, null, FALSE, 10 SECONDS)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [summoner?.current?.name]'s [real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
key = C.key
@@ -674,7 +674,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
return
G.next_reset = world.time + GUARDIAN_RESET_COOLDOWN
to_chat(src, "You attempt to reset [G.real_name] 's personality... ")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance. ")
diff --git a/code/modules/guardian/guardianbuilder.dm b/code/modules/guardian/guardianbuilder.dm
index 4492b08cacd..e5a97fa23ee 100644
--- a/code/modules/guardian/guardianbuilder.dm
+++ b/code/modules/guardian/guardianbuilder.dm
@@ -225,7 +225,7 @@
used = FALSE
return FALSE
// IMPORTANT - if we're debugging, the user gets thrown into the stand
- var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE)
+ var/list/mob/dead/observer/candidates = debug_mode ? list(user) : pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
var/mob/living/simple_animal/hostile/guardian/G = new(user, theme, guardian_color)
diff --git a/code/modules/guardian/standarrow.dm b/code/modules/guardian/standarrow.dm
index c2e6b3457c5..583ee3fc2b4 100644
--- a/code/modules/guardian/standarrow.dm
+++ b/code/modules/guardian/standarrow.dm
@@ -161,7 +161,7 @@
G.name = new_name
/obj/item/stand_arrow/proc/get_stand(mob/living/carbon/H, datum/guardian_stats/stats)
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", ROLE_HOLOPARASITE, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Guardian Spirit of [H.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
var/mob/living/simple_animal/hostile/guardian/G = new(H, GUARDIAN_MAGIC, rgb(rand(1, 255), rand(1, 255), rand(1, 255)))
diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm
index 81c921f6ef1..3a28fde67a2 100644
--- a/code/modules/jobs/jobs.dm
+++ b/code/modules/jobs/jobs.dm
@@ -175,7 +175,7 @@ GLOBAL_LIST_INIT(exp_jobsmap, list(
GLOBAL_LIST_INIT(exp_specialmap, list(
EXP_TYPE_LIVING = list(), // all living mobs
EXP_TYPE_ANTAG = list(),
- EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Space Bartender","Beach Bum","Skeleton","Zombie","Space Bar Patron","Lavaland Syndicate",JOB_NAME_PAI,"Ghost Role"), // Ghost roles
+ EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Beach Bum","Skeleton","Zombie","Lavaland Syndicate",JOB_NAME_PAI,"Ghost Role"), // Ghost roles
EXP_TYPE_GHOST = list() // dead people, observers
))
GLOBAL_PROTECT(exp_jobsmap)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 19d1d67f1c6..25fb54ec741 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -513,9 +513,7 @@
if(client.prefs.active_character.joblessrole != RETURNTOLOBBY)
return TRUE
// If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so.
- var/has_antags = FALSE
- if(client.prefs.be_special.len > 0)
- has_antags = TRUE
+ var/has_antags = (length(client.prefs.role_preferences) + length(client.prefs.active_character?.role_preferences_character)) > 0
if(!length(client.prefs.active_character.job_preferences))
if(!ineligible_for_roles)
to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences. ")
diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm
deleted file mode 100644
index 6370747ad46..00000000000
--- a/code/modules/mob/dead/observer/notificationprefs.dm
+++ /dev/null
@@ -1,53 +0,0 @@
-/mob/dead/observer/verb/show_notificationprefs()
- set category = "Ghost"
- set name = "Notification preferences"
- set desc = "Notification preferences"
-
- var/datum/notificationpanel/panel = new(usr)
-
- panel.ui_interact(usr)
-
-
-
-/datum/notificationpanel
- var/client/user
-
-/datum/notificationpanel/New(user)
- if (ismob(user))
- var/mob/M = user
- if (!M.client)
- CRASH("Ghost role notification panel attempted to open to a mob without a client")
- src.user = M.client
- else
- src.user = user
-
-
-/datum/notificationpanel/ui_state(mob/user)
- return GLOB.observer_state
-
-/datum/notificationpanel/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "NotificationPreferences")
- ui.open()
-
-/datum/notificationpanel/ui_data(mob/user)
- . = list()
- .["ignore"] = list()
- for(var/key in GLOB.poll_ignore_desc)
- .["ignore"] += list(list(
- "key" = key,
- "enabled" = (user.ckey in GLOB.poll_ignore[key]),
- "desc" = GLOB.poll_ignore_desc[key]
- ))
-
-
-/datum/notificationpanel/ui_act(action, params)
- if(..())
- return
- switch (action)
- if ("toggle_ignore")
- var/key = params["key"]
- if (key && islist(GLOB.poll_ignore[key]))
- GLOB.poll_ignore[key] ^= list(user.ckey)
- . = TRUE
diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm
index 6d6396d172f..fbae9c34847 100644
--- a/code/modules/mob/living/carbon/alien/alien.dm
+++ b/code/modules/mob/living/carbon/alien/alien.dm
@@ -3,7 +3,7 @@
icon = 'icons/mob/alien.dmi'
gender = FEMALE //All xenos are girls!!
dna = null
- faction = list(ROLE_ALIEN)
+ faction = list(FACTION_ALIEN)
ventcrawler = VENTCRAWLER_ALWAYS
sight = SEE_MOBS
see_in_dark = 4
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index 3fd29dfdfe0..6d4ada4ce49 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -115,12 +115,12 @@
var/recent_queen_death = 0 //Indicates if the queen died recently, aliens are heavily weakened while this is active.
/obj/item/organ/alien/hivenode/Insert(mob/living/carbon/M, special = 0)
- M.faction |= ROLE_ALIEN
+ M.faction |= FACTION_ALIEN
ADD_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune")
return ..()
/obj/item/organ/alien/hivenode/Remove(mob/living/carbon/M, special = 0)
- M.faction -= ROLE_ALIEN
+ M.faction -= FACTION_ALIEN
REMOVE_TRAIT(M, TRAIT_XENO_IMMUNE, "xeno immune")
return ..()
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index 1aee89c7515..dc4a6ac2b9e 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -90,7 +90,7 @@
bursting = TRUE
- var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA)
+ var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, /datum/role_preference/midround_ghost/xenomorph, 10 SECONDS, POLL_IGNORE_ALIEN_LARVA) // separate poll from xeno event spawns
if(QDELETED(src) || QDELETED(owner))
return
diff --git a/code/modules/mob/living/living_sentience.dm b/code/modules/mob/living/living_sentience.dm
index 2e349cbcd38..d778bac1562 100644
--- a/code/modules/mob/living/living_sentience.dm
+++ b/code/modules/mob/living/living_sentience.dm
@@ -1,15 +1,19 @@
//WHY ISN'T THIS COMPONENT
+/// The ban type to check if somebody attempts to play this "playable mob"
+/mob/living/var/playable_bantype
+
/mob/living/ghostize(can_reenter_corpse, sentience_retention)
. = ..()
switch(sentience_retention)
if (SENTIENCE_RETAIN)
if (playable) //so the alert goes through for observing ghosts
- set_playable()
+ set_playable(playable_bantype)
if (SENTIENCE_FORCE)
- set_playable()
+ set_playable(playable_bantype)
if (SENTIENCE_ERASE)
playable = FALSE
+ playable_bantype = null
/mob/living/attack_ghost(mob/user)
. = ..()
@@ -34,6 +38,10 @@
if(key)
to_chat(user, "Someone else already took [name]. ")
return TRUE
+ if(!SSticker.HasRoundStarted())
+ return
+ if(!user?.client?.can_take_ghost_spawner(playable_bantype, TRUE, flags_1 & ADMIN_SPAWNED_1))
+ return
key = user.key
log_game("[key_name(src)] took control of [name].")
remove_from_spawner_menu()
@@ -41,10 +49,11 @@
to_chat(src, "[get_spawner_flavour_text()] ")
return TRUE
-/mob/living/proc/set_playable()
+/mob/living/proc/set_playable(ban_type = null, poll_ignore_key = null)
playable = TRUE
+ playable_bantype = ban_type
if (!key) //check if there is nobody already inhibiting this mob
- notify_ghosts("[name] can be controlled", null, enter_link="(Click to play) ", source=src, action=NOTIFY_ATTACK, ignore_key = name)
+ notify_ghosts("[name] can be controlled", null, enter_link="(Click to play) ", source=src, action=NOTIFY_ATTACK, ignore_key = poll_ignore_key)
LAZYADD(GLOB.mob_spawners["[name]"], src)
GLOB.poi_list |= src
SSmobs.update_spawners()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 1fd3611725a..dc57aca536b 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -904,7 +904,7 @@
/mob/living/silicon/robot/modules/syndicate
icon_state = "synd_sec"
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
bubble_icon = "syndibot"
req_access = list(ACCESS_SYNDICATE)
lawupdate = FALSE
diff --git a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
index b873ae66255..9f666beb24f 100644
--- a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
+++ b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
@@ -24,7 +24,7 @@
desc = "The Syndicate sends their regards."
emagged = 2
noloot = TRUE
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
/mob/living/simple_animal/bot/secbot/grievous/nullcrate/ComponentInitialize()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
index b5dd3f01af4..c82610ca84f 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
@@ -47,12 +47,6 @@
/obj/effect/mob_spawn/drone/attack_ghost(mob/user)
if(is_banned_from(user.ckey, ROLE_DRONE) || QDELETED(src) || QDELETED(user))
return
- if(CONFIG_GET(flag/use_age_restriction_for_jobs))
- if(!isnum_safe(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention
- return
- if(user.client.player_age < DRONE_MINIMUM_AGE)
- to_chat(user, "You're too new to play as a drone! Please try again in [DRONE_MINIMUM_AGE - user.client.player_age] days. ")
- return
if(!SSticker.mode)
to_chat(user, "Can't become a drone before the game has started.")
return
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
index 057facfda3e..0d22d332ef3 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
@@ -18,7 +18,7 @@
health = 30
maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger
initial_language_holder = /datum/language_holder/drone/syndicate
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
speak_emote = list("hisses")
bubble_icon = "syndibot"
heavy_emp_damage = 10
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 80f89524fb8..08c9c2448b6 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -462,7 +462,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
var/mob/living/simple_animal/hostile/guardian/G = input(src, "Pick the guardian you wish to reset", "Guardian Reset") as null|anything in sortNames(guardians)
if(G)
to_chat(src, "You attempt to reset [G.real_name] 's personality... ")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_PAI, null, FALSE, 100)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance. ")
@@ -540,7 +540,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
return
used = TRUE
to_chat(user, "[use_message]")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_HOLOPARASITE, null, 10 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm
index 3cd5f245d6a..c85b891ea68 100644
--- a/code/modules/mob/living/simple_animal/hostile/alien.dm
+++ b/code/modules/mob/living/simple_animal/hostile/alien.dm
@@ -24,7 +24,7 @@
attack_sound = 'sound/weapons/bladeslice.ogg'
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
- faction = list(ROLE_ALIEN)
+ faction = list(FACTION_ALIEN)
status_flags = CANPUSH
minbodytemp = 0
see_in_dark = 8
diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm
index f17d40a8148..3cb408d47c7 100644
--- a/code/modules/mob/living/simple_animal/hostile/carp.dm
+++ b/code/modules/mob/living/simple_animal/hostile/carp.dm
@@ -144,7 +144,7 @@
gender = FEMALE
speak_emote = list("squeaks")
gold_core_spawnable = NO_SPAWN
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
AIStatus = AI_OFF
/// Keeping track of the nuke disk for the functionality of storing it.
var/obj/item/disk/nuclear/disky
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index 484f5249ecb..b3758fc7464 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -156,7 +156,7 @@ While using this makes the system rely on OnFire, it still gives options for tim
addtimer(CALLBACK(src, PROC_REF(spawn_elite)), 30)
return
visible_message("Something within [src] stirs... ")
- var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, src, POLL_IGNORE_SENTIENCE_POTION)
+ var/list/candidates = pollCandidatesForMob("Do you want to play as a lavaland elite?", ROLE_LAVALAND_ELITE, null, 10 SECONDS, src)
if(candidates.len)
audible_message("The stirring sounds increase in volume! ")
elitemind = pick(candidates)
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 9d75eb659f7..af8d6db1272 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -40,7 +40,7 @@
loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier)
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
check_friendly_fire = 1
status_flags = CANPUSH
del_on_death = TRUE
@@ -287,7 +287,7 @@
environment_smash = ENVIRONMENT_SMASH_NONE
attacktext = "cuts"
attack_sound = 'sound/weapons/bladeslice.ogg'
- faction = list(ROLE_SYNDICATE)
+ faction = list(FACTION_SYNDICATE)
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
mob_size = MOB_SIZE_TINY
diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm
index c365014d62f..e4f104a8afb 100644
--- a/code/modules/mob/living/simple_animal/hostile/wizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm
@@ -20,7 +20,7 @@
a_intent = INTENT_HARM
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
- faction = list(ROLE_WIZARD)
+ faction = list(FACTION_WIZARD)
status_flags = CANPUSH
retreat_distance = 3 //out of fireball range
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 6e449acdfdd..fd3cd1dd0c7 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -113,7 +113,7 @@
. = ..()
set_nutrition(SLIME_DEFAULT_NUTRITION)
if(transformeffects & SLIME_EFFECT_LIGHT_PINK)
- set_playable()
+ set_playable(ROLE_SENTIENCE)
/mob/living/simple_animal/slime/Destroy()
set_target(null)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 157565c96aa..c6e307add8b 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -490,15 +490,17 @@
log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.")
message_admins("[key_name_admin(usr)] has offered control of ([ADMIN_LOOKUPFLW(M)]) to ghosts")
var/poll_message = "Do you want to play as [M.real_name]?"
+ var/ban_key = BAN_ROLE_ALL_ANTAGONISTS
if(M.mind && M.mind.assigned_role)
poll_message = "[poll_message] Job:[M.mind.assigned_role]."
if(M.mind && M.mind.special_role)
poll_message = "[poll_message] Status:[M.mind.special_role]."
else if(M.mind)
- var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
+ var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist)
if(A)
poll_message = "[poll_message] Status:[A.name]."
- var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M)
+ ban_key = A.banning_key
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 10 SECONDS, M, ignore_category = FALSE)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm
index 8b8ea63bc9c..a3b656b5f09 100644
--- a/code/modules/ninja/ninja_event.dm
+++ b/code/modules/ninja/ninja_event.dm
@@ -45,7 +45,7 @@ Contents:
return MAP_ERROR
//selecting a candidate player
- var/list/candidates = get_candidates(ROLE_NINJA, null, ROLE_NINJA)
+ var/list/candidates = get_candidates(ROLE_NINJA, /datum/role_preference/midround_ghost/ninja)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 9f42c08f3a4..d165cdd8943 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -583,15 +583,17 @@
/obj/item/projectile/magic/wipe/proc/possession_test(var/mob/living/carbon/M)
var/datum/brain_trauma/special/imaginary_friend/trapped_owner/trauma = M.gain_trauma(/datum/brain_trauma/special/imaginary_friend/trapped_owner)
var/poll_message = "Do you want to play as [M.real_name]?"
+ var/ban_key = BAN_ROLE_ALL_ANTAGONISTS
if(M.mind?.assigned_role)
poll_message = "[poll_message] Job:[M.mind.assigned_role]."
if(M.mind?.special_role)
poll_message = "[poll_message] Status:[M.mind.special_role]."
else if(M.mind)
- var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
+ var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist)
if(A)
poll_message = "[poll_message] Status:[A.name]."
- var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, null, FALSE, 100, M)
+ ban_key = A.banning_key
+ var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ban_key, null, 10 SECONDS, M, ignore_category = FALSE)
if(M.stat == DEAD)//boo.
return
if(LAZYLEN(candidates))
diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm
index 345cbcad35d..d9fc56a01e8 100644
--- a/code/modules/religion/rites.dm
+++ b/code/modules/religion/rites.dm
@@ -395,8 +395,7 @@
var/turf/altar_turf = get_turf(religious_tool)
new /obj/effect/temp_visual/cult/blood/long(altar_turf)
new /obj/effect/temp_visual/dir_setting/curse/long(altar_turf)
- var/list/jobbans = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE)
- var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", jobbans, null, FALSE,)
+ var/list/candidates = pollGhostCandidates("Do you wish to be resurrected as a Holy Summoned Undead?", ROLE_HOLY_SUMMONED, null, FALSE)
if(!length(candidates))
to_chat(user, "The soul pool is empty...")
new /obj/effect/gibspawner/human/bodypartless(altar_turf)
@@ -558,8 +557,7 @@
var/turf/altar_turf = get_turf(religious_tool)
new /obj/effect/temp_visual/bluespace_fissure/long(altar_turf)
user.visible_message("A tear in reality appears above the altar! ")
- var/list/jobbans = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE)
- var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", jobbans, null, FALSE)
+ var/list/candidates = pollGhostCandidates("Do you wish to be summoned as a Holy Carp?", ROLE_HOLY_SUMMONED, null, FALSE)
if(!length(candidates))
new /obj/effect/gibspawner/generic(altar_turf)
user.visible_message("The carp pool was not strong enough to bring forth a space carp.")
diff --git a/code/modules/research/xenobiology/crossbreeding/warping.dm b/code/modules/research/xenobiology/crossbreeding/warping.dm
index 87fda9a828e..2ae9c6b746d 100644
--- a/code/modules/research/xenobiology/crossbreeding/warping.dm
+++ b/code/modules/research/xenobiology/crossbreeding/warping.dm
@@ -713,7 +713,7 @@ GLOBAL_DATUM(blue_storage, /obj/item/storage/backpack/holding/bluespace)
return
to_chat(user, "The rune is trying to repair [host.name]'s soul! ")
- var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, host, POLL_IGNORE_SHADE)//todo: fix desc
+ var/list/candidates = pollCandidatesForMob("Do you want to replace the soul of [host.name]?", ROLE_SENTIENCE, null, 5 SECONDS, host, POLL_IGNORE_SHADE)
if(length(candidates) && !host.key) //check if anyone wanted to play as the dead person and check if no one's in control of the body one last time.
var/mob/dead/observer/ghost = pick(candidates)
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 221e42c019c..dfa8e98894e 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -748,7 +748,7 @@
to_chat(user, "You offer [src] to [SM]... ")
being_used = TRUE
- var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
+ var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]? (Sentience Potion)", ROLE_SENTIENCE, null, 5 SECONDS, SM)
if(length(candidates))
var/mob/dead/observer/C = pick(candidates)
SM.key = C.key
diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm
index 7e1e2b1c871..be12c9f2f6c 100644
--- a/code/modules/ruins/lavaland_ruin_code.dm
+++ b/code/modules/ruins/lavaland_ruin_code.dm
@@ -115,6 +115,7 @@
outfit = /datum/outfit/lavaland_syndicate
assignedrole = "Lavaland Syndicate"
use_cooldown = TRUE
+ banType = ROLE_LAVALAND_SYNDICATE
/obj/effect/mob_spawn/human/lavaland_syndicate/special(mob/living/new_spawn)
new_spawn.grant_language(/datum/language/codespeak)
@@ -133,7 +134,7 @@
implants = list(/obj/item/implant/weapons_auth)
/datum/outfit/lavaland_syndicate/post_equip(mob/living/carbon/human/H)
- H.faction |= ROLE_SYNDICATE
+ H.faction |= FACTION_SYNDICATE
/obj/effect/mob_spawn/human/lavaland_syndicate/comms
name = "Syndicate Comms Agent"
diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
index b8f42696f2d..ea141a15cdc 100644
--- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
@@ -465,6 +465,7 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337)
back = /obj/item/storage/backpack/satchel/leather
suit = /obj/item/clothing/suit/toggle/labcoat
use_cooldown = TRUE
+ banType = ROLE_HOTEL_STAFF
/obj/item/paper/crumpled/docslogs
name = "Research Logs"
diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm
index c49e6cc64ce..88ae992e973 100644
--- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm
+++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/assassination.dm
@@ -35,7 +35,7 @@
/datum/orbital_objective/assassination/generate_objective_stuff(turf/chosen_turf)
var/mob/living/carbon/human/created_human = new(chosen_turf)
//Maybe polling ghosts would be better than the shintience code
- created_human.set_playable()
+ created_human.set_playable(ROLE_SURVIVALIST)
created_human.mind_initialize()
//Remove nearby dangers
for(var/mob/living/simple_animal/hostile/SA in range(10, created_human))
diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm
index de86fe6ff67..c9d288670df 100644
--- a/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm
+++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/objective_types/vip_extraction.dm
@@ -36,7 +36,7 @@
/datum/orbital_objective/vip_recovery/generate_objective_stuff(turf/chosen_turf)
var/mob/living/carbon/human/created_human = new(chosen_turf)
//Maybe polling ghosts would be better than the shintience code
- created_human.set_playable()
+ created_human.set_playable(ROLE_EXPLORATION_VIP)
created_human.mind_initialize()
//Remove nearby dangers
for(var/mob/living/simple_animal/hostile/SA in range(10, created_human))
diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm
index 29ddd49e9f9..5cdee0bd98b 100644
--- a/code/modules/shuttle/syndicate.dm
+++ b/code/modules/shuttle/syndicate.dm
@@ -29,7 +29,7 @@
. = ..()
/obj/machinery/computer/shuttle_flight/syndicate/allowed(mob/M)
- if(issilicon(M) && !(ROLE_SYNDICATE in M.faction))
+ if(issilicon(M) && !(FACTION_SYNDICATE in M.faction))
return FALSE
return ..()
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index edf8ab0bad3..77c992afe94 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -57,8 +57,10 @@
#include "create_and_destroy.dm"
#endif
+#include "antag_datums.dm"
#include "dynamic_ruleset_sanity.dm"
#include "keybinding_init.dm"
+#include "gamemode_sanity.dm"
#include "reagent_id_typos.dm"
#include "reagent_recipe_collisions.dm"
#include "spawn_humans.dm"
diff --git a/code/modules/unit_tests/antag_datums.dm b/code/modules/unit_tests/antag_datums.dm
new file mode 100644
index 00000000000..fdea39f2ea3
--- /dev/null
+++ b/code/modules/unit_tests/antag_datums.dm
@@ -0,0 +1,15 @@
+/// Verifies that antag datums have banning_keys.
+/datum/unit_test/antag_datum_sanity
+
+/datum/unit_test/antag_datum_sanity/Run()
+ for (var/datum/antagonist/antag as anything in subtypesof(/datum/antagonist))
+ if(ispath(antag, /datum/antagonist/custom))
+ continue
+ var/name = initial(antag.name)
+ if (!name || name == "Antagonist")
+ Fail("[antag] has no name set!")
+ if (!initial(antag.banning_key))
+ Fail("[antag] has no banning_key set!")
+ var/category = initial(antag.antagpanel_category)
+ if (initial(antag.show_in_antagpanel) && (!category || category == "Uncategorized"))
+ Fail("[antag] shows in the antag panel, but has no category set!")
diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm
index 61200a29bb3..84a111a0745 100644
--- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm
+++ b/code/modules/unit_tests/dynamic_ruleset_sanity.dm
@@ -10,6 +10,17 @@
Fail("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.")
else if (!has_scaling_cost && !is_lone)
Fail("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.")
+ var/name = initial(ruleset.name)
+ if(!name)
+ Fail("[ruleset] has no name!")
+ if(name == "Extended" || name == "Meteor") // These rulesets don't spawn antags and are exempt.
+ continue
+ var/datum/antagonist/antag_datum = initial(ruleset.antag_datum)
+ if (!ispath(antag_datum, /datum/antagonist) || !initial(antag_datum.banning_key))
+ Fail("[ruleset] has no antag_datum with a banning key!")
+ var/role_pref = initial(ruleset.role_preference)
+ if (!role_pref || !ispath(role_pref, /datum/role_preference))
+ Fail("[ruleset] has no role preference!")
for (var/datum/dynamic_ruleset/midround/ruleset as anything in subtypesof(/datum/dynamic_ruleset/midround) - /datum/dynamic_ruleset/midround/from_ghosts)
var/midround_ruleset_style = initial(ruleset.midround_ruleset_style)
diff --git a/code/modules/unit_tests/gamemode_sanity.dm b/code/modules/unit_tests/gamemode_sanity.dm
new file mode 100644
index 00000000000..2ac7e458f0b
--- /dev/null
+++ b/code/modules/unit_tests/gamemode_sanity.dm
@@ -0,0 +1,20 @@
+/// Verifies that gamemodes have various fields
+/datum/unit_test/gamemode_sanity
+
+/datum/unit_test/gamemode_sanity/Run()
+ for (var/datum/game_mode/mode as anything in subtypesof(/datum/game_mode))
+ var/name = initial(mode.name)
+ if (!name)
+ Fail("[mode] has no name set!")
+ var/config_tag = initial(mode.config_tag)
+ if (!config_tag)
+ Fail("[mode] has no config_tag set!")
+ // These gamemodes don't spawn antags directly and are exempt.
+ if(!initial(mode.required_enemies) && !initial(mode.recommended_enemies))
+ continue
+ var/datum/antagonist/antag_datum = initial(mode.antag_datum)
+ if (!ispath(antag_datum, /datum/antagonist) || !initial(antag_datum.banning_key))
+ Fail("[mode] has no antag_datum with a banning key!")
+ var/role_pref = initial(mode.role_preference)
+ if (!role_pref || !ispath(role_pref, /datum/role_preference))
+ Fail("[mode] has no role_preference set!")
diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm
index 8ba217223f2..97efac692b7 100644
--- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm
+++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm
@@ -131,7 +131,7 @@
man.key = M.ckey
/datum/xenoartifact_trait/minor/sentient/proc/get_canidate(obj/item/xenoartifact/X, mob/M)
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", ROLE_SENTIENCE, null, FALSE, 8 SECONDS, POLL_IGNORE_SENTIENCE_POTION)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the maleviolent force inside the [X.name]?", ROLE_SENTIENT_XENOARTIFACT, null, 8 SECONDS)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
setup_sentience(X, C.ckey)
@@ -197,6 +197,7 @@
short_desc = "You're a maleviolent sentience, possesing an ancient alien artifact."
flavour_text = "Return to your master..."
use_cooldown = TRUE
+ banType = ROLE_SENTIENT_XENOARTIFACT
invisibility = 101
var/obj/item/xenoartifact/artifact
diff --git a/config/dbconfig.txt b/config/dbconfig.txt
index 17550ca61b1..6d1e40ed08e 100644
--- a/config/dbconfig.txt
+++ b/config/dbconfig.txt
@@ -3,11 +3,11 @@
## administration, and the in game library.
## Should SQL be enabled? Uncomment to enable
-#SQL_ENABLED
+SQL_ENABLED
## Server the MySQL database can be found at.
## Examples: localhost, 200.135.5.43, www.mysqldb.com, etc.
-ADDRESS localhost
+ADDRESS 127.0.0.1
## MySQL server port (default is 3306).
PORT 3306
diff --git a/config/dynamic.json b/config/dynamic.json
index 55cd5dc0f04..f7a22b96803 100644
--- a/config/dynamic.json
+++ b/config/dynamic.json
@@ -7,20 +7,9 @@
"weight": 5,
"required_candidates": 1,
"minimum_required_age": 0,
- "requirements": [
- 10,
- 10,
- 10,
- 10,
- 10,
- 10,
- 10,
- 10,
- 10,
- 10
- ],
+ "requirements": [8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
"antag_cap": {
- "denominator": 24
+ "denominator": 38
},
"protected_roles": [
"Security Officer",
@@ -29,9 +18,7 @@
"Head of Security",
"Captain"
],
- "restricted_roles": [
- "Cyborg"
- ]
+ "restricted_roles": ["Cyborg"]
}
},
"Midround": {},
diff --git a/html/admin/banpanel.css b/html/admin/banpanel.css
index f19ef6e1ba2..86fe6f84e33 100644
--- a/html/admin/banpanel.css
+++ b/html/admin/banpanel.css
@@ -1,7 +1,7 @@
.middle {
- margin-left: 5px;
- margin-right: 5px;
- max-width: 125px;
+ margin-left: 5px;
+ margin-right: 5px;
+ max-width: 125px;
}
.right {
@@ -70,10 +70,18 @@
background-color: #6eaa2c;
}
-.ghostandotherroles {
+.ghostroles {
background-color: #5c00e6;
}
.antagonistpositions {
- background-color: #6d3f40;
+ background-color: #6d3f40;
+}
+
+.forcedantagonistpositions {
+ background-color: #064d10;
+}
+
+.other {
+ background-color: #cf7474;
}
diff --git a/nsv13.dme b/nsv13.dme
index 08d24ee8f07..54578d4e2ae 100644
--- a/nsv13.dme
+++ b/nsv13.dme
@@ -1777,6 +1777,13 @@
#include "code\modules\antagonists\revenant\revenant_blight.dm"
#include "code\modules\antagonists\revenant\revenant_spawn_event.dm"
#include "code\modules\antagonists\revolution\revolution.dm"
+#include "code\modules\antagonists\role_preference\_role_preference.dm"
+#include "code\modules\antagonists\role_preference\role_antagonists.dm"
+#include "code\modules\antagonists\role_preference\role_changeling.dm"
+#include "code\modules\antagonists\role_preference\role_midrounds.dm"
+#include "code\modules\antagonists\role_preference\role_operative.dm"
+#include "code\modules\antagonists\role_preference\role_traitor.dm"
+#include "code\modules\antagonists\role_preference\role_wizard.dm"
#include "code\modules\antagonists\roundstart_special\special_antagonist.dm"
#include "code\modules\antagonists\roundstart_special\undercover\undercover.dm"
#include "code\modules\antagonists\santa\santa.dm"
@@ -2506,7 +2513,6 @@
#include "code\modules\mob\dead\new_player\sprite_accessories.dm"
#include "code\modules\mob\dead\observer\login.dm"
#include "code\modules\mob\dead\observer\logout.dm"
-#include "code\modules\mob\dead\observer\notificationprefs.dm"
#include "code\modules\mob\dead\observer\observer.dm"
#include "code\modules\mob\dead\observer\observer_movement.dm"
#include "code\modules\mob\dead\observer\orbit.dm"
@@ -3844,6 +3850,9 @@
#include "nsv13\code\modules\antagonists\simple_teamchat.dm"
#include "nsv13\code\modules\antagonists\boarders\boarders.dm"
#include "nsv13\code\modules\antagonists\boarders\pirate_boarders.dm"
+#include "nsv13\code\modules\antagonists\ghostship\ghost_ship.dm"
+#include "nsv13\code\modules\antagonists\role_preference\role_antagonists.dm"
+#include "nsv13\code\modules\antagonists\role_preference\role_midrounds.dm"
#include "nsv13\code\modules\atmospherics\gasmixtures\reactions.dm"
#include "nsv13\code\modules\atmospherics\machinery\components\binary_devices\constrictor.dm"
#include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\outlet_injector.dm"
diff --git a/nsv13/code/controllers/subsystem/overmap_mode.dm b/nsv13/code/controllers/subsystem/overmap_mode.dm
index a634ee5957e..c0690a54508 100644
--- a/nsv13/code/controllers/subsystem/overmap_mode.dm
+++ b/nsv13/code/controllers/subsystem/overmap_mode.dm
@@ -659,7 +659,7 @@ SUBSYSTEM_DEF(overmap_mode)
if("Cancel")
return
if("Open")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(target_ship.faction)] [initial(target_ship.name)]?", ROLE_GHOSTSHIP, null, null, 20 SECONDS, POLL_IGNORE_GHOSTSHIP)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(target_ship.faction)] [initial(target_ship.name)]?", ROLE_GHOSTSHIP, /datum/role_preference/midround_ghost/ghost_ship, 20 SECONDS, POLL_IGNORE_GHOSTSHIP)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
target_ghost = C
diff --git a/nsv13/code/game/gamemodes/bloodling.dm b/nsv13/code/game/gamemodes/bloodling.dm
index 87706ff243f..a075a456632 100644
--- a/nsv13/code/game/gamemodes/bloodling.dm
+++ b/nsv13/code/game/gamemodes/bloodling.dm
@@ -2,7 +2,8 @@
name = "bloodling"
config_tag = "bloodling"
report_type = "bloodling"
- antag_flag = ROLE_BLOODLING
+ role_preference = /datum/role_preference/antagonist/bloodling
+ antag_datum = /datum/antagonist/bloodling
false_report_weight = 10
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list(JOB_NAME_SECURITYOFFICER, JOB_NAME_WARDEN, JOB_NAME_HEADOFSECURITY, JOB_NAME_CAPTAIN, JOB_NAME_BRIGPHYSICIAN)
@@ -108,19 +109,21 @@ Helper proc to spawn the lil' blood alien creature in a vent! Adapted from alien
if(master) //There's already a master
return
if(bloodlings.len < bloodling_amount)
- if(ROLE_BLOODLING in character.client.prefs.be_special)
- if(!is_banned_from(character.ckey, list(ROLE_BLOODLING, ROLE_SYNDICATE)) && !QDELETED(character))
- if(age_check(character.client))
- if(!(character.job in restricted_jobs))
- if(!master) //Make him the master
- master = spawn_bloodling()
- if(!master)
- return FALSE //yeah okay your shit map doesn't support bloodling RIP
- master.key = character.client.ckey
- bloodlings += master.mind
- qdel(character) //Bye!
- //Otherwise, make him a new thrall...
- character.mind.add_antag_datum(/datum/antagonist/changeling/bloodling_thrall)
+ if(!QDELETED(character) && character.client?.should_include_for_role(
+ banning_key = initial(antag_datum.banning_key),
+ role_preference_key = role_preference,
+ req_hours = initial(antag_datum.required_living_playtime),
+ ))
+ if(!(character.job in restricted_jobs)) if(!(character.job in restricted_jobs))
+ if(!master) //Make him the master
+ master = spawn_bloodling()
+ if(!master)
+ return FALSE //yeah okay your shit map doesn't support bloodling RIP
+ master.key = character.client.ckey
+ bloodlings += master.mind
+ qdel(character) //Bye!
+ //Otherwise, make him a new thrall...
+ character.mind.add_antag_datum(/datum/antagonist/changeling/bloodling_thrall)
/datum/game_mode/bloodling/generate_report()
diff --git a/nsv13/code/game/gamemodes/pvp/pvp.dm b/nsv13/code/game/gamemodes/pvp/pvp.dm
index e2e82902605..703a8eb2491 100644
--- a/nsv13/code/game/gamemodes/pvp/pvp.dm
+++ b/nsv13/code/game/gamemodes/pvp/pvp.dm
@@ -16,8 +16,8 @@ GLOBAL_LIST_EMPTY(syndi_crew_leader_spawns)
required_players = 24 //40 // 40 to make 20 v 20
required_enemies = 12 //20
recommended_enemies = 15
- antag_flag = ROLE_SYNDI_CREW
- enemy_minimum_age = 0
+ role_preference = /datum/role_preference/antagonist/pvp
+ antag_datum = /datum/antagonist/nukeop/syndi_crew
announce_span = "danger"
announce_text = "The Syndicate are planning an all out assault!\n\
diff --git a/nsv13/code/game/gamemodes/pvp/roles.dm b/nsv13/code/game/gamemodes/pvp/roles.dm
index 91c2b025464..98dfa96d77f 100644
--- a/nsv13/code/game/gamemodes/pvp/roles.dm
+++ b/nsv13/code/game/gamemodes/pvp/roles.dm
@@ -53,7 +53,7 @@
/datum/antagonist/nukeop/syndi_crew
name = "Syndicate crew"
nukeop_outfit = /datum/outfit/syndicate/no_crystals/syndi_crew
- job_rank = ROLE_SYNDI_CREW
+ banning_key = ROLE_SYNDI_CREW
tips = "galactic_conquest"
give_objectives = FALSE //Their objective is to win the game
@@ -230,7 +230,7 @@ Singleton to handle conquest roles. This exists to populate the roles list and n
/datum/antagonist/nukeop/syndi_crew/strategist
name = "Syndicate Strategist"
nukeop_outfit = /datum/outfit/syndicate/no_crystals/syndi_crew/strategist
- job_rank = ROLE_SYNDI_CREW
+ banning_key = ROLE_SYNDI_CREW
/datum/outfit/syndicate/no_crystals/syndi_crew/strategist
name = "Syndicate Strategist"
@@ -383,7 +383,7 @@ Singleton to handle conquest roles. This exists to populate the roles list and n
/datum/antagonist/nukeop/syndi_crew/clown
name = "Syndicate Clown"
nukeop_outfit = /datum/outfit/syndicate/clownop/no_crystals/jojo_reference
- job_rank = ROLE_SYNDI_CREW
+ banning_key = ROLE_SYNDI_CREW
/datum/antagonist/nukeop/syndi_crew/clown/give_alias()
owner.current.fully_replace_character_name(owner.current.real_name, owner.current.client.prefs.active_character.custom_names["clown"])
diff --git a/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm b/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm
index 8eadc64edbc..4bee8db8109 100644
--- a/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm
+++ b/nsv13/code/game/objects/effects/spawners/custom_ghost_role_spawners.dm
@@ -29,7 +29,7 @@
. = ..()
var/area/A = get_area(src)
if(A)
- notify_ghosts("A Syndicate Crewmember is about to thaw from cryo on \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_SYNDICATE)
+ notify_ghosts("A Syndicate Crewmember is about to thaw from cryo on \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
/obj/effect/mob_spawn/human/nsv13/nt_prisoner/
name = "a prisoner stasis pod"
diff --git a/nsv13/code/modules/antagonists/bloodling.dm b/nsv13/code/modules/antagonists/bloodling.dm
index 69c3dc87153..e30d74b9320 100644
--- a/nsv13/code/modules/antagonists/bloodling.dm
+++ b/nsv13/code/modules/antagonists/bloodling.dm
@@ -283,6 +283,7 @@ Infestation! If given a human, it makes them a changeling thrall. If given any o
var/antag_hud_type = ANTAG_HUD_BLOODLING
var/antag_hud_name = "bloodling_thrall"
var/component_type = /datum/component/bloodling
+ banning_key = ROLE_BLOODLING
/datum/antagonist/bloodling/greet()
to_chat(owner.current, "We are the master! ")
@@ -813,7 +814,7 @@ Depending on what creature the entity gives life to, this can be EXTREMELY stron
refund_biomass(user, biomass_cost)
return FALSE
- var/list/candidates = pollCandidatesForMob("Do you want to play as a bloodling minion?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
+ var/list/candidates = pollCandidatesForMob("Do you want to play as a bloodling minion?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M) // see poll_ignore.dm
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
var/datum/component/bloodling/B = user.GetComponent(/datum/component/bloodling)
diff --git a/nsv13/code/modules/antagonists/ghostship/ghost_ship.dm b/nsv13/code/modules/antagonists/ghostship/ghost_ship.dm
new file mode 100644
index 00000000000..000247bb89c
--- /dev/null
+++ b/nsv13/code/modules/antagonists/ghostship/ghost_ship.dm
@@ -0,0 +1,8 @@
+/datum/antagonist/ghost_ship
+ name = "Ghost Ship"
+ show_name_in_check_antagonists = TRUE
+ show_in_antagpanel = FALSE
+ show_in_roundend = FALSE
+ banning_key = ROLE_GHOSTSHIP
+
+///Used for tracking and because the role_preferences needs an antag_datum to point at.
diff --git a/nsv13/code/modules/antagonists/role_preference/role_antagonists.dm b/nsv13/code/modules/antagonists/role_preference/role_antagonists.dm
new file mode 100644
index 00000000000..b46cee28337
--- /dev/null
+++ b/nsv13/code/modules/antagonists/role_preference/role_antagonists.dm
@@ -0,0 +1,7 @@
+/datum/role_preference/antagonist/bloodling
+ name = "Bloodling"
+ antag_datum = /datum/antagonist/bloodling
+
+/datum/role_preference/antagonist/pvp
+ name = "Galactic Conquest"
+ antag_datum = /datum/antagonist/nukeop/syndi_crew
diff --git a/nsv13/code/modules/antagonists/role_preference/role_midrounds.dm b/nsv13/code/modules/antagonists/role_preference/role_midrounds.dm
new file mode 100644
index 00000000000..38bf28a2bc9
--- /dev/null
+++ b/nsv13/code/modules/antagonists/role_preference/role_midrounds.dm
@@ -0,0 +1,7 @@
+/datum/role_preference/midround_ghost/ghost_ship
+ name = "Ghost Ship"
+ antag_datum = /datum/antagonist/ghost_ship
+
+/datum/role_preference/midround_ghost/boarder
+ name = "Boarder"
+ antag_datum = /datum/antagonist/traitor/boarder
diff --git a/nsv13/code/modules/cargo/objective_cargo.dm b/nsv13/code/modules/cargo/objective_cargo.dm
index 231b2242a96..15a86587059 100644
--- a/nsv13/code/modules/cargo/objective_cargo.dm
+++ b/nsv13/code/modules/cargo/objective_cargo.dm
@@ -42,7 +42,7 @@
/obj/structure/closet/crate/large/freight_objective/proc/poll_for_ghost_sentience()
for ( var/mob/living/simple_animal/M in contents )
if ( rand( 1, 20 ) == 20 ) // Random sentient mob event!
- var/list/candidates = pollCandidatesForMob("Do you want to play as [M]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M, POLL_IGNORE_SENTIENCE_POTION)
+ var/list/candidates = pollCandidatesForMob("Do you want to play as [M]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, M)
M.AIStatus = AI_ON // Keep the mob asleep unless the poll receives no candidates
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
diff --git a/nsv13/code/modules/overmap/ai-skynet.dm b/nsv13/code/modules/overmap/ai-skynet.dm
index eab22f31145..ef7f5a28f00 100644
--- a/nsv13/code/modules/overmap/ai-skynet.dm
+++ b/nsv13/code/modules/overmap/ai-skynet.dm
@@ -716,7 +716,7 @@ Adding tasks is easy! Just define a datum for it.
var/target_location = locate(rand(round(world.maxx/2) + 10, world.maxx - 39), rand(40, world.maxy - 39), OM.z)
var/obj/structure/overmap/selected_ship = pick(ship_list)
var/target_ghost
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(selected_ship.faction)] [initial(selected_ship.name)]?", ROLE_GHOSTSHIP, null, null, 20 SECONDS, POLL_IGNORE_GHOSTSHIP)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [initial(selected_ship.faction)] [initial(selected_ship.name)]?", ROLE_GHOSTSHIP, /datum/role_preference/midround_ghost/ghost_ship, 20 SECONDS, POLL_IGNORE_GHOSTSHIP)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
target_ghost = C
diff --git a/nsv13/code/modules/overmap/boarding/boarding.dm b/nsv13/code/modules/overmap/boarding/boarding.dm
index 5b96c56c9ed..cdad8f74b11 100644
--- a/nsv13/code/modules/overmap/boarding/boarding.dm
+++ b/nsv13/code/modules/overmap/boarding/boarding.dm
@@ -34,7 +34,7 @@
//20 or more players? You're allowed "real" boarders.
if(player_check >= min_players_for_ghosts) // Remove the low pop boarder camping
- candidates = pollCandidatesForMob("Do you want to play as a boarding team member?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 10 SECONDS, src)
+ candidates = pollCandidatesForMob("Do you want to play as a boarding team member?", ROLE_OPERATIVE, /datum/role_preference/midround_ghost/boarder, 10 SECONDS, src)
//No candidates? Well! Guess you get to deal with some KNPCs :))))))
if(!length(candidates))
return spawn_knpcs(amount, faction_selection)
diff --git a/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm b/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm
index 307cbf05edd..ed63d498484 100644
--- a/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm
+++ b/nsv13/code/modules/overmap/boarding/ghost_role_spawners.dm
@@ -1,3 +1,25 @@
+/obj/effect/mob_spawn/human/syndicate
+ name = "Syndicate Operative"
+ roundstart = FALSE
+ death = FALSE
+ icon = 'icons/obj/machines/sleeper.dmi'
+ icon_state = "sleeper_s"
+ outfit = /datum/outfit/syndicate_empty
+ assignedrole = "Space Syndicate" //I know this is really dumb, but Syndicate operative is nuke ops
+
+/datum/outfit/syndicate_empty
+ name = "Syndicate Operative Empty"
+ uniform = /obj/item/clothing/under/syndicate
+ shoes = /obj/item/clothing/shoes/combat
+ gloves = /obj/item/clothing/gloves/combat
+ ears = /obj/item/radio/headset/syndicate/alt
+ back = /obj/item/storage/backpack
+ implants = list(/obj/item/implant/weapons_auth)
+ id = /obj/item/card/id/syndicate
+
+/datum/outfit/syndicate_empty/post_equip(mob/living/carbon/human/H)
+ H.faction |= FACTION_SYNDICATE
+
/datum/outfit/syndicate_empty/boarding/captain
name = "Syndicate Captain (Boarding)"
id = /obj/item/card/id/syndicate/nuke_leader
diff --git a/nsv13/code/modules/overmap/overmap_ghosts.dm b/nsv13/code/modules/overmap/overmap_ghosts.dm
index 5f0f1f385ee..56b960f254a 100644
--- a/nsv13/code/modules/overmap/overmap_ghosts.dm
+++ b/nsv13/code/modules/overmap/overmap_ghosts.dm
@@ -16,7 +16,7 @@
if("Cancel")
return
if("Open")
- var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [src.faction] [src.name]?", ROLE_GHOSTSHIP, null, null, 20 SECONDS, POLL_IGNORE_GHOSTSHIP)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to pilot a [src.faction] [src.name]?", ROLE_GHOSTSHIP, /datum/role_preference/midround_ghost/ghost_ship, 20 SECONDS, POLL_IGNORE_GHOSTSHIP)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
target_ghost = C
@@ -84,6 +84,10 @@
ghost.hud_type = /datum/hud //Mostly blank hud
ghost.key = target.key
+ //More or less a modified version of how the morph antag gets the antag datum.
+ if(ghost.mind)
+ ghost.mind.add_antag_datum(/datum/antagonist/ghost_ship)
+
//Allows player to hear hails
mobs_in_ship += ghost
diff --git a/tgui/packages/tgui/interfaces/NotificationPreferences.js b/tgui/packages/tgui/interfaces/NotificationPreferences.js
deleted file mode 100644
index 91d992ee420..00000000000
--- a/tgui/packages/tgui/interfaces/NotificationPreferences.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { useBackend } from '../backend';
-import { Section, Button } from '../components';
-import { Window } from '../layouts';
-
-export const NotificationPreferences = (props, context) => {
- const { act, data } = useBackend(context);
-
- const ignoresPreSort = data.ignore || [];
- const ignores = ignoresPreSort.sort((a, b) => {
- const descA = a.desc.toLowerCase();
- const descB = b.desc.toLowerCase();
- if (descA < descB) {
- return -1;
- }
- if (descA > descB) {
- return 1;
- }
- return 0;
- });
-
- return (
-
-
-
- {ignores.map(ignore => (
- act('toggle_ignore', { key: ignore.key })} />
- ))}
-
-
-
- );
-};