diff --git a/_maps/RandomRuins/SpaceRuins/listeningstation.dmm b/_maps/RandomRuins/SpaceRuins/listeningstation.dmm
index 9371c5207a4..9e2fd7bef46 100644
--- a/_maps/RandomRuins/SpaceRuins/listeningstation.dmm
+++ b/_maps/RandomRuins/SpaceRuins/listeningstation.dmm
@@ -997,7 +997,6 @@
/area/ruin/space/has_grav/listeningstation)
"bx" = (
/obj/effect/mob_spawn/human/lavaland_syndicate/comms/space{
- assignedrole = "Space Syndicate";
dir = 8
},
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 33ed59e50d2..2b26a3224ee 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -25,6 +25,8 @@
#define COMSIG_GLOB_BUTTON_PRESSED "!button_pressed"
/// job subsystem has spawned and equipped a new mob
#define COMSIG_GLOB_JOB_AFTER_SPAWN "!job_after_spawn"
+/// job datum has been called to deal with the aftermath of a latejoin spawn
+#define COMSIG_GLOB_JOB_AFTER_LATEJOIN_SPAWN "!job_after_latejoin_spawn"
/// crewmember joined the game (mob/living, rank)
#define COMSIG_GLOB_CREWMEMBER_JOINED "!crewmember_joined"
/// Random event is trying to roll. (/datum/round_event_control/random_event)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index d284cedc30c..1d133b1af99 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -236,3 +236,16 @@ GLOBAL_LIST_INIT(book_types, typecacheof(list(
/obj/item/book,
/obj/item/spellbook,
/obj/item/storage/book)))
+
+
+// Jobs
+#define is_assistant_job(job_type) (istype(job_type, /datum/job/assistant))
+#define is_bartender_job(job_type) (istype(job_type, /datum/job/bartender))
+#define is_captain_job(job_type) (istype(job_type, /datum/job/captain))
+#define is_chaplain_job(job_type) (istype(job_type, /datum/job/chaplain))
+#define is_clown_job(job_type) (istype(job_type, /datum/job/clown))
+#define is_detective_job(job_type) (istype(job_type, /datum/job/detective))
+#define is_scientist_job(job_type) (istype(job_type, /datum/job/scientist))
+#define is_security_officer_job(job_type) (istype(job_type, /datum/job/security_officer))
+#define is_research_director_job(job_type) (istype(job_type, /datum/job/research_director))
+#define is_unassigned_job(job_type) (istype(job_type, /datum/job/unassigned))
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 52dd1877de1..21349fa19c6 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -56,3 +56,18 @@
#define DEPARTMENT_SCIENCE (1<<5)
#define DEPARTMENT_MEDICAL (1<<6)
#define DEPARTMENT_SILICON (1<<7)
+
+/* Job datum job_flags */
+/// Whether the mob is announced on arrival.
+#define JOB_ANNOUNCE_ARRIVAL (1<<0)
+/// Whether the mob is added to the crew manifest.
+#define JOB_CREW_MANIFEST (1<<1)
+/// Whether the mob is equipped through SSjob.EquipRank() on spawn.
+#define JOB_EQUIP_RANK (1<<2)
+/// Whether the job is considered a regular crew member of the station. Equipment such as AI and cyborgs not included.
+#define JOB_CREW_MEMBER (1<<3)
+/// Whether this job can be joined through the new_player menu.
+#define JOB_NEW_PLAYER_JOINABLE (1<<4)
+
+#define FACTION_NONE "None"
+#define FACTION_STATION "Station"
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index c8014079047..d72ac49f49b 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -129,3 +129,22 @@
//recommened client FPS
#define RECOMMENDED_FPS 40
+
+// randomise_appearance_prefs() and randomize_human_appearance() proc flags
+#define RANDOMIZE_GENDER (1<<0)
+#define RANDOMIZE_SPECIES (1<<1)
+#define RANDOMIZE_NAME (1<<2)
+#define RANDOMIZE_AGE (1<<3)
+#define RANDOMIZE_UNDERWEAR (1<<4)
+#define RANDOMIZE_UNDERWEAR_COLOR (1<<5)
+#define RANDOMIZE_UNDERSHIRT (1<<6)
+#define RANDOMIZE_SOCKS (1<<7)
+#define RANDOMIZE_BACKPACK (1<<8)
+#define RANDOMIZE_JUMPSUIT_STYLE (1<<9)
+#define RANDOMIZE_HAIRSTYLE (1<<10)
+#define RANDOMIZE_FACIAL_HAIRSTYLE (1<<11)
+#define RANDOMIZE_HAIR_COLOR (1<<12)
+#define RANDOMIZE_FACIAL_HAIR_COLOR (1<<13)
+#define RANDOMIZE_SKIN_TONE (1<<14)
+#define RANDOMIZE_EYE_COLOR (1<<15)
+#define RANDOMIZE_FEATURES (1<<16)
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 7525a1adcbd..e1344c904cb 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -40,10 +40,46 @@
#define ROLE_LAVALAND "Lavaland"
#define ROLE_INTERNAL_AFFAIRS "Internal Affairs Agent"
#define ROLE_FAMILIES "Familes Antagonists"
+
+#define ROLE_POSITRONIC_BRAIN "Positronic Brain"
+#define ROLE_FREE_GOLEM "Free Golem"
+#define ROLE_SERVANT_GOLEM "Servant Golem"
+#define ROLE_NUCLEAR_OPERATIVE "Nuclear Operative"
+#define ROLE_CLOWN_OPERATIVE "Clown Operative"
+#define ROLE_LONE_OPERATIVE "Lone Operative"
+#define ROLE_NIGHTMARE "Nightmare"
+#define ROLE_WIZARD_APPRENTICE "apprentice"
+#define ROLE_SLAUGHTER_DEMON "Slaughter Demon"
+#define ROLE_SENTIENT_DISEASE "Sentient Disease"
+#define ROLE_MORPH "Morph"
+#define ROLE_SANTA "Santa"
+#define ROLE_FUGITIVE "Fugitive"
+
+//Spawner roles
+#define ROLE_GHOST_ROLE "Ghost Role"
+#define ROLE_SPIDER "Spider"
+#define ROLE_EXILE "Exile"
+#define ROLE_FUGITIVE_HUNTER "Fugitive Hunter"
+#define ROLE_ESCAPED_PRISONER "Escaped Prisoner"
+#define ROLE_LIFEBRINGER "Lifebringer"
+#define ROLE_ASHWALKER "Ash Walker"
+#define ROLE_LAVALAND_SYNDICATE "Lavaland Syndicate"
+#define ROLE_HERMIT "Hermit"
+#define ROLE_BEACH_BUM "Beach Bum"
+#define ROLE_HOTEL_STAFF "Hotel Staff"
+#define ROLE_SPACE_SYNDICATE "Space Syndicate"
#define ROLE_SYNDICATE_CYBERSUN "Cybersun Space Syndicate" //Ghost role syndi from Forgottenship ruin
#define ROLE_SYNDICATE_CYBERSUN_CAPTAIN "Cybersun Space Syndicate Captain" //Forgottenship captain syndie
-#define ROLE_FREE_GOLEM "Free Golem"
#define ROLE_HEADSLUG_CHANGELING "Headslug Changeling"
+#define ROLE_SPACE_PIRATE "Space Pirate"
+#define ROLE_ANCIENT_CREW "Ancient Crew"
+#define ROLE_SPACE_DOCTOR "Space Doctor"
+#define ROLE_SPACE_BARTENDER "Space Bartender"
+#define ROLE_SPACE_BAR_PATRON "Space Bar Patron"
+#define ROLE_SKELETON "Skeleton"
+#define ROLE_ZOMBIE "Zombie"
+#define ROLE_MAINTENANCE_DRONE "Maintenance Drone"
+
/// This defines the antagonists you can operate with in the settings.
/// Keys are the antagonist, values are the number of days since the player's
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 7e119b0dc0c..a6e93db70fc 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -123,9 +123,9 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
if (CONFIG_GET(flag/log_econ))
WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]")
-/proc/log_manifest(ckey, datum/mind/mind,mob/body, latejoin = FALSE)
+/proc/log_manifest(ckey, datum/mind/mind, mob/body, latejoin = FALSE)
if (CONFIG_GET(flag/log_manifest))
- WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]")
+ WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role.title] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]")
/proc/log_bomber(atom/user, details, atom/bomb, additional_details, message_admins = TRUE)
var/bomb_message = "[details][bomb ? " [bomb.name] at [AREACOORD(bomb)]": ""][additional_details ? " [additional_details]" : ""]."
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 22787ea5136..7d1cd02fc42 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -8,6 +8,9 @@
#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL))
#define CULT_POLL_WAIT 2400
+/// Returns either the error landmark or the location of the room. Needless to say, if this is used, it means things have gone awry.
+#define GET_ERROR_ROOM ((locate(/obj/effect/landmark/error) in GLOB.landmarks_list) || locate(4,4,1))
+
/proc/get_area_name(atom/X, format_text = FALSE)
var/area/A = isarea(X) ? X : get_area(X)
if(!A)
@@ -455,7 +458,7 @@
var/mob/living/carbon/human/new_character = new//The mob being spawned.
SSjob.SendToLateJoin(new_character)
- G_found.client.prefs.copy_to(new_character)
+ G_found.client.prefs.safe_transfer_prefs_to(new_character)
new_character.dna.update_dna_identity()
new_character.key = G_found.key
@@ -497,7 +500,7 @@
return
if(!GLOB.announcement_systems.len)
return
- if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role))
+ if(!(character.mind.assigned_role.job_flags & JOB_ANNOUNCE_ARRIVAL))
return
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 5d7f87ade1a..9c31637e0e2 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1047,31 +1047,33 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
return 0
//For creating consistent icons for human looking simple animals
-/proc/get_flat_human_icon(icon_id, datum/job/J, datum/preferences/prefs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null)
+/proc/get_flat_human_icon(icon_id, datum/job/job, datum/preferences/prefs, dummy_key, showDirs = GLOB.cardinals, outfit_override = null)
var/static/list/humanoid_icon_cache = list()
- if(!icon_id || !humanoid_icon_cache[icon_id])
- var/mob/living/carbon/human/dummy/body = generate_or_wait_for_human_dummy(dummy_key)
-
- if(prefs)
- prefs.copy_to(body,TRUE,FALSE)
- if(J)
- J.equip(body, TRUE, FALSE, outfit_override = outfit_override)
- else if (outfit_override)
- body.equipOutfit(outfit_override,visualsOnly = TRUE)
-
-
- var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
- COMPILE_OVERLAYS(body)
- for(var/D in showDirs)
- var/icon/partial = getFlatIcon(body, defdir=D)
- out_icon.Insert(partial,dir=D)
-
- humanoid_icon_cache[icon_id] = out_icon
- dummy_key? unset_busy_human_dummy(dummy_key) : qdel(body)
- return out_icon
- else
+ if(icon_id && humanoid_icon_cache[icon_id])
return humanoid_icon_cache[icon_id]
+ var/mob/living/carbon/human/dummy/body = generate_or_wait_for_human_dummy(dummy_key)
+
+ if(prefs)
+ prefs.apply_prefs_to(body, TRUE)
+
+ var/datum/outfit/outfit = outfit_override || job?.outfit
+ if(job)
+ body.dna.species.pre_equip_species_outfit(job, body, TRUE)
+ if(outfit)
+ body.equipOutfit(outfit, TRUE)
+
+ var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
+ COMPILE_OVERLAYS(body)
+ for(var/D in showDirs)
+ var/icon/partial = getFlatIcon(body, defdir=D)
+ out_icon.Insert(partial,dir=D)
+
+ humanoid_icon_cache[icon_id] = out_icon
+ dummy_key? unset_busy_human_dummy(dummy_key) : qdel(body)
+ return out_icon
+
+
//Hook, override to run code on- wait this is images
//Images have dir without being an atom, so they get their own definition.
//Lame.
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index df8fdf83fde..979af067e06 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -45,7 +45,7 @@
var/mob/living/carbon/human/H = L
category = "humans"
if(H.mind)
- mob_data["job"] = H.mind.assigned_role
+ mob_data["job"] = H.mind.assigned_role.title
else
mob_data["job"] = "Unknown"
mob_data["species"] = H.dna.species.name
@@ -183,7 +183,7 @@
/datum/controller/subsystem/ticker/proc/HandleRandomHardcoreScore(client/player_client)
if(!ishuman(player_client?.mob))
return FALSE
- var/mob/living/carbon/human/human_mob = player_client?.mob
+ var/mob/living/carbon/human/human_mob = player_client.mob
if(!human_mob.hardcore_survival_score) ///no score no glory
return FALSE
@@ -197,9 +197,9 @@
didthegamerwin = FALSE
if(!didthegamerwin)
return FALSE
- player_client?.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score * 2))
+ player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score * 2))
else if(human_mob.onCentCom())
- player_client?.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score))
+ player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score))
/datum/controller/subsystem/ticker/proc/declare_completion()
@@ -542,31 +542,14 @@
* * award: Achievement to give service department
*/
/datum/controller/subsystem/ticker/proc/award_service(award)
- for(var/mob/living/carbon/human/service_member as anything in GLOB.human_list)
- if(!service_member.mind)
+ for(var/mob/living/carbon/human/human as anything in GLOB.human_list)
+ if(!human.client || !human.mind)
continue
- var/datum/mind/service_mind = service_member.mind
- if(!service_mind.assigned_role)
+ var/datum/job/human_job = human.mind.assigned_role
+ if(!(human_job.departments & DEPARTMENT_SERVICE))
continue
- for(var/job in GLOB.service_food_positions)
- if(service_mind.assigned_role != job)
- continue
- //general awards
- service_member.client?.give_award(award, service_member)
- if(service_mind.assigned_role == "Cook")
- var/datum/venue/restaurant = SSrestaurant.all_venues[/datum/venue/restaurant]
- var/award_score = restaurant.total_income
- var/award_status = service_member.client.get_award_status(/datum/award/score/chef_tourist_score)
- if(award_score > award_status)
- award_score -= award_status
- service_member.client?.give_award(/datum/award/score/chef_tourist_score, service_member, award_score)
- if(service_mind.assigned_role == "Bartender")
- var/datum/venue/bar = SSrestaurant.all_venues[/datum/venue/bar]
- var/award_score = bar.total_income
- var/award_status = service_member.client.get_award_status(/datum/award/score/bartender_tourist_score)
- if(award_score - award_status > 0)
- award_score -= award_status
- service_member.client?.give_award(/datum/award/score/bartender_tourist_score, service_member, award_score)
+ human_job.award_service(human.client, award)
+
/datum/controller/subsystem/ticker/proc/medal_report()
if(GLOB.commendations.len)
@@ -677,8 +660,8 @@
/proc/printplayer(datum/mind/ply, fleecheck)
var/jobtext = ""
- if(ply.assigned_role)
- jobtext = " the [ply.assigned_role]"
+ if(!is_unassigned_job(ply.assigned_role))
+ jobtext = " the [ply.assigned_role.title]"
var/text = "[ply.key] was [ply.name][jobtext] and"
if(ply.current)
if(ply.current.stat == DEAD)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 812820287f0..2cdf763d8d1 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -199,10 +199,10 @@ Turf and target are separate in case you want to teleport some distance from a t
var/loop = 1
var/safety = 0
- var/banned = C ? is_banned_from(C.ckey, "Appearance") : null
+ var/random = CONFIG_GET(flag/force_random_names) || (C ? is_banned_from(C.ckey, "Appearance") : FALSE)
while(loop && safety < 5)
- if(C?.prefs.custom_names[role] && !safety && !banned)
+ if(!safety && !random && C?.prefs.custom_names[role])
newname = C.prefs.custom_names[role]
else
switch(role)
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index 0080e1499d7..3e7fc9574e0 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -309,8 +309,7 @@ Versioning
if(!L.suiciding && !first_death.len)
first_death["name"] = "[(L.real_name == L.name) ? L.real_name : "[L.real_name] as [L.name]"]"
first_death["role"] = null
- if(L.mind.assigned_role)
- first_death["role"] = L.mind.assigned_role
+ first_death["role"] = L.mind.assigned_role.title
first_death["area"] = "[AREACOORD(L)]"
first_death["damage"] = "[L.getBruteLoss()]/[L.getFireLoss()]/[L.getToxLoss()]/[L.getOxyLoss()]/[L.getCloneLoss()]"
first_death["last_words"] = L.last_words
@@ -324,7 +323,7 @@ Versioning
"}, list(
"name" = L.real_name,
"key" = L.ckey,
- "job" = L.mind.assigned_role,
+ "job" = L.mind.assigned_role.title,
"special" = L.mind.special_role,
"pod" = get_area_name(L, TRUE),
"laname" = L.lastattacker,
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index d6ee85f6f8d..691adf7be17 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -3,16 +3,19 @@ SUBSYSTEM_DEF(job)
init_order = INIT_ORDER_JOBS
flags = SS_NO_FIRE
- var/list/occupations = list() //List of all jobs
+ /// List of all jobs.
+ var/list/all_occupations = list()
+ /// List of jobs that can be joined through the starting menu.
+ var/list/joinable_occupations = list()
var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles
var/list/type_occupations = list() //Dict of all jobs, keys are types
var/list/unassigned = list() //Players who need jobs
var/initial_players_to_assign = 0 //used for checking against population caps
var/list/prioritized_jobs = list()
- var/list/latejoin_trackers = list() //Don't read this list, use GetLateJoinTurfs() instead
+ var/list/latejoin_trackers = list()
- var/overflow_role = "Assistant"
+ var/overflow_role = /datum/job/assistant
var/list/level_order = list(JP_HIGH,JP_MEDIUM,JP_LOW)
@@ -52,12 +55,11 @@ SUBSYSTEM_DEF(job)
var/safe_code_timer_id
/// The loc to which the emergency safe code has been requested for delivery.
var/turf/safe_code_request_loc
- /// If TRUE, the "Captain" job will always be given the code to the spare ID safe and always have a "Captain on deck!" announcement.
- var/always_promote_captain_job = TRUE
+
/datum/controller/subsystem/job/Initialize(timeofday)
setup_job_lists()
- if(!occupations.len)
+ if(!length(all_occupations))
SetupOccupations()
if(CONFIG_GET(flag/load_jobs_from_txt))
LoadJobs()
@@ -65,79 +67,83 @@ SUBSYSTEM_DEF(job)
set_overflow_role(CONFIG_GET(string/overflow_job))
return ..()
+
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
- var/datum/job/new_overflow = GetJob(new_overflow_role)
+ var/datum/job/new_overflow = ispath(new_overflow_role) ? GetJobType(new_overflow_role) : GetJob(new_overflow_role)
+ if(!new_overflow)
+ JobDebug("Failed to set new overflow role: [new_overflow_role]")
+ CRASH("set_overflow_role failed | new_overflow_role: [isnull(new_overflow_role) ? "null" : new_overflow_role]")
var/cap = CONFIG_GET(number/overflow_cap)
new_overflow.allow_bureaucratic_error = FALSE
new_overflow.spawn_positions = cap
new_overflow.total_positions = cap
- if(new_overflow_role != overflow_role)
- var/datum/job/old_overflow = GetJob(overflow_role)
- old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
- old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
- old_overflow.total_positions = initial(old_overflow.total_positions)
- overflow_role = new_overflow_role
- JobDebug("Overflow role set to : [new_overflow_role]")
+ if(new_overflow.type == overflow_role)
+ return
+ var/datum/job/old_overflow = GetJobType(overflow_role)
+ old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
+ old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
+ old_overflow.total_positions = initial(old_overflow.total_positions)
+ overflow_role = new_overflow.type
+ JobDebug("Overflow role set to : [new_overflow.type]")
-/datum/controller/subsystem/job/proc/SetupOccupations(faction = "Station")
- occupations = list()
+
+/datum/controller/subsystem/job/proc/SetupOccupations()
+ all_occupations = list()
+ joinable_occupations = list()
var/list/all_jobs = subtypesof(/datum/job)
- if(!all_jobs.len)
+ if(!length(all_jobs))
to_chat(world, span_boldannounce("Error setting up jobs, no job datums found"))
return FALSE
- for(var/J in all_jobs)
- var/datum/job/job = new J()
- if(!job)
- continue
- if(job.faction != faction)
- continue
+ for(var/job_type in all_jobs)
+ var/datum/job/job = new job_type()
if(!job.config_check())
continue
if(!job.map_check()) //Even though we initialize before mapping, this is fine because the config is loaded at new
testing("Removed [job.type] due to map config")
continue
- occupations += job
+ all_occupations += job
name_occupations[job.title] = job
- type_occupations[J] = job
+ type_occupations[job_type] = job
+ if(job.job_flags & JOB_NEW_PLAYER_JOINABLE)
+ joinable_occupations += job
return TRUE
/datum/controller/subsystem/job/proc/GetJob(rank)
- if(!occupations.len)
+ if(!length(all_occupations))
SetupOccupations()
return name_occupations[rank]
/datum/controller/subsystem/job/proc/GetJobType(jobtype)
- if(!occupations.len)
+ if(!length(all_occupations))
SetupOccupations()
return type_occupations[jobtype]
-/datum/controller/subsystem/job/proc/AssignRole(mob/dead/new_player/player, rank, latejoin = FALSE)
- JobDebug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
- if(player?.mind && rank)
- var/datum/job/job = GetJob(rank)
- if(!job)
- return FALSE
- if(is_banned_from(player.ckey, rank) || QDELETED(player))
- return FALSE
- if(!job.player_old_enough(player.client))
- return FALSE
- if(job.required_playtime_remaining(player.client))
- return FALSE
- var/position_limit = job.total_positions
- if(!latejoin)
- position_limit = job.spawn_positions
- JobDebug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]")
- player.mind.assigned_role = rank
- unassigned -= player
- job.current_positions++
- return TRUE
- JobDebug("AR has failed, Player: [player], Rank: [rank]")
- return FALSE
+
+/datum/controller/subsystem/job/proc/AssignRole(mob/dead/new_player/player, datum/job/job, latejoin = FALSE)
+ JobDebug("Running AR, Player: [player], Rank: [isnull(job) ? "null" : job.type], LJ: [latejoin]")
+ if(!player?.mind || !job)
+ JobDebug("AR has failed, Player: [player], Rank: [isnull(job) ? "null" : job.type]")
+ return FALSE
+ if(is_banned_from(player.ckey, job.title) || QDELETED(player))
+ return FALSE
+ if(!job.player_old_enough(player.client))
+ return FALSE
+ if(job.required_playtime_remaining(player.client))
+ return FALSE
+ var/position_limit = job.total_positions
+ if(!latejoin)
+ position_limit = job.spawn_positions
+ JobDebug("Player: [player] is now Rank: [job.title], JCP:[job.current_positions], JPL:[position_limit]")
+ player.mind.set_assigned_role(job)
+ unassigned -= player
+ job.current_positions++
+ return TRUE
+
/datum/controller/subsystem/job/proc/FreeRole(rank)
if(!rank)
@@ -173,14 +179,13 @@ SUBSYSTEM_DEF(job)
candidates += player
return candidates
+
/datum/controller/subsystem/job/proc/GiveRandomJob(mob/dead/new_player/player)
JobDebug("GRJ Giving random job, Player: [player]")
. = FALSE
- for(var/datum/job/job in shuffle(occupations))
- if(!job)
- continue
+ for(var/datum/job/job as anything in shuffle(joinable_occupations))
- if(istype(job, GetJob(SSjob.overflow_role))) // We don't want to give him assistant, that's boring!
+ if(istype(job, GetJobType(overflow_role))) // We don't want to give him assistant, that's boring!
continue
if(job.title in GLOB.command_positions) //If you want a command position, select it!
@@ -207,16 +212,17 @@ SUBSYSTEM_DEF(job)
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
JobDebug("GRJ Random job given, Player: [player], Job: [job]")
- if(AssignRole(player, job.title))
+ if(AssignRole(player, job))
return TRUE
+
/datum/controller/subsystem/job/proc/ResetOccupations()
JobDebug("Occupations reset.")
- for(var/i in GLOB.new_player_list)
- var/mob/dead/new_player/player = i
- if((player) && (player.mind))
- player.mind.assigned_role = null
- player.mind.special_role = null
+ for(var/mob/dead/new_player/player as anything in GLOB.new_player_list)
+ if(!player?.mind)
+ continue
+ player.mind.set_assigned_role(GetJobType(/datum/job/unassigned))
+ player.mind.special_role = null
SetupOccupations()
unassigned = list()
return
@@ -237,7 +243,7 @@ SUBSYSTEM_DEF(job)
if(!candidates.len)
continue
var/mob/dead/new_player/candidate = pick(candidates)
- if(AssignRole(candidate, command_position))
+ if(AssignRole(candidate, job))
return TRUE
return FALSE
@@ -255,7 +261,7 @@ SUBSYSTEM_DEF(job)
if(!candidates.len)
continue
var/mob/dead/new_player/candidate = pick(candidates)
- AssignRole(candidate, command_position)
+ AssignRole(candidate, job)
/// Attempts to fill out all available AI positions.
/datum/controller/subsystem/job/proc/fill_ai_positions()
@@ -269,7 +275,7 @@ SUBSYSTEM_DEF(job)
candidates = FindOccupationCandidates(ai_job, level)
if(candidates.len)
var/mob/dead/new_player/candidate = pick(candidates)
- if(AssignRole(candidate, "AI"))
+ if(AssignRole(candidate, GetJobType(/datum/job/ai)))
break
@@ -286,7 +292,7 @@ SUBSYSTEM_DEF(job)
//Get the players who are ready
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
- if(player.ready == PLAYER_READY_TO_PLAY && player.check_preferences() && player.mind && !player.mind.assigned_role)
+ if(player.ready == PLAYER_READY_TO_PLAY && player.check_preferences() && player.mind && is_unassigned_job(player.mind.assigned_role))
unassigned += player
initial_players_to_assign = unassigned.len
@@ -315,12 +321,12 @@ SUBSYSTEM_DEF(job)
//People who wants to be the overflow role, sure, go on.
JobDebug("DO, Running Overflow Check 1")
- var/datum/job/overflow = GetJob(SSjob.overflow_role)
- var/list/overflow_candidates = FindOccupationCandidates(overflow, JP_LOW)
+ var/datum/job/overflow_datum = GetJobType(overflow_role)
+ var/list/overflow_candidates = FindOccupationCandidates(overflow_datum, JP_LOW)
JobDebug("AC1, Candidates: [overflow_candidates.len]")
for(var/mob/dead/new_player/player in overflow_candidates)
JobDebug("AC1 pass, Player: [player]")
- AssignRole(player, SSjob.overflow_role)
+ AssignRole(player, GetJobType(overflow_role))
overflow_candidates -= player
JobDebug("DO, AC1 end")
@@ -343,7 +349,7 @@ SUBSYSTEM_DEF(job)
// Hopefully this will add more randomness and fairness to job giving.
// Loop through all levels from high to low
- var/list/shuffledoccupations = shuffle(occupations)
+ var/list/shuffledoccupations = shuffle(joinable_occupations)
for(var/level in level_order)
//Check the head jobs first each level
CheckHeadPositions(level)
@@ -383,7 +389,7 @@ SUBSYSTEM_DEF(job)
// If the job isn't filled
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
JobDebug("DO pass, Player: [player], Level:[level], Job:[job.title]")
- AssignRole(player, job.title)
+ AssignRole(player, job)
unassigned -= player
break
@@ -398,7 +404,7 @@ SUBSYSTEM_DEF(job)
//Mop up people who can't leave.
for(var/mob/dead/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?)
if(!GiveRandomJob(player))
- if(!AssignRole(player, SSjob.overflow_role)) //If everything is already filled, make them an assistant
+ if(!AssignRole(player, GetJobType(overflow_role))) //If everything is already filled, make them an assistant
return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
return TRUE
@@ -408,11 +414,12 @@ SUBSYSTEM_DEF(job)
if(PopcapReached())
RejectPlayer(player)
else if(player.client.prefs.joblessrole == BEOVERFLOW)
- var/allowed_to_be_a_loser = !is_banned_from(player.ckey, SSjob.overflow_role)
+ var/datum/job/overflow_role_datum = GetJobType(overflow_role)
+ var/allowed_to_be_a_loser = !is_banned_from(player.ckey, overflow_role_datum.title)
if(QDELETED(player) || !allowed_to_be_a_loser)
RejectPlayer(player)
else
- if(!AssignRole(player, SSjob.overflow_role))
+ if(!AssignRole(player, overflow_role_datum))
RejectPlayer(player)
else if(player.client.prefs.joblessrole == BERANDOMJOB)
if(!GiveRandomJob(player))
@@ -426,95 +433,49 @@ SUBSYSTEM_DEF(job)
message_admins(message)
RejectPlayer(player)
+
//Gives the player the stuff he should have with his rank
-/datum/controller/subsystem/job/proc/EquipRank(mob/M, rank, joined_late = FALSE, is_captain = FALSE)
- var/mob/dead/new_player/newplayer
- var/mob/living/living_mob
- if(!joined_late)
- newplayer = M
- living_mob = newplayer.new_character
- else
- living_mob = M
+/datum/controller/subsystem/job/proc/EquipRank(mob/living/equipping, datum/job/job, client/player_client)
+ equipping.job = job.title
- var/datum/job/job = GetJob(rank)
+ SEND_SIGNAL(equipping, COMSIG_JOB_RECEIVED, job)
- living_mob.job = rank
+ equipping.mind?.set_assigned_role(job)
- SEND_SIGNAL(living_mob, COMSIG_JOB_RECEIVED, living_mob.job)
+ if(player_client)
+ to_chat(player_client, "You are the [job.title].")
- //If we joined at roundstart we should be positioned at our workstation
- if(!joined_late)
- var/spawning_handled = FALSE
- var/obj/S = null
- if(HAS_TRAIT(SSstation, STATION_TRAIT_LATE_ARRIVALS) && job.random_spawns_possible)
- SendToLateJoin(living_mob, buckle = TRUE, search_empty_chair = FALSE)
- spawning_handled = TRUE
- else if(HAS_TRAIT(SSstation, STATION_TRAIT_RANDOM_ARRIVALS) && job.random_spawns_possible)
- DropLandAtRandomHallwayPoint(living_mob)
- spawning_handled = TRUE
- else if(HAS_TRAIT(SSstation, STATION_TRAIT_HANGOVER) && job.random_spawns_possible)
- for(var/obj/effect/landmark/start/hangover/hangover_spawn in GLOB.start_landmarks_list)
- S = hangover_spawn
- if(locate(/mob/living) in hangover_spawn.loc) //so we can revert to spawning them on top of eachother if something goes wrong
- continue
- hangover_spawn.used = TRUE
- break
- else if(length(GLOB.jobspawn_overrides[rank]))
- S = pick(GLOB.jobspawn_overrides[rank])
+ equipping.on_job_equipping(job)
+
+ job.announce_job(equipping)
+
+ if(player_client?.holder)
+ if(CONFIG_GET(flag/auto_deadmin_players) || (player_client.prefs?.toggles & DEADMIN_ALWAYS))
+ player_client.holder.auto_deadmin()
else
- for(var/_sloc in GLOB.start_landmarks_list)
- var/obj/effect/landmark/start/sloc = _sloc
- if(sloc.name != rank)
- continue
- S = sloc
- if(locate(/mob/living) in sloc.loc) //so we can revert to spawning them on top of eachother if something goes wrong
- continue
- sloc.used = TRUE
- break
- if(S)
- S.JoinPlayerHere(living_mob, FALSE)
- if(!S && !spawning_handled) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper
- log_world("Couldn't find a round start spawn point for [rank]")
- if(!SendToLateJoin(living_mob))
- living_mob.move_to_error_room()
+ handle_auto_deadmin_roles(player_client, job.title)
+ if(player_client)
+ to_chat(player_client, "As the [job.title] you answer directly to [job.supervisors]. Special circumstances may change this.")
- if(living_mob.mind)
- living_mob.mind.assigned_role = rank
+ job.radio_help_message(equipping)
- to_chat(M, "You are the [rank].")
- if(job)
- var/new_mob = job.equip(living_mob, null, null, joined_late, null, M.client, is_captain)//silicons override this proc to return a mob
- if(ismob(new_mob))
- living_mob = new_mob
- if(!joined_late)
- newplayer.new_character = living_mob
- else
- M = living_mob
-
- if(M.client.holder)
- if(CONFIG_GET(flag/auto_deadmin_players) || (M.client.prefs?.toggles & DEADMIN_ALWAYS))
- M.client.holder.auto_deadmin()
- else
- handle_auto_deadmin_roles(M.client, rank)
-
- to_chat(M, "As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.")
- job.radio_help_message(M)
+ if(player_client)
if(job.req_admin_notify)
- to_chat(M, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.")
+ to_chat(player_client, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.")
if(CONFIG_GET(number/minimal_access_threshold))
- to_chat(M, span_notice("As this station was initially staffed with a [CONFIG_GET(flag/jobs_have_minimal_access) ? "full crew, only your job's necessities" : "skeleton crew, additional access may"] have been added to your ID card."))
+ to_chat(player_client, span_notice("As this station was initially staffed with a [CONFIG_GET(flag/jobs_have_minimal_access) ? "full crew, only your job's necessities" : "skeleton crew, additional access may"] have been added to your ID card."))
- var/related_policy = get_policy(rank)
- if(related_policy)
- to_chat(M,related_policy)
- if(ishuman(living_mob))
- var/mob/living/carbon/human/wageslave = living_mob
- living_mob.add_memory("Your account ID is [wageslave.account_id].")
- if(job && living_mob)
- job.after_spawn(living_mob, M, joined_late) // note: this happens before the mob has a key! M will always have a client, living_mob might not.
+ var/related_policy = get_policy(job.title)
+ if(related_policy)
+ to_chat(player_client, related_policy)
+
+ if(ishuman(equipping))
+ var/mob/living/carbon/human/wageslave = equipping
+ wageslave.add_memory("Your account ID is [wageslave.account_id].")
+
+ job.after_spawn(equipping, player_client)
- return living_mob
/datum/controller/subsystem/job/proc/handle_auto_deadmin_roles(client/C, rank)
if(!C?.holder)
@@ -564,14 +525,14 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/LoadJobs()
var/jobstext = file2text("[global.config.directory]/jobs.txt")
- for(var/datum/job/J in occupations)
- var/regex/jobs = new("[J.title]=(-1|\\d+),(-1|\\d+)")
+ for(var/datum/job/job as anything in joinable_occupations)
+ var/regex/jobs = new("[job.title]=(-1|\\d+),(-1|\\d+)")
jobs.Find(jobstext)
- J.total_positions = text2num(jobs.group[1])
- J.spawn_positions = text2num(jobs.group[2])
+ job.total_positions = text2num(jobs.group[1])
+ job.spawn_positions = text2num(jobs.group[2])
/datum/controller/subsystem/job/proc/HandleFeedbackGathering()
- for(var/datum/job/job in occupations)
+ for(var/datum/job/job as anything in joinable_occupations)
var/high = 0 //high
var/medium = 0 //medium
var/low = 0 //low
@@ -580,7 +541,7 @@ SUBSYSTEM_DEF(job)
var/young = 0 //account too young
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
- if(!(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role))
+ if(!(player.ready == PLAYER_READY_TO_PLAY && player.mind && is_unassigned_job(player.mind.assigned_role)))
continue //This player is not ready
if(is_banned_from(player.ckey, job.title) || QDELETED(player))
banned++
@@ -629,10 +590,10 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/Recover()
set waitfor = FALSE
- var/oldjobs = SSjob.occupations
+ var/oldjobs = SSjob.all_occupations
sleep(20)
- for (var/datum/job/J in oldjobs)
- INVOKE_ASYNC(src, .proc/RecoverJob, J)
+ for (var/datum/job/job as anything in oldjobs)
+ INVOKE_ASYNC(src, .proc/RecoverJob, job)
/datum/controller/subsystem/job/proc/RecoverJob(datum/job/J)
var/datum/job/newjob = GetJob(J.title)
@@ -652,10 +613,10 @@ SUBSYSTEM_DEF(job)
return
..()
-/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE, search_empty_chair = TRUE)
+/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE)
var/atom/destination
- if(M.mind && M.mind.assigned_role && length(GLOB.jobspawn_overrides[M.mind.assigned_role])) //We're doing something special today.
- destination = pick(GLOB.jobspawn_overrides[M.mind.assigned_role])
+ if(M.mind && !is_unassigned_job(M.mind.assigned_role) && length(GLOB.jobspawn_overrides[M.mind.assigned_role.title])) //We're doing something special today.
+ destination = pick(GLOB.jobspawn_overrides[M.mind.assigned_role.title])
destination.JoinPlayerHere(M, FALSE)
return TRUE
@@ -664,49 +625,39 @@ SUBSYSTEM_DEF(job)
destination.JoinPlayerHere(M, buckle)
return TRUE
+ destination = get_last_resort_spawn_points()
+ destination.JoinPlayerHere(M, buckle)
+
+
+/datum/controller/subsystem/job/proc/get_last_resort_spawn_points()
//bad mojo
- var/area/shuttle/arrival/spawnarea = GLOB.areas_by_type[/area/shuttle/arrival]
- if(spawnarea)
- if(search_empty_chair) // search carefully for an unoccupied chair for the character can buckle to.
- var/list/chairs = list()
- for(var/obj/structure/chair/chair in spawnarea)
- chairs += chair
- while(chairs.len)
- var/obj/structure/chair/chosen_chair = pick_n_take(chairs)
- if(!chosen_chair.has_buckled_mobs())
- chosen_chair.JoinPlayerHere(M, buckle)
- return TRUE
- //we either couldn't find a free chair or couldn't be bothered to.
- var/obj/structure/chair/chair = locate() in spawnarea
- if(chair)
- chair.JoinPlayerHere(M, buckle)
- return TRUE
+ var/area/shuttle/arrival/arrivals_area = GLOB.areas_by_type[/area/shuttle/arrival]
+ if(arrivals_area)
+ //first check if we can find a chair
+ var/obj/structure/chair/shuttle_chair = locate() in arrivals_area
+ if(shuttle_chair)
+ return shuttle_chair
//last hurrah
- var/list/avail = list()
- for(var/turf/T in spawnarea)
- if(!T.is_blocked_turf(TRUE))
- avail += T
- if(avail.len)
- destination = pick(avail)
- destination.JoinPlayerHere(M, FALSE)
- return TRUE
+ var/list/turf/available_turfs = list()
+ for(var/turf/arrivals_turf in arrivals_area)
+ if(!arrivals_turf.is_blocked_turf(TRUE))
+ available_turfs += arrivals_turf
+ if(length(available_turfs))
+ return pick(available_turfs)
//pick an open spot on arrivals and dump em
var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival))
- if(arrivals_turfs.len)
- for(var/turf/T in arrivals_turfs)
- if(!T.is_blocked_turf(TRUE))
- T.JoinPlayerHere(M, FALSE)
- return TRUE
+ if(length(arrivals_turfs))
+ for(var/turf/arrivals_turf in arrivals_turfs)
+ if(!arrivals_turf.is_blocked_turf(TRUE))
+ return arrivals_turf
//last chance, pick ANY spot on arrivals and dump em
- destination = arrivals_turfs[1]
- destination.JoinPlayerHere(M, FALSE)
- return TRUE
- else
- var/msg = "Unable to send mob [M] to late join!"
- message_admins(msg)
- CRASH(msg)
+ return pick(arrivals_turfs)
+
+ stack_trace("Unable to find last resort spawn point.")
+ return GET_ERROR_ROOM
+
///Lands specified mob at a random spot in the hallways
/datum/controller/subsystem/job/proc/DropLandAtRandomHallwayPoint(mob/living/living_mob)
@@ -724,10 +675,9 @@ SUBSYSTEM_DEF(job)
///////////////////////////////////
/datum/controller/subsystem/job/proc/get_living_heads()
. = list()
- for(var/i in GLOB.human_list)
- var/mob/living/carbon/human/player = i
- if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.command_positions))
- . |= player.mind
+ for(var/mob/living/carbon/human/player as anything in GLOB.human_list)
+ if(player.stat != DEAD && (player.mind?.assigned_role.departments & DEPARTMENT_COMMAND))
+ . += player.mind
////////////////////////////
@@ -735,30 +685,27 @@ SUBSYSTEM_DEF(job)
////////////////////////////
/datum/controller/subsystem/job/proc/get_all_heads()
. = list()
- for(var/i in GLOB.mob_list)
- var/mob/player = i
- if(player.mind && (player.mind.assigned_role in GLOB.command_positions))
- . |= player.mind
+ for(var/mob/living/carbon/human/player as anything in GLOB.human_list)
+ if(player.mind?.assigned_role.departments & DEPARTMENT_COMMAND)
+ . += player.mind
//////////////////////////////////////////////
//Keeps track of all living security members//
//////////////////////////////////////////////
/datum/controller/subsystem/job/proc/get_living_sec()
. = list()
- for(var/i in GLOB.human_list)
- var/mob/living/carbon/human/player = i
- if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.security_positions))
- . |= player.mind
+ for(var/mob/living/carbon/human/player as anything in GLOB.human_list)
+ if(player.stat != DEAD && (player.mind?.assigned_role.departments & DEPARTMENT_SECURITY))
+ . += player.mind
////////////////////////////////////////
//Keeps track of all security members//
////////////////////////////////////////
/datum/controller/subsystem/job/proc/get_all_sec()
. = list()
- for(var/i in GLOB.human_list)
- var/mob/living/carbon/human/player = i
- if(player.mind && (player.mind.assigned_role in GLOB.security_positions))
- . |= player.mind
+ for(var/mob/living/carbon/human/player as anything in GLOB.human_list)
+ if(player.mind?.assigned_role.departments & DEPARTMENT_SECURITY)
+ . += player.mind
/datum/controller/subsystem/job/proc/JobDebug(message)
log_job_debug(message)
@@ -839,4 +786,4 @@ SUBSYSTEM_DEF(job)
/// Blindly assigns the required roles to every player in the dynamic_forced_occupations list.
/datum/controller/subsystem/job/proc/assign_priority_positions()
for(var/mob/new_player in dynamic_forced_occupations)
- AssignRole(new_player, dynamic_forced_occupations[new_player])
+ AssignRole(new_player, GetJob(dynamic_forced_occupations[new_player]))
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index dbd7b7e1834..c4f00ac7215 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -63,10 +63,6 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
if(badquirk)
cli.prefs.save_character()
- if(ishuman(user))
- var/mob/living/carbon/human/human = user
- human.hardcore_survival_score = cli.prefs.hardcore_survival_score //Only do this if we actually asign quirks, to prevent sillicons etc from getting the points.
-
// Assign wayfinding pinpointer granting quirk if they're new
if(cli.get_exp_living(TRUE) < EXP_ASSIGN_WAYFINDER && !user.has_quirk(/datum/quirk/item_quirk/needswayfinder))
user.add_quirk(/datum/quirk/item_quirk/needswayfinder)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 308de11d31a..2e85c319a0c 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -329,7 +329,11 @@ SUBSYSTEM_DEF(ticker)
var/mob/dead/new_player/player = i
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
GLOB.joined_player_list += player.ckey
- player.create_character(FALSE)
+ var/atom/destination = player.mind.assigned_role.get_roundstart_spawn_point()
+ if(!destination) // Failed to fetch a proper roundstart location, won't be going anywhere.
+ player.new_player_panel()
+ continue
+ player.create_character(destination)
else
player.new_player_panel()
CHECK_TICK
@@ -359,35 +363,47 @@ SUBSYSTEM_DEF(ticker)
if(is_banned_from(new_player_mob.ckey, list("Captain")))
CHECK_TICK
continue
+ if(!ishuman(new_player_mob.new_character))
+ continue
var/mob/living/carbon/human/new_player_human = new_player_mob.new_character
- if(istype(new_player_human) && new_player_human.mind?.assigned_role)
- // Keep a rolling tally of who'll get the cap's spare ID vault code.
- // Check assigned_role's priority and curate the candidate list appropriately.
- var/player_assigned_role = new_player_human.mind.assigned_role
- var/spare_id_priority = SSjob.chain_of_command[player_assigned_role]
- if(spare_id_priority)
- if(spare_id_priority < highest_rank)
- spare_id_candidates.Cut()
- spare_id_candidates += new_player_mob
- highest_rank = spare_id_priority
- else if(spare_id_priority == highest_rank)
- spare_id_candidates += new_player_mob
- CHECK_TICK
+ if(!new_player_human.mind || is_unassigned_job(new_player_human.mind.assigned_role))
+ continue
+ // Keep a rolling tally of who'll get the cap's spare ID vault code.
+ // Check assigned_role's priority and curate the candidate list appropriately.
+ var/player_assigned_role = new_player_human.mind.assigned_role.title
+ var/spare_id_priority = SSjob.chain_of_command[player_assigned_role]
+ if(spare_id_priority)
+ if(spare_id_priority < highest_rank)
+ spare_id_candidates.Cut()
+ spare_id_candidates += new_player_mob
+ highest_rank = spare_id_priority
+ else if(spare_id_priority == highest_rank)
+ spare_id_candidates += new_player_mob
+ CHECK_TICK
if(length(spare_id_candidates))
picked_spare_id_candidate = pick(spare_id_candidates)
for(var/mob/dead/new_player/new_player_mob as anything in GLOB.new_player_list)
+ if(!isliving(new_player_mob.new_character))
+ CHECK_TICK
+ continue
var/mob/living/new_player_living = new_player_mob.new_character
- if(istype(new_player_living) && new_player_living.mind?.assigned_role)
- var/player_assigned_role = new_player_living.mind.assigned_role
- var/player_is_captain = (picked_spare_id_candidate == new_player_mob) || (SSjob.always_promote_captain_job && (player_assigned_role == "Captain"))
- if(player_is_captain)
- captainless = FALSE
- if(player_assigned_role != new_player_living.mind.special_role)
- new_player_living = SSjob.EquipRank(new_player_mob, player_assigned_role, FALSE, player_is_captain)
- if(CONFIG_GET(flag/roundstart_traits) && ishuman(new_player_living))
- SSquirks.AssignQuirks(new_player_living, new_player_mob.client)
+ if(!new_player_living.mind || is_unassigned_job(new_player_living.mind.assigned_role))
+ CHECK_TICK
+ continue
+ var/datum/job/player_assigned_role = new_player_living.mind.assigned_role
+ if(ishuman(new_player_living) && CONFIG_GET(flag/roundstart_traits))
+ if(new_player_mob.client.prefs.should_be_random_hardcore(player_assigned_role, new_player_living.mind))
+ new_player_mob.client.prefs.hardcore_random_setup(new_player_living)
+ SSquirks.AssignQuirks(new_player_living, new_player_mob.client)
+ if(player_assigned_role.job_flags & JOB_EQUIP_RANK)
+ SSjob.EquipRank(new_player_living, player_assigned_role, new_player_mob.client)
+ if(picked_spare_id_candidate == new_player_mob)
+ captainless = FALSE
+ var/acting_captain = !is_captain_job(player_assigned_role)
+ SSjob.promote_to_captain(new_player_living, acting_captain)
+ OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, player_assigned_role.get_captaincy_announcement(new_player_living)))
CHECK_TICK
if(captainless)
@@ -397,6 +413,7 @@ SUBSYSTEM_DEF(ticker)
to_chat(new_player_mob, span_notice("Captainship not forced on anyone."))
CHECK_TICK
+
/datum/controller/subsystem/ticker/proc/decide_security_officer_departments(
list/new_players,
list/departments,
@@ -406,7 +423,7 @@ SUBSYSTEM_DEF(ticker)
for (var/mob/dead/new_player/new_player_mob as anything in new_players)
var/mob/living/carbon/human/character = new_player_mob.new_character
- if (istype(character) && character.mind?.assigned_role == "Security Officer")
+ if (istype(character) && is_security_officer_job(character.mind?.assigned_role))
officer_mobs += character
var/datum/client_interface/client = GET_CLIENT(new_player_mob)
diff --git a/code/datums/brain_damage/creepy_trauma.dm b/code/datums/brain_damage/creepy_trauma.dm
index 9d5dfbabb8d..9d13af33caf 100644
--- a/code/datums/brain_damage/creepy_trauma.dm
+++ b/code/datums/brain_damage/creepy_trauma.dm
@@ -122,12 +122,15 @@
var/list/special_pool = list() //The special list, for quirk-based
var/chosen_victim //The obsession target
- for(var/mob/Player in GLOB.player_list)//prevents crewmembers falling in love with nuke ops they never met, and other annoying hijinks
- if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) && !isbrain(Player) && Player.client && Player != owner && SSjob.GetJob(Player.mind.assigned_role))
- viable_minds += Player.mind
- for(var/datum/mind/possible_target in viable_minds)
+ for(var/mob/player as anything in GLOB.player_list)//prevents crewmembers falling in love with nuke ops they never met, and other annoying hijinks
+ if(!player.client || !player.mind || isnewplayer(player) || player.stat == DEAD || isbrain(player) || player == owner)
+ continue
+ if(!(player.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
+ continue
+ viable_minds += player.mind
+ for(var/datum/mind/possible_target as anything in viable_minds)
if(possible_target != owner && ishuman(possible_target.current))
- var/job = possible_target.assigned_role
+ var/job = possible_target.assigned_role.title
if (trait_obsessions[job] != null && HAS_TRAIT(owner, trait_obsessions[job]))
special_pool += possible_target.current
possible_targets += possible_target.current
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index 50b877ae45c..ae61315098e 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -105,7 +105,7 @@
var/gender = pick(MALE, FEMALE)
real_name = random_unique_name(gender)
name = real_name
- human_image = get_flat_human_icon(null, pick(SSjob.occupations))
+ human_image = get_flat_human_icon(null, pick(SSjob.joinable_occupations))
/mob/camera/imaginary_friend/proc/Show()
if(!client) //nobody home
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index aceceac67f8..cfc7504885f 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -167,12 +167,8 @@
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
continue
- if(I.restricted_roles.len)
- var/is_inaccessible = TRUE
- for(var/R in I.restricted_roles)
- if(R == user.mind.assigned_role || debug)
- is_inaccessible = FALSE
- if(is_inaccessible)
+ if(length(I.restricted_roles))
+ if(!debug && !(user.mind.assigned_role.title in I.restricted_roles))
continue
if(I.restricted_species)
if(ishuman(user))
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index f0f26775af4..95990b75159 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -227,14 +227,8 @@
/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H, client/C)
set waitfor = FALSE
var/static/list/show_directions = list(SOUTH, WEST)
- if(H.mind && (H.mind.assigned_role != H.mind.special_role))
- var/assignment
- if(H.mind.assigned_role)
- assignment = H.mind.assigned_role
- else if(H.job)
- assignment = H.job
- else
- assignment = "Unassigned"
+ if(H.mind?.assigned_role.job_flags & JOB_CREW_MANIFEST)
+ var/assignment = H.mind.assigned_role.title
var/static/record_id_num = 1001
var/id = num2hex(record_id_num++,6)
@@ -302,9 +296,9 @@
//Locked Record
var/datum/data/record/L = new()
- L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") //surely this should just be id, like the others?
+ L.fields["id"] = md5("[H.real_name][assignment]") //surely this should just be id, like the others?
L.fields["name"] = H.real_name
- L.fields["rank"] = H.mind.assigned_role
+ L.fields["rank"] = assignment
L.fields["age"] = H.age
L.fields["gender"] = H.gender
if(H.gender == "male")
@@ -324,7 +318,7 @@
return
/datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C, show_directions = list(SOUTH))
- var/datum/job/J = SSjob.GetJob(H.mind.assigned_role)
+ var/datum/job/J = H.mind.assigned_role
var/datum/preferences/P
if(!C)
C = H.client
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index cc5ccb49936..f5ad9f5c547 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -471,7 +471,7 @@
/mob/living/carbon/proc/create_dna()
dna = new /datum/dna(src)
if(!dna.species)
- var/rando_race = pick(GLOB.roundstart_races)
+ var/rando_race = GLOB.species_list[pick(GLOB.roundstart_races)]
dna.species = new rando_race()
//proc used to update the mob's appearance after its dna UI has been changed
diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm
index f4e5847cfc8..5eae09c57a4 100644
--- a/code/datums/martial/cqc.dm
+++ b/code/datums/martial/cqc.dm
@@ -227,10 +227,6 @@
/// Refreshes the valid areas from the cook job singleton, otherwise uses the default kitchen area as a fallback option. See also [/datum/job/cook/New].
/datum/martial_art/cqc/under_siege/proc/refresh_valid_areas()
var/datum/job/cook/cook_job = SSjob.GetJobType(/datum/job/cook)
- if(!istype(cook_job))
- kitchen_areas = list(/area/service/kitchen)
- return
-
kitchen_areas = cook_job.kitchen_areas.Copy()
/// Limits where the chef's CQC can be used to only whitelisted areas.
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 0db7909c5bd..36a6d8b5984 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -38,7 +38,8 @@
var/memory
- var/assigned_role
+ /// Job datum indicating the mind's role. This should always exist after initialization, as a reference to a singleton.
+ var/datum/job/assigned_role
var/special_role
var/list/restricted_roles = list()
var/list/datum/objective/objectives = list()
@@ -92,6 +93,7 @@
key = _key
martial_art = default_martial_art
init_known_skills()
+ set_assigned_role(SSjob.GetJobType(/datum/job/unassigned)) // Unassigned by default.
/datum/mind/Destroy()
SSticker.minds -= src
@@ -495,10 +497,14 @@
A.admin_remove(usr)
if (href_list["role_edit"])
- var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in sortList(SSjob.station_jobs)
- if (!new_role)
+ var/new_role = input("Select new role", "Assigned role", assigned_role.title) as null|anything in sortList(SSjob.station_jobs)
+ if(isnull(new_role))
return
- assigned_role = new_role
+ var/datum/job/new_job = SSjob.GetJob(new_role)
+ if (!new_job)
+ to_chat(usr, span_warning("Job not found."))
+ return
+ set_assigned_role(new_role)
else if (href_list["memory_edit"])
var/new_memo = stripped_multiline_input(usr, "Write new memory", "Memory", memory, MAX_MESSAGE_LEN)
@@ -695,11 +701,14 @@
special_role = ROLE_CHANGELING
return C
+
/datum/mind/proc/make_wizard()
- if(!has_antag_datum(/datum/antagonist/wizard))
- special_role = ROLE_WIZARD
- assigned_role = ROLE_WIZARD
- add_antag_datum(/datum/antagonist/wizard)
+ if(has_antag_datum(/datum/antagonist/wizard))
+ return
+ set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
+ special_role = ROLE_WIZARD
+ add_antag_datum(/datum/antagonist/wizard)
+
/datum/mind/proc/make_rev()
var/datum/antagonist/rev/head/head = new()
@@ -803,12 +812,22 @@
var/datum/addiction/affected_addiction = SSaddiction.all_addictions[type]
return affected_addiction.on_lose_addiction_points(src)
+
+/// Setter for the assigned_role job datum.
+/datum/mind/proc/set_assigned_role(datum/job/new_role)
+ if(assigned_role == new_role)
+ return
+ . = assigned_role
+ assigned_role = new_role
+
+
/mob/dead/new_player/sync_mind()
return
/mob/dead/observer/sync_mind()
return
+
//Initialisation procs
/mob/proc/mind_initialize()
if(mind)
@@ -821,28 +840,26 @@
mind.name = real_name
mind.set_current(src)
+
/mob/living/carbon/mind_initialize()
..()
last_mind = mind
-//HUMAN
-/mob/living/carbon/human/mind_initialize()
- ..()
- if(!mind.assigned_role)
- mind.assigned_role = "Unassigned" //default
//AI
/mob/living/silicon/ai/mind_initialize()
- ..()
- mind.assigned_role = "AI"
+ . = ..()
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/ai))
+
//BORG
/mob/living/silicon/robot/mind_initialize()
- ..()
- mind.assigned_role = "Cyborg"
+ . = ..()
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/cyborg))
+
//PAI
/mob/living/silicon/pai/mind_initialize()
- ..()
- mind.assigned_role = ROLE_PAI
+ . = ..()
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/personal_ai))
mind.special_role = ""
diff --git a/code/datums/mutations/holy_mutation/honorbound.dm b/code/datums/mutations/holy_mutation/honorbound.dm
index fc72db7748c..0426710fd67 100644
--- a/code/datums/mutations/holy_mutation/honorbound.dm
+++ b/code/datums/mutations/holy_mutation/honorbound.dm
@@ -79,8 +79,8 @@
return
var/datum/mind/guilty_conscience = user.mind
if(guilty_conscience) //sec and medical are immune to becoming guilty through attack (we don't check holy because holy shouldn't be able to attack eachother anyways)
- var/job = guilty_conscience.assigned_role
- if(job in (GLOB.security_positions + GLOB.medical_positions))
+ var/datum/job/job = guilty_conscience.assigned_role
+ if(job.departments & (DEPARTMENT_MEDICAL | DEPARTMENT_SECURITY))
return
if(declaration)
to_chat(owner, span_notice("[user] is now considered guilty by [GLOB.deity] from your declaration."))
@@ -107,12 +107,12 @@
//THE JUST (Applies over guilt except for med, so you best be careful!)
if(ishuman(target_creature))
var/mob/living/carbon/human/target_human = target_creature
- var/job = target_human.mind?.assigned_role
+ var/datum/job/job = target_human.mind?.assigned_role
var/is_holy = target_human.mind?.holy_role
- if(job in GLOB.security_positions || is_holy)
+ if(is_holy || (job?.departments & DEPARTMENT_SECURITY))
to_chat(honorbound_human, span_warning("There is nothing righteous in attacking the just."))
return FALSE
- if(job in GLOB.medical_positions)
+ if(job?.departments & DEPARTMENT_MEDICAL)
to_chat(honorbound_human, span_warning("If you truly think this healer is not innocent, declare them guilty."))
return FALSE
//THE INNOCENT
@@ -272,7 +272,7 @@
if(!silent)
to_chat(user, span_warning("Followers of [GLOB.deity] cannot be evil!"))
return FALSE
- if(guilty_conscience.assigned_role in GLOB.security_positions)
+ if(guilty_conscience.assigned_role.departments & DEPARTMENT_SECURITY)
if(!silent)
to_chat(user, span_warning("Members of security are uncorruptable! You cannot declare one evil!"))
return FALSE
diff --git a/code/datums/quirks/negative.dm b/code/datums/quirks/negative.dm
index 3510c39ec41..335901bd364 100644
--- a/code/datums/quirks/negative.dm
+++ b/code/datums/quirks/negative.dm
@@ -166,7 +166,7 @@
heirloom_type = pick(holder_species.family_heirlooms)
else
// Our quirk holder's job
- var/datum/job/holder_job = SSjob.GetJob(human_holder.mind?.assigned_role)
+ var/datum/job/holder_job = human_holder.mind?.assigned_role
if(holder_job && LAZYLEN(holder_job.family_heirlooms))
heirloom_type = pick(holder_job.family_heirlooms)
diff --git a/code/datums/quirks/neutral.dm b/code/datums/quirks/neutral.dm
index 8db41b269ec..4510717dc56 100644
--- a/code/datums/quirks/neutral.dm
+++ b/code/datums/quirks/neutral.dm
@@ -147,7 +147,7 @@
quirk_holder.add_client_colour(/datum/client_colour/monochrome)
/datum/quirk/monochromatic/post_add()
- if(quirk_holder.mind.assigned_role == "Detective")
+ if(is_detective_job(quirk_holder.mind.assigned_role))
to_chat(quirk_holder, span_boldannounce("Mmm. Nothing's ever clear on this station. It's all shades of gray..."))
quirk_holder.playsound_local(quirk_holder, 'sound/ambience/ambidet1.ogg', 50, FALSE)
diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm
index 04004e8c4a9..ab1796e35ee 100644
--- a/code/datums/station_traits/negative_traits.dm
+++ b/code/datums/station_traits/negative_traits.dm
@@ -46,17 +46,26 @@
/datum/station_trait/hangover/New()
. = ..()
- RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
+ RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_LATEJOIN_SPAWN, .proc/on_job_after_spawn)
-/datum/station_trait/hangover/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/living_mob, mob/spawned_mob, joined_late)
+
+/datum/station_trait/hangover/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned_mob)
SIGNAL_HANDLER
- if(joined_late)
+ if(!prob(35))
return
- if(prob(35))
- var/obj/item/hat = pick(list(/obj/item/clothing/head/sombrero, /obj/item/clothing/head/fedora, /obj/item/clothing/mask/balaclava, /obj/item/clothing/head/ushanka, /obj/item/clothing/head/cardborg, /obj/item/clothing/head/pirate, /obj/item/clothing/head/cone))
- hat = new hat(spawned_mob)
- spawned_mob.equip_to_slot(hat, ITEM_SLOT_HEAD)
+ var/obj/item/hat = pick(
+ /obj/item/clothing/head/sombrero,
+ /obj/item/clothing/head/fedora,
+ /obj/item/clothing/mask/balaclava,
+ /obj/item/clothing/head/ushanka,
+ /obj/item/clothing/head/cardborg,
+ /obj/item/clothing/head/pirate,
+ /obj/item/clothing/head/cone,
+ )
+ hat = new hat(spawned_mob)
+ spawned_mob.equip_to_slot_or_del(hat, ITEM_SLOT_HEAD)
+
/datum/station_trait/blackout
name = "Blackout"
@@ -86,11 +95,20 @@
trait_type = STATION_TRAIT_NEGATIVE
weight = 5
show_in_report = TRUE
- var/list/jobs_to_use = list("Clown", "Bartender", "Cook", "Botanist", "Cargo Technician", "Mime", "Janitor", "Prisoner")
var/chosen_job
/datum/station_trait/overflow_job_bureaucracy/New()
. = ..()
+ var/list/jobs_to_use = list(
+ /datum/job/clown,
+ /datum/job/bartender,
+ /datum/job/cook,
+ /datum/job/botanist,
+ /datum/job/cargo_technician,
+ /datum/job/mime,
+ /datum/job/janitor,
+ /datum/job/prisoner,
+ )
chosen_job = pick(jobs_to_use)
RegisterSignal(SSjob, COMSIG_SUBSYSTEM_POST_INITIALIZE, .proc/set_overflow_job_override)
diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm
index 585588e97b7..e26c3d5232f 100644
--- a/code/datums/station_traits/positive_traits.dm
+++ b/code/datums/station_traits/positive_traits.dm
@@ -97,11 +97,13 @@
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
-/datum/station_trait/scarves/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/living_mob, mob/M, joined_late)
+
+/datum/station_trait/scarves/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER
var/scarf_type = pick(scarves)
- living_mob.equip_to_slot_or_del(new scarf_type(living_mob), ITEM_SLOT_NECK, initial = FALSE)
+ spawned.equip_to_slot_or_del(new scarf_type(spawned), ITEM_SLOT_NECK, initial = FALSE)
+
/datum/station_trait/filled_maint
name = "Filled up maintenance"
@@ -143,7 +145,8 @@
report_message = "All members of [department_name] have received an implant to notify each other if one of them dies. This should help improve job-safety!"
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
-/datum/station_trait/deathrattle_department/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/living_mob, mob/M, joined_late)
+
+/datum/station_trait/deathrattle_department/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER
if(!(job.departments & department_to_apply_to))
@@ -151,7 +154,7 @@
var/obj/item/implant/deathrattle/implant_to_give = new()
deathrattle_group.register(implant_to_give)
- implant_to_give.implant(living_mob, living_mob, TRUE, TRUE)
+ implant_to_give.implant(spawned, spawned, TRUE, TRUE)
/datum/station_trait/deathrattle_department/service
@@ -211,12 +214,13 @@
blacklist = subtypesof(/datum/station_trait/deathrattle_department)
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, .proc/on_job_after_spawn)
-/datum/station_trait/deathrattle_all/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/living_mob, mob/M, joined_late)
+
+/datum/station_trait/deathrattle_all/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER
var/obj/item/implant/deathrattle/implant_to_give = new()
deathrattle_group.register(implant_to_give)
- implant_to_give.implant(living_mob, living_mob, TRUE, TRUE)
+ implant_to_give.implant(spawned, spawned, TRUE, TRUE)
/datum/station_trait/wallets
diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm
index b8a3d99e41f..b7eb87e0bf9 100644
--- a/code/datums/voice_of_god_command.dm
+++ b/code/datums/voice_of_god_command.dm
@@ -68,18 +68,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
to_remove_string = "(\\L|^)([to_remove_string])(\\L|$)"
message = replacetext(message, regex(to_remove_string, "i"), "")
- var/power_multiplier = base_multiplier
-
- if(user.mind)
- //Chaplains are very good at speaking with the voice of god
- if(user.mind.assigned_role == "Chaplain")
- power_multiplier *= 2
- //Command staff has authority
- if(user.mind.assigned_role in GLOB.command_positions)
- power_multiplier *= 1.4
- //Why are you speaking
- if(user.mind.assigned_role == "Mime")
- power_multiplier *= 0.5
+ var/power_multiplier = base_multiplier * (user.mind?.assigned_role.voice_of_god_power || 1)
//Cultists are closer to their gods and are more powerful, but they'll give themselves away
if(is_cultie)
@@ -88,7 +77,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
//Now get the proper job titles and check for matches.
var/job_message = get_full_job_name(message)
for(var/mob/living/candidate in candidates)
- var/their_role = candidate.mind?.assigned_role
+ var/their_role = candidate.mind?.assigned_role.title
if(their_role && findtext(job_message, their_role))
specific_listeners |= candidate //focus on those with the specified job. "|=" instead "+=" so "Mrs. Capri the Captain" doesn't get affected twice.
@@ -154,8 +143,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
cooldown = COOLDOWN_STUN
/datum/voice_of_god_command/silence/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
- if(user.mind && (user.mind.assigned_role == "Curator" || user.mind.assigned_role == "Mime"))
- power_multiplier *= 3
+ power_multiplier *= user.mind?.assigned_role?.voice_of_god_silence_power || 1
for(var/mob/living/carbon/target in listeners)
target.silent += (10 * power_multiplier)
@@ -400,7 +388,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
/datum/voice_of_god_command/honk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, get_turf(user), 'sound/items/bikehorn.ogg', 300, 1), 2.5 SECONDS)
- if(user.mind?.assigned_role == "Clown")
+ if(is_clown_job(user.mind?.assigned_role))
. = COOLDOWN_STUN //it slips.
for(var/mob/living/carbon/target in listeners)
target.slip(14 SECONDS * power_multiplier)
diff --git a/code/game/alternate_appearance.dm b/code/game/alternate_appearance.dm
index 2153104c91c..71ad1478565 100644
--- a/code/game/alternate_appearance.dm
+++ b/code/game/alternate_appearance.dm
@@ -166,7 +166,7 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/blessed_aware/mobShouldSee(mob/M)
- if(M.mind && (M.mind.assigned_role == "Chaplain"))
+ if(is_chaplain_job(M.mind?.assigned_role))
return TRUE
if (istype(M, /mob/living/simple_animal/hostile/construct/wraith))
return TRUE
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 4495dbbb96c..eed4a3cfb23 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -681,3 +681,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/// A hook so areas can modify the incoming args (of what??)
/area/proc/PlaceOnTopReact(list/new_baseturfs, turf/fake_turf_type, flags)
return flags
+
+
+/// Called when a living mob that spawned here, joining the round, receives the player client.
+/area/proc/on_joining_game(mob/living/boarder)
+ return
diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm
index 7ae8a43e3a8..d6dd38e64ac 100644
--- a/code/game/area/areas/shuttles.dm
+++ b/code/game/area/areas/shuttles.dm
@@ -99,10 +99,20 @@
desc = "Weeeeee"
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
+
/area/shuttle/arrival
name = "Arrival Shuttle"
area_flags = UNIQUE_AREA// SSjob refers to this area for latejoiners
+
+/area/shuttle/arrival/on_joining_game(mob/living/boarder)
+ if(SSshuttle.arrivals?.mode == SHUTTLE_CALL)
+ var/atom/movable/screen/splash/Spl = new(boarder.client, TRUE)
+ Spl.Fade(TRUE)
+ boarder.playsound_local(get_turf(boarder), 'sound/voice/ApproachingTG.ogg', 25)
+ boarder.update_parallax_teleport()
+
+
/area/shuttle/pod_1
name = "Escape Pod One"
area_flags = NONE
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index fe1721b1c77..b4eeae7ec9f 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -6,13 +6,13 @@
/datum/dynamic_ruleset/latejoin/trim_candidates()
for(var/mob/P in candidates)
- if(!P.client || !P.mind || !P.mind.assigned_role) // Are they connected?
+ if(!P.client || !P.mind || is_unassigned_job(P.mind.assigned_role)) // Are they connected?
candidates.Remove(P)
else if (P.client.get_remaining_days(minimum_required_age) > 0)
candidates.Remove(P)
- else if(P.mind.assigned_role in restricted_roles) // Does their job allow for it?
+ else if(P.mind.assigned_role.title in restricted_roles) // Does their job allow for it?
candidates.Remove(P)
- else if((exclusive_roles.len > 0) && !(P.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
+ else if((exclusive_roles.len > 0) && !(P.mind.assigned_role.title in exclusive_roles)) // Is the rule exclusive to their job?
candidates.Remove(P)
else 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)))
@@ -28,7 +28,7 @@
for (var/mob/M in GLOB.alive_player_list)
if (M.stat == DEAD)
continue // Dead players cannot count as opponents
- if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
+ if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
var/threat = round(mode.threat_level/10)
@@ -96,7 +96,7 @@
return FALSE
var/head_check = 0
for(var/mob/player in GLOB.alive_player_list)
- if (player.mind.assigned_role in GLOB.command_positions)
+ if (player.mind.assigned_role.departments & DEPARTMENT_COMMAND)
head_check++
return (head_check >= required_heads_of_staff)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index ffb6679af7f..98854b7f000 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -55,13 +55,13 @@
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?
+ if (restrict_ghost_roles && (M.mind.assigned_role.title in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
trimmed_list.Remove(M)
continue
- if (M.mind.assigned_role in restricted_roles) // Does their job allow it?
+ if (M.mind.assigned_role.title in restricted_roles) // Does their job allow it?
trimmed_list.Remove(M)
continue
- if ((exclusive_roles.len > 0) && !(M.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
+ if ((exclusive_roles.len > 0) && !(M.mind.assigned_role.title in exclusive_roles)) // Is the rule exclusive to their job?
trimmed_list.Remove(M)
continue
return trimmed_list
@@ -78,7 +78,7 @@
for (var/mob/M in GLOB.alive_player_list)
if (M.stat == DEAD || !M.client)
continue // Dead/disconnected players cannot count as opponents
- if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
+ if (M.mind && (M.mind.assigned_role.title in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role.title in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
var/threat = round(mode.threat_level/10)
@@ -422,8 +422,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.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative))
+ new_character.mind.special_role = ROLE_NUCLEAR_OPERATIVE
if (index == 1) // Our first guy is the leader
var/datum/antagonist/nukeop/leader/new_role = new
nuke_team = new_role.nuke_team
@@ -543,7 +543,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nightmare
name = "Nightmare"
antag_datum = /datum/antagonist/nightmare
- antag_flag = "Nightmare"
+ antag_flag = ROLE_NIGHTMARE
antag_flag_override = ROLE_ALIEN
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
@@ -570,8 +570,8 @@
var/mob/living/carbon/human/S = new (pick(spawn_locs))
player_mind.transfer_to(S)
- player_mind.assigned_role = "Nightmare"
- player_mind.special_role = "Nightmare"
+ player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nightmare))
+ player_mind.special_role = ROLE_NIGHTMARE
player_mind.add_antag_datum(/datum/antagonist/nightmare)
S.set_species(/datum/species/shadow/nightmare)
@@ -589,7 +589,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/space_dragon
name = "Space Dragon"
antag_datum = /datum/antagonist/space_dragon
- antag_flag = "Space Dragon"
+ antag_flag = ROLE_SPACE_DRAGON
antag_flag_override = ROLE_SPACE_DRAGON
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
@@ -614,8 +614,8 @@
var/mob/living/simple_animal/hostile/space_dragon/S = new (pick(spawn_locs))
player_mind.transfer_to(S)
- player_mind.assigned_role = "Space Dragon"
- player_mind.special_role = "Space Dragon"
+ player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
+ player_mind.special_role = ROLE_SPACE_DRAGON
player_mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index b05d9b83d3b..41a52444695 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -60,9 +60,7 @@
flags = HIGH_IMPACT_RULESET
/datum/dynamic_ruleset/roundstart/malf_ai/ready(forced)
- var/datum/job/ai_job = SSjob.GetJob("AI")
- if(!ai_job)
- return FALSE
+ var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
// If we're not forced, we're going to make sure we can actually have an AI in this shift,
if(!forced && min(ai_job.total_positions - ai_job.current_positions, ai_job.spawn_positions) <= 0)
@@ -73,7 +71,7 @@
/datum/dynamic_ruleset/roundstart/malf_ai/pre_execute(population)
. = ..()
- var/datum/job/ai_job = SSjob.GetJob("AI")
+ var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
// Maybe a bit too pedantic, but there should never be more malf AIs than there are available positions, spawn positions or antag cap allocations.
var/num_malf = min(get_antag_cap(population), min(ai_job.total_positions - ai_job.current_positions, ai_job.spawn_positions))
for (var/i in 1 to num_malf)
@@ -248,7 +246,7 @@
var/mob/M = pick_n_take(candidates)
if (M)
assigned += M.mind
- M.mind.assigned_role = ROLE_WIZARD
+ M.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
M.mind.special_role = ROLE_WIZARD
return TRUE
@@ -350,8 +348,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.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative))
+ M.mind.special_role = ROLE_NUCLEAR_OPERATIVE
return TRUE
/datum/dynamic_ruleset/roundstart/nuclear/execute()
@@ -577,8 +575,8 @@
qdel(S)
new /obj/machinery/nuclearbomb/syndicate/bananium(T)
for(var/datum/mind/V in assigned)
- V.assigned_role = "Clown Operative"
- V.special_role = "Clown Operative"
+ V.set_assigned_role(SSjob.GetJobType(/datum/job/clown_operative))
+ V.special_role = ROLE_CLOWN_OPERATIVE
//////////////////////////////////////////////
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 2814fc857f9..a622e6cc247 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -150,31 +150,6 @@ GLOBAL_LIST_EMPTY(objectives)
update_explanation_text()
return target
-/datum/objective/proc/find_target_by_role(role, role_type=FALSE,invert=FALSE)//Option sets either to check assigned role or special role. Default to assigned., invert inverts the check, eg: "Don't choose a Ling"
- var/list/datum/mind/owners = get_owners()
- var/list/possible_targets = list()
- for(var/datum/mind/possible_target in get_crewmember_minds())
- if(!(possible_target in owners) && ishuman(possible_target.current))
- var/is_role = FALSE
- if(role_type)
- if(possible_target.special_role == role)
- is_role = TRUE
- else
- if(possible_target.assigned_role == role)
- is_role = TRUE
-
- if(invert)
- if(is_role)
- continue
- possible_targets += possible_target
- break
- else if(is_role)
- possible_targets += possible_target
- break
- if(length(possible_targets))
- target = pick(possible_targets)
- update_explanation_text()
- return target
/datum/objective/proc/update_explanation_text()
if(team_explanation_text && LAZYLEN(get_owners()) > 1)
@@ -195,10 +170,6 @@ GLOBAL_LIST_EMPTY(objectives)
var/target_role_type=FALSE
martyr_compatible = TRUE
-/datum/objective/assassinate/find_target_by_role(role, role_type=FALSE,invert=FALSE)
- if(!invert)
- target_role_type = role_type
- ..()
/datum/objective/assassinate/check_completion()
return completed || (!considered_alive(target) || considered_afk(target) || considered_exiled(target))
@@ -206,7 +177,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/assassinate/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
+ explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role]."
else
explanation_text = "Free Objective"
@@ -226,10 +197,6 @@ GLOBAL_LIST_EMPTY(objectives)
var/target_role_type=FALSE
martyr_compatible = 1
-/datum/objective/mutiny/find_target_by_role(role, role_type=FALSE,invert=FALSE)
- if(!invert)
- target_role_type = role_type
- ..()
/datum/objective/mutiny/check_completion()
if(!target || !considered_alive(target) || considered_afk(target) || considered_exiled(target))
@@ -240,7 +207,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/mutiny/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Assassinate or exile [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
+ explanation_text = "Assassinate or exile [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role]."
else
explanation_text = "Free Objective"
@@ -249,17 +216,13 @@ GLOBAL_LIST_EMPTY(objectives)
var/target_role_type=FALSE
martyr_compatible = TRUE
-/datum/objective/maroon/find_target_by_role(role, role_type=FALSE,invert=FALSE)
- if(!invert)
- target_role_type = role_type
- ..()
/datum/objective/maroon/check_completion()
return !target || !considered_alive(target) || (!target.current.onCentCom() && !target.current.onSyndieBase())
/datum/objective/maroon/update_explanation_text()
if(target?.current)
- explanation_text = "Prevent [target.name], the [!target_role_type ? target.assigned_role : target.special_role], from escaping alive."
+ explanation_text = "Prevent [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role], from escaping alive."
else
explanation_text = "Free Objective"
@@ -270,10 +233,6 @@ GLOBAL_LIST_EMPTY(objectives)
name = "debrain"
var/target_role_type=0
-/datum/objective/debrain/find_target_by_role(role, role_type=FALSE,invert=FALSE)
- if(!invert)
- target_role_type = role_type
- ..()
/datum/objective/debrain/check_completion()
if(!target)//If it's a free objective.
@@ -293,7 +252,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/debrain/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Steal the brain of [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
+ explanation_text = "Steal the brain of [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role]."
else
explanation_text = "Free Objective"
@@ -306,11 +265,6 @@ GLOBAL_LIST_EMPTY(objectives)
var/target_role_type = FALSE
var/human_check = TRUE
-/datum/objective/protect/find_target_by_role(role, role_type=FALSE,invert=FALSE)
- if(!invert)
- target_role_type = role_type
- ..()
- return target
/datum/objective/protect/check_completion()
var/obj/item/organ/brain/brain_target
@@ -322,7 +276,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/protect/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Protect [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
+ explanation_text = "Protect [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role]."
else
explanation_text = "Free Objective"
@@ -338,10 +292,6 @@ GLOBAL_LIST_EMPTY(objectives)
martyr_compatible = TRUE //why not?
var/target_role_type
-/datum/objective/jailbreak/find_target_by_role(role, role_type, invert=FALSE)
- if(!invert)
- target_role_type = role_type
- return ..()
/datum/objective/jailbreak/check_completion()
return completed || (considered_escaped(target))
@@ -349,7 +299,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/jailbreak/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Ensure that [target.name], the [!target_role_type ? target.assigned_role : target.special_role] escapes alive and out of custody."
+ explanation_text = "Ensure that [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role] escapes alive and out of custody."
else
explanation_text = "Free Objective"
@@ -365,7 +315,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/jailbreak/detain/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Ensure that [target.name], the [!target_role_type ? target.assigned_role : target.special_role] is delivered to nanotrasen alive and in custody."
+ explanation_text = "Ensure that [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role] is delivered to nanotrasen alive and in custody."
else
explanation_text = "Free Objective"
@@ -485,7 +435,7 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/escape/escape_with_identity/update_explanation_text()
if(target?.current)
target_real_name = target.current.real_name
- explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role]"
+ explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role.title]"
var/mob/living/carbon/human/H
if(ishuman(target.current))
H = target.current
@@ -597,7 +547,7 @@ GLOBAL_LIST_EMPTY(possible_items)
if(!is_unique_objective(possible_item.targetitem,dupe_search_range))
continue
for(var/datum/mind/M in owners)
- if(M.current.mind.assigned_role in possible_item.excludefromjob)
+ if(M.current.mind.assigned_role.title in possible_item.excludefromjob)
continue check_items
approved_targets += possible_item
if (length(approved_targets))
diff --git a/code/game/machinery/computer/arena.dm b/code/game/machinery/computer/arena.dm
index d264cd24418..931d31867c6 100644
--- a/code/game/machinery/computer/arena.dm
+++ b/code/game/machinery/computer/arena.dm
@@ -188,7 +188,7 @@
if(!isobserver(oldbody))
return
var/mob/living/carbon/human/M = new/mob/living/carbon/human(get_turf(spawnpoint))
- oldbody.client.prefs.copy_to(M)
+ oldbody.client.prefs.safe_transfer_prefs_to(M, is_antag = TRUE)
M.set_species(/datum/species/human) // Could use setting per team
M.equipOutfit(outfits[team] ? outfits[team] : default_outfit)
M.faction += team //In case anyone wants to add team based stuff to arena special effects
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index b43863be3f3..ce035532d66 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -226,9 +226,9 @@ GLOBAL_LIST_EMPTY(cryopod_computers)
crew_member["name"] = mob_occupant.real_name
- if(mob_occupant.mind && mob_occupant.mind.assigned_role)
+ if(mob_occupant.mind)
// Handle job slot/tater cleanup.
- var/job = mob_occupant.mind.assigned_role
+ var/job = mob_occupant.mind.assigned_role.title
crew_member["job"] = job
SSjob.FreeRole(job)
if(LAZYLEN(mob_occupant.mind.objectives))
@@ -300,12 +300,9 @@ GLOBAL_LIST_EMPTY(cryopod_computers)
return
if(target == user)
- var/datum/antagonist/antag = target.mind.has_antag_datum(/datum/antagonist)
-
- var/datum/job/target_job = SSjob.GetJob(target.mind.assigned_role)
-
- if(target_job && target_job.req_admin_notify)
+ if(target.mind.assigned_role.req_admin_notify)
tgui_alert(target, "You're an important role! [AHELP_FIRST_MESSAGE]")
+ var/datum/antagonist/antag = target.mind.has_antag_datum(/datum/antagonist)
if(antag)
tgui_alert(target, "You're \a [antag.name]! [AHELP_FIRST_MESSAGE]")
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index d6d76e8e943..2f01db6189d 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -136,7 +136,7 @@
short_desc = "You are a spider."
important_info = "Follow your directives at all costs."
faction = list("spiders")
- assignedrole = "Spider"
+ spawner_job_path = /datum/job/spider
banType = ROLE_ALIEN
ready = FALSE
radial_based = TRUE
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 30f255c0702..335adef94b9 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -761,7 +761,7 @@
var/intern_threshold = (CONFIG_GET(number/use_low_living_hour_intern_hours) * 60) || (CONFIG_GET(number/use_exp_restrictions_heads_hours) * 60) || INTERN_THRESHOLD_FALLBACK_HOURS * 60
var/playtime = user.client.get_exp_living(pure_numeric = TRUE)
- if((intern_threshold >= playtime) && (user.mind?.assigned_role in SSjob.station_jobs))
+ if((intern_threshold >= playtime) && (user.mind?.assigned_role.title in SSjob.station_jobs))
is_intern = TRUE
update_label()
return
diff --git a/code/game/objects/items/mail.dm b/code/game/objects/items/mail.dm
index 63e972468f6..ac877c6d3a2 100644
--- a/code/game/objects/items/mail.dm
+++ b/code/game/objects/items/mail.dm
@@ -139,13 +139,13 @@
/// Accepts a mind to initialize goodies for a piece of mail.
/obj/item/mail/proc/initialize_for_recipient(datum/mind/recipient)
- name = "[initial(name)] for [recipient.name] ([recipient.assigned_role])"
+ name = "[initial(name)] for [recipient.name] ([recipient.assigned_role.title])"
recipient_ref = WEAKREF(recipient)
var/mob/living/body = recipient.current
var/list/goodies = generic_goodies
- var/datum/job/this_job = SSjob.name_occupations[recipient.assigned_role]
+ var/datum/job/this_job = recipient.assigned_role
if(this_job)
if(this_job.paycheck_department && department_colors[this_job.paycheck_department])
color = department_colors[this_job.paycheck_department]
@@ -222,7 +222,7 @@
if(human.stat == DEAD || !human.mind)
continue
// Skip wizards, nuke ops, cyborgs; Centcom does not send them mail
- if(!SSjob.GetJob(human.mind.assigned_role) || (human.mind.assigned_role in GLOB.nonhuman_positions))
+ if(!(human.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
continue
mail_recipients += human.mind
diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm
index e10da7998ee..a7b2699ad5c 100644
--- a/code/game/objects/items/religion.dm
+++ b/code/game/objects/items/religion.dm
@@ -45,7 +45,7 @@
if(H.stat == DEAD || H == user)
continue
if(H.mind && (has_job_loyalties || has_role_loyalties))
- if(has_job_loyalties && (H.mind.assigned_role in job_loyalties))
+ if(has_job_loyalties && (H.mind.assigned_role.title in job_loyalties))
inspired += H
else if(has_role_loyalties && (H.mind.special_role in role_loyalties))
inspired += H
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index a210e2d3086..4ba45a62d72 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -301,6 +301,7 @@
O.updatename(B.client)
B.mind.transfer_to(O)
+ playsound(O.loc, 'sound/voice/liveagain.ogg', 75, TRUE)
if(O.mind && O.mind.special_role)
O.mind.store_memory("As a cyborg, you must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.")
diff --git a/code/game/objects/structures/fugitive_role_spawners.dm b/code/game/objects/structures/fugitive_role_spawners.dm
index afdff7afec9..18b24584094 100644
--- a/code/game/objects/structures/fugitive_role_spawners.dm
+++ b/code/game/objects/structures/fugitive_role_spawners.dm
@@ -1,7 +1,7 @@
/obj/effect/mob_spawn/human/fugitive
- assignedrole = "Fugitive Hunter"
+ spawner_job_path = /datum/job/fugitive_hunter
flavour_text = "" //the flavor text will be the backstory argument called on the antagonist's greet, see hunter.dm for details
roundstart = FALSE
death = FALSE
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 9c57a48d29c..d8f9f3fae07 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -17,7 +17,7 @@
Your goal is to protect the vault you are assigned to, cultivate the seeds passed onto you, \
and eventually bring life to this desolate planet while waiting for contact from your creators. \
Estimated time of last contact: Deployment, 5000 millennia ago."
- assignedrole = "Lifebringer"
+ spawner_job_path = /datum/job/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", \
@@ -89,7 +89,7 @@
flavour_text = "The wastes are sacred ground, its monsters a blessed bounty. \
You have seen lights in the distance... they foreshadow the arrival of outsiders that seek to tear apart the Necropolis and its domain. \
Fresh sacrifices for your nest."
- assignedrole = "Ash Walker"
+ spawner_job_path = /datum/job/ash_walker
var/datum/team/ashwalkers/team
var/obj/structure/ash_walker_eggshell/eggshell
@@ -149,7 +149,7 @@
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"
+ spawner_job_path = /datum/job/exile
/obj/effect/mob_spawn/human/exile/Destroy()
new/obj/structure/fluff/empty_sleeper(get_turf(src))
@@ -230,9 +230,9 @@
else
H.fully_replace_character_name(null, name)
if(has_owner)
- new_spawn.mind.assigned_role = "Servant Golem"
+ new_spawn.mind.set_assigned_role(SSjob.GetJobType(/datum/job/servant_golem))
else
- new_spawn.mind.assigned_role = "Free Golem"
+ new_spawn.mind.set_assigned_role(SSjob.GetJobType(/datum/job/free_golem))
/obj/effect/mob_spawn/human/golem/attack_hand(mob/user, list/modifiers)
. = ..()
@@ -283,7 +283,7 @@
flavour_text = "Each day you barely scrape by, and between the terrible conditions of your makeshift shelter, \
the hostile creatures, and the ash drakes swooping down from the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... "
- assignedrole = "Hermit"
+ spawner_job_path = /datum/job/hermit
/obj/effect/mob_spawn/human/hermit/Initialize(mapload)
. = ..()
@@ -329,7 +329,7 @@
mob_species = /datum/species/human
short_desc = "You've been hunting polar bears for 40 years now! What do these 'NaniteTrans' newcomers want?"
flavour_text = "You were fine hunting polar bears and taming wolves out here on your own, but now that there are corporate stooges around, you need to watch your step."
- assignedrole = "Hermit"
+ spawner_job_path = /datum/job/hermit
/datum/outfit/hermit
name = "Lavaland hermit"
@@ -353,7 +353,7 @@
short_desc = "You're a prisoner, sentenced to hard work in one of Nanotrasen's labor camps, but it seems as \
though fate has other plans for you."
flavour_text = "Good. It seems as though your ship crashed. You remember that you were convicted of "
- assignedrole = "Escaped Prisoner"
+ spawner_job_path = /datum/job/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)
@@ -392,7 +392,7 @@
short_desc = "You are a staff member of a top-of-the-line space hotel!"
flavour_text = "You are a staff member of a top-of-the-line space hotel! Cater to guests, advertise the hotel, and make sure the manager doesn't fire you."
important_info = "Do NOT leave the hotel, as that is grounds for contract termination."
- assignedrole = "Hotel Staff"
+ spawner_job_path = /datum/job/hotel_staff
/datum/outfit/hotelstaff
name = "Hotel Staff"
@@ -432,7 +432,7 @@
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
+ spawner_job_path = /datum/job/space_syndicate
/datum/outfit/syndicate_empty
name = "Syndicate Operative Empty"
@@ -519,7 +519,7 @@
id = /obj/item/card/id/away/old/sec
r_pocket = /obj/item/restraints/handcuffs
l_pocket = /obj/item/assembly/flash/handheld
- assignedrole = "Ancient Crew"
+ spawner_job_path = /datum/job/ancient_crew
/obj/effect/mob_spawn/human/oldsec/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
@@ -545,7 +545,7 @@
id = /obj/item/card/id/away/old/eng
gloves = /obj/item/clothing/gloves/color/fyellow/old
l_pocket = /obj/item/tank/internals/emergency_oxygen
- assignedrole = "Ancient Crew"
+ spawner_job_path = /datum/job/ancient_crew
/obj/effect/mob_spawn/human/oldeng/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
@@ -570,7 +570,7 @@
shoes = /obj/item/clothing/shoes/laceup
id = /obj/item/card/id/away/old/sci
l_pocket = /obj/item/stack/medical/bruise_pack
- assignedrole = "Ancient Crew"
+ spawner_job_path = /datum/job/ancient_crew
/obj/effect/mob_spawn/human/oldsci/Destroy()
new/obj/structure/showcase/machinery/oldpod/used(drop_location())
@@ -593,7 +593,7 @@
show_flavour = FALSE //Flavour only exists for spawners menu
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"
+ spawner_job_path = /datum/job/space_pirate
///Rank of the pirate on the ship, it's used in generating pirate names!
var/rank = "Deserter"
///Whether or not it will spawn a fluff structure upon opening.
@@ -683,14 +683,15 @@
flavour_text = "Your ship docks after a long time somewhere in hostile space, reporting a malfunction. You are stuck here, with Nanotrasen station nearby. Fix the ship, find a way to power it and follow your captain's orders."
important_info = "Obey orders given by your captain. DO NOT let the ship fall into enemy hands."
outfit = /datum/outfit/syndicatespace/syndicrew
- assignedrole = ROLE_SYNDICATE_CYBERSUN
+ spawner_job_path = /datum/job/syndicate_cybersun
/datum/outfit/syndicatespace/syndicrew/post_equip(mob/living/carbon/human/H)
H.faction |= ROLE_SYNDICATE
/obj/effect/mob_spawn/human/syndicatespace/special(mob/living/new_spawn)
new_spawn.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND)
- var/policy = get_policy(assignedrole)
+ var/datum/job/spawn_job = SSjob.GetJobType(spawner_job_path)
+ var/policy = get_policy(spawn_job.policy_index)
if(policy)
to_chat(new_spawn, span_bold("[policy]"))
@@ -700,7 +701,7 @@
flavour_text = "Your ship docks after a long time somewhere in hostile space, reporting a malfunction. You are stuck here, with Nanotrasen station nearby. Command your crew and turn your ship into the most protected fortress."
important_info = "Protect the ship and secret documents in your backpack with your own life."
outfit = /datum/outfit/syndicatespace/syndicaptain
- assignedrole = ROLE_SYNDICATE_CYBERSUN_CAPTAIN
+ spawner_job_path = /datum/job/syndicate_cybersun_captain
/datum/outfit/syndicatespace/syndicaptain/post_equip(mob/living/carbon/human/H)
H.faction |= ROLE_SYNDICATE
@@ -749,7 +750,7 @@
icon_state = "sleeper"
short_desc = "You're, like, totally a dudebro, bruh."
flavour_text = "Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?"
- assignedrole = "Beach Bum"
+ spawner_job_path = /datum/job/beach_bum
/obj/effect/mob_spawn/human/beach/alive/lifeguard
short_desc = "You're a spunky lifeguard!"
@@ -785,7 +786,7 @@
icon_state = "sleeper"
short_desc = "You are a space bartender!"
flavour_text = "Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect."
- assignedrole = "Space Bartender"
+ spawner_job_path = /datum/job/space_bartender
/datum/outfit/spacebartender
name = "Space Bartender"
@@ -811,7 +812,7 @@
icon_state = "remains"
short_desc = "By unknown powers, your skeletal remains have been reanimated!"
flavour_text = "Walk this mortal plane and terrorize all living adventurers who dare cross your path."
- assignedrole = "Skeleton"
+ spawner_job_path = /datum/job/skeleton
/obj/effect/mob_spawn/human/skeleton/alive/special(mob/living/new_spawn)
to_chat(new_spawn, "You have this horrible lurching feeling deep down that your binding to this world will fail if you abandon this zone... Were you reanimated to protect something?")
@@ -842,7 +843,7 @@
permanent = TRUE
uses = -1
outfit = /datum/outfit/spacebartender
- assignedrole = "Space Bar Patron"
+ spawner_job_path = /datum/job/space_bar_patron
/obj/effect/mob_spawn/human/alive/space_bar_patron/attack_hand(mob/user, list/modifiers)
var/despawn = tgui_alert(usr, "Return to cryosleep? (Warning, Your mob will be deleted!)", null, list("Yes", "No"))
@@ -886,7 +887,7 @@
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
short_desc = "You are a space doctor!"
- assignedrole = "Space Doctor"
+ spawner_job_path = /datum/job/space_doctor
/obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/doctor)
. = ..()
diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm
index 671ce261e0e..988203a7eeb 100644
--- a/code/game/objects/structures/life_candle.dm
+++ b/code/game/objects/structures/life_candle.dm
@@ -85,8 +85,7 @@
if(!body)
body = new mob_type(T)
var/mob/ghostie = mind.get_ghost(TRUE)
- if(ghostie.client && ghostie.client.prefs)
- ghostie.client.prefs.copy_to(body)
+ ghostie.client?.prefs?.safe_transfer_prefs_to(body)
mind.transfer_to(body)
else
body.forceMove(T)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 1138cba4f4e..b58fdb32df9 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -827,8 +827,7 @@
dat += "
"
- for(var/j in SSjob.occupations)
- var/datum/job/job = j
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
count++
var/J_title = html_encode(job.title)
var/J_opPos = html_encode(job.total_positions - (job.total_positions - job.current_positions))
diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm
index bf2109b16c3..4955e126361 100644
--- a/code/modules/admin/antag_panel.dm
+++ b/code/modules/admin/antag_panel.dm
@@ -99,7 +99,7 @@ GLOBAL_VAR(antag_prototypes)
var/out = "[name][(current && (current.real_name!=name))?" (as [current.real_name])":""]
"
out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
"
- out += "Assigned role: [assigned_role]. Edit
"
+ out += "Assigned role: [assigned_role.title]. Edit
"
out += "Faction and special role: [special_role]
"
var/special_statuses = get_special_statuses()
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 17088f47a49..ebb7603025b 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -916,7 +916,7 @@
//Job + antagonist
if(M.mind)
- special_role_description = "Role: [M.mind.assigned_role]; Antagonist: "
+ special_role_description = "Role: [M.mind.assigned_role.title]; Antagonist: "
var/i = 0
for(var/datum/antagonist/A in M.mind.antag_datums)
special_role_description += "[A.name]"
@@ -966,7 +966,7 @@
var/Add = href_list["addjobslot"]
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
if(job.title == Add)
job.total_positions += 1
break
@@ -980,7 +980,7 @@
var/Add = href_list["customjobslot"]
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
if(job.title == Add)
var/newtime = null
newtime = input(usr, "How many jebs do you want?", "Add wanted posters", "[newtime]") as num|null
@@ -998,7 +998,7 @@
var/Remove = href_list["removejobslot"]
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
if(job.title == Remove && job.total_positions - job.current_positions > 0)
job.total_positions -= 1
break
@@ -1011,7 +1011,7 @@
var/Unlimit = href_list["unlimitjobslot"]
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
if(job.title == Unlimit)
job.total_positions = -1
break
@@ -1024,7 +1024,7 @@
var/Limit = href_list["limitjobslot"]
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
if(job.title == Limit)
job.total_positions = job.current_positions
break
diff --git a/code/modules/admin/verbs/ai_triumvirate.dm b/code/modules/admin/verbs/ai_triumvirate.dm
index b7beadd4d8d..1cc7c01a8a0 100644
--- a/code/modules/admin/verbs/ai_triumvirate.dm
+++ b/code/modules/admin/verbs/ai_triumvirate.dm
@@ -16,7 +16,7 @@ GLOBAL_DATUM(triple_ai_controller, /datum/triple_ai_controller)
/datum/triple_ai_controller/proc/on_occupations_divided(datum/source)
SIGNAL_HANDLER
- for(var/datum/job/ai/ai_datum in SSjob.occupations)
+ for(var/datum/job/ai/ai_datum in SSjob.joinable_occupations)
ai_datum.spawn_positions = 3
for(var/obj/effect/landmark/start/ai/secondary/secondary_ai_spawn in GLOB.start_landmarks_list)
secondary_ai_spawn.latejoin_active = TRUE
@@ -35,10 +35,10 @@ GLOBAL_DATUM(triple_ai_controller, /datum/triple_ai_controller)
to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.", confidential = TRUE)
return
- var/datum/job/job = SSjob.GetJob("AI")
+ var/datum/job/job = SSjob.GetJobType(/datum/job/ai)
if(!job)
to_chat(usr, "Unable to locate the AI job", confidential = TRUE)
- return
+ CRASH("triple_ai() called, no /datum/job/ai to be found.")
if(!GLOB.triple_ai_controller)
GLOB.triple_ai_controller = new()
diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm
index fdbf33dd777..81c22cfd73a 100644
--- a/code/modules/admin/verbs/anonymousnames.dm
+++ b/code/modules/admin/verbs/anonymousnames.dm
@@ -88,7 +88,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
/datum/anonymous_theme/proc/anonymous_all_players()
var/datum/anonymous_theme/theme = GLOB.current_anonymous_theme
for(var/mob/living/player in GLOB.player_list)
- if(!player.mind || (!ishuman(player) && !issilicon(player)) || !SSjob.GetJob(player.mind.assigned_role))
+ if(!player.mind || (!ishuman(player) && !issilicon(player)) || player.mind.assigned_role.faction != FACTION_STATION)
continue
if(issilicon(player))
player.fully_replace_character_name(player.real_name, theme.anonymous_ai_name(isAI(player)))
@@ -109,13 +109,14 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
/datum/anonymous_theme/proc/restore_all_players()
priority_announce("Names and Identities have been restored.", "Identity Restoration", SSstation.announcer.get_rand_alert_sound())
for(var/mob/living/player in GLOB.player_list)
- if(!player.mind || (!ishuman(player) && !issilicon(player)) || !SSjob.GetJob(player.mind.assigned_role))
+ if(!player.mind || (!ishuman(player) && !issilicon(player)) || player.mind.assigned_role.faction != FACTION_STATION)
continue
var/old_name = player.real_name //before restoration
if(issilicon(player))
player.apply_pref_name("[isAI(player) ? "ai" : "cyborg"]", player.client)
else
- player.client.prefs.copy_to(player, antagonist = (LAZYLEN(player.mind.antag_datums) > 0), is_latejoiner = FALSE)
+ player.client.prefs.sanitize_chosen_prefs() // Just in case they changed unlawfully.
+ player.client.prefs.apply_prefs_to(player) // This is not sound logic, as the prefs may have changed since then.
player.fully_replace_character_name(old_name, player.real_name) //this changes IDs and PDAs and whatnot
/**
@@ -153,7 +154,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
priority_announce("As punishment for this station's poor productivity when compared to neighbor stations, names and identities will be restricted until further notice.", "Finance Report", SSstation.announcer.get_rand_alert_sound())
/datum/anonymous_theme/employees/anonymous_name(mob/target)
- var/is_head_of_staff = (target.mind.assigned_role in GLOB.command_positions)
+ var/is_head_of_staff = target.mind.assigned_role.departments & DEPARTMENT_COMMAND
var/name = "[is_head_of_staff ? "Manager" : "Employee"] "
for(var/i in 1 to 6)
if(prob(30) || i == 1)
diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm
index 591ef53d4ef..7fe396a59f6 100644
--- a/code/modules/admin/verbs/ert.dm
+++ b/code/modules/admin/verbs/ert.dm
@@ -120,7 +120,7 @@
if(isobserver(usr))
var/mob/living/carbon/human/admin_officer = new (spawnpoints[1])
var/chosen_outfit = usr.client?.prefs?.brief_outfit
- usr.client.prefs.copy_to(admin_officer)
+ usr.client.prefs.safe_transfer_prefs_to(admin_officer, is_antag = TRUE)
admin_officer.equipOutfit(chosen_outfit)
admin_officer.key = usr.key
else
@@ -175,7 +175,7 @@
//Spawn the body
var/mob/living/carbon/human/ert_operative = new ertemplate.mobtype(spawnloc)
- chosen_candidate.client.prefs.copy_to(ert_operative)
+ chosen_candidate.client.prefs.safe_transfer_prefs_to(ert_operative, is_antag = TRUE)
ert_operative.key = chosen_candidate.key
if(ertemplate.enforce_human || !(ert_operative.dna.species.changesource_flags & ERT_SPAWN)) // Don't want any exploding plasmemes
@@ -194,7 +194,7 @@
ert_antag.random_names = ertemplate.random_names
ert_operative.mind.add_antag_datum(ert_antag,ert_team)
- ert_operative.mind.assigned_role = ert_antag.name
+ ert_operative.mind.set_assigned_role(SSjob.GetJobType(ert_antag.ert_job_path))
//Logging and cleanup
log_game("[key_name(ert_operative)] has been selected as an [ert_antag.name]")
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 16511eac815..30b3dab47c1 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -305,7 +305,7 @@ GLOBAL_VAR_INIT(say_disabled, FALSE)
for(var/obj/item/I in D)
qdel(I)
randomize_human(D)
- JB.equip(D, TRUE, FALSE)
+ D.dress_up_as_job(JB, TRUE)
COMPILE_OVERLAYS(D)
var/icon/I = icon(getFlatIcon(D), frame = 1)
final.Insert(I, JB.title)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index faa216b8ef3..e2346562a9e 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -235,54 +235,6 @@
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Mute [feedback_string]", "[P.muted & mute_type]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-//I use this proc for respawn character too. /N
-/proc/create_xeno(ckey)
- if(!ckey)
- var/list/candidates = list()
- 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))
- continue //we don't want to be an alium
- if(M.client.is_afk())
- continue //we are afk
- if(M.mind && M.mind.current && M.mind.current.stat != DEAD)
- continue //we have a live body we are tied to
- candidates += M.ckey
- if(candidates.len)
- ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in sortKey(candidates)
- else
- to_chat(usr, span_danger("Error: create_xeno(): no suitable candidates."), confidential = TRUE)
- if(!istext(ckey))
- return FALSE
-
- var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Praetorian","Hunter","Sentinel","Drone","Larva")
- var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : null
- var/mob/living/carbon/alien/new_xeno
- switch(alien_caste)
- if("Queen")
- new_xeno = new /mob/living/carbon/alien/humanoid/royal/queen(spawn_here)
- if("Praetorian")
- new_xeno = new /mob/living/carbon/alien/humanoid/royal/praetorian(spawn_here)
- if("Hunter")
- new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here)
- if("Sentinel")
- new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here)
- if("Drone")
- new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here)
- if("Larva")
- new_xeno = new /mob/living/carbon/alien/larva(spawn_here)
- else
- return FALSE
- if(!spawn_here)
- SSjob.SendToLateJoin(new_xeno, FALSE)
-
- new_xeno.ckey = ckey
- var/msg = span_notice("[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste].")
- message_admins(msg)
- admin_ticket_log(new_xeno, msg)
- return TRUE
-
/*
If a guy was gibbed and you want to revive him, this is a good way to do so.
Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one.
@@ -310,43 +262,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
return
if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something
- //Check if they were an alien
- if(G_found.mind.assigned_role == ROLE_ALIEN)
- if(tgui_alert(usr,"This character appears to have been an alien. Would you like to respawn them as such?",,list("Yes","No"))=="Yes")
- var/turf/T
- if(GLOB.xeno_spawn.len)
- T = pick(GLOB.xeno_spawn)
-
- var/mob/living/carbon/alien/new_xeno
- switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were.
- if("Hunter")
- new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T)
- if("Sentinel")
- new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T)
- if("Drone")
- new_xeno = new /mob/living/carbon/alien/humanoid/drone(T)
- if("Praetorian")
- new_xeno = new /mob/living/carbon/alien/humanoid/royal/praetorian(T)
- if("Queen")
- new_xeno = new /mob/living/carbon/alien/humanoid/royal/queen(T)
- else//If we don't know what special role they have, for whatever reason, or they're a larva.
- create_xeno(G_found.ckey)
- return
-
- if(!T)
- SSjob.SendToLateJoin(new_xeno, FALSE)
-
- //Now to give them their mind back.
- G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use
- new_xeno.key = G_found.key
- to_chat(new_xeno, "You have been fully respawned. Enjoy the game.", confidential = TRUE)
- var/msg = span_adminnotice("[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno.")
- message_admins(msg)
- admin_ticket_log(new_xeno, msg)
- return //all done. The ghost is auto-deleted
-
//check if they were a monkey
- else if(findtext(G_found.real_name,"monkey"))
+ if(findtext(G_found.real_name,"monkey"))
if(tgui_alert(usr,"This character appears to have been a monkey. Would you like to respawn them as such?",,list("Yes","No"))=="Yes")
var/mob/living/carbon/human/species/monkey/new_monkey = new
SSjob.SendToLateJoin(new_monkey)
@@ -359,7 +276,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
return //all done. The ghost is auto-deleted
- //Ok, it's not a xeno or a monkey. So, spawn a human.
+ //Ok, it's not a monkey. So, spawn a human.
var/mob/living/carbon/human/new_character = new//The mob being spawned.
SSjob.SendToLateJoin(new_character)
@@ -367,7 +284,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something
/*Try and locate a record for the person being respawned through GLOB.data_core.
This isn't an exact science but it does the trick more often than not.*/
- var/id = md5("[G_found.real_name][G_found.mind.assigned_role]")
+ var/id = md5("[G_found.real_name][G_found.mind.assigned_role.title]")
record_found = find_record("id", id, GLOB.data_core.locked)
@@ -377,9 +294,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.age = record_found.fields["age"]
new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], null, record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"])
else
- var/datum/preferences/A = new()
- A.copy_to(new_character)
- A.real_name = G_found.real_name
+ new_character.randomize_human_appearance()
new_character.dna.update_dna_identity()
new_character.name = new_character.real_name
@@ -388,8 +303,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use
else
new_character.mind_initialize()
- if(!new_character.mind.assigned_role)
- new_character.mind.assigned_role = "Assistant"//If they somehow got a null assigned role.
+ if(is_unassigned_job(new_character.mind.assigned_role))
+ new_character.mind.set_assigned_role(SSjob.GetJobType(SSjob.overflow_role))
new_character.key = G_found.key
@@ -406,10 +321,9 @@ Traitors and the like can also be revived with the previous role mostly intact.
//Now for special roles and equipment.
var/datum/antagonist/traitor/traitordatum = new_character.mind.has_antag_datum(/datum/antagonist/traitor)
if(traitordatum)
- SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)
+ SSjob.EquipRank(new_character, new_character.mind.assigned_role, new_character.client)
traitordatum.equip()
-
switch(new_character.mind.special_role)
if(ROLE_WIZARD)
new_character.forceMove(pick(GLOB.wizardstart))
@@ -429,23 +343,23 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.forceMove(pick(ninja_spawn))
else//They may also be a cyborg or AI.
- switch(new_character.mind.assigned_role)
- if("Cyborg")//More rigging to make em' work and check if they're traitor.
+ switch(new_character.mind.assigned_role.type)
+ if(/datum/job/cyborg)//More rigging to make em' work and check if they're traitor.
new_character = new_character.Robotize(TRUE)
- if("AI")
+ if(/datum/job/ai)
new_character = new_character.AIize()
else
- SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them.
+ if(!traitordatum) // Already equipped there.
+ SSjob.EquipRank(new_character, new_character.mind.assigned_role, new_character.client)//Or we simply equip them.
//Announces the character on all the systems, based on the record.
- if(!issilicon(new_character))//If they are not a cyborg/AI.
- if(!record_found&&new_character.mind.assigned_role!=new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. MODE people are not announced anyway.
- //Power to the user!
- if(tgui_alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,list("No","Yes"))=="Yes")
- GLOB.data_core.manifest_inject(new_character)
+ if(!record_found && (new_character.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
+ //Power to the user!
+ if(tgui_alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,list("No","Yes"))=="Yes")
+ GLOB.data_core.manifest_inject(new_character)
- if(tgui_alert(new_character,"Would you like an active AI to announce this character?",,list("No","Yes"))=="Yes")
- AnnounceArrival(new_character, new_character.mind.assigned_role)
+ if(tgui_alert(new_character,"Would you like an active AI to announce this character?",,list("No","Yes"))=="Yes")
+ AnnounceArrival(new_character, new_character.mind.assigned_role.title)
var/msg = span_adminnotice("[admin] has respawned [player_key] as [new_character.real_name].")
message_admins(msg)
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 3bd8235db15..7aa1238c464 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -86,11 +86,9 @@
if("infinite_sec")
if(!is_debugger)
return
- var/datum/job/J = SSjob.GetJob("Security Officer")
- if(!J)
- return
- J.total_positions = -1
- J.spawn_positions = -1
+ var/datum/job/sec_job = SSjob.GetJobType(/datum/job/security_officer)
+ sec_job.total_positions = -1
+ sec_job.spawn_positions = -1
message_admins("[key_name_admin(holder)] has removed the cap on security officers.")
//Buttons for helpful stuff. This is where people land in the tgui
if("clear_virus")
@@ -607,7 +605,7 @@
if (length(players))
var/mob/chosen = players[1]
if (chosen.client)
- chosen.client.prefs.copy_to(spawnedMob)
+ chosen.client.prefs.safe_transfer_prefs_to(spawnedMob, is_antag = TRUE)
spawnedMob.key = chosen.key
players -= chosen
if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit))
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 54fd4e61d9d..9922c969a28 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -108,16 +108,19 @@ GLOBAL_LIST_EMPTY(antagonists)
hud.leave_hud(mob_override)
set_antag_hud(mob_override, null)
-// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
+
+/// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE)
- var/mob/living/carbon/human/H = mob_override
- if(H && istype(H) && owner.assigned_role == "Clown")
- if(removing) // They're a clown becoming an antag, remove clumsy
- H.dna.remove_mutation(CLOWNMUT)
- if(!silent && message)
- to_chat(H, span_boldnotice("[message]"))
- else
- H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
+ if(!ishuman(mob_override) || !is_clown_job(owner.assigned_role))
+ return
+ var/mob/living/carbon/human/human_override = mob_override
+ if(removing) // They're a clown becoming an antag, remove clumsy
+ human_override.dna.remove_mutation(CLOWNMUT)
+ if(!silent && message)
+ to_chat(human_override, span_boldnotice("[message]"))
+ else
+ human_override.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
+
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 92e75040fa2..a530c0befe1 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -72,7 +72,7 @@
/obj/item/antag_spawner/contract/spawn_antag(client/C, turf/T, kind ,datum/mind/user)
new /obj/effect/particle_effect/smoke(T)
var/mob/living/carbon/human/M = new/mob/living/carbon/human(T)
- C.prefs.copy_to(M)
+ C.prefs.safe_transfer_prefs_to(M, is_antag = TRUE)
M.key = C.key
var/datum/mind/app_mind = M.mind
@@ -87,10 +87,8 @@
app.wiz_team = master_wizard.wiz_team
master_wizard.wiz_team.add_member(app_mind)
app_mind.add_antag_datum(app)
- //TODO Kill these if possible
- app_mind.assigned_role = "Apprentice"
- app_mind.special_role = "apprentice"
- //
+ app_mind.set_assigned_role(SSjob.GetJobType(/datum/job/wizard_apprentice))
+ app_mind.special_role = ROLE_WIZARD_APPRENTICE
SEND_SOUND(M, sound('sound/effects/magic.ogg'))
///////////BORGS AND OPERATIVES
@@ -105,7 +103,7 @@
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/borg_to_spawn
- var/special_role_name = "Nuclear Operative" ///The name of the special role given to the recruit
+ var/special_role_name = ROLE_NUCLEAR_OPERATIVE ///The name of the special role given to the recruit
var/datum/outfit/syndicate/outfit = /datum/outfit/syndicate/no_crystals ///The applied outfit
var/datum/antagonist/nukeop/antag_datum = /datum/antagonist/nukeop ///The antag datam applied
/// Style used by the droppod
@@ -147,7 +145,7 @@
/obj/item/antag_spawner/nuke_ops/spawn_antag(client/C, turf/T, kind, datum/mind/user)
var/mob/living/carbon/human/nukie = new()
var/obj/structure/closet/supplypod/pod = setup_pod()
- C.prefs.copy_to(nukie)
+ C.prefs.safe_transfer_prefs_to(nukie, is_antag = TRUE)
nukie.ckey = C.key
var/datum/mind/op_mind = nukie.mind
@@ -165,7 +163,7 @@
/obj/item/antag_spawner/nuke_ops/clown
name = "clown operative beacon"
desc = "A single-use beacon designed to quickly launch reinforcement clown operatives into the field."
- special_role_name = "Clown Operative"
+ special_role_name = ROLE_CLOWN_OPERATIVE
outfit = /datum/outfit/syndicate/clownop/no_crystals
antag_datum = /datum/antagonist/nukeop/clownop
pod_style = STYLE_HONK
@@ -265,8 +263,8 @@
var/obj/effect/dummy/phased_mob/holder = new /obj/effect/dummy/phased_mob(T)
var/mob/living/simple_animal/hostile/imp/slaughter/S = new demon_type(holder)
S.key = C.key
- S.mind.assigned_role = S.name
- S.mind.special_role = S.name
+ S.mind.set_assigned_role(SSjob.GetJobType(/datum/job/slaughter_demon))
+ S.mind.special_role = ROLE_SLAUGHTER_DEMON
S.mind.add_antag_datum(antag_type)
to_chat(S, S.playstyle_string)
to_chat(S, "You are currently not currently in the same plane of existence as the station. \
diff --git a/code/modules/antagonists/abductor/abductee/abductee_objectives.dm b/code/modules/antagonists/abductor/abductee/abductee_objectives.dm
index f1883196448..2ef97101ff1 100644
--- a/code/modules/antagonists/abductor/abductee/abductee_objectives.dm
+++ b/code/modules/antagonists/abductor/abductee/abductee_objectives.dm
@@ -31,12 +31,11 @@
explanation_text = "Capture"
/datum/objective/abductee/capture/New()
- var/list/jobs = SSjob.occupations.Copy()
- for(var/X in jobs)
- var/datum/job/J = X
- if(J.current_positions < 1)
- jobs -= J
- if(jobs.len > 0)
+ var/list/jobs = SSjob.joinable_occupations.Copy()
+ for(var/datum/job/job as anything in jobs)
+ if(job.current_positions < 1)
+ jobs -= job
+ if(length(jobs) > 0)
var/datum/job/target = pick(jobs)
explanation_text += " a [target.title]."
else
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 74d02ea8bf7..2f792e77f40 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -16,6 +16,8 @@ GLOBAL_LIST_INIT(possible_abductor_names, list("Alpha","Beta","Gamma","Delta","E
var/outfit
var/landmark_type
var/greet_text
+ /// Type path for the associated job datum.
+ var/role_job = /datum/job/abductor_agent
/datum/antagonist/abductor/agent
@@ -33,10 +35,12 @@ GLOBAL_LIST_INIT(possible_abductor_names, list("Alpha","Beta","Gamma","Delta","E
landmark_type = /obj/effect/landmark/abductor/scientist
greet_text = "Use your experimental console and surgical equipment to monitor your agent and experiment upon abducted humans."
show_in_antagpanel = TRUE
+ role_job = /datum/job/abductor_scientist
/datum/antagonist/abductor/scientist/onemanteam
name = "Abductor Solo"
outfit = /datum/outfit/abductor/scientist/onemanteam
+ role_job = /datum/job/abductor_solo
/datum/antagonist/abductor/create_team(datum/team/abductor_team/new_team)
if(!new_team)
@@ -49,8 +53,8 @@ GLOBAL_LIST_INIT(possible_abductor_names, list("Alpha","Beta","Gamma","Delta","E
return team
/datum/antagonist/abductor/on_gain()
- owner.special_role = "[name]"
- owner.assigned_role = "[name]"
+ owner.set_assigned_role(SSjob.GetJobType(role_job))
+ owner.special_role = ROLE_ABDUCTOR
objectives += team.objectives
finalize_abductor()
ADD_TRAIT(owner, TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm
index 7fe55e45677..61c16c3a6d1 100644
--- a/code/modules/antagonists/creep/creep.dm
+++ b/code/modules/antagonists/creep/creep.dm
@@ -60,7 +60,7 @@
objectives_left += "heirloom"
// If they have no coworkers, jealousy will pick someone else on the station. This will never be a free objective.
- if(obsessionmind.assigned_role && obsessionmind.assigned_role != "Captain")
+ if(!is_captain_job(obsessionmind.assigned_role))
objectives_left += "jealous"
for(var/i in 1 to 3)
@@ -140,7 +140,7 @@
/datum/objective/assassinate/obsessed/update_explanation_text()
..()
if(target?.current)
- explanation_text = "Murder [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
+ explanation_text = "Murder [target.name], the [!target_role_type ? target.assigned_role.title : target.special_role]."
else
message_admins("WARNING! [ADMIN_LOOKUPFLW(owner)] obsessed objectives forged without an obsession!")
explanation_text = "Free Objective"
@@ -156,55 +156,30 @@
else
explanation_text = "Free Objective"
+
/datum/objective/assassinate/jealous/proc/find_coworker(datum/mind/oldmind)//returning null = free objective
- if(!oldmind.assigned_role)
+ if(is_unassigned_job(oldmind.assigned_role))
return
var/list/viable_coworkers = list()
var/list/all_coworkers = list()
- var/chosen_department
- var/their_chosen_department
- //note that command and sillycone are gone because borgs can't be obsessions and the heads have their respective department. Sorry cap, your place is more with centcom or something
- if(oldmind.assigned_role in GLOB.security_positions)
- chosen_department = "security"
- if(oldmind.assigned_role in GLOB.engineering_positions)
- chosen_department = "engineering"
- if(oldmind.assigned_role in GLOB.medical_positions)
- chosen_department = "medical"
- if(oldmind.assigned_role in GLOB.science_positions)
- chosen_department = "science"
- if(oldmind.assigned_role in GLOB.supply_positions)
- chosen_department = "supply"
- if(oldmind.assigned_role in GLOB.service_positions)
- chosen_department = "service"
- for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
- if(!H.mind)
+ var/our_departments = oldmind.assigned_role.departments
+ for(var/mob/living/carbon/human/human_alive in GLOB.alive_mob_list)
+ if(!human_alive.mind)
continue
- if(!SSjob.GetJob(H.mind.assigned_role) || H == oldmind.current || H.mind.has_antag_datum(/datum/antagonist/obsessed))
+ if(human_alive == oldmind.current || human_alive.mind.assigned_role.faction != FACTION_STATION || human_alive.mind.has_antag_datum(/datum/antagonist/obsessed))
continue //the jealousy target has to have a job, and not be the obsession or obsessed.
- all_coworkers += H.mind
- //this won't be called often thankfully.
- if(H.mind.assigned_role in GLOB.security_positions)
- their_chosen_department = "security"
- if(H.mind.assigned_role in GLOB.engineering_positions)
- their_chosen_department = "engineering"
- if(H.mind.assigned_role in GLOB.medical_positions)
- their_chosen_department = "medical"
- if(H.mind.assigned_role in GLOB.science_positions)
- their_chosen_department = "science"
- if(H.mind.assigned_role in GLOB.supply_positions)
- their_chosen_department = "supply"
- if(H.mind.assigned_role in GLOB.service_positions)
- their_chosen_department = "service"
- if(their_chosen_department != chosen_department)
+ all_coworkers += human_alive.mind
+ if(!(our_departments & human_alive.mind.assigned_role.departments))
continue
- viable_coworkers += H.mind
+ viable_coworkers += human_alive.mind
- if(viable_coworkers.len > 0)//find someone in the same department
+ if(length(viable_coworkers))//find someone in the same department
target = pick(viable_coworkers)
- else if(all_coworkers.len > 0)//find someone who works on the station
+ else if(length(all_coworkers))//find someone who works on the station
target = pick(all_coworkers)
return oldmind
+
/datum/objective/spendtime //spend some time around someone, handled by the obsessed trauma since that ticks
name = "spendtime"
var/timer = 1800 //5 minutes
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index 00ade2c08f1..a9e92c5f684 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -288,7 +288,7 @@
human.apply_overlay(HALO_LAYER)
/datum/team/cult/proc/make_image(datum/objective/sacrifice/sac_objective)
- var/datum/job/job_of_sacrifice = SSjob.GetJob(sac_objective.target.assigned_role)
+ var/datum/job/job_of_sacrifice = sac_objective.target.assigned_role
var/datum/preferences/prefs_of_sacrifice = sac_objective.target.current.client.prefs
var/icon/reshape = get_flat_human_icon(null, job_of_sacrifice, prefs_of_sacrifice, list(SOUTH))
reshape.Shift(SOUTH, 4)
@@ -341,7 +341,7 @@
/datum/objective/sacrifice/update_explanation_text()
if(target)
- explanation_text = "Sacrifice [target], the [target.assigned_role] via invoking an Offer rune with [target.p_them()] on it and three acolytes around it."
+ explanation_text = "Sacrifice [target], the [target.assigned_role.title] via invoking an Offer rune with [target.p_them()] on it and three acolytes around it."
else
explanation_text = "The veil has already been weakened here, proceed to the final objective."
diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm
index e1803ee246b..145ccd48610 100644
--- a/code/modules/antagonists/disease/disease_datum.dm
+++ b/code/modules/antagonists/disease/disease_datum.dm
@@ -6,8 +6,8 @@
var/disease_name = ""
/datum/antagonist/disease/on_gain()
- owner.special_role = "Sentient Disease"
- owner.assigned_role = "Sentient Disease"
+ owner.set_assigned_role(SSjob.GetJobType(/datum/job/sentient_disease))
+ owner.special_role = ROLE_SENTIENT_DISEASE
var/datum/objective/O = new /datum/objective/disease_infect()
O.owner = owner
objectives += O
diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
index 9b12dc7cdd7..587f7e6557f 100644
--- a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
+++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm
@@ -289,7 +289,7 @@
break
if(!targeted)
break
- targets["[targeted.current.real_name] the [targeted.assigned_role][is_teammate ? " (ally)" : ""]"] = targeted.current
+ targets["[targeted.current.real_name] the [targeted.assigned_role.title][is_teammate ? " (ally)" : ""]"] = targeted.current
heart.target = targets[input(user,"Choose your next target","Target") in targets]
qdel(temp_objective)
if(heart.target)
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 89f5defeae3..8ca4556d093 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -5,6 +5,11 @@
/datum/antagonist/ert
name = "Emergency Response Officer"
+ can_elimination_hijack = ELIMINATION_PREVENT
+ show_in_antagpanel = FALSE
+ show_to_ghosts = TRUE
+ antag_moodlet = /datum/mood_event/focused
+ suicide_cry = "FOR NANOTRASEN!!"
var/datum/team/ert/ert_team
var/leader = FALSE
var/datum/outfit/outfit = /datum/outfit/centcom/ert/security
@@ -15,11 +20,9 @@
var/rip_and_tear = FALSE
var/equip_ert = TRUE
var/forge_objectives_for_ert = TRUE
- can_elimination_hijack = ELIMINATION_PREVENT
- show_in_antagpanel = FALSE
- show_to_ghosts = TRUE
- antag_moodlet = /datum/mood_event/focused
- suicide_cry = "FOR NANOTRASEN!!"
+ /// Typepath indicating the kind of job datum this ert member will have.
+ var/ert_job_path = /datum/job/ert_generic
+
/datum/antagonist/ert/on_gain()
if(random_names)
diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm
index 2f7d4770e9c..76af56865b8 100644
--- a/code/modules/antagonists/gang/gang.dm
+++ b/code/modules/antagonists/gang/gang.dm
@@ -267,15 +267,15 @@
free_clothes = list(/obj/item/clothing/head/beanie/purple,
/obj/item/clothing/under/color/lightpurple,
/obj/item/toy/crayon/spraycan)
- var/list/cop_roles = list("Security Officer", "Warden", "Detective", "Head of Security")
gang_objective = "We're looking to make a deal with the security pigs on this station after the shift. We scratch their back, they scratch ours. You feel me? Keep all of security safe from any trouble, and make sure they get out alive."
antag_hud_name = "Ballas"
/datum/antagonist/gang/purple/check_gang_objective()
- for(var/mob/M in GLOB.player_list)
- if(M.mind.assigned_role in cop_roles)
- if(!considered_alive(M) && !M.suiciding)
- return FALSE
+ for(var/mob/player as anything in GLOB.player_list)
+ if(!(player.mind.assigned_role.departments & DEPARTMENT_SECURITY))
+ continue
+ if(!player.suiciding && !considered_alive(player))
+ return FALSE
return TRUE
/datum/antagonist/gang/green
@@ -361,10 +361,11 @@
if(istype(M.current.loc, /obj/structure/closet/crate/coffin))
continue
return FALSE
- for(var/mob/M in GLOB.player_list)
- if(M.mind.assigned_role == "Chaplain")
- if(!considered_alive(M) && !M.suiciding)
- return FALSE
+ for(var/mob/player as anything in GLOB.player_list)
+ if(!is_chaplain_job(player.mind.assigned_role))
+ continue
+ if(!player.suiciding && !considered_alive(player))
+ return FALSE
return TRUE
/datum/antagonist/gang/tunnel_snakes
@@ -449,10 +450,11 @@
antag_hud_name = "Monarch"
/datum/antagonist/gang/henchmen/check_gang_objective() // gotta arch dr. venture indirectly
- for(var/mob/M in GLOB.player_list)
- if(M.mind.assigned_role == "Research Director")
- if(considered_alive(M))
- return FALSE
+ for(var/mob/player as anything in GLOB.player_list)
+ if(!is_research_director_job(player.mind.assigned_role))
+ continue
+ if(!player.suiciding && considered_alive(player))
+ return FALSE
return TRUE
/datum/antagonist/gang/yakuza
diff --git a/code/modules/antagonists/gang/handler.dm b/code/modules/antagonists/gang/handler.dm
index 1a374b3a859..4421ff68ecd 100644
--- a/code/modules/antagonists/gang/handler.dm
+++ b/code/modules/antagonists/gang/handler.dm
@@ -480,14 +480,14 @@ GLOBAL_VAR_INIT(deaths_during_shift, 0)
//Spawn the body
var/mob/living/carbon/human/cop = new(spawnloc)
- chosen_candidate.client.prefs.copy_to(cop)
+ chosen_candidate.client.prefs.safe_transfer_prefs_to(cop, is_antag = TRUE)
cop.key = chosen_candidate.key
//Give antag datum
var/datum/antagonist/ert/families/ert_antag = new cops_to_send
cop.mind.add_antag_datum(ert_antag)
- cop.mind.assigned_role = ert_antag.name
+ cop.mind.set_assigned_role(SSjob.GetJobType(ert_antag.ert_job_path))
SSjob.SendToLateJoin(cop)
//Logging and cleanup
diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm
index ca498f952d3..bbb1cbfaecb 100644
--- a/code/modules/antagonists/morph/morph.dm
+++ b/code/modules/antagonists/morph/morph.dm
@@ -226,8 +226,8 @@
return MAP_ERROR
var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(GLOB.xeno_spawn))
player_mind.transfer_to(S)
- player_mind.assigned_role = "Morph"
- player_mind.special_role = "Morph"
+ player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/morph))
+ player_mind.special_role = ROLE_MORPH
player_mind.add_antag_datum(/datum/antagonist/morph)
SEND_SOUND(S, sound('sound/magic/mutate.ogg'))
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a morph by an event.")
diff --git a/code/modules/antagonists/nukeop/clownop.dm b/code/modules/antagonists/nukeop/clownop.dm
index f97e091afe9..32beaa42f7d 100644
--- a/code/modules/antagonists/nukeop/clownop.dm
+++ b/code/modules/antagonists/nukeop/clownop.dm
@@ -7,7 +7,7 @@
suicide_cry = "HAPPY BIRTHDAY!!"
/datum/antagonist/nukeop/clownop/admin_add(datum/mind/new_owner,mob/admin)
- new_owner.assigned_role = "Clown Operative"
+ new_owner.set_assigned_role(SSjob.GetJobType(/datum/job/clown_operative))
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has clown op'ed [key_name_admin(new_owner)].")
log_admin("[key_name(admin)] has clown op'ed [key_name(new_owner)].")
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 008e1f172e9..45129657e5b 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -1,5 +1,5 @@
/datum/antagonist/nukeop
- name = "Nuclear Operative"
+ name = ROLE_NUCLEAR_OPERATIVE
roundend_category = "syndicate operatives" //just in case
antagpanel_category = "NukeOp"
job_rank = ROLE_OPERATIVE
@@ -123,7 +123,7 @@
nuke_team = new_team
/datum/antagonist/nukeop/admin_add(datum/mind/new_owner,mob/admin)
- new_owner.assigned_role = ROLE_SYNDICATE
+ new_owner.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_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/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index b5f7d94c943..c2afbe08cf6 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -107,7 +107,7 @@
to_chat(src, "Be sure to read the wiki page to learn more.")
if(!generated_objectives_and_spells)
generated_objectives_and_spells = TRUE
- mind.assigned_role = ROLE_REVENANT
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/revenant))
mind.special_role = ROLE_REVENANT
SEND_SOUND(src, sound('sound/effects/ghost.ogg'))
mind.add_antag_datum(/datum/antagonist/revenant)
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index 8cfdd76d19e..88308173ac6 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -22,7 +22,7 @@
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
. = ..()
if(.)
- if(new_owner.assigned_role in GLOB.command_positions)
+ if(new_owner.assigned_role.departments & DEPARTMENT_COMMAND)
return FALSE
if(new_owner.unconvertable)
return FALSE
@@ -417,7 +417,7 @@
if (isnull(mind))
continue
- if (!(mind.assigned_role in GLOB.command_positions + GLOB.security_positions))
+ if (!(mind.assigned_role.departments & (DEPARTMENT_SECURITY|DEPARTMENT_COMMAND)))
continue
if (mind in ex_revs + ex_headrevs)
diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm
index d784ee96fbf..5887b364318 100644
--- a/code/modules/antagonists/slaughter/slaughterevent.dm
+++ b/code/modules/antagonists/slaughter/slaughterevent.dm
@@ -35,8 +35,8 @@
var/obj/effect/dummy/phased_mob/holder = new /obj/effect/dummy/phased_mob((pick(spawn_locs)))
var/mob/living/simple_animal/hostile/imp/slaughter/S = new (holder)
player_mind.transfer_to(S)
- player_mind.assigned_role = "Slaughter Demon"
- player_mind.special_role = "Slaughter Demon"
+ player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/slaughter_demon))
+ player_mind.special_role = ROLE_SLAUGHTER_DEMON
player_mind.add_antag_datum(/datum/antagonist/slaughter)
to_chat(S, S.playstyle_string)
to_chat(S, "You are currently not currently in the same plane of existence as the station. Blood Crawl near a blood pool to manifest.")
diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm
index f64994abe8f..b220d6ebf9a 100644
--- a/code/modules/antagonists/space_ninja/space_ninja.dm
+++ b/code/modules/antagonists/space_ninja/space_ninja.dm
@@ -118,12 +118,12 @@
if(give_equipment)
equip_space_ninja(owner.current)
- owner.current.mind.assigned_role = ROLE_NINJA
+ owner.current.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_ninja))
owner.current.mind.special_role = ROLE_NINJA
return ..()
/datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin)
- new_owner.assigned_role = ROLE_NINJA
+ new_owner.set_assigned_role(SSjob.GetJobType(/datum/job/space_ninja))
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has ninja'ed [key_name_admin(new_owner)].")
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index de90d732cbc..9cac0e7e940 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -126,7 +126,7 @@
objectives += kill_objective
return
- if(prob(DOWNLOAD_PROB) && !(locate(/datum/objective/download) in objectives) && !(owner.assigned_role in list("Research Director", "Scientist", "Roboticist", "Geneticist")))
+ if(prob(DOWNLOAD_PROB) && !(locate(/datum/objective/download) in objectives) && !(owner.assigned_role.departments & DEPARTMENT_SCIENCE))
var/datum/objective/download/download_objective = new
download_objective.owner = owner
download_objective.gen_amount_goal()
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index 9385e2bf3c0..c6ea5185076 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -681,7 +681,7 @@
owner = user
return
if(user != owner)
- if(user.mind.special_role == "apprentice")
+ if(user.mind.special_role == ROLE_WIZARD_APPRENTICE)
to_chat(user, "If you got caught sneaking a peek from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not.")
else
to_chat(user, span_warning("The [name] does not recognize you as its owner and refuses to open!"))
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index 9263d62e26e..329e150380a 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -32,7 +32,8 @@
var/burn_damage = 0
var/datum/disease/disease = null //Do they start with a pre-spawned disease?
var/mob_color //Change the mob's color
- var/assignedrole
+ /// Typepath indicating the kind of job datum this ert member will have.
+ var/spawner_job_path = /datum/job/ghost_role
var/show_flavour = TRUE
var/banType = ROLE_LAVALAND
var/ghost_usable = TRUE
@@ -41,6 +42,7 @@
/// If the spawner uses radials
var/radial_based = FALSE
+
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/attack_ghost(mob/user)
if(!SSticker.HasRoundStarted() || !loc || !ghost_usable)
@@ -137,8 +139,7 @@
var/datum/objective/O = new/datum/objective(objective)
O.owner = MM
A.objectives += O
- if(assignedrole)
- M.mind.assigned_role = assignedrole
+ M.mind.set_assigned_role(SSjob.GetJobType(spawner_job_path))
special(M)
MM.name = M.real_name
if(uses > 0)
@@ -155,7 +156,7 @@
var/datum/outfit/outfit = /datum/outfit //If this is a path, it will be instanced in Initialize()
var/disable_pda = TRUE
var/disable_sensors = TRUE
- assignedrole = "Ghost Role"
+ spawner_job_path = /datum/job/ghost_role
var/husk = null
//these vars are for lazy mappers to override parts of the outfit
@@ -450,7 +451,7 @@
name = "rotting corpse"
mob_name = "zombie"
mob_species = /datum/species/zombie
- assignedrole = "Zombie"
+ spawner_job_path = /datum/job/zombie
/obj/effect/mob_spawn/human/abductor
name = "abductor"
diff --git a/code/modules/capture_the_flag/capture_the_flag.dm b/code/modules/capture_the_flag/capture_the_flag.dm
index 9bd0ad23eba..6f836cad196 100644
--- a/code/modules/capture_the_flag/capture_the_flag.dm
+++ b/code/modules/capture_the_flag/capture_the_flag.dm
@@ -376,7 +376,7 @@
return //picked nothing, admin disabled it, cheating to respawn faster, cheating to respawn... while in game?
chosen_class = ctf_gear[result]
var/mob/living/carbon/human/M = new /mob/living/carbon/human(get_turf(src))
- new_team_member.prefs.copy_to(M)
+ new_team_member.prefs.safe_transfer_prefs_to(M, is_antag = TRUE)
M.set_species(/datum/species/synth)
M.key = new_team_member.key
M.faction += team
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 9aac18a97f3..6063ea43cc0 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -81,7 +81,20 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/eye_color = "000" //Eye color
var/datum/species/pref_species = new /datum/species/human() //Mutant race
var/list/features = list("mcolor" = "FFF", "ethcolor" = "9c3030", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None", "legs" = "Normal Legs", "moth_wings" = "Plain", "moth_antennae" = "Plain", "moth_markings" = "None")
- var/list/randomise = list(RANDOM_UNDERWEAR = TRUE, RANDOM_UNDERWEAR_COLOR = TRUE, RANDOM_UNDERSHIRT = TRUE, RANDOM_SOCKS = TRUE, RANDOM_BACKPACK = TRUE, RANDOM_JUMPSUIT_STYLE = TRUE, RANDOM_HAIRSTYLE = TRUE, RANDOM_HAIR_COLOR = TRUE, RANDOM_FACIAL_HAIRSTYLE = TRUE, RANDOM_FACIAL_HAIR_COLOR = TRUE, RANDOM_SKIN_TONE = TRUE, RANDOM_EYE_COLOR = TRUE)
+ var/list/randomise = list(
+ RANDOM_UNDERWEAR = TRUE,
+ RANDOM_UNDERWEAR_COLOR = TRUE,
+ RANDOM_UNDERSHIRT = TRUE,
+ RANDOM_SOCKS = TRUE,
+ RANDOM_BACKPACK = TRUE,
+ RANDOM_JUMPSUIT_STYLE = TRUE,
+ RANDOM_HAIRSTYLE = TRUE,
+ RANDOM_HAIR_COLOR = TRUE,
+ RANDOM_FACIAL_HAIRSTYLE = TRUE,
+ RANDOM_FACIAL_HAIR_COLOR = TRUE,
+ RANDOM_SKIN_TONE = TRUE,
+ RANDOM_EYE_COLOR = TRUE,
+ )
var/phobia = "spiders"
var/list/custom_names = list()
@@ -133,9 +146,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/action_buttons_screen_locs = list()
- ///This var stores the amount of points the owner will get for making it out alive.
- var/hardcore_survival_score = 0
-
///Someone thought we were nice! We get a little heart in OOC until we join the server past the below time (we can keep it until the end of the round otherwise)
var/hearted
///If we have a hearted commendations, we honor it every time the player loads preferences until this time has been passed
@@ -167,7 +177,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(load_character())
return
//we couldn't load character data so just randomize the character appearance + name
- random_character() //let's create a random character then - rather than a fat, bald and naked man.
+ randomise_appearance_prefs() //let's create a random character then - rather than a fat, bald and naked man.
key_bindings = deepCopyList(GLOB.hotkey_keybinding_list_by_key) // give them default keybinds and update their movement keys
C?.set_macros()
real_name = pref_species.random_name(gender,1)
@@ -883,7 +893,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/width = widthPerColumn
var/HTML = ""
- if(SSjob.occupations.len <= 0)
+ if(length(SSjob.joinable_occupations) <= 0)
HTML += "The job SSticker is not yet finished creating jobs, please try again later"
HTML += "Done
" // Easier to press up here.
@@ -898,8 +908,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows.
var/datum/job/lastJob
+ var/datum/job/overflow_role = SSjob.GetJobType(SSjob.overflow_role)
- for(var/datum/job/job in sortList(SSjob.occupations, /proc/cmp_job_display_asc))
+ for(var/datum/job/job as anything in sortList(SSjob.joinable_occupations, /proc/cmp_job_display_asc))
index += 1
if(index >= limit)
@@ -925,7 +936,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/available_in_days = job.available_in_days(user.client)
HTML += "[rank] \[IN [(available_in_days)] DAYS\] | "
continue
- if((job_preferences[SSjob.overflow_role] == JP_LOW) && (rank != SSjob.overflow_role) && !is_banned_from(user.ckey, SSjob.overflow_role))
+ if((job_preferences[overflow_role.title] == JP_LOW) && (rank != overflow_role.title) && !is_banned_from(user.ckey, overflow_role.title))
HTML += "[rank] | "
continue
if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs
@@ -964,8 +975,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
HTML += ""
- if(rank == SSjob.overflow_role)//Overflow is special
- if(job_preferences[SSjob.overflow_role] == JP_LOW)
+ if(rank == overflow_role.title)//Overflow is special
+ if(job_preferences[overflow_role.title] == JP_LOW)
HTML += "Yes"
else
HTML += "No"
@@ -981,7 +992,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
HTML += "
"
HTML += ""
- var/message = "Be an [SSjob.overflow_role] if preferences unavailable"
+ var/message = "Be an [overflow_role.title] if preferences unavailable"
if(joblessrole == BERANDOMJOB)
message = "Get random job if preferences unavailable"
else if(joblessrole == RETURNTOLOBBY)
@@ -1009,11 +1020,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return TRUE
/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl)
- if(!SSjob || SSjob.occupations.len <= 0)
+ if(!SSjob || length(SSjob.joinable_occupations) <= 0)
return
var/datum/job/job = SSjob.GetJob(role)
- if(!job)
+ if(!job || !(job.job_flags & JOB_NEW_PLAYER_JOINABLE))
user << browse(null, "window=mob_occupation")
ShowChoices(user)
return
@@ -1021,7 +1032,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if (!isnum(desiredLvl))
to_chat(user, span_danger("UpdateJobPreference - desired level was not a number. Please notify coders!"))
ShowChoices(user)
- return
+ CRASH("UpdateJobPreference called with desiredLvl value of [isnull(desiredLvl) ? "null" : desiredLvl]")
var/jpval = null
switch(desiredLvl)
@@ -1032,7 +1043,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(1)
jpval = JP_HIGH
- if(role == SSjob.overflow_role)
+ if(job.type == SSjob.overflow_role)
if(job_preferences[job.title] == JP_LOW)
jpval = null
else
@@ -1158,7 +1169,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("random")
switch(joblessrole)
if(RETURNTOLOBBY)
- if(is_banned_from(user.ckey, SSjob.overflow_role))
+ var/datum/job/overflow_role = SSjob.GetJobType(SSjob.overflow_role)
+ if(is_banned_from(user.ckey, overflow_role.title))
joblessrole = BERANDOMJOB
else
joblessrole = BEOVERFLOW
@@ -1248,7 +1260,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("suit")
jumpsuit_style = pick(GLOB.jumpsuitlist)
if("all")
- random_character(gender)
+ apply_character_randomization_prefs()
if("input")
@@ -1856,8 +1868,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("changeslot")
if(!load_character(text2num(href_list["num"])))
- random_character()
- real_name = random_unique_name(gender)
+ randomise_appearance_prefs()
save_character()
if("tab")
@@ -1873,37 +1884,33 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ShowChoices(user)
return 1
-/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE, character_setup = FALSE, antagonist = FALSE, is_latejoiner = TRUE)
- hardcore_survival_score = 0 //Set to 0 to prevent you getting points from last another time.
+/// Sanitization checks to be performed before using these preferences.
+/datum/preferences/proc/sanitize_chosen_prefs()
+ if(!(pref_species.id in GLOB.roundstart_races) && !(pref_species.id in (CONFIG_GET(keyed_list/roundstart_no_hard_check))))
+ pref_species = new /datum/species/human
+ save_character()
- if((randomise[RANDOM_SPECIES] || randomise[RANDOM_HARDCORE]) && !character_setup)
+ if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == SPECIES_HUMAN))
+ var/firstspace = findtext(real_name, " ")
+ var/name_length = length(real_name)
+ if(!firstspace) //we need a surname
+ real_name += " [pick(GLOB.last_names)]"
+ else if(firstspace == name_length)
+ real_name += "[pick(GLOB.last_names)]"
- random_species()
- if((randomise[RANDOM_BODY] || (randomise[RANDOM_BODY_ANTAG] && antagonist) || randomise[RANDOM_HARDCORE]) && !character_setup)
- slot_randomized = TRUE
- random_character(gender, antagonist)
+/// Sanitizes the preferences, applies the randomization prefs, and then applies the preference to the human mob.
+/datum/preferences/proc/safe_transfer_prefs_to(mob/living/carbon/human/character, icon_updates = TRUE, is_antag = FALSE)
+ apply_character_randomization_prefs(is_antag)
+ sanitize_chosen_prefs()
+ apply_prefs_to(character, icon_updates)
- if((randomise[RANDOM_NAME] || (randomise[RANDOM_NAME_ANTAG] && antagonist) || randomise[RANDOM_HARDCORE]) && !character_setup)
- slot_randomized = TRUE
- real_name = pref_species.random_name(gender)
-
- if(randomise[RANDOM_HARDCORE] && parent.mob.mind && !character_setup)
- if(can_be_random_hardcore())
- hardcore_random_setup(character, antagonist, is_latejoiner)
-
- if(roundstart_checks)
- if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == SPECIES_HUMAN))
- var/firstspace = findtext(real_name, " ")
- var/name_length = length(real_name)
- if(!firstspace) //we need a surname
- real_name += " [pick(GLOB.last_names)]"
- else if(firstspace == name_length)
- real_name += "[pick(GLOB.last_names)]"
+/// Applies the given preferences to a human mob.
+/datum/preferences/proc/apply_prefs_to(mob/living/carbon/human/character, icon_updates = TRUE)
character.real_name = real_name
- character.name = character.real_name
+ character.name = real_name
character.gender = gender
character.age = age
@@ -1932,15 +1939,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.jumpsuit_style = jumpsuit_style
- var/datum/species/chosen_species
- chosen_species = pref_species.type
- if(roundstart_checks && !(pref_species.id in GLOB.roundstart_races) && !(pref_species.id in (CONFIG_GET(keyed_list/roundstart_no_hard_check))))
- chosen_species = /datum/species/human
- pref_species = new /datum/species/human
- save_character()
-
character.dna.features = features.Copy()
- character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE)
+ character.set_species(pref_species.type, icon_update = FALSE, pref_load = TRUE)
character.dna.real_name = character.real_name
if(pref_species.mutant_bodyparts["tail_lizard"])
@@ -1953,15 +1953,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.update_hair()
character.update_body_parts()
-/datum/preferences/proc/can_be_random_hardcore()
- if(parent.mob.mind.assigned_role in GLOB.command_positions) //No command staff
+
+/// Returns whether the parent mob should have the random hardcore settings enabled. Assumes it has a mind.
+/datum/preferences/proc/should_be_random_hardcore(datum/job/job, datum/mind/mind)
+ if(!randomise[RANDOM_HARDCORE])
return FALSE
- for(var/A in parent.mob.mind.antag_datums)
- var/datum/antagonist/antag
+ if(job.departments & DEPARTMENT_COMMAND) //No command staff
+ return FALSE
+ for(var/datum/antagonist/antag as anything in mind.antag_datums)
if(antag.get_team()) //No team antags
return FALSE
return TRUE
+
/datum/preferences/proc/get_default_name(name_id)
switch(name_id)
if("human")
diff --git a/code/modules/clothing/outfits/event.dm b/code/modules/clothing/outfits/event.dm
index a35cbccebe0..8ab4478dc00 100644
--- a/code/modules/clothing/outfits/event.dm
+++ b/code/modules/clothing/outfits/event.dm
@@ -18,8 +18,8 @@
if(visualsOnly)
return
H.fully_replace_character_name(H.real_name, "Santa Claus")
- H.mind.assigned_role = "Santa"
- H.mind.special_role = "Santa"
+ H.mind.set_assigned_role(SSjob.GetJobType(/datum/job/santa))
+ H.mind.special_role = ROLE_SANTA
H.hairstyle = "Long Hair 3"
H.facial_hairstyle = "Beard (Full)"
diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm
index 04ad4d0611a..9221741feef 100644
--- a/code/modules/clothing/outfits/vr.dm
+++ b/code/modules/clothing/outfits/vr.dm
@@ -8,7 +8,7 @@
shoes = /obj/item/clothing/shoes/sneakers/black
/datum/outfit/vr/pre_equip(mob/living/carbon/human/H)
- H.dna.species.before_equip_job(null, H)
+ H.dna.species.pre_equip_species_outfit(null, H)
/datum/outfit/vr/syndicate
name = "Syndicate VR Operative - Basic"
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index f6760fbeb0c..184ab5797af 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -112,13 +112,13 @@
if(slot == ITEM_SLOT_FEET)
if(enabled_waddle)
user.AddElement(/datum/element/waddling)
- if(user.mind && user.mind.assigned_role == "Clown")
+ if(is_clown_job(user.mind?.assigned_role))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "clownshoes", /datum/mood_event/clownshoes)
/obj/item/clothing/shoes/clown_shoes/dropped(mob/user)
. = ..()
user.RemoveElement(/datum/element/waddling)
- if(user.mind && user.mind.assigned_role == "Clown")
+ if(is_clown_job(user.mind?.assigned_role))
SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "clownshoes")
/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user)
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index a283b206e22..84822dfaa97 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -704,8 +704,7 @@
/obj/item/clothing/suit/space/hardsuit/clown/mob_can_equip(mob/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
if(!..() || !ishuman(M))
return FALSE
- var/mob/living/carbon/human/H = M
- if(H.mind.assigned_role == "Clown")
+ if(is_clown_job(M.mind.assigned_role))
return TRUE
else
return FALSE
diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm
index ee8c5ed48e8..8abf0718875 100644
--- a/code/modules/events/alien_infestation.dm
+++ b/code/modules/events/alien_infestation.dm
@@ -67,10 +67,14 @@
while(spawncount > 0 && vents.len && candidates.len)
var/obj/vent = pick_n_take(vents)
- var/client/C = pick_n_take(candidates)
-
+ var/client/candidate_client = pick_n_take(candidates)
+ var/datum/mind/candidate_mind = candidate_client.mob.mind
+ if(!candidate_mind)
+ continue
var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
- new_xeno.key = C.key
+ candidate_mind.transfer_to(new_xeno)
+ candidate_mind.set_assigned_role(SSjob.GetJobType(/datum/job/xenomorph))
+ candidate_mind.special_role = ROLE_ALIEN
new_xeno.move_into_vent(vent)
spawncount--
diff --git a/code/modules/events/brain_trauma.dm b/code/modules/events/brain_trauma.dm
index eda231ce6f5..7350fd682fb 100644
--- a/code/modules/events/brain_trauma.dm
+++ b/code/modules/events/brain_trauma.dm
@@ -14,7 +14,7 @@
continue
if(!H.getorgan(/obj/item/organ/brain)) // If only I had a brain
continue
- if(!SSjob.GetJob(H.mind.assigned_role) || (H.mind.assigned_role in GLOB.nonhuman_positions)) //please stop giving my centcom admin gimmicks full body paralysis
+ if(!(H.mind.assigned_role.job_flags & JOB_CREW_MEMBER)) //please stop giving my centcom admin gimmicks full body paralysis
continue
traumatize(H)
announce_to_ghosts(H)
diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm
index 2a5292b4b88..f46775fc102 100644
--- a/code/modules/events/bureaucratic_error.dm
+++ b/code/modules/events/bureaucratic_error.dm
@@ -11,7 +11,7 @@
priority_announce("A recent bureaucratic error in the Organic Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert")
/datum/round_event/bureaucratic_error/start()
- var/list/jobs = SSjob.occupations.Copy()
+ var/list/jobs = SSjob.joinable_occupations.Copy()
if(prob(33)) // Only allows latejoining as a single role. Add latejoin AI bluespace pods for fun later.
var/datum/job/overflow = pick_n_take(jobs)
overflow.spawn_positions = -1
@@ -22,8 +22,7 @@
continue
current.total_positions = 0
else // Adds/removes a random amount of job slots from all jobs.
- for(var/job in jobs)
- var/datum/job/current = job
+ for(var/datum/job/current as anything in jobs)
if(!current.allow_bureaucratic_error)
continue
var/ran = rand(-2,4)
diff --git a/code/modules/events/creep_awakening.dm b/code/modules/events/creep_awakening.dm
index b8842119ba4..6e9720ea1fc 100644
--- a/code/modules/events/creep_awakening.dm
+++ b/code/modules/events/creep_awakening.dm
@@ -13,7 +13,7 @@
continue
if(H.stat == DEAD)
continue
- if(!SSjob.GetJob(H.mind.assigned_role) || (H.mind.assigned_role in GLOB.nonhuman_positions)) //only station jobs sans nonhuman roles, prevents ashwalkers trying to stalk with crewmembers they never met
+ if(!(H.mind.assigned_role.job_flags & JOB_CREW_MEMBER)) //only station jobs sans nonhuman roles, prevents ashwalkers trying to stalk with crewmembers they never met
continue
if(H.mind.has_antag_datum(/datum/antagonist/obsessed))
continue
diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm
index e3e2b4ff63b..accf534cc3f 100644
--- a/code/modules/events/fake_virus.dm
+++ b/code/modules/events/fake_virus.dm
@@ -6,7 +6,7 @@
/datum/round_event/fake_virus/start()
var/list/fake_virus_victims = list()
for(var/mob/living/carbon/human/victim in shuffle(GLOB.player_list))
- if(victim.stat == DEAD || HAS_TRAIT(victim, TRAIT_CRITICAL_CONDITION) || !SSjob.GetJob(victim.mind.assigned_role) || (victim.mind.assigned_role in GLOB.nonhuman_positions))
+ if(victim.stat == DEAD || HAS_TRAIT(victim, TRAIT_CRITICAL_CONDITION) || !(victim.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
continue
fake_virus_victims += victim
diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/fugitive_spawning.dm
index 1705e2dc566..b392387f8dd 100644
--- a/code/modules/events/fugitive_spawning.dm
+++ b/code/modules/events/fugitive_spawning.dm
@@ -65,8 +65,8 @@
player_mind.active = TRUE
var/mob/living/carbon/human/S = new(landing_turf)
player_mind.transfer_to(S)
- player_mind.assigned_role = "Fugitive"
- player_mind.special_role = "Fugitive"
+ player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/fugitive))
+ player_mind.special_role = ROLE_FUGITIVE
player_mind.add_antag_datum(/datum/antagonist/fugitive)
var/datum/antagonist/fugitive/fugitiveantag = player_mind.has_antag_datum(/datum/antagonist/fugitive)
INVOKE_ASYNC(fugitiveantag, /datum/antagonist/fugitive.proc/greet, backstory) //some fugitives have a sleep on their greet, so we don't want to stop the entire antag granting proc with fluff
diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm
index fc4b660d5cb..5e398a236d3 100644
--- a/code/modules/events/heart_attack.dm
+++ b/code/modules/events/heart_attack.dm
@@ -10,7 +10,7 @@
for(var/mob/living/carbon/human/victim in shuffle(GLOB.player_list))
if(victim.stat == DEAD || HAS_TRAIT(victim, TRAIT_CRITICAL_CONDITION) || !victim.can_heartattack() || victim.has_status_effect(STATUS_EFFECT_EXERCISED) || (/datum/disease/heart_failure in victim.diseases) || victim.undergoing_cardiac_arrest())
continue
- if(!SSjob.GetJob(victim.mind.assigned_role) || (victim.mind.assigned_role in GLOB.nonhuman_positions))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event
+ if(!(victim.mind.assigned_role.job_flags & JOB_CREW_MEMBER))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event
continue
if(victim.satiety <= -60) //Multiple junk food items recently
heart_attack_contestants[victim] = 3
diff --git a/code/modules/events/nightmare.dm b/code/modules/events/nightmare.dm
index eb01dc77204..92af1b424e5 100644
--- a/code/modules/events/nightmare.dm
+++ b/code/modules/events/nightmare.dm
@@ -33,8 +33,8 @@
var/mob/living/carbon/human/S = new ((pick(spawn_locs)))
player_mind.transfer_to(S)
- player_mind.assigned_role = "Nightmare"
- player_mind.special_role = "Nightmare"
+ player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nightmare))
+ player_mind.special_role = ROLE_NIGHTMARE
player_mind.add_antag_datum(/datum/antagonist/nightmare)
S.set_species(/datum/species/shadow/nightmare)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
diff --git a/code/modules/events/operative.dm b/code/modules/events/operative.dm
index af876cd436c..a4a7f05abd0 100644
--- a/code/modules/events/operative.dm
+++ b/code/modules/events/operative.dm
@@ -24,12 +24,11 @@
return MAP_ERROR
var/mob/living/carbon/human/operative = new(pick(spawn_locs))
- var/datum/preferences/A = new
- A.copy_to(operative)
+ operative.randomize_human_appearance(~RANDOMIZE_SPECIES)
operative.dna.update_dna_identity()
var/datum/mind/Mind = new /datum/mind(selected.key)
- Mind.assigned_role = "Lone Operative"
- Mind.special_role = "Lone Operative"
+ Mind.set_assigned_role(SSjob.GetJobType(/datum/job/lone_operative))
+ Mind.special_role = ROLE_LONE_OPERATIVE
Mind.active = TRUE
Mind.transfer_to(operative)
Mind.add_antag_datum(/datum/antagonist/nukeop/lone)
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 5d155de4385..2ae710bafd8 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -440,15 +440,14 @@
/datum/export/pirate/ransom/get_cost(atom/movable/AM)
var/mob/living/carbon/human/H = AM
- if(H.stat != CONSCIOUS || !H.mind || !H.mind.assigned_role) //mint condition only
+ if(H.stat != CONSCIOUS || !H.mind) //mint condition only
return 0
else if("pirate" in H.faction) //can't ransom your fellow pirates to CentCom!
return 0
+ else if(H.mind.assigned_role.title in GLOB.command_positions)
+ return 3000
else
- if(H.mind.assigned_role in GLOB.command_positions)
- return 3000
- else
- return 1000
+ return 1000
/datum/export/pirate/parrot
cost = 2000
diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/space_dragon.dm
index 869aa90197b..0f106e7210f 100644
--- a/code/modules/events/space_dragon.dm
+++ b/code/modules/events/space_dragon.dm
@@ -34,8 +34,8 @@
var/mob/living/simple_animal/hostile/space_dragon/dragon = new (pick(spawn_locs))
dragon.key = key
- dragon.mind.assigned_role = "Space Dragon"
- dragon.mind.special_role = "Space Dragon"
+ dragon.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
+ dragon.mind.special_role = ROLE_SPACE_DRAGON
dragon.mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(dragon, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.")
diff --git a/code/modules/events/space_ninja.dm b/code/modules/events/space_ninja.dm
index 0f2f3860903..c81aa033e0d 100644
--- a/code/modules/events/space_ninja.dm
+++ b/code/modules/events/space_ninja.dm
@@ -45,8 +45,9 @@
/proc/create_space_ninja(spawn_loc)
var/mob/living/carbon/human/new_ninja = new(spawn_loc)
- var/datum/preferences/random_human_options = new()//Randomize appearance for the ninja.
- random_human_options.real_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]"
- random_human_options.copy_to(new_ninja)
+ new_ninja.randomize_human_appearance(~(RANDOMIZE_NAME|RANDOMIZE_SPECIES))
+ var/new_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]"
+ new_ninja.name = new_name
+ new_ninja.real_name = new_name
new_ninja.dna.update_dna_identity()
return new_ninja
diff --git a/code/modules/events/wizard/departmentrevolt.dm b/code/modules/events/wizard/departmentrevolt.dm
index 3f05aba548b..b80e63d1dd1 100644
--- a/code/modules/events/wizard/departmentrevolt.dm
+++ b/code/modules/events/wizard/departmentrevolt.dm
@@ -89,19 +89,16 @@
department_target = pick(independent_departments)
nation.generate_nation_objectives(dangerous, department_target)
- for(var/i in GLOB.human_list)
- var/mob/living/carbon/human/possible_separatist = i
+ for(var/mob/living/carbon/human/possible_separatist as anything in GLOB.human_list)
if(!possible_separatist.mind)
continue
var/datum/mind/separatist_mind = possible_separatist.mind
- if(!separatist_mind.assigned_role)
+ if(!(separatist_mind.assigned_role.title in jobs_to_revolt))
continue
- for(var/job in jobs_to_revolt)
- if(separatist_mind.assigned_role == job)
- citizens += possible_separatist
- separatist_mind.add_antag_datum(/datum/antagonist/separatist, nation, department)
- nation.add_member(separatist_mind)
- possible_separatist.log_message("Was made into a separatist, long live [nation_name]!", LOG_ATTACK, color="red")
+ citizens += possible_separatist
+ separatist_mind.add_antag_datum(/datum/antagonist/separatist, nation, department)
+ nation.add_member(separatist_mind)
+ possible_separatist.log_message("Was made into a separatist, long live [nation_name]!", LOG_ATTACK, color="red")
if(citizens.len)
var/jobs_english_list = english_list(jobs_to_revolt)
diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm
index a3a480f8655..68675c9509b 100644
--- a/code/modules/food_and_drinks/pizzabox.dm
+++ b/code/modules/food_and_drinks/pizzabox.dm
@@ -377,7 +377,7 @@
var/list/pineapple_pizza_liker = pizza_types.Copy()
pineapple_pizza_liker -= /obj/item/food/pizza/pineapple
pizza_preferences[nommer.ckey] = pickweight(pineapple_pizza_liker)
- else if(nommer.mind && nommer.mind.assigned_role == "Botanist")
+ else if(nommer.mind?.assigned_role.title == /datum/job/botanist)
pizza_preferences[nommer.ckey] = /obj/item/food/pizza/dank
else
pizza_preferences[nommer.ckey] = pickweight(pizza_types)
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index fac8e157e9f..e92d473a693 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -188,7 +188,7 @@
/datum/holiday/april_fools/celebrate()
. = ..()
- SSjob.set_overflow_role("Clown")
+ SSjob.set_overflow_role(/datum/job/clown)
SSticker.login_music = 'sound/ambience/clown.ogg'
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/P = i
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index b05d48d22f4..bc280270f46 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -1,5 +1,5 @@
/datum/job
- //The name of the job , used for preferences, bans and more. Make sure you know what you're doing before changing this.
+ /// The name of the job , used for preferences, bans and more. Make sure you know what you're doing before changing this.
var/title = "NOPE"
/// Innate skill levels unlocked at roundstart. Based on config.jobs_have_minimal_access config setting, for example with a skeleton crew. Format is list(/datum/skill/foo = SKILL_EXP_NOVICE) with exp as an integer or as per code/_DEFINES/skills.dm
@@ -7,38 +7,40 @@
/// Innate skill levels unlocked at roundstart. Based on config.jobs_have_minimal_access config setting, for example with a full crew. Format is list(/datum/skill/foo = SKILL_EXP_NOVICE) with exp as an integer or as per code/_DEFINES/skills.dm
var/list/minimal_skills
- //Determines who can demote this position
+ /// Determines who can demote this position
var/department_head = list()
- //Tells the given channels that the given mob is the new department head. See communications.dm for valid channels.
+ /// Tells the given channels that the given mob is the new department head. See communications.dm for valid channels.
var/list/head_announce = null
- //Bitflags for the job
+ /// Bitflags for the job
var/auto_deadmin_role_flags = NONE
- //Players will be allowed to spawn in as jobs that are set to "Station"
- var/faction = "None"
+ /// Players will be allowed to spawn in as jobs that are set to "Station"
+ var/faction = FACTION_NONE
- //How many players can be this job
+ /// How many players can be this job
var/total_positions = 0
- //How many players can spawn in as this job
+ /// How many players can spawn in as this job
var/spawn_positions = 0
- //How many players have this job
+ /// How many players have this job
var/current_positions = 0
- //Supervisors, who this person answers to directly
+ /// Supervisors, who this person answers to directly
var/supervisors = ""
- //Sellection screen color
+ /// Selection screen color
var/selection_color = "#ffffff"
+ /// What kind of mob type joining players with this job as their assigned role are spawned as.
+ var/spawn_type = /mob/living/carbon/human
- //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect.
+ /// If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect.
var/req_admin_notify
- //If you have the use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.)
+ /// If you have the use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.)
var/minimal_player_age = 0
var/outfit = null
@@ -83,6 +85,18 @@
/// List of family heirlooms this job can get with the family heirloom quirk. List of types.
var/list/family_heirlooms
+ /// All values = (JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK)
+ var/job_flags = NONE
+
+ /// Multiplier for general usage of the voice of god.
+ var/voice_of_god_power = 1
+ /// Multiplier for the silence command of the voice of god.
+ var/voice_of_god_silence_power = 1
+
+ /// String. If set to a non-empty one, it will be the key for the policy text value to show this role on spawn.
+ var/policy_index = ""
+
+
/datum/job/New()
. = ..()
var/list/jobs_changes = get_map_changes()
@@ -105,26 +119,24 @@
return job_changes[endpart]
-//Only override this proc
-//H is usually a human unless an /equip override transformed it
-/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
- //do actions on H but send messages to M as the key may not have been transferred_yet
- SEND_GLOBAL_SIGNAL(COMSIG_GLOB_JOB_AFTER_SPAWN, src, H, M, latejoin)
- if(mind_traits)
- for(var/t in mind_traits)
- ADD_TRAIT(H.mind, t, JOB_TRAIT)
- var/obj/item/organ/liver/liver = H.getorganslot(ORGAN_SLOT_LIVER)
+/// Executes after the mob has been spawned in the map. Client might not be yet in the mob, and is thus a separate variable.
+/datum/job/proc/after_spawn(mob/living/spawned, client/player_client)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_JOB_AFTER_SPAWN, src, spawned, player_client)
+ for(var/trait in mind_traits)
+ ADD_TRAIT(spawned.mind, trait, JOB_TRAIT)
+ var/obj/item/organ/liver/liver = spawned.getorganslot(ORGAN_SLOT_LIVER)
if(liver)
- for(var/t in liver_traits)
- ADD_TRAIT(liver, t, JOB_TRAIT)
+ for(var/trait in liver_traits)
+ ADD_TRAIT(liver, trait, JOB_TRAIT)
+
+ if(!ishuman(spawned))
+ return
var/list/roundstart_experience
- if(!ishuman(H))
- return
-
if(!config) //Needed for robots.
roundstart_experience = minimal_skills
@@ -134,48 +146,39 @@
roundstart_experience = skills
if(roundstart_experience)
- var/mob/living/carbon/human/experiencer = H
+ var/mob/living/carbon/human/experiencer = spawned
for(var/i in roundstart_experience)
experiencer.mind.adjust_experience(i, roundstart_experience[i], TRUE)
-/datum/job/proc/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- if(head_announce)
- announce_head(H, head_announce)
-/datum/job/proc/override_latejoin_spawn(mob/living/carbon/human/H) //Return TRUE to force latejoining to not automatically place the person in latejoin shuttle/whatever.
- return FALSE
+/datum/job/proc/announce_job(mob/living/joining_mob)
+ if(head_announce)
+ announce_head(joining_mob, head_announce)
+
//Used for a special check of whether to allow a client to latejoin as this job.
/datum/job/proc/special_check_latejoin(client/C)
return TRUE
-//Don't override this unless the job transforms into a non-human (Silicons do this for example)
-/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source, is_captain = FALSE)
- if(!H)
- return FALSE
- if(CONFIG_GET(flag/enforce_human_authority) && (title in GLOB.command_positions))
- if(H.dna.species.id != SPECIES_HUMAN)
- H.set_species(/datum/species/human)
- H.apply_pref_name("human", preference_source)
- if(!visualsOnly)
- var/datum/bank_account/bank_account = new(H.real_name, src, H.dna.species.payday_modifier)
- bank_account.payday(STARTING_PAYCHECKS, TRUE)
- H.account_id = bank_account.account_id
- //Equip the rest of the gear
- H.dna.species.before_equip_job(src, H, visualsOnly)
+/mob/living/proc/on_job_equipping(datum/job/equipping)
+ return
- if(outfit_override || outfit)
- H.equipOutfit(outfit_override ? outfit_override : outfit, visualsOnly)
+/mob/living/carbon/human/on_job_equipping(datum/job/equipping)
+ var/datum/bank_account/bank_account = new(real_name, equipping, dna.species.payday_modifier)
+ bank_account.payday(STARTING_PAYCHECKS, TRUE)
+ account_id = bank_account.account_id
- if(!visualsOnly && is_captain)
- var/is_acting_captain = (title != "Captain")
- SSjob.promote_to_captain(H, is_acting_captain)
+ dress_up_as_job(equipping)
- H.dna.species.after_equip_job(src, H, visualsOnly)
- if(!visualsOnly && announce)
- announce(H, is_captain)
+/mob/living/proc/dress_up_as_job(datum/job/equipping, visual_only = FALSE)
+ return
+
+/mob/living/carbon/human/dress_up_as_job(datum/job/equipping, visual_only = FALSE)
+ dna.species.pre_equip_species_outfit(equipping, src, visual_only)
+ equipOutfit(equipping.outfit, visual_only)
+
/datum/job/proc/announce_head(mob/living/carbon/human/H, channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels.
if(H && GLOB.announcement_systems.len)
@@ -302,3 +305,143 @@
/// An overridable getter for more dynamic goodies.
/datum/job/proc/get_mail_goodies(mob/recipient)
return mail_goodies
+
+
+/datum/job/proc/award_service(client/winner, award)
+ return
+
+
+/datum/job/proc/get_captaincy_announcement(mob/living/captain)
+ return "Due to extreme staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+
+
+/// Returns an atom where the mob should spawn in.
+/datum/job/proc/get_roundstart_spawn_point()
+ if(random_spawns_possible)
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_LATE_ARRIVALS))
+ return get_latejoin_spawn_point()
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_RANDOM_ARRIVALS))
+ return get_safe_random_station_turf(typesof(/area/hallway)) || get_latejoin_spawn_point()
+ if(HAS_TRAIT(SSstation, STATION_TRAIT_HANGOVER))
+ var/obj/effect/landmark/start/hangover_spawn_point
+ for(var/obj/effect/landmark/start/hangover/hangover_landmark in GLOB.start_landmarks_list)
+ hangover_spawn_point = hangover_landmark
+ if(hangover_landmark.used) //so we can revert to spawning them on top of eachother if something goes wrong
+ continue
+ hangover_landmark.used = TRUE
+ break
+ return hangover_spawn_point || get_latejoin_spawn_point()
+ if(length(GLOB.jobspawn_overrides[title]))
+ return pick(GLOB.jobspawn_overrides[title])
+ var/obj/effect/landmark/start/spawn_point = get_default_roundstart_spawn_point()
+ if(!spawn_point) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper
+ return get_latejoin_spawn_point()
+ return spawn_point
+
+
+/// Handles finding and picking a valid roundstart effect landmark spawn point, in case no uncommon different spawning events occur.
+/datum/job/proc/get_default_roundstart_spawn_point()
+ for(var/obj/effect/landmark/start/spawn_point as anything in GLOB.start_landmarks_list)
+ if(spawn_point.name != title)
+ continue
+ . = spawn_point
+ if(spawn_point.used) //so we can revert to spawning them on top of eachother if something goes wrong
+ continue
+ spawn_point.used = TRUE
+ break
+ if(!.)
+ log_world("Couldn't find a round start spawn point for [title]")
+
+
+/// Finds a valid latejoin spawn point, checking for events and special conditions.
+/datum/job/proc/get_latejoin_spawn_point()
+ if(length(GLOB.jobspawn_overrides[title])) //We're doing something special today.
+ return pick(GLOB.jobspawn_overrides[title])
+ if(length(SSjob.latejoin_trackers))
+ return pick(SSjob.latejoin_trackers)
+ return SSjob.get_last_resort_spawn_points()
+
+
+/// Called after a successful latejoin spawn.
+/datum/job/proc/after_latejoin_spawn(mob/living/spawning)
+ SHOULD_CALL_PARENT(TRUE)
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_JOB_AFTER_LATEJOIN_SPAWN, src, spawning)
+
+
+/// Spawns the mob to be played as, taking into account preferences and the desired spawn point.
+/datum/job/proc/get_spawn_mob(client/player_client, atom/spawn_point)
+ var/mob/living/spawn_instance
+ if(ispath(spawn_type, /mob/living/silicon/ai))
+ // This is unfortunately necessary because of snowflake AI init code. To be refactored.
+ spawn_instance = new spawn_type(get_turf(spawn_point), null, player_client.mob)
+ else
+ spawn_instance = new spawn_type(player_client.mob.loc)
+ spawn_point.JoinPlayerHere(spawn_instance, TRUE)
+ spawn_instance.apply_prefs_job(player_client, src)
+ if(!player_client)
+ qdel(spawn_instance)
+ return // Disconnected while checking for the appearance ban.
+ return spawn_instance
+
+
+/// Applies the preference options to the spawning mob, taking the job into account. Assumes the client has the proper mind.
+/mob/living/proc/apply_prefs_job(client/player_client, datum/job/job)
+
+
+/mob/living/carbon/human/apply_prefs_job(client/player_client, datum/job/job)
+ var/fully_randomize = GLOB.current_anonymous_theme || player_client.prefs.should_be_random_hardcore(job, player_client.mob.mind) || is_banned_from(player_client.ckey, "Appearance")
+ if(!player_client)
+ return // Disconnected while checking for the appearance ban.
+ if(fully_randomize)
+ if(CONFIG_GET(flag/enforce_human_authority) && (job.departments & DEPARTMENT_COMMAND))
+ if(player_client.prefs.pref_species.id != SPECIES_HUMAN)
+ player_client.prefs.pref_species = new /datum/species/human
+ player_client.prefs.randomise_appearance_prefs(~RANDOMIZE_SPECIES)
+ else
+ player_client.prefs.randomise_appearance_prefs()
+ player_client.prefs.apply_prefs_to(src)
+ if(GLOB.current_anonymous_theme)
+ fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(src))
+ else
+ var/is_antag = (player_client.mob.mind in GLOB.pre_setup_antags)
+ if(CONFIG_GET(flag/enforce_human_authority) && (job.departments & DEPARTMENT_COMMAND))
+ player_client.prefs.randomise[RANDOM_SPECIES] = FALSE
+ if(player_client.prefs.pref_species.id != SPECIES_HUMAN)
+ player_client.prefs.pref_species = new /datum/species/human
+ player_client.prefs.safe_transfer_prefs_to(src, TRUE, is_antag)
+ if(CONFIG_GET(flag/force_random_names))
+ player_client.prefs.real_name = player_client.prefs.pref_species.random_name(player_client.prefs.gender, TRUE)
+ dna.update_dna_identity()
+
+
+/mob/living/silicon/ai/apply_prefs_job(client/player_client, datum/job/job)
+ if(GLOB.current_anonymous_theme)
+ fully_replace_character_name(real_name, GLOB.current_anonymous_theme.anonymous_ai_name(TRUE))
+ return
+ apply_pref_name("ai", player_client) // This proc already checks if the player is appearance banned.
+ set_core_display_icon(null, player_client)
+
+
+/mob/living/silicon/robot/apply_prefs_job(client/player_client, datum/job/job)
+ if(mmi)
+ var/organic_name
+ if(GLOB.current_anonymous_theme)
+ organic_name = GLOB.current_anonymous_theme.anonymous_name(src)
+ else if(player_client.prefs.randomise[RANDOM_NAME] || CONFIG_GET(flag/force_random_names) || is_banned_from(player_client.ckey, "Appearance"))
+ if(!player_client)
+ return // Disconnected while checking the appearance ban.
+ organic_name = player_client.prefs.pref_species.random_name(player_client.prefs.gender, TRUE)
+ else
+ if(!player_client)
+ return // Disconnected while checking the appearance ban.
+ organic_name = player_client.prefs.real_name
+
+ mmi.name = "[initial(mmi.name)]: [organic_name]"
+ if(mmi.brain)
+ mmi.brain.name = "[organic_name]'s brain"
+ if(mmi.brainmob)
+ mmi.brainmob.real_name = organic_name //the name of the brain inside the cyborg is the robotized human's name.
+ mmi.brainmob.name = organic_name
+ // If this checks fails, then the name will have been handled during initialization.
+ if(!GLOB.current_anonymous_theme && player_client.prefs.custom_names["cyborg"] != DEFAULT_CYBORG_NAME)
+ apply_pref_name("cyborg", player_client)
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index d7cfe8ddd1e..ffb9e640bf5 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -1,11 +1,12 @@
/datum/job/ai
title = "AI"
auto_deadmin_role_flags = DEADMIN_POSITION_SILICON
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
- selection_color = "#ccffcc"
supervisors = "your laws"
+ selection_color = "#ccffcc"
+ spawn_type = /mob/living/silicon/ai
req_admin_notify = TRUE
minimal_player_age = 30
exp_requirements = 180
@@ -15,44 +16,56 @@
allow_bureaucratic_error = FALSE
departments = DEPARTMENT_SILICON
random_spawns_possible = FALSE
+ job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK
var/do_special_check = TRUE
-/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, datum/outfit/outfit_override, client/preference_source = null)
- if(visualsOnly)
- CRASH("dynamic preview is unsupported")
- . = H.AIize(latejoin,preference_source)
-/datum/job/ai/after_spawn(mob/H, mob/M, latejoin)
+/datum/job/ai/after_spawn(mob/living/spawned, client/player_client)
. = ..()
- if(latejoin)
- var/obj/structure/ai_core/latejoin_inactive/lateJoinCore
- for(var/obj/structure/ai_core/latejoin_inactive/P in GLOB.latejoin_ai_cores)
- if(P.is_available())
- lateJoinCore = P
- GLOB.latejoin_ai_cores -= P
- break
- if(lateJoinCore)
- lateJoinCore.available = FALSE
- H.forceMove(lateJoinCore.loc)
- qdel(lateJoinCore)
- var/mob/living/silicon/ai/AI = H
- if(GLOB.current_anonymous_theme)
- AI.fully_replace_character_name(AI.real_name, GLOB.current_anonymous_theme.anonymous_ai_name(TRUE))
- else
- AI.apply_pref_name("ai", M.client) //If this runtimes oh well jobcode is fucked. //what is this no energy attitude man
- AI.set_core_display_icon(null, M.client)
-
//we may have been created after our borg
if(SSticker.current_state == GAME_STATE_SETTING_UP)
for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
if(!R.connected_ai)
R.TryConnectToAI()
- if(latejoin)
- announce(AI)
-/datum/job/ai/override_latejoin_spawn()
- return TRUE
+/datum/job/ai/get_roundstart_spawn_point()
+ return get_latejoin_spawn_point()
+
+
+/datum/job/ai/get_latejoin_spawn_point()
+ var/list/primary_spawn_points = list() // Ideal locations.
+ var/list/secondary_spawn_points = list() // Fallback locations.
+ for(var/obj/effect/landmark/start/ai/spawn_point in GLOB.landmarks_list)
+ if(spawn_point.used)
+ secondary_spawn_points += list(spawn_point)
+ continue
+ if(spawn_point.primary_ai)
+ primary_spawn_points = list(spawn_point)
+ break // Bingo.
+ primary_spawn_points += spawn_point
+ var/obj/effect/landmark/start/ai/chosen_spawn_point
+ if(length(primary_spawn_points))
+ chosen_spawn_point = pick(primary_spawn_points)
+ else if(length(secondary_spawn_points))
+ chosen_spawn_point = pick(secondary_spawn_points)
+ else
+ CRASH("Failed to find any AI spawn points.")
+ chosen_spawn_point.used = TRUE
+ return chosen_spawn_point
+
+
+/datum/job/ai/get_latejoin_spawn_point()
+ for(var/obj/structure/ai_core/latejoin_inactive/inactive_core as anything in GLOB.latejoin_ai_cores)
+ if(!inactive_core.is_available())
+ continue
+ GLOB.latejoin_ai_cores -= inactive_core
+ inactive_core.available = FALSE
+ . = inactive_core.loc
+ qdel(inactive_core)
+ return
+ return ..()
+
/datum/job/ai/special_check_latejoin(client/C)
if(!do_special_check)
@@ -64,9 +77,12 @@
return TRUE
return FALSE
-/datum/job/ai/announce(mob/living/silicon/ai/AI)
+
+/datum/job/ai/announce_job(mob/living/joining_mob)
. = ..()
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "[AI] has been downloaded to an empty bluespace-networked AI core at [AREACOORD(AI)]."))
+ if(SSticker.HasRoundStarted())
+ minor_announce("[joining_mob] has been downloaded to an empty bluespace-networked AI core at [AREACOORD(joining_mob)].")
+
/datum/job/ai/config_check()
return CONFIG_GET(flag/allow_ai)
diff --git a/code/modules/jobs/job_types/antagonists/abductor_agent.dm b/code/modules/jobs/job_types/antagonists/abductor_agent.dm
new file mode 100644
index 00000000000..d08b15e2447
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/abductor_agent.dm
@@ -0,0 +1,2 @@
+/datum/job/abductor_agent
+ title = "Abductor Agent"
diff --git a/code/modules/jobs/job_types/antagonists/abductor_scientist.dm b/code/modules/jobs/job_types/antagonists/abductor_scientist.dm
new file mode 100644
index 00000000000..a7224080c53
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/abductor_scientist.dm
@@ -0,0 +1,2 @@
+/datum/job/abductor_scientist
+ title = "Abductor Scientist"
diff --git a/code/modules/jobs/job_types/antagonists/abductor_solo.dm b/code/modules/jobs/job_types/antagonists/abductor_solo.dm
new file mode 100644
index 00000000000..feac2e7657e
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/abductor_solo.dm
@@ -0,0 +1,2 @@
+/datum/job/abductor_solo
+ title = "Abductor Solo"
diff --git a/code/modules/jobs/job_types/antagonists/clown_operative.dm b/code/modules/jobs/job_types/antagonists/clown_operative.dm
new file mode 100644
index 00000000000..7c1a333da2c
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/clown_operative.dm
@@ -0,0 +1,2 @@
+/datum/job/clown_operative
+ title = ROLE_CLOWN_OPERATIVE
diff --git a/code/modules/jobs/job_types/antagonists/lone_operative.dm b/code/modules/jobs/job_types/antagonists/lone_operative.dm
new file mode 100644
index 00000000000..53bab251b1b
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/lone_operative.dm
@@ -0,0 +1,2 @@
+/datum/job/lone_operative
+ title = ROLE_LONE_OPERATIVE
diff --git a/code/modules/jobs/job_types/antagonists/morph.dm b/code/modules/jobs/job_types/antagonists/morph.dm
new file mode 100644
index 00000000000..033191572ca
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/morph.dm
@@ -0,0 +1,2 @@
+/datum/job/morph
+ title = ROLE_MORPH
diff --git a/code/modules/jobs/job_types/antagonists/nightmare.dm b/code/modules/jobs/job_types/antagonists/nightmare.dm
new file mode 100644
index 00000000000..47d16f47ecd
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/nightmare.dm
@@ -0,0 +1,2 @@
+/datum/job/nightmare
+ title = ROLE_NIGHTMARE
diff --git a/code/modules/jobs/job_types/antagonists/nuclear_operative.dm b/code/modules/jobs/job_types/antagonists/nuclear_operative.dm
new file mode 100644
index 00000000000..3c45f61e051
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/nuclear_operative.dm
@@ -0,0 +1,10 @@
+/datum/job/nuclear_operative
+ title = ROLE_NUCLEAR_OPERATIVE
+
+
+/datum/job/nuclear_operative/get_roundstart_spawn_point()
+ return pick(GLOB.nukeop_start)
+
+
+/datum/job/nuclear_operative/get_latejoin_spawn_point()
+ return pick(GLOB.nukeop_start)
diff --git a/code/modules/jobs/job_types/antagonists/revenant.dm b/code/modules/jobs/job_types/antagonists/revenant.dm
new file mode 100644
index 00000000000..0f25ddd35c8
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/revenant.dm
@@ -0,0 +1,2 @@
+/datum/job/revenant
+ title = ROLE_REVENANT
diff --git a/code/modules/jobs/job_types/antagonists/sentient_disease.dm b/code/modules/jobs/job_types/antagonists/sentient_disease.dm
new file mode 100644
index 00000000000..4c91ae4ffa1
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/sentient_disease.dm
@@ -0,0 +1,2 @@
+/datum/job/sentient_disease
+ title = ROLE_SENTIENT_DISEASE
diff --git a/code/modules/jobs/job_types/antagonists/slaughter_demon.dm b/code/modules/jobs/job_types/antagonists/slaughter_demon.dm
new file mode 100644
index 00000000000..f209d4ac2af
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/slaughter_demon.dm
@@ -0,0 +1,2 @@
+/datum/job/slaughter_demon
+ title = ROLE_SLAUGHTER_DEMON
diff --git a/code/modules/jobs/job_types/antagonists/space_dragon.dm b/code/modules/jobs/job_types/antagonists/space_dragon.dm
new file mode 100644
index 00000000000..0e5b0ef87f1
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/space_dragon.dm
@@ -0,0 +1,2 @@
+/datum/job/space_dragon
+ title = ROLE_SPACE_DRAGON
diff --git a/code/modules/jobs/job_types/antagonists/space_ninja.dm b/code/modules/jobs/job_types/antagonists/space_ninja.dm
new file mode 100644
index 00000000000..269d6cb882c
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/space_ninja.dm
@@ -0,0 +1,2 @@
+/datum/job/space_ninja
+ title = ROLE_NINJA
diff --git a/code/modules/jobs/job_types/antagonists/space_wizard.dm b/code/modules/jobs/job_types/antagonists/space_wizard.dm
new file mode 100644
index 00000000000..71250636c2c
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/space_wizard.dm
@@ -0,0 +1,3 @@
+/datum/job/space_wizard
+ title = ROLE_WIZARD
+ faction = ROLE_WIZARD
diff --git a/code/modules/jobs/job_types/antagonists/wizard_apprentice.dm b/code/modules/jobs/job_types/antagonists/wizard_apprentice.dm
new file mode 100644
index 00000000000..b4d318f438b
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/wizard_apprentice.dm
@@ -0,0 +1,3 @@
+/datum/job/wizard_apprentice
+ title = "Apprentice" //Turns out that ROLE_WIZARD_APPRENTICE is lower-case, and DB stuff is linked to it.
+ faction = ROLE_WIZARD
diff --git a/code/modules/jobs/job_types/antagonists/xenomorph.dm b/code/modules/jobs/job_types/antagonists/xenomorph.dm
new file mode 100644
index 00000000000..a354a517ac6
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/xenomorph.dm
@@ -0,0 +1,2 @@
+/datum/job/xenomorph
+ title = ROLE_ALIEN
diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm
index b99e3132a23..0de04858c7a 100644
--- a/code/modules/jobs/job_types/assistant.dm
+++ b/code/modules/jobs/job_types/assistant.dm
@@ -3,7 +3,7 @@ Assistant
*/
/datum/job/assistant
title = "Assistant"
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 5
spawn_positions = 5
supervisors = "absolutely everyone"
@@ -29,6 +29,9 @@ Assistant
/obj/item/crowbar/large = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/assistant
name = "Assistant"
jobtype = /datum/job/assistant
diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm
index 01019a318e5..eeb3a77d03b 100644
--- a/code/modules/jobs/job_types/atmospheric_technician.dm
+++ b/code/modules/jobs/job_types/atmospheric_technician.dm
@@ -1,7 +1,7 @@
/datum/job/atmospheric_technician
title = "Atmospheric Technician"
department_head = list("Chief Engineer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 3
spawn_positions = 2
supervisors = "the chief engineer"
@@ -23,6 +23,9 @@
family_heirlooms = list(/obj/item/lighter, /obj/item/lighter/greyscale, /obj/item/storage/box/matches)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/atmos
name = "Atmospheric Technician"
jobtype = /datum/job/atmospheric_technician
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index fd06f8c7ccc..b576fdffa60 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -1,7 +1,7 @@
/datum/job/bartender
title = "Bartender"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel"
@@ -25,6 +25,20 @@
/obj/item/stack/sheet/mineral/uranium = 10,
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
+/datum/job/bartender/award_service(client/winner, award)
+ winner.give_award(award, winner.mob)
+
+ var/datum/venue/bar = SSrestaurant.all_venues[/datum/venue/bar]
+ var/award_score = bar.total_income
+ var/award_status = winner.get_award_status(/datum/award/score/bartender_tourist_score)
+ if(award_score - award_status > 0)
+ award_score -= award_status
+ winner.give_award(/datum/award/score/bartender_tourist_score, winner.mob, award_score)
+
+
/datum/outfit/job/bartender
name = "Bartender"
jobtype = /datum/job/bartender
diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm
index ea6de611f4a..e7368c10896 100644
--- a/code/modules/jobs/job_types/botanist.dm
+++ b/code/modules/jobs/job_types/botanist.dm
@@ -1,7 +1,7 @@
/datum/job/botanist
title = "Botanist"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 3
spawn_positions = 2
supervisors = "the head of personnel"
@@ -27,6 +27,9 @@
/obj/item/food/monkeycube/bee = 2
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/botanist
name = "Botanist"
jobtype = /datum/job/botanist
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 30655be96d4..15d729beec9 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -2,7 +2,7 @@
title = "Captain"
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY
department_head = list("CentCom")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "Nanotrasen officials and Space law"
@@ -32,10 +32,14 @@
/obj/item/reagent_containers/food/drinks/bottle/champagne = 10
)
-/datum/job/captain/announce(mob/living/carbon/human/H, announce_captaincy = TRUE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 1.4 //Command staff has authority
+
+
+/datum/job/captain/get_captaincy_announcement(mob/living/captain)
+ return "Captain [captain.real_name] on deck!"
+
/datum/outfit/job/captain
name = "Captain"
diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm
index da781bd2979..3e9ea2a8420 100644
--- a/code/modules/jobs/job_types/cargo_technician.dm
+++ b/code/modules/jobs/job_types/cargo_technician.dm
@@ -1,7 +1,7 @@
/datum/job/cargo_technician
title = "Cargo Technician"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 3
spawn_positions = 2
supervisors = "the quartermaster and the head of personnel"
@@ -26,6 +26,9 @@
/obj/item/gun/ballistic/rifle/boltaction = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/cargo_tech
name = "Cargo Technician"
jobtype = /datum/job/cargo_technician
diff --git a/code/modules/jobs/job_types/chaplain.dm b/code/modules/jobs/job_types/chaplain.dm
index 1fb33215bfc..4f96d64046b 100644
--- a/code/modules/jobs/job_types/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain.dm
@@ -1,7 +1,7 @@
/datum/job/chaplain
title = "Chaplain"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel"
@@ -26,9 +26,16 @@
/obj/item/toy/plush/ratplush = 1
)
-/datum/job/chaplain/after_spawn(mob/living/H, mob/M)
- . = ..()
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+ voice_of_god_power = 2 //Chaplains are very good at speaking with the voice of god
+
+
+/datum/job/chaplain/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ if(!ishuman(spawned))
+ return
+ var/mob/living/carbon/human/H = spawned
var/obj/item/storage/book/bible/booze/B = new
if(GLOB.religion)
@@ -53,18 +60,18 @@
H.mind.holy_role = HOLY_ROLE_HIGHPRIEST
var/new_religion = DEFAULT_RELIGION
- if(M.client && M.client.prefs.custom_names["religion"])
- new_religion = M.client.prefs.custom_names["religion"]
+ if(player_client?.prefs.custom_names["religion"])
+ new_religion = player_client.prefs.custom_names["religion"]
var/new_deity = DEFAULT_DEITY
- if(M.client && M.client.prefs.custom_names["deity"])
- new_deity = M.client.prefs.custom_names["deity"]
+ if(player_client?.prefs.custom_names["deity"])
+ new_deity = player_client.prefs.custom_names["deity"]
B.deity_name = new_deity
var/new_bible = DEFAULT_BIBLE
- if(M.client && M.client.prefs.custom_names["bible"])
- new_bible = M.client.prefs.custom_names["bible"]
+ if(player_client?.prefs.custom_names["bible"])
+ new_bible = player_client.prefs.custom_names["bible"]
switch(lowertext(new_religion))
if("homosexuality", "gay", "penis", "ass", "cock", "cocks")
diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm
index 257f4591ad6..082cd4dece6 100644
--- a/code/modules/jobs/job_types/chemist.dm
+++ b/code/modules/jobs/job_types/chemist.dm
@@ -1,7 +1,7 @@
/datum/job/chemist
title = "Chemist"
department_head = list("Chief Medical Officer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
supervisors = "the chief medical officer"
@@ -30,6 +30,9 @@
/obj/item/paper/secretrecipe = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/chemist
name = "Chemist"
jobtype = /datum/job/chemist
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index bd5d1a7e992..e8eac766f5a 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -3,7 +3,7 @@
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("Captain")
head_announce = list("Engineering")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the captain"
@@ -37,10 +37,14 @@
/obj/effect/spawner/lootdrop/space/fancytool/engineonly = 3
)
-/datum/job/chief_engineer/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 1.4 //Command staff has authority
+
+
+/datum/job/chief_engineer/get_captaincy_announcement(mob/living/captain)
+ return "Due to staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+
/datum/outfit/job/ce
name = "Chief Engineer"
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index 3f0ccf5a2f7..6963d00ddfc 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -3,7 +3,7 @@
department_head = list("Captain")
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
head_announce = list(RADIO_CHANNEL_MEDICAL)
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the captain"
@@ -34,10 +34,14 @@
)
family_heirlooms = list(/obj/item/storage/firstaid/ancient/heirloom)
-/datum/job/chief_medical_officer/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 1.4 //Command staff has authority
+
+
+/datum/job/chief_medical_officer/get_captaincy_announcement(mob/living/captain)
+ return "Due to staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+
/datum/outfit/job/cmo
name = "Chief Medical Officer"
diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm
index ca17d874b58..babd996ec49 100644
--- a/code/modules/jobs/job_types/clown.dm
+++ b/code/modules/jobs/job_types/clown.dm
@@ -1,7 +1,7 @@
/datum/job/clown
title = "Clown"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel"
@@ -28,9 +28,15 @@
family_heirlooms = list(/obj/item/bikehorn/golden)
-/datum/job/clown/after_spawn(mob/living/carbon/human/H, mob/M)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
+/datum/job/clown/after_spawn(mob/living/spawned, client/player_client)
. = ..()
- H.apply_pref_name("clown", M.client)
+ if(!ishuman(spawned))
+ return
+ spawned.apply_pref_name("clown", player_client)
+
/datum/outfit/job/clown
name = "Clown"
diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm
index f1cb63a7ab7..58062a85c9d 100644
--- a/code/modules/jobs/job_types/cook.dm
+++ b/code/modules/jobs/job_types/cook.dm
@@ -1,7 +1,7 @@
/datum/job/cook
title = "Cook"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 1
supervisors = "the head of personnel"
@@ -24,6 +24,9 @@
family_heirlooms = list(/obj/item/reagent_containers/food/condiment/saltshaker, /obj/item/kitchen/rollingpin, /obj/item/clothing/head/chefhat)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/job/cook/New()
. = ..()
var/list/job_changes = SSmapping.config.job_changes
@@ -64,6 +67,18 @@
/obj/item/kitchen/knife/butcher = 2
)
+
+/datum/job/cook/award_service(client/winner, award)
+ winner.give_award(award, winner.mob)
+
+ var/datum/venue/restaurant = SSrestaurant.all_venues[/datum/venue/restaurant]
+ var/award_score = restaurant.total_income
+ var/award_status = winner.get_award_status(/datum/award/score/chef_tourist_score)
+ if(award_score > award_status)
+ award_score -= award_status
+ winner.give_award(/datum/award/score/chef_tourist_score, winner.mob, award_score)
+
+
/datum/outfit/job/cook
name = "Cook"
jobtype = /datum/job/cook
diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm
index a80b2900830..de6b764cf69 100644
--- a/code/modules/jobs/job_types/curator.dm
+++ b/code/modules/jobs/job_types/curator.dm
@@ -1,7 +1,7 @@
/datum/job/curator
title = "Curator"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel"
@@ -18,6 +18,11 @@
family_heirlooms = list(/obj/item/pen/fountain, /obj/item/storage/pill_bottle/dice)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_silence_power = 3
+
+
/datum/outfit/job/curator
name = "Curator"
jobtype = /datum/job/curator
diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm
index cc741a7a3aa..ad2e2adf59c 100644
--- a/code/modules/jobs/job_types/cyborg.dm
+++ b/code/modules/jobs/job_types/cyborg.dm
@@ -1,11 +1,12 @@
/datum/job/cyborg
title = "Cyborg"
auto_deadmin_role_flags = DEADMIN_POSITION_SILICON
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 0
spawn_positions = 1
supervisors = "your laws and the AI" //Nodrak
selection_color = "#ddffdd"
+ spawn_type = /mob/living/silicon/robot
minimal_player_age = 21
exp_requirements = 120
exp_type = EXP_TYPE_CREW
@@ -13,15 +14,17 @@
display_order = JOB_DISPLAY_ORDER_CYBORG
departments = DEPARTMENT_SILICON
random_spawns_possible = FALSE
+ job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK
-/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null, client/preference_source = null)
- if(visualsOnly)
- CRASH("dynamic preview is unsupported")
- return H.Robotize(FALSE, latejoin)
-/datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, mob/M)
- R.updatename(M.client)
- R.gender = NEUTER
+/datum/job/cyborg/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ if(!iscyborg(spawned))
+ return
+ spawned.gender = NEUTER
+ var/mob/living/silicon/robot/robot_spawn = spawned
+ robot_spawn.notify_ai(NEW_BORG)
+
/datum/job/cyborg/radio_help_message(mob/M)
to_chat(M, "Prefix your message with :b to speak with other cyborgs and AI.")
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 8dff7c6f5ed..3cb5e9ce83b 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -2,7 +2,7 @@
title = "Detective"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
department_head = list("Head of Security")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of security"
@@ -36,6 +36,9 @@
family_heirlooms = list(/obj/item/reagent_containers/food/drinks/bottle/whiskey)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/detective
name = "Detective"
jobtype = /datum/job/detective
diff --git a/code/modules/jobs/job_types/ert/ert_generic.dm b/code/modules/jobs/job_types/ert/ert_generic.dm
new file mode 100644
index 00000000000..4758ac75b28
--- /dev/null
+++ b/code/modules/jobs/job_types/ert/ert_generic.dm
@@ -0,0 +1,2 @@
+/datum/job/ert_generic
+ title = "ERT Generic"
diff --git a/code/modules/jobs/job_types/event/fugitive.dm b/code/modules/jobs/job_types/event/fugitive.dm
new file mode 100644
index 00000000000..5df01d4d358
--- /dev/null
+++ b/code/modules/jobs/job_types/event/fugitive.dm
@@ -0,0 +1,2 @@
+/datum/job/fugitive
+ title = ROLE_FUGITIVE
diff --git a/code/modules/jobs/job_types/event/santa.dm b/code/modules/jobs/job_types/event/santa.dm
new file mode 100644
index 00000000000..d6382215a7d
--- /dev/null
+++ b/code/modules/jobs/job_types/event/santa.dm
@@ -0,0 +1,2 @@
+/datum/job/santa
+ title = ROLE_SANTA
diff --git a/code/modules/jobs/job_types/free_golem.dm b/code/modules/jobs/job_types/free_golem.dm
new file mode 100644
index 00000000000..1fcfca7048b
--- /dev/null
+++ b/code/modules/jobs/job_types/free_golem.dm
@@ -0,0 +1,2 @@
+/datum/job/free_golem
+ title = ROLE_FREE_GOLEM
diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm
index d7ec5d92f95..b62c3cdbf27 100644
--- a/code/modules/jobs/job_types/geneticist.dm
+++ b/code/modules/jobs/job_types/geneticist.dm
@@ -1,7 +1,7 @@
/datum/job/geneticist
title = "Geneticist"
department_head = list("Research Director")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
supervisors = "the research director"
@@ -25,6 +25,9 @@
family_heirlooms = list(/obj/item/clothing/under/shorts/purple)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/geneticist
name = "Geneticist"
jobtype = /datum/job/geneticist
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 12ab9d2a30f..8d400d2c9d8 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -3,7 +3,7 @@
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("Captain")
head_announce = list(RADIO_CHANNEL_SUPPLY, RADIO_CHANNEL_SERVICE)
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the captain"
@@ -33,10 +33,14 @@
family_heirlooms = list(/obj/item/reagent_containers/food/drinks/trophy/silver_cup)
-/datum/job/head_of_personnel/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 1.4 //Command staff has authority
+
+
+/datum/job/head_of_personnel/get_captaincy_announcement(mob/living/captain)
+ return "Due to staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+
/datum/outfit/job/hop
name = "Head of Personnel"
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 1cae68fe922..d77fe30b7d9 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -3,7 +3,7 @@
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY
department_head = list("Captain")
head_announce = list(RADIO_CHANNEL_SECURITY)
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the captain"
@@ -29,10 +29,14 @@
family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law)
-/datum/job/head_of_security/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 1.4 //Command staff has authority
+
+
+/datum/job/head_of_security/get_captaincy_announcement(mob/living/captain)
+ return "Due to staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+
/datum/outfit/job/hos
name = "Head of Security"
diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm
index 759c73e3cef..5a0d5d5ccd5 100644
--- a/code/modules/jobs/job_types/janitor.dm
+++ b/code/modules/jobs/job_types/janitor.dm
@@ -1,7 +1,7 @@
/datum/job/janitor
title = "Janitor"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 1
supervisors = "the head of personnel"
@@ -24,6 +24,9 @@
/obj/item/lightreplacer = 10
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/janitor
name = "Janitor"
jobtype = /datum/job/janitor
diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm
index 35774a530f1..5753238055a 100644
--- a/code/modules/jobs/job_types/lawyer.dm
+++ b/code/modules/jobs/job_types/lawyer.dm
@@ -1,7 +1,7 @@
/datum/job/lawyer
title = "Lawyer"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
supervisors = "the head of personnel"
@@ -22,6 +22,9 @@
family_heirlooms = list(/obj/item/gavelhammer, /obj/item/book/manual/wiki/security_space_law)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/lawyer
name = "Lawyer"
jobtype = /datum/job/lawyer
diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm
index 257f8c280cc..4f736116ed2 100644
--- a/code/modules/jobs/job_types/medical_doctor.dm
+++ b/code/modules/jobs/job_types/medical_doctor.dm
@@ -1,7 +1,7 @@
/datum/job/doctor
title = "Medical Doctor"
department_head = list("Chief Medical Officer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 5
spawn_positions = 3
supervisors = "the chief medical officer"
@@ -31,6 +31,9 @@
/obj/effect/spawner/lootdrop/memeorgans = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/doctor
name = "Medical Doctor"
jobtype = /datum/job/doctor
diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm
index f78712eee02..6d5afeb4453 100644
--- a/code/modules/jobs/job_types/mime.dm
+++ b/code/modules/jobs/job_types/mime.dm
@@ -1,7 +1,7 @@
/datum/job/mime
title = "Mime"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel"
@@ -25,8 +25,18 @@
/obj/item/book/mimery = 1,
)
-/datum/job/mime/after_spawn(mob/living/carbon/human/H, mob/M)
- H.apply_pref_name("mime", M.client)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 0.5 //Why are you speaking
+ voice_of_god_silence_power = 3
+
+
+/datum/job/mime/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ if(!ishuman(spawned))
+ return
+ spawned.apply_pref_name("mime", player_client)
+
/datum/outfit/job/mime
name = "Mime"
diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm
index f5a97f30758..33943c361ca 100644
--- a/code/modules/jobs/job_types/paramedic.dm
+++ b/code/modules/jobs/job_types/paramedic.dm
@@ -1,7 +1,7 @@
/datum/job/paramedic
title = "Paramedic"
department_head = list("Chief Medical Officer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
supervisors = "the chief medical officer"
@@ -30,6 +30,9 @@
/obj/item/reagent_containers/hypospray/medipen/survival/luxury = 5
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/paramedic
name = "Paramedic"
jobtype = /datum/job/paramedic
diff --git a/code/modules/jobs/job_types/personal_ai.dm b/code/modules/jobs/job_types/personal_ai.dm
new file mode 100644
index 00000000000..0f575cbf698
--- /dev/null
+++ b/code/modules/jobs/job_types/personal_ai.dm
@@ -0,0 +1,2 @@
+/datum/job/personal_ai
+ title = ROLE_PAI
diff --git a/code/modules/jobs/job_types/positronic_brain.dm b/code/modules/jobs/job_types/positronic_brain.dm
new file mode 100644
index 00000000000..d51b95dca14
--- /dev/null
+++ b/code/modules/jobs/job_types/positronic_brain.dm
@@ -0,0 +1,2 @@
+/datum/job/positronic_brain
+ title = ROLE_POSITRONIC_BRAIN
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 20265c76e28..035905994c4 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -1,7 +1,7 @@
/datum/job/prisoner
title = "Prisoner"
department_head = list("The Security Team")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 0
spawn_positions = 2
supervisors = "the security team"
@@ -20,6 +20,9 @@
family_heirlooms = list(/obj/item/pen/blue)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/prisoner
name = "Prisoner"
jobtype = /datum/job/prisoner
diff --git a/code/modules/jobs/job_types/psychologist.dm b/code/modules/jobs/job_types/psychologist.dm
index 2effa1e4e10..c9e2c27c485 100644
--- a/code/modules/jobs/job_types/psychologist.dm
+++ b/code/modules/jobs/job_types/psychologist.dm
@@ -1,7 +1,7 @@
/datum/job/psychologist
title = "Psychologist"
department_head = list("Head of Personnel","Chief Medical Officer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel and the chief medical officer"
@@ -26,6 +26,9 @@
/obj/item/gun/syringe = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/psychologist
name = "Psychologist"
jobtype = /datum/job/psychologist
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index dd234145cad..99a2be7e117 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -1,7 +1,7 @@
/datum/job/quartermaster
title = "Quartermaster"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of personnel"
@@ -24,10 +24,8 @@
/obj/item/circuitboard/machine/emitter = 3
)
-/datum/job/quartermaster/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to extreme staffing shortages, newly promoted Acting Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
/datum/outfit/job/quartermaster
name = "Quartermaster"
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index e3efda19756..a0e1ee6b619 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -3,7 +3,7 @@
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
department_head = list("Captain")
head_announce = list("Science")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the captain"
@@ -34,10 +34,14 @@
family_heirlooms = list(/obj/item/toy/plush/slimeplushie)
-/datum/job/research_director/announce(mob/living/carbon/human/H, announce_captaincy = FALSE)
- ..()
- if(announce_captaincy)
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/minor_announce, "Due to staffing shortages, newly promoted Acting Captain [H.real_name] on deck!"))
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+ voice_of_god_power = 1.4 //Command staff has authority
+
+
+/datum/job/research_director/get_captaincy_announcement(mob/living/captain)
+ return "Due to staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+
/datum/outfit/job/rd
name = "Research Director"
diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm
index 17abb5a043b..a954f0696f9 100644
--- a/code/modules/jobs/job_types/roboticist.dm
+++ b/code/modules/jobs/job_types/roboticist.dm
@@ -1,7 +1,7 @@
/datum/job/roboticist
title = "Roboticist"
department_head = list("Research Director")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
supervisors = "the research director"
@@ -27,6 +27,9 @@
family_heirlooms = list(/obj/item/toy/plush/pkplush)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/job/roboticist/New()
. = ..()
family_heirlooms += subtypesof(/obj/item/toy/mecha)
diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm
index 9760686722f..f8910371514 100644
--- a/code/modules/jobs/job_types/scientist.dm
+++ b/code/modules/jobs/job_types/scientist.dm
@@ -1,7 +1,7 @@
/datum/job/scientist
title = "Scientist"
department_head = list("Research Director")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 5
spawn_positions = 3
supervisors = "the research director"
@@ -27,6 +27,9 @@
/obj/item/camera_bug = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/scientist
name = "Scientist"
jobtype = /datum/job/scientist
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 2cb9fd5cfaf..fd12abfae38 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -2,7 +2,7 @@
title = "Security Officer"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
department_head = list("Head of Security")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 5 //Handled in /datum/controller/occupations/proc/setup_officer_positions()
spawn_positions = 5 //Handled in /datum/controller/occupations/proc/setup_officer_positions()
supervisors = "the head of security, and the head of your assigned department (if applicable)"
@@ -34,6 +34,9 @@
/obj/item/melee/baton/boomerang/loaded = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY))
/**
@@ -44,28 +47,28 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S
*/
GLOBAL_LIST_EMPTY(security_officer_distribution)
-/datum/job/security_officer/after_spawn(mob/living/carbon/human/H, mob/M, latejoin = FALSE)
+
+/datum/job/security_officer/after_latejoin_spawn(mob/living/spawning)
. = ..()
+ if(ishuman(spawning))
+ var/department = setup_department(spawning, spawning.client)
+ if(department)
+ announce_latejoin(spawning, department, GLOB.security_officer_distribution)
- var/department
- var/prefered_department = M.client?.prefs?.prefered_security_department
- if (!isnull(prefered_department))
- department = get_my_department(H, prefered_department)
+/// Returns the department this mob was assigned to, if any.
+/datum/job/security_officer/proc/setup_department(mob/living/carbon/human/spawning, client/player_client)
+ var/department = player_client?.prefs?.prefered_security_department
+ if (!isnull(department))
+ department = get_my_department(spawning, department)
- if (latejoin)
- announce_latejoin(H, department, GLOB.security_officer_distribution)
-
- // In the event we're a latejoin, or otherwise aren't in the round-start distributions.
- // This is outside the latejoin check because this should theoretically still run if
- // a player isn't in the distributions, but isn't a late join.
- GLOB.security_officer_distribution[REF(H)] = department
+ // This should theoretically still run if a player isn't in the distributions, but isn't a late join.
+ GLOB.security_officer_distribution[REF(spawning)] = department
var/ears = null
var/accessory = null
var/list/dep_trim = null
var/destination = null
- var/spawn_point = pick(LAZYACCESS(GLOB.department_security_spawns, department))
switch(department)
if(SEC_DEPT_SUPPLY)
@@ -90,40 +93,42 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
accessory = /obj/item/clothing/accessory/armband/science
if(accessory)
- var/obj/item/clothing/under/U = H.w_uniform
- U.attach_accessory(new accessory)
+ var/obj/item/clothing/under/worn_under = spawning.w_uniform
+ worn_under.attach_accessory(new accessory)
+
if(ears)
- if(H.ears)
- qdel(H.ears)
- H.equip_to_slot_or_del(new ears(H),ITEM_SLOT_EARS)
+ if(spawning.ears)
+ qdel(spawning.ears)
+ spawning.equip_to_slot_or_del(new ears(spawning),ITEM_SLOT_EARS)
// If there's a departmental sec trim to apply to the card, overwrite.
if(dep_trim)
- var/obj/item/card/id/worn_id = H.wear_id
+ var/obj/item/card/id/worn_id = spawning.wear_id
SSid_access.apply_trim_to_card(worn_id, dep_trim)
- H.sec_hud_set_ID()
+ spawning.sec_hud_set_ID()
- var/teleport = 0
- if(!CONFIG_GET(flag/sec_start_brig))
- if(destination || spawn_point)
- teleport = 1
- if(teleport)
- var/turf/T
+ var/spawn_point = pick(LAZYACCESS(GLOB.department_security_spawns, department))
+
+ if(!CONFIG_GET(flag/sec_start_brig) && (destination || spawn_point))
if(spawn_point)
- T = get_turf(spawn_point)
- H.Move(T)
+ spawning.Move(get_turf(spawn_point))
else
var/list/possible_turfs = get_area_turfs(destination)
while (length(possible_turfs))
- var/I = rand(1, possible_turfs.len)
- var/turf/target = possible_turfs[I]
- if (H.Move(target))
+ var/random_index = rand(1, length(possible_turfs))
+ var/turf/target = possible_turfs[random_index]
+ if (spawning.Move(target))
break
- possible_turfs.Cut(I,I+1)
- if(department)
- to_chat(M, "You have been assigned to [department]!")
- else
- to_chat(M, "You have not been assigned to any department. Patrol the halls and help where needed.")
+ possible_turfs.Cut(random_index, random_index + 1)
+
+ if(player_client)
+ if(department)
+ to_chat(player_client, "You have been assigned to [department]!")
+ else
+ to_chat(player_client, "You have not been assigned to any department. Patrol the halls and help where needed.")
+
+ return department
+
/datum/job/security_officer/proc/announce_latejoin(
mob/officer,
diff --git a/code/modules/jobs/job_types/servant_golem.dm b/code/modules/jobs/job_types/servant_golem.dm
new file mode 100644
index 00000000000..dfc76e89476
--- /dev/null
+++ b/code/modules/jobs/job_types/servant_golem.dm
@@ -0,0 +1,2 @@
+/datum/job/servant_golem
+ title = ROLE_SERVANT_GOLEM
diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm
index 8b6cd8da628..7d9b391b052 100644
--- a/code/modules/jobs/job_types/shaft_miner.dm
+++ b/code/modules/jobs/job_types/shaft_miner.dm
@@ -1,7 +1,7 @@
/datum/job/shaft_miner
title = "Shaft Miner"
department_head = list("Head of Personnel")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 3
spawn_positions = 3
supervisors = "the quartermaster and the head of personnel"
@@ -19,6 +19,9 @@
family_heirlooms = list(/obj/item/pickaxe/mini, /obj/item/shovel)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/miner
name = "Shaft Miner"
jobtype = /datum/job/shaft_miner
diff --git a/code/modules/jobs/job_types/spawner/ancient_crew.dm b/code/modules/jobs/job_types/spawner/ancient_crew.dm
new file mode 100644
index 00000000000..69084976806
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/ancient_crew.dm
@@ -0,0 +1,3 @@
+/datum/job/ancient_crew
+ title = ROLE_ANCIENT_CREW
+ policy_index = ROLE_ANCIENT_CREW
diff --git a/code/modules/jobs/job_types/spawner/ash_walker.dm b/code/modules/jobs/job_types/spawner/ash_walker.dm
new file mode 100644
index 00000000000..9a57228527f
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/ash_walker.dm
@@ -0,0 +1,3 @@
+/datum/job/ash_walker
+ title = ROLE_ASHWALKER
+ policy_index = ROLE_ASHWALKER
diff --git a/code/modules/jobs/job_types/spawner/beach_bum.dm b/code/modules/jobs/job_types/spawner/beach_bum.dm
new file mode 100644
index 00000000000..b86edb4d121
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/beach_bum.dm
@@ -0,0 +1,3 @@
+/datum/job/beach_bum
+ title = ROLE_BEACH_BUM
+ policy_index = ROLE_BEACH_BUM
diff --git a/code/modules/jobs/job_types/spawner/escaped_prisoner.dm b/code/modules/jobs/job_types/spawner/escaped_prisoner.dm
new file mode 100644
index 00000000000..bb68e5e7f35
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/escaped_prisoner.dm
@@ -0,0 +1,3 @@
+/datum/job/escaped_prisoner
+ title = ROLE_ESCAPED_PRISONER
+ policy_index = ROLE_ESCAPED_PRISONER
diff --git a/code/modules/jobs/job_types/spawner/exile.dm b/code/modules/jobs/job_types/spawner/exile.dm
new file mode 100644
index 00000000000..0baf9e9b1b0
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/exile.dm
@@ -0,0 +1,3 @@
+/datum/job/exile
+ title = ROLE_EXILE
+ policy_index = ROLE_EXILE
diff --git a/code/modules/jobs/job_types/spawner/fugitve_hunter.dm b/code/modules/jobs/job_types/spawner/fugitve_hunter.dm
new file mode 100644
index 00000000000..4589eedd67e
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/fugitve_hunter.dm
@@ -0,0 +1,3 @@
+/datum/job/fugitive_hunter
+ title = ROLE_FUGITIVE_HUNTER
+ policy_index = ROLE_FUGITIVE_HUNTER
diff --git a/code/modules/jobs/job_types/spawner/ghost_role.dm b/code/modules/jobs/job_types/spawner/ghost_role.dm
new file mode 100644
index 00000000000..df49b67302e
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/ghost_role.dm
@@ -0,0 +1,5 @@
+// Generic ghost role.
+
+/datum/job/ghost_role
+ title = ROLE_GHOST_ROLE
+ policy_index = ROLE_GHOST_ROLE
diff --git a/code/modules/jobs/job_types/spawner/hermit.dm b/code/modules/jobs/job_types/spawner/hermit.dm
new file mode 100644
index 00000000000..3c828bcfb63
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/hermit.dm
@@ -0,0 +1,3 @@
+/datum/job/hermit
+ title = ROLE_HERMIT
+ policy_index = ROLE_HERMIT
diff --git a/code/modules/jobs/job_types/spawner/hotel_staff.dm b/code/modules/jobs/job_types/spawner/hotel_staff.dm
new file mode 100644
index 00000000000..3dc3a13571a
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/hotel_staff.dm
@@ -0,0 +1,3 @@
+/datum/job/hotel_staff
+ title = ROLE_HOTEL_STAFF
+ policy_index = ROLE_HOTEL_STAFF
diff --git a/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm b/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm
new file mode 100644
index 00000000000..a22e798274a
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/lavaland_syndicate.dm
@@ -0,0 +1,3 @@
+/datum/job/lavaland_syndicate
+ title = ROLE_LAVALAND_SYNDICATE
+ policy_index = ROLE_LAVALAND_SYNDICATE
diff --git a/code/modules/jobs/job_types/spawner/lifebringer.dm b/code/modules/jobs/job_types/spawner/lifebringer.dm
new file mode 100644
index 00000000000..550cbb3c254
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/lifebringer.dm
@@ -0,0 +1,3 @@
+/datum/job/lifebringer
+ title = ROLE_LIFEBRINGER
+ policy_index = ROLE_LIFEBRINGER
diff --git a/code/modules/jobs/job_types/spawner/maintenance_drone.dm b/code/modules/jobs/job_types/spawner/maintenance_drone.dm
new file mode 100644
index 00000000000..4d46eb44983
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/maintenance_drone.dm
@@ -0,0 +1,3 @@
+/datum/job/maintenance_drone
+ title = ROLE_MAINTENANCE_DRONE
+ policy_index = ROLE_MAINTENANCE_DRONE
diff --git a/code/modules/jobs/job_types/spawner/skeleton.dm b/code/modules/jobs/job_types/spawner/skeleton.dm
new file mode 100644
index 00000000000..9f3c443a0d9
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/skeleton.dm
@@ -0,0 +1,3 @@
+/datum/job/skeleton
+ title = ROLE_SKELETON
+ policy_index = ROLE_SKELETON
diff --git a/code/modules/jobs/job_types/spawner/space_bar_patron.dm b/code/modules/jobs/job_types/spawner/space_bar_patron.dm
new file mode 100644
index 00000000000..1d1af939d87
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/space_bar_patron.dm
@@ -0,0 +1,3 @@
+/datum/job/space_bar_patron
+ title = ROLE_SPACE_BAR_PATRON
+ policy_index = ROLE_SPACE_BAR_PATRON
diff --git a/code/modules/jobs/job_types/spawner/space_bartender.dm b/code/modules/jobs/job_types/spawner/space_bartender.dm
new file mode 100644
index 00000000000..c28bdd6895a
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/space_bartender.dm
@@ -0,0 +1,3 @@
+/datum/job/space_bartender
+ title = ROLE_SPACE_BARTENDER
+ policy_index = ROLE_SPACE_BARTENDER
diff --git a/code/modules/jobs/job_types/spawner/space_doctor.dm b/code/modules/jobs/job_types/spawner/space_doctor.dm
new file mode 100644
index 00000000000..b1199263e03
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/space_doctor.dm
@@ -0,0 +1,3 @@
+/datum/job/space_doctor
+ title = ROLE_SPACE_DOCTOR
+ policy_index = ROLE_SPACE_DOCTOR
diff --git a/code/modules/jobs/job_types/spawner/space_pirate.dm b/code/modules/jobs/job_types/spawner/space_pirate.dm
new file mode 100644
index 00000000000..cfada841c25
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/space_pirate.dm
@@ -0,0 +1,3 @@
+/datum/job/space_pirate
+ title = ROLE_SPACE_PIRATE
+ policy_index = ROLE_SPACE_PIRATE
diff --git a/code/modules/jobs/job_types/spawner/space_syndicate.dm b/code/modules/jobs/job_types/spawner/space_syndicate.dm
new file mode 100644
index 00000000000..8ab88337e50
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/space_syndicate.dm
@@ -0,0 +1,3 @@
+/datum/job/space_syndicate
+ title = ROLE_SPACE_SYNDICATE
+ policy_index = ROLE_SPACE_SYNDICATE
diff --git a/code/modules/jobs/job_types/spawner/spider.dm b/code/modules/jobs/job_types/spawner/spider.dm
new file mode 100644
index 00000000000..d55e6f4490d
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/spider.dm
@@ -0,0 +1,3 @@
+/datum/job/spider
+ title = ROLE_SPIDER
+ policy_index = ROLE_SPIDER
diff --git a/code/modules/jobs/job_types/spawner/syndicate_cybersun.dm b/code/modules/jobs/job_types/spawner/syndicate_cybersun.dm
new file mode 100644
index 00000000000..5deba33fce8
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/syndicate_cybersun.dm
@@ -0,0 +1,3 @@
+/datum/job/syndicate_cybersun
+ title = ROLE_SYNDICATE_CYBERSUN
+ policy_index = ROLE_SYNDICATE_CYBERSUN
diff --git a/code/modules/jobs/job_types/spawner/syndicate_cybersun_captain.dm b/code/modules/jobs/job_types/spawner/syndicate_cybersun_captain.dm
new file mode 100644
index 00000000000..697a5908f46
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/syndicate_cybersun_captain.dm
@@ -0,0 +1,3 @@
+/datum/job/syndicate_cybersun_captain
+ title = ROLE_SYNDICATE_CYBERSUN_CAPTAIN
+ policy_index = ROLE_SYNDICATE_CYBERSUN_CAPTAIN
diff --git a/code/modules/jobs/job_types/spawner/zombie.dm b/code/modules/jobs/job_types/spawner/zombie.dm
new file mode 100644
index 00000000000..10df7b38bb3
--- /dev/null
+++ b/code/modules/jobs/job_types/spawner/zombie.dm
@@ -0,0 +1,3 @@
+/datum/job/zombie
+ title = ROLE_ZOMBIE
+ policy_index = ROLE_ZOMBIE
diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm
index 690090bdba3..3543df807ad 100644
--- a/code/modules/jobs/job_types/station_engineer.dm
+++ b/code/modules/jobs/job_types/station_engineer.dm
@@ -1,7 +1,7 @@
/datum/job/station_engineer
title = "Station Engineer"
department_head = list("Chief Engineer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 5
spawn_positions = 5
supervisors = "the chief engineer"
@@ -30,6 +30,9 @@
/obj/item/clothing/head/hardhat/red/upgraded = 1
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/engineer
name = "Station Engineer"
jobtype = /datum/job/station_engineer
diff --git a/code/modules/jobs/job_types/unassigned.dm b/code/modules/jobs/job_types/unassigned.dm
new file mode 100644
index 00000000000..60fe3fe2ae8
--- /dev/null
+++ b/code/modules/jobs/job_types/unassigned.dm
@@ -0,0 +1,8 @@
+/**
+ * This type is used to indicate a lack of a job.
+ * The mind variable assigned_role will point here by default.
+ * As any other job datum, this is a singleton.
+ **/
+
+/datum/job/unassigned
+ title = "Unassigned"
diff --git a/code/modules/jobs/job_types/virologist.dm b/code/modules/jobs/job_types/virologist.dm
index b5e6a5e64ce..2018aac3691 100644
--- a/code/modules/jobs/job_types/virologist.dm
+++ b/code/modules/jobs/job_types/virologist.dm
@@ -1,7 +1,7 @@
/datum/job/virologist
title = "Virologist"
department_head = list("Chief Medical Officer")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the chief medical officer"
@@ -31,6 +31,8 @@
/obj/item/stack/sheet/mineral/uranium = 5
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
/datum/outfit/job/virologist
name = "Virologist"
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index ba6f7579a7c..d1df59b00a1 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -2,7 +2,7 @@
title = "Warden"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
department_head = list("Head of Security")
- faction = "Station"
+ faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
supervisors = "the head of security"
@@ -35,6 +35,9 @@
/obj/item/storage/box/lethalshot = 5
)
+ job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE
+
+
/datum/outfit/job/warden
name = "Warden"
jobtype = /datum/job/warden
diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm
index 91bc2cca361..68fb986a3c0 100644
--- a/code/modules/jobs/jobs.dm
+++ b/code/modules/jobs/jobs.dm
@@ -99,10 +99,31 @@ GLOBAL_LIST_INIT(exp_jobsmap, list(
EXP_TYPE_SERVICE = list("titles" = service_positions)
))
+// TO DO: Replace this with job datum flags instead.
GLOBAL_LIST_INIT(exp_specialmap, list(
EXP_TYPE_LIVING = list(), // all living mobs
EXP_TYPE_ANTAG = list(),
- EXP_TYPE_SPECIAL = list("Lifebringer","Ash Walker","Exile","Servant Golem","Free Golem","Hermit","Translocated Vet","Escaped Prisoner","Hotel Staff","SuperFriend","Space Syndicate","Ancient Crew","Space Doctor","Space Bartender","Beach Bum","Skeleton","Zombie","Space Bar Patron","Lavaland Syndicate","Maintenance Drone","Ghost Role"), // Ghost roles
+ EXP_TYPE_SPECIAL = list(
+ ROLE_LIFEBRINGER,
+ ROLE_ASHWALKER,
+ ROLE_EXILE,
+ ROLE_SERVANT_GOLEM,
+ ROLE_FREE_GOLEM,
+ ROLE_HERMIT,
+ ROLE_ESCAPED_PRISONER,
+ ROLE_HOTEL_STAFF,
+ ROLE_SPACE_SYNDICATE,
+ ROLE_ANCIENT_CREW,
+ ROLE_SPACE_DOCTOR,
+ ROLE_SPACE_BARTENDER,
+ ROLE_BEACH_BUM,
+ ROLE_SKELETON,
+ ROLE_ZOMBIE,
+ ROLE_SPACE_BAR_PATRON,
+ ROLE_LAVALAND_SYNDICATE,
+ ROLE_MAINTENANCE_DRONE,
+ ROLE_GHOST_ROLE,
+ ), // Ghost roles
EXP_TYPE_GHOST = list() // dead people, observers
))
GLOBAL_PROTECT(exp_jobsmap)
@@ -114,9 +135,9 @@ GLOBAL_PROTECT(exp_specialmap)
if(!job_title)
return list()
- for(var/datum/job/J in SSjob.occupations)
- if(J.title == job_title)
- return J.department_head //this is a list
+ for(var/datum/job/job as anything in SSjob.joinable_occupations)
+ if(job.title == job_title)
+ return job.department_head //this is a list
/proc/get_full_job_name(job)
var/static/regex/cap_expand = new("cap(?!tain)")
diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm
index 7bb1b788975..1ec1571ef29 100644
--- a/code/modules/mafia/controller.dm
+++ b/code/modules/mafia/controller.dm
@@ -580,7 +580,7 @@
mafia_panel.Grant(H)
var/client/player_client = GLOB.directory[role.player_key]
if(player_client)
- player_client.prefs.copy_to(H)
+ player_client.prefs.safe_transfer_prefs_to(H, is_antag = TRUE)
role.body = H
player_role_lookup[H] = role
H.key = role.player_key
diff --git a/code/modules/mob/dead/crew_manifest.dm b/code/modules/mob/dead/crew_manifest.dm
index 079be96594f..46d7a262d87 100644
--- a/code/modules/mob/dead/crew_manifest.dm
+++ b/code/modules/mob/dead/crew_manifest.dm
@@ -40,7 +40,7 @@
list("flag" = DEPARTMENT_SILICON, "name" = "Silicon")
)
- for(var/job in SSjob.occupations)
+ for(var/job in SSjob.joinable_occupations)
// Check if there are additional open positions or if there is no limit
if ((job["total_positions"] > 0 && job["total_positions"] > job["current_positions"]) || (job["total_positions"] == -1))
for(var/department in departments)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 7a276f4b212..8e92857dfb4 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -1,22 +1,22 @@
#define LINKIFY_READY(string, value) "[string]"
/mob/dead/new_player
- var/ready = 0
- var/spawning = 0//Referenced when you want to delete the new_player later on in the code.
-
flags_1 = NONE
-
invisibility = INVISIBILITY_ABSTRACT
-
density = FALSE
stat = DEAD
hud_possible = list()
- var/mob/living/new_character //for instant transfer once the round is set up
-
- //Used to make sure someone doesn't get spammed with messages if they're ineligible for roles
+ var/ready = FALSE
+ /// Referenced when you want to delete the new_player later on in the code.
+ var/spawning = FALSE
+ /// For instant transfer once the round is set up
+ var/mob/living/new_character
+ ///Used to make sure someone doesn't get spammed with messages if they're ineligible for roles.
var/ineligible_for_roles = FALSE
+
+
/mob/dead/new_player/Initialize()
if(client && SSticker.state == GAME_STATE_STARTUP)
var/atom/movable/screen/splash/S = new(client, TRUE, TRUE)
@@ -269,14 +269,14 @@
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
var/datum/job/job = SSjob.GetJob(rank)
- if(!job)
+ if(!(job.job_flags & JOB_NEW_PLAYER_JOINABLE))
return JOB_UNAVAILABLE_GENERIC
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
- if(job.title == "Assistant")
+ if(is_assistant_job(job))
if(isnum(client.player_age) && client.player_age <= 14) //Newbies can always be assistants
return JOB_AVAILABLE
- for(var/datum/job/J in SSjob.occupations)
- if(J && J.current_positions < J.total_positions && J.title != job.title)
+ for(var/datum/job/other_job as anything in SSjob.joinable_occupations)
+ if(other_job.current_positions < other_job.total_positions && other_job != job)
return JOB_UNAVAILABLE_SLOTFULL
else
return JOB_UNAVAILABLE_SLOTFULL
@@ -298,7 +298,6 @@
tgui_alert(usr, get_job_unavailable_error_message(error, rank))
return FALSE
- var/arrivals_docked = TRUE
if(SSshuttle.arrivals)
close_spawn_windows() //In case we get held up
if(SSshuttle.arrivals.damaged && CONFIG_GET(flag/arrivals_shuttle_require_safe_latejoin))
@@ -307,38 +306,42 @@
if(CONFIG_GET(flag/arrivals_shuttle_require_undocked))
SSshuttle.arrivals.RequireUndocked(src)
- arrivals_docked = SSshuttle.arrivals.mode != SHUTTLE_CALL
//Remove the player from the join queue if he was in one and reset the timer
SSticker.queued_players -= src
SSticker.queue_delay = 4
- SSjob.AssignRole(src, rank, 1)
-
- var/mob/living/character = create_character(TRUE) //creates the human and transfers vars and mind
-
- var/is_captain = FALSE
- // If we don't have an assigned cap yet, check if this person qualifies for some from of captaincy.
- if(!SSjob.assigned_captain && ishuman(character) && SSjob.chain_of_command[rank] && !is_banned_from(ckey, list("Captain")))
- is_captain = TRUE
- // If we already have a captain, are they a "Captain" rank and are we allowing multiple of them to be assigned?
- else if(SSjob.always_promote_captain_job && (rank == "Captain"))
- is_captain = TRUE
-
- var/equip = SSjob.EquipRank(character, rank, TRUE, is_captain)
- if(isliving(equip)) //Borgs get borged in the equip, so we need to make sure we handle the new mob.
- character = equip
-
var/datum/job/job = SSjob.GetJob(rank)
- if(job && !job.override_latejoin_spawn(character))
- SSjob.SendToLateJoin(character)
- if(!arrivals_docked)
- var/atom/movable/screen/splash/Spl = new(character.client, TRUE)
- Spl.Fade(TRUE)
- character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25)
+ SSjob.AssignRole(src, job, TRUE)
- character.update_parallax_teleport()
+ mind.late_joiner = TRUE
+ var/atom/destination = mind.assigned_role.get_latejoin_spawn_point()
+ if(!destination)
+ CRASH("Failed to find a latejoin spawn point.")
+ var/mob/living/character = create_character(destination)
+ if(!character)
+ CRASH("Failed to create a character for latejoin.")
+ transfer_character()
+
+ SSjob.EquipRank(character, job, character.client)
+
+ #define IS_NOT_CAPTAIN 0
+ #define IS_ACTING_CAPTAIN 1
+ #define IS_FULL_CAPTAIN 2
+ var/is_captain = IS_NOT_CAPTAIN
+ // If we already have a captain, are they a "Captain" rank and are we allowing multiple of them to be assigned?
+ if(is_captain_job(job))
+ is_captain = IS_FULL_CAPTAIN
+ // If we don't have an assigned cap yet, check if this person qualifies for some from of captaincy.
+ else if(!SSjob.assigned_captain && ishuman(character) && SSjob.chain_of_command[rank] && !is_banned_from(ckey, list("Captain")))
+ is_captain = IS_ACTING_CAPTAIN
+ if(is_captain != IS_NOT_CAPTAIN)
+ minor_announce(job.get_captaincy_announcement(character))
+ SSjob.promote_to_captain(character, is_captain == IS_ACTING_CAPTAIN)
+ #undef IS_NOT_CAPTAIN
+ #undef IS_ACTING_CAPTAIN
+ #undef IS_FULL_CAPTAIN
SSticker.minds += character.mind
character.client.init_verbs() // init verbs for the late join
@@ -434,54 +437,37 @@
popup.set_content(jointext(dat, ""))
popup.open(FALSE) // 0 is passed to open so that it doesn't use the onclose() proc
-/mob/dead/new_player/proc/create_character(transfer_after)
- spawning = 1
+
+/// Creates, assigns and returns the new_character to spawn as. Assumes a valid mind.assigned_role exists.
+/mob/dead/new_player/proc/create_character(atom/destination)
+ spawning = TRUE
close_spawn_windows()
- var/mob/living/carbon/human/H = new(loc)
-
- var/frn = CONFIG_GET(flag/force_random_names)
- if(!frn)
- frn = is_banned_from(ckey, "Appearance")
- if(QDELETED(src))
- return
- if(frn)
- client.prefs.random_character()
- client.prefs.real_name = client.prefs.pref_species.random_name(gender,1)
-
- var/is_antag
- if(mind in GLOB.pre_setup_antags)
- is_antag = TRUE
-
- client.prefs.copy_to(H, antagonist = is_antag, is_latejoiner = transfer_after)
-
- if(GLOB.current_anonymous_theme)//overrides random name because it achieves the same effect and is an admin enabled event tool
- randomize_human(H)
- H.fully_replace_character_name(null, GLOB.current_anonymous_theme.anonymous_name(H))
-
- H.dna.update_dna_identity()
- if(mind)
- if(transfer_after)
- mind.late_joiner = TRUE
- mind.active = FALSE //we wish to transfer the key manually
+ mind.active = FALSE //we wish to transfer the key manually
+ var/mob/living/spawning_mob = mind.assigned_role.get_spawn_mob(client, destination)
+ if(QDELETED(src) || !client)
+ return // Disconnected while checking for the appearance ban.
+ if(!isAI(spawning_mob)) // Unfortunately there's still snowflake AI code out there.
mind.original_character_slot_index = client.prefs.default_slot
- mind.transfer_to(H) //won't transfer key since the mind is not active
- mind.set_original_character(H)
-
- H.name = real_name
+ mind.transfer_to(spawning_mob) //won't transfer key since the mind is not active
+ mind.set_original_character(spawning_mob)
client.init_verbs()
- . = H
+ . = spawning_mob
new_character = .
- if(transfer_after)
- transfer_character()
+
/mob/dead/new_player/proc/transfer_character()
. = new_character
- if(.)
- new_character.key = key //Manually transfer the key to log them in,
- new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC)
- new_character = null
- qdel(src)
+ if(!.)
+ return
+ new_character.key = key //Manually transfer the key to log them in,
+ new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC)
+ var/area/joined_area = get_area(new_character.loc)
+ if(joined_area)
+ joined_area.on_joining_game(new_character)
+ new_character = null
+ qdel(src)
+
/mob/dead/new_player/proc/ViewManifest()
if(!client)
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index a36dc2d2bef..d4b97fc22bc 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -1,16 +1,64 @@
+/// Fully randomizes everything in the character.
+/datum/preferences/proc/randomise_appearance_prefs(randomise_flags = ALL)
+ if(randomise_flags & RANDOMIZE_GENDER)
+ gender = pick(MALE, FEMALE, PLURAL)
+ switch(gender)
+ if(MALE, FEMALE)
+ body_type = gender
+ else
+ body_type = pick(MALE, FEMALE)
+ if(randomise_flags & RANDOMIZE_SPECIES)
+ var/rando_race = GLOB.species_list[pick(GLOB.roundstart_races)]
+ pref_species = new rando_race()
+ if(randomise_flags & RANDOMIZE_NAME)
+ real_name = pref_species.random_name(gender, TRUE)
+ if(randomise_flags & RANDOMIZE_AGE)
+ age = rand(AGE_MIN, AGE_MAX)
+ if(randomise_flags & RANDOMIZE_UNDERWEAR)
+ underwear = random_underwear(gender)
+ if(randomise_flags & RANDOMIZE_UNDERWEAR_COLOR)
+ underwear_color = random_short_color()
+ if(randomise_flags & RANDOMIZE_UNDERSHIRT)
+ undershirt = random_undershirt(gender)
+ if(randomise_flags & RANDOMIZE_SOCKS)
+ socks = random_socks()
+ if(randomise_flags & RANDOMIZE_BACKPACK)
+ backpack = random_backpack()
+ if(randomise_flags & RANDOMIZE_JUMPSUIT_STYLE)
+ jumpsuit_style = pick(GLOB.jumpsuitlist)
+ if(randomise_flags & RANDOMIZE_HAIRSTYLE)
+ hairstyle = random_hairstyle(gender)
+ if(randomise_flags & RANDOMIZE_FACIAL_HAIRSTYLE)
+ facial_hairstyle = random_facial_hairstyle(gender)
+ if(randomise_flags & RANDOMIZE_HAIR_COLOR)
+ hair_color = random_short_color()
+ if(randomise_flags & RANDOMIZE_FACIAL_HAIR_COLOR)
+ facial_hair_color = random_short_color()
+ if(randomise_flags & RANDOMIZE_SKIN_TONE)
+ skin_tone = random_skin_tone()
+ if(randomise_flags & RANDOMIZE_EYE_COLOR)
+ eye_color = random_eye_color()
+ if(randomise_flags & RANDOMIZE_FEATURES)
+ features = random_features()
- //The mob should have a gender you want before running this proc. Will run fine without H
-/datum/preferences/proc/random_character(gender_override, antag_override = FALSE)
+
+/// Randomizes the character according to preferences.
+/datum/preferences/proc/apply_character_randomization_prefs(antag_override = FALSE)
+ if(!randomise[RANDOM_BODY] && !(antag_override && randomise[RANDOM_BODY_ANTAG]))
+ return // Prefs say "no, thank you"
+ if(randomise[RANDOM_GENDER] || antag_override && randomise[RANDOM_GENDER_ANTAG])
+ gender = pick(MALE, FEMALE, PLURAL)
+ switch(gender)
+ if(MALE, FEMALE)
+ body_type = gender
+ else
+ body_type = pick(MALE, FEMALE)
if(randomise[RANDOM_SPECIES])
random_species()
- else if(randomise[RANDOM_NAME])
- real_name = pref_species.random_name(gender,1)
- if(gender_override && !(randomise[RANDOM_GENDER] || randomise[RANDOM_GENDER_ANTAG] && antag_override))
- gender = gender_override
- else
- gender = pick(MALE,FEMALE,PLURAL)
- if(randomise[RANDOM_AGE] || randomise[RANDOM_AGE_ANTAG] && antag_override)
- age = rand(AGE_MIN,AGE_MAX)
+ else if(randomise[RANDOM_NAME] || antag_override && randomise[RANDOM_NAME_ANTAG])
+ real_name = pref_species.random_name(gender, TRUE)
+ if(randomise[RANDOM_AGE] || antag_override && randomise[RANDOM_AGE_ANTAG])
+ age = rand(AGE_MIN, AGE_MAX)
if(randomise[RANDOM_UNDERWEAR])
underwear = random_underwear(gender)
if(randomise[RANDOM_UNDERWEAR_COLOR])
@@ -35,14 +83,8 @@
skin_tone = random_skin_tone()
if(randomise[RANDOM_EYE_COLOR])
eye_color = random_eye_color()
- if(!pref_species)
- var/rando_race = pick(GLOB.roundstart_races)
- pref_species = new rando_race()
features = random_features()
- if(gender in list(MALE, FEMALE))
- body_type = gender
- else
- body_type = pick(MALE, FEMALE)
+
/datum/preferences/proc/random_species()
var/random_species_type = GLOB.species_list[pick(GLOB.roundstart_races)]
@@ -50,21 +92,22 @@
if(randomise[RANDOM_NAME])
real_name = pref_species.random_name(gender,1)
-///Setup a hardcore random character and calculate their hardcore random score
-/datum/preferences/proc/hardcore_random_setup(mob/living/carbon/human/character, antagonist, is_latejoiner)
- var/rand_gender = pick(list(MALE, FEMALE, PLURAL))
- random_character(rand_gender, antagonist)
- select_hardcore_quirks()
- hardcore_survival_score = hardcore_survival_score ** 1.2 //30 points would be about 60 score
- if(is_latejoiner)//prevent them from cheatintg
- hardcore_survival_score = 0
-///Go through all quirks that can be used in hardcore mode and select some based on a random budget.
+///Setup the random hardcore quirks and give the character the new score prize.
+/datum/preferences/proc/hardcore_random_setup(mob/living/carbon/human/character)
+ var/next_hardcore_score = select_hardcore_quirks()
+ character.hardcore_survival_score = next_hardcore_score ** 1.2 //30 points would be about 60 score
+
+
+/**
+ * Goes through all quirks that can be used in hardcore mode and select some based on a random budget.
+ * Returns the new value to be gained with this setup, plus the previously earned score.
+ **/
/datum/preferences/proc/select_hardcore_quirks()
+ . = 0
var/quirk_budget = rand(8, 35)
-
all_quirks = list() //empty it out
var/list/available_hardcore_quirks = SSquirks.hardcore_quirks.Copy()
@@ -102,9 +145,10 @@
all_quirks += initial(picked_quirk.name)
quirk_budget -= available_hardcore_quirks[picked_quirk]
- hardcore_survival_score += available_hardcore_quirks[picked_quirk]
+ . += available_hardcore_quirks[picked_quirk]
available_hardcore_quirks -= picked_quirk
+
/datum/preferences/proc/update_preview_icon()
// Determine what job is marked as 'High' priority, and dress them up as such.
var/datum/job/previewJob
@@ -125,11 +169,11 @@
// Set up the dummy for its photoshoot
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
- copy_to(mannequin, 1, TRUE, TRUE)
+ apply_prefs_to(mannequin, TRUE)
if(previewJob)
mannequin.job = previewJob.title
- previewJob.equip(mannequin, TRUE, preference_source = parent)
+ mannequin.dress_up_as_job(previewJob, TRUE)
COMPILE_OVERLAYS(mannequin)
parent.show_character_previews(new /mutable_appearance(mannequin))
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index eb76d763b8d..d142a821596 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -808,13 +808,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
name = client.prefs.real_name
/mob/dead/observer/proc/set_ghost_appearance()
- if((!client) || (!client.prefs))
+ if(!client?.prefs)
return
- if(client.prefs.randomise[RANDOM_NAME])
- client.prefs.real_name = random_unique_name(gender)
- if(client.prefs.randomise[RANDOM_BODY])
- client.prefs.random_character(gender)
+ client.prefs.apply_character_randomization_prefs()
if(HAIR in client.prefs.pref_species.species_traits)
hairstyle = client.prefs.hairstyle
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index dcb978da98e..33acf48fef2 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -269,7 +269,7 @@
/mob/living/carbon/human/get_blood_id()
if(HAS_TRAIT(src, TRAIT_HUSK))
return
- if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS] && mind && mind.assigned_role == "Clown")
+ if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS] && is_clown_job(mind?.assigned_role))
return /datum/reagent/colorful_reagent
if(dna.species.exotic_blood)
return dna.species.exotic_blood
diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm
index db407626e0a..8133052e3f6 100644
--- a/code/modules/mob/living/brain/brain.dm
+++ b/code/modules/mob/living/brain/brain.dm
@@ -23,7 +23,7 @@
/mob/living/brain/proc/create_dna()
stored_dna = new /datum/dna/stored(src)
if(!stored_dna.species)
- var/rando_race = pick(GLOB.roundstart_races)
+ var/rando_race = GLOB.species_list[pick(GLOB.roundstart_races)]
stored_dna.species = new rando_race()
/mob/living/brain/Destroy()
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index e65e66bf413..273411c5fed 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -21,7 +21,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
///Message sent as a visible message on failure
var/fail_message = "The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?"
///Role assigned to the newly created mind
- var/new_role = "Positronic Brain"
+ var/posibrain_job_path = /datum/job/positronic_brain
///Visible message sent when a player possesses the brain
var/new_mob_message = "The positronic brain chimes quietly."
///Examine message when the posibrain has no mob
@@ -125,7 +125,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
brainmob.timeofhostdeath = C.timeofdeath
brainmob.set_stat(CONSCIOUS)
if(brainmob.mind)
- brainmob.mind.assigned_role = new_role
+ brainmob.mind.set_assigned_role(SSjob.GetJobType(posibrain_job_path))
if(C.mind)
C.mind.transfer_to(brainmob)
@@ -148,7 +148,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
var/policy = get_policy(ROLE_POSIBRAIN)
if(policy)
to_chat(brainmob, policy)
- brainmob.mind.assigned_role = new_role
+ brainmob.mind.set_assigned_role(SSjob.GetJobType(posibrain_job_path))
brainmob.set_stat(CONSCIOUS)
visible_message(new_mob_message)
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index f11d0d76436..fa1f5208906 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -66,7 +66,7 @@ GLOBAL_LIST_EMPTY(dummy_mob_list)
else
//even if target isn't a carbon, if they have a client we can make the
//dummy look like what their human would look like based on their prefs
- target?.client?.prefs?.copy_to(copycat, icon_updates=TRUE, roundstart_checks=FALSE, character_setup=TRUE)
+ target?.client?.prefs?.apply_prefs_to(copycat, TRUE)
return copycat
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index c2938d5eb7a..447c63f902c 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1007,8 +1007,8 @@
/mob/living/carbon/human/get_exp_list(minutes)
. = ..()
- if(mind.assigned_role in SSjob.name_occupations)
- .[mind.assigned_role] = minutes
+ if(mind.assigned_role.title in SSjob.name_occupations)
+ .[mind.assigned_role.title] = minutes
/mob/living/carbon/human/monkeybrain
ai_controller = /datum/ai_controller/monkey
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 4794cbcd465..7fc991fe811 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -663,10 +663,10 @@
///Overrides the point value that the mob is worth
/mob/living/carbon/human/singularity_act()
. = 20
- if(mind)
- if((mind.assigned_role == "Station Engineer") || (mind.assigned_role == "Chief Engineer") )
+ switch(mind?.assigned_role.type)
+ if(/datum/job/chief_engineer, /datum/job/station_engineer)
. = 100
- if(mind.assigned_role == "Clown")
+ if(/datum/job/clown)
. = rand(-1000, 1000)
..() //Called afterwards because getting the mind after getting gibbed is sketchy
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 8b3a4460356..374f4538679 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -238,3 +238,53 @@
destination.undershirt = undershirt
destination.socks = socks
destination.jumpsuit_style = jumpsuit_style
+
+
+/// Fully randomizes everything according to the given flags.
+/mob/living/carbon/human/proc/randomize_human_appearance(randomise_flags = ALL)
+ if(randomise_flags & RANDOMIZE_GENDER)
+ gender = pick(MALE, FEMALE, PLURAL)
+ switch(gender)
+ if(MALE, FEMALE)
+ body_type = gender
+ else
+ body_type = pick(MALE, FEMALE)
+ if(randomise_flags & RANDOMIZE_SPECIES)
+ set_species(GLOB.species_list[pick(GLOB.roundstart_races)], FALSE)
+ if(randomise_flags & RANDOMIZE_NAME)
+ var/new_name = dna.species.random_name(gender, TRUE)
+ name = new_name
+ real_name = new_name
+ if(randomise_flags & RANDOMIZE_AGE)
+ age = rand(AGE_MIN, AGE_MAX)
+ if(randomise_flags & RANDOMIZE_UNDERWEAR)
+ underwear = random_underwear(gender)
+ if(randomise_flags & RANDOMIZE_UNDERWEAR_COLOR)
+ underwear_color = random_short_color()
+ if(randomise_flags & RANDOMIZE_UNDERSHIRT)
+ undershirt = random_undershirt(gender)
+ if(randomise_flags & RANDOMIZE_SOCKS)
+ socks = random_socks()
+ if(randomise_flags & RANDOMIZE_BACKPACK)
+ backpack = random_backpack()
+ if(randomise_flags & RANDOMIZE_JUMPSUIT_STYLE)
+ jumpsuit_style = pick(GLOB.jumpsuitlist)
+ if(randomise_flags & RANDOMIZE_HAIRSTYLE)
+ hairstyle = random_hairstyle(gender)
+ if(randomise_flags & RANDOMIZE_FACIAL_HAIRSTYLE)
+ facial_hairstyle = random_facial_hairstyle(gender)
+ if(randomise_flags & RANDOMIZE_HAIR_COLOR)
+ hair_color = random_short_color()
+ if(randomise_flags & RANDOMIZE_FACIAL_HAIR_COLOR)
+ facial_hair_color = random_short_color()
+ if(randomise_flags & RANDOMIZE_SKIN_TONE)
+ skin_tone = random_skin_tone()
+ if(randomise_flags & RANDOMIZE_EYE_COLOR)
+ eye_color = random_eye_color()
+ var/obj/item/organ/eyes/organ_eyes = getorgan(/obj/item/organ/eyes)
+ if(organ_eyes)
+ if(!initial(organ_eyes.eye_color))
+ organ_eyes.eye_color = eye_color
+ organ_eyes.old_eye_color = eye_color
+ if(randomise_flags & RANDOMIZE_FEATURES)
+ dna.features = random_features()
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index cf06b7cb1c5..85b437a418a 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1150,11 +1150,11 @@ GLOBAL_LIST_EMPTY(roundstart_races)
H.visible_message(span_notice("[H] start putting on [I]..."), span_notice("You start putting on [I]..."))
return do_after(H, I.equip_delay_self, target = H)
-/datum/species/proc/before_equip_job(datum/job/J, mob/living/carbon/human/H)
+
+/// Equips the necessary species-relevant gear before putting on the rest of the uniform.
+/datum/species/proc/pre_equip_species_outfit(datum/job/job, mob/living/carbon/human/equipping, visuals_only = FALSE)
return
-/datum/species/proc/after_equip_job(datum/job/J, mob/living/carbon/human/H)
- H.update_mutant_bodyparts()
/datum/species/proc/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H, delta_time, times_fired)
if(chem.type == exotic_blood)
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index b5415c1e8c0..acb0b7cba40 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -78,11 +78,13 @@
no_protection = TRUE
. = ..()
-/datum/species/plasmaman/before_equip_job(datum/job/J, mob/living/carbon/human/H, visualsOnly = FALSE)
- if(J.plasmaman_outfit)
- H.equipOutfit(J.plasmaman_outfit, visualsOnly)
- H.internal = H.get_item_for_held_index(2)
- H.update_internals_hud_icon(1)
+
+/datum/species/plasmaman/pre_equip_species_outfit(datum/job/job, mob/living/carbon/human/equipping, visuals_only = FALSE)
+ if(job.plasmaman_outfit)
+ equipping.equipOutfit(job.plasmaman_outfit, visuals_only)
+ equipping.internal = equipping.get_item_for_held_index(2)
+ equipping.update_internals_hud_icon(1)
+
/datum/species/plasmaman/random_name(gender,unique,lastname)
if(unique)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 1b65669e6c2..667f0871aa7 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -521,7 +521,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(drunkenness >= 11 && slurring < 5)
slurring += 0.6 * delta_time
- if(mind && (mind.assigned_role == "Scientist" || mind.assigned_role == "Research Director"))
+ if(mind && (is_scientist_job(mind.assigned_role) || is_research_director_job(mind.assigned_role)))
if(SSresearch.science_tech)
if(drunkenness >= 12.9 && drunkenness <= 13.8)
drunkenness = round(drunkenness, 0.01)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index de7c110560b..e10341c1f78 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1975,7 +1975,7 @@
if(mind && mind.special_role && !(mind.datum_flags & DF_VAR_EDITED))
exp_list[mind.special_role] = minutes
- if(mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])
- exp_list[mind.assigned_role] = minutes
+ if(mind.assigned_role.title in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])
+ exp_list[mind.assigned_role.title] = minutes
return exp_list
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 0f2e57680fc..b56ce7538ff 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -49,23 +49,21 @@
//If this body is meant to be a borg controlled by the AI player
if(shell)
make_shell()
+ else
+ //MMI stuff. Held togheter by magic. ~Miauw
+ if(!mmi?.brainmob)
+ mmi = new (src)
+ mmi.brain = new /obj/item/organ/brain(mmi)
+ mmi.brain.organ_flags |= ORGAN_FROZEN
+ mmi.brain.name = "[real_name]'s brain"
+ mmi.name = "[initial(mmi.name)]: [real_name]"
+ mmi.set_brainmob(new /mob/living/brain(mmi))
+ mmi.brainmob.name = src.real_name
+ mmi.brainmob.real_name = src.real_name
+ mmi.brainmob.container = mmi
+ mmi.update_appearance()
+ setup_default_name()
- //MMI stuff. Held togheter by magic. ~Miauw
- else if(!mmi || !mmi.brainmob)
- mmi = new (src)
- mmi.brain = new /obj/item/organ/brain(mmi)
- mmi.brain.organ_flags |= ORGAN_FROZEN
- mmi.brain.name = "[real_name]'s brain"
- mmi.name = "[initial(mmi.name)]: [real_name]"
- mmi.set_brainmob(new /mob/living/brain(mmi))
- mmi.brainmob.name = src.real_name
- mmi.brainmob.real_name = src.real_name
- mmi.brainmob.container = mmi
- mmi.update_appearance()
-
- INVOKE_ASYNC(src, .proc/updatename)
-
- playsound(loc, 'sound/voice/liveagain.ogg', 75, TRUE)
aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
@@ -163,26 +161,39 @@
model.transform_to(model_list[input_model])
-/mob/living/silicon/robot/proc/updatename(client/C)
+
+/// Used to setup the a basic and (somewhat) unique name for the robot.
+/mob/living/silicon/robot/proc/setup_default_name()
+ var/new_name
+ if(GLOB.current_anonymous_theme) //only robotic renames will allow for anything other than the anonymous one
+ new_name = GLOB.current_anonymous_theme.anonymous_ai_name(FALSE)
+ else if(custom_name)
+ new_name = custom_name
+ else
+ new_name = get_standard_name()
+ if(new_name != real_name)
+ fully_replace_character_name(real_name, new_name)
+
+
+/// Updates the borg name taking the client preferences into account.
+/mob/living/silicon/robot/proc/updatename(client/pref_source)
if(shell)
return
- if(!C)
- C = client
+ if(!pref_source)
+ pref_source = client
var/changed_name = ""
- if(custom_name)
- changed_name = custom_name
if(GLOB.current_anonymous_theme) //only robotic renames will allow for anything other than the anonymous one
changed_name = GLOB.current_anonymous_theme.anonymous_ai_name(FALSE)
- if(!changed_name && C && C.prefs.custom_names["cyborg"] != DEFAULT_CYBORG_NAME)
- apply_pref_name("cyborg", C)
+ else if(custom_name)
+ changed_name = custom_name
+ else if(pref_source && pref_source.prefs.custom_names["cyborg"] != DEFAULT_CYBORG_NAME)
+ apply_pref_name("cyborg", pref_source)
return //built in camera handled in proc
- if(!changed_name)
+ else
changed_name = get_standard_name()
- real_name = changed_name
- name = real_name
- if(!QDELETED(builtInCamera))
- builtInCamera.c_tag = real_name //update the camera name too
+ fully_replace_character_name(real_name, changed_name)
+
/mob/living/silicon/robot/proc/get_standard_name()
return "[(designation ? "[designation] " : "")][mmi.braintype]-[ident]"
@@ -670,14 +681,17 @@
. = TRUE
toggle_headlamp(FALSE, TRUE) //This will reenable borg headlamps if doomsday is currently going on still.
+
/mob/living/silicon/robot/fully_replace_character_name(oldname, newname)
- ..()
- if(oldname != real_name)
- notify_ai(RENAME, oldname, newname)
+ . = ..()
+ if(!.)
+ return
+ notify_ai(RENAME, oldname, newname)
if(!QDELETED(builtInCamera))
builtInCamera.c_tag = real_name
custom_name = newname
+
/mob/living/silicon/robot/proc/ResetModel()
SEND_SIGNAL(src, COMSIG_BORG_SAFE_DECONSTRUCT)
uneq_all()
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
index 9b18cd5fa2c..d014051dab9 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
@@ -26,7 +26,7 @@
short_desc = "You are a Maintenance Drone."
flavour_text = "Born out of science, your purpose is to maintain Space Station 13. Maintenance Drones can become the backbone of a healthy station."
important_info = "You MUST read and follow your laws carefully."
- assignedrole = "Maintenance Drone"
+ spawner_job_path = /datum/job/maintenance_drone
/obj/effect/mob_spawn/drone/Initialize()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
index 1d6c85d33cf..6679a856327 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
@@ -65,7 +65,7 @@
short_desc = "You are a Syndicate Maintenance Drone."
flavour_text = "In a prior life, you maintained a Nanotrasen Research Station. Abducted from your home, you were given some upgrades... and now serve an enemy of your former masters."
important_info = ""
- assignedrole = null
+ spawner_job_path = /datum/job/ghost_role
/obj/effect/mob_spawn/drone/syndrone/badass
name = "badass syndrone shell"
@@ -117,7 +117,7 @@
short_desc = "You are a drone on Kosmicheskaya Stantsiya 13."
flavour_text = "Something has brought you out of hibernation, and the station is in gross disrepair."
important_info = "Build, repair, maintain and improve the station that housed you on activation."
- assignedrole = null
+ spawner_job_path = /datum/job/ghost_role
/mob/living/simple_animal/drone/derelict
name = "derelict drone"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 380b10d929e..2faf78e7d6f 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -474,13 +474,12 @@ GLOBAL_DATUM(blackbox, /obj/machinery/smartfridge/black_box)
/obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user)
if(..() && ishuman(user) && !(user in affected_targets))
- var/mob/living/carbon/human/H = user
- for(var/obj/item/W in H)
- H.dropItemToGround(W)
- var/datum/job/clown/C = new /datum/job/clown()
- C.equip(H)
- qdel(C)
- affected_targets.Add(H)
+ var/mob/living/carbon/human/new_clown = user
+ for(var/obj/item/to_strip in new_clown)
+ new_clown.dropItemToGround(to_strip)
+ new_clown.dress_up_as_job(SSjob.GetJobType(/datum/job/clown))
+ affected_targets.Add(new_clown)
+
/obj/machinery/anomalous_crystal/theme_warp //Warps the area you're in to look like a new one
observer_desc = "This crystal warps the area around it to a theme."
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 794b1fc36f7..f86b7815daa 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1052,14 +1052,17 @@
*
* Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn
*/
-/mob/proc/fully_replace_character_name(oldname,newname)
+/mob/proc/fully_replace_character_name(oldname, newname)
if(!newname)
log_message("[src] failed name change from [oldname] as no new name was specified", LOG_OWNERSHIP)
return FALSE
+ if(oldname == newname)
+ log_message("[src] failed name change as the new name was the same as the old one: [oldname]", LOG_OWNERSHIP)
+ return FALSE
log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP)
- log_played_names(ckey,newname)
+ log_played_names(ckey, newname)
real_name = newname
name = newname
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index b5fafd9ed30..1205b60cd89 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -392,14 +392,14 @@
log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.")
message_admins("[key_name_admin(usr)] has offered control of ([ADMIN_LOOKUPFLW(M)]) to ghosts")
var/poll_message = "Do you want to play as [M.real_name]?"
- if(M.mind && M.mind.assigned_role)
- poll_message = "[poll_message] Job:[M.mind.assigned_role]."
- if(M.mind && M.mind.special_role)
- poll_message = "[poll_message] Status:[M.mind.special_role]."
- else if(M.mind)
- var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
- if(A)
- poll_message = "[poll_message] Status:[A.name]."
+ if(M.mind)
+ poll_message = "[poll_message] Job: [M.mind.assigned_role.title]."
+ if(M.mind.special_role)
+ poll_message = "[poll_message] Status: [M.mind.special_role]."
+ else
+ var/datum/antagonist/A = M.mind.has_antag_datum(/datum/antagonist/)
+ if(A)
+ poll_message = "[poll_message] Status: [A.name]."
var/list/mob/dead/observer/candidates = pollCandidatesForMob(poll_message, ROLE_PAI, FALSE, 100, M)
if(LAZYLEN(candidates))
@@ -500,10 +500,11 @@
. = list()
. += "[type]"
if(mind)
- . += mind.assigned_role
+ if(mind.assigned_role.policy_index)
+ . += mind.assigned_role.policy_index
. += mind.special_role //In case there's something special leftover, try to avoid
- for(var/datum/antagonist/A in mind.antag_datums)
- . += "[A.type]"
+ for(var/datum/antagonist/antag_datum as anything in mind.antag_datums)
+ . += "[antag_datum.type]"
///Can the mob see reagents inside of containers?
/mob/proc/can_see_reagents()
diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm
index 1217b49a9e5..91baf04ab2e 100644
--- a/code/modules/mob/mob_transformation_simple.dm
+++ b/code/modules/mob/mob_transformation_simple.dm
@@ -47,7 +47,7 @@
D.updateappearance(mutcolor_update=1, mutations_overlay_update=1)
else if(ishuman(M))
var/mob/living/carbon/human/H = M
- client.prefs.copy_to(H)
+ client.prefs.safe_transfer_prefs_to(H)
H.dna.update_dna_identity()
if(mind && isliving(M))
diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
index cbe9941a95d..12ca0072602 100644
--- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm
+++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm
@@ -37,22 +37,33 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
..()
change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
+
+/datum/computer_file/program/job_management/proc/can_edit_job(datum/job/job)
+ if(!job || !(job.job_flags & JOB_CREW_MEMBER) || (job.title in blacklisted))
+ return FALSE
+ return TRUE
+
+
/datum/computer_file/program/job_management/proc/can_open_job(datum/job/job)
- if(!(job?.title in blacklisted))
- if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
- var/delta = (world.time / 10) - GLOB.time_last_changed_position
- if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
- return TRUE
+ if(!can_edit_job(job))
+ return FALSE
+ if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
+ var/delta = (world.time / 10) - GLOB.time_last_changed_position
+ if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
+ return TRUE
return FALSE
+
/datum/computer_file/program/job_management/proc/can_close_job(datum/job/job)
- if(!(job?.title in blacklisted))
- if(job.total_positions > length(GLOB.player_list) * (max_relative_positions / 100))
- var/delta = (world.time / 10) - GLOB.time_last_changed_position
- if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
- return TRUE
+ if(!can_edit_job(job))
+ return FALSE
+ if(job.total_positions > length(GLOB.player_list) * (max_relative_positions / 100))
+ var/delta = (world.time / 10) - GLOB.time_last_changed_position
+ if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
+ return TRUE
return FALSE
+
/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
@@ -95,7 +106,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
return
var/priority_target = params["target"]
var/datum/job/j = SSjob.GetJob(priority_target)
- if(!j)
+ if(!j || !can_edit_job(j))
return
if(j.total_positions <= j.current_positions)
return
@@ -119,7 +130,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
data["authed"] = authed
var/list/pos = list()
- for(var/j in SSjob.occupations)
+ for(var/j in SSjob.joinable_occupations)
var/datum/job/job = j
if(job.title in blacklisted)
continue
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index 5089aab7ca7..48be4771801 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -228,7 +228,7 @@
clumsy_check = FALSE
/obj/item/gun/ballistic/revolver/reverse/can_trigger_gun(mob/living/user)
- if((HAS_TRAIT(user, TRAIT_CLUMSY)) || (user.mind && user.mind.assigned_role == "Clown"))
+ if(HAS_TRAIT(user, TRAIT_CLUMSY) || is_clown_job(user.mind?.assigned_role))
return ..()
if(process_fire(user, user, FALSE, null, BODY_ZONE_HEAD))
user.visible_message(span_warning("[user] somehow manages to shoot [user.p_them()]self in the face!"), span_userdanger("You somehow shoot yourself in the face! How the hell?!"))
diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm
index 29fc7b58eb8..89854f7c203 100644
--- a/code/modules/projectiles/pins.dm
+++ b/code/modules/projectiles/pins.dm
@@ -147,7 +147,7 @@
if(HAS_TRAIT(user, TRAIT_CLUMSY)) //clumsy
return TRUE
if(user.mind)
- if(user.mind.assigned_role == "Clown") //traitor clowns can use this, even though they're technically not clumsy
+ if(is_clown_job(user.mind.assigned_role)) //traitor clowns can use this, even though they're technically not clumsy
return TRUE
if(user.mind.has_antag_datum(/datum/antagonist/nukeop/clownop)) //clown ops aren't clumsy by default and technically don't have an assigned role of "Clown", but come on, they're basically clowns
return TRUE
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 4476e93737f..49643a50136 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -260,7 +260,7 @@
new_mob = new path(M.loc)
if("humanoid")
- new_mob = new /mob/living/carbon/human(M.loc)
+ var/mob/living/carbon/human/new_human = new (M.loc)
if(prob(50))
var/list/chooseable_races = list()
@@ -269,17 +269,16 @@
if(initial(S.changesource_flags) & WABBAJACK)
chooseable_races += speciestype
- if(chooseable_races.len)
- new_mob.set_species(pick(chooseable_races))
+ if(length(chooseable_races))
+ new_human.set_species(pick(chooseable_races))
- var/datum/preferences/A = new() //Randomize appearance for the human
- A.copy_to(new_mob, icon_updates=0)
-
- var/mob/living/carbon/human/H = new_mob
- H.update_body()
- H.update_hair()
- H.update_body_parts()
- H.dna.update_dna_identity()
+ // Randomize everything but the species, which was already handled above.
+ new_human.randomize_human_appearance(~RANDOMIZE_SPECIES)
+ new_human.update_body()
+ new_human.update_hair()
+ new_human.update_body_parts()
+ new_human.dna.update_dna_identity()
+ new_mob = new_human
if(!new_mob)
return
@@ -575,8 +574,8 @@
/obj/projectile/magic/wipe/proc/possession_test(mob/living/carbon/M)
var/datum/brain_trauma/special/imaginary_friend/trapped_owner/trauma = M.gain_trauma(/datum/brain_trauma/special/imaginary_friend/trapped_owner)
var/poll_message = "Do you want to play as [M.real_name]?"
- if(M.mind && M.mind.assigned_role)
- poll_message = "[poll_message] Job:[M.mind.assigned_role]."
+ if(M.mind)
+ poll_message = "[poll_message] Job:[M.mind.assigned_role.title]."
if(M.mind && M.mind.special_role)
poll_message = "[poll_message] Status:[M.mind.special_role]."
else if(M.mind)
diff --git a/code/modules/religion/religion_structures.dm b/code/modules/religion/religion_structures.dm
index c58238b90f6..ef2289de54d 100644
--- a/code/modules/religion/religion_structures.dm
+++ b/code/modules/religion/religion_structures.dm
@@ -67,7 +67,7 @@
/obj/structure/altar_of_gods/proc/get_chaplains()
var/chaplain_string = ""
for(var/mob/living/carbon/human/potential_chap in GLOB.player_list)
- if(potential_chap.key && potential_chap.mind?.assigned_role == "Chaplain")
+ if(potential_chap.key && is_chaplain_job(potential_chap.mind?.assigned_role))
if(chaplain_string)
chaplain_string += ", "
chaplain_string += "[potential_chap] ([potential_chap.key])"
diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm
index b52bf07243f..67a7261c104 100644
--- a/code/modules/ruins/lavaland_ruin_code.dm
+++ b/code/modules/ruins/lavaland_ruin_code.dm
@@ -120,7 +120,7 @@
flavour_text = "Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. Continue your research as best you can, and try to keep a low profile."
important_info = "The base is rigged with explosives, DO NOT abandon it or let it fall into enemy hands!"
outfit = /datum/outfit/lavaland_syndicate
- assignedrole = "Lavaland Syndicate"
+ spawner_job_path = /datum/job/lavaland_syndicate
/obj/effect/mob_spawn/human/lavaland_syndicate/special(mob/living/new_spawn)
new_spawn.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND)
diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
index fc645e3158d..e75359d7536 100644
--- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
@@ -492,7 +492,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
name = "Doctor Hilbert"
mob_name = "Doctor Hilbert"
mob_gender = "male"
- assignedrole = null
+ spawner_job_path = /datum/job/ghost_role
ghost_usable = FALSE
oxy_damage = 500
mob_species = /datum/species/skeleton
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 34416d6f737..25f765b81d0 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -268,7 +268,7 @@
// the shuttle system doesn't know who these people are, but they
// must be important, surely
var/obj/item/card/id/ID = new(src)
- var/datum/job/J = pick(SSjob.occupations)
+ var/datum/job/J = pick(SSjob.joinable_occupations)
ID.registered_name = S.random_name(pick(MALE, FEMALE))
ID.assignment = J.title
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index c049371edea..5764c990e5f 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -215,8 +215,8 @@
/obj/structure/table/wood/bar/proc/is_barstaff(mob/living/user)
. = FALSE
if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.mind && H.mind.assigned_role == "Bartender")
+ var/mob/living/carbon/human/human_user = user
+ if(is_bartender_job(human_user.mind?.assigned_role))
return TRUE
var/obj/item/card/id/ID = user.get_idcard(FALSE)
diff --git a/code/modules/unit_tests/security_officer_distribution.dm b/code/modules/unit_tests/security_officer_distribution.dm
index 0b2ad98b878..c5906feea6a 100644
--- a/code/modules/unit_tests/security_officer_distribution.dm
+++ b/code/modules/unit_tests/security_officer_distribution.dm
@@ -59,7 +59,7 @@
var/mob/living/carbon/human/new_character = allocate(/mob/living/carbon/human)
new_character.mind_initialize()
- new_character.mind.assigned_role = "Security Officer"
+ new_character.mind.set_assigned_role(SSjob.GetJobType(/datum/job/security_officer))
new_player.new_character = new_character
new_player.mock_client = mock_client
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
index 8aed6dc52b3..78c125daed8 100644
--- a/code/modules/vehicles/cars/clowncar.dm
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -39,7 +39,7 @@
/obj/vehicle/sealed/car/clowncar/auto_assign_occupant_flags(mob/M)
if(ishuman(M))
var/mob/living/carbon/human/H = M
- if(H.mind?.assigned_role == "Clown") //Ensures only clowns can drive the car. (Including more at once)
+ if(is_clown_job(H.mind?.assigned_role)) //Ensures only clowns can drive the car. (Including more at once)
add_control_flags(H, VEHICLE_CONTROL_DRIVE)
RegisterSignal(H, COMSIG_MOB_CLICKON, .proc/fire_cannon_at)
M.log_message("has entered [src] as a possible driver", LOG_ATTACK)
diff --git a/tgstation.dme b/tgstation.dme
index cb93d1b161c..e19bda9ea86 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2372,6 +2372,7 @@
#include "code\modules\jobs\job_types\curator.dm"
#include "code\modules\jobs\job_types\cyborg.dm"
#include "code\modules\jobs\job_types\detective.dm"
+#include "code\modules\jobs\job_types\free_golem.dm"
#include "code\modules\jobs\job_types\geneticist.dm"
#include "code\modules\jobs\job_types\head_of_personnel.dm"
#include "code\modules\jobs\job_types\head_of_security.dm"
@@ -2380,6 +2381,8 @@
#include "code\modules\jobs\job_types\medical_doctor.dm"
#include "code\modules\jobs\job_types\mime.dm"
#include "code\modules\jobs\job_types\paramedic.dm"
+#include "code\modules\jobs\job_types\personal_ai.dm"
+#include "code\modules\jobs\job_types\positronic_brain.dm"
#include "code\modules\jobs\job_types\prisoner.dm"
#include "code\modules\jobs\job_types\psychologist.dm"
#include "code\modules\jobs\job_types\quartermaster.dm"
@@ -2387,10 +2390,53 @@
#include "code\modules\jobs\job_types\roboticist.dm"
#include "code\modules\jobs\job_types\scientist.dm"
#include "code\modules\jobs\job_types\security_officer.dm"
+#include "code\modules\jobs\job_types\servant_golem.dm"
#include "code\modules\jobs\job_types\shaft_miner.dm"
#include "code\modules\jobs\job_types\station_engineer.dm"
+#include "code\modules\jobs\job_types\unassigned.dm"
#include "code\modules\jobs\job_types\virologist.dm"
#include "code\modules\jobs\job_types\warden.dm"
+#include "code\modules\jobs\job_types\antagonists\abductor_agent.dm"
+#include "code\modules\jobs\job_types\antagonists\abductor_scientist.dm"
+#include "code\modules\jobs\job_types\antagonists\abductor_solo.dm"
+#include "code\modules\jobs\job_types\antagonists\clown_operative.dm"
+#include "code\modules\jobs\job_types\antagonists\lone_operative.dm"
+#include "code\modules\jobs\job_types\antagonists\morph.dm"
+#include "code\modules\jobs\job_types\antagonists\nightmare.dm"
+#include "code\modules\jobs\job_types\antagonists\nuclear_operative.dm"
+#include "code\modules\jobs\job_types\antagonists\revenant.dm"
+#include "code\modules\jobs\job_types\antagonists\sentient_disease.dm"
+#include "code\modules\jobs\job_types\antagonists\slaughter_demon.dm"
+#include "code\modules\jobs\job_types\antagonists\space_dragon.dm"
+#include "code\modules\jobs\job_types\antagonists\space_ninja.dm"
+#include "code\modules\jobs\job_types\antagonists\space_wizard.dm"
+#include "code\modules\jobs\job_types\antagonists\wizard_apprentice.dm"
+#include "code\modules\jobs\job_types\antagonists\xenomorph.dm"
+#include "code\modules\jobs\job_types\ert\ert_generic.dm"
+#include "code\modules\jobs\job_types\event\fugitive.dm"
+#include "code\modules\jobs\job_types\event\santa.dm"
+#include "code\modules\jobs\job_types\spawner\ancient_crew.dm"
+#include "code\modules\jobs\job_types\spawner\ash_walker.dm"
+#include "code\modules\jobs\job_types\spawner\beach_bum.dm"
+#include "code\modules\jobs\job_types\spawner\escaped_prisoner.dm"
+#include "code\modules\jobs\job_types\spawner\exile.dm"
+#include "code\modules\jobs\job_types\spawner\fugitve_hunter.dm"
+#include "code\modules\jobs\job_types\spawner\ghost_role.dm"
+#include "code\modules\jobs\job_types\spawner\hermit.dm"
+#include "code\modules\jobs\job_types\spawner\hotel_staff.dm"
+#include "code\modules\jobs\job_types\spawner\lavaland_syndicate.dm"
+#include "code\modules\jobs\job_types\spawner\lifebringer.dm"
+#include "code\modules\jobs\job_types\spawner\maintenance_drone.dm"
+#include "code\modules\jobs\job_types\spawner\skeleton.dm"
+#include "code\modules\jobs\job_types\spawner\space_bar_patron.dm"
+#include "code\modules\jobs\job_types\spawner\space_bartender.dm"
+#include "code\modules\jobs\job_types\spawner\space_doctor.dm"
+#include "code\modules\jobs\job_types\spawner\space_pirate.dm"
+#include "code\modules\jobs\job_types\spawner\space_syndicate.dm"
+#include "code\modules\jobs\job_types\spawner\spider.dm"
+#include "code\modules\jobs\job_types\spawner\syndicate_cybersun.dm"
+#include "code\modules\jobs\job_types\spawner\syndicate_cybersun_captain.dm"
+#include "code\modules\jobs\job_types\spawner\zombie.dm"
#include "code\modules\keybindings\bindings_atom.dm"
#include "code\modules\keybindings\bindings_client.dm"
#include "code\modules\keybindings\focus.dm"