diff --git a/.github/workflows/extra_pr_labels.yml b/.github/workflows/extra_pr_labels.yml
index f42a69933e3..08c629edce5 100644
--- a/.github/workflows/extra_pr_labels.yml
+++ b/.github/workflows/extra_pr_labels.yml
@@ -19,7 +19,7 @@ jobs:
commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request."
- name: Apply labels based on changed files
if: github.event_name != 'push'
- uses: actions/labeler@main
+ uses: actions/labeler@v4.3.0
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
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/_maps/map_files/Galactica/Galactica2.dmm b/_maps/map_files/Galactica/Galactica2.dmm
index 671564e3edb..a94c783bad5 100644
--- a/_maps/map_files/Galactica/Galactica2.dmm
+++ b/_maps/map_files/Galactica/Galactica2.dmm
@@ -8596,9 +8596,9 @@
/obj/effect/turf_decal/stripes/line{
dir = 1
},
-/obj/machinery/atmospherics/components/binary/valve/digital/on/layer4{
- dir = 8;
- name = "To Fueltank"
+/obj/machinery/atmospherics/components/binary/volume_pump/layer4{
+ dir = 4;
+ name = "Nucleium Tank To FTL"
},
/turf/open/floor/durasteel/techfloor_grid,
/area/engine/engineering/reactor_core)
@@ -38352,9 +38352,9 @@
/obj/machinery/atmospherics/pipe/simple/orange/visible/layer2{
dir = 4
},
-/obj/machinery/atmospherics/components/binary/valve/digital/on/layer4{
- dir = 8;
- name = "To Fueltank"
+/obj/machinery/atmospherics/components/binary/volume_pump/layer4{
+ name = "Nucleium Tank Out";
+ dir = 8
},
/turf/open/floor/durasteel/techfloor_grid,
/area/engine/engineering/reactor_core)
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/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index 0cc106ec9cf..b0e97e05e9b 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
-#define TGS_DMAPI_VERSION "6.6.2"
+#define TGS_DMAPI_VERSION "6.7.0"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
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/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 2934a830c8d..052b64f035f 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -413,6 +413,10 @@
config_entry_value = null
min_val = 500
+/datum/config_entry/number/client_warn_build
+ default = null
+ min_val = 0
+
/datum/config_entry/string/client_warn_message
config_entry_value = "Your version of byond may have issues or be blocked from accessing this server in the future."
@@ -429,6 +433,10 @@
config_entry_value = null
min_val = 0
+/datum/config_entry/number/client_max_build
+ config_entry_value = null
+ min_val = 0
+
/datum/config_entry/number/minute_topic_limit
config_entry_value = null
min_val = 0
diff --git a/code/controllers/subsystem/explosion.dm b/code/controllers/subsystem/explosion.dm
index 88560ae1d4f..f038003535e 100644
--- a/code/controllers/subsystem/explosion.dm
+++ b/code/controllers/subsystem/explosion.dm
@@ -415,6 +415,8 @@ SUBSYSTEM_DEF(explosions)
var/turf/T = locate(epicenter.x, epicenter.y, affecting_z)
if(!T)
continue
+ if(devastation_range - z_reduction <= 0 && heavy_impact_range - z_reduction <= 0 && light_impact_range - z_reduction <= 0) //NSV13 - explosions still relaying with 0-0-0 can cause REALLY weird behavior.
+ continue
SSexplosions.explode(T,
max(devastation_range - z_reduction, 0),
max(heavy_impact_range - z_reduction, 0),
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/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index fae0e73b471..6dddb203bc4 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -359,7 +359,7 @@ GENE SCANNER
else if(S.mutantstomach != initial(S.mutantstomach))
mutant = TRUE
- to_chat(user, "Species: [S.name][mutant ? "-derived mutant" : ""]")
+ to_chat(user, "Species: [S.name][mutant ? "-derived mutant" : ""]") //NSV13 - species name is colored depending on special conditions.
to_chat(user, "Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)")
// Time of death
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 += "
"
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/client_procs.dm b/code/modules/client/client_procs.dm
index 8091627245b..62c19659dec 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -4,7 +4,6 @@
#define UPLOAD_LIMIT 10485760 //Restricts client uploads to the server to 1MB //Could probably do with being lower.
-#define MAX_RECOMMENDED_CLIENT 1589
GLOBAL_LIST_INIT(blacklisted_builds, list(
"1407" = "bug preventing client display overrides from working leads to clients being able to see things/mobs they shouldn't be able to see",
@@ -264,28 +263,44 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]")
var/alert_mob_dupe_login = FALSE
+ var/alert_admin_multikey = FALSE
if(CONFIG_GET(flag/log_access))
- for(var/I in GLOB.clients)
- if(!I || I == src)
+ var/list/joined_players = list()
+ for(var/player_ckey in GLOB.joined_player_list)
+ joined_players[player_ckey] = 1
+
+ for(var/joined_player_ckey in (GLOB.directory | joined_players))
+ if (!joined_player_ckey || joined_player_ckey == ckey)
continue
- var/client/C = I
- if(C.key && (C.key != key) )
- var/matches
- if( (C.address == address) )
- matches += "IP ([address])"
- if( (C.computer_id == computer_id) )
- if(matches)
- matches += " and "
- matches += "ID ([computer_id])"
- alert_mob_dupe_login = TRUE
- if(matches)
- if(C)
- message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)].")
- log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)].")
- else
- message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(C)] (no longer logged in). ")
- log_access("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).")
+ var/datum/preferences/joined_player_preferences = GLOB.preferences_datums[joined_player_ckey]
+ if(!joined_player_preferences)
+ continue //this shouldn't happen.
+
+ var/client/C = GLOB.directory[joined_player_ckey]
+ var/in_round = ""
+ if (joined_players[joined_player_ckey])
+ in_round = " who has played in the current round"
+ var/message_type = "Notice"
+
+ var/matches
+ if(joined_player_preferences.last_ip == address)
+ matches += "IP ([address])"
+ if(joined_player_preferences.last_id == computer_id)
+ if(matches)
+ matches = "BOTH [matches] and "
+ alert_admin_multikey = TRUE
+ message_type = "MULTIKEY"
+ matches += "Computer ID ([computer_id])"
+ alert_mob_dupe_login = TRUE
+
+ if(matches)
+ if(C)
+ message_admins("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [key_name_admin(C)][in_round].")
+ log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [key_name(C)][in_round].")
+ else
+ message_admins("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round]. ")
+ log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round].")
if(GLOB.player_details[ckey])
player_details = GLOB.player_details[ckey]
player_details.byond_version = full_version
@@ -310,12 +325,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
to_chat_immediate(src, "Byond build [byond_build] ([byond_version].[byond_build]) has been blacklisted for the following reason: [GLOB.blacklisted_builds[num2text(byond_build)]].")
to_chat_immediate(src, "Please download a new version of byond. If [byond_build] is the latest, you can go to BYOND's website to download other versions.")
if(connecting_admin)
- to_chat(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions")
+ to_chat_immediate(src, "As an admin, you are being allowed to continue using this version, but please consider changing byond versions")
else
qdel(src)
return
- if(byond_build > MAX_RECOMMENDED_CLIENT)
- to_chat(src, "Your version of byond is over the maximum recommended version for clients (build [MAX_RECOMMENDED_CLIENT]) and may be unstable.")
+
+ var/max_recommended_client = CONFIG_GET(number/client_max_build)
+ if(byond_build > max_recommended_client)
+ to_chat(src, "Your version of byond is over the maximum recommended version for clients (build [max_recommended_client]) and may be unstable.")
to_chat(src, "Please download an older version of byond. You can go to BYOND's website to download other versions.")
if(SSinput.initialized)
set_macros()
@@ -323,41 +340,50 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
// Initialize tgui panel
tgui_panel.Initialize()
- if(alert_mob_dupe_login)
- spawn()
- alert(mob, "You have logged in already with another key this round, please log out of this one NOW or risk being banned!")
+ if(alert_mob_dupe_login && !holder)
+ var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!"
+ if (alert_admin_multikey)
+ dupe_login_message += "\nAdmins have been informed."
+ message_admins("MULTIKEYING: [key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")
+ log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")
+ spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer
+ alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please.
+ to_chat_immediate(mob, "[dupe_login_message]")
+
connection_time = world.time
connection_realtime = world.realtime
connection_timeofday = world.timeofday
winset(src, null, "command=\".configure graphics-hwmode on\"")
- var/cev = CONFIG_GET(number/client_error_version)
- var/ceb = CONFIG_GET(number/client_error_build)
- var/cwv = CONFIG_GET(number/client_warn_version)
- if (byond_version < cev || byond_build < ceb) //Out of date client.
+ var/breaking_version = CONFIG_GET(number/client_error_version)
+ var/breaking_build = CONFIG_GET(number/client_error_build)
+ var/warn_version = CONFIG_GET(number/client_warn_version)
+ var/warn_build = CONFIG_GET(number/client_warn_build)
+
+ if (byond_version < breaking_version || (byond_version == breaking_version && byond_build < breaking_build)) //Out of date client.
to_chat_immediate(src, "Your version of BYOND is too old:")
to_chat_immediate(src, CONFIG_GET(string/client_error_message))
to_chat_immediate(src, "Your version: [byond_version].[byond_build]")
- to_chat_immediate(src, "Required version: [cev].[ceb] or later")
+ to_chat_immediate(src, "Required version: [breaking_version].[breaking_build] or later")
to_chat_immediate(src, "Visit BYOND's website to get the latest version of BYOND.")
if (connecting_admin)
- to_chat(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade")
+ to_chat_immediate(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade")
else
qdel(src)
- return 0
- else if (byond_version < cwv) //We have words for this client.
+ return
+ else if (byond_version < warn_version || (byond_version == warn_version && byond_build < warn_build)) //We have words for this client.
if(CONFIG_GET(flag/client_warn_popup))
var/msg = "Your version of byond may be getting out of date: "
msg += CONFIG_GET(string/client_warn_message) + "
"
- msg += "Your version: [byond_version] "
- msg += "Required version to remove this message: [cwv] or later "
+ msg += "Your version: [byond_version].[byond_build] "
+ msg += "Required version to remove this message: [warn_version].[warn_build] or later "
msg += "Visit BYOND's website to get the latest version of BYOND. "
src << browse(msg, "window=warning_popup")
else
to_chat(src, "Your version of byond may be getting out of date:")
to_chat(src, CONFIG_GET(string/client_warn_message))
- to_chat(src, "Your version: [byond_version]")
- to_chat(src, "Required version to remove this message: [cwv] or later")
+ to_chat(src, "Your version: [byond_version].[byond_build]")
+ to_chat(src, "Required version to remove this message: [warn_version].[warn_build] or later")
to_chat(src, "Visit BYOND's website to get the latest version of BYOND.")
if (connection == "web" && !connecting_admin)
@@ -831,7 +857,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/url = winget(src, null, "url")
//special javascript to make them reconnect under a new window.
src << browse({"byond://[url]?token=[token]"}, "border=0;titlebar=0;size=1x1;window=redirect")
- to_chat(src, {"You will be automatically taken to the game, if not, click here to be taken manually"})
+ to_chat_immediate(src, {"You will be automatically taken to the game, if not, click here to be taken manually"})
/client/proc/note_randomizer_user()
add_system_note("CID-Error", "Detected as using a cid randomizer.")
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("
"
+ 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 += "
Updated development-side TGUI bootstrapping, updated internal TGUI API
-
Fixed some bugs related to TGUI Boxes not computing class names properly.
-
Refactored TGUI asset loading.
-
-
someone543 updated:
-
-
fixed Kadesh cruisers from being boarded
-
borgs can now leftclick to enter transport small crafts
-
-
-
06 October 2023
-
SerynEngi updated:
-
-
Added new nucleum preset engineering tile
-
Nucleum tank to the Gladius
-
Added nucleum gas to the nucleum tanks on the Gladius, Galactica, Tycoon and Aetherwhisp
-
-
-
29 September 2023
-
Absolucy updated:
-
-
LOOC is much less janky now. You can properly both hear LOOC as a ghost, and both hear and talk on LOOC while inside another object (including cases such as split personalities, recalled holoparasites, desynchronized mobs, jaunting mobs)!
-
Fixed admins seeing LOOC message senders as themselves rather the actual sender
-
-
-
21 September 2023
-
BlueHNT updated:
-
-
Added new Ju Jitsu move
-
Tweaked Ju Jitsu
-
-
DeltaFire15 updated:
-
-
Overmap objects with players should be a bit less likely to get stuck in nullspace.
-
-
Ikalpo updated:
-
-
fixed VLS eating torps that get unloaded
-
-
bruhlookatthisdood updated:
-
-
Fixes snake atmospherics having wrong injector and vent types in fuel mix chamber
-
Fixes snake atmospherics having wrong vent type in air chamber
fixed Gladius cell lockers not being genpop lockers
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index a438826d218..f57aa3b2506 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -1815,3 +1815,58 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- rscadd: Ghost controlled ships can now use gauss
- rscadd: Added coffee machines to PVP ships
- tweak: Syndicate coffee drinkers now get a altered mood event
+2023-11-24:
+ Bokkiewokkie:
+ - rscadd: Added Syndicate light fighter plushie
+ - rscadd: Added DIY fighter plush bomb kit traitor item
+2023-12-01:
+ Bokkiewokkie:
+ - imageadd: Added munitions shirts, tank tops and socks
+ DeltaFire15:
+ - tweak: Some small Galactica changes. Don't worry about it.
+ - bugfix: Fixes a random weird thing with explosions I stumbled upon.
+ covertcorvid:
+ - config: Made repo config client version match the server
+ - server: Increased max recommended client version to 515.1608
+2023-12-15:
+ Bokkiewokkie:
+ - rscadd: Added special offers to traders, which have to be unlocked with faction
+ ticket points
+ - balance: Made gun parts available for purchase at Whiterapids' Munitions
+ DeltaFire15:
+ - bugfix: Ethereal starvation no longer has a nutrition window where increased brute
+ vulnerability was not handled.
+ itsmeow, EvilDragonfiend:
+ - rscadd: All non-ghost role antagonist preferences are now per-character. Global
+ toggle buttons toggle the preference on all characters at once.
+ - refactor: Rewrote antag preference internals to allow for better control of antag
+ preferences and more distinct usages.
+ - refactor: Cleaned up antag bans, with more distinct settings and new general categories.
+ - tweak: All antag prefs will start on by default rather than off. The same applies
+ to newly added ones.
+ - bugfix: Fixed various antag bans being bypassable.
+ - bugfix: Fixed various ghost polls using the wrong preference or ban types.
+ - rscdel: Removed BYOND account age checks from antag roles in favor of living hours.
+ - rscdel: Removed "Midround Antagonist" preference in favor of unique preferences
+ per midround.
+ - rscadd: Added distinct role preferences for Antagonists (Roundstart/Latejoin)
+ and Midrounds (Ghost/Living).
+ - rscadd: Almost all ghost antagonist polls have a "Never for this round" option
+ now.
+ - rscdel: Removed the ghost notification preferences menu (poll ignore menu).
+ - tweak: The lavaland beach bartender can now speak Stoner.
+ - rscdel: Removed some unused mob spawners from the code.
+ - rscadd: Added 4hr playtime checks to roundstart/latejoin antag roles. Wizard and
+ nukeops are 8hr.
+ - bugfix: Fixed a typo in the skeleton spawner description.
+ - bugfix: Fixed unnecessary DB queries due to is_banned_from not properly ckey-ifying
+ the ckey parameter and causing cache misses.
+2023-12-29:
+ DeltaFire15:
+ - bugfix: Syndicate AI boarders no longer silently breach your hull.
+ - bugfix: Thirring Drive Pylons should no longer be able to get stuck in the "shutting
+ down" state.
+2024-01-03:
+ Bokkiewokkie:
+ - imageadd: Changed Syndicate small craft color scheme to be more red
+ - code_imp: Renamed syndicate sabre and rapier sprite files
diff --git a/nsv13.dme b/nsv13.dme
index d7ab5d32414..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,9 +3850,12 @@
#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\unary_devices\outlet_injector.dm"
#include "nsv13\code\modules\atmospherics\machinery\components\binary_devices\constrictor.dm"
+#include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\outlet_injector.dm"
#include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\tank.dm"
#include "nsv13\code\modules\atmospherics\machinery\components\unary_devices\vent_pump.dm"
#include "nsv13\code\modules\cargo\mission_rewards.dm"
@@ -3910,10 +3919,12 @@
#include "nsv13\code\modules\mining\asteroid.dm"
#include "nsv13\code\modules\mob\mob.dm"
#include "nsv13\code\modules\mob\mob_helpers.dm"
+#include "nsv13\code\modules\mob\dead\new_player\sprite_accessories.dm"
#include "nsv13\code\modules\mob\dead\observer\oberserver.dm"
#include "nsv13\code\modules\mob\living\carbon\carbon.dm"
#include "nsv13\code\modules\mob\living\carbon\examine_tgui.dm"
#include "nsv13\code\modules\mob\living\carbon\human\nsv_emotes.dm"
+#include "nsv13\code\modules\mob\living\carbon\human\nsv_human_helpers.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\catgirl.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\nanotrasen_knpc.dm"
#include "nsv13\code\modules\mob\living\carbon\human\species_types\other_knpc.dm"
diff --git a/nsv13/code/__DEFINES/overmap.dm b/nsv13/code/__DEFINES/overmap.dm
index beb76f66c13..8bb8ce50961 100644
--- a/nsv13/code/__DEFINES/overmap.dm
+++ b/nsv13/code/__DEFINES/overmap.dm
@@ -113,3 +113,9 @@ GLOBAL_LIST_INIT(overmap_impact_sounds, list('nsv13/sound/effects/ship/freespace
#define MASS_LARGE 7 //20-40 Players - Medium Capital Ships
#define MASS_TITAN 150 //40+ Players - Large Capital Ships
#define MASS_IMMOBILE 200 //Things that should not be moving. See: stations
+
+//Fun tools
+#define SHIELD_NOEFFECT 0 //!Shield failed to absorb hit.
+#define SHIELD_ABSORB 1 //!Shield absorbed hit.
+#define SHIELD_FORCE_DEFLECT 2 //!Shield absorbed hit and is redirecting projectile with slightly turned vector.
+#define SHIELD_FORCE_REFLECT 3 //!Shield absorbed hit and is redirecting projectile in reverse direction.
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/datums/holocall.dm b/nsv13/code/datums/holocall.dm
index 9f7b712eab5..8b6d7711b09 100644
--- a/nsv13/code/datums/holocall.dm
+++ b/nsv13/code/datums/holocall.dm
@@ -26,7 +26,7 @@
DELAY 20
PRESET /datum/preset_holoimage/corgi
NAME Burst Data
- LANGUAGE /datum/language/eal
+ LANGUAGE /datum/language/machine
SAY START NTINTEL METADATA
SAY RECORDED 12-17-0000
SAY SECURITY CLASS UNCLASSIFIED
@@ -320,7 +320,7 @@
DELAY 50
SAY If you need to shut down the reactor, lower the nucleium injection rate slowly. You can cycle coolant in an emergency for a quick cooling boost.
DELAY 50
- SAY The reaction can be terminated when the reactor core is under 100 Celsius. Ensure cooling is adequate to achieve this.
+ SAY The reaction can be terminated when the reactor core is under 200 Celsius. Ensure cooling is adequate to achieve this.
DELAY 50
SAY Finally. If your minimum input power ever starts to converge on the maximum, you are heading towards an emission. Rectify this immediately, or shut down the reactor safely.
DELAY 50
@@ -328,7 +328,7 @@
DELAY 50
SAY Do your duty. This tape should be destroyed after use. Shield technology does not exist. Glory to Nanotrasen.
NAME Burst Data
- LANGUAGE /datum/language/eal
+ LANGUAGE /datum/language/machine
DELAY 20
SAY START METADATA
SAY RECORDED 5-25-0000
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/general_quarters/dropship_types.dm b/nsv13/code/game/general_quarters/dropship_types.dm
index 80bf3107f12..322745c3c05 100644
--- a/nsv13/code/game/general_quarters/dropship_types.dm
+++ b/nsv13/code/game/general_quarters/dropship_types.dm
@@ -189,7 +189,7 @@ Credit to TGMC for the interior sprites for all these!
/obj/structure/overmap/small_craft/transport/sabre/syndicate
name = "\improper Syndicate Utility Vessel"
desc = "A boarding craft for rapid troop deployment. It contains a full combat medical bay for establishing FOBs."
- icon = 'nsv13/icons/overmap/syndicate/syn_raptor.dmi'
+ icon = 'nsv13/icons/overmap/syndicate/syn_sabre.dmi'
req_one_access = list(ACCESS_SYNDICATE)
faction = "syndicate"
start_emagged = TRUE
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/game/objects/items/nsv13_plushes.dm b/nsv13/code/game/objects/items/nsv13_plushes.dm
index ab35636fe29..e648207b27a 100644
--- a/nsv13/code/game/objects/items/nsv13_plushes.dm
+++ b/nsv13/code/game/objects/items/nsv13_plushes.dm
@@ -25,6 +25,11 @@
attack_verb = list("bumped", "rammed", "missiled")
squeak_override = list('nsv13/sound/effects/fighters/autocannon.ogg'=1)
+/obj/item/toy/plush/lfighter/synlfighter
+ name = "Syndicate light fighter plush"
+ desc = "An adorable stuffed toy shaped like a Su-818 Rapier light fighter with Syndicate livery."
+ icon_state = "synlightfighter"
+
/obj/item/toy/plush/transport
name = "utility craft plush"
desc = "An adorable stuffed toy shaped like a Su-437 Sabre utility vessel."
diff --git a/nsv13/code/game/objects/items/storage_items.dm b/nsv13/code/game/objects/items/storage_items.dm
index 50c2c0f8d3c..3bbae9f0c02 100644
--- a/nsv13/code/game/objects/items/storage_items.dm
+++ b/nsv13/code/game/objects/items/storage_items.dm
@@ -63,3 +63,11 @@
new /obj/item/clothing/gloves/maid(src)
new /obj/item/clothing/under/costume/maid(src)
+/obj/item/storage/box/syndie_kit/plushie
+ name = "\improper DIY plushbomb kit"
+
+/obj/item/storage/box/syndie_kit/plushie/PopulateContents()
+ new /obj/item/toy/plush/lfighter/synlfighter(src)
+ new /obj/item/kitchen/knife/combat/survival(src)
+ new /obj/item/grenade/syndieminibomb(src)
+ new /obj/item/screwdriver(src)
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/mob/dead/new_player/sprite_accessories.dm b/nsv13/code/modules/mob/dead/new_player/sprite_accessories.dm
new file mode 100644
index 00000000000..603431e0bb2
--- /dev/null
+++ b/nsv13/code/modules/mob/dead/new_player/sprite_accessories.dm
@@ -0,0 +1,20 @@
+
+/datum/sprite_accessory/undershirt/munitions
+ icon = 'nsv13/icons/mob/underwear.dmi'
+ name = "Shirt (Munitions)"
+ icon_state = "shirt_mun"
+
+/datum/sprite_accessory/undershirt/tank_munitions
+ icon = 'nsv13/icons/mob/underwear.dmi'
+ name = "Tank Top (Munitions)"
+ icon_state = "tank_mun"
+
+/datum/sprite_accessory/socks/munitions_thigh
+ icon = 'nsv13/icons/mob/underwear.dmi'
+ name = "Thigh-high (Munitions)"
+ icon_state = "mun_thigh"
+
+/datum/sprite_accessory/socks/munitions_knee
+ icon = 'nsv13/icons/mob/underwear.dmi'
+ name = "Knee-high (Munitions)"
+ icon_state = "mun_knee"
diff --git a/nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm b/nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm
new file mode 100644
index 00000000000..c54880f6993
--- /dev/null
+++ b/nsv13/code/modules/mob/living/carbon/human/nsv_human_helpers.dm
@@ -0,0 +1,25 @@
+/**
+ * # `species_examine_font()`
+ *
+ * This gets a humanoid's special examine font, which is used to color their species name during examine / health analyzing.
+ * The first of these that applies is returned.
+ * Returns:
+ * * Metallic font if robotic
+ * * Cyan if a toxinlover
+ * * Yellow-ish if an Ethereal
+ * * Purple if plasmaperson
+ * * Rock / Brownish if a golem
+ * * Green if none of the others apply (aka, generic organic)
+*/
+/mob/living/carbon/human/proc/species_examine_font()
+ if((MOB_ROBOTIC in mob_biotypes))
+ return "sc_robotic"
+ if(HAS_TRAIT(src, TRAIT_TOXINLOVER))
+ return "sc_toxlover"
+ if(isethereal(src))
+ return "sc_ethereal"
+ if(isplasmaman(src))
+ return "sc_plasmaman"
+ if(isgolem(src))
+ return "sc_golem"
+ return "sc_normal"
diff --git a/nsv13/code/modules/overmap/FTL/components/drive.dm b/nsv13/code/modules/overmap/FTL/components/drive.dm
index 35323a02579..a1880a21904 100644
--- a/nsv13/code/modules/overmap/FTL/components/drive.dm
+++ b/nsv13/code/modules/overmap/FTL/components/drive.dm
@@ -363,6 +363,8 @@ Preset classes of FTL drive with pre-programmed behaviours
jump_speed_pylon = initial(jump_speed_pylon)
if(shutdown_pylons)
for(var/obj/machinery/atmospherics/components/binary/drive_pylon/P as() in pylons)
+ if(P.pylon_state == PYLON_STATE_OFFLINE || P.pylon_state == PYLON_STATE_SHUTDOWN)
+ continue
P.set_state(PYLON_STATE_SHUTDOWN)
cooldown = TRUE
addtimer(CALLBACK(src, PROC_REF(post_cooldown), auto_spool_enabled), FTL_COOLDOWN)
diff --git a/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm b/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm
index aefbebf3fc6..f779618c35d 100644
--- a/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm
+++ b/nsv13/code/modules/overmap/FTL/components/drive_pylon.dm
@@ -387,16 +387,9 @@
else
to_chat(user, "You don't think it would be wise to touch this right now.")
-
-#undef PYLON_STATE_OFFLINE
-#undef PYLON_STATE_STARTING
-#undef PYLON_STATE_WARMUP
-#undef PYLON_STATE_SPOOLING
-#undef PYLON_STATE_ACTIVE
-#undef PYLON_STATE_SHUTDOWN
-
#undef MAX_WASTE_OUTPUT_PRESSURE
#undef MAX_WASTE_STORAGE_PRESSURE
+#undef WASTE_GAS_HEAT
#undef PYLON_ACTIVE_EXPONENT
#undef POWER_FAIL_TOLERANCE
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..39ed9034bb9 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)
@@ -107,6 +107,11 @@
message_admins("KNPC boarder spawn aborted. This ship does not support KNPCs (add some patrol nodes!)")
throw EXCEPTION("KNPC boarder spawn aborted. This ship does not support KNPCs (add some patrol nodes!)")
+ switch(faction_selection)
+ if("syndicate")
+ relay('nsv13/sound/effects/ship/boarding_pod.ogg', "You can hear several tethers attaching to the ship.")
+ else //No other special cases exist but this is a switch anyways to support them in the future (pirates have no tell)
+
var/obj/structure/closet/supplypod/syndicate_odst/toLaunch = new()
var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
toLaunch.forceMove(shippingLane)
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/fighters/_fighters.dm b/nsv13/code/modules/overmap/fighters/_fighters.dm
index 05fa41abc6d..7b930cdfd01 100644
--- a/nsv13/code/modules/overmap/fighters/_fighters.dm
+++ b/nsv13/code/modules/overmap/fighters/_fighters.dm
@@ -455,7 +455,7 @@ Been a mess since 2018, we'll fix it someday (probably)
/obj/structure/overmap/small_craft/combat/light/syndicate //PVP MODE
name = "Syndicate Light Fighter"
desc = "The Syndicate's answer to Nanotrasen's light fighter craft, this fighter is designed to maintain aerial supremacy."
- icon = 'nsv13/icons/overmap/syndicate/syn_viper.dmi'
+ icon = 'nsv13/icons/overmap/syndicate/syn_rapier.dmi'
req_one_access = ACCESS_SYNDICATE
faction = "syndicate"
start_emagged = TRUE
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/nsv13/code/modules/overmap/pdsr.dm b/nsv13/code/modules/overmap/pdsr.dm
index 36ee7c4b56f..6c52dcb62d7 100644
--- a/nsv13/code/modules/overmap/pdsr.dm
+++ b/nsv13/code/modules/overmap/pdsr.dm
@@ -9,8 +9,11 @@
#define REACTOR_STATE_SHUTTING_DOWN 4
#define REACTOR_STATE_EMISSION 5
+#define DENSITY_LOW 0 //! Deflects only heavy hits.
+#define DENSITY_HIGH 1 //! Deflects all hits.
+
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor
- name = "mk I Prototype Defence Screen Reactor"
+ name = "mk II Prototype Defence Screen Reactor"
desc = "A highly experimental, unstable and highly illegal nucleium driven reactor for the generation of defensive screens."
icon = 'nsv13/icons/obj/machinery/pdsr.dmi'
icon_state = "idle"
@@ -46,10 +49,20 @@
var/last_coolant_time = 0 //Last time we called to flush coolant
var/flushing_coolant = 0 //Are we currently flushing coolant
var/emission_tracker = 0 //Used to track emission timers
+ ///Time when our reactor was last shutdown.
+ var/powerdown_time = 0
+ ///If this is already detonating
+ var/detonating = FALSE
//!Shield Vars
- var/list/shield = list("integrity" = 0, "max_integrity" = 0, "stability" = 0)
+ var/list/shield = list("integrity" = 0, "max_integrity" = 0, "stability" = 0, "density" = DENSITY_HIGH)
var/power_input = 0 //How much power is currently allocated
+ ///Did we get enough power last power tick?
+ var/power_demand_met = FALSE
+ ///How much power did we use during the power tick?
+ var/last_power_use = 0
+ ///How much power was in the grid during power tick?
+ var/last_avail_power = 0
var/screen_regen = 50 //Allocation to regenerate the !shields
var/screen_hardening = 50 //Allocation to strengthen the !shields
var/min_power_input = 0 //Minimum power required to sustain !shield integrity
@@ -95,17 +108,28 @@
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/try_use_power(amount)
var/turf/T = get_turf(src)
C = T.get_cable_node()
- if(C?.surplus() > amount)
+ if(C?.surplus() >= amount)
C.powernet.load += amount
+ last_power_use = amount
+ power_demand_met = TRUE
+ last_avail_power = C?.surplus()
return TRUE
+ power_demand_met = FALSE
+ last_power_use = 0
+ last_avail_power = C?.surplus()
return FALSE
-/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/process()
+/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/process_atmos()
update_parents()
- if(next_slowprocess < world.time)
+ if(next_slowprocess <= world.time)
slowprocess()
next_slowprocess = world.time + 1 SECONDS
+/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/process()
+ if(state == REACTOR_STATE_IDLE)
+ return
+ try_use_power(power_input)
+
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/slowprocess()
var/datum/gas_mixture/nucleium_input = airs[2]
var/datum/gas_mixture/coolant_input = airs[1]
@@ -124,31 +148,32 @@
return
current_uptime ++
- reaction_containment += 5
+ reaction_containment += 20 // ~5 seconds as opposed to 20 for core start.
if(reaction_containment >= 100)
reaction_containment = 100
if(reaction_injection_rate < 2.5)
say("Error: Unable to initialise reaction, insufficient nucleium injection.")
- reaction_containment = 0
- current_uptime = 0
- state = REACTOR_STATE_IDLE
+ handle_shutdown()
return
if(nuc_in < reaction_injection_rate)
say("Error: Unable to initialise reaction, insufficient nucleium available.")
- reaction_containment = 0
- current_uptime = 0
- state = REACTOR_STATE_IDLE
+ handle_shutdown()
+ return
+ if(!power_demand_met)
+ say("Error: Power allocation exceeding grid capacity. Failed to initiate reaction.")
+ handle_shutdown()
return
- var/errors = rand(20, 200)
+ var/errors = rand(20, 199)
say("Initiating Reaction - Injecting Nucleium.")
say("Reaction Initialized - [errors] runtimes supressed.")
reaction_temperature = 100 //Flash start to 100
+ shield["stability"] = 50 //begin at 50 during startup.
state = REACTOR_STATE_RUNNING
if(state == REACTOR_STATE_RUNNING)
- if(nuc_in >= reaction_injection_rate) //If we are running in nominal conditions...
+ if(nuc_in >= reaction_injection_rate && reaction_injection_rate >= 2.5) //If we are running in nominal conditions...
nucleium_input.adjust_moles(GAS_NUCLEIUM, -reaction_injection_rate)
//Handle reaction rate adjustments here
var/target_reaction_rate = ((0.5 + (1e-03 * (reaction_injection_rate ** 2))) + (current_uptime / 2000)) * 16
@@ -157,7 +182,7 @@
reaction_temperature += reaction_rate * 0.35 //Function goes
handle_polarity(TRUE)
- else if(nuc_in < reaction_injection_rate) //If we are running without sufficient nucleium...
+ else //If we are running without sufficient nucleium...
if(nuc_in <= 0) //...and none at all
var/target_reaction_rate = 0
var/delta_reaction_rate = target_reaction_rate - reaction_rate
@@ -176,7 +201,7 @@
handle_polarity(TRUE)
if(reaction_rate > 5) //TEMP USE FUNCTIONS
- reaction_energy_output = (reaction_rate + (reaction_injection_rate / 2)) * (2 - (current_uptime / 20000)) //FUNCTIONS
+ reaction_energy_output = (reaction_rate + (min(nuc_in, reaction_injection_rate) / 2)) * (2 - (current_uptime / 20000)) //FUNCTIONS
radiation_pulse(src, reaction_energy_output)
else
@@ -205,21 +230,21 @@
state = REACTOR_STATE_SHUTTING_DOWN
else
say("Error: Reaction Prematurely Terminated - Inspect all systems for damage.")
- state = REACTOR_STATE_IDLE
+ var/list/overload_candidate = list()
+ for(var/obj/machinery/defence_screen_relay/DSR in GLOB.machines)
+ if(DSR.powered() && DSR.overloaded == FALSE)
+ overload_candidate += DSR
for(var/I = 0, I < 3, I++) //Overload Three Relays
- var/list/overload_candidate = list()
- for(var/obj/machinery/defence_screen_relay/DSR in GLOB.machines)
- if(DSR.powered() && DSR.overloaded == FALSE)
- overload_candidate += DSR
- if(overload_candidate.len > 0)
- var/obj/machinery/defence_screen_relay/DSRC = pick(overload_candidate)
- DSRC.overload()
+ if(overload_candidate.len > 0)
+ var/obj/machinery/defence_screen_relay/DSRC = pick_n_take(overload_candidate)
+ DSRC.overload()
depower_shield()
OM.take_quadrant_hit(rand(100, 200), "forward_port")
OM.take_quadrant_hit(rand(100, 200), "forward_starboard")
OM.take_quadrant_hit(rand(100, 200), "aft_port")
OM.take_quadrant_hit(rand(100, 200), "aft_starboard")
+ state = REACTOR_STATE_SHUTTING_DOWN
handle_screens()
handle_temperature()
@@ -283,22 +308,23 @@
//////Reactor Procs//////
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_containment() //We manage poweruse and containment here
- if(try_use_power(power_input))
- if(power_input > max_power_input && power_input <= 1.25 * max_power_input) //Overloading Containment - Rapid Rise
+ if(power_demand_met)
+ if(last_power_use > max_power_input && last_power_use <= 1.25 * max_power_input) //Overloading Containment - Rapid Rise
var/overloading_containment = reaction_containment
if(overloading_containment < 25)
overloading_containment = 25
var/overloading_function = ((382 * NUM_E **(0.0764 * overloading_containment)) / ((50 + NUM_E ** (0.0764 * overloading_containment)) ** 2)) * 14
- reaction_containment += overloading_function * (power_input / max_power_input)
+ reaction_containment += overloading_function * (last_power_use / max_power_input)
current_uptime ++ //Overloading has a cost
- else if(power_input >= min_power_input && power_input <= max_power_input) //Nominal Containment - Maintain Containment
+ else if(last_power_use >= min_power_input && last_power_use <= max_power_input) //Nominal Containment - Maintain Containment
var/containment_function = ((382 * NUM_E **(0.0764 * reaction_containment)) / ((50 + NUM_E ** (0.0764 * reaction_containment)) ** 2)) * 10
- reaction_containment += containment_function * (power_input / max_power_input)
+ reaction_containment += containment_function * (last_power_use / max_power_input)
- else if(power_input < min_power_input && power_input >= 0.75 * min_power_input) //Insufficient Power for Containment - Slow Loss
+ else if(last_power_use < min_power_input && last_power_use >= 0.75 * min_power_input) //Insufficient Power for Containment - Slow Loss
var/loss_function = ((382 * NUM_E **(0.0764 * reaction_containment)) / ((50 + NUM_E ** (0.0764 * reaction_containment)) ** 2)) * 4
- reaction_containment += loss_function * (power_input / max_power_input)
+ reaction_containment += loss_function * (last_power_use / max_power_input)
+
if(reaction_containment > 100)
reaction_containment = 100
@@ -307,7 +333,7 @@
reaction_containment = 0
emission_tracker = world.time
say("Error: Catatstropic Containment Failure - Initializing Emergency Termination Protocols")
- playsound(src, 'sound/magic/lightning_chargeup.ogg', 100, 0, 15, 10, 10) //Replace me later?
+ playsound(src, 'sound/magic/lightning_chargeup.ogg', 100, FALSE, 15) //Replace me later?
state = REACTOR_STATE_EMISSION //Whoops
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_power_reqs() //How much power is required
@@ -320,7 +346,7 @@
if(reaction_containment < 33 && state == REACTOR_STATE_RUNNING)
if(next_alarm_sfx < world.time)
next_alarm_sfx = world.time + 3 SECONDS
- playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, 0, 10, 10)
+ playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, FALSE, 10)
if(next_alarm_message < world.time)
next_alarm_message = world.time + 15 SECONDS
say("DANGER: Reaction Containment Critical. Emission Imminent.")
@@ -328,7 +354,7 @@
if(state == REACTOR_STATE_EMISSION)
if(next_alarm_sfx < world.time)
next_alarm_sfx = world.time + 3 SECONDS
- playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, 0, 10, 10)
+ playsound(src, 'nsv13/sound/effects/ship/pdsr_warning.ogg', 100, FALSE, 10)
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_polarity(var/injecting = FALSE)
if(reaction_polarity_timer < world.time) //Handle the trend
@@ -343,11 +369,7 @@
else
reaction_polarity -= 0.027
- if(reaction_polarity > 1)
- reaction_polarity = 1
-
- if(reaction_polarity < -1)
- reaction_polarity = -1
+ reaction_polarity = clamp(reaction_polarity, -1, 1)
var/polarity_function = abs(0.5 * (reaction_polarity ** 2)) //RECHECK THIS WHEN NOT DEAD
reaction_containment -= polarity_function
@@ -360,7 +382,7 @@
if(in_view_range(M, src))
to_chat(M, "A stream of particles erupts from the [src]!")
M.flash_act(1, 0, 1)
- playsound(src, 'sound/magic/repulse.ogg', 100, 0, 5, 5)
+ playsound(src, 'sound/magic/repulse.ogg', 100, FALSE, 5)
//more goes here
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_emission_release()
@@ -368,13 +390,13 @@
if(DSR.powered() && DSR.overloaded == FALSE)
DSR.overload()
- var/emission_energy = reaction_energy_output * (1 + (current_uptime / 1000))
+ var/emission_energy = max(10, reaction_energy_output) * (1 + (current_uptime / 1000))
radiation_pulse(src, emission_energy ** 2, 10, 1, 1)
for(var/mob/living/M in OM.mobs_in_ship)
if(M.client)
- M.flash_act((emission_energy / 10), 0 , 1)
- M.Knockdown(emission_energy / 10)
+ M.flash_act(clamp(emission_energy, 30, 100), TRUE, TRUE)
+ M.Knockdown(clamp(emission_energy, 20, 100))
M.adjust_disgust(rand(20, 50))
to_chat(M, "A wash of radiation passes through you!")
@@ -390,6 +412,7 @@
say("Inspect all systems for damage.")
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_temperature()
+ reaction_containment -= (reaction_temperature / 50) + (current_uptime / 2000)
var/turf/open/L = get_turf(src)
if(!istype(L) || !(L.air))
return
@@ -401,7 +424,6 @@
env.set_temperature(temperature += delta_env / 2)
air_update_turf()
- reaction_containment -= (reaction_temperature / 50) + (current_uptime / 2000)
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/handle_atmos_check()
for(var/obj/machinery/defence_screen_relay/DSR in GLOB.machines)
@@ -418,6 +440,10 @@
flushing_coolant = 0
reaction_energy_output = 0
emission_tracker = 0
+ powerdown_time = world.time
+ last_power_use = 0
+ power_demand_met = FALSE
+ last_avail_power = 0
depower_shield()
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/update_icon()
@@ -442,22 +468,28 @@
//////Shield Procs//////
-/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/absorb_hit(damage)
+/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/absorb_hit(obj/item/projectile/proj)
+ var/damage = proj.damage
if(!active)
- return FALSE //!shields not raised
+ return SHIELD_NOEFFECT //!shields not raised
+
+ if(shield["density"] == DENSITY_LOW && (proj.flag != "overmap_heavy" || proj.damage_type == BURN)) //Low density mode does not get hit by low impact projectiles, but also does not help vs. energy weapons.
+ return SHIELD_NOEFFECT
if(shield["integrity"] >= damage)
shield["integrity"] -= damage //Deduct from !shield
var/current_hit = world.time
- if(current_hit <= last_hit + 10) //1 Second
- shield["stability"] -= rand((damage / 10), (damage / 5)) //Rapid hits will reduce stability greatly
+ if(current_hit <= last_hit + 1 SECONDS) //1 Second
+ shield["stability"] -= min(30, rand((damage / 10), (damage / 5))) //Rapid hits will reduce stability greatly
else
- shield["stability"] -= rand((damage / 50), (damage / 25)) //Reduce !shield stability
+ shield["stability"] -= min(10, rand((damage / 50), (damage / 25))) //Reduce !shield stability
last_hit = current_hit //Set our last hit
check_stability()
- return TRUE
+ return SHIELD_FORCE_DEFLECT
+
+ return SHIELD_NOEFFECT
/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/proc/check_stability()
if(shield["stability"] <= 0)
@@ -483,12 +515,16 @@
active = TRUE //Renable !shields
else if(active)
- shield["stability"] += power_input / ((max_power_input * 1.5) - max(min_power_input, 0))
- if(shield["stability"] > 100)
- shield["stability"] = 100
var/hardening_allocation = max(((screen_hardening / 100) * reaction_energy_output), 0)
shield["max_integrity"] = hardening_allocation * (connected_relays * 10) //Each relay is worth 10 base
var/regen_allocation = max(((screen_regen / 100) * reaction_energy_output), 0)
+
+ var/stability_recovery = last_power_use / ((max_power_input * 1.5) - max(min_power_input, 0))
+ if(screen_regen == 100) //Stopping field emission entirely helps with stabilization.
+ stability_recovery *= 5
+ shield["stability"] += stability_recovery
+ if(shield["stability"] > 100)
+ shield["stability"] = 100
shield["integrity"] += regen_allocation
if(shield["integrity"] > shield["max_integrity"])
shield["integrity"] = shield["max_integrity"]
@@ -507,10 +543,26 @@
shield["stability"] = 0
active = FALSE
+/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/ex_act(severity, target)
+ if(CHECK_BITFIELD(resistance_flags, INDESTRUCTIBLE))
+ return
+ if(QDELETED(src))
+ return
+ if(detonating)
+ return
+ detonating = TRUE
+ visible_message("[src] destabilizes violently.")
+ radiation_pulse(src, 5000)
+ explosion(get_turf(src), 5, 8, 0, 10, ignorecap = TRUE, flame_range = 10)
+ qdel(src)
+
+
+
+
//////MAINFRAME CONSOLE//////
/obj/machinery/computer/ship/defence_screen_mainframe_reactor //For controlling the reactor
- name = "mk I Prototype Defence Screen Mainframe"
+ name = "mk II Prototype Defence Screen Mainframe"
desc = "The mainframe controller for the PDSR"
icon_screen = "idhos" //temp
req_access = list(ACCESS_ENGINE)
@@ -536,12 +588,12 @@
/obj/machinery/computer/ship/defence_screen_mainframe_reactor/attack_hand(mob/user)
if(!allowed(user))
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Access denied")
return
if(!reactor)
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Unable to detect linked reactor")
return
@@ -551,7 +603,7 @@
. = ..()
if(!reactor)
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Unable to detect linked reactor")
return
ui_interact(user)
@@ -560,7 +612,7 @@
. = ..()
if(!reactor)
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Unable to detect linked reactor")
return
ui_interact(user)
@@ -591,12 +643,8 @@
return
var/adjust = text2num(params["adjust"])
if(action == "injection_allocation")
- if(adjust && isnum(adjust))
- reactor.reaction_injection_rate = adjust
- if(reactor.reaction_injection_rate > 25)
- reactor.reaction_injection_rate = 25
- if(reactor.reaction_injection_rate < 0)
- reactor.reaction_injection_rate = 0
+ if(isnum(adjust))
+ reactor.reaction_injection_rate = clamp(adjust, 0, 25)
switch(action)
if("polarity")
@@ -604,6 +652,9 @@
return
if("ignition")
+ if(world.time < reactor.powerdown_time + 30 SECONDS)
+ reactor.say("Realigning Particle Emitter - Field Unavailable.")
+ return
if(reactor.state == REACTOR_STATE_IDLE)
if(reactor.power_input >= reactor.min_power_input)
reactor.say("Initiating Reaction - Charging Containment Field")
@@ -633,6 +684,7 @@
reactor.say("Error: Unable to comply - Reactor parameters outside safe shutdown limits")
return
+
/obj/machinery/computer/ship/defence_screen_mainframe_reactor/ui_data(mob/user)
var/list/data = list()
data["r_temp"] = reactor.reaction_temperature
@@ -654,13 +706,13 @@
return data
/obj/item/circuitboard/computer/defence_screen_mainframe_reactor
- name = "mk I Prototype Defence Screen Mainframe (Computer Board)"
+ name = "mk II Prototype Defence Screen Mainframe (Computer Board)"
build_path = /obj/machinery/computer/ship/defence_screen_mainframe_reactor
//////SCREEN MANIPULATOR//////
/obj/machinery/computer/ship/defence_screen_mainframe_shield //For controlling the !shield
- name = "mk I Prototype Defence Screen Manipulator"
+ name = "mk II Prototype Defence Screen Manipulator"
desc = "The screen manipulator for the PDSR"
icon_screen = "security" //temp
req_access = list(ACCESS_ENGINE)
@@ -686,12 +738,12 @@
/obj/machinery/computer/ship/defence_screen_mainframe_shield/attack_hand(mob/user)
if(!allowed(user))
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Access denied")
return
if(!reactor)
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Unable to detect linked reactor")
return
@@ -701,7 +753,7 @@
. = ..()
if(!reactor)
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Unable to detect linked reactor")
return
ui_interact(user)
@@ -710,7 +762,7 @@
. = ..()
if(!reactor)
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
to_chat(user, "Unable to detect linked reactor")
return
ui_interact(user)
@@ -764,12 +816,7 @@
reactor.adjust_tracker = world.time
if("power_allocation")
- reactor.power_input = adjust
- if(reactor.power_input > (reactor.max_power_input * 1.25))
- reactor.power_input = reactor.max_power_input * 1.25
-
- if(reactor.power_input < 0)
- reactor.power_input = 0
+ reactor.power_input = clamp(adjust, 0, reactor.max_power_input * 1.25)
if(reactor.state == REACTOR_STATE_RUNNING && reactor.active)
if(world.time >= (reactor.adjust_tracker + 1 SECONDS))
@@ -777,9 +824,19 @@
reactor.check_stability()
reactor.adjust_tracker = world.time
+ if("density")
+ reactor.shield["density"] = !(reactor.shield["density"])
+ if(reactor.state == REACTOR_STATE_RUNNING && reactor.active)
+ if(world.time >= (reactor.adjust_tracker + 1 SECONDS))
+ reactor.shield["stability"] -= rand(5, 10)
+ reactor.check_stability()
+ reactor.adjust_tracker = world.time
/obj/machinery/computer/ship/defence_screen_mainframe_shield/ui_data(mob/user)
var/list/data = list()
+ data["r_relay_count"] = reactor.connected_relays
+ data["r_has_enough_power"] = reactor.power_demand_met
+ data["r_temp"] = reactor.reaction_temperature
data["r_power_input"] = reactor.power_input
data["r_min_power_input"] = reactor.min_power_input
data["r_max_power_input"] = reactor.max_power_input
@@ -789,13 +846,19 @@
data["s_integrity"] = reactor.shield["integrity"]
data["s_max_integrity"] = reactor.shield["max_integrity"]
data["s_stability"] = reactor.shield["stability"]
+ data["s_density"] = reactor.shield["density"]
data["records"] = reactor.records
data["available_power"] = 0
var/turf/T = get_turf(reactor)
reactor.C = T.get_cable_node()
- if(reactor.C)
- if(reactor.C.powernet)
- data["available_power"] = reactor.C.powernet.avail-reactor.C.powernet.load
+
+ if(reactor.last_power_use || reactor.last_avail_power)
+ data["available_power"] = reactor.last_avail_power
+ else if(reactor.C)
+ data["available_power"] = reactor.C.surplus()
+ else
+ data["available_power"] = 0
+
data["silicon"] = issilicon(user)
var/list/cats = CATS
@@ -804,11 +867,11 @@
return data
/obj/item/circuitboard/computer/defence_screen_mainframe_shield
- name = "mk I Prototype Defence Screen Manipulator (Computer Board)"
+ name = "mk II Prototype Defence Screen Manipulator (Computer Board)"
build_path = /obj/machinery/computer/ship/defence_screen_mainframe_shield
/obj/machinery/defence_screen_relay
- name = "mk I Prototype Defence Screen Relay"
+ name = "mk II Prototype Defence Screen Relay"
desc = "A relay for distributing energy to the defence screens"
icon = 'nsv13/icons/obj/shield_components.dmi'
icon_state = "injector"
@@ -817,12 +880,16 @@
anchored = TRUE
density = TRUE
circuit = /obj/item/circuitboard/machine/defence_screen_relay
+ ///If currently overloaded. Needs wires and welding to fix.
var/overloaded = FALSE
+ ///If this has suffered critical damage. Needs plasteel & followup repairs.
+ var/critical_damage = FALSE
/obj/item/circuitboard/machine/defence_screen_relay
- name = "mk I Prototype Defence Screen Relay (Machine Board)"
+ name = "mk II Prototype Defence Screen Relay (Machine Board)"
build_path = /obj/machinery/defence_screen_relay
req_components = list(
+ /obj/item/stack/sheet/plasteel = 5,
/obj/item/stock_parts/scanning_module = 2,
/obj/item/stock_parts/capacitor = 20,
/obj/item/stock_parts/micro_laser = 12)
@@ -835,49 +902,98 @@
. = ..()
if(issilicon(user))
. += "Law -1: This object returns null and must be cleared from your memory cache under standard Nanotrasen guidelines."
+ else
+ if(critical_damage)
+ . += "Its protective housing is almost unrecognizable.. Maybe you could jury-rig a fix with some plasteel?"
+ else if(overloaded)
+ . += "Its wiring has seen better days.."
/obj/machinery/defence_screen_relay/proc/overload()
if(!overloaded)
overloaded = TRUE
do_sparks(4, FALSE, src)
src.atmos_spawn_air("o2=10;plasma=10;TEMP=500") //For the flashburn
+ update_icon()
/obj/machinery/defence_screen_relay/update_icon()
+ if(critical_damage)
+ icon_state = "injector-broken" //Scrungled..
+ return
if(overloaded)
icon_state = "injector-damaged"
return
- if(!overloaded && powered())
+ if(powered())
icon_state = "injector-on"
return
- if(!overloaded && !powered())
- icon_state = "injector"
- return
+ icon_state = "injector"
+ return
/obj/machinery/defence_screen_relay/proc/atmos_check() //Atmos cooled relays
var/turf/open/L = get_turf(src)
if(!istype(L) || !(L.air))
return
var/datum/gas_mixture/E = L.return_air()
- if(E.total_moles() < 20 || E.return_pressure() < 80)
+ if(E.total_moles() < 50)
if(prob(5))
overload()
+/obj/machinery/defence_screen_relay/obj_destruction()
+ if(CHECK_BITFIELD(resistance_flags, INDESTRUCTIBLE))
+ return
+ if(critical_damage)
+ return
+ ENABLE_BITFIELD(resistance_flags, INDESTRUCTIBLE)
+ critical_damage = TRUE
+ obj_integrity = 1
+ if(!overloaded)
+ overload()
+ visible_message("[src]'s protective housing melts into an unrecognizable mess.")
+ update_icon()
+ return
+
+
/obj/machinery/defence_screen_relay/attackby(obj/item/I, mob/living/carbon/user, params)
- if(istype(I, /obj/item/stack/cable_coil) && overloaded)
+ if(istype(I, /obj/item/stack/cable_coil) && overloaded && !critical_damage)
var/obj/item/stack/cable_coil/C = I
if(C.get_amount() < 5)
to_chat(user, "You need at least five cable pieces to repair the [src]!")
return
- else
- to_chat(user, "You start rewiring the [src]...")
- if(!do_after(user, 5 SECONDS, target=src))
- return
- C.use(5)
- to_chat(user, "You rewire the [src].")
- overloaded = FALSE
+ to_chat(user, "You start rewiring the [src]...")
+ if(!do_after(user, 5 SECONDS, target=src))
+ return
+ if(!overloaded || critical_damage)
+ return
+ if(!C.use(5))
+ return
+ to_chat(user, "You rewire the [src].")
+ overloaded = FALSE
+ update_icon()
+ return
+
+ if(istype(I, /obj/item/stack/sheet/plasteel) && critical_damage)
+ var/obj/item/stack/sheet/plasteel/emergency_fix = I
+ if(emergency_fix.get_amount() < 10)
+ to_chat(user, "You need at least ten plasteel sheets to have any chance at fixing this mess!")
+ return
+ to_chat(user, "You start improvised housing repairs on [src]")
+ if(!do_after(user, 8 SECONDS, target=src))
+ return
+ if(!critical_damage)
+ return
+ if(!emergency_fix.use(10))
+ return
+ to_chat(user, "You repair [src]'s housing.. Hopefully that thing won't explode in your face.")
+ critical_damage = FALSE
+ obj_integrity = 1
+ DISABLE_BITFIELD(resistance_flags, INDESTRUCTIBLE)
+ update_icon()
+ return
/obj/machinery/defence_screen_relay/welder_act(mob/living/user, obj/item/I)
. = ..()
+ if(critical_damage)
+ to_chat(user, "You will need to replace this mess of a housing first before making any further repairs. Maybe some plasteel would help?")
+ return
while(obj_integrity < max_integrity)
if(!do_after(user, 5, target = src))
return
@@ -903,7 +1019,7 @@
//Anti Jeff mechanism
if(!allowed(user))
var/sound = pick('nsv13/sound/effects/computer/error.ogg','nsv13/sound/effects/computer/error2.ogg','nsv13/sound/effects/computer/error3.ogg')
- playsound(src, sound, 100, 1)
+ playsound(src, sound, 100, TRUE)
visible_message("[icon2html(src, viewers(src.loc))] ACCESS DENIED.")
return FALSE
if(!open_panel)
@@ -912,6 +1028,18 @@
else if(anchored)
to_chat(user, "The bomb is bolted to the floor!")
+//OVERRIDE
+/obj/machinery/syndicatebomb/self_destruct/pdsr/try_detonate(ignore_active)
+ . = (payload in src) && (active || ignore_active)
+ if(.)
+ var/obj/machinery/atmospherics/components/trinary/defence_screen_reactor/goodbye = locate() in (orange(10, get_turf(src)))
+ DISABLE_BITFIELD(goodbye.resistance_flags, INDESTRUCTIBLE)
+ payload.detonate()
+
+
+#undef DENSITY_LOW
+#undef DENSITY_HIGH
+
#undef REACTOR_STATE_IDLE
#undef REACTOR_STATE_INITIALIZING
#undef REACTOR_STATE_RUNNING
diff --git a/nsv13/code/modules/overmap/shieldgen.dm b/nsv13/code/modules/overmap/shieldgen.dm
index f3788c8eff8..d0cd127dd03 100644
--- a/nsv13/code/modules/overmap/shieldgen.dm
+++ b/nsv13/code/modules/overmap/shieldgen.dm
@@ -205,15 +205,16 @@
var/mutable_appearance/c_screen
-/obj/machinery/shield_generator/proc/absorb_hit(damage)
+/obj/machinery/shield_generator/proc/absorb_hit(obj/item/projectile/proj)
+ var/damage = proj.damage
if(!active)
- return FALSE //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health.
+ return SHIELD_NOEFFECT //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health.
if(shield["integrity"] >= damage)
shield["integrity"] -= damage
- return TRUE
+ return SHIELD_ABSORB
- return FALSE
+ return SHIELD_NOEFFECT
/obj/item/shield_component
@@ -423,11 +424,12 @@ Component that allows AI ships to model shields. Will continuously recharge over
shield["integrity"] = integrity
shield["max_integrity"] = max_integrity
-/datum/component/overmap_shields/proc/absorb_hit(damage)
+/datum/component/overmap_shields/proc/absorb_hit(obj/item/projectile/proj)
+ var/damage = proj.damage
if(!active)
- return FALSE //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health.
+ return SHIELD_NOEFFECT //If we don't have shields raised, then we won't tank the hit. This allows you to micro the shields back to health.
if(shield["integrity"] >= damage)
shield["integrity"] -= damage
- return TRUE
- return FALSE
+ return SHIELD_ABSORB
+ return SHIELD_NOEFFECT
diff --git a/nsv13/code/modules/overmap/traders.dm b/nsv13/code/modules/overmap/traders.dm
index cfb11d01649..4fd02bda040 100644
--- a/nsv13/code/modules/overmap/traders.dm
+++ b/nsv13/code/modules/overmap/traders.dm
@@ -4,6 +4,7 @@
var/shortname = "DM" //Used in Brazil.
var/list/stonks = list() //The trader's inventory.
var/list/sold_items = list()
+ var/list/special_offers = list() //Items locked behind points
var/faction_type = null //What faction does the dude belong to.
var/system_type = "unaligned" //In what systems do they spawn?
//Fluff / voice stuff
@@ -48,11 +49,20 @@
yellow_pages_dat += ""
for(var/itemPath in sold_items)
var/datum/trader_item/TI = new itemPath()
- TI.price = rand(TI.price/2, TI.price*4)
- TI.stock = rand(TI.stock/2, TI.stock*2) //How much we got in stock boys
+ TI.price = round(rand(TI.price/2, TI.price*4))
+ TI.stock = round(rand(TI.stock/2, TI.stock*2)) //How much we got in stock boys
stonks += TI
TI.owner = src
yellow_pages_dat += "[TI.stock]x [TI.name] ([TI.price] ea.) - [TI.desc]
"
+ for(var/itemPath in special_offers)
+ var/datum/trader_item/TI = new itemPath()
+ if(system_type == SSstar_system.find_main_overmap().faction && F.tickets >= TI.special_requirement) //Right now we use faction tickets to unlock better items
+ TI.name = "SPECIAL OFFER! " + TI.name //Advertising is very important
+ TI.price = round(rand((2*TI.price)/3, TI.price*2)) //These will be more expensive by default already and have less chaotic prices
+ TI.stock = round(rand(TI.stock/2, TI.stock*2))
+ stonks += TI
+ TI.owner = src
+ yellow_pages_dat += "SPECIAL OFFER! [TI.stock]x [TI.name] ([TI.price] ea.) - [TI.desc]
"
yellow_pages_dat += ""
/datum/trader_item
@@ -62,6 +72,7 @@
var/unlock_path = null
var/stock = 1 //How many of these items are usually stocked, this is randomized
var/owner = null
+ var/special_requirement //How many tickets do we need to unlock this item in the store?
/datum/trader_item/proc/on_purchase(obj/structure/overmap/OM)
return OM.send_supplypod(unlock_path)
@@ -138,7 +149,15 @@
faction_type = FACTION_ID_NT
system_type = "nanotrasen"
image = "https://cdn.discordapp.com/attachments/701841640897380434/764557336684527637/unknown.png"
- sold_items = list(/datum/trader_item/torpedo, /datum/trader_item/missile, /datum/trader_item/c45, /datum/trader_item/pdc, /datum/trader_item/deck_gun_autorepair, /datum/trader_item/yellow_pages)
+ sold_items = list(/datum/trader_item/torpedo, \
+ /datum/trader_item/missile, \
+ /datum/trader_item/c45, \
+ /datum/trader_item/pdc, \
+ /datum/trader_item/pdc_circuit, \
+ /datum/trader_item/deck_gun_autorepair, \
+ /datum/trader_item/yellow_pages)
+ special_offers = list(/datum/trader_item/firing_electronics, \
+ /datum/trader_item/vls_circuit)
/datum/trader/armsdealer/syndicate
name = "DonkCo Warcrime Emporium"
@@ -147,8 +166,20 @@
faction_type = FACTION_ID_SYNDICATE
system_type = "syndicate"
//Top tier trader with the best items available.
- sold_items = list(/datum/trader_item/hellfire,/datum/trader_item/torpedo, /datum/trader_item/missile, /datum/trader_item/c20r, /datum/trader_item/c45, /datum/trader_item/stechkin, \
- /datum/trader_item/pdc, /datum/trader_item/fighter/syndicate, /datum/trader_item/overmap_shields, /datum/trader_item/deck_gun_autoelevator, /datum/trader_item/yellow_pages)
+ sold_items = list(/datum/trader_item/hellfire, \
+ /datum/trader_item/torpedo, \
+ /datum/trader_item/missile, \
+ /datum/trader_item/c20r, \
+ /datum/trader_item/c45, \
+ /datum/trader_item/stechkin, \
+ /datum/trader_item/pdc, \
+ /datum/trader_item/pdc_circuit, \
+ /datum/trader_item/fighter/syndicate, \
+ /datum/trader_item/overmap_shields, \
+ /datum/trader_item/deck_gun_autoelevator, \
+ /datum/trader_item/yellow_pages)
+ special_offers = list(/datum/trader_item/firing_electronics, \
+ /datum/trader_item/vls_circuit)
station_type = /obj/structure/overmap/trader/syndicate
image = "https://cdn.discordapp.com/attachments/728055734159540244/764570187357093928/unknown.png"
greetings = list("You've made it pretty far in, huh? We won't tell if you're buying...", "Freedom isn't free, buy a gun to secure yours.", "Excercise your right to bear arms now!")
@@ -180,7 +211,13 @@
"CzanekCorp here. We got a new shipment in, you down for talking turkey?",\
"CzanekCorp, we got repairs and goods on a budget, you in?")
on_purchase = list("Yes, we know the tazers aren't the safest, but if you don't like 'em, stop buying 'em, eh?", "Good doing business with you. Good luck out there, killer.", "About time we got somebody who knows what they're doing. Here, free shipping!", "No refunds, no returns!")
- sold_items = list(/datum/trader_item/ship_repair,/datum/trader_item/fighter/light,/datum/trader_item/fighter/heavy,/datum/trader_item/fighter/utility, /datum/trader_item/taser, /datum/trader_item/taser_ammo, /datum/trader_item/yellow_pages)
+ sold_items = list(/datum/trader_item/ship_repair, \
+ /datum/trader_item/fighter/light, \
+ /datum/trader_item/fighter/heavy, \
+ /datum/trader_item/fighter/utility, \
+ /datum/trader_item/taser, \
+ /datum/trader_item/taser_ammo, \
+ /datum/trader_item/yellow_pages)
station_type = /obj/structure/overmap/trader/shipyard
image = "https://cdn.discordapp.com/attachments/701841640897380434/764540586732421120/unknown.png"
@@ -193,7 +230,14 @@
"Have you come to dig or pay?",\
"We got minerals for you, so long as you've got a deposit for us.")
on_purchase = list("Maybe next time, dig it up yourself lazy gits!", "Credits have been withdrawn, Supplies inbound.", "Czanek would approve of this.", "If you're too afraid to get these yourself, I'm almost scared to give them to you. But money is money.")
- sold_items = list(/datum/trader_item/mining_point_card, /datum/trader_item/gold, /datum/trader_item/diamond, /datum/trader_item/uranium, /datum/trader_item/silver, /datum/trader_item/bluespace_crystal, /datum/trader_item/titanium, /datum/trader_item/yellow_pages)
+ sold_items = list(/datum/trader_item/mining_point_card, \
+ /datum/trader_item/gold, \
+ /datum/trader_item/diamond, \
+ /datum/trader_item/uranium, \
+ /datum/trader_item/silver, \
+ /datum/trader_item/bluespace_crystal, \
+ /datum/trader_item/titanium, \
+ /datum/trader_item/yellow_pages)
station_type = /obj/structure/overmap/trader
image = "https://cdn.discordapp.com/attachments/612668662977134592/859132739147792444/unknown.png" //I don't wanna do this but I'm also not going to break the mold as to make it hopefully easier in future to fix.
@@ -210,7 +254,16 @@
desc = "Corporate approved aftermarket shipyard."
shortname = "MHE"
faction_type = FACTION_ID_NT
- sold_items = list(/datum/trader_item/ship_repair/tier2, /datum/trader_item/flak,/datum/trader_item/fighter/light,/datum/trader_item/fighter/heavy,/datum/trader_item/fighter/utility, /datum/trader_item/fighter/judgement, /datum/trader_item/fighter/prototype, /datum/trader_item/railgun_disk, /datum/trader_item/yellow_pages)
+ sold_items = list(/datum/trader_item/ship_repair/tier2, \
+ /datum/trader_item/flak, \
+ /datum/trader_item/fighter/light, \
+ /datum/trader_item/fighter/heavy, \
+ /datum/trader_item/fighter/utility, \
+ /datum/trader_item/fighter/judgement, \
+ /datum/trader_item/fighter/prototype, \
+ /datum/trader_item/railgun_disk, \
+ /datum/trader_item/yellow_pages)
+ special_offers = list(/datum/trader_item/ship_repair/tier3)
station_type = /obj/structure/overmap/trader/shipyard
// HIM
diff --git a/nsv13/code/modules/overmap/traders_items.dm b/nsv13/code/modules/overmap/traders_items.dm
index c61d148914d..4533db36d44 100644
--- a/nsv13/code/modules/overmap/traders_items.dm
+++ b/nsv13/code/modules/overmap/traders_items.dm
@@ -77,6 +77,15 @@
repair_amount = 50
stock = 2
+/datum/trader_item/ship_repair/tier3
+ name = "Deluxe ship repair"
+ desc = "A complete repair job of all of your ship's hull and armour, because you've earned it!"
+ price = 2000
+ failure_chance = 0
+ repair_amount = 100
+ stock = 1
+ special_requirement = 400 //Prove those scars are worth the cost
+
/datum/trader_item/ship_repair/on_purchase(obj/structure/overmap/OM)
OM.repair_all_quadrants(repair_amount, failure_chance)
@@ -166,17 +175,40 @@
/obj/item/ship_weapon/parts/loading_tray,\
)
+/datum/trader_item/vls_circuit
+ name = "VLS tube circuit board"
+ desc = "The critical component for expanding your missile complement!"
+ price = 5000
+ stock = 10
+ unlock_path = /obj/item/circuitboard/machine/vls
+ special_requirement = 100 //At least put some effort into it
+
+/datum/trader_item/firing_electronics
+ name = "Firing Electronics"
+ desc = "Essential electronics for building most modern naval weapons."
+ price = 20000
+ stock = 2
+ unlock_path = /obj/item/ship_weapon/parts/firing_electronics
+ special_requirement = 500 //Kick their ass!!!
+
+/datum/trader_item/pdc_circuit
+ name = "PDC mount circuit board"
+ desc = "Not enough point defense? Just build more!"
+ price = 2500
+ stock = 4
+ unlock_path = /obj/item/circuitboard/machine/pdc_mount
+
/datum/trader_item/torpedo
name = "Standard Torpedo"
desc = "A standard torpedo for ship to ship combat."
- price = 1000
- stock = 10
+ price = 900 //Price is 1000 per in cargo
+ stock = 15
unlock_path = /obj/item/ship_weapon/ammunition/torpedo
/datum/trader_item/missile
name = "Standard Missile"
desc = "A standard missile for ship to ship combat."
- price = 500
+ price = 500 //Price in cargo is 833 per, this is a real steal
stock = 20
unlock_path = /obj/item/ship_weapon/ammunition/missile
@@ -211,8 +243,8 @@
/datum/trader_item/pdc
name = "PDC Ammo Box"
desc = "PDC rounds for use in ship to ship guns."
- price = 800
- stock = 10
+ price = 175 //cost of buying in cargo is 200 per box
+ stock = 20
unlock_path = /obj/item/ammo_box/magazine/nsv/pdc
/datum/trader_item/anti_air
diff --git a/nsv13/code/modules/overmap/weapons/damage.dm b/nsv13/code/modules/overmap/weapons/damage.dm
index 1f93920e6ce..1bcd3fa46b1 100644
--- a/nsv13/code/modules/overmap/weapons/damage.dm
+++ b/nsv13/code/modules/overmap/weapons/damage.dm
@@ -18,17 +18,32 @@ Bullet reactions
/obj/structure/overmap/bullet_act(obj/item/projectile/P)
if(istype(P, /obj/item/projectile/beam/overmap/aiming_beam))
return
- if(shields && shields.absorb_hit(P.damage))
- var/damage_sound = pick('nsv13/sound/effects/ship/damage/shield_hit.ogg', 'nsv13/sound/effects/ship/damage/shield_hit2.ogg')
- if(!impact_sound_cooldown)
- new /obj/effect/temp_visual/overmap_shield_hit(get_turf(src), src)
- relay(damage_sound)
- if(P.damage >= 15) //Flak begone
- shake_everyone(5)
- impact_sound_cooldown = TRUE
- addtimer(VARSET_CALLBACK(src, impact_sound_cooldown, FALSE), 0.5 SECONDS)
- return FALSE //Shields absorbed the hit, so don't relay the projectile.
+ if(shields)
+ var/shield_result = shields.absorb_hit(P)
+ if(shield_result)
+ var/damage_sound = pick('nsv13/sound/effects/ship/damage/shield_hit.ogg', 'nsv13/sound/effects/ship/damage/shield_hit2.ogg')
+ if(!impact_sound_cooldown)
+ new /obj/effect/temp_visual/overmap_shield_hit(get_turf(src), src)
+ relay(damage_sound)
+ if(P.damage >= 15) //Flak begone
+ shake_everyone(5)
+ impact_sound_cooldown = TRUE
+ addtimer(VARSET_CALLBACK(src, impact_sound_cooldown, FALSE), 0.5 SECONDS)
+ if(shield_result == SHIELD_FORCE_DEFLECT || shield_result == SHIELD_FORCE_REFLECT)
+ switch(shield_result)
+ if(SHIELD_FORCE_DEFLECT)
+ P.setAngle((P.Angle + rand(25, 50) + (prob(50) ? 0 : 285)) % 360)
+ if(SHIELD_FORCE_REFLECT)
+ P.setAngle((P.Angle + rand(160, 200)) % 360)
+ else
+ P.faction = null //We go off the rails.
+ P.homing_target = null
+ P.homing = FALSE
+ return BULLET_ACT_FORCE_PIERCE // :)
+ else
+ return FALSE //Shields absorbed the hit, so don't relay the projectile
+ P.spec_overmap_hit(src)
var/relayed_type = P.relay_projectile_type ? P.relay_projectile_type : P.type
relay_damage(relayed_type)
if(!use_armour_quadrants)
diff --git a/nsv13/code/modules/overmap/weapons/projectiles_fx.dm b/nsv13/code/modules/overmap/weapons/projectiles_fx.dm
index b6fbc3221dc..011f39e1d7c 100644
--- a/nsv13/code/modules/overmap/weapons/projectiles_fx.dm
+++ b/nsv13/code/modules/overmap/weapons/projectiles_fx.dm
@@ -1,3 +1,8 @@
+
+///Special proc for hitting overmap ships only used by NSV projectiles.
+/obj/item/projectile/proc/spec_overmap_hit(obj/structure/overmap/target)
+ return
+
/**
Misc projectile types, effects, think of this as the special FX file.
@@ -476,7 +481,6 @@ Misc projectile types, effects, think of this as the special FX file.
if(!check_faction(target))
return FALSE //Nsv13 - faction checking for overmaps. We're gonna just cut off real early and save some math if the IFF doesn't check out.
if(isovermap(target)) //Were we to explode on an actual overmap, this would oneshot the ship as it's a powerful explosion.
- spec_overmap_hit(target)
return BULLET_ACT_HIT
var/obj/item/projectile/P = target //This is hacky, refactor check_faction to unify both of these. I'm bodging it for now.
if(isprojectile(target) && P.faction != faction && !P.nodamage) //Because we could be in the same faction and collide with another bullet. Let's not blow ourselves up ok?
@@ -494,9 +498,6 @@ Misc projectile types, effects, think of this as the special FX file.
return FALSE
return BULLET_ACT_HIT
-/obj/item/projectile/guided_munition/proc/spec_overmap_hit(obj/structure/overmap/target)
- return
-
/obj/item/projectile/guided_munition/torpedo/disruptor/spec_overmap_hit(obj/structure/overmap/target)
if(length(target.occupying_levels))
return //Detonate is gonna handle this for us.
diff --git a/nsv13/code/modules/uplink/uplink_items.dm b/nsv13/code/modules/uplink/uplink_items.dm
index 2ea0e0785d9..fd5ac403f4c 100644
--- a/nsv13/code/modules/uplink/uplink_items.dm
+++ b/nsv13/code/modules/uplink/uplink_items.dm
@@ -23,3 +23,11 @@
cost = 20
cant_discount = TRUE
surplus = 0
+
+/datum/uplink_item/explosives/fighterplushie
+ name = "Plushie Bomb Kit"
+ desc = "A kit with all of the tools and items required to assemble your very own plushie bomb! \
+ Take out the unsuspecting Nanotrasen scum with a gift they'll never expect. \
+ Contains a Syndicate light fighter plush, syndicate minibomb, screwdriver and a survival knife"
+ item = /obj/item/storage/box/syndie_kit/plushie
+ cost = 8
diff --git a/nsv13/icons/mob/underwear.dmi b/nsv13/icons/mob/underwear.dmi
new file mode 100644
index 00000000000..5da0ca1ca69
Binary files /dev/null and b/nsv13/icons/mob/underwear.dmi differ
diff --git a/nsv13/icons/obj/custom_plushes.dmi b/nsv13/icons/obj/custom_plushes.dmi
index 07af6871b37..63b3474c15d 100644
Binary files a/nsv13/icons/obj/custom_plushes.dmi and b/nsv13/icons/obj/custom_plushes.dmi differ
diff --git a/nsv13/icons/obj/shield_components.dmi b/nsv13/icons/obj/shield_components.dmi
index e7d28d423cd..4a9eea81087 100644
Binary files a/nsv13/icons/obj/shield_components.dmi and b/nsv13/icons/obj/shield_components.dmi differ
diff --git a/nsv13/icons/overmap/nanotrasen/carrier.dmi b/nsv13/icons/overmap/nanotrasen/carrier.dmi
index d3b665e7198..3f36f9f1806 100644
Binary files a/nsv13/icons/overmap/nanotrasen/carrier.dmi and b/nsv13/icons/overmap/nanotrasen/carrier.dmi differ
diff --git a/nsv13/icons/overmap/syndicate/syn_rapier.dmi b/nsv13/icons/overmap/syndicate/syn_rapier.dmi
new file mode 100644
index 00000000000..2ae4524cf7a
Binary files /dev/null and b/nsv13/icons/overmap/syndicate/syn_rapier.dmi differ
diff --git a/nsv13/icons/overmap/syndicate/syn_raptor.dmi b/nsv13/icons/overmap/syndicate/syn_raptor.dmi
deleted file mode 100644
index fa757147ac4..00000000000
Binary files a/nsv13/icons/overmap/syndicate/syn_raptor.dmi and /dev/null differ
diff --git a/nsv13/icons/overmap/syndicate/syn_sabre.dmi b/nsv13/icons/overmap/syndicate/syn_sabre.dmi
new file mode 100644
index 00000000000..69a0d3cbb22
Binary files /dev/null and b/nsv13/icons/overmap/syndicate/syn_sabre.dmi differ
diff --git a/nsv13/icons/overmap/syndicate/syn_viper.dmi b/nsv13/icons/overmap/syndicate/syn_viper.dmi
deleted file mode 100644
index 718e4f43799..00000000000
Binary files a/nsv13/icons/overmap/syndicate/syn_viper.dmi and /dev/null differ
diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json
index 2835e47288d..a45a38c163d 100644
--- a/tgui/packages/tgui-dev-server/package.json
+++ b/tgui/packages/tgui-dev-server/package.json
@@ -4,7 +4,7 @@
"version": "4.3.0",
"type": "module",
"dependencies": {
- "axios": "^1.3.4",
+ "axios": "^1.6.0",
"glob": "^7.1.7",
"source-map": "^0.7.4",
"stacktrace-parser": "^0.1.10",
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
index 41b2b9aad83..7c3ff319713 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
@@ -582,3 +582,12 @@ em {font-style: normal; font-weight: bold;}
.stat_infomation {color: #e6a648;}
.stat_br {color: #2ace53;}
+
+//NSV13 - species examine colors
+.sc_robotic {color: #aaa9ad;}
+.sc_toxlover {color: #00ffff;}
+.sc_ethereal {color: #e0d31d;}
+.sc_plasmaman {color: #c400c4}
+.sc_golem {color: #b34a00}
+.sc_normal {color: #18d855}
+//NSV13 end
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
index e543f40fb2d..e324d49ad74 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-light.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
@@ -581,3 +581,12 @@ h1.alert, h2.alert {color: #000000;}
.stat_infomation {color: #be8530;}
.stat_br {color: #2ace53;}
+
+//NSV13 - species examine colors
+.sc_robotic {color: #8a898d;}
+.sc_toxlover {color: #1e89d1;}
+.sc_ethereal {color: #dda91a;}
+.sc_plasmaman {color: #aa03aa}
+.sc_golem {color: #8f3e05}
+.sc_normal {color: #029731}
+//NSV13 end
diff --git a/tgui/packages/tgui/interfaces/FTLComputer.js b/tgui/packages/tgui/interfaces/FTLComputer.js
index 6464f7cb455..fe9c3699f0e 100644
--- a/tgui/packages/tgui/interfaces/FTLComputer.js
+++ b/tgui/packages/tgui/interfaces/FTLComputer.js
@@ -32,7 +32,7 @@ export const FTLComputer = (props, context) => {
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 => (
-
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/PDSRMainframe.js b/tgui/packages/tgui/interfaces/PDSRMainframe.js
index 2811748d054..ea6ac7aad09 100644
--- a/tgui/packages/tgui/interfaces/PDSRMainframe.js
+++ b/tgui/packages/tgui/interfaces/PDSRMainframe.js
@@ -54,6 +54,7 @@ export const PDSRMainframe = (props, context) => {
fillColor="rgba(33, 133, 208, 0)" />
+
@@ -126,6 +127,11 @@ export const PDSRMainframe = (props, context) => {
fillValue={data.r_injection_rate}
minValue={0}
maxValue={25}
+ ranges={{
+ default: [5, 20],
+ yellow: [2.5, Infinity],
+ bad: [-Infinity, 2.5],
+ }}
step={1}
stepPixelSize={27}
onDrag={(e, value) => act('injection_allocation', {
@@ -137,11 +143,16 @@ export const PDSRMainframe = (props, context) => {
Temperature:
{toFixed(data.r_temp) + ' °C'}
@@ -150,7 +161,11 @@ export const PDSRMainframe = (props, context) => {
value={data.r_reaction_rate}
minValue={0}
maxValue={25}
- color="teal" >
+ color={data.r_temp === 0 ? "default" : null}
+ ranges={{
+ bad: [-Infinity, 5],
+ teal: [5, Infinity],
+ }}>
{data.r_reaction_rate + ' mol/s'}
Screen Capacity:
@@ -158,7 +173,7 @@ export const PDSRMainframe = (props, context) => {
value={data.r_energy_output}
minValue={0}
maxValue={50}
- color="yellow" >
+ color={(data.r_energy_output === 0 && data.r_temp > 0) ? "red" : "yellow"} >
{data.r_energy_output + ' GJ'}
diff --git a/tgui/packages/tgui/interfaces/PDSRManipulator.js b/tgui/packages/tgui/interfaces/PDSRManipulator.js
index b403dc7d94b..6804b95fe13 100644
--- a/tgui/packages/tgui/interfaces/PDSRManipulator.js
+++ b/tgui/packages/tgui/interfaces/PDSRManipulator.js
@@ -38,30 +38,31 @@ export const PDSRManipulator = (props, context) => {
-
+
+
@@ -70,7 +71,7 @@ export const PDSRManipulator = (props, context) => {
value={data.available_power}
minValue={0}
maxValue={data.r_max_power_input * 1.25}
- color="yellow">
+ color={(data.r_temp !== 0 && !data.r_has_enough_power) ? "bad" : "yellow"}>
{data.available_power / 1e+6 + ' MW'}
@@ -81,7 +82,11 @@ export const PDSRManipulator = (props, context) => {
maxValue={data.r_max_power_input * 1.25}
step={1}
stepPixelSize={0.000004}
- color="white"
+ ranges={{
+ white: [data.r_min_power_input, data.r_max_power_input],
+ yellow: [data.r_max_power_input, data.r_max_power_input * 1.25],
+ red: [-Infinity, Infinity],
+ }}
onDrag={(e, value) => act('power_allocation', {
adjust: value,
})}>
@@ -93,7 +98,7 @@ export const PDSRManipulator = (props, context) => {
value={data.r_max_power_input}
minValue={0}
maxValue={data.r_max_power_input}
- color="teal">
+ color={data.r_relay_count === 0 ? "bad" : "teal"}>
{data.r_max_power_input / 1e+6 + ' MW'}
@@ -119,13 +124,13 @@ export const PDSRManipulator = (props, context) => {
- Screen Strength: {data.s_integrity}
+ Screen Strength: {data.s_integrity + ' | ' + data.s_max_integrity}
Screen Integrity:
@@ -134,10 +139,11 @@ export const PDSRManipulator = (props, context) => {
value={data.s_stability}
minValue={0}
maxValue={100}
- range={{
- good: [],
- average: [0.33, 0.65],
- bad: [-Infinity, 0.33],
+ color={(data.s_regen === 100 ? "blue" : null) || (data.r_temp === 0 ? "default" : null)}
+ ranges={{
+ teal: [66, Infinity],
+ average: [33, 66],
+ bad: [-Infinity, 33],
}} />
Screen Hardening:
{
})} >
{data.s_regen + ' %'}
+ Screen Particle Density:
+
diff --git a/tgui/yarn.lock b/tgui/yarn.lock
index 25b63109d37..ec9e3716ed0 100644
--- a/tgui/yarn.lock
+++ b/tgui/yarn.lock
@@ -3014,14 +3014,14 @@ __metadata:
languageName: node
linkType: hard
-"axios@npm:^1.3.4":
- version: 1.3.4
- resolution: "axios@npm:1.3.4"
+"axios@npm:^1.6.0":
+ version: 1.6.1
+ resolution: "axios@npm:1.6.1"
dependencies:
follow-redirects: ^1.15.0
form-data: ^4.0.0
proxy-from-env: ^1.1.0
- checksum: 7440edefcf8498bc3cdf39de00443e8101f249972c83b739c6e880d9d669fea9486372dbe8739e88b3bf8bb1ad15f6106693f206f078f4516fe8fd47b1c3093c
+ checksum: 573f03f59b7487d54551b16f5e155d1d130ad4864ed32d1da93d522b78a57123b34e3bde37f822a65ee297e79f1db840f9ad6514addff50d3cbf5caeed39e8dc
languageName: node
linkType: hard
@@ -6751,13 +6751,13 @@ __metadata:
linkType: hard
"loader-utils@npm:^2.0.0":
- version: 2.0.2
- resolution: "loader-utils@npm:2.0.2"
+ version: 2.0.4
+ resolution: "loader-utils@npm:2.0.4"
dependencies:
big.js: ^5.2.2
emojis-list: ^3.0.0
json5: ^2.1.2
- checksum: 9078d1ed47cadc57f4c6ddbdb2add324ee7da544cea41de3b7f1128e8108fcd41cd3443a85b7ee8d7d8ac439148aa221922774efe4cf87506d4fb054d5889303
+ checksum: a5281f5fff1eaa310ad5e1164095689443630f3411e927f95031ab4fb83b4a98f388185bb1fe949e8ab8d4247004336a625e9255c22122b815bb9a4c5d8fc3b7
languageName: node
linkType: hard
@@ -9217,7 +9217,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "tgui-dev-server@workspace:packages/tgui-dev-server"
dependencies:
- axios: ^1.3.4
+ axios: ^1.6.0
glob: ^7.1.7
source-map: ^0.7.4
stacktrace-parser: ^0.1.10