diff --git a/.editorconfig b/.editorconfig
index 471170c449e..59f09ca6fd9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,5 +16,9 @@ indent_style = space
[*.md]
trim_trailing_whitespace = false
+[*.sql]
+indent_style = space
+indent_size = 2
+
[Dockerfile]
indent_style = space
diff --git a/SQL/database_changelog.md b/SQL/database_changelog.md
index 42bb72672a2..12b3f4cbad8 100644
--- a/SQL/database_changelog.md
+++ b/SQL/database_changelog.md
@@ -22,4 +22,8 @@ Database migrated to DBCore. Schema will start at MAJOR 1, MINOR 1.
### 11/21/22 - 1.2 - silicons
-persist_keyed_strings added.
+`persist_keyed_strings` added.
+
+### 2/4/23 - 1.3 - silicons
+
+`character` table added.
diff --git a/SQL/database_schema.sql b/SQL/database_schema.sql
index 0c40797a67f..b99026520e8 100644
--- a/SQL/database_schema.sql
+++ b/SQL/database_schema.sql
@@ -16,6 +16,32 @@ CREATE TABLE IF NOT EXISTS `%_PREFIX_%schema_revision` (
PRIMARY KEY (`major`, `minor`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+-- Player lookup table --
+-- Used to look up player ID from ckey, as well as --
+-- store last computerid/ip for a ckey. --
+CREATE TABLE IF NOT EXISTS `%_PREFIX_%player_lookup` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `ckey` varchar(32) NOT NULL,
+ `firstseen` datetime NOT NULL,
+ `lastseen` datetime NOT NULL,
+ `ip` varchar(18) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player',
+ `playerid` int(11),
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `ckey` (`ckey`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- Primary player table --
+-- Allows for one-to-many player-ckey association. --
+CREATE TABLE IF NOT EXISTS `%_PREFIX_%player` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `flags` int(24) NOT NULL DEFAULT 0,
+ `firstseen` datetime NOT NULL DEFAULT Now(),
+ `lastseen` datetime NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
--
-- Table structure for table `round`
--
@@ -31,6 +57,47 @@ CREATE TABLE IF NOT EXISTS `%_PREFIX_%round` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+-- Connection log --
+-- Logs all connections to the server. --
+CREATE TABLE IF NOT EXISTS `%_PREFIX_%connection_log` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `datetime` datetime NOT NULL,
+ `serverip` varchar(16) NOT NULL,
+ `ckey` varchar(32) NOT NULL,
+ `ip` varchar(16) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ PRIMARY KEY(`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- Persistence - Object Storage: Strings --
+CREATE TABLE IF NOT EXISTS `%_PREFIX_%persist_keyed_strings` (
+ `created` DATETIME NOT NULL DEFAULT Now(),
+ `modified` DATETIME NOT NULL,
+ `key` VARCHAR(64) NOT NULL,
+ `value` MEDIUMTEXT NULL,
+ `group` VARCHAR(64) NOT NULL,
+ `revision` INT(11) NOT NULL,
+ PRIMARY KEY(`key`, `group`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- /datum/character - Character Table --
+CREATE TABLE IF NOT EXISTS `%_PREFIX_%character` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `created` DATETIME NOT NULL DEFAULT Now(),
+ `last_played` DATETIME NULL,
+ `last_persisted` DATETIME NULL,
+ `playerid` INT(11) NOT NULL,
+ `canonical_name` VARCHAR(128) NOT NULL,
+ `persist_data` MEDIUMTEXT NULL,
+ `character_type` VARCHAR(64) NOT NULL,
+ PRIMARY KEY(`id`),
+ CONSTRAINT `character_has_player` FOREIGN KEY (`playerid`)
+ REFERENCES `%_PREFIX_%player` (`id`)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+ UNIQUE (`playerid`, `canonical_name`, `character_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
CREATE TABLE IF NOT EXISTS `%_PREFIX_%admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
@@ -86,29 +153,6 @@ CREATE TABLE IF NOT EXISTS `%_PREFIX_%feedback` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
-CREATE TABLE IF NOT EXISTS `%_PREFIX_%player_lookup` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `ckey` varchar(32) NOT NULL,
- `firstseen` datetime NOT NULL,
- `lastseen` datetime NOT NULL,
- `ip` varchar(18) NOT NULL,
- `computerid` varchar(32) NOT NULL,
- `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player',
- `playerid` int(11),
- PRIMARY KEY (`id`),
- UNIQUE KEY `ckey` (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
---- Primary player table ---
---- Allows for one-to-many player-ckey association. ---
-CREATE TABLE IF NOT EXISTS `%_PREFIX_%player` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `flags` int(24) NOT NULL DEFAULT 0,
- `firstseen` datetime NOT NULL DEFAULT Now(),
- `lastseen` datetime NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
CREATE TABLE IF NOT EXISTS `%_PREFIX_%poll_option` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pollid` int(11) NOT NULL,
@@ -226,25 +270,3 @@ CREATE TABLE IF NOT EXISTS `%_PREFIX_%population` (
`time` DATETIME NOT NULL ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
-CREATE TABLE IF NOT EXISTS `%_PREFIX_%connection_log` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `datetime` datetime NOT NULL,
- `serverip` varchar(16) NOT NULL,
- `ckey` varchar(32) NOT NULL,
- `ip` varchar(16) NOT NULL,
- `computerid` varchar(32) NOT NULL,
- PRIMARY KEY(`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
-/* Object Persistence Store */
-/* These are not multi-server synchronization safe! It is expected that you DO NOT share these databases */
-CREATE TABLE IF NOT EXISTS `%_PREFIX_%persist_keyed_strings` (
- `created` DATETIME NOT NULL DEFAULT Now(),
- `modified` DATETIME NOT NULL,
- `key` VARCHAR(64) NOT NULL,
- `value` MEDIUMTEXT NULL,
- `group` VARCHAR(64) NOT NULL,
- `revision` INT(11) NOT NULL,
- PRIMARY KEY(`key`, `group`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
diff --git a/SQL/database_schema_prefixed.sql b/SQL/database_schema_prefixed.sql
index deca75a12e6..d09dac1427e 100644
--- a/SQL/database_schema_prefixed.sql
+++ b/SQL/database_schema_prefixed.sql
@@ -2,7 +2,7 @@
* make sure to bump schema version and mark changes in database_changelog.md!
*
* default prefix is rp_
- * find replace case sensitive %_PREFIX_%
+ * find replace case sensitive rp_
* PRESERVE ANY vr_'s! We need to replace those tables and features at some point, that's how we konw.
**/
@@ -16,6 +16,32 @@ CREATE TABLE IF NOT EXISTS `rp_schema_revision` (
PRIMARY KEY (`major`, `minor`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+-- Player lookup table --
+-- Used to look up player ID from ckey, as well as --
+-- store last computerid/ip for a ckey. --
+CREATE TABLE IF NOT EXISTS `rp_player_lookup` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `ckey` varchar(32) NOT NULL,
+ `firstseen` datetime NOT NULL,
+ `lastseen` datetime NOT NULL,
+ `ip` varchar(18) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player',
+ `playerid` int(11),
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `ckey` (`ckey`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- Primary player table --
+-- Allows for one-to-many player-ckey association. --
+CREATE TABLE IF NOT EXISTS `rp_player` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `flags` int(24) NOT NULL DEFAULT 0,
+ `firstseen` datetime NOT NULL DEFAULT Now(),
+ `lastseen` datetime NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
--
-- Table structure for table `round`
--
@@ -31,6 +57,47 @@ CREATE TABLE IF NOT EXISTS `rp_round` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+-- Connection log --
+-- Logs all connections to the server. --
+CREATE TABLE IF NOT EXISTS `rp_connection_log` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `datetime` datetime NOT NULL,
+ `serverip` varchar(16) NOT NULL,
+ `ckey` varchar(32) NOT NULL,
+ `ip` varchar(16) NOT NULL,
+ `computerid` varchar(32) NOT NULL,
+ PRIMARY KEY(`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- Persistence - Object Storage: Strings --
+CREATE TABLE IF NOT EXISTS `rp_persist_keyed_strings` (
+ `created` DATETIME NOT NULL DEFAULT Now(),
+ `modified` DATETIME NOT NULL,
+ `key` VARCHAR(64) NOT NULL,
+ `value` MEDIUMTEXT NULL,
+ `group` VARCHAR(64) NOT NULL,
+ `revision` INT(11) NOT NULL,
+ PRIMARY KEY(`key`, `group`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- /datum/character - Character Table --
+CREATE TABLE IF NOT EXISTS `rp_character` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `created` DATETIME NOT NULL DEFAULT Now(),
+ `last_played` DATETIME NULL,
+ `last_persisted` DATETIME NULL,
+ `playerid` INT(11) NOT NULL,
+ `canonical_name` VARCHAR(128) NOT NULL,
+ `persist_data` MEDIUMTEXT NULL,
+ `character_type` VARCHAR(64) NOT NULL,
+ PRIMARY KEY(`id`),
+ CONSTRAINT `character_has_player` FOREIGN KEY (`playerid`)
+ REFERENCES `rp_player` (`id`)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+ UNIQUE (`playerid`, `canonical_name`, `character_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
CREATE TABLE IF NOT EXISTS `rp_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ckey` varchar(32) NOT NULL,
@@ -86,27 +153,6 @@ CREATE TABLE IF NOT EXISTS `rp_feedback` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
-CREATE TABLE IF NOT EXISTS `rp_player_lookup` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `ckey` varchar(32) NOT NULL,
- `firstseen` datetime NOT NULL,
- `lastseen` datetime NOT NULL,
- `ip` varchar(18) NOT NULL,
- `computerid` varchar(32) NOT NULL,
- `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player',
- `playerid` int(11),
- PRIMARY KEY (`id`),
- UNIQUE KEY `ckey` (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
-CREATE TABLE IF NOT EXISTS `rp_player` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `flags` int(24) NOT NULL DEFAULT 0,
- `firstseen` datetime NOT NULL DEFAULT Now(),
- `lastseen` datetime NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
CREATE TABLE IF NOT EXISTS `rp_poll_option` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pollid` int(11) NOT NULL,
@@ -224,25 +270,3 @@ CREATE TABLE IF NOT EXISTS `rp_population` (
`time` DATETIME NOT NULL ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
-CREATE TABLE IF NOT EXISTS `rp_connection_log` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `datetime` datetime NOT NULL,
- `serverip` varchar(16) NOT NULL,
- `ckey` varchar(32) NOT NULL,
- `ip` varchar(16) NOT NULL,
- `computerid` varchar(32) NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
-/* Object Persistence Store */
-/* These are not multi-server synchronization safe! It is expected that you DO NOT share these databases */
-CREATE TABLE IF NOT EXISTS `rp_persist_keyed_strings` (
- `created` DATETIME NOT NULL DEFAULT Now(),
- `modified` DATETIME NOT NULL,
- `key` VARCHAR(64) NOT NULL,
- `value` MEDIUMTEXT NULL,
- `group` VARCHAR(64) NOT NULL,
- `revision` INT(11) NOT NULL,
- PRIMARY KEY(`key`, `group`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
diff --git a/citadel.dme b/citadel.dme
index 73712768a1b..1006aa1dc72 100644
--- a/citadel.dme
+++ b/citadel.dme
@@ -67,7 +67,6 @@
#include "code\__DEFINES\movespeed_modification.dm"
#include "code\__DEFINES\objects.dm"
#include "code\__DEFINES\parameters_world.dm"
-#include "code\__DEFINES\persistence.dm"
#include "code\__DEFINES\planets.dm"
#include "code\__DEFINES\plants.dm"
#include "code\__DEFINES\qdel.dm"
@@ -193,6 +192,7 @@
#include "code\__DEFINES\languages\translation.dm"
#include "code\__DEFINES\mapping\multiz.dm"
#include "code\__DEFINES\misc\message_ranges.dm"
+#include "code\__DEFINES\mobs\characteristics.dm"
#include "code\__DEFINES\mobs\grab.dm"
#include "code\__DEFINES\mobs\health.dm"
#include "code\__DEFINES\mobs\intent.dm"
@@ -443,6 +443,7 @@
#include "code\controllers\configuration\entries\lobby.dm"
#include "code\controllers\configuration\entries\logging.dm"
#include "code\controllers\configuration\entries\resources.dm"
+#include "code\controllers\configuration\entries\skills.dm"
#include "code\controllers\configuration\entries\urls.dm"
#include "code\controllers\configuration_old\configuration.dm"
#include "code\controllers\configuration_old\configuration_vr.dm"
@@ -485,6 +486,7 @@
#include "code\controllers\subsystem\planets.dm"
#include "code\controllers\subsystem\plants.dm"
#include "code\controllers\subsystem\radiation.dm"
+#include "code\controllers\subsystem\repository.dm"
#include "code\controllers\subsystem\server_maint.dm"
#include "code\controllers\subsystem\shuttles.dm"
#include "code\controllers\subsystem\sonar.dm"
@@ -527,6 +529,7 @@
#include "code\controllers\subsystem\persistence\_persistence.dm"
#include "code\controllers\subsystem\persistence\bunker.dm"
#include "code\controllers\subsystem\persistence\objects.dm"
+#include "code\controllers\subsystem\persistence\objects\characters.dm"
#include "code\controllers\subsystem\persistence\objects\unique_string.dm"
#include "code\controllers\subsystem\processing\chemistry.dm"
#include "code\controllers\subsystem\processing\circuits.dm"
@@ -562,6 +565,7 @@
#include "code\datums\position_point_vector.dm"
#include "code\datums\profile.dm"
#include "code\datums\progressbar.dm"
+#include "code\datums\prototype.dm"
#include "code\datums\radiation_wave.dm"
#include "code\datums\recipe.dm"
#include "code\datums\soul_link.dm"
@@ -2063,16 +2067,15 @@
#include "code\modules\catalogue\cataloguer_vr.dm"
#include "code\modules\client\client procs_vr.dm"
#include "code\modules\client\client.dm"
+#include "code\modules\client\client_data.dm"
#include "code\modules\client\client_procs.dm"
-#include "code\modules\client\data.dm"
-#include "code\modules\client\database.dm"
-#include "code\modules\client\player_details.dm"
+#include "code\modules\client\player_data.dm"
#include "code\modules\client\spam_prevention.dm"
#include "code\modules\client\statpanel.dm"
#include "code\modules\client\throttling.dm"
#include "code\modules\client\ui_style.dm"
#include "code\modules\client\viewport.dm"
-#include "code\modules\client\winset_wrappers.dm"
+#include "code\modules\client\wrappers.dm"
#include "code\modules\client\verbs\minimap.dm"
#include "code\modules\client\verbs\ooc.dm"
#include "code\modules\client\verbs\panic_bunker_player.dm"
@@ -2512,9 +2515,9 @@
#include "code\modules\integrated_electronics\subtypes\time.dm"
#include "code\modules\integrated_electronics\subtypes\trig.dm"
#include "code\modules\integrated_electronics\~defines\~defines.dm"
-#include "code\modules\jobs\_alt_title.dm"
#include "code\modules\jobs\access.dm"
#include "code\modules\jobs\access_datum.dm"
+#include "code\modules\jobs\alt_title.dm"
#include "code\modules\jobs\department.dm"
#include "code\modules\jobs\job.dm"
#include "code\modules\jobs\jobs.dm"
@@ -2870,7 +2873,6 @@
#include "code\modules\mob\perspective.dm"
#include "code\modules\mob\say.dm"
#include "code\modules\mob\say_vr.dm"
-#include "code\modules\mob\skillset.dm"
#include "code\modules\mob\status_procs.dm"
#include "code\modules\mob\throwing.dm"
#include "code\modules\mob\transform_procs.dm"
@@ -2885,6 +2887,24 @@
#include "code\modules\mob\_modifiers\traits.dm"
#include "code\modules\mob\_modifiers\traits_phobias.dm"
#include "code\modules\mob\_modifiers\unholy.dm"
+#include "code\modules\mob\characteristics\helpers.dm"
+#include "code\modules\mob\characteristics\holder.dm"
+#include "code\modules\mob\characteristics\mob.dm"
+#include "code\modules\mob\characteristics\presets.dm"
+#include "code\modules\mob\characteristics\skill.dm"
+#include "code\modules\mob\characteristics\stat.dm"
+#include "code\modules\mob\characteristics\talent.dm"
+#include "code\modules\mob\characteristics\ui.dm"
+#include "code\modules\mob\characteristics\skills\engineering.dm"
+#include "code\modules\mob\characteristics\skills\logistics.dm"
+#include "code\modules\mob\characteristics\skills\medical.dm"
+#include "code\modules\mob\characteristics\skills\misc.dm"
+#include "code\modules\mob\characteristics\skills\science.dm"
+#include "code\modules\mob\characteristics\skills\security.dm"
+#include "code\modules\mob\characteristics\skills\service.dm"
+#include "code\modules\mob\characteristics\skills\voidcraft.dm"
+#include "code\modules\mob\characteristics\stats\gaming.dm"
+#include "code\modules\mob\characteristics\talents\placeholder.dm"
#include "code\modules\mob\dead\death.dm"
#include "code\modules\mob\dead\observer\free_vr.dm"
#include "code\modules\mob\dead\observer\login.dm"
@@ -3679,7 +3699,6 @@
#include "code\modules\preferences\preference_setup\loadout\loadout_utility.dm"
#include "code\modules\preferences\preference_setup\loadout\loadout_xeno.dm"
#include "code\modules\preferences\preference_setup\occupation\occupation.dm"
-#include "code\modules\preferences\preference_setup\skills\skills.dm"
#include "code\modules\preferences\preference_setup\vore\01_ears.dm"
#include "code\modules\preferences\preference_setup\vore\02_size.dm"
#include "code\modules\preferences\preference_setup\vore\03_egg.dm"
@@ -3935,6 +3954,7 @@
#include "code\modules\rogueminer_vr\wrappers.dm"
#include "code\modules\rogueminer_vr\zone_console.dm"
#include "code\modules\rogueminer_vr\zonemaster.dm"
+#include "code\modules\roles\role.dm"
#include "code\modules\security levels\keycard authentication.dm"
#include "code\modules\security levels\security levels.dm"
#include "code\modules\shieldgen\directional_shield.dm"
diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm
index 0865d039242..7dafd0b4ff6 100644
--- a/code/__DEFINES/controllers/_subsystems.dm
+++ b/code/__DEFINES/controllers/_subsystems.dm
@@ -72,18 +72,20 @@ DEFINE_BITFIELD(runlevels, list(
*? The numbers just define the ordering, they are meaningless otherwise.
*/
-#define INIT_ORDER_FAIL2TOPIC 104
-#define INIT_ORDER_STATPANELS 103
-#define INIT_ORDER_PROTOTYPES 102
-#define INIT_ORDER_DBCORE 101
-#define INIT_ORDER_INPUT 100
-#define INIT_ORDER_JOBS 99
-#define INIT_ORDER_CHARACTERS 98
-#define INIT_ORDER_SOUNDS 95
+// todo: tg init brackets
+
+#define INIT_ORDER_FAIL2TOPIC 200
+#define INIT_ORDER_TIMER 195
+#define INIT_ORDER_DBCORE 190
+#define INIT_ORDER_REPOSITORY 180
+#define INIT_ORDER_STATPANELS 170
+#define INIT_ORDER_INPUT 160
+#define INIT_ORDER_JOBS 150
+#define INIT_ORDER_CHARACTERS 140
+#define INIT_ORDER_SOUNDS 130
+#define INIT_ORDER_GARBAGE 120
#define INIT_ORDER_VIS 80
-#define INIT_ORDER_GARBAGE 70
#define INIT_ORDER_SERVER_MAINT 65
-#define INIT_ORDER_TIMER 60
#define INIT_ORDER_INSTRUMENTS 50
#define INIT_ORDER_EARLY_ASSETS 48
#define INIT_ORDER_SQLITE 40
@@ -125,40 +127,63 @@ DEFINE_BITFIELD(runlevels, list(
*? If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
*/
+//? Background Subsystems - Below normal
+// Any ../subsystem/.. is here unless it doesn't have SS_BACKGROUND in subsystem_flags!
+// This means by default, ../subsystem/processing/.. is here!
+
+#define FIRE_PRIORITY_RADIATION 10 //! laggy as hell, bottom barrel until optimizations are done.
+#define FIRE_PRIORITY_GARBAGE 15
+#define FIRE_PRIORITY_CHARACTERS 25
+#define FIRE_PRIORITY_PARALLAX 30
+#define FIRE_PRIORITY_AIR 35
+#define FIRE_PRIORITY_PROCESS 45
+// DEFAULT PRIORITY IS HERE
+#define FIRE_PRIORITY_PLANETS 75
+
+//? Normal Subsystems - Above background, below ticker
+// Any ../subsystem/.. without SS_TICKER or SS_BACKGROUND in subsystem_flags is here!
+
#define FIRE_PRIORITY_PING 5
#define FIRE_PRIORITY_SHUTTLES 5
-#define FIRE_PRIORITY_NIGHTSHIFT 6
#define FIRE_PRIORITY_PLANTS 5
+#define FIRE_PRIORITY_NIGHTSHIFT 6
#define FIRE_PRIORITY_VOTE 9
-#define FIRE_PRIORITY_AI 10
#define FIRE_PRIORITY_VIS 10
#define FIRE_PRIORITY_SERVER_MAINT 10
#define FIRE_PRIORITY_ZMIMIC 10
-#define FIRE_PRIORITY_GARBAGE 15
#define FIRE_PRIORITY_ALARMS 20
-#define FIRE_PRIORITY_CHARSETUP 25
#define FIRE_PRIORITY_SPACEDRIFT 25
#define FIRE_PRIORITY_AIRFLOW 30
-#define FIRE_PRIORITY_PARALLAX 30
-#define FIRE_PRIORITY_AIR 35
#define FIRE_PRIORITY_OBJ 40
-#define FIRE_PRIORITY_PROCESS 45
-#define FIRE_PRIORITY_DEFAULT 50
-#define FIRE_PRIORITY_LIGHTING 50
-#define FIRE_PRIORITY_PLANETS 75
-#define FIRE_PRIORITY_INSTRUMENTS 90
-#define FIRE_PRIORITY_MACHINES 100
-#define FIRE_PRIORITY_ASSETS 105
-#define FIRE_PRIORITY_TGUI 110
-#define FIRE_PRIORITY_PROJECTILES 150
-#define FIRE_PRIORITY_THROWING 150
+// DEFAULT PRIORITY IS HERE
+#define FIRE_PRIORITY_LIGHTING 50
+#define FIRE_PRIORITY_INSTRUMENTS 90
+#define FIRE_PRIORITY_ASSET_LOADING 100
+#define FIRE_PRIORITY_MACHINES 100
+#define FIRE_PRIORITY_TGUI 110
+#define FIRE_PRIORITY_STATPANELS 400
+#define FIRE_PRIORITY_OVERLAYS 500
+
+//? Ticker Subsystems - Highest priority
+// Any subsystem flagged with SS_TICKER is here!
+// Do not unnecessarily set your subsystem as TICKER.
+// Is your feature as important as movement, chat, or timers?
+// Probably not! Go to normal bracket instead!
+
+#define FIRE_PRIORITY_AI 10 //! WHY IS THIS SSTICKER???
+// DEFAULT PRIORITY IS HERE
+#define FIRE_PRIORITY_PROJECTILES 150 //! this probably shouldn't be ssticker
+#define FIRE_PRIORITY_THROWING 150 //! this probably shouldn't be ssticker
#define FIRE_PRIORITY_CHAT 400
-#define FIRE_PRIORITY_STATPANELS 400
-#define FIRE_PRIORITY_OVERLAYS 500
-#define FIRE_PRIORITY_SMOOTHING 500
+#define FIRE_PRIORITY_SMOOTHING 500 //! this probably shouldn't be ssticker
#define FIRE_PRIORITY_TIMER 700
#define FIRE_PRIORITY_INPUT 1000 //! Never drop input.
+//? Special
+
+/// This is used as the default regardless of bucket. Check above.
+#define FIRE_PRIORITY_DEFAULT 50
+
/**
* Create a new timer and add it to the queue.
* Arguments:
diff --git a/code/__DEFINES/controllers/dbcore.dm b/code/__DEFINES/controllers/dbcore.dm
index 8f9f885f19f..fffb47bc476 100644
--- a/code/__DEFINES/controllers/dbcore.dm
+++ b/code/__DEFINES/controllers/dbcore.dm
@@ -15,4 +15,4 @@
*
* make sure you add an update to the schema_version stable in the db changelog
*/
-#define DB_MINOR_VERSION 2
+#define DB_MINOR_VERSION 3
diff --git a/code/__DEFINES/controllers/persistence.dm b/code/__DEFINES/controllers/persistence.dm
index cf9383192a9..3bfee20bca8 100644
--- a/code/__DEFINES/controllers/persistence.dm
+++ b/code/__DEFINES/controllers/persistence.dm
@@ -1,3 +1,26 @@
-//! Object Storage System - Strings
+//? Object Storage System - Groups
+
/// default group for null groups
-#define OBJECT_PERSISTENCE_STRING_GROUP_NULL ""
+#define OBJECT_PERSISTENCE_GROUP_NONE ""
+/// group for map persistence key
+#define OBJECT_PERSISTENCE_GROUP_FOR_MAP_KEY(_key) "map_[_key]"
+
+//? Object Storage System - Character Types
+
+/// /datum/character/human
+#define OBJECT_PERSISTENCE_CHARACTER_TYPE_HUMAN "human"
+
+//? legacy below
+
+// Direct filename paths
+
+#define PERSISTENCE_FILE_BUNKER_PASSTHROUGH "data/persistence/bunker_passthrough.json"
+
+// Filenames for putting under directories
+
+#define PERSISTENCE_FILENAME_OBJECTS "objects.json"
+
+// Directories
+
+#define PERSISTENCE_MAP_ROOT_DIRECTORY "data/persistence/maps"
+
diff --git a/code/__DEFINES/controllers/prototypes.dm b/code/__DEFINES/controllers/prototypes.dm
deleted file mode 100644
index 1075e03a048..00000000000
--- a/code/__DEFINES/controllers/prototypes.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-//! types
-/// dud for testing
-#define YAML_PROTOTYPE_DUD "Dud"
-/// lore datums
-#define YAML_PROTOTYPE_LORE "Lore"
diff --git a/code/__DEFINES/directional.dm b/code/__DEFINES/directional.dm
index 77f57458101..e1efa3d8a91 100644
--- a/code/__DEFINES/directional.dm
+++ b/code/__DEFINES/directional.dm
@@ -30,6 +30,7 @@
#define DIRFLIP(d) turn(d, 180)
/// Inverse direction, taking into account UP|DOWN if necessary.
+//? STOP USING THIS. Use global.reverse_dir!!
#define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) )
/// Create directional subtypes for a path to simplify mapping.
diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm
index ed01ed8b588..0c80e8dc553 100644
--- a/code/__DEFINES/maps.dm
+++ b/code/__DEFINES/maps.dm
@@ -46,7 +46,7 @@ require only minor tweaks.
// helpers for modifying jobs, used in various job_changes.dm files
#define MAP_JOB_CHECK if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return; }
#define MAP_JOB_CHECK_BASE if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return ..(); }
-#define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() }
+#define MAP_REMOVE_JOB(jobpath) /datum/role/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() }
#define SPACERUIN_MAP_EDGE_PAD 15
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index b5bdb1b8017..fbb3f1e3dcd 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -110,7 +110,7 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
#define ANNOUNCER_NAME "Facility PA"
-#define DEFAULT_JOB_TYPE /datum/job/station/assistant
+#define DEFAULT_JOB_TYPE /datum/role/job/station/assistant
//Assistant/Visitor/Whatever
#define USELESS_JOB "Visitor"
diff --git a/code/__DEFINES/mobs/characteristics.dm b/code/__DEFINES/mobs/characteristics.dm
new file mode 100644
index 00000000000..2a826b65248
--- /dev/null
+++ b/code/__DEFINES/mobs/characteristics.dm
@@ -0,0 +1,56 @@
+//! General
+
+// none yet
+
+//! Skills
+
+//? skill levels.
+//? These must be SEQUENTIAL FROM 1 TO X.
+#define CHARACTER_SKILL_UNTRAINED 1
+#define CHARACTER_SKILL_NOVICE 2
+#define CHARACTER_SKILL_TRAINED 3
+#define CHARACTER_SKILL_EXPERIENCED 4
+#define CHARACTER_SKILL_PROFESSIONAL 5
+
+#define CHARACTER_SKILL_ENUM_MIN 1
+#define CHARACTER_SKILL_ENUM_MAX 5
+
+//? Skill costs
+/// baseline skillpoints
+#define SKILLPOINTS_BASELINE 36
+/// for a negligible gain from the last level
+#define SKILLCOST_INCREMENT_NEGLIGIBLE 1
+/// for a mild gain from the last level
+#define SKILLCOST_INCREMENT_MINOR 2
+/// for a moderate gain from the last level
+#define SKILLCOST_INCREMENT_MODERATE 3
+/// for a major gain from the last level
+#define SKILLCOST_INCREMENT_MAJOR 4
+/// for an extreme gain from the last level
+#define SKILLCOST_INCREMENT_EXTREME 6
+
+//? Skill scaling
+/// constant * 2 ** level diff
+#define SKILL_SCALING_EXPONENTIAL_HARD 1
+/// constant * level diff
+#define SKILL_SCALING_LINEAR 2
+/// constant * 1.5 ** level diff
+#define SKILL_SCALING_EXPONENTIAL_SOFT 3
+
+//! Stats
+
+//? stat datatypes
+/// a number
+#define CHARACTER_STAT_NUMERIC "num"
+/// text data
+#define CHARACTER_STAT_STRING "str"
+/// boolean
+#define CHARACTER_STAT_BOOL "bool"
+/// a datum of some kind - NOT RECOMMENDED
+#define CHARACTER_STAT_DATUM "datum"
+/// unknown
+#define CHARACTER_STAT_UNKNOWN "unkw"
+
+//! Talents
+
+// none yet
diff --git a/code/__DEFINES/persistence.dm b/code/__DEFINES/persistence.dm
deleted file mode 100644
index f1d8285b0ba..00000000000
--- a/code/__DEFINES/persistence.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-// Direct filename paths
-
-#define PERSISTENCE_FILE_BUNKER_PASSTHROUGH "data/persistence/bunker_passthrough.json"
-
-// Filenames for putting under directories
-
-#define PERSISTENCE_FILENAME_OBJECTS "objects.json"
-
-// Directories
-
-#define PERSISTENCE_MAP_ROOT_DIRECTORY "data/persistence/maps"
diff --git a/code/__DEFINES/preferences/data_keys_character.dm b/code/__DEFINES/preferences/data_keys_character.dm
index 449267e6cf3..0a6577cc64a 100644
--- a/code/__DEFINES/preferences/data_keys_character.dm
+++ b/code/__DEFINES/preferences/data_keys_character.dm
@@ -16,3 +16,6 @@
#define CHARACTER_DATA_JOBS "jobs"
#define CHARACTER_DATA_ALT_TITLES "alt_titles"
#define CHARACTER_DATA_OVERFLOW_MODE "overflow_mode"
+
+//? Skills
+#define CHARACTER_DATA_SKILLS "skills"
diff --git a/code/__DEFINES/preferences/load_order.dm b/code/__DEFINES/preferences/load_order.dm
index 748ab0aec58..c9f0f3ed583 100644
--- a/code/__DEFINES/preferences/load_order.dm
+++ b/code/__DEFINES/preferences/load_order.dm
@@ -10,3 +10,5 @@
#define PREFERENCE_LOAD_ORDER_LORE_FACTION 55
// language loads last as intrinsincs need to load first
#define PREFERENCE_LOAD_ORDER_LANGUAGE 60
+// occupations load last as lore stuff need to load first
+#define PREFERENCE_LOAD_ORDER_OCCUPATIONS 70
diff --git a/code/__DEFINES/preferences/procs.dm b/code/__DEFINES/preferences/procs.dm
index 6a7b98b8264..ecd12baafed 100644
--- a/code/__DEFINES/preferences/procs.dm
+++ b/code/__DEFINES/preferences/procs.dm
@@ -14,5 +14,5 @@
#define PREF_COPY_TO_DO_NOT_RENDER (1<<23)
/// helper
-#define PREF_COPY_TO_IS_SPAWNING (flags & (PREF_COPY_TO_FOR_ROUNDSTART | PREF_COPY_TO_FOR_LATEJOIN | PREF_COPY_TO_FOR_GHOSTROLE))
+#define PREF_COPYING_TO_CHECK_IS_SPAWNING(flags) (flags & (PREF_COPY_TO_FOR_ROUNDSTART | PREF_COPY_TO_FOR_LATEJOIN | PREF_COPY_TO_FOR_GHOSTROLE))
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 0e8f06f93dd..622fe7c6efd 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -315,8 +315,7 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
M = C.mob
else if(istype(whom, /datum/mind))
var/datum/mind/mind = whom
- key = mind.key
- ckey = ckey(key)
+ ckey = mind.ckey
if(mind.current)
M = mind.current
if(M.client)
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index e2cd3f28288..3d461e80e53 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -42,7 +42,7 @@ var/datum/category_collection/underwear/global_underwear = new()
//!Backpacks - The load order here is important to maintain. Don't go swapping these around.
var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Messenger Bag", "RIG", "Duffle Bag")
var/global/list/pdachoicelist = list("Default", "Slim", "Old", "Rugged","Minimalist", "Holographic", "Wrist-Bound")
-var/global/list/exclude_jobs = list(/datum/job/station/ai,/datum/job/station/cyborg)
+var/global/list/exclude_jobs = list(/datum/role/job/station/ai,/datum/role/job/station/cyborg)
//* Visual nets
GLOBAL_LIST_EMPTY(visual_nets)
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index f12963cdc39..d579c56b9f7 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -861,7 +861,7 @@ 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/role/job/J, 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)
diff --git a/code/__HELPERS/sorts/comparators.dm b/code/__HELPERS/sorts/comparators.dm
index e5b3d726d14..aceff459102 100644
--- a/code/__HELPERS/sorts/comparators.dm
+++ b/code/__HELPERS/sorts/comparators.dm
@@ -122,7 +122,7 @@ GLOBAL_VAR_INIT(cmp_field, "name")
/**
* Sorts jobs by department, and then by flag within department.
*/
-/proc/cmp_job_datums(datum/job/a, datum/job/b)
+/proc/cmp_job_datums(datum/role/job/a, datum/role/job/b)
. = 0
if( LAZYLEN(a.departments) && LAZYLEN(b.departments) )
// Makes a list that contains only departments that were in both.
diff --git a/code/controllers/configuration/entries/skills.dm b/code/controllers/configuration/entries/skills.dm
new file mode 100644
index 00000000000..4f105deddc0
--- /dev/null
+++ b/code/controllers/configuration/entries/skills.dm
@@ -0,0 +1,5 @@
+/datum/config_entry/flag/characteristics_enabled
+ config_entry_value = TRUE
+
+/datum/config_entry/flag/characteristics_active
+ config_entry_value = FALSE
diff --git a/code/controllers/configuration_old/configuration.dm b/code/controllers/configuration_old/configuration.dm
index 12dd8415f6f..4eafe402287 100644
--- a/code/controllers/configuration_old/configuration.dm
+++ b/code/controllers/configuration_old/configuration.dm
@@ -48,7 +48,6 @@
var/objectives_disabled = 0 //if objectives are disabled or not
var/protect_roles_from_antagonist = 0// If security and such can be traitor/cult/other
var/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke.
- var/allow_Metadata = 0 // Metadata is supported.
var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1.
var/fps = 20
var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
@@ -516,9 +515,6 @@
if ("feature_object_spell_system")
config_legacy.feature_object_spell_system = 1
- if ("allow_metadata")
- config_legacy.allow_Metadata = 1
-
if ("traitor_scaling")
config_legacy.traitor_scaling = 1
diff --git a/code/controllers/configuration_old/configuration_vr.dm b/code/controllers/configuration_old/configuration_vr.dm
index e9f776b45bd..8fae7b7d57d 100644
--- a/code/controllers/configuration_old/configuration_vr.dm
+++ b/code/controllers/configuration_old/configuration_vr.dm
@@ -6,7 +6,6 @@
var/time_off = FALSE
var/pto_job_change = FALSE
var/pto_cap = 100 //Hours
- var/require_flavor = FALSE
/hook/startup/proc/read_vs_config()
var/list/Lines = world.file2list("config/legacy/config.txt")
@@ -45,6 +44,4 @@
config_legacy.time_off = TRUE
if ("pto_job_change")
config_legacy.pto_job_change = TRUE
- if ("require_flavor")
- config_legacy.require_flavor = TRUE
return 1
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index d5c34875173..092b3761bf6 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -284,7 +284,7 @@
//usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
//should attempt to salvage what it can from the old instance of subsystem
/datum/controller/subsystem/Recover()
- return
+ return TRUE
/datum/controller/subsystem/vv_edit_var(var_name, var_value)
switch (var_name)
diff --git a/code/controllers/subsystem/asset_loading.dm b/code/controllers/subsystem/asset_loading.dm
index 5010d7e13af..f51ac236352 100644
--- a/code/controllers/subsystem/asset_loading.dm
+++ b/code/controllers/subsystem/asset_loading.dm
@@ -5,7 +5,7 @@
*/
SUBSYSTEM_DEF(asset_loading)
name = "Asset Loading"
- priority = FIRE_PRIORITY_ASSETS
+ priority = FIRE_PRIORITY_ASSET_LOADING
subsystem_flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/datum/asset/generate_queue = list()
diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm
index cf8c2780a98..e18fc1943a4 100644
--- a/code/controllers/subsystem/assets.dm
+++ b/code/controllers/subsystem/assets.dm
@@ -20,8 +20,6 @@ SUBSYSTEM_DEF(assets)
transport = newtransport
transport.Load()
-
-
/datum/controller/subsystem/assets/Initialize(timeofday)
for(var/type in typesof(/datum/asset))
var/datum/asset/A = type
diff --git a/code/controllers/subsystem/characters/_characters.dm b/code/controllers/subsystem/characters/_characters.dm
index 23009184f9e..6bd991b16e2 100644
--- a/code/controllers/subsystem/characters/_characters.dm
+++ b/code/controllers/subsystem/characters/_characters.dm
@@ -7,7 +7,7 @@
SUBSYSTEM_DEF(characters)
name = "Characters"
init_order = INIT_ORDER_CHARACTERS
- priority = FIRE_PRIORITY_CHARSETUP
+ priority = FIRE_PRIORITY_CHARACTERS
subsystem_flags = SS_BACKGROUND
wait = 1 SECOND
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
diff --git a/code/controllers/subsystem/characters/backgrounds.dm b/code/controllers/subsystem/characters/backgrounds.dm
index 8a2f54a410b..c18e9e46214 100644
--- a/code/controllers/subsystem/characters/backgrounds.dm
+++ b/code/controllers/subsystem/characters/backgrounds.dm
@@ -45,21 +45,25 @@
tim_sort(character_religions, /proc/cmp_auto_compare, TRUE)
tim_sort(character_factions, /proc/cmp_auto_compare, TRUE)
-/datum/controller/subsystem/characters/proc/available_citizenships(species_id)
+/datum/controller/subsystem/characters/proc/available_citizenships(species_id, category)
. = list()
for(var/id in character_citizenships)
var/datum/lore/character_background/citizenship/L = character_citizenships[id]
+ if(category && (L.category != category))
+ continue
if(L.check_species_id(species_id))
. += L
-/datum/controller/subsystem/characters/proc/available_religions(species_id)
+/datum/controller/subsystem/characters/proc/available_religions(species_id, category)
. = list()
for(var/id in character_religions)
var/datum/lore/character_background/religion/L = character_religions[id]
+ if(category && (L.category != category))
+ continue
if(L.check_species_id(species_id))
. += L
-/datum/controller/subsystem/characters/proc/available_factions(species_id, origin_id, citizenship_id)
+/datum/controller/subsystem/characters/proc/available_factions(species_id, origin_id, citizenship_id, category)
. = list()
for(var/id in character_factions)
var/datum/lore/character_background/faction/L = character_factions[id]
@@ -67,6 +71,8 @@
continue
if(L.citizenship_whitelist && !(citizenship_id in L.citizenship_whitelist))
continue
+ if(category && (L.category != category))
+ continue
if(L.check_species_id(species_id))
. += L
@@ -79,30 +85,30 @@
if(L.check_species_id(species_id))
. += L
-/datum/controller/subsystem/characters/proc/resolve_citizenship(id)
+/datum/controller/subsystem/characters/proc/resolve_citizenship(id_or_typepath)
RETURN_TYPE(/datum/lore/character_background/citizenship)
- if(ispath(id))
- var/datum/lore/character_background/bg = id
- id = initial(bg.id)
- return character_citizenships[id]
+ if(ispath(id_or_typepath))
+ var/datum/lore/character_background/bg = id_or_typepath
+ id_or_typepath = initial(bg.id)
+ return character_citizenships[id_or_typepath]
-/datum/controller/subsystem/characters/proc/resolve_faction(id)
+/datum/controller/subsystem/characters/proc/resolve_faction(id_or_typepath)
RETURN_TYPE(/datum/lore/character_background/faction)
- if(ispath(id))
- var/datum/lore/character_background/bg = id
- id = initial(bg.id)
- return character_factions[id]
+ if(ispath(id_or_typepath))
+ var/datum/lore/character_background/bg = id_or_typepath
+ id_or_typepath = initial(bg.id)
+ return character_factions[id_or_typepath]
-/datum/controller/subsystem/characters/proc/resolve_religion(id)
+/datum/controller/subsystem/characters/proc/resolve_religion(id_or_typepath)
RETURN_TYPE(/datum/lore/character_background/religion)
- if(ispath(id))
- var/datum/lore/character_background/bg = id
- id = initial(bg.id)
- return character_religions[id]
+ if(ispath(id_or_typepath))
+ var/datum/lore/character_background/bg = id_or_typepath
+ id_or_typepath = initial(bg.id)
+ return character_religions[id_or_typepath]
-/datum/controller/subsystem/characters/proc/resolve_origin(id)
+/datum/controller/subsystem/characters/proc/resolve_origin(id_or_typepath)
RETURN_TYPE(/datum/lore/character_background/origin)
- if(ispath(id))
- var/datum/lore/character_background/bg = id
- id = initial(bg.id)
- return character_origins[id]
+ if(ispath(id_or_typepath))
+ var/datum/lore/character_background/bg = id_or_typepath
+ id_or_typepath = initial(bg.id)
+ return character_origins[id_or_typepath]
diff --git a/code/controllers/subsystem/job/_job.dm b/code/controllers/subsystem/job/_job.dm
index 3f62678d064..185916fa6fe 100644
--- a/code/controllers/subsystem/job/_job.dm
+++ b/code/controllers/subsystem/job/_job.dm
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(job)
/// List of all jobs
var/list/occupations
/// Dict of all jobs, keys are titles
- var/list/datum/job/name_occupations
+ var/list/datum/role/job/name_occupations
/// Dict of all jobs, keys are types
var/list/type_occupations
/// jobs by id
@@ -43,7 +43,7 @@ SUBSYSTEM_DEF(job)
// todo: this is shit but it works
job_pref_ui_cache = list()
for(var/id in job_lookup)
- var/datum/job/J = job_lookup[id]
+ var/datum/role/job/J = job_lookup[id]
if(!(J.join_types & JOB_ROUNDSTART))
continue
var/faction = J.faction
@@ -67,13 +67,13 @@ SUBSYSTEM_DEF(job)
job_lookup = list()
name_occupations = list()
type_occupations = list()
- var/list/all_jobs = subtypesof(/datum/job)
+ var/list/all_jobs = subtypesof(/datum/role/job)
if(!all_jobs.len)
to_chat(world, SPAN_WARNING( "Error setting up jobs, no job datums found"))
return FALSE
for(var/J in all_jobs)
- var/datum/job/job = J
+ var/datum/role/job/job = J
if(initial(job.abstract_type) == J)
continue
job = new J
@@ -104,7 +104,7 @@ SUBSYSTEM_DEF(job)
return TRUE
-/datum/controller/subsystem/job/proc/add_to_departments(datum/job/J)
+/datum/controller/subsystem/job/proc/add_to_departments(datum/role/job/J)
// Adds to the regular job lists in the departments, which allow multiple departments for a job.
for(var/D in J.departments)
var/datum/department/dept = LAZYACCESS(department_datums, D)
@@ -163,8 +163,8 @@ SUBSYSTEM_DEF(job)
// Returns a reference to the primary department datum that a job is in.
// Can receive job datum refs, typepaths, or job title strings.
-/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/job/J)
- if(!istype(J, /datum/job))
+/datum/controller/subsystem/job/proc/get_primary_department_of_job(datum/role/job/J)
+ if(!istype(J, /datum/role/job))
if(ispath(J))
J = job_by_type(J)
else if(istext(J))
diff --git a/code/controllers/subsystem/job/_legacy_job_stuff.dm b/code/controllers/subsystem/job/_legacy_job_stuff.dm
index 25e983c2e35..3e5d0665cdf 100644
--- a/code/controllers/subsystem/job/_legacy_job_stuff.dm
+++ b/code/controllers/subsystem/job/_legacy_job_stuff.dm
@@ -16,7 +16,7 @@
/datum/controller/subsystem/job/proc/AssignRole(mob/new_player/player, rank, latejoin = 0)
job_debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
if(player && player.mind && rank)
- var/datum/job/job = get_job(rank)
+ var/datum/role/job/job = get_job(rank)
var/reasons = job.check_client_availability_one(player.client)
if(reasons != ROLE_AVAILABLE)
job_debug("AR failed: player [player], rank [rank], latejoin [latejoin], failed for [reasons]")
@@ -34,13 +34,13 @@
/// Making additional slot on the fly.
/datum/controller/subsystem/job/proc/FreeRole(rank)
- var/datum/job/job = get_job(rank)
+ var/datum/role/job/job = get_job(rank)
if(job && job.total_positions != -1)
job.total_positions++
return 1
return 0
-/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level)
+/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/role/job/job, level)
job_debug("Running FOC, Job: [job], Level: [level]")
var/list/candidates = list()
for(var/mob/new_player/player in divide_unassigned)
@@ -56,7 +56,7 @@
/datum/controller/subsystem/job/proc/GiveRandomJob(mob/new_player/player)
job_debug("GRJ Giving random job, Player: [player]")
- for(var/datum/job/job in shuffle(occupations))
+ for(var/datum/role/job/job in shuffle(occupations))
var/reasons = job.check_client_availability_one(player.client)
if(reasons != ROLE_AVAILABLE)
job_debug("GRJ failed for [reasons] on [job.id]")
@@ -75,7 +75,7 @@
/datum/controller/subsystem/job/proc/FillHeadPosition()
for(var/level in JOB_PRIORITY_HIGH to JOB_PRIORITY_LOW step -1)
for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND))
- var/datum/job/job = get_job(command_position)
+ var/datum/role/job/job = get_job(command_position)
if(!job)
continue
var/list/candidates = FindOccupationCandidates(job, level)
@@ -121,7 +121,7 @@
*/
/datum/controller/subsystem/job/proc/CheckHeadPositions(level)
for(var/command_position in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND))
- var/datum/job/job = get_job(command_position)
+ var/datum/role/job/job = get_job(command_position)
if(!job)
continue
var/list/candidates = FindOccupationCandidates(job, level)
@@ -145,7 +145,7 @@
//Holder for Triumvirate is stored in the SSticker, this just processes it
if(SSticker && SSticker.triai)
- for(var/datum/job/A in occupations)
+ for(var/datum/role/job/A in occupations)
if(A.title == "AI")
A.spawn_positions = 3
break
@@ -162,7 +162,7 @@
//People who wants to be assistants, sure, go on.
job_debug("DO, Running Assistant Check 1")
- var/datum/job/assist = new DEFAULT_JOB_TYPE ()
+ var/datum/role/job/assist = new DEFAULT_JOB_TYPE ()
var/list/assistant_candidates = FindOccupationCandidates(assist, JOB_PRIORITY_HIGH)
job_debug("AC1, Candidates: [assistant_candidates.len]")
for(var/mob/new_player/player in assistant_candidates)
@@ -195,7 +195,7 @@
for(var/mob/new_player/player in divide_unassigned)
// Loop through all jobs
- for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY
+ for(var/datum/role/job/job in shuffledoccupations) // SHUFFLE ME BABY
if(job.title in SSticker.mode.disabled_jobs)
continue
var/reasons = job.check_client_availability_one(player.client)
@@ -241,7 +241,7 @@
if(!H)
return null
- var/datum/job/job = get_job(rank)
+ var/datum/role/job/job = get_job(rank)
var/list/spawn_in_storage = list()
var/real_species_name = H.species.name
@@ -486,7 +486,7 @@
continue
if(name && value)
- var/datum/job/J = get_job(name)
+ var/datum/role/job/J = get_job(name)
if(!J) continue
J.total_positions = text2num(value)
J.spawn_positions = text2num(value)
@@ -497,7 +497,7 @@
/datum/controller/subsystem/job/proc/HandleFeedbackGathering()
- for(var/datum/job/job in occupations)
+ for(var/datum/role/job/job in occupations)
var/tmp_str = "|[job.title]|"
var/level1 = 0 //high
@@ -532,7 +532,7 @@
var/fail_deadly = FALSE
- var/datum/job/J = SSjob.get_job(rank)
+ var/datum/role/job/J = SSjob.get_job(rank)
fail_deadly = J?.offmap_spawn
var/preferred_method
var/datum/spawnpoint/spawnpos
diff --git a/code/controllers/subsystem/job/jexp.dm b/code/controllers/subsystem/job/jexp.dm
index 141086fa28f..a0f497d491e 100644
--- a/code/controllers/subsystem/job/jexp.dm
+++ b/code/controllers/subsystem/job/jexp.dm
@@ -11,9 +11,9 @@
*/
/client/proc/has_jexp_bypass()
SHOULD_NOT_SLEEP(TRUE)
- return !!(admin_datums[ckey] || (database.player_flags & PLAYER_FLAG_JEXP_EXEMPT))
+ return !!(admin_datums[ckey] || (player.player_flags & PLAYER_FLAG_JEXP_EXEMPT))
/client/proc/has_jexp_bypass_blocking()
- database.block_on_available()
- return !!(admin_datums[ckey] || (database.player_flags & PLAYER_FLAG_JEXP_EXEMPT))
+ player.block_on_available()
+ return !!(admin_datums[ckey] || (player.player_flags & PLAYER_FLAG_JEXP_EXEMPT))
diff --git a/code/controllers/subsystem/job/job_manager.dm b/code/controllers/subsystem/job/job_manager.dm
index d6f34440d02..bbea024f340 100644
--- a/code/controllers/subsystem/job/job_manager.dm
+++ b/code/controllers/subsystem/job/job_manager.dm
@@ -2,23 +2,23 @@
/datum/controller/subsystem/job
/datum/controller/subsystem/job/proc/job_by_id(id)
- RETURN_TYPE(/datum/job)
+ RETURN_TYPE(/datum/role/job)
return job_lookup[id]
/datum/controller/subsystem/job/proc/job_by_type(path)
- RETURN_TYPE(/datum/job)
+ RETURN_TYPE(/datum/role/job)
return type_occupations[path]
// todo: this should not be used most of the time, id/type is better
/datum/controller/subsystem/job/proc/job_by_title(title)
- RETURN_TYPE(/datum/job)
+ RETURN_TYPE(/datum/role/job)
return name_occupations[title]
/datum/controller/subsystem/job/proc/all_job_ids(faction)
RETURN_TYPE(/list)
. = list()
if(faction)
- for(var/datum/job/J as anything in occupations)
+ for(var/datum/role/job/J as anything in occupations)
if(J.faction != faction)
continue
. += J.id
@@ -30,7 +30,7 @@
RETURN_TYPE(/list)
. = list()
if(faction)
- for(var/datum/job/J as anything in occupations)
+ for(var/datum/role/job/J as anything in occupations)
if(J.faction != faction)
continue
. += J.type
@@ -43,7 +43,7 @@
RETURN_TYPE(/list)
. = list()
if(faction)
- for(var/datum/job/J as anything in occupations)
+ for(var/datum/role/job/J as anything in occupations)
if(J.faction != faction)
continue
. += J.title
@@ -55,10 +55,10 @@
RETURN_TYPE(/list)
. = list()
if(faction)
- for(var/datum/job/J as anything in occupations)
+ for(var/datum/role/job/J as anything in occupations)
if(J.faction != faction)
continue
. += J
else
- for(var/datum/job/J as anything in occupations)
+ for(var/datum/role/job/J as anything in occupations)
. += J
diff --git a/code/controllers/subsystem/job/joining.dm b/code/controllers/subsystem/job/joining.dm
index 009201c7544..9cf7c50a7cf 100644
--- a/code/controllers/subsystem/job/joining.dm
+++ b/code/controllers/subsystem/job/joining.dm
@@ -1,5 +1,5 @@
/*
-/datum/controller/subsystem/job/proc/ProcessRoundstartPlayer(mob/M, datum/job/J, loadout = TRUE, client/C)
+/datum/controller/subsystem/job/proc/ProcessRoundstartPlayer(mob/M, datum/role/job/J, loadout = TRUE, client/C)
// autodetect
if(!C)
C = M.client
@@ -16,7 +16,7 @@
SendToRoundstart(M, C, J)
PostJoin(M, J, C, FALSE)
-/datum/controller/subsystem/job/proc/ProcessLatejoinPlayer(mob/M, datum/job/J, loadout = TRUE, client/C)
+/datum/controller/subsystem/job/proc/ProcessLatejoinPlayer(mob/M, datum/role/job/J, loadout = TRUE, client/C)
// autodetect
if(!C)
C = M.client
@@ -33,7 +33,7 @@
SendToLatejoin(M, C, job = J)
PostJoin(M, J, C, TRUE)
-/datum/controller/subsystem/job/proc/GreetPlayer(mob/M, datum/job/J, latejoin, client/C)
+/datum/controller/subsystem/job/proc/GreetPlayer(mob/M, datum/role/job/J, latejoin, client/C)
var/client/output = C || M.client
if(!J)
return
@@ -52,7 +52,7 @@
to_chat(output, "Your account ID is [wageslave.account_id].")
M.add_memory("Your account ID is [wageslave.account_id].")
-/datum/controller/subsystem/job/proc/EquipPlayer(mob/M, datum/job/J, loadout = TRUE, datum/preferences/prefs, announce, latejoin, client/C)
+/datum/controller/subsystem/job/proc/EquipPlayer(mob/M, datum/role/job/J, loadout = TRUE, datum/preferences/prefs, announce, latejoin, client/C)
if(!istype(J))
J = GetJobAuto(J)
ASSERT(istype(J))
@@ -73,7 +73,7 @@
HandleLoadoutLeftovers(M, leftovers, null, C)
-/datum/controller/subsystem/job/proc/PostJoin(mob/M, datum/job/J, client/C, latejoin)
+/datum/controller/subsystem/job/proc/PostJoin(mob/M, datum/role/job/J, client/C, latejoin)
// job handling
if(J)
SSpersistence.antag_rep_change[M.client.ckey] += J.GetAntagRep()
@@ -110,7 +110,7 @@
if(latejoin)
AnnounceJoin(M, J, C)
-/datum/controller/subsystem/job/proc/AnnounceJoin(mob/M, datum/job/J, client/C)
+/datum/controller/subsystem/job/proc/AnnounceJoin(mob/M, datum/role/job/J, client/C)
if(istype(get_area(M), /area/shuttle/arrival) && SSshuttle.arrivals)
SSshuttle.arrivals.QueueAnnounce(M, J.title)
else
@@ -123,7 +123,7 @@
* - latejoin - latejoining mob?
* - force - bypass checks
*/
-/datum/controller/subsystem/job/proc/Assign(datum/mind/M, datum/job/J, latejoin = FALSE, force = FALSE)
+/datum/controller/subsystem/job/proc/Assign(datum/mind/M, datum/role/job/J, latejoin = FALSE, force = FALSE)
if(ismob(M))
var/mob/_M = M
M = _M.mind
@@ -145,7 +145,7 @@
J.current_positions++
return TRUE
-/datum/controller/subsystem/job/proc/CanAssign(M, datum/job/J, latejoin)
+/datum/controller/subsystem/job/proc/CanAssign(M, datum/role/job/J, latejoin)
var/mob/checking = ismob(M) && M
if(!checking)
if(istype(M, /datum/mind))
@@ -175,7 +175,7 @@
*/
/datum/controller/subsystem/job/proc/SendToLatejoin(mob/M, client/C = M.client, faction, job, method, override)
if(!override && M.mind?.assigned_role)
- var/datum/job/J = SSjob.GetJobName(M.mind.assigned_role)
+ var/datum/role/job/J = SSjob.GetJobName(M.mind.assigned_role)
if(J)
faction = J.faction
job = J.GetID()
@@ -193,7 +193,7 @@
subsystem_log(error_message)
CRASH(error_message) // this is serious.
-/datum/controller/subsystem/job/proc/SendToRoundstart(mob/M, client/C, datum/job/J)
+/datum/controller/subsystem/job/proc/SendToRoundstart(mob/M, client/C, datum/role/job/J)
var/atom/movable/landmark/spawnpoint/S = GetRoundstartSpawnpoint(M, C, J.GetID(), J.faction)
if(!S)
stack_trace("Couldn't find a roundstart spawnpoint for [M] ([C]) - [J.type] ([J.faction]).")
@@ -206,7 +206,7 @@
/datum/controller/subsystem/job/proc/handle_auto_deadmin_roles(client/C, rank)
if(!C?.holder)
return TRUE
- var/datum/job/job = GetJobAuto(rank)
+ var/datum/role/job/job = GetJobAuto(rank)
if(!job)
return
if((job.auto_deadmin_role_flags & DEADMIN_POSITION_HEAD) && (CONFIG_GET(flag/auto_deadmin_heads) || (C.prefs?.deadmin & DEADMIN_POSITION_HEAD)))
diff --git a/code/controllers/subsystem/job/roundstart.dm b/code/controllers/subsystem/job/roundstart.dm
index 7c2651ba752..327d5a29474 100644
--- a/code/controllers/subsystem/job/roundstart.dm
+++ b/code/controllers/subsystem/job/roundstart.dm
@@ -41,7 +41,7 @@
// i'm going to figure out if i can speed it up BEFORE rewriting everything.
/*
-/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag)
+/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/role/job/job, level, flag)
JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
var/list/candidates = list()
for(var/mob/new_player/player in unassigned)
@@ -71,7 +71,7 @@
/datum/controller/subsystem/job/proc/GiveRandomJob(mob/new_player/player)
JobDebug("GRJ Giving random job, Player: [player]")
. = FALSE
- for(var/datum/job/job in shuffle(GetAllJobs()))
+ for(var/datum/role/job/job in shuffle(GetAllJobs()))
if(!job)
continue
@@ -124,7 +124,7 @@
//This is basically to ensure that there's atleast a few heads in the round
/datum/controller/subsystem/job/proc/FillHeadPosition()
for(var/level in level_order)
- for(var/datum/job/job as anything in GetDepartmentJobDatums(/datum/department/command))
+ for(var/datum/role/job/job as anything in GetDepartmentJobDatums(/datum/department/command))
if(!job)
continue
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
@@ -140,7 +140,7 @@
//This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level
//This is also to ensure we get as many heads as possible
/datum/controller/subsystem/job/proc/CheckHeadPositions(level)
- for(var/datum/job/job as anything in GetDepartmentJobDatums(/datum/department/command))
+ for(var/datum/role/job/job as anything in GetDepartmentJobDatums(/datum/department/command))
if(!job)
continue
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
@@ -153,7 +153,7 @@
/datum/controller/subsystem/job/proc/FillAIPosition()
var/ai_selected = 0
- var/datum/job/job = GetJobType(/datum/job/ai)
+ var/datum/role/job/job = GetJobType(/datum/role/job/ai)
if(!job)
return 0
for(var/i = job.total_positions, i > 0, i--)
@@ -179,7 +179,7 @@
//Holder for Triumvirate is stored in the SSticker, this just processes it
if(SSticker.triai)
- for(var/datum/job/ai/A in GetAllJobs())
+ for(var/datum/role/job/ai/A in GetAllJobs())
A.roundstart_positions = 3
var/left = 2
for(var/atom/movable/landmark/spawnpoint/job/ai/secondary/S in GetAllSpawnpoints())
@@ -217,7 +217,7 @@
//People who wants to be the overflow role, sure, go on.
JobDebug("DO, Running Overflow Check 1")
- var/datum/job/overflow = GetJobName(SSjob.overflow_role)
+ var/datum/role/job/overflow = GetJobName(SSjob.overflow_role)
var/list/overflow_candidates = FindOccupationCandidates(overflow, JP_LOW)
JobDebug("AC1, Candidates: [overflow_candidates?.len]")
for(var/mob/new_player/player in overflow_candidates)
@@ -256,7 +256,7 @@
RejectPlayer(player)
// Loop through all jobs
- for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY
+ for(var/datum/role/job/job in shuffledoccupations) // SHUFFLE ME BABY
if(!job)
continue
@@ -315,7 +315,7 @@
for(var/required_group in required_jobs)
var/group_ok = TRUE
for(var/rank in required_group)
- var/datum/job/J = GetJobName(rank)
+ var/datum/role/job/J = GetJobName(rank)
if(!J)
SSticker.mode.setup_error = "Invalid job [rank] in gamemode required jobs."
return FALSE
@@ -352,7 +352,7 @@
/datum/controller/subsystem/job/proc/setup_officer_positions()
- var/datum/job/J = SSjob.GetJobType(/datum/job/officer)
+ var/datum/role/job/J = SSjob.GetJobType(/datum/role/job/officer)
if(!J)
CRASH("setup_officer_positions(): Security officer job is missing")
diff --git a/code/controllers/subsystem/persist_vr.dm b/code/controllers/subsystem/persist_vr.dm
index 36745c3b41e..dcb1b738d0b 100644
--- a/code/controllers/subsystem/persist_vr.dm
+++ b/code/controllers/subsystem/persist_vr.dm
@@ -34,7 +34,7 @@ SUBSYSTEM_DEF(persist)
continue
// Try and detect job and department of mob
- var/datum/job/J = detect_job(M)
+ var/datum/role/job/J = detect_job(M)
if(!istype(J) || !J.pto_type || !J.timeoff_factor)
if (MC_TICK_CHECK)
return
diff --git a/code/controllers/subsystem/persistence/objects/characters.dm b/code/controllers/subsystem/persistence/objects/characters.dm
new file mode 100644
index 00000000000..45980eafacd
--- /dev/null
+++ b/code/controllers/subsystem/persistence/objects/characters.dm
@@ -0,0 +1,248 @@
+/**
+ * ? Character Persistence System
+ */
+/datum/controller/subsystem/persistence
+ /// loaded characters - "[id]" = /datum/character instance
+ var/list/character_cache = list()
+
+/**
+ * flushes a character to db
+ * a new character is valid to save this way.
+ *
+ * @params
+ * * char - character datum
+ * * persisting - update persistence info if mob is given
+ */
+/datum/controller/subsystem/persistence/proc/save_character(datum/character/char, mob/persisting)
+ // pause admin proccall guard
+ var/__oldusr = usr
+ usr = null
+ // section below can never be allowed to runtime
+
+ // if we have id, we're updating
+ if(char.character_id)
+ // last played is not updated by this proc
+ // everything else can though!
+ var/datum/db_query/update_query = SSdbcore.NewQuery(
+ "UPDATE [format_table_name("character")] \
+ SET[persisting? " last_persisted = NOW()," : ""] canonical_name = :name, persist_data = :data, \
+ playerid = :pid \
+ WHERE id = :id",
+ list(
+ "id" = char.character_id,
+ "pid" = char.player_id,
+ "name" = ckey(char.canonical_name),
+ "data" = persisting? json_encode(char.make_persist_data(persisting)) : json_encode(list()),
+ )
+ )
+ update_query.Execute(async = FALSE)
+ qdel(update_query)
+ else
+ var/datum/db_query/insert_query = SSdbcore.NewQuery(
+ "INSERT INTO [format_table_name("character")] \
+ (`created`, `last_played`, `last_persisted`, `playerid`, `canonical_name`, \
+ `persist_data`, `character_type`) \
+ VALUES (NOW(), NULL, [persisting? "NOW" : "NULL"], :pid, :name, :data, :type)",
+ list(
+ "pid" = char.player_id,
+ "name" = ckey(char.canonical_name),
+ "data" = persisting? json_encode(char.make_persist_data(persisting)) : json_encode(list()),
+ "type" = char.character_type,
+ ),
+ )
+ insert_query.Execute(async = FALSE)
+ char.character_id = isnum(insert_query.last_insert_id)? insert_query.last_insert_id : text2num(insert_query.last_insert_id)
+ qdel(insert_query)
+ // fetch our ID immediately to repopulate
+ fetch_character(char.character_id, TRUE)
+
+ // resume admin proccall guard
+ usr = __oldusr
+
+/**
+ * fetches a character datum
+ * you should not hold references to it yourself
+ * refetch when you need it!
+ *
+ * @params
+ * * id - character id
+ * * force - reload from sql if it's in cache
+ */
+/datum/controller/subsystem/persistence/proc/fetch_character(id, force = FALSE)
+ ASSERT(isnum(id))
+
+ // pause admin proccall guard
+ var/__oldusr = usr
+ usr = null
+ // section below can never be allowed to runtime
+
+ var/datum/character/loaded = character_cache[num2text(id, 16)]
+ if(!force && loaded)
+ return loaded
+
+ var/datum/db_query/load_query = SSdbcore.NewQuery(
+ "SELECT `created`, `last_played`, `last_persisted`, `playerid`, `canonical_name`, `persist_data`, `character_type` FROM \
+ [format_table_name("character")] WHERE id = :id",
+ list(
+ "id" = id,
+ )
+ )
+ load_query.Execute(async = FALSE)
+
+ if(!load_query.NextRow())
+ character_cache -= num2text(id, 16)
+ qdel(load_query)
+ return null
+
+ var/char_type = load_query.item[7]
+ var/datum_type = character_type_to_datum_path(char_type)
+
+ if(!datum_type)
+ . = null
+ CRASH("unexpected char_type: [char_type]")
+
+ // one's there, make one if it isn't already there
+ if(!istype(loaded, datum_type))
+ character_cache[num2text(id, 16)] = (loaded = new datum_type)
+
+ loaded.character_id = id
+ loaded.player_id = text2num(load_query.item[4])
+ loaded.created_at = load_query.item[1]
+ loaded.played_at = load_query.item[2]
+ loaded.persisted_at = load_query.item[3]
+ loaded.canonical_name = load_query.item[5]
+ loaded.read_persist_data(load_query.item[6]? safe_json_decode(load_query.item[6], list()) : list())
+
+ . = loaded
+
+ qdel(load_query)
+
+ // resume admin proccall guard
+ usr = __oldusr
+
+/**
+ * returns a list of character ids for a player
+ *
+ * @params
+ * * playerid - player id
+ * * fetch - fetch the character datums in the process
+ * * force - forcefully fetch the character even if it's cached
+ */
+/datum/controller/subsystem/persistence/proc/query_characters(playerid, fetch = FALSE, force = FALSE)
+ ASSERT(isnum(playerid))
+
+ // pause admin proccall guard
+ var/__oldusr = usr
+ usr = null
+ // section below can never be allowed to runtime
+
+ . = list()
+ var/datum/db_query/iteration_query = SSdbcore.ExecuteQuery(
+ "SELECT id FROM [format_table_name("character")] WHERE playerid = :id",
+ list(
+ "id" = playerid
+ )
+ )
+ while(iteration_query.NextRow())
+ . += text2num(iteration_query.item[1])
+ if(!length(.))
+ return
+
+ // resume admin proccall guard
+ usr = __oldusr
+
+ // fetch if needed
+ if(fetch)
+ for(var/id in .)
+ fetch_character(id, force)
+
+/**
+ * mark a character as having played
+ *
+ * @params
+ * * id - character id
+ */
+/datum/controller/subsystem/persistence/proc/character_played(id)
+ ASSERT(isnum(id))
+
+ // pause admin proccall guard
+ var/__oldusr = usr
+ usr = null
+ // section below can never be allowed to runtime
+
+ var/datum/db_query/mark_query = SSdbcore.ExecuteQuery(
+ "UPDATE [format_table_name("character")] SET last_played = NOW() WHERE id = :id",
+ list(
+ "id" = id
+ )
+ )
+ . = !!mark_query.affected
+
+ // resume admin proccall guard
+ usr = __oldusr
+
+/**
+ * hardcoded switch: what character type string corrosponds to what /datum/character
+ */
+/datum/controller/subsystem/persistence/proc/character_type_to_datum_path(what)
+ switch(what)
+ if(OBJECT_PERSISTENCE_CHARACTER_TYPE_HUMAN)
+ return /datum/character/human
+
+/datum/character
+ abstract_type = /datum/character
+ /// our character id, if we're already in the sql database
+ var/character_id
+ /// character type - should never change
+ var/character_type
+ /// our player id
+ var/player_id
+ /// our ckey(name) - used to avoid spaces/whatever getting in the way
+ var/canonical_name
+ /// created - SQL datetime - not modifiable
+ var/created_at
+ /// last played SQL datetime - not modifiable directly, use proc
+ var/played_at
+ /// last persisted SQL datetime - not modifiable directly
+ var/persisted_at
+
+/**
+ * reads from a given mob
+ *
+ * this *is* allowed to touch the mob's /datum/mind!
+ */
+/datum/character/proc/read_from(mob/M)
+ return
+
+/**
+ * writes to a given mob
+ *
+ * this *is* allowed to touch the mob's /datum/mind!
+ */
+/datum/character/proc/write_to(mob/M)
+ return
+
+/**
+ * gets data to persist
+ *
+ * @return list of k-v entries
+ */
+/datum/character/proc/make_persist_data(mob/M)
+ return list()
+
+/**
+ * loads fields from persisting data
+ */
+/datum/character/proc/read_persist_data(list/data)
+ return
+
+/**
+ * changes the name of this character
+ * only ever change names this way, this'll handle the necessary updates, within the subsystem and on /datum/player's.
+ */
+/datum/character/proc/immediate_rename(new_name)
+ canonical_name = ckey(new_name)
+ SSpersistence.save_character(src)
+
+/datum/character/human
+ character_type = OBJECT_PERSISTENCE_CHARACTER_TYPE_HUMAN
diff --git a/code/controllers/subsystem/persistence/objects/unique_string.dm b/code/controllers/subsystem/persistence/objects/unique_string.dm
index e60dc326c71..bab43458ad3 100644
--- a/code/controllers/subsystem/persistence/objects/unique_string.dm
+++ b/code/controllers/subsystem/persistence/objects/unique_string.dm
@@ -59,7 +59,7 @@
//! Why the usr fuckery? Because we intentionally wish to obfuscate admin proccalls, since we properly sanitize **everything** in these procs.
-/datum/controller/subsystem/persistence/proc/LoadString(group = OBJECT_PERSISTENCE_STRING_GROUP_NULL, key)
+/datum/controller/subsystem/persistence/proc/LoadString(group = OBJECT_PERSISTENCE_GROUP_NONE, key)
if(!SSdbcore.Connect())
return
var/oldusr = usr
@@ -78,7 +78,7 @@
. = query.item[1]
qdel(query)
-/datum/controller/subsystem/persistence/proc/SaveString(group = OBJECT_PERSISTENCE_STRING_GROUP_NULL, key, value)
+/datum/controller/subsystem/persistence/proc/SaveString(group = OBJECT_PERSISTENCE_GROUP_NONE, key, value)
if(!SSdbcore.Connect())
return
var/oldusr = usr
diff --git a/code/controllers/subsystem/prototypes/_prototypes.dm b/code/controllers/subsystem/prototypes/_prototypes.dm
deleted file mode 100644
index db94bb81e9a..00000000000
--- a/code/controllers/subsystem/prototypes/_prototypes.dm
+++ /dev/null
@@ -1,50 +0,0 @@
-//! This file is WIP and currently unused.
-/**
- * yaml prototype loader, now with blackjack and hookers
- *
- * someone from ss14 come put me out of my misery please
- */
-SUBSYSTEM_DEF(prototypes)
- name = "Prototypes"
- init_order = INIT_ORDER_PROTOTYPES
- subsystem_flags = SS_NO_FIRE
-
- /// prototype cache
- var/list/prototypes
-
-/datum/controller/subsystem/prototypes/Initialize()
- Reload()
- return ..()
-
-/datum/controller/subsystem/prototypes/Recover()
- Reload()
- return ..()
-
-/datum/controller/subsystem/prototypes/proc/Reload()
- subsystem_log("reloading...")
- prototypes = list()
- var/list/walking = list("prototypes/")
- for(var/dir in walking)
- var/list/files = flist(dir)
- for(var/path in files)
- if(path[length(path)] == "/")
- walking += dir + path
- else
- Load(dir + path)
-
-/datum/controller/subsystem/prototypes/proc/Load(fname)
- if(!fexists(fname))
- CRASH("failed to load filename [fname]")
- var/yaml = file2text(file(fname))
- var/list/L = yaml_decode(yaml)
- subsystem_log("loading [fname]")
-
-/datum/controller/subsystem/prototypes/proc/Type(t)
- switch(t)
- if(YAML_PROTOTYPE_LORE)
- return /datum/prototype/lore
- if(YAML_PROTOTYPE_DUD)
- return /datum/prototype/dud
-
-/datum/controller/subsystem/prototypes/proc/Resolve(domain, id)
- return prototypes[domain]?[id]
diff --git a/code/controllers/subsystem/prototypes/dud.dm b/code/controllers/subsystem/prototypes/dud.dm
deleted file mode 100644
index b7fc1be0879..00000000000
--- a/code/controllers/subsystem/prototypes/dud.dm
+++ /dev/null
@@ -1,7 +0,0 @@
-/datum/prototype/dud
- domain = YAML_PROTOTYPE_DUD
-
- var/list/read
-
-/datum/prototype/dud/Read(list/data)
- read = data.Copy()
diff --git a/code/controllers/subsystem/prototypes/lore.dm b/code/controllers/subsystem/prototypes/lore.dm
deleted file mode 100644
index 4744149725d..00000000000
--- a/code/controllers/subsystem/prototypes/lore.dm
+++ /dev/null
@@ -1,16 +0,0 @@
-/datum/prototype/lore
- domain = YAML_PROTOTYPE_LORE
-
-/datum/prototype/lore/Read(list/data)
-
-
-/datum/lore_shard
-
-/**
- * has a fleet
- */
-/datum/lore_shard/fleet
-
-/**
- *
- */
diff --git a/code/controllers/subsystem/prototypes/prototype.dm b/code/controllers/subsystem/prototypes/prototype.dm
deleted file mode 100644
index 3838d5ddee8..00000000000
--- a/code/controllers/subsystem/prototypes/prototype.dm
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * lightweight datums that load
- * prototype data based on type
- * and do some processing on them to get
- * important tidbits out, as well as
- * doing semantic linking when needed.
- */
-/datum/prototype
- /// type but we can't call it type :(
- var/domain
- /// id
- var/id
-
-/datum/prototype/New(id, list/data)
- src.id = id
- Initialize(data)
-
-/datum/prototype/proc/Initialize(list/data)
- return
diff --git a/code/controllers/subsystem/radiation.dm b/code/controllers/subsystem/radiation.dm
index 03b9de1d8ea..5b36c2af659 100644
--- a/code/controllers/subsystem/radiation.dm
+++ b/code/controllers/subsystem/radiation.dm
@@ -5,6 +5,7 @@
SUBSYSTEM_DEF(radiation)
name = "Radiation"
+ priority = FIRE_PRIORITY_RADIATION
subsystem_flags = SS_NO_INIT | SS_BACKGROUND
wait = 1 SECONDS
diff --git a/code/controllers/subsystem/repository.dm b/code/controllers/subsystem/repository.dm
new file mode 100644
index 00000000000..26e3a6b0a30
--- /dev/null
+++ b/code/controllers/subsystem/repository.dm
@@ -0,0 +1,102 @@
+// TODO: file unticked
+// see [code/datums/prototype.dm] for why.
+
+/**
+ * global singleton storage and fetcher
+ */
+SUBSYSTEM_DEF(repository)
+ name = "Repository"
+ subsystem_flags = SS_NO_FIRE
+ init_order = INIT_ORDER_REPOSITORY
+
+ /// by-type lookup
+ var/list/type_lookup
+ /// by-id lookup
+ var/list/uid_lookup
+ /// fetched subtype lists
+ var/list/subtype_lists
+
+/datum/controller/subsystem/repository/Initialize()
+ uid_lookup = list()
+ type_lookup = list()
+ subtype_lists = list()
+ generate()
+ return ..()
+
+/datum/controller/subsystem/repository/Recover()
+ . = ..()
+ src.type_lookup = SSrepository.type_lookup
+ if(!islist(src.type_lookup))
+ src.type_lookup = list()
+ . = FALSE
+ src.uid_lookup = SSrepository.uid_lookup
+ if(!islist(src.uid_lookup))
+ src.uid_lookup = list()
+ . = FALSE
+
+/**
+ * prototypes returned should generally not be modified.
+ * prototypes returned from a typepath input should never, ever be modified.
+ */
+/datum/controller/subsystem/repository/proc/fetch(datum/prototype/type_or_id)
+ if(isnull(type_or_id))
+ return
+ if(istext(type_or_id))
+ return uid_lookup[type_or_id]
+ . = type_lookup[type_or_id]
+ if(.)
+ return
+ // types are complicated, is it lazy?
+ if(initial(type_or_id.lazy))
+ // if so, init it
+ register_internal((. = new type_or_id), TRUE, TRUE)
+ else
+ CRASH("failed to fetch a hardcoded prototype")
+
+/**
+ * lists returned should never, ever be modified.
+ * this fetches subtypes, not the first type on purpose.
+ */
+/datum/controller/subsystem/repository/proc/fetch_subtypes(path)
+ RETURN_TYPE(/list)
+ ASSERT(ispath(path, /datum/prototype))
+ if(subtype_lists[path])
+ return subtype_lists[path]
+ var/list/generating = list()
+ subtype_lists[path] = generating
+ for(var/fetching as anything in subtypesof(path))
+ var/datum/prototype/instance = fetch(fetching)
+ generating += instance
+ return generating
+
+/datum/controller/subsystem/repository/proc/register(datum/prototype/instance, force)
+ return register_internal(instance, force, FALSE)
+
+/datum/controller/subsystem/repository/proc/register_internal(datum/prototype/instance, force, hardcoded)
+ PRIVATE_PROC(TRUE)
+ if(uid_lookup[instance] && !force)
+ return FALSE
+ uid_lookup[instance] = instance
+ if(hardcoded)
+ type_lookup[instance.type] = instance
+ return TRUE
+
+/datum/controller/subsystem/repository/proc/unregister(datum/prototype/instance)
+ if(type_lookup[instance.type] == instance)
+ CRASH("tried to unregister a hardcoded instance")
+ if(!instance.unregister())
+ CRASH("instance refused to unregister. this is undefined behavior.")
+ uid_lookup -= instance.uid
+ return TRUE
+
+/**
+ * regenerates entries, kicking out anything that's in the way
+ */
+/datum/controller/subsystem/repository/proc/generate()
+ for(var/datum/prototype/instance as anything in subtypesof(/datum/prototype))
+ if(initial(instance.abstract_type) == instance)
+ continue
+ if(initial(instance.lazy))
+ continue
+ instance = new instance
+ register_internal(instance, TRUE, TRUE)
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 112e55a7587..300da509d8b 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -58,18 +58,16 @@ SUBSYSTEM_DEF(statpanels)
var/list/additional = player._statpanel_data()
// server data has priority
// assert primary status tab
- var/server_data
+ var/server_data = "%5b%5d" // "[]"
if(player.statpanel_tab("Status", TRUE))
server_data = cache_server_data
// assert admin tabs - these are special and do not check for additional info
- else if(player.statpanel_tab("MC", is_admin))
+ if(player.statpanel_tab("MC", is_admin))
server_data = fetch_mc_data()
- else if(player.statpanel_tab("Tickets", is_admin))
+ if(player.statpanel_tab("Tickets", is_admin))
server_data = fetch_ticket_data()
- else if(player.statpanel_tab("SDQL2", is_admin && length(GLOB.sdql2_queries)))
+ if(player.statpanel_tab("SDQL2", is_admin && length(GLOB.sdql2_queries)))
server_data = fetch_sdql2_data()
- else
- server_data = "%5b%5d" // "[]"
// send additional
player << output("[server_data];[url_encode(json_encode(additional))]", "statbrowser:byond_update")
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 7ee887a0bab..c49135d6690 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -605,10 +605,10 @@ SUBSYSTEM_DEF(ticker)
var/temprole = Mind.special_role
if(temprole) //if they are an antagonist of some sort.
if(temprole in total_antagonists) //If the role exists already, add the name to it
- total_antagonists[temprole] += ", [Mind.name]([Mind.key])"
+ total_antagonists[temprole] += ", [Mind.name]([Mind.ckey])"
else
total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob
- total_antagonists[temprole] += ": [Mind.name]([Mind.key])"
+ total_antagonists[temprole] += ": [Mind.name]([Mind.ckey])"
//Now print them all into the log!
log_game("Antagonists at round end were...")
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index b8ff8476447..89dda9afb6c 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -102,7 +102,7 @@
break
isactive[name] = active ? "Active" : "Inactive"
- var/datum/job/J = SSjob.get_job(real_rank)
+ var/datum/role/job/J = SSjob.get_job(real_rank)
if(J?.offmap_spawn)
off[name] = rank
@@ -310,7 +310,7 @@ GLOBAL_LIST_EMPTY(PDA_Manifest)
var/list/all_jobs = get_job_datums()
- for(var/datum/job/J in all_jobs)
+ for(var/datum/role/job/J in all_jobs)
if(J.title == rank) //If we have a rank, just default to using that.
real_title = rank
break
@@ -331,7 +331,7 @@ GLOBAL_LIST_EMPTY(PDA_Manifest)
if(H.mind && !player_is_antag(H.mind, only_offstation_roles = 1))
var/assignment = GetAssignment(H)
var/hidden
- var/datum/job/J = SSjob.get_job(H.mind.assigned_role)
+ var/datum/role/job/J = SSjob.get_job(H.mind.assigned_role)
hidden = J?.offmap_spawn
/* Note: Due to cached_character_icon, a number of emergent properties occur due to the initialization
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index 84352d94bb1..0c0761a700b 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -264,3 +264,45 @@
return
SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index))
TIMER_COOLDOWN_END(source, index)
+
+//? simple serialize/deserialize; it's expected to use this for simple datums only.
+//? do not use this for /atoms, SSpersistence handles that!
+
+/**
+ * serializes us to a list
+ * note that *everything* will be trampled down to a number or text.
+ * do not store raw types.
+ *
+ * reserved:
+ * "type" - this is always the type at time of saving. this is for current limitations, DO NOT use this if at all possible.
+ */
+/datum/proc/serialize()
+ return list("type" = "[type]")
+
+/**
+ * deserializes from a list
+ *
+ * @params
+ * * data - json_decode()'d list.
+ */
+/datum/proc/deserialize(list/data)
+ return
+
+/**
+ * make a datum from a serialized list
+ * you are responsible for knowing what datums this is valid for.
+ * you are responsible for sanitizing the input.
+ */
+/proc/deserialize_datum(list/data)
+ if(istext(data))
+ data = json_decode(data)
+ var/path = text2path(data["type"])
+ ASSERT(ispath(path, /datum))
+ return (new path):deserialize(data)
+
+/**
+ * serializes a datum into a json text string
+ */
+/proc/serialize_datum(datum/D)
+ ASSERT(isdatum(D))
+ return json_encode(D.serialize())
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 7cdc9a7c4c6..90c948c522c 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -26,17 +26,23 @@
*/
/datum/mind
- var/key
+ /// ckey of mind
+ var/ckey
/// Replaces mob/var/original_name
var/name
var/mob/living/current
var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds.
var/active = FALSE
- //? Original Character Data
+ //? Characteristics
+ /// characteristics holder
+ var/datum/characteristics_holder/characteristics
+
+ //? Preferences
/**
* original save data
* ! TODO: REMOVE THIS; we shouldn't keep this potentially big list all round. !
+ * todo: don't actually remove it, just only save relevant data (?)
*/
var/list/original_save_data
/// original economic modifier from backgrounds
@@ -52,7 +58,7 @@
var/role_alt_title
- var/datum/job/assigned_job
+ var/datum/role/job/assigned_job
var/list/datum/objective/objectives = list()
var/list/datum/objective/special_verbs = list()
@@ -86,10 +92,21 @@
/// Used to store what traits the player had picked out in their preferences before joining, in text form.
var/list/traits = list()
-/datum/mind/New(var/key)
- src.key = key
+/datum/mind/New(ckey)
+ src.ckey = ckey
- ..()
+/datum/mind/Destroy()
+ QDEL_NULL(characteristics)
+ return ..()
+
+/**
+ * make sure we have a characteristics holder
+ */
+/datum/mind/proc/characteristics_holder()
+ if(!characteristics)
+ characteristics = new
+ characteristics.associate_with_mind(src)
+ return characteristics
/datum/mind/proc/transfer_to(mob/living/new_character)
if(!istype(new_character))
@@ -99,6 +116,7 @@
current.remove_changeling_powers()
remove_verb(current, /datum/changeling/proc/EvolutionMenu)
current.mind = null
+ characteristics?.disassociate_from_mob(current)
SSnanoui.user_transferred(current, new_character) // transfer active NanoUI instances to new user
if(new_character.mind) //remove any mind currently in our new body's mind variable
@@ -106,12 +124,13 @@
current = new_character //link ourself to our new body
new_character.mind = src //and link our new body to ourself
+ characteristics?.associate_with_mob(current)
if(changeling)
new_character.make_changeling()
if(active)
- new_character.key = key //now transfer the key to link the client to our new body
+ new_character.ckey = ckey //now transfer the ckey to link the client to our new body
// if(new_character.client) //TODO: Eye Contact
// LAZYCLEARLIST(new_character.client.recent_examines)
@@ -141,7 +160,7 @@
return
var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
"
- out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
"
+ out += "Mind currently owned by ckey: [ckey] [active?"(synced)":"(not synced)"]
"
out += "Assigned role: [assigned_role]. Edit
"
out += "
"
out += "Factions and special roles:
"
@@ -419,7 +438,7 @@
// var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() No longer needed, uses stored in mind
var/crystals
crystals = tcrystals
- crystals = input("Amount of telecrystals for [key]", crystals) as null|num
+ crystals = input("Amount of telecrystals for [ckey]", crystals) as null|num
if (!isnull(crystals))
tcrystals = crystals
@@ -498,9 +517,9 @@
//Initialisation procs
/mob/proc/mind_initialize()
if(mind)
- mind.key = key
+ mind.ckey = ckey
else
- mind = new /datum/mind(key)
+ mind = new /datum/mind(ckey)
mind.original = src
if(SSticker)
SSticker.minds += mind
@@ -571,3 +590,42 @@
. = ..()
mind.assigned_role = "Juggernaut"
mind.special_role = "Cultist"
+
+//? Preferences Checks
+
+/datum/mind/proc/original_background_religion()
+ RETURN_TYPE(/datum/lore/character_background/religion)
+ var/id = original_save_data?[CHARACTER_DATA_RELIGION]
+ if(isnull(id))
+ return
+ return SScharacters.resolve_religion(id)
+
+/datum/mind/proc/original_background_citizenship()
+ RETURN_TYPE(/datum/lore/character_background/citizenship)
+ var/id = original_save_data?[CHARACTER_DATA_CITIZENSHIP]
+ if(isnull(id))
+ return
+ return SScharacters.resolve_citizenship(id)
+
+/datum/mind/proc/original_background_origin()
+ RETURN_TYPE(/datum/lore/character_background/origin)
+ var/id = original_save_data?[CHARACTER_DATA_ORIGIN]
+ if(isnull(id))
+ return
+ return SScharacters.resolve_origin(id)
+
+/datum/mind/proc/original_background_faction()
+ RETURN_TYPE(/datum/lore/character_background/faction)
+ var/id = original_save_data?[CHARACTER_DATA_FACTION]
+ if(isnull(id))
+ return
+ return SScharacters.resolve_faction(id)
+
+/datum/mind/proc/original_background_datums()
+ . = list(
+ original_background_citizenship(),
+ original_background_faction(),
+ original_background_origin(),
+ original_background_religion(),
+ )
+ listclearnulls(.)
diff --git a/code/datums/prototype.dm b/code/datums/prototype.dm
new file mode 100644
index 00000000000..22650764e69
--- /dev/null
+++ b/code/datums/prototype.dm
@@ -0,0 +1,83 @@
+/*
+candidates for conversion:
+- /datum/role
+- /datum/material
+- /datum/lore
+- /datum/design
+*/
+
+/**
+ * global singletons fetched from SSrepository
+ *
+ * they can be registered, or non-registered.
+ *
+ * ids are optional, but no id means it can only be fetched by type. set anonymous to TRUE for that!
+ *
+ * all prototypes should eventually be serializable
+ */
+/datum/prototype
+ abstract_type = /datum/prototype
+
+ //? Identity
+ /// namespace - should be unique to a given domain or kind of prototype, e.g. /datum/prototype/lore, /datum/prototype/outfit, etc
+ /// this should NEVER be changed at runtime!
+ /// changing this may cause persistent data to be thrown out.
+ /// you have been warned.
+ var/namespace
+ /// identifier - must be unique within a namespace
+ var/identifier
+ /// anonymous? if true, we should not have a coded identifier.
+ var/anonymous = FALSE
+
+ /// our id - must be unique globally. DO NOT EDIT THIS, EDIT [identifier].
+ var/uid
+ /// uid next global on /datum/prototype
+ var/static/uid_next = 0
+
+ /// should this be saved?
+ // todo: not yet implemented
+ var/savable = FALSE
+ /// lazyloaded
+ var/lazy = FALSE
+
+/datum/prototype/New()
+ if(anonymous)
+ generate_anonymous_uid()
+ else
+ uid = "[namespace]_[identifier]"
+
+/datum/prototype/proc/generate_anonymous_uid()
+ uid = "[namespace]_[num2text(world.realtime, 16)]_[++uid_next]"
+
+/**
+ * called on register
+ * always call return ..() *LAST* so side effects can be cleaned up on every level on failure.
+ *
+ * @return TRUE / FALSE to allow / deny register; PLEASE clean up side effects if you make this fail!
+ */
+/datum/prototype/proc/register()
+ return TRUE
+
+/**
+ * called on unregister
+ * this should never fail; returning FALSE causes a fatal runtime to be generated.
+ *
+ * @return TRUE / FALSE on success / failure
+ */
+/datum/prototype/proc/unregister()
+ return TRUE
+
+/datum/prototype/serialize()
+ . = ..()
+ .[NAMEOF(src, identifier)] = identifier
+
+/datum/prototype/deserialize(list/data)
+ . = ..()
+ identifier = data[NAMEOF(src, identifier)]
+ uid = "[namespace]_[identifier]"
+
+/**
+ * checks that our identifier is set properly
+ */
+/datum/prototype/proc/assert_identifier()
+ return !anonymous && uid == "[namespace]_[identifier]" && namespace == initial(namespace)
diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm
index b42c9a82e72..0d7c82ed724 100644
--- a/code/game/antagonist/antagonist.dm
+++ b/code/game/antagonist/antagonist.dm
@@ -179,17 +179,17 @@
/datum/antagonist/proc/draft_antagonist(var/datum/mind/player)
//Check if the player can join in this antag role, or if the player has already been given an antag role.
if(!can_become_antag(player) || (player.assigned_role in roundstart_restricted))
- log_debug(SPAN_DEBUG("[player.key] was selected for [role_text] by lottery, but is not allowed to be that role."))
+ log_debug(SPAN_DEBUG("[player.ckey] was selected for [role_text] by lottery, but is not allowed to be that role."))
return 0
if(player.special_role)
- log_debug(SPAN_DEBUG("[player.key] was selected for [role_text] by lottery, but they already have a special role."))
+ log_debug(SPAN_DEBUG("[player.ckey] was selected for [role_text] by lottery, but they already have a special role."))
return 0
if(!(flags & ANTAG_OVERRIDE_JOB) && (!player.current || istype(player.current, /mob/new_player)))
- log_debug(SPAN_DEBUG("[player.key] was selected for [role_text] by lottery, but they have not joined the game."))
+ log_debug(SPAN_DEBUG("[player.ckey] was selected for [role_text] by lottery, but they have not joined the game."))
return 0
pending_antagonists |= player
- log_debug(SPAN_DEBUG("[player.key] has been selected for [role_text] by lottery."))
+ log_debug(SPAN_DEBUG("[player.ckey] has been selected for [role_text] by lottery."))
//Ensure that antags with ANTAG_OVERRIDE_JOB do not occupy job slots.
if(flags & ANTAG_OVERRIDE_JOB)
diff --git a/code/game/antagonist/antagonist_panel.dm b/code/game/antagonist/antagonist_panel.dm
index 079e1e09e40..3f397d29c38 100644
--- a/code/game/antagonist/antagonist_panel.dm
+++ b/code/game/antagonist/antagonist_panel.dm
@@ -27,13 +27,13 @@
var/mob/M = player.current
dat += ""
if(M)
- dat += "| [M.real_name]/([player.key])"
+ dat += " | [M.real_name]/([player.ckey])"
if(!M.client) dat += " (logged out)"
if(M.stat == DEAD) dat += " (DEAD)"
dat += " | "
dat += "\[PP]\[PM\]\[TP\] | "
else
- dat += "[player.key] Mob not found! | "
+ dat += "[player.ckey] Mob not found! | "
dat += "
"
dat += "
"
diff --git a/code/game/antagonist/antagonist_print.dm b/code/game/antagonist/antagonist_print.dm
index 69d98eeff8f..64e54638dc4 100644
--- a/code/game/antagonist/antagonist_print.dm
+++ b/code/game/antagonist/antagonist_print.dm
@@ -49,7 +49,7 @@
/datum/antagonist/proc/print_player_lite(var/datum/mind/ply)
var/role = ply.assigned_role ? "\improper[ply.assigned_role]" : "\improper[ply.special_role]"
- var/text = "
[ply.name] ([ply.key]) as \a [role] ("
+ var/text = "
[ply.name] ([ply.ckey]) as \a [role] ("
if(ply.current)
if(ply.current.stat == DEAD)
text += "died"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 70d48a21751..f2ac9d2d843 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -498,17 +498,17 @@ var/global/list/additional_antag_types = list()
if(D.mind && (D.mind.original == L || D.mind.current == L))
if(L.stat == DEAD)
if(L.suiciding) //Suicider
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n"
+ msg += "[L.name] ([ckey(D.mind.ckey)]), the [L.job] (Suicide)\n"
continue //Disconnected client
else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n"
+ msg += "[L.name] ([ckey(D.mind.ckey)]), the [L.job] (Dead)\n"
continue //Dead mob, ghost abandoned
else
if(D.can_reenter_corpse)
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Adminghosted)\n"
+ msg += "[L.name] ([ckey(D.mind.ckey)]), the [L.job] (Adminghosted)\n"
continue //Lolwhat
else
- msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n"
+ msg += "[L.name] ([ckey(D.mind.ckey)]), the [L.job] (Ghosted)\n"
continue //Ghosted while alive
msg += "" // close the span from right at the top
diff --git a/code/game/landmarks/spawnpoint/_spawnpoint.dm b/code/game/landmarks/spawnpoint/_spawnpoint.dm
index 208363cfc0e..595a3a830d1 100644
--- a/code/game/landmarks/spawnpoint/_spawnpoint.dm
+++ b/code/game/landmarks/spawnpoint/_spawnpoint.dm
@@ -136,7 +136,7 @@
* name - spawnee's name
* job_name - job's name - useful for alt titles
*/
-/obj/landmark/spawnpoint/proc/RenderAnnounceMessage(mob/M, client/C, datum/job/J, name, job_name)
+/obj/landmark/spawnpoint/proc/RenderAnnounceMessage(mob/M, client/C, datum/role/job/J, name, job_name)
return "[name || "Unknown"] will arrive shortly."
/**
diff --git a/code/game/landmarks/spawnpoint/station/jobs.dm b/code/game/landmarks/spawnpoint/station/jobs.dm
index d7f14cdac44..d14872b2298 100644
--- a/code/game/landmarks/spawnpoint/station/jobs.dm
+++ b/code/game/landmarks/spawnpoint/station/jobs.dm
@@ -1,7 +1,7 @@
/obj/landmark/spawnpoint/job/assistant
name = "Assistant"
icon_state = "Assistant"
- job_path = /datum/job/station/assistant
+ job_path = /datum/role/job/station/assistant
/obj/landmark/spawnpoint/job/assistant/override
spawns_left = INFINITY
@@ -11,199 +11,199 @@
/obj/landmark/spawnpoint/job/janitor
name = "Janitor"
icon_state = "Janitor"
- job_path = /datum/job/station/janitor
+ job_path = /datum/role/job/station/janitor
/obj/landmark/spawnpoint/job/cargo_technician
name = "Cargo Technician"
icon_state = "Cargo Technician"
- job_path = /datum/job/station/cargo_tech
+ job_path = /datum/role/job/station/cargo_tech
/obj/landmark/spawnpoint/job/bartender
name = "Bartender"
icon_state = "Bartender"
- job_path = /datum/job/station/bartender
+ job_path = /datum/role/job/station/bartender
/obj/landmark/spawnpoint/job/clown
name = "Clown"
icon_state = "Clown"
- job_path = /datum/job/station/clown
+ job_path = /datum/role/job/station/clown
/obj/landmark/spawnpoint/job/mime
name = "Mime"
icon_state = "Mime"
- job_path = /datum/job/station/mime
+ job_path = /datum/role/job/station/mime
/obj/landmark/spawnpoint/job/quartermaster
name = "Quartermaster"
icon_state = "Quartermaster"
- job_path = /datum/job/station/quartermaster
+ job_path = /datum/role/job/station/quartermaster
/obj/landmark/spawnpoint/job/atmospheric_technician
name = "Atmospheric Technician"
icon_state = "Atmospheric Technician"
- job_path = /datum/job/station/atmos
+ job_path = /datum/role/job/station/atmos
/obj/landmark/spawnpoint/job/chef
name = "Cook"
icon_state = "Cook"
- job_path = /datum/job/station/chef
+ job_path = /datum/role/job/station/chef
/obj/landmark/spawnpoint/job/shaft_miner
name = "Shaft Miner"
icon_state = "Shaft Miner"
- job_path = /datum/job/station/mining
+ job_path = /datum/role/job/station/mining
/obj/landmark/spawnpoint/job/security_officer
name = "Security Officer"
icon_state = "Security Officer"
- job_path = /datum/job/station/officer
+ job_path = /datum/role/job/station/officer
/obj/landmark/spawnpoint/job/botanist
name = "Botanist"
icon_state = "Botanist"
- job_path = /datum/job/station/hydro
+ job_path = /datum/role/job/station/hydro
/obj/landmark/spawnpoint/job/head_of_security
name = "Head of Security"
icon_state = "Head of Security"
- job_path = /datum/job/station/head_of_security
+ job_path = /datum/role/job/station/head_of_security
/obj/landmark/spawnpoint/job/captain
name = "Captain"
icon_state = "Captain"
- job_path = /datum/job/station/captain
+ job_path = /datum/role/job/station/captain
/obj/landmark/spawnpoint/job/detective
name = "Detective"
icon_state = "Detective"
- job_path = /datum/job/station/detective
+ job_path = /datum/role/job/station/detective
/obj/landmark/spawnpoint/job/warden
name = "Warden"
icon_state = "Warden"
- job_path = /datum/job/station/warden
+ job_path = /datum/role/job/station/warden
/obj/landmark/spawnpoint/job/chief_engineer
name = "Chief Engineer"
icon_state = "Chief Engineer"
- job_path = /datum/job/station/chief_engineer
+ job_path = /datum/role/job/station/chief_engineer
/obj/landmark/spawnpoint/job/senior_engineer
name = "Senior Engineer"
icon_state = "Chief Engineer"
- job_path = /datum/job/station/senior_engineer
+ job_path = /datum/role/job/station/senior_engineer
/obj/landmark/spawnpoint/job/head_of_personnel
name = "Head of Personnel"
icon_state = "Head of Personnel"
- job_path = /datum/job/station/head_of_personnel
+ job_path = /datum/role/job/station/head_of_personnel
/obj/landmark/spawnpoint/job/librarian
name = "Curator"
icon_state = "Curator"
- job_path = /datum/job/station/librarian
+ job_path = /datum/role/job/station/librarian
/obj/landmark/spawnpoint/job/lawyer
name = "Lawyer"
icon_state = "Lawyer"
- job_path = /datum/job/station/lawyer
+ job_path = /datum/role/job/station/lawyer
/obj/landmark/spawnpoint/job/station_engineer
name = "Station Engineer"
icon_state = "Station Engineer"
- job_path = /datum/job/station/engineer
+ job_path = /datum/role/job/station/engineer
/obj/landmark/spawnpoint/job/medical_doctor
name = "Medical Doctor"
icon_state = "Medical Doctor"
- job_path = /datum/job/station/doctor
+ job_path = /datum/role/job/station/doctor
/obj/landmark/spawnpoint/job/head_nurse
name = "Head Nurse"
// icon_state = "Medical Doctor"
- job_path = /datum/job/station/head_nurse
+ job_path = /datum/role/job/station/head_nurse
/obj/landmark/spawnpoint/job/paramedic
name = "Paramedic"
icon_state = "Paramedic"
- job_path = /datum/job/station/paramedic
+ job_path = /datum/role/job/station/paramedic
/obj/landmark/spawnpoint/job/scientist
name = "Scientist"
icon_state = "Scientist"
- job_path = /datum/job/station/scientist
+ job_path = /datum/role/job/station/scientist
/obj/landmark/spawnpoint/job/senior_researcher
name = "Senior Researcher"
// icon_state = "Scientist"
- job_path = /datum/job/station/senior_researcher
+ job_path = /datum/role/job/station/senior_researcher
/obj/landmark/spawnpoint/job/chemist
name = "Chemist"
icon_state = "Chemist"
- job_path = /datum/job/station/chemist
+ job_path = /datum/role/job/station/chemist
/obj/landmark/spawnpoint/job/roboticist
name = "Roboticist"
icon_state = "Roboticist"
- job_path = /datum/job/station/roboticist
+ job_path = /datum/role/job/station/roboticist
/obj/landmark/spawnpoint/job/research_director
name = "Research Director"
icon_state = "Research Director"
- job_path = /datum/job/station/research_director
+ job_path = /datum/role/job/station/research_director
// /obj/landmark/spawnpoint/job/geneticist
// name = "Geneticist"
// icon_state = "Geneticist"
-// job_path = /datum/job/station/geneticist
+// job_path = /datum/role/job/station/geneticist
/obj/landmark/spawnpoint/job/chief_medical_officer
name = "Chief Medical Officer"
icon_state = "Chief Medical Officer"
- job_path = /datum/job/station/chief_medical_officer
+ job_path = /datum/role/job/station/chief_medical_officer
// /obj/landmark/spawnpoint/job/virologist
// name = "Virologist"
// icon_state = "Virologist"
-// job_path = /datum/job/station/virologist
+// job_path = /datum/role/job/station/virologist
/obj/landmark/spawnpoint/job/chaplain
name = "Chaplain"
icon_state = "Chaplain"
- job_path = /datum/job/station/chaplain
+ job_path = /datum/role/job/station/chaplain
/obj/landmark/spawnpoint/job/cyborg
name = "Cyborg"
icon_state = "Cyborg"
- job_path = /datum/job/station/cyborg
+ job_path = /datum/role/job/station/cyborg
/obj/landmark/spawnpoint/job/entertainer
name = "Entertainer"
- job_path = /datum/job/station/entertainer
+ job_path = /datum/role/job/station/entertainer
/obj/landmark/spawnpoint/job/pilot
name = "Pilot"
- job_path = /datum/job/station/pilot
+ job_path = /datum/role/job/station/pilot
/obj/landmark/spawnpoint/job/command_secretary
name = "Command Secretary"
- job_path = /datum/job/station/command_secretary
+ job_path = /datum/role/job/station/command_secretary
/obj/landmark/spawnpoint/job/explorer
name = "Explorer"
- job_path = /datum/job/station/explorer
+ job_path = /datum/role/job/station/explorer
/obj/landmark/spawnpoint/job/field_medic
name = "Field Medic"
- job_path = /datum/job/station/field_medic
+ job_path = /datum/role/job/station/field_medic
/obj/landmark/spawnpoint/job/pathfinder
name = "Pathfinder"
- job_path = /datum/job/station/pathfinder
+ job_path = /datum/role/job/station/pathfinder
/obj/landmark/spawnpoint/job/psychiatrist
name = "Psychiatrist"
- job_path = /datum/job/station/psychiatrist
+ job_path = /datum/role/job/station/psychiatrist
/obj/landmark/spawnpoint/job/xenobotanist
name = "Xenobotanist"
@@ -216,7 +216,7 @@
icon_state = "AI"
latejoin = TRUE // AIize in transform_procs.dm uses get_latejoin_spawnpoint() for spawning in a AI -- including at roundstart.
delete_on_roundstart = TRUE
- job_path = /datum/job/station/ai
+ job_path = /datum/role/job/station/ai
prevent_mob_stack = FALSE
spawns_left = 1
var/primary_ai = TRUE
diff --git a/code/game/landmarks/spawnpoint/tradeport.dm b/code/game/landmarks/spawnpoint/tradeport.dm
index 4112d043e5f..24adc1185e2 100644
--- a/code/game/landmarks/spawnpoint/tradeport.dm
+++ b/code/game/landmarks/spawnpoint/tradeport.dm
@@ -1,6 +1,6 @@
/obj/landmark/spawnpoint/job/trader
name = "Trader"
- job_path = /datum/job/trader
+ job_path = /datum/role/job/trader
latejoin = TRUE
latejoin_override = TRUE
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 92b95298c30..f164a14d74b 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -80,7 +80,7 @@
if(clonemind.current && clonemind.current.stat != DEAD) // Mind is associated with a non-dead body.
return FALSE
if(clonemind.active) // Somebody is using that mind.
- if(ckey(clonemind.key) != R.ckey)
+ if(clonemind.ckey != R.ckey)
return FALSE
else
for(var/mob/observer/dead/G in GLOB.player_list)
diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm
index 56e21f307a7..80253bb8c12 100644
--- a/code/game/machinery/computer/card.dm
+++ b/code/game/machinery/computer/card.dm
@@ -219,7 +219,7 @@
if(is_centcom())
access = get_centcom_access(t1)
else
- var/datum/job/jobdatum = SSjob.get_job(t1)
+ var/datum/role/job/jobdatum = SSjob.get_job(t1)
if(!jobdatum)
to_chat(usr, "No log exists for this job: [t1]")
return
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 8cc62969653..4870e1de2b9 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -322,9 +322,9 @@
return
if ((!subject.ckey) || (!subject.client))
scantemp = "Error: Mental interface failure."
- if(subject.stat == DEAD && subject.mind && subject.mind.key) // If they're dead and not in their body, tell them to get in it.
+ if(subject.stat == DEAD && subject.mind && subject.mind.ckey) // If they're dead and not in their body, tell them to get in it.
for(var/mob/observer/dead/ghost in GLOB.player_list)
- if(ghost.ckey == ckey(subject.mind.key))
+ if(ghost.ckey == ckey(subject.mind.ckey))
ghost.notify_revive("Someone is trying to scan your body in the cloner. Re-enter your body if you want to be revived!", 'sound/effects/genetics.ogg')
break
return
diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm
index a7fce3060c4..6f20c867eff 100644
--- a/code/game/machinery/computer/timeclock_vr.dm
+++ b/code/game/machinery/computer/timeclock_vr.dm
@@ -92,7 +92,7 @@
if(card)
data["card"] = "[card]"
data["assignment"] = card.assignment
- var/datum/job/job = SSjob.get_job(card.rank)
+ var/datum/role/job/job = SSjob.get_job(card.rank)
if(job)
data["job_datum"] = list(
"title" = job.title,
@@ -145,19 +145,21 @@
update_icon()
return TRUE
-
/obj/machinery/computer/timeclock/proc/getOpenOnDutyJobs(var/mob/user, var/department)
var/list/available_jobs = list()
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/role/job/job in SSjob.occupations)
if(isOpenOnDutyJob(user, department, job))
- available_jobs[job.title] = list(job.title)
- if(job.alt_titles)
- for(var/alt_job in job.alt_titles)
- if(alt_job != job.title)
- available_jobs[job.title] += alt_job
+ var/list/titles = available_titles(user, job)
+ if(!length(titles))
+ continue
+ available_jobs[job.title] = titles
return available_jobs
-/obj/machinery/computer/timeclock/proc/isOpenOnDutyJob(var/mob/user, var/department, var/datum/job/job)
+/obj/machinery/computer/timeclock/proc/available_titles(mob/user, var/datum/role/job/job)
+ var/list/datum/lore/character_background/backgrounds = user.mind?.original_background_datums()
+ return job.alt_title_query(backgrounds)
+
+/obj/machinery/computer/timeclock/proc/isOpenOnDutyJob(var/mob/user, var/department, var/datum/role/job/job)
return job \
&& job.is_position_available() \
&& !job.whitelist_only \
@@ -165,15 +167,18 @@
&& job.player_old_enough(user.client) \
&& job.pto_type == department \
&& !job.disallow_jobhop \
- && job.timeoff_factor > 0
+ && job.timeoff_factor > 0 \
+ && (job.check_mob_availability_one(user) == ROLE_AVAILABLE)
/obj/machinery/computer/timeclock/proc/makeOnDuty(var/newrank, var/newassignment)
- var/datum/job/oldjob = SSjob.get_job(card.rank)
- var/datum/job/newjob = SSjob.get_job(newrank)
+ var/datum/role/job/oldjob = SSjob.get_job(card.rank)
+ var/datum/role/job/newjob = SSjob.get_job(newrank)
if(!oldjob || !isOpenOnDutyJob(usr, oldjob.pto_type, newjob))
return
if(newassignment != newjob.title && !(newassignment in newjob.alt_titles))
return
+ if(!newjob.alt_title_check(newassignment, usr.mind?.original_background_datums()))
+ return
if(newjob)
card.access = newjob.get_access()
card.rank = newjob.title
@@ -190,12 +195,12 @@
return
/obj/machinery/computer/timeclock/proc/makeOffDuty()
- var/datum/job/foundjob = SSjob.get_job(card.rank)
+ var/datum/role/job/foundjob = SSjob.get_job(card.rank)
if(!foundjob)
return
var/new_dept = foundjob.pto_type || PTO_CIVILIAN
- var/datum/job/ptojob = null
- for(var/datum/job/job in SSjob.occupations)
+ var/datum/role/job/ptojob = null
+ for(var/datum/role/job/job in SSjob.occupations)
if(job.pto_type == new_dept && job.timeoff_factor < 0)
ptojob = job
break
diff --git a/code/game/objects/items/devices/scanners_vr.dm b/code/game/objects/items/devices/scanners_vr.dm
index 40d104fbf21..6ceb8bd08d9 100644
--- a/code/game/objects/items/devices/scanners_vr.dm
+++ b/code/game/objects/items/devices/scanners_vr.dm
@@ -77,9 +77,9 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob
output += "Sleeve Pair: "
if(!H.ckey)
output += "No mind in that body [stored_mind != null ? "\[Upload\]" : null]
"
- else if(H.mind && ckey(H.mind.key) != H.ckey)
+ else if(H.mind && H.mind.ckey != H.ckey)
output += "May not be correct body
"
- else if(H.mind && ckey(H.mind.key) == H.ckey)
+ else if(H.mind && H.mind.ckey == H.ckey)
output += "Appears to be correct mind in body
"
else
output += "Unable to perform comparison
"
diff --git a/code/game/objects/items/id_cards/station_ids.dm b/code/game/objects/items/id_cards/station_ids.dm
index 0d2c3cbc09a..2de8877df28 100644
--- a/code/game/objects/items/id_cards/station_ids.dm
+++ b/code/game/objects/items/id_cards/station_ids.dm
@@ -24,7 +24,7 @@
var/primary_color = rgb(0,0,0) // Obtained by eyedroppering the stripe in the middle of the card
var/secondary_color = rgb(0,0,0) // Likewise for the oval in the top-left corner
- var/datum/job/job_access_type = /datum/job/station/assistant // Job type to acquire access rights from, if any
+ var/datum/role/job/job_access_type = /datum/role/job/station/assistant // Job type to acquire access rights from, if any
//alt titles are handled a bit weirdly in order to unobtrusively integrate into existing ID system
var/assignment = null //can be alt title or the actual job
@@ -124,7 +124,7 @@
/obj/item/card/id/Initialize(mapload)
. = ..()
- var/datum/job/J = SSjob.get_job(rank)
+ var/datum/role/job/J = SSjob.get_job(rank)
if(J)
access = J.get_access()
@@ -138,14 +138,14 @@
name = "secretary ID"
assignment = "Command Secretary"
rank = "Command Secretary"
- job_access_type = /datum/job/station/command_secretary
+ job_access_type = /datum/role/job/station/command_secretary
/obj/item/card/id/silver/hop
name = "\improper HoP ID"
assignment = "Head of Personnel"
rank = "Head of Personnel"
desc = "A card which represents the balance between those that serve and those that are served."
- job_access_type = /datum/job/station/head_of_personnel
+ job_access_type = /datum/role/job/station/head_of_personnel
/obj/item/card/id/gold
name = "gold identification card"
@@ -158,14 +158,14 @@
name = "\improper Facility Director's ID"
assignment = "Facility Director"
rank = "Facility Director"
- job_access_type = /datum/job/station/captain
+ job_access_type = /datum/role/job/station/captain
/obj/item/card/id/gold/captain/spare
name = "\improper Facility Director's spare ID"
desc = "The spare ID of the High Lord himself."
registered_name = "Facility Director"
icon_state = "gold-id-alternate"
- job_access_type = /datum/job/station/captain
+ job_access_type = /datum/role/job/station/captain
/obj/item/card/id/synthetic
name = "\improper Synthetic ID"
@@ -229,31 +229,31 @@
name = "doctor ID"
assignment = "Medical Doctor"
rank = "Medical Doctor"
- job_access_type = /datum/job/station/doctor
+ job_access_type = /datum/role/job/station/doctor
/obj/item/card/id/medical/chemist
name = "chemist ID"
assignment = "Chemist"
rank = "Chemist"
- job_access_type = /datum/job/station/chemist
+ job_access_type = /datum/role/job/station/chemist
/obj/item/card/id/medical/geneticist
name = "geneticist ID"
assignment = "Geneticist"
rank = "Geneticist"
- job_access_type = /datum/job/station/doctor //geneticist
+ job_access_type = /datum/role/job/station/doctor //geneticist
/obj/item/card/id/medical/psychiatrist
name = "psychiatrist ID"
assignment = "Psychiatrist"
rank = "Psychiatrist"
- job_access_type = /datum/job/station/psychiatrist
+ job_access_type = /datum/role/job/station/psychiatrist
/obj/item/card/id/medical/paramedic
name = "paramedic ID"
assignment = "Paramedic"
rank = "Paramedic"
- job_access_type = /datum/job/station/paramedic
+ job_access_type = /datum/role/job/station/paramedic
/obj/item/card/id/medical/head
name = "\improper CMO ID"
@@ -262,7 +262,7 @@
secondary_color = rgb(255,223,127)
assignment = "Chief Medical Officer"
rank = "Chief Medical Officer"
- job_access_type = /datum/job/station/chief_medical_officer
+ job_access_type = /datum/role/job/station/chief_medical_officer
/obj/item/card/id/security
name = "security identification card"
@@ -275,19 +275,19 @@
name = "officer ID"
assignment = "Security Officer"
rank = "Security Officer"
- job_access_type = /datum/job/station/officer
+ job_access_type = /datum/role/job/station/officer
/obj/item/card/id/security/detective
name = "detective ID"
assignment = "Detective"
rank = "Detective"
- job_access_type = /datum/job/station/detective
+ job_access_type = /datum/role/job/station/detective
/obj/item/card/id/security/warden
name = "warden ID"
assignment = "Warden"
rank = "Warden"
- job_access_type = /datum/job/station/warden
+ job_access_type = /datum/role/job/station/warden
/obj/item/card/id/security/head
name = "\improper HoS ID"
@@ -296,7 +296,7 @@
secondary_color = rgb(255,223,127)
assignment = "Head of Security"
rank = "Head of Security"
- job_access_type = /datum/job/station/head_of_security
+ job_access_type = /datum/role/job/station/head_of_security
/obj/item/card/id/engineering
name = "engineering identification card"
@@ -309,13 +309,13 @@
name = "engineer ID"
assignment = "Station Engineer"
rank = "Station Engineer"
- job_access_type = /datum/job/station/engineer
+ job_access_type = /datum/role/job/station/engineer
/obj/item/card/id/engineering/atmos
name = "atmospherics ID"
assignment = "Atmospheric Technician"
rank = "Atmospheric Technician"
- job_access_type = /datum/job/station/atmos
+ job_access_type = /datum/role/job/station/atmos
/obj/item/card/id/engineering/head
name = "\improper CE ID"
@@ -324,7 +324,7 @@
secondary_color = rgb(255,223,127)
assignment = "Chief Engineer"
rank = "Chief Engineer"
- job_access_type = /datum/job/station/chief_engineer
+ job_access_type = /datum/role/job/station/chief_engineer
/obj/item/card/id/science
name = "science identification card"
@@ -337,19 +337,19 @@
name = "scientist ID"
assignment = "Scientist"
rank = "Scientist"
- job_access_type = /datum/job/station/scientist
+ job_access_type = /datum/role/job/station/scientist
/obj/item/card/id/science/xenobiologist
name = "xenobiologist ID"
assignment = "Xenobiologist"
rank = "Xenobiologist"
- job_access_type = /datum/job/station/scientist // /datum/job/station/xenobiologist
+ job_access_type = /datum/role/job/station/scientist // /datum/role/job/station/xenobiologist
/obj/item/card/id/science/roboticist
name = "roboticist ID"
assignment = "Roboticist"
rank = "Roboticist"
- job_access_type = /datum/job/station/roboticist
+ job_access_type = /datum/role/job/station/roboticist
/obj/item/card/id/science/head
name = "\improper RD ID"
@@ -358,7 +358,7 @@
secondary_color = rgb(255,223,127)
assignment = "Research Director"
rank = "Research Director"
- job_access_type = /datum/job/station/research_director
+ job_access_type = /datum/role/job/station/research_director
/obj/item/card/id/cargo
name = "cargo identification card"
@@ -371,13 +371,13 @@
name = "cargo ID"
assignment = "Cargo Technician"
rank = "Cargo Technician"
- job_access_type = /datum/job/station/cargo_tech
+ job_access_type = /datum/role/job/station/cargo_tech
/obj/item/card/id/cargo/mining
name = "mining ID"
assignment = "Shaft Miner"
rank = "Shaft Miner"
- job_access_type = /datum/job/station/mining
+ job_access_type = /datum/role/job/station/mining
/obj/item/card/id/cargo/head
name = "\improper Quartermaster's ID"
@@ -386,12 +386,12 @@
secondary_color = rgb(255,223,127)
assignment = "Quartermaster"
rank = "Quartermaster"
- job_access_type = /datum/job/station/quartermaster
+ job_access_type = /datum/role/job/station/quartermaster
/obj/item/card/id/assistant
assignment = USELESS_JOB
rank = USELESS_JOB
- job_access_type = /datum/job/station/assistant
+ job_access_type = /datum/role/job/station/assistant
/obj/item/card/id/civilian
name = "civilian identification card"
@@ -401,61 +401,61 @@
secondary_color = rgb(95,159,191)
assignment = "Civilian"
rank = "Assistant"
- job_access_type = /datum/job/station/assistant
+ job_access_type = /datum/role/job/station/assistant
/obj/item/card/id/civilian/bartender
name = "bartender ID"
assignment = "Bartender"
rank = "Bartender"
- job_access_type = /datum/job/station/bartender
+ job_access_type = /datum/role/job/station/bartender
/obj/item/card/id/civilian/botanist
name = "botanist ID"
assignment = "Botanist"
rank = "Botanist"
- job_access_type = /datum/job/station/hydro
+ job_access_type = /datum/role/job/station/hydro
/obj/item/card/id/civilian/chaplain
name = "chaplain ID"
assignment = "Chaplain"
rank = "Chaplain"
- job_access_type = /datum/job/station/chaplain
+ job_access_type = /datum/role/job/station/chaplain
/obj/item/card/id/civilian/chef
name = "chef ID"
assignment = "Chef"
rank = "Chef"
- job_access_type = /datum/job/station/chef
+ job_access_type = /datum/role/job/station/chef
/obj/item/card/id/civilian/internal_affairs_agent
name = "internal affairs ID"
assignment = "Internal Affairs Agent"
rank = "Internal Affairs Agent"
- job_access_type = /datum/job/station/lawyer
+ job_access_type = /datum/role/job/station/lawyer
/obj/item/card/id/civilian/janitor
name = "janitor ID"
assignment = "Janitor"
rank = "Janitor"
- job_access_type = /datum/job/station/janitor
+ job_access_type = /datum/role/job/station/janitor
/obj/item/card/id/civilian/librarian
name = "librarian ID"
assignment = "Librarian"
rank = "Librarian"
- job_access_type = /datum/job/station/librarian
+ job_access_type = /datum/role/job/station/librarian
/obj/item/card/id/civilian/clown
name = "clown ID"
assignment = "Clown"
rank = "Clown"
- job_access_type = /datum/job/station/clown
+ job_access_type = /datum/role/job/station/clown
/obj/item/card/id/civilian/mime
name = "mime ID"
assignment = "Mime"
rank = "Mime"
- job_access_type = /datum/job/station/mime
+ job_access_type = /datum/role/job/station/mime
/obj/item/card/id/civilian/head //This is not the HoP. There's no position that uses this right now.
name = "\improper Services Officer ID"
@@ -511,7 +511,7 @@
rank = "Field Medic"
primary_color = rgb(47,189,189)
secondary_color = rgb(127,223,223)
- job_access_type = /datum/job/station/field_medic
+ job_access_type = /datum/role/job/station/field_medic
/obj/item/card/id/explorer
name = "identification card"
@@ -522,12 +522,12 @@
/obj/item/card/id/explorer/pilot
assignment = "Pilot"
rank = "Pilot"
- job_access_type = /datum/job/station/pilot
+ job_access_type = /datum/role/job/station/pilot
/obj/item/card/id/explorer/explorer
assignment = "Explorer"
rank = "Explorer"
- job_access_type = /datum/job/station/explorer
+ job_access_type = /datum/role/job/station/explorer
/obj/item/card/id/explorer/head
name = "identification card"
@@ -539,4 +539,4 @@
/obj/item/card/id/explorer/head/pathfinder
assignment = "Pathfinder"
rank = "Pathfinder"
- job_access_type = /datum/job/station/pathfinder
+ job_access_type = /datum/role/job/station/pathfinder
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 8ae9e760259..9122cef6648 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -208,10 +208,7 @@
if(..())
return FALSE
- if(istype(R.translation_context, /datum/translation_context/simple/silicons))
- qdel(R.translation_context)
- R.translation_context = new /datum/translation_context/variable/learning/silicons
- R.sync_translation_context()
+ R.create_translation_context(/datum/translation_context/variable/learning/silicons)
return TRUE
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 96862c1a4c9..baa75a97412 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -1021,7 +1021,7 @@ var/list/admin_verbs_event_manager = list(
set category = "Admin"
if(holder)
var/list/jobs = list()
- for (var/datum/job/J in SSjob.occupations)
+ for (var/datum/role/job/J in SSjob.occupations)
if (J.current_positions >= J.total_positions && J.total_positions != -1)
jobs += J.title
if (!jobs.len)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 5736759ab2a..c12495f5c76 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -397,7 +397,7 @@
jobs += "| Command Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -418,7 +418,7 @@
jobs += "
| Security Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -439,7 +439,7 @@
jobs += "
| Engineering Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -460,7 +460,7 @@
jobs += "
| Cargo Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -481,7 +481,7 @@
jobs += "
| Medical Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -502,7 +502,7 @@
jobs += "
| Science Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -522,7 +522,7 @@
jobs += "
| Science Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -542,7 +542,7 @@
jobs += "
| Civilian Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -569,7 +569,7 @@
jobs += "
| Non-human Positions |
|---|
"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC))
if(!jobPos) continue
- var/datum/job/job = SSjob.get_job(jobPos)
+ var/datum/role/job/job = SSjob.get_job(jobPos)
if(!job) continue
if(jobban_isbanned(M, job.title))
@@ -656,56 +656,56 @@
if("commanddept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("securitydept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SECURITY))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("engineeringdept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_ENGINEERING))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("cargodept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CARGO))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("medicaldept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_MEDICAL))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("sciencedept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_RESEARCH))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("explorationdept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_PLANET))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("civiliandept")
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_CIVILIAN))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
if("nonhumandept")
joblist += "pAI"
for(var/jobPos in SSjob.get_job_titles_in_department(DEPARTMENT_SYNTHETIC))
if(!jobPos) continue
- var/datum/job/temp = SSjob.get_job(jobPos)
+ var/datum/role/job/temp = SSjob.get_job(jobPos)
if(!temp) continue
joblist += temp.title
else
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 8846f40967d..870585ef2d4 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -164,6 +164,7 @@
*/
+// todo: set_state(state) proc so this is less of a snowflake mess
#define SDQL2_STATE_ERROR 0
#define SDQL2_STATE_IDLE 1
@@ -184,16 +185,18 @@
#define SDQL2_OPTIONS_DEFAULT (SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
-#define SDQL2_IS_RUNNING (state == SDQL2_STATE_EXECUTING || state == SDQL2_STATE_SEARCHING || state == SDQL2_STATE_SWITCHING || state == SDQL2_STATE_PRESEARCH)
-#define SDQL2_HALT_CHECK if(!SDQL2_IS_RUNNING) {state = SDQL2_STATE_HALTING; return FALSE;};
+#define SDQL2_IS_RUNNING (running == TRUE)
+#define SDQL2_HALT_CHECK if(running == FALSE) {state = SDQL2_STATE_HALTING; running = FALSE; return FALSE;};
-#define SDQL2_TICK_CHECK ((options & SDQL2_OPTION_HIGH_PRIORITY)? CHECK_TICK_HIGH_PRIORITY : CHECK_TICK)
+#define SDQL2_TICK_CHECK ((high_priority == TRUE)? CHECK_TICK_HIGH_PRIORITY : CHECK_TICK)
#define SDQL2_STAGE_SWITCH_CHECK if(state != SDQL2_STATE_SWITCHING){\
if(state == SDQL2_STATE_HALTING){\
state = SDQL2_STATE_IDLE;\
+ running = FALSE;\
return FALSE}\
state = SDQL2_STATE_ERROR;\
+ running = FALSE;\
CRASH("SDQL2 fatal error");};
/client/proc/SDQL2_query(query_text as message)
@@ -282,8 +285,12 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
/datum/SDQL2_query
var/list/query_tree
+ /// is running? separate for fast-ness
+ var/running = FALSE
var/state = SDQL2_STATE_IDLE
var/options = SDQL2_OPTIONS_DEFAULT
+ /// high priority? separate for fast-ness
+ var/high_priority = FALSE
var/superuser = FALSE //Run things like proccalls without using admin protections
var/allow_admin_interact = TRUE //Allow admins to do things to this excluding varedit these two vars
var/static/id_assign = 1
@@ -325,6 +332,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
/datum/SDQL2_query/Destroy()
state = SDQL2_STATE_HALTING
+ running = FALSE
query_tree = null
obj_count_all = null
obj_count_eligible = null
@@ -419,6 +427,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
message_admins(msg)
log_admin(msg)
state = SDQL2_STATE_HALTING
+ running = FALSE
/datum/SDQL2_query/proc/admin_run(mob/user = usr)
if(SDQL2_IS_RUNNING)
@@ -449,6 +458,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
switch(value)
if("high")
options |= SDQL2_OPTION_HIGH_PRIORITY
+ high_priority = TRUE
if("autogc")
switch(value)
if("keep_alive")
@@ -470,6 +480,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
obj_count_eligible = 0
obj_count_finished = 0
start_time = REALTIMEOFDAY
+ running = TRUE
state = SDQL2_STATE_PRESEARCH
var/list/search_tree = PreSearch()
@@ -485,6 +496,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
end_time = REALTIMEOFDAY
state = SDQL2_STATE_IDLE
+ running = FALSE
finished = TRUE
. = TRUE
if(show_next_to_key)
@@ -509,6 +521,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
if("explain")
SDQL_testout(query_tree["explain"])
state = SDQL2_STATE_HALTING
+ running = FALSE
return
if("call")
. = query_tree["on"]
diff --git a/code/modules/admin/verbs/debug/reestablish_db_connection.dm b/code/modules/admin/verbs/debug/reestablish_db_connection.dm
index f40e05f1cfc..33f0e9d8524 100644
--- a/code/modules/admin/verbs/debug/reestablish_db_connection.dm
+++ b/code/modules/admin/verbs/debug/reestablish_db_connection.dm
@@ -30,4 +30,4 @@
message_admins("Database connection re-established")
message_admins("Reloading client database data...")
for(var/client/C in GLOB.clients)
- C.database?.load()
+ C.player?.load()
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 68aa57e2ab9..2c9616ff3d2 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -630,7 +630,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
to_chat(src, "Only administrators may use this command.")
return
if(SSjob)
- for(var/datum/job/job in SSjob.occupations)
+ for(var/datum/role/job/job in SSjob.occupations)
to_chat(src, "[job.title]: [job.total_positions]")
feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm
index ac3de9fee47..390429d41dc 100644
--- a/code/modules/admin/verbs/tripAI.dm
+++ b/code/modules/admin/verbs/tripAI.dm
@@ -7,7 +7,7 @@
return
if(SSjob && SSticker)
- var/datum/job/job = SSjob.get_job("AI")
+ var/datum/role/job/job = SSjob.get_job("AI")
if(!job)
to_chat(usr, "Unable to locate the AI job")
return
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
index 367e1dace0b..58a215d74cd 100644
--- a/code/modules/admin/view_variables/debug_variables.dm
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -48,8 +48,12 @@
else if (islist(value))
var/list/L = value
var/list/items = list()
-
- if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD)))
+ // don't expand if it's:
+ // 1. overlays - this info is rarely needing to be accessed unless you're doing overlay debugging
+ // 2. underlays - ditto
+ // 3. GLOB - there's a metric ton of lists on global variables and we want to avoid admins needing to download MB's of data instantly
+ // 4. if the list is too long otherwise
+ if (L.len > 0 && !(name == "underlays" || name == "overlays" || D == GLOB || L.len > (IS_NORMAL_LIST(L) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD)))
for (var/i in 1 to L.len)
var/key = L[i]
var/val
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index d8971d6d7c6..6de67a81b73 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -59,9 +59,9 @@
M.equip_to_slot_or_del(new src.corpseback(M), SLOT_ID_BACK)
if(src.corpseid == 1)
var/obj/item/card/id/W = new(M)
- var/datum/job/jobdatum
- for(var/jobtype in typesof(/datum/job))
- var/datum/job/J = new jobtype
+ var/datum/role/job/jobdatum
+ for(var/jobtype in typesof(/datum/role/job))
+ var/datum/role/job/J = new jobtype
if(J.title == corpseidaccess)
jobdatum = J
break
diff --git a/code/modules/client/client.dm b/code/modules/client/client.dm
index ceb710ef588..e51461aa8d6 100644
--- a/code/modules/client/client.dm
+++ b/code/modules/client/client.dm
@@ -42,7 +42,7 @@
/// Persistent round-by-round data holder
var/datum/client_data/persistent
/// Database data
- var/datum/client_dbdata/database
+ var/datum/player_data/player
//! Rendering
/// Click catcher
diff --git a/code/modules/client/data.dm b/code/modules/client/client_data.dm
similarity index 100%
rename from code/modules/client/data.dm
rename to code/modules/client/client_data.dm
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 1fdb918a2ed..8748a10531d 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -280,13 +280,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
log_admin_private("Notice: [key_name(src)] has the same [matches] as [key_name(C)] (no longer logged in).")
- if(GLOB.player_details[ckey])
- player_details = GLOB.player_details[ckey]
- player_details.byond_version = full_version
- else
- player_details = new(ckey)
- player_details.byond_version = full_version
- GLOB.player_details[ckey] = player_details
*/
//! WARNING: mob.login is always called async, aka immediately returns on sleep.
@@ -303,8 +296,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
// resolve database data
// this is down here because player_lookup won't have an entry for us until log_client_to_db() runs!!
- database = new(ckey)
- database.log_connect()
+ player = new(ckey)
+ player.log_connect()
if (byond_version >= 512)
if (!byond_build || byond_build < 1386)
@@ -450,7 +443,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
log_access("Logout: [key_name(src)]")
GLOB.ahelp_tickets.ClientLogout(src)
persistent = null
- database = null
+ player = null
if(prefs)
prefs.client = null
prefs = null
diff --git a/code/modules/client/database.dm b/code/modules/client/player_data.dm
similarity index 88%
rename from code/modules/client/database.dm
rename to code/modules/client/player_data.dm
index f1dcd867fd1..d2ed3d8f9ee 100644
--- a/code/modules/client/database.dm
+++ b/code/modules/client/player_data.dm
@@ -2,7 +2,7 @@
* holds db-related data
* loaded every connect
*/
-/datum/client_dbdata
+/datum/player_data
//! intrinsics
/// our ckey
var/ckey
@@ -23,7 +23,7 @@
/// player age
var/player_age
-/datum/client_dbdata/New(ckey)
+/datum/player_data/New(ckey)
src.ckey = ckey
if(!src.ckey)
return
@@ -32,7 +32,7 @@
/**
* async
*/
-/datum/client_dbdata/proc/load()
+/datum/player_data/proc/load()
if(!SSdbcore.Connect())
if(isnull(available))
available = FALSE
@@ -40,7 +40,7 @@
INVOKE_ASYNC(src, .proc/load_blocking)
return TRUE
-/datum/client_dbdata/proc/load_blocking()
+/datum/player_data/proc/load_blocking()
// allow admin proccalls - there's no args here.
var/was_proccall = !!IsAdminAdvancedProcCall()
var/old_usr = usr
@@ -48,7 +48,7 @@
_load_lock(was_proccall)
usr = old_usr
-/datum/client_dbdata/proc/_load_lock(was_proccall)
+/datum/player_data/proc/_load_lock(was_proccall)
if(IsAdminAdvancedProcCall())
return
if(loading)
@@ -59,7 +59,7 @@
_load()
loading = FALSE
-/datum/client_dbdata/proc/_load()
+/datum/player_data/proc/_load()
if(IsAdminAdvancedProcCall())
return
var/datum/db_query/lookup
@@ -105,7 +105,7 @@
qdel(lookup)
register_new_player(lookup_firstseen, lookup_id)
-/datum/client_dbdata/proc/register_new_player(migrate_firstseen, lookup_id)
+/datum/player_data/proc/register_new_player(migrate_firstseen, lookup_id)
if(IsAdminAdvancedProcCall())
return
// new person!
@@ -149,7 +149,7 @@
/**
* async
*/
-/datum/client_dbdata/proc/save()
+/datum/player_data/proc/save()
set waitfor = FALSE
// why are we in here if we're write locked?
if(saving)
@@ -168,7 +168,7 @@
saving = FALSE
usr = old_usr
-/datum/client_dbdata/proc/_save()
+/datum/player_data/proc/_save()
qdel(SSdbcore.ExecuteQuery(
"UPDATE [format_table_name("player")] SET flags = :flags WHERE id = :id",
list(
@@ -180,11 +180,11 @@
/**
* async
*/
-/datum/client_dbdata/proc/log_connect()
+/datum/player_data/proc/log_connect()
set waitfor = FALSE
update_last_seen()
-/datum/client_dbdata/proc/update_last_seen()
+/datum/player_data/proc/update_last_seen()
// don't interrupt
if(!block_on_available())
return FALSE
@@ -199,7 +199,7 @@
/**
* sync
*/
-/datum/client_dbdata/proc/player_age()
+/datum/player_data/proc/player_age()
UNTIL(!loading)
return player_age
@@ -207,7 +207,7 @@
* block until we know if we're available
* then return if we are
*/
-/datum/client_dbdata/proc/block_on_available()
+/datum/player_data/proc/block_on_available()
UNTIL(!isnull(available))
return available
@@ -215,5 +215,5 @@
* returns if we're available
* if we don't know yet, return false
*/
-/datum/client_dbdata/proc/immediately_available()
+/datum/player_data/proc/immediately_available()
return !!available
diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm
deleted file mode 100644
index 5e00e61c1df..00000000000
--- a/code/modules/client/player_details.dm
+++ /dev/null
@@ -1,28 +0,0 @@
-
-///assoc list of ckey -> /datum/player_details
-GLOBAL_LIST_EMPTY(player_details)
-
-// todo: roll into client_data datums
-/datum/player_details
- var/list/player_actions = list()
- var/list/logging = list()
- var/list/post_login_callbacks = list()
- var/list/post_logout_callbacks = list()
- var/list/played_names = list() //List of names this key played under this round
- var/byond_version = "Unknown"
-/* var/datum/achievement_data/achievements
-
-/datum/player_details/New(key)
- achievements = new(key)
-*/
-/proc/log_played_names(ckey, ...)
- if(!ckey)
- return
- if(args.len < 2)
- return
- var/list/names = args.Copy(2)
- var/datum/player_details/P = GLOB.player_details[ckey]
- if(P)
- for(var/name in names)
- if(name)
- P.played_names |= name
diff --git a/code/modules/client/winset_wrappers.dm b/code/modules/client/wrappers.dm
similarity index 100%
rename from code/modules/client/winset_wrappers.dm
rename to code/modules/client/wrappers.dm
diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm
index fef2bb72452..ffffd337146 100644
--- a/code/modules/events/meteors.dm
+++ b/code/modules/events/meteors.dm
@@ -93,10 +93,8 @@
. = ..()
if(!victim)
return
- var/skill = victim.get_helm_skill()
+ // todo: implement skill checks with math on this, do actual overmaps physics
var/speed = victim.get_speed_legacy()
- if(skill >= SKILL_PROF)
- . = round(. * 0.5)
if(!victim.is_moving()) // Standing still means less shit flies your way
. = round(. * 0.1)
if(speed < SHIP_SPEED_SLOW) // Slow and steady
@@ -104,12 +102,3 @@
if(speed > SHIP_SPEED_FAST) // Sanic stahp
. *= 2
- // Smol ship evasion
- if(victim.vessel_size < SHIP_SIZE_LARGE && speed < SHIP_SPEED_FAST)
- var/skill_needed = SKILL_PROF
- if(speed < SHIP_SPEED_SLOW)
- skill_needed = SKILL_ADEPT
- if(victim.vessel_size < SHIP_SIZE_SMALL)
- skill_needed = skill_needed - 1
- if(skill >= max(skill_needed, victim.skill_needed))
- . = round(. * 0.5)
diff --git a/code/modules/ghostroles/instantiator.dm b/code/modules/ghostroles/instantiator.dm
index 0f18a669f42..e7865ec97c3 100644
--- a/code/modules/ghostroles/instantiator.dm
+++ b/code/modules/ghostroles/instantiator.dm
@@ -159,9 +159,11 @@
/datum/ghostrole_instantiator/human/player_static/Create(client/C, atom/location, list/params)
var/mob/living/carbon/human/H = ..()
var/list/errors = list()
+ // todo: respect warnings; we ignore them right now so we don't block joins.
if(!C.prefs.spawn_checks(PREF_COPY_TO_FOR_GHOSTROLE, errors))
to_chat(C, SPAN_WARNING("An error has occured while attempting to spawn you in:
[errors.Join("
")]"))
return
+
LoadSavefile(C, H)
return H
diff --git a/code/modules/ghostroles/menu.dm b/code/modules/ghostroles/menu.dm
index 1763e6d3603..07cdf83d827 100644
--- a/code/modules/ghostroles/menu.dm
+++ b/code/modules/ghostroles/menu.dm
@@ -16,7 +16,7 @@ GLOBAL_DATUM_INIT(ghostrole_menu, /datum/ghostrole_menu, new)
var/list/spawners = list()
.["spawners"] = spawners
for(var/id in GLOB.ghostroles)
- var/datum/ghostrole/role = GLOB.ghostroles[id]
+ var/datum/role/ghostrole/role = GLOB.ghostroles[id]
if(!istype(role))
stack_trace("non ghostrole [role] ([id]) pruned from ghostroles list.")
GLOB.ghostroles -= id
@@ -37,7 +37,7 @@ GLOBAL_DATUM_INIT(ghostrole_menu, /datum/ghostrole_menu, new)
if(!isobserver(usr))
return
var/id = params["id"]
- var/datum/ghostrole/role = get_ghostrole_datum(id)
+ var/datum/role/ghostrole/role = get_ghostrole_datum(id)
if(!role)
return
switch(action)
diff --git a/code/modules/ghostroles/role.dm b/code/modules/ghostroles/role.dm
index a3aa1cf2333..08052c27cbd 100644
--- a/code/modules/ghostroles/role.dm
+++ b/code/modules/ghostroles/role.dm
@@ -2,8 +2,8 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
/proc/init_ghostroles()
. = list()
- for(var/path in subtypesof(/datum/ghostrole))
- var/datum/ghostrole/G = path
+ for(var/path in subtypesof(/datum/role/ghostrole))
+ var/datum/role/ghostrole/G = path
if(initial(G.abstract_type) == path)
continue
if(initial(G.lazy_init))
@@ -17,16 +17,16 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
if(GLOB.ghostroles[path])
return GLOB.ghostroles[path]
var/is_this_a_path = ispath(path)? path : text2path(path)
- if(ispath(is_this_a_path, /datum/ghostrole))
+ if(ispath(is_this_a_path, /datum/role/ghostrole))
GLOB.ghostroles[is_this_a_path] = new is_this_a_path
return GLOB.ghostroles[is_this_a_path]
/**
* Ghostrole datums
*/
-/datum/ghostrole
+/datum/role/ghostrole
/// Abstract type.
- abstract_type = /datum/ghostrole
+ abstract_type = /datum/role/ghostrole
/// name
var/name = "Unnamed Role"
@@ -61,12 +61,12 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
/// inject params during spawning
var/list/inject_params
-/datum/ghostrole/New(_id)
+/datum/role/ghostrole/New(_id)
if(ispath(instantiator, /datum/ghostrole_instantiator))
instantiator = new instantiator
id = _id || type
-/datum/ghostrole/proc/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/proc/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
if(show_standard_greeting)
to_chat(created, "You have spawned as a ghostrole. These roles should be taken seriously. Be sure to follow the directives in your spawntext (if any), as well as the server rules. Beyond that, roleplay your character however you see fit! Spawntext as follows;
")
if(spawntext)
@@ -74,7 +74,7 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
if(spawnpoint.spawntext)
to_chat(created, "[spawntext]
")
-/datum/ghostrole/proc/ImportantInfo(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/proc/ImportantInfo(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
if(important_info)
to_chat(created, "[important_info]
")
@@ -83,7 +83,7 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
*
* Return TRUe on success, or a string of why it failed.
*/
-/datum/ghostrole/proc/AttemptSpawn(client/C, datum/component/ghostrole_spawnpoint/chosen_spawnpoint)
+/datum/role/ghostrole/proc/AttemptSpawn(client/C, datum/component/ghostrole_spawnpoint/chosen_spawnpoint)
if(BanCheck(C))
return "You can't spawn as [src] due to an active job-ban."
if(!AllowSpawn(C))
@@ -112,13 +112,13 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
GLOB.ghostrole_menu.queue_update()
return TRUE
-/datum/ghostrole/proc/Instantiate(client/C, atom/loc, list/params)
+/datum/role/ghostrole/proc/Instantiate(client/C, atom/loc, list/params)
var/mob/living/L = instantiator.Run(C, loc, params)
. = istype(L) && L
if(.)
L.mind?.assigned_role = assigned_role || name
-/datum/ghostrole/proc/Transfer(client/C, mob/created)
+/datum/role/ghostrole/proc/Transfer(client/C, mob/created)
if(!isnewplayer(C.mob))
C.mob.ghostize(TRUE, TRUE)
created.ckey = C.ckey
@@ -127,25 +127,25 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
/**
* Ran before anything else is at AttemptSpawn()
*/
-/datum/ghostrole/proc/PreInstantiate(client/C)
+/datum/role/ghostrole/proc/PreInstantiate(client/C)
return TRUE
/**
* Checks if the client is a valid user mob and if we can allow a spawn from them
*/
-/datum/ghostrole/proc/AllowSpawn(client/C, list/params)
+/datum/role/ghostrole/proc/AllowSpawn(client/C, list/params)
if(!isobserver(C.mob) && !isnewplayer(C.mob))
return FALSE
if(SpawnsLeft(C) <= 0)
return FALSE
return TRUE
-/datum/ghostrole/proc/SpawnsLeft(client/C)
+/datum/role/ghostrole/proc/SpawnsLeft(client/C)
if(spawnerless)
return max(0, slots - spawns)
return min(max(0, slots - spawns), TallySpawnpointSlots(C))
-/datum/ghostrole/proc/TallySpawnpointSlots(client/C)
+/datum/role/ghostrole/proc/TallySpawnpointSlots(client/C)
var/list/datum/component/ghostrole_spawnpoint/spawnpoints = GLOB.ghostrole_spawnpoints[id]
. = 0
for(var/datum/component/ghostrole_spawnpoint/S as anything in spawnpoints)
@@ -156,7 +156,7 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
*
* For spawnerless ghostroles, return null.
*/
-/datum/ghostrole/proc/GetSpawnpoint(client/C)
+/datum/role/ghostrole/proc/GetSpawnpoint(client/C)
if(!allow_pick_spawner)
return SAFEPICK(GLOB.ghostrole_spawnpoints[id])
var/list/datum/component/ghostrole_spawnpoint/spawnpoints = GLOB.ghostrole_spawnpoints[id]
@@ -172,13 +172,13 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
*
* spawnpoint can be null for spawnerless ghostroles.
*/
-/datum/ghostrole/proc/GetSpawnLoc(client/C, datum/component/ghostrole_spawnpoint/spawnpoint)
+/datum/role/ghostrole/proc/GetSpawnLoc(client/C, datum/component/ghostrole_spawnpoint/spawnpoint)
return spawnpoint?.Turf()
/**
* Spawnpoint can be null here, if we're not using a spawnpoint
*/
-/datum/ghostrole/proc/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/proc/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
Greet(created, spawnpoint, params)
ImportantInfo(created, spawnpoint, params)
if(automatic_objective)
@@ -190,15 +190,15 @@ GLOBAL_LIST_INIT(ghostroles, init_ghostroles())
/**
* Ban check.
*/
-/datum/ghostrole/proc/BanCheck(client/C)
+/datum/role/ghostrole/proc/BanCheck(client/C)
if(!jobban_role)
return FALSE
return jobban_isbanned(C.mob, jobban_role)
-/datum/ghostrole/proc/GiveCustomObjective(mob/created, objective)
+/datum/role/ghostrole/proc/GiveCustomObjective(mob/created, objective)
created.GhostroleGiveCustomObjective(src, objective)
-/mob/proc/GhostroleGiveCustomObjective(datum/ghostrole/R, objective)
+/mob/proc/GhostroleGiveCustomObjective(datum/role/ghostrole/R, objective)
if(!mind)
mind_initialize()
if(!mind)
diff --git a/code/modules/ghostroles/roles/ashlander.dm b/code/modules/ghostroles/roles/ashlander.dm
index f42c0cb70d5..070e7178321 100644
--- a/code/modules/ghostroles/roles/ashlander.dm
+++ b/code/modules/ghostroles/roles/ashlander.dm
@@ -1,4 +1,4 @@
-/datum/ghostrole/ashlander
+/datum/role/ghostrole/ashlander
name = "Ashlander"
assigned_role = "Ashlander"
desc = "You are an Ashlander! An old and storied race of subterranean xenos."
@@ -6,7 +6,7 @@
important_info = "The nomadic Ashlanders are a neutral party. The Ashlander race (Scorian), is selected by default. If you accidentally swap, make sure to change it back. Ashlanders are all permadeath characters. They have gray skin of varying hues, red eyes, and - typically - white, black, or brown hair. These options are selectable through the appearance menu, directly below the race block, and above hairstyles. "
instantiator = /datum/ghostrole_instantiator/human/random/species/ashlander
-/datum/ghostrole/ashlander/Instantiate(client/C, atom/loc, list/params)
+/datum/role/ghostrole/ashlander/Instantiate(client/C, atom/loc, list/params)
var/rp = rand(1, 7)
switch(rp)
if(1)
@@ -25,7 +25,7 @@
params["fluff"] = "priest"
return ..()
-/datum/ghostrole/ashlander/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/ashlander/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/flavour_text = "Fine particles of ash slip past the fluttering Goliath hide covering your doorway to settle on the floor of the yurt. \
The hide is patched, and worn from years of use - it was gifted to you many Storms ago. Outside, the baking heat of the planet's surface \
@@ -120,7 +120,7 @@
icon_state = "yurt"
anchored = TRUE
density = TRUE
- role_type = /datum/ghostrole/ashlander
+ role_type = /datum/role/ghostrole/ashlander
role_spawns = 1
//var/datum/team/ashlanders/team
@@ -139,7 +139,7 @@
params["fluff"] = "sentry"
return ..()
-/datum/ghostrole/ashlander/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/ashlander/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/flavour_text = "Fine particles of ash slip past the fluttering Goliath hide covering your doorway to settle on the floor of the yurt. \
The hide is patched, and worn from years of use - it was gifted to you many Storms ago. Outside, the baking heat of the planet's surface \
@@ -213,7 +213,7 @@
else
to_chat(created, "You have awoken outside of your natural home! Whether you decide to return below the surface, or make due with your current surroundings is your own decision.")
-/datum/ghostrole/ashlander/AllowSpawn(client/C, list/params)
+/datum/role/ghostrole/ashlander/AllowSpawn(client/C, list/params)
if(params && params["team"])
var/datum/team/ashlanders/team = params["team"]
if(C.ckey in team.players_spawned)
@@ -221,7 +221,7 @@
return FALSE
return ..()
-/datum/ghostrole/ashlander/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/ashlander/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
if(params["team"])
var/datum/team/ashlanders/team = spawnpoint.params["team"]
diff --git a/code/modules/ghostroles/roles/cybersun_cruiser.dm b/code/modules/ghostroles/roles/cybersun_cruiser.dm
index b87e009ff3a..11388ef8631 100644
--- a/code/modules/ghostroles/roles/cybersun_cruiser.dm
+++ b/code/modules/ghostroles/roles/cybersun_cruiser.dm
@@ -1,8 +1,8 @@
-/datum/ghostrole/cybersun
- abstract_type = /datum/ghostrole/cybersun
+/datum/role/ghostrole/cybersun
+ abstract_type = /datum/role/ghostrole/cybersun
assigned_role = "Space Syndicate"
-/datum/ghostrole/cybersun/ship
+/datum/role/ghostrole/cybersun/ship
name = "Cybersun Ship Operative"
name = "Syndicate Battlecruiser Ship Operative"
desc = "You are a crewmember aboard the syndicate flagship: the SBC Starfury."
@@ -10,14 +10,14 @@
Furthermore, the armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives."
instantiator = /datum/ghostrole_instantiator/human/random/cybersun/ship
-/datum/ghostrole/cybersun/assault
+/datum/role/ghostrole/cybersun/assault
name = "Cybersun Assault Operative"
desc = "You are an assault operative aboard the syndicate flagship: the SBC Starfury."
spawntext = "Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with. \
Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!"
instantiator = /datum/ghostrole_instantiator/human/random/cybersun/assault
-/datum/ghostrole/cybersun/captain
+/datum/role/ghostrole/cybersun/captain
name = "Cybersun Ship Captain"
desc = "You are the captain aboard the syndicate flagship: the SBC Starfury."
spawntext = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal. \
@@ -50,13 +50,13 @@
role_type = null
/obj/structure/ghost_role_spawner/syndicate/battlecruiser
- role_type = /datum/ghostrole/cybersun/ship
+ role_type = /datum/role/ghostrole/cybersun/ship
/obj/structure/ghost_role_spawner/syndicate/battlecruiser/assault
- role_type = /datum/ghostrole/cybersun/assault
+ role_type = /datum/role/ghostrole/cybersun/assault
/obj/structure/ghost_role_spawner/syndicate/battlecruiser/captain
- role_type = /datum/ghostrole/cybersun/captain
+ role_type = /datum/role/ghostrole/cybersun/captain
/datum/outfit/syndicate_empty
name = "Syndicate Operative Empty"
diff --git a/code/modules/ghostroles/roles/demonic_friend.dm b/code/modules/ghostroles/roles/demonic_friend.dm
index 3f96e2e86c8..b170806734e 100644
--- a/code/modules/ghostroles/roles/demonic_friend.dm
+++ b/code/modules/ghostroles/roles/demonic_friend.dm
@@ -1,10 +1,10 @@
-/datum/ghostrole/demonic_friend
+/datum/role/ghostrole/demonic_friend
name = "Demonic Friend"
desc = "You are someone's demonic friend from hell."
instantiator = /datum/ghostrole_instantiator/human/random/demonic_friend
assigned_role = "SuperFriend"
-/datum/ghostrole/demonic_friend/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/demonic_friend/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
if(params["spell"])
var/obj/effect/proc_holder/spell/targeted/summon_friend/S = spawnpoint?.params["spell"]
@@ -23,7 +23,7 @@
else
addtimer(CALLBACK(created, /mob/proc/dust), 15 SECONDS)
-/datum/ghostrole/demonic_friend/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/demonic_friend/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
if(params["owner"])
var/datum/mind/owner = spawnpoint?.params["owner"]
@@ -55,7 +55,7 @@
desc = "Oh boy! Oh boy! A friend!"
icon = 'icons/obj/cardboard_cutout.dmi'
icon_state = "cutout_basic"
- role_type = /datum/ghostrole/demonic_friend
+ role_type = /datum/role/ghostrole/demonic_friend
/datum/outfit/demonic_friend
name = "Demonic Friend"
@@ -65,4 +65,4 @@
back = /obj/item/storage/backpack
implants = list(/obj/item/implant/mindshield) //No revolutionaries, he's MY friend.
id = /obj/item/card/id
- access_clone = /datum/job/assistant
+ access_clone = /datum/role/job/assistant
diff --git a/code/modules/ghostroles/roles/fugitives.dm b/code/modules/ghostroles/roles/fugitives.dm
index dc2bdd33b08..208fba26ee0 100644
--- a/code/modules/ghostroles/roles/fugitives.dm
+++ b/code/modules/ghostroles/roles/fugitives.dm
@@ -1,9 +1,9 @@
-/datum/ghostrole/fugitive_hunter
+/datum/role/ghostrole/fugitive_hunter
name = "Fugitive Hunter"
desc = "Independent bounty hunters sent after fugitives"
instantiator = /datum/ghostrole_instantiator/human/random/fugitive_hunter
-/datum/ghostrole/fugitive_hunter/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/fugitive_hunter/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/datum/antagonist/fugitive_hunter/fughunter = new
fughunter.backstory = params["bcakstory"]
@@ -31,7 +31,7 @@
return ..()
/obj/structure/ghost_role_spawner/fugitive_hunter
- role_type = /datum/ghostrole/fugitive_hunter
+ role_type = /datum/role/ghostrole/fugitive_hunter
var/backstory
var/outfit
diff --git a/code/modules/ghostroles/roles/ghost_cafe.dm b/code/modules/ghostroles/roles/ghost_cafe.dm
index c44b2467007..44aa3229c80 100644
--- a/code/modules/ghostroles/roles/ghost_cafe.dm
+++ b/code/modules/ghostroles/roles/ghost_cafe.dm
@@ -1,5 +1,5 @@
-/datum/ghostrole/ghost_cafe
+/datum/role/ghostrole/ghost_cafe
name = "Ghost Cafe Visitor"
assigned_role = "Ghost Cafe Visitor"
desc = "Off-station area for ghosts to roleplay in."
@@ -11,7 +11,7 @@
name = "Ghost Cafe Sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- role_type = /datum/ghostrole/ghost_cafe
+ role_type = /datum/role/ghostrole/ghost_cafe
role_spawns = INFINITY
/datum/action/toggle_dead_chat_mob
@@ -97,7 +97,7 @@
H.remove_alt_appearance("ghost_cafe_disguise")
currently_disguised = FALSE
-/datum/ghostrole/ghost_cafe/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/ghost_cafe/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
to_chat(created,"Ghosting is free!")
diff --git a/code/modules/ghostroles/roles/golem.dm b/code/modules/ghostroles/roles/golem.dm
index 69d0b99648b..4a28ea35f28 100644
--- a/code/modules/ghostroles/roles/golem.dm
+++ b/code/modules/ghostroles/roles/golem.dm
@@ -1,7 +1,7 @@
-/datum/ghostrole/golem
+/datum/role/ghostrole/golem
instantiator = /datum/ghostrole_instantiator/human/random/species/golem
-/datum/ghostrole/golem/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/golem/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/mob/living/carbon/human/H = created
if(!istype(H))
@@ -11,18 +11,18 @@
return
to_chat(created, G.info_text)
-/datum/ghostrole/golem/free
+/datum/role/ghostrole/golem/free
name = "Free Golem"
desc = "You are a Free Golem. Your family worships The Liberator."
spawntext = "In his infinite and divine wisdom, he set your clan free to \
travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \
golems, so that no golem may ever be forced to serve again."
-/datum/ghostrole/golem/free/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/golem/free/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
to_chat(created, span_boldwarning("Build golem shells in the autolathe, and feed refined mineral sheets to the shells to bring them to life! You are generally a peaceful group unless provoked."))
-/datum/ghostrole/golem/servant
+/datum/role/ghostrole/golem/servant
name = "Servant Golem"
desc = "You are a golem."
spawntext = "You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools."
@@ -30,7 +30,7 @@
"servant" = TRUE
)
-/datum/ghostrole/golem/servant/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/golem/servant/PostInstantiate(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/datum/mind/creator_mind = params["creator"]
if(!creator_mind)
@@ -85,13 +85,13 @@
return ..(mapload, list(
"species" = species,
"creator" = creator && (istype(creator, /datum/mind)? creator : creator.mind)
- ), (has_owner && creator)? /datum/ghostrole/golem/servant : /datum/ghostrole/golem/free)
+ ), (has_owner && creator)? /datum/role/ghostrole/golem/servant : /datum/role/ghostrole/golem/free)
/obj/structure/ghost_role_spawner/golem/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(isgolem(user) && can_transfer)
// this is a bit special
// we want them to keep their mind, so....
- var/datum/ghostrole/G = get_ghostrole_datum(/datum/ghostrole/golem/free)
+ var/datum/role/ghostrole/G = get_ghostrole_datum(/datum/role/ghostrole/golem/free)
if(!G?.instantiator)
CRASH("Couldn't locate freegolem instantiator")
var/datum/ghostrole_instantiator/I = G.instantiator
diff --git a/code/modules/ghostroles/roles/hermit.dm b/code/modules/ghostroles/roles/hermit.dm
index 199b1267497..a39ca2d7f0a 100644
--- a/code/modules/ghostroles/roles/hermit.dm
+++ b/code/modules/ghostroles/roles/hermit.dm
@@ -1,11 +1,11 @@
-/datum/ghostrole/hermit
+/datum/role/ghostrole/hermit
name = "Space Hermit"
assigned_role = "Hermit"
desc = "A stranded cryo-occupant in deep space."
spawntext = "You've been late to awaken from your cryo slumber. Blasted machine, you set it to 10 days not 10 weeks! Where have the others gone while we were out? Did they manage to survive?"
instantiator = /datum/ghostrole_instantiator/human/random/hermit
-/datum/ghostrole/hermit/Instantiate(client/C, atom/loc, list/params)
+/datum/role/ghostrole/hermit/Instantiate(client/C, atom/loc, list/params)
var/rp = rand(1, 4)
switch(rp)
if(1)
@@ -18,7 +18,7 @@
params["fluff"] = "tourist"
return ..()
-/datum/ghostrole/hermit/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/hermit/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/flavour_text = "Each day you barely scrape by, and between the terrible conditions of your makeshift shelter, \
the hostile creatures, and the relentless yawn of the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
@@ -75,7 +75,7 @@
desc = "A humming sleeper with a silhouetted occupant inside. Its stasis function is broken and it's likely being used as a bed."
icon = 'icons/obj/spawners.dmi'
icon_state = "cryostasis_sleeper"
- role_type = /datum/ghostrole/hermit
+ role_type = /datum/role/ghostrole/hermit
qdel_on_deplete = TRUE
/obj/structure/ghost_role_spawner/hermit/Destroy()
diff --git a/code/modules/ghostroles/roles/hotel.dm b/code/modules/ghostroles/roles/hotel.dm
index 15b8a4afd75..5a86f4ead6b 100644
--- a/code/modules/ghostroles/roles/hotel.dm
+++ b/code/modules/ghostroles/roles/hotel.dm
@@ -1,11 +1,11 @@
-/datum/ghostrole/space_hotel
+/datum/role/ghostrole/space_hotel
instantiator = /datum/ghostrole_instantiator/human/random/space_hotel
name = "Space Hotel Staff"
desc = "You are a staff member of a top-of-the-line space hotel! Cater to guests and make sure the manager doesn't fire you."
automatic_objective = "You are a staff member of a top-of-the-line space hotel! Cater to guests and make sure the manager doesn't fire you."
assigned_role = "Hotel Staff"
-/datum/ghostrole/space_hotel/security
+/datum/role/ghostrole/space_hotel/security
instantiator = /datum/ghostrole_instantiator/human/random/space_hotel/security
name = "Space Hotel Security"
desc = "You have been assigned to this hotel to protect the interests of the company while keeping the peace between \
@@ -25,7 +25,7 @@
/obj/structure/ghost_role_spawner/space_hotel //not free antag u little shits
name = "staff sleeper"
desc = "A sleeper designed for long-term stasis between guest visits."
- role_type = /datum/ghostrole/space_hotel
+ role_type = /datum/role/ghostrole/space_hotel
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper_s"
@@ -35,7 +35,7 @@
/obj/structure/ghost_role_spawner/space_hotel/security
name = "hotel security sleeper"
- role_type = /datum/ghostrole/space_hotel/security
+ role_type = /datum/role/ghostrole/space_hotel/security
/datum/outfit/hotelstaff
name = "Hotel Staff"
diff --git a/code/modules/ghostroles/roles/lifebringer.dm b/code/modules/ghostroles/roles/lifebringer.dm
index a38a5fa6309..869e273bd33 100644
--- a/code/modules/ghostroles/roles/lifebringer.dm
+++ b/code/modules/ghostroles/roles/lifebringer.dm
@@ -1,4 +1,4 @@
-/datum/ghostrole/seed_vault
+/datum/role/ghostrole/seed_vault
name = "Lifebringer"
desc = "You are a sentient ecosystem, an example of the mastery over life that your creators possessed."
spawntext = "Your masters, benevolent as they were, created uncounted seed vaults and spread them across \
@@ -31,7 +31,7 @@
desc = "An ancient machine that seems to be used for storing plant matter. The glass is obstructed by a mat of vines."
icon = 'icons/obj/lavaland/spawners.dmi'
icon_state = "terrarium"
- role_type = /datum/ghostrole/seed_vault
+ role_type = /datum/role/ghostrole/seed_vault
/obj/structure/ghost_role_spawner/seed_vault/Destroy()
new/obj/structure/fluff/empty_terrarium(get_turf(src))
diff --git a/code/modules/ghostroles/roles/oldresearch.dm b/code/modules/ghostroles/roles/oldresearch.dm
index 72b924b5fc7..901b6740fd6 100644
--- a/code/modules/ghostroles/roles/oldresearch.dm
+++ b/code/modules/ghostroles/roles/oldresearch.dm
@@ -1,4 +1,4 @@
-/datum/ghostrole/old_research
+/datum/role/ghostrole/old_research
name = "Oldstation Crew"
allow_pick_spawner = TRUE
desc = "You were a Nanotrasen employee from an era past, stationed upon a state of the art research station. \
@@ -21,7 +21,7 @@
return /datum/outfit/old_research/scientist
/obj/structure/ghost_role_spawner/old_research
- role_type = /datum/ghostrole/old_research
+ role_type = /datum/role/ghostrole/old_research
name = "old cryogenics pod"
desc = "A humming cryo pod. You can barely recognise a security uniform underneath the built up ice. The machine is attempting to wake up its occupant."
icon = 'icons/obj/machines/sleeper.dmi'
diff --git a/code/modules/ghostroles/roles/pirate.dm b/code/modules/ghostroles/roles/pirate.dm
index 1ce7290a865..fb42bdddcac 100644
--- a/code/modules/ghostroles/roles/pirate.dm
+++ b/code/modules/ghostroles/roles/pirate.dm
@@ -1,4 +1,4 @@
-/datum/ghostrole/pirate
+/datum/role/ghostrole/pirate
name = "Pirate"
assigned_role = "Pirate"
desc = "You are a pirate! A legendary, if oft maligned, profession."
@@ -6,7 +6,7 @@
important_info = "You are a member of a pirate crew. You pillage, kidnap, and steal for profit and pleasure. Although you recently moved into this system, it is owned by NanoTrasen. A Corporate presence provides plenty of opportunities for plunder, but beware! Certain areas are considered off limits, even to pirates. Only a fool would anger Nebula Gas by raiding their station, although NebGas vessels in transit are fair game. Attempting to visit NanoTrasen's primary facility is equally dangerous and ill-advised. Focusing on isolated vessels in flight or expeditions on planets may be the most reliable way to score precious booty. Proteans and Xenochimerae are currently excluded from being Pirates, if you own either Whitelist."
instantiator = /datum/ghostrole_instantiator/human/random/species/pirate
-/datum/ghostrole/pirate/Instantiate(client/C, atom/loc, list/params)
+/datum/role/ghostrole/pirate/Instantiate(client/C, atom/loc, list/params)
var/rp = rand(1, 3)
switch(rp)
if(1)
@@ -17,7 +17,7 @@
params["fluff"] = "professional"
return ..()
-/datum/ghostrole/pirate/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/pirate/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/flavour_text = "The sound of something dripping on the top of your bunk unit wakes you up. Spears of light shine in through old \
bullet holes. The unit's door slides back with the push of a button, letting stale recycled air rush out. A yellowed poster on the wall \
@@ -67,7 +67,7 @@
icon_state = "piratebunk"
anchored = TRUE
density = TRUE
- role_type = /datum/ghostrole/pirate
+ role_type = /datum/role/ghostrole/pirate
role_spawns = 1
//This is from the original untranslated DM. It still isn't translated, but this is neat and maybe we should use it sometime? It seems worth retaining for now.
@@ -77,7 +77,7 @@
desc = "A cryo sleeper smelling faintly of rum. The sleeper looks unstable. Perhaps the pirate within can be killed with the right tools..."
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- role_type = /datum/ghostrole/pirate
+ role_type = /datum/role/ghostrole/pirate
role_params = list(
"rank" = "Mate"
)
diff --git a/code/modules/ghostroles/roles/prisoner.dm b/code/modules/ghostroles/roles/prisoner.dm
index 1909d71cbbf..9cfcca92e1c 100644
--- a/code/modules/ghostroles/roles/prisoner.dm
+++ b/code/modules/ghostroles/roles/prisoner.dm
@@ -1,10 +1,10 @@
-/datum/ghostrole/lavaland_prisoner
+/datum/role/ghostrole/lavaland_prisoner
name = "Lavaland Prisoner"
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."
instantiator = /datum/ghostrole_instantiator/human/random/lavaland_prisoner
assigned_role = "Escaped Prisoner"
-/datum/ghostrole/lavaland_prisoner/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint)
+/datum/role/ghostrole/lavaland_prisoner/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint)
. = ..()
var/list/crimes = list("murder", "larceny", "embezzlement", "unionization", "dereliction of duty", "kidnapping", "gross incompetence", "grand theft", "collaboration with the Syndicate", \
"worship of a forbidden deity", "interspecies relations", "mutiny")
@@ -25,7 +25,7 @@
desc = "A sleeper designed to put its occupant into a deep coma, unbreakable until the sleeper turns off. This one's glass is cracked and you can see a pale, sleeping face staring out."
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper_s"
- role_type = /datum/ghostrole/lavaland_prisoner
+ role_type = /datum/role/ghostrole/lavaland_prisoner
/datum/outfit/lavalandprisoner
name = "Lavaland Prisoner"
diff --git a/code/modules/ghostroles/roles/timeless_prison.dm b/code/modules/ghostroles/roles/timeless_prison.dm
index 4a89b733ee1..4c2e1fde0ad 100644
--- a/code/modules/ghostroles/roles/timeless_prison.dm
+++ b/code/modules/ghostroles/roles/timeless_prison.dm
@@ -1,11 +1,11 @@
-/datum/ghostrole/timeless_prison
+/datum/role/ghostrole/timeless_prison
name = "Timeless Prisoner"
desc = "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."
instantiator = /datum/ghostrole_instantiator/human/random/species/shadow
assigned_role = "Exile"
-/datum/ghostrole/timeless_prison/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
+/datum/role/ghostrole/timeless_prison/Greet(mob/created, datum/component/ghostrole_spawnpoint/spawnpoint, list/params)
. = ..()
var/wish = rand(1,4)
switch(wish)
@@ -33,7 +33,7 @@
desc = "Although this stasis pod looks medicinal, it seems as though it's meant to preserve something for a very long time."
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- role_type = /datum/ghostrole/timeless_prison
+ role_type = /datum/role/ghostrole/timeless_prison
/obj/structure/ghost_role_spawner/exile/Destroy()
new/obj/structure/fluff/empty_sleeper(get_turf(src))
diff --git a/code/modules/ghostroles/roles/vet.dm b/code/modules/ghostroles/roles/vet.dm
index 86fcf6b6417..068e2ed2d3c 100644
--- a/code/modules/ghostroles/roles/vet.dm
+++ b/code/modules/ghostroles/roles/vet.dm
@@ -1,4 +1,4 @@
-/datum/ghostrole/lavaland_vet
+/datum/role/ghostrole/lavaland_vet
name = "Lavaland Vet"
desc = "You are a animal doctor who just woke up in lavaland"
assigned_role = "Translocated Vet"
@@ -10,7 +10,7 @@
/obj/structure/ghost_role_spawner/lavaland_vet
name = "broken rejuvenation pod"
desc = "A small sleeper typically used to instantly restore minor wounds. This one seems broken, and its occupant is comatose."
- role_type = /datum/ghostrole/lavaland_vet
+ role_type = /datum/role/ghostrole/lavaland_vet
/obj/structure/ghost_role_spawner/lavaland_vet/Destroy()
var/obj/structure/fluff/empty_sleeper/S = new(drop_location())
diff --git a/code/modules/ghostroles/spawner.dm b/code/modules/ghostroles/spawner.dm
index 7a2f538faac..59322690e6b 100644
--- a/code/modules/ghostroles/spawner.dm
+++ b/code/modules/ghostroles/spawner.dm
@@ -40,7 +40,7 @@
else
AddComponent(/datum/component/ghostrole_spawnpoint, role_type, role_spawns, role_params, /obj/structure/ghost_role_spawner/proc/on_spawn, null, special_spawntext)
-/obj/structure/ghost_role_spawner/proc/on_spawn(mob/created, datum/ghostrole/role, list/params, datum/component/ghostrole_spawnpoint/spawnpoint)
+/obj/structure/ghost_role_spawner/proc/on_spawn(mob/created, datum/role/ghostrole/role, list/params, datum/component/ghostrole_spawnpoint/spawnpoint)
if(qdel_on_deplete && !spawnpoint.SpawnsLeft())
qdel(src)
@@ -66,7 +66,7 @@
return ..()
/obj/structure/ghost_role_spawner/custom/proc/GenerateRole(id = role_type, mob_path = src.mob_path)
- var/datum/ghostrole/G = get_ghostrole_datum(id)
+ var/datum/role/ghostrole/G = get_ghostrole_datum(id)
if(G)
return
G = new(id)
diff --git a/code/modules/ghostroles/spawnpoint.dm b/code/modules/ghostroles/spawnpoint.dm
index 7c91eec5345..7792d5727b5 100644
--- a/code/modules/ghostroles/spawnpoint.dm
+++ b/code/modules/ghostroles/spawnpoint.dm
@@ -30,7 +30,7 @@ GLOBAL_LIST_EMPTY(ghostrole_spawnpoints)
src.spawntext = spawntext
src.proc_to_call_or_callback = proc_to_call_or_callback
if(notify_ghosts)
- var/datum/ghostrole/role = get_ghostrole_datum(role_type)
+ var/datum/role/ghostrole/role = get_ghostrole_datum(role_type)
if(!role)
return
notify_ghosts("Ghostrole spawner created: [role.name] - [parent] - [get_area(parent)]", source = parent, ignore_mapload = TRUE, flashwindow = FALSE)
@@ -72,7 +72,7 @@ GLOBAL_LIST_EMPTY(ghostrole_spawnpoints)
/datum/component/ghostrole_spawnpoint/proc/SpawnsLeft(client/C)
return max(0, max_spawns - spawns)
-/datum/component/ghostrole_spawnpoint/proc/OnSpawn(mob/created, datum/ghostrole/role)
+/datum/component/ghostrole_spawnpoint/proc/OnSpawn(mob/created, datum/role/ghostrole/role)
if(istype(proc_to_call_or_callback))
proc_to_call_or_callback.Invoke(created, role, params, src)
spawns++
@@ -89,13 +89,13 @@ GLOBAL_LIST_EMPTY(ghostrole_spawnpoints)
/datum/component/ghostrole_spawnpoint/proc/Examine(datum/source, list/examine_list)
if(isobserver(source))
- var/datum/ghostrole/role = get_ghostrole_datum(role_type)
+ var/datum/role/ghostrole/role = get_ghostrole_datum(role_type)
if(!role)
return
examine_list += "Click> this ghostrole spawner to become a [role.name]!"
/datum/component/ghostrole_spawnpoint/proc/GhostInteract(datum/source, mob/user)
- var/datum/ghostrole/role = get_ghostrole_datum(role_type)
+ var/datum/role/ghostrole/role = get_ghostrole_datum(role_type)
if(!role)
to_chat(user, SPAN_DANGER("No ghostrole datum found: [role_type]. Contact a coder!"))
if(!(datum_flags & DF_VAR_EDITED))
diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm
index 2e9e9f8f739..5258ffa3a9e 100644
--- a/code/modules/integrated_electronics/passive/power.dm
+++ b/code/modules/integrated_electronics/passive/power.dm
@@ -143,7 +143,7 @@
var/list/data = reagents.get_data(I)
if(data && istype(data["donor"], /mob/living/carbon/human))
var/mob/living/carbon/human/H = data["donor"]
- if(H.mind && H.mind.key)
+ if(H.mind && H.mind.ckey)
power *= 10
if(reagents.remove_reagent(I, 1))
assembly.give_power(fuel[I])
diff --git a/code/modules/jobs/_alt_title.dm b/code/modules/jobs/_alt_title.dm
deleted file mode 100644
index acf676a2a90..00000000000
--- a/code/modules/jobs/_alt_title.dm
+++ /dev/null
@@ -1,8 +0,0 @@
-/////////////////////////////////////////
-// Alt Title Code
-/////////////////////////////////////////
-
-/datum/alt_title
- var/title = "GENERIC ALT TITLE" // What the Alt-Title is called
- var/title_blurb = null // What's amended to the job description for this Job title. If nothing's added, leave null.
- var/title_outfit = null // The outfit used by the alt-title. If it's the same as the base job, leave this null.
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index 9873b21f113..7fd37b6268f 100644
--- a/code/modules/jobs/access.dm
+++ b/code/modules/jobs/access.dm
@@ -192,9 +192,9 @@
/proc/get_all_jobs()
var/list/all_jobs = list()
- var/list/all_datums = typesof(/datum/job)
+ var/list/all_datums = typesof(/datum/role/job)
all_datums -= exclude_jobs
- var/datum/job/jobdatum
+ var/datum/role/job/jobdatum
for(var/jobtype in all_datums)
jobdatum = new jobtype
all_jobs.Add(jobdatum.title)
diff --git a/code/modules/jobs/alt_title.dm b/code/modules/jobs/alt_title.dm
new file mode 100644
index 00000000000..c72f61ec909
--- /dev/null
+++ b/code/modules/jobs/alt_title.dm
@@ -0,0 +1,24 @@
+/////////////////////////////////////////
+// Alt Title Code
+/////////////////////////////////////////
+
+/datum/prototype/alt_title
+ abstract_type = /datum/prototype/alt_title
+ namespace = "role_title"
+ anonymous = TRUE
+
+ var/title = "GENERIC ALT TITLE" // What the Alt-Title is called
+ var/title_blurb = null // What's amended to the job description for this Job title. If nothing's added, leave null.
+ var/title_outfit = null // The outfit used by the alt-title. If it's the same as the base job, leave this null.
+
+ /// restricted: require these hardcoded lore datums to be associated with the characters by typepath or id.
+ /// null for anything/anyone
+ var/list/background_restricted
+
+/datum/prototype/alt_title/New()
+ for(var/i in 1 to length(background_restricted))
+ var/thing = background_restricted[i]
+ if(ispath(thing))
+ var/datum/lore/character_background/bg = thing
+ background_restricted[i] = initial(bg.id)
+ return ..()
diff --git a/code/modules/jobs/job.dm b/code/modules/jobs/job.dm
index 88b57c04c06..e7616da4fc4 100644
--- a/code/modules/jobs/job.dm
+++ b/code/modules/jobs/job.dm
@@ -1,6 +1,6 @@
-/datum/job
+/datum/role/job
/// Abstract type.
- abstract_type = /datum/job
+ abstract_type = /datum/role/job
//? Intrinsics
/// ID of the job, used for save/load
@@ -18,8 +18,8 @@
/// starting money multiplier
var/economy_payscale = ECONOMY_PAYSCALE_JOB_DEFAULT
+ //? unsorted
// Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access
-
/// Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population).
var/list/minimal_access = list()
/// Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!).
@@ -41,6 +41,9 @@
var/selection_color = COLOR_WHITE
/// List of alternate titles; There is no need for an alt-title datum for the base job title.
var/list/alt_titles = null
+ // todo: optimize this, it's very non-performant.
+ /// Strict title mode: If an alt title is available for a specific background someone has, only that and other alt titles with that background can be chosen.
+ var/strict_titles = FALSE
/// 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 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.)
@@ -83,16 +86,18 @@
// Disallow joining as this job midround from off-duty position via going on-duty
var/disallow_jobhop = FALSE
-/datum/job/New()
+/datum/role/job/New()
. = ..()
GLOB.department_accounts = GLOB.department_accounts || departments_managed
+//? Availability
+
/**
* checks slots remaining
*
* @return 0 to number of slots remaining
*/
-/datum/job/proc/slots_remaining(latejoin)
+/datum/role/job/proc/slots_remaining(latejoin)
if(!latejoin)
if(spawn_positions == -1)
return INFINITY
@@ -110,7 +115,7 @@
*
* todo: check ckey proc too?
*/
-/datum/job/proc/check_client_availability(client/C, check_char, latejoin)
+/datum/role/job/proc/check_client_availability(client/C, check_char, latejoin)
. = NONE
if(whitelist_only && !config.check_job_whitelist(ckey(title), C.ckey))
. |= ROLE_UNAVAILABLE_WHITELIST
@@ -143,7 +148,7 @@
*
* todo: check ckey proc too?
*/
-/datum/job/proc/check_client_availability_one(client/C, check_char, latejoin)
+/datum/role/job/proc/check_client_availability_one(client/C, check_char, latejoin)
. = NONE
if(whitelist_only && !config.check_job_whitelist(ckey(title), C.ckey))
return ROLE_UNAVAILABLE_WHITELIST
@@ -166,10 +171,30 @@
// todo: JEXP/ROLE-EXP hours system
+// todo: this entire system is hellish and needs redone i hate preferences code
+/**
+ * checks if we're available for a given *mob*, but short circuits with the most common
+ * checks first.
+ *
+ * this is used for stuff like jobswitch code
+ */
+/datum/role/job/proc/check_mob_availability_one(mob/M)
+ . = NONE
+ if(whitelist_only) // don't even bother checking mind
+ return ROLE_UNAVAILABLE_WHITELIST
+ else if(!slots_remaining(TRUE))
+ return ROLE_UNAVAILABLE_SLOTS_FULL
+ else if(jobban_isbanned(M, title))
+ return ROLE_UNAVAILABLE_BANNED
+ if(M.mind)
+ var/datum/lore/character_background/faction/fact = M.mind.original_background_faction()
+ if(fact && !fact.check_job_id(id))
+ return ROLE_UNAVAILABLE_CHAR_FACTION
+ // todo: species check
/**
* get an user-friendly reason of why they can't spawn as us
*/
-/datum/job/proc/get_availability_reason(client/C, reason)
+/datum/role/job/proc/get_availability_reason(client/C, reason)
if(reason & ROLE_UNAVAILABLE_BANNED)
return "BANNED"
if(reason & ROLE_UNAVAILABLE_SLOTS_FULL)
@@ -193,7 +218,7 @@
/**
* get a short abbreviation for why they can't spawn as us; used for preferences
*/
-/datum/job/proc/get_availability_error(client/C, reason)
+/datum/role/job/proc/get_availability_error(client/C, reason)
if(reason & ROLE_UNAVAILABLE_BANNED)
return "BANNED"
if(reason & ROLE_UNAVAILABLE_SLOTS_FULL)
@@ -205,7 +230,7 @@
if(reason & ROLE_UNAVAILABLE_WHITELIST)
return "WHITELISTED"
if(reason & ROLE_UNAVAILABLE_CONNECT_TIME)
- return "IN [available_in_days(C)] DAYS"
+ return C? "IN [available_in_days(C)] DAYS" : "MIN ACCOUNT AGE"
if(reason & ROLE_UNAVAILABLE_CHAR_AGE)
return "MIN AGE: [minimum_character_age]"
if(reason & ROLE_UNAVAILABLE_CHAR_FACTION)
@@ -214,16 +239,90 @@
return "SPECIES"
return "UNKNOWN (BUG)"
-/datum/job/proc/equip(var/mob/living/carbon/human/H, var/alt_title)
+//? Alt Titles
+
+/**
+ * get available alt title names for a given set of character backgrounds
+ */
+/datum/role/job/proc/alt_title_query(list/datum/lore/character_background/backgrounds = list())
+ RETURN_TYPE(/list)
+ var/list/transformed = list()
+ for(var/datum/lore/character_background/bg as anything in backgrounds)
+ transformed += bg.id
+ if(strict_titles)
+ var/list/normal = list(title)
+ var/list/restricted = list()
+ for(var/title in alt_titles)
+ var/datum/prototype/alt_title/alt_datum = SSrepository.fetch(alt_titles[title])
+ if(isnull(alt_datum))
+ continue
+ if(isnull(alt_datum.background_restricted))
+ normal |= alt_datum.title
+ continue
+ if(!length(alt_datum.background_restricted & transformed))
+ normal -= alt_datum.title
+ // allow us to forcefully register the "main" title under alt title system
+ continue
+ restricted |= alt_datum.title
+ . = length(restricted)? restricted : normal
+ else
+ var/list/found = list(title)
+ for(var/title in alt_titles)
+ var/datum/prototype/alt_title/alt_datum = SSrepository.fetch(alt_titles[title])
+ if(isnull(alt_datum))
+ continue
+ if(isnull(alt_datum.background_restricted))
+ found |= alt_datum.title
+ continue
+ if(!length(alt_datum.background_restricted & transformed))
+ found -= alt_datum.title
+ // allow us to forcefully register the "main" title under alt title system
+ continue
+ found |= alt_datum.title
+ . = found
+ return length(.)? . : list(title)
+
+/**
+ * chcek if an alt title is available for a given set of backgrounds
+ */
+/datum/role/job/proc/alt_title_check(alt_title, list/datum/lore/character_background/backgrounds = list())
+ var/list/transformed = list()
+ for(var/datum/lore/character_background/bg as anything in backgrounds)
+ transformed += bg.id
+ if(strict_titles)
+ var/found = FALSE
+ for(var/other_title in alt_titles)
+ var/datum/prototype/alt_title/alt_datum = SSrepository.fetch(alt_titles[other_title])
+ if(length(alt_datum.background_restricted & transformed))
+ found = TRUE
+ break
+ var/datum/prototype/alt_title/alt_datum = SSrepository.fetch(alt_titles?[alt_title])
+ if(isnull(alt_datum))
+ if(alt_title == title)
+ return !found
+ else
+ return FALSE
+ return length(alt_datum.background_restricted & transformed) || (!found && !alt_datum.background_restricted)
+ else
+ if(alt_title == title)
+ return TRUE
+ var/datum/prototype/alt_title/alt_datum = SSrepository.fetch(alt_titles?[alt_title])
+ if(isnull(alt_datum))
+ return FALSE
+ return isnull(alt_datum.background_restricted) || length(transformed & alt_datum.background_restricted)
+
+//? Unsorted
+
+/datum/role/job/proc/equip(var/mob/living/carbon/human/H, var/alt_title)
var/datum/outfit/outfit = get_outfit(H, alt_title)
if(!outfit)
return FALSE
. = outfit.equip(H, title, alt_title)
return 1
-/datum/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title)
+/datum/role/job/proc/get_outfit(var/mob/living/carbon/human/H, var/alt_title)
if(alt_title && alt_titles)
- var/datum/alt_title/A = alt_titles[alt_title]
+ var/datum/prototype/alt_title/A = alt_titles[alt_title]
if(A && initial(A.title_outfit))
. = initial(A.title_outfit)
. = . || outfit_type
@@ -232,11 +331,11 @@
// TODO: job refactor
-/datum/job/proc/get_economic_payscale()
+/datum/role/job/proc/get_economic_payscale()
var/datum/department/D = SSjob.get_primary_department_of_job(src)
return economy_payscale * (istype(D)? D.economy_payscale : 1)
-/datum/job/proc/setup_account(var/mob/living/carbon/human/H)
+/datum/role/job/proc/setup_account(var/mob/living/carbon/human/H)
if(!account_allowed || (H.mind && H.mind.initial_account))
return
@@ -260,61 +359,61 @@
to_chat(H, "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]")
// Overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/qdel()
-/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title)
+/datum/role/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title)
var/datum/outfit/outfit = get_outfit(H, alt_title)
if(!outfit)
return FALSE
. = outfit.equip_base(H, title, alt_title)
-/datum/job/proc/get_access()
+/datum/role/job/proc/get_access()
if(!config || config_legacy.jobs_have_minimal_access)
return src.minimal_access.Copy()
else
return src.access.Copy()
// If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
-/datum/job/proc/player_old_enough(client/C)
+/datum/role/job/proc/player_old_enough(client/C)
return (available_in_days(C) == 0) // Available in 0 days = available right now = player is old enough to play.
-/datum/job/proc/available_in_days(client/C)
+/datum/role/job/proc/available_in_days(client/C)
if(C.has_jexp_bypass())
return 0
if(C && config_legacy.use_age_restriction_for_jobs && isnum(C.player_age) && isnum(minimal_player_age))
return max(0, minimal_player_age - C.player_age)
return 0
-/datum/job/proc/apply_fingerprints(var/mob/living/carbon/human/target)
+/datum/role/job/proc/apply_fingerprints(var/mob/living/carbon/human/target)
if(!istype(target))
return 0
for(var/obj/item/item in target.contents)
apply_fingerprints_to_item(target, item)
return 1
-/datum/job/proc/apply_fingerprints_to_item(var/mob/living/carbon/human/holder, var/obj/item/item)
+/datum/role/job/proc/apply_fingerprints_to_item(var/mob/living/carbon/human/holder, var/obj/item/item)
item.add_fingerprint(holder,1)
if(item.contents.len)
for(var/obj/item/sub_item in item.contents)
apply_fingerprints_to_item(holder, sub_item)
-/datum/job/proc/is_position_available()
+/datum/role/job/proc/is_position_available()
return (current_positions < total_positions) || (total_positions == -1)
-/datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title)
+/datum/role/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title)
return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title)
-/datum/job/proc/get_description_blurb(var/alt_title)
+/datum/role/job/proc/get_description_blurb(var/alt_title)
var/list/message = list()
message |= desc
if(alt_title && alt_titles)
var/typepath = alt_titles[alt_title]
if(typepath)
- var/datum/alt_title/A = new typepath()
+ var/datum/prototype/alt_title/A = new typepath()
if(A.title_blurb)
message |= A.title_blurb
return message
-/datum/job/proc/get_job_icon()
+/datum/role/job/proc/get_job_icon()
if(!SSjob.job_icons[title])
var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#job_icon")
dress_mannequin(mannequin)
@@ -327,17 +426,17 @@
return SSjob.job_icons[title]
-/datum/job/proc/dress_mannequin(mob/living/carbon/human/dummy/mannequin/mannequin)
+/datum/role/job/proc/dress_mannequin(mob/living/carbon/human/dummy/mannequin/mannequin)
mannequin.delete_inventory(TRUE)
equip_preview(mannequin)
if(mannequin.back)
qdel(mannequin.back)
/// Check client-specific availability rules.
-/datum/job/proc/player_has_enough_pto(client/C)
+/datum/role/job/proc/player_has_enough_pto(client/C)
return timeoff_factor >= 0 || (C && LAZYACCESS(C.department_hours, pto_type) > 0)
-/datum/job/proc/equip_backpack(mob/living/carbon/human/H)
+/datum/role/job/proc/equip_backpack(mob/living/carbon/human/H)
switch(H.backbag)
if(2)
H.equip_to_slot_or_del(new /obj/item/storage/backpack(H), SLOT_ID_BACK)
diff --git a/code/modules/jobs/job_types/station.dm b/code/modules/jobs/job_types/station.dm
index 16261213156..987b9ce6922 100644
--- a/code/modules/jobs/job_types/station.dm
+++ b/code/modules/jobs/job_types/station.dm
@@ -1,3 +1,3 @@
-/datum/job/station
+/datum/role/job/station
faction = JOB_FACTION_STATION
- abstract_type = /datum/job/station
+ abstract_type = /datum/role/job/station
diff --git a/code/modules/jobs/job_types/station/admin/centcom_officer.dm b/code/modules/jobs/job_types/station/admin/centcom_officer.dm
index ac58f180c2b..b545b362293 100644
--- a/code/modules/jobs/job_types/station/admin/centcom_officer.dm
+++ b/code/modules/jobs/job_types/station/admin/centcom_officer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/centcom_officer //For Business
+/datum/role/job/station/centcom_officer //For Business
id = JOB_ID_CENTCOM_OFFICER
title = "CentCom Officer"
economy_payscale = ECONOMY_PAYSCALE_JOB_ADMIN
@@ -22,7 +22,7 @@
pto_type = PTO_CIVILIAN
-/datum/job/station/centcom_officer/get_access()
+/datum/role/job/station/centcom_officer/get_access()
return get_all_accesses().Copy()
/datum/outfit/job/station/centcom_officer
diff --git a/code/modules/jobs/job_types/station/admin/emergency_responder.dm b/code/modules/jobs/job_types/station/admin/emergency_responder.dm
index b1a712cbedb..655fc715d65 100644
--- a/code/modules/jobs/job_types/station/admin/emergency_responder.dm
+++ b/code/modules/jobs/job_types/station/admin/emergency_responder.dm
@@ -1,4 +1,4 @@
-/datum/job/station/emergency_responder //For staff managing/leading ERTs
+/datum/role/job/station/emergency_responder //For staff managing/leading ERTs
id = JOB_ID_EMERGENCY_RESPONDER
title = "Emergency Responder"
economy_payscale = ECONOMY_PAYSCALE_JOB_ADMIN
@@ -21,7 +21,7 @@
pto_type = PTO_CIVILIAN
-/datum/job/station/emergency_responder/get_access()
+/datum/role/job/station/emergency_responder/get_access()
return get_all_accesses().Copy()
/datum/outfit/job/station/emergency_responder
diff --git a/code/modules/jobs/job_types/station/civillian/assistant.dm b/code/modules/jobs/job_types/station/civillian/assistant.dm
index a51d3b62d09..e324735904c 100644
--- a/code/modules/jobs/job_types/station/civillian/assistant.dm
+++ b/code/modules/jobs/job_types/station/civillian/assistant.dm
@@ -1,4 +1,4 @@
-/datum/job/station/assistant
+/datum/role/job/station/assistant
id = JOB_ID_ASSISTANT
title = USELESS_JOB
flag = ASSISTANT
@@ -9,39 +9,39 @@
spawn_positions = -1
supervisors = "nobody! You don't work here"
selection_color = "#515151"
- access = list() //See /datum/job/station/assistant/get_access()
- minimal_access = list() //See /datum/job/station/assistant/get_access()
+ access = list() //See /datum/role/job/station/assistant/get_access()
+ minimal_access = list() //See /datum/role/job/station/assistant/get_access()
timeoff_factor = 0
outfit_type = /datum/outfit/job/station/assistant
alt_titles = list(
- "Visitor" = /datum/alt_title/visitor,
- "Server" = /datum/alt_title/server,
- "Morale Officer" = /datum/alt_title/morale_officer,
- "Assistant" = /datum/alt_title/assistant
+ "Visitor" = /datum/prototype/alt_title/visitor,
+ "Server" = /datum/prototype/alt_title/server,
+ "Morale Officer" = /datum/prototype/alt_title/morale_officer,
+ "Assistant" = /datum/prototype/alt_title/assistant
)
-/datum/job/station/assistant/get_access()
+/datum/role/job/station/assistant/get_access()
if(config_legacy.assistant_maint)
return list(access_maint_tunnels)
else
return list()
-/datum/job/station/assistant/get_access()
+/datum/role/job/station/assistant/get_access()
return list()
-/datum/alt_title/visitor
+/datum/prototype/alt_title/visitor
title = "Visitor"
title_outfit = /datum/outfit/job/station/assistant/visitor
-/datum/alt_title/server
+/datum/prototype/alt_title/server
title = "Server"
title_outfit = /datum/outfit/job/station/assistant/server
-/datum/alt_title/morale_officer
+/datum/prototype/alt_title/morale_officer
title = "Morale Officer"
-/datum/alt_title/assistant
+/datum/prototype/alt_title/assistant
title = "Assistant"
title_outfit = /datum/outfit/job/station/assistant
diff --git a/code/modules/jobs/job_types/station/civillian/chaplain.dm b/code/modules/jobs/job_types/station/civillian/chaplain.dm
index e4efd1743f7..fab61cd2405 100644
--- a/code/modules/jobs/job_types/station/civillian/chaplain.dm
+++ b/code/modules/jobs/job_types/station/civillian/chaplain.dm
@@ -1,4 +1,4 @@
-/datum/job/station/chaplain
+/datum/role/job/station/chaplain
id = JOB_ID_CHAPLAIN
title = "Chaplain"
flag = CHAPLAIN
@@ -15,23 +15,23 @@
outfit_type = /datum/outfit/job/station/chaplain
desc = "The Chaplain ministers to the spiritual needs of the crew."
alt_titles = list(
- "Counselor" = /datum/alt_title/counselor,
- "Religious Affairs Advisor" = /datum/alt_title/chaplain/advisor
+ "Counselor" = /datum/prototype/alt_title/counselor,
+ "Religious Affairs Advisor" = /datum/prototype/alt_title/chaplain/advisor
)
// Chaplain Alt Titles
-/datum/alt_title/counselor
+/datum/prototype/alt_title/counselor
title = "Counselor"
title_blurb = "The Counselor attends to the emotional needs of the crew, without a specific medicinal or spiritual focus."
-/datum/alt_title/chaplain/advisor
+/datum/prototype/alt_title/chaplain/advisor
title = "Religious Affairs Advisor"
-/datum/job/station/chaplain/equip(mob/living/carbon/human/H, src)
+/datum/role/job/station/chaplain/equip(mob/living/carbon/human/H, src)
. = ..()
if(H.mind)
H.mind.isholy = TRUE
-/datum/job/station/chaplain/equip(var/mob/living/carbon/human/H, var/alt_title, var/ask_questions = TRUE)
+/datum/role/job/station/chaplain/equip(var/mob/living/carbon/human/H, var/alt_title, var/ask_questions = TRUE)
. = ..()
if(!.)
return
diff --git a/code/modules/jobs/job_types/station/civillian/clown.dm b/code/modules/jobs/job_types/station/civillian/clown.dm
index 5c493471aba..a6c552478e0 100644
--- a/code/modules/jobs/job_types/station/civillian/clown.dm
+++ b/code/modules/jobs/job_types/station/civillian/clown.dm
@@ -1,4 +1,4 @@
-/datum/job/station/clown
+/datum/role/job/station/clown
id = JOB_ID_CLOWN
title = "Clown"
flag = CLOWN
@@ -14,15 +14,15 @@
whitelist_only = 1
outfit_type = /datum/outfit/job/station/clown
pto_type = PTO_CIVILIAN
- alt_titles = list("Jester" = /datum/alt_title/clown/jester, "Fool" = /datum/alt_title/clown/fool)
+ alt_titles = list("Jester" = /datum/prototype/alt_title/clown/jester, "Fool" = /datum/prototype/alt_title/clown/fool)
-/datum/alt_title/clown/jester
+/datum/prototype/alt_title/clown/jester
title = "Jester"
-/datum/alt_title/clown/fool
+/datum/prototype/alt_title/clown/fool
title = "Fool"
-/datum/job/station/clown/get_access()
+/datum/role/job/station/clown/get_access()
if(config_legacy.assistant_maint)
return list(access_maint_tunnels, access_entertainment, access_clown, access_tomfoolery)
else
diff --git a/code/modules/jobs/job_types/station/civillian/entertainer.dm b/code/modules/jobs/job_types/station/civillian/entertainer.dm
index 0ac739ec305..d8a4bb27187 100644
--- a/code/modules/jobs/job_types/station/civillian/entertainer.dm
+++ b/code/modules/jobs/job_types/station/civillian/entertainer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/entertainer
+/datum/role/job/station/entertainer
id = JOB_ID_ENTERTAINER
title = "Entertainer"
flag = ENTERTAINER
@@ -15,50 +15,50 @@
outfit_type = /datum/outfit/job/station/assistant
desc = "An entertainer does just that, entertains! Put on plays, play music, sing songs, tell stories, or read your favorite fanfic."
alt_titles = list(
- "Performer" = /datum/alt_title/entertainer/performer,
- "Musician" = /datum/alt_title/entertainer/musician,
- "Stagehand" = /datum/alt_title/entertainer/stagehand,
- "Actor" = /datum/alt_title/entertainer/actor,
- "Dancer" = /datum/alt_title/entertainer/dancer,
- "Singer" = /datum/alt_title/entertainer/singer,
- "Magician" = /datum/alt_title/entertainer/magician,
- "Comedian" = /datum/alt_title/entertainer/comedian,
- "Tragedian" = /datum/alt_title/entertainer/tragedian
+ "Performer" = /datum/prototype/alt_title/entertainer/performer,
+ "Musician" = /datum/prototype/alt_title/entertainer/musician,
+ "Stagehand" = /datum/prototype/alt_title/entertainer/stagehand,
+ "Actor" = /datum/prototype/alt_title/entertainer/actor,
+ "Dancer" = /datum/prototype/alt_title/entertainer/dancer,
+ "Singer" = /datum/prototype/alt_title/entertainer/singer,
+ "Magician" = /datum/prototype/alt_title/entertainer/magician,
+ "Comedian" = /datum/prototype/alt_title/entertainer/comedian,
+ "Tragedian" = /datum/prototype/alt_title/entertainer/tragedian
)
// Entertainer Alt Titles
-/datum/alt_title/entertainer/actor
+/datum/prototype/alt_title/entertainer/actor
title = "Actor"
title_blurb = "An Actor is someone who acts out a role! Whatever sort of character it is, get into it and impress people with power of comedy and tragedy!"
-/datum/alt_title/entertainer/performer
+/datum/prototype/alt_title/entertainer/performer
title = "Performer"
title_blurb = "A Performer is someone who performs! Whatever sort of performance will come to your mind, the world's a stage!"
-/datum/alt_title/entertainer/musician
+/datum/prototype/alt_title/entertainer/musician
title = "Musician"
title_blurb = "A Musician is someone who makes music with a wide variety of musical instruments!"
-/datum/alt_title/entertainer/stagehand
+/datum/prototype/alt_title/entertainer/stagehand
title = "Stagehand"
title_blurb = "A Stagehand typically performs everything the rest of the entertainers don't. Operate lights, shutters, windows, or narrate through your voicebox!"
-/datum/alt_title/entertainer/dancer
+/datum/prototype/alt_title/entertainer/dancer
title = "Dancer"
title_blurb = "A Dancer is someone who impresses people through power of their own body! From waltz to breakdance, as long as crowd as cheering!"
-/datum/alt_title/entertainer/singer
+/datum/prototype/alt_title/entertainer/singer
title = "Singer"
title_blurb = "A Singer is someone with gift of melodious voice! Impress people with your vocal range!"
-/datum/alt_title/entertainer/magician
+/datum/prototype/alt_title/entertainer/magician
title = "Magician"
title_blurb = "A Magician is someone who awes those around them with impossible! Show off your repertoire of magic tricks, while keeping the secret hidden!"
-/datum/alt_title/entertainer/comedian
+/datum/prototype/alt_title/entertainer/comedian
title = "Comedian"
title_blurb = "A Comedian will focus on making people laugh with the power of wit! Telling jokes, stand-up comedy, you are here to make others smile!"
-/datum/alt_title/entertainer/tragedian
+/datum/prototype/alt_title/entertainer/tragedian
title = "Tragedian"
title_blurb = "A Tragedian will focus on making people think about life and world around them! Life is a tragedy, and who's better to convey its emotions than you?"
diff --git a/code/modules/jobs/job_types/station/civillian/internals_affairs_agent.dm b/code/modules/jobs/job_types/station/civillian/internals_affairs_agent.dm
index a7bd677208a..59e6de27150 100644
--- a/code/modules/jobs/job_types/station/civillian/internals_affairs_agent.dm
+++ b/code/modules/jobs/job_types/station/civillian/internals_affairs_agent.dm
@@ -1,4 +1,4 @@
-/datum/job/station/lawyer
+/datum/role/job/station/lawyer
id = JOB_ID_LAWYER
title = "Internal Affairs Agent"
flag = LAWYER
@@ -14,16 +14,16 @@
minimal_player_age = 7
outfit_type = /datum/outfit/job/station/internal_affairs_agent
- alt_titles = list("Regulatory Affairs Agent" = /datum/alt_title/iaa/regulator)
+ alt_titles = list("Regulatory Affairs Agent" = /datum/prototype/alt_title/iaa/regulator)
desc = "An Internal Affairs Agent makes sure that the crew is following Standard Operating Procedure. They also \
handle complaints against crew members, and can have issues brought to the attention of Central Command, \
assuming their paperwork is in order."
-/datum/alt_title/iaa/regulator
+/datum/prototype/alt_title/iaa/regulator
title = "Regulatory Affairs Agent"
/*
-/datum/job/station/lawyer/equip(var/mob/living/carbon/human/H)
+/datum/role/job/station/lawyer/equip(var/mob/living/carbon/human/H)
. = ..()
if(.)
H.implant_loyalty(H)
diff --git a/code/modules/jobs/job_types/station/civillian/librarian.dm b/code/modules/jobs/job_types/station/civillian/librarian.dm
index da5fa141704..234d41513ac 100644
--- a/code/modules/jobs/job_types/station/civillian/librarian.dm
+++ b/code/modules/jobs/job_types/station/civillian/librarian.dm
@@ -1,4 +1,4 @@
-/datum/job/station/librarian
+/datum/role/job/station/librarian
id = JOB_ID_LIBRARIAN
title = "Librarian"
flag = LIBRARIAN
@@ -16,51 +16,51 @@
outfit_type = /datum/outfit/job/station/librarian
desc = "The Librarian curates the book selection in the Library, so the crew might enjoy it."
alt_titles = list(
- "Journalist" = /datum/alt_title/librarian/journalist,
- "Reporter" = /datum/alt_title/librarian/reporter,
- "Writer" = /datum/alt_title/librarian/writer,
- "Historian" = /datum/alt_title/librarian/historian,
- "Archivist" = /datum/alt_title/librarian/archivist,
- "Professor" = /datum/alt_title/librarian/professor,
- "Academic" = /datum/alt_title/librarian/academic,
- "Philosopher" = /datum/alt_title/librarian/philosopher
+ "Journalist" = /datum/prototype/alt_title/librarian/journalist,
+ "Reporter" = /datum/prototype/alt_title/librarian/reporter,
+ "Writer" = /datum/prototype/alt_title/librarian/writer,
+ "Historian" = /datum/prototype/alt_title/librarian/historian,
+ "Archivist" = /datum/prototype/alt_title/librarian/archivist,
+ "Professor" = /datum/prototype/alt_title/librarian/professor,
+ "Academic" = /datum/prototype/alt_title/librarian/academic,
+ "Philosopher" = /datum/prototype/alt_title/librarian/philosopher
)
-/datum/alt_title/librarian/librarian/reporter
+/datum/prototype/alt_title/librarian/librarian/reporter
title = "Reporter"
title_blurb = "Although NanoTrasen's official Press outlet is managed by Central Command, they often hire freelance journalists for local coverage."
title_outfit = /datum/outfit/job/station/librarian/reporter
// Librarian Alt Titles
-/datum/alt_title/librarian/journalist
+/datum/prototype/alt_title/librarian/journalist
title = "Journalist"
title_blurb = "The Journalist uses the Library as a base of operations, from which they can report the news and goings-on on the station with their camera."
-/datum/alt_title/librarian/writer
+/datum/prototype/alt_title/librarian/writer
title = "Writer"
title_blurb = "The Writer uses the Library as a quiet place to write whatever it is they choose to write."
-/datum/alt_title/librarian/reporter
+/datum/prototype/alt_title/librarian/reporter
title = "Reporter"
title_blurb = "The Reporter uses the Library as a base of operations, from which they can report the news and goings-on on the station with their camera."
-/datum/alt_title/librarian/historian
+/datum/prototype/alt_title/librarian/historian
title = "Historian"
title_blurb = "The Historian uses the Library as a base of operation to record any important events occuring on station."
-/datum/alt_title/librarian/archivist
+/datum/prototype/alt_title/librarian/archivist
title = "Archivist"
title_blurb = "The Archivist uses the Library as a base of operation to record any important events occuring on station."
-/datum/alt_title/librarian/professor
+/datum/prototype/alt_title/librarian/professor
title = "Professor"
title_blurb = "The Professor uses the Library as a base of operations to share their vast knowledge with the crew."
-/datum/alt_title/librarian/academic
+/datum/prototype/alt_title/librarian/academic
title = "Academic"
title_blurb = "The Academic uses the Library as a base of operations to share their vast knowledge with the crew."
-/datum/alt_title/librarian/philosopher
+/datum/prototype/alt_title/librarian/philosopher
title = "Philosopher"
title_blurb = "The Philosopher uses the Library as a base of operation to ruminate on nature of life and other great questions, and share their opinions with the crew."
diff --git a/code/modules/jobs/job_types/station/civillian/mime.dm b/code/modules/jobs/job_types/station/civillian/mime.dm
index 5fb531489f5..1a92fbae218 100644
--- a/code/modules/jobs/job_types/station/civillian/mime.dm
+++ b/code/modules/jobs/job_types/station/civillian/mime.dm
@@ -1,4 +1,4 @@
-/datum/job/station/mime
+/datum/role/job/station/mime
id = JOB_ID_MIME
title = "Mime"
flag = MIME
@@ -11,15 +11,15 @@
access = list(access_entertainment)
minimal_access = list(access_entertainment)
desc = "A Mime is there to entertain the crew and keep high morale using unbelievable performances and acting skills!"
- alt_titles = list("Poseur" = /datum/alt_title/mime/poseur)
+ alt_titles = list("Poseur" = /datum/prototype/alt_title/mime/poseur)
whitelist_only = 1
outfit_type = /datum/outfit/job/station/mime
pto_type = PTO_CIVILIAN
-/datum/alt_title/mime/poseur
+/datum/prototype/alt_title/mime/poseur
title = "Poseur"
-/datum/job/station/mime/get_access()
+/datum/role/job/station/mime/get_access()
if(config_legacy.assistant_maint)
return list(access_maint_tunnels, access_entertainment, access_tomfoolery, access_mime)
else
diff --git a/code/modules/jobs/job_types/station/civillian/pilot.dm b/code/modules/jobs/job_types/station/civillian/pilot.dm
index 54add9f7f1e..b62a488b189 100644
--- a/code/modules/jobs/job_types/station/civillian/pilot.dm
+++ b/code/modules/jobs/job_types/station/civillian/pilot.dm
@@ -1,4 +1,4 @@
-/datum/job/station/pilot
+/datum/role/job/station/pilot
id = JOB_ID_PILOT
title = "Pilot"
economy_payscale = ECONOMY_PAYSCALE_JOB_HELM
@@ -17,15 +17,15 @@
outfit_type = /datum/outfit/job/station/pilot
desc = "A Pilot flies the various shuttles in the Virgo-Erigone System."
alt_titles = list(
- "Co-Pilot" = /datum/alt_title/co_pilot,
- "Navigator" = /datum/alt_title/navigator
+ "Co-Pilot" = /datum/prototype/alt_title/co_pilot,
+ "Navigator" = /datum/prototype/alt_title/navigator
)
-/datum/alt_title/co_pilot
+/datum/prototype/alt_title/co_pilot
title = "Co-Pilot"
title_blurb = "A Co-Pilot is there primarily to assist main pilot as well as learn from them"
-/datum/alt_title/navigator
+/datum/prototype/alt_title/navigator
title = "Navigator"
/datum/outfit/job/station/pilot
diff --git a/code/modules/jobs/job_types/station/command/captain.dm b/code/modules/jobs/job_types/station/command/captain.dm
index c6c1499095f..6b0dff7a601 100644
--- a/code/modules/jobs/job_types/station/command/captain.dm
+++ b/code/modules/jobs/job_types/station/command/captain.dm
@@ -1,6 +1,6 @@
var/datum/legacy_announcement/minor/captain_announcement = new(do_newscast = 1)
-/datum/job/station/captain
+/datum/role/job/station/captain
id = JOB_ID_CAPTAIN
title = "Facility Director"
economy_payscale = ECONOMY_PAYSCALE_JOB_CAPTAIN
@@ -29,26 +29,26 @@ var/datum/legacy_announcement/minor/captain_announcement = new(do_newscast = 1)
they do not understand everything, and are expected to delegate tasks to the appropriate crew member. The Facility Director is expected to \
have an understanding of Standard Operating Procedure, and is subject to it, and legal action, in the same way as every other crew member."
alt_titles = list(
- "Overseer"= /datum/alt_title/overseer,
- "Site Manager" = /datum/alt_title/captain/site,
- "Director of Operations" = /datum/alt_title/captain/director,
- "Captain" = /datum/alt_title/captain/captain
+ "Overseer"= /datum/prototype/alt_title/overseer,
+ "Site Manager" = /datum/prototype/alt_title/captain/site,
+ "Director of Operations" = /datum/prototype/alt_title/captain/director,
+ "Captain" = /datum/prototype/alt_title/captain/captain
)
-/datum/job/station/captain/get_access()
+/datum/role/job/station/captain/get_access()
return get_all_station_access().Copy()
-/datum/alt_title/overseer
+/datum/prototype/alt_title/overseer
title = "Overseer"
-/datum/alt_title/captain/site
+/datum/prototype/alt_title/captain/site
title = "Site Manager"
-/datum/alt_title/captain/director
+/datum/prototype/alt_title/captain/director
title = "Director of Operations"
-/datum/alt_title/captain/captain
+/datum/prototype/alt_title/captain/captain
title = "Captain"
/datum/outfit/job/station/captain
diff --git a/code/modules/jobs/job_types/station/command/command_secretary.dm b/code/modules/jobs/job_types/station/command/command_secretary.dm
index 10a14fa78c2..f0aa46cbd81 100644
--- a/code/modules/jobs/job_types/station/command/command_secretary.dm
+++ b/code/modules/jobs/job_types/station/command/command_secretary.dm
@@ -1,4 +1,4 @@
-/datum/job/station/command_secretary
+/datum/role/job/station/command_secretary
id = JOB_ID_COMMAND_SECRETARY
title = "Command Secretary"
economy_payscale = ECONOMY_PAYSCALE_JOB_HELM
@@ -22,30 +22,30 @@
They are not Heads of Staff, and have no real authority."
alt_titles = list(
- "Command Liaison" = /datum/alt_title/command_liaison,
- "Bridge Secretary" = /datum/alt_title/bridge_secretary,
- "Command Assistant" = /datum/alt_title/command_assistant,
- "Command Intern" = /datum/alt_title/command_intern,
- "Helmsman" = /datum/alt_title/commsec/helmsman,
- "Bridge Officer" = /datum/alt_title/commsec/officer
+ "Command Liaison" = /datum/prototype/alt_title/command_liaison,
+ "Bridge Secretary" = /datum/prototype/alt_title/bridge_secretary,
+ "Command Assistant" = /datum/prototype/alt_title/command_assistant,
+ "Command Intern" = /datum/prototype/alt_title/command_intern,
+ "Helmsman" = /datum/prototype/alt_title/commsec/helmsman,
+ "Bridge Officer" = /datum/prototype/alt_title/commsec/officer
)
-/datum/alt_title/command_liaison
+/datum/prototype/alt_title/command_liaison
title = "Command Liaison"
-/datum/alt_title/bridge_secretary
+/datum/prototype/alt_title/bridge_secretary
title = "Bridge Secretary"
-/datum/alt_title/command_assistant
+/datum/prototype/alt_title/command_assistant
title = "Command Assistant"
-/datum/alt_title/command_intern
+/datum/prototype/alt_title/command_intern
title = "Command Intern"
-/datum/alt_title/commsec/helmsman
+/datum/prototype/alt_title/commsec/helmsman
title = "Helmsman"
-/datum/alt_title/commsec/officer
+/datum/prototype/alt_title/commsec/officer
title = "Bridge Officer"
title_outfit = /datum/outfit/job/station/command_secretary/bridge_officer
diff --git a/code/modules/jobs/job_types/station/command/head_of_personnel.dm b/code/modules/jobs/job_types/station/command/head_of_personnel.dm
index b2336c113a8..b71a3742041 100644
--- a/code/modules/jobs/job_types/station/command/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/station/command/head_of_personnel.dm
@@ -1,4 +1,4 @@
-/datum/job/station/head_of_personnel
+/datum/role/job/station/head_of_personnel
id = JOB_ID_HEAD_OF_PERSONNEL
title = "Head of Personnel"
flag = HOP
@@ -25,8 +25,8 @@
manage the Supply department, through the Quartermaster. In addition, the Head of Personnel oversees the personal accounts \
of the crew, including their money and access. If necessary, the Head of Personnel is first in line to assume Acting Command."
alt_titles = list(
- "Crew Resources Officer" = /datum/alt_title/cro,
- "Deputy Director" = /datum/alt_title/hop/deputy
+ "Crew Resources Officer" = /datum/prototype/alt_title/cro,
+ "Deputy Director" = /datum/prototype/alt_title/hop/deputy
)
access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers,
@@ -42,10 +42,10 @@
access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station,
access_hop, access_RC_announce, access_keycard_auth, access_gateway)
-/datum/alt_title/cro
+/datum/prototype/alt_title/cro
title = "Crew Resources Officer"
-/datum/alt_title/hop/deputy
+/datum/prototype/alt_title/hop/deputy
title = "Deputy Director"
/datum/outfit/job/station/head_of_personnel
diff --git a/code/modules/jobs/job_types/station/engineering/atmospherics_technician.dm b/code/modules/jobs/job_types/station/engineering/atmospherics_technician.dm
index a5846a64f51..b901faad8c1 100644
--- a/code/modules/jobs/job_types/station/engineering/atmospherics_technician.dm
+++ b/code/modules/jobs/job_types/station/engineering/atmospherics_technician.dm
@@ -1,4 +1,4 @@
-/datum/job/station/atmos
+/datum/role/job/station/atmos
id = JOB_ID_ATMOSPHERIC_TECHNICIAN
title = "Atmospheric Technician"
flag = ATMOSTECH
@@ -21,19 +21,19 @@
understanding of the pipes, vents, and scrubbers that move gasses around the station, and to be familiar with proper firefighting procedure."
alt_titles = list(
- "Atmospherics Maintainer" = /datum/alt_title/atmos_maint,
- "Pipe Network Specialist" = /datum/alt_title/pipe_spec,
- "Disposals Technician" = /datum/alt_title/disposals_tech
+ "Atmospherics Maintainer" = /datum/prototype/alt_title/atmos_maint,
+ "Pipe Network Specialist" = /datum/prototype/alt_title/pipe_spec,
+ "Disposals Technician" = /datum/prototype/alt_title/disposals_tech
)
// Atmos Tech Alt Titles
-/datum/alt_title/atmos_maint
+/datum/prototype/alt_title/atmos_maint
title = "Atmospherics Maintainer"
-/datum/alt_title/pipe_spec
+/datum/prototype/alt_title/pipe_spec
title = "Pipe Network Specialist"
-/datum/alt_title/disposals_tech
+/datum/prototype/alt_title/disposals_tech
title = "Disposals Technician"
title_blurb = "A Disposals Technician is an Atmospheric Technician still and can fulfill all the same duties, although specializes more in disposals delivery system's operations and configurations."
diff --git a/code/modules/jobs/job_types/station/engineering/chief_engineer.dm b/code/modules/jobs/job_types/station/engineering/chief_engineer.dm
index 0ef2f33ea79..b8f083c500b 100644
--- a/code/modules/jobs/job_types/station/engineering/chief_engineer.dm
+++ b/code/modules/jobs/job_types/station/engineering/chief_engineer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/chief_engineer
+/datum/role/job/station/chief_engineer
id = JOB_ID_CHIEF_ENGINEER
title = "Chief Engineer"
economy_payscale = ECONOMY_PAYSCALE_JOB_COMMAND
@@ -29,9 +29,9 @@
access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_ai_upload)
minimal_player_age = 7
alt_titles = list(
- "Head Engineer" = /datum/alt_title/head_engineer,
- "Maintenance Manager" = /datum/alt_title/maintenance_manager,
- "Engineering Director" = /datum/alt_title/engineering_director
+ "Head Engineer" = /datum/prototype/alt_title/head_engineer,
+ "Maintenance Manager" = /datum/prototype/alt_title/maintenance_manager,
+ "Engineering Director" = /datum/prototype/alt_title/engineering_director
)
outfit_type = /datum/outfit/job/station/chief_engineer
@@ -39,13 +39,13 @@
of manpower as much as they handle hands-on operations and repairs. They are also expected to keep the rest of the station informed of \
any structural threats to the station that may be hazardous to health or disruptive to work."
-/datum/alt_title/engineering_director
+/datum/prototype/alt_title/engineering_director
title = "Engineering Director"
-/datum/alt_title/head_engineer
+/datum/prototype/alt_title/head_engineer
title = "Head Engineer"
-/datum/alt_title/maintenance_manager
+/datum/prototype/alt_title/maintenance_manager
title = "Maintenance Manager"
/datum/outfit/job/station/chief_engineer
diff --git a/code/modules/jobs/job_types/station/engineering/senior_engineer.dm b/code/modules/jobs/job_types/station/engineering/senior_engineer.dm
index 45a6b020d2a..ff93b5a69c5 100644
--- a/code/modules/jobs/job_types/station/engineering/senior_engineer.dm
+++ b/code/modules/jobs/job_types/station/engineering/senior_engineer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/senior_engineer
+/datum/role/job/station/senior_engineer
title = "Senior Engineer"
id = JOB_ID_SENIOR_ENGINEER
economy_payscale = ECONOMY_PAYSCALE_JOB_SENIOR
diff --git a/code/modules/jobs/job_types/station/engineering/station_engineer.dm b/code/modules/jobs/job_types/station/engineering/station_engineer.dm
index ada0676d957..56a03132879 100644
--- a/code/modules/jobs/job_types/station/engineering/station_engineer.dm
+++ b/code/modules/jobs/job_types/station/engineering/station_engineer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/engineer
+/datum/role/job/station/engineer
id = JOB_ID_STATION_ENGINEER
title = "Station Engineer"
flag = ENGINEER
@@ -15,11 +15,11 @@
minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction)
alt_titles = list(
- "Maintenance Technician" = /datum/alt_title/maint_tech,
- "Engine Technician" = /datum/alt_title/engine_tech,
- "Electrician" = /datum/alt_title/electrician,
- "Apprentice Engineer" = /datum/alt_title/apprentice_engineer,
- "Construction Engineer" = /datum/alt_title/construction_engi
+ "Maintenance Technician" = /datum/prototype/alt_title/maint_tech,
+ "Engine Technician" = /datum/prototype/alt_title/engine_tech,
+ "Electrician" = /datum/prototype/alt_title/electrician,
+ "Apprentice Engineer" = /datum/prototype/alt_title/apprentice_engineer,
+ "Construction Engineer" = /datum/prototype/alt_title/construction_engi
)
minimal_player_age = 3
@@ -27,25 +27,25 @@
outfit_type = /datum/outfit/job/station/station_engineer
desc = "An Engineer keeps the station running. They repair damages, keep the atmosphere stable, and ensure that power is being \
generated and distributed. On quiet shifts, they may be called upon to make cosmetic alterations to the station."
-/datum/alt_title/maint_tech
+/datum/prototype/alt_title/maint_tech
title = "Maintenance Technician"
title_blurb = "A Maintenance Technician is generally a junior Engineer, and can be expected to run the mildly unpleasant or boring tasks that other \
Engineers don't care to do."
-/datum/alt_title/engine_tech
+/datum/prototype/alt_title/engine_tech
title = "Engine Technician"
title_blurb = "An Engine Technician tends to the engine, most commonly a Supermatter crystal. They are expected to be able to keep it stable, and \
possibly even run it beyond normal tolerances."
-/datum/alt_title/electrician
+/datum/prototype/alt_title/electrician
title = "Electrician"
title_blurb = "An Electrician's primary duty is making sure power is properly distributed thoughout the station, utilizing solars, substations, and other \
methods to ensure every department has power in an emergency."
-/datum/alt_title/apprentice_engineer
+/datum/prototype/alt_title/apprentice_engineer
title = "Apprentice Engineer"
-/datum/alt_title/construction_engi
+/datum/prototype/alt_title/construction_engi
title = "Construction Engineer"
title_blurb = "A Construction Engineer fulfills similar duties to other engineers, but usually occupies spare time with construction of extra facilities in dedicated areas or \
as additions to station layout."
diff --git a/code/modules/jobs/job_types/station/exploration/explorer.dm b/code/modules/jobs/job_types/station/exploration/explorer.dm
index e8472166b25..de0972550a6 100644
--- a/code/modules/jobs/job_types/station/exploration/explorer.dm
+++ b/code/modules/jobs/job_types/station/exploration/explorer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/explorer
+/datum/role/job/station/explorer
id = JOB_ID_EXPLORER
title = "Explorer"
economy_payscale = ECONOMY_PAYSCALE_JOB_DANGER
@@ -16,26 +16,26 @@
outfit_type = /datum/outfit/job/station/explorer
desc = "An Explorer searches for interesting things, and returns them to the station."
alt_titles = list(
- "Surveyor" = /datum/alt_title/surveyor,
- "Offsite Scout" = /datum/alt_title/offsite_scout,
- "Field Scout" = /datum/alt_title/explorer/field_scout,
- "Pioneer" = /datum/alt_title/explorer/pioneer,
- "Jr. Explorer" = /datum/alt_title/explorer/junior
+ "Surveyor" = /datum/prototype/alt_title/surveyor,
+ "Offsite Scout" = /datum/prototype/alt_title/offsite_scout,
+ "Field Scout" = /datum/prototype/alt_title/explorer/field_scout,
+ "Pioneer" = /datum/prototype/alt_title/explorer/pioneer,
+ "Jr. Explorer" = /datum/prototype/alt_title/explorer/junior
)
-/datum/alt_title/surveyor
+/datum/prototype/alt_title/surveyor
title = "Surveyor"
-/datum/alt_title/offsite_scout
+/datum/prototype/alt_title/offsite_scout
title = "Offsite Scout"
-/datum/alt_title/explorer/field_scout
+/datum/prototype/alt_title/explorer/field_scout
title = "Field Scout"
-/datum/alt_title/explorer/pioneer
+/datum/prototype/alt_title/explorer/pioneer
title = "Pioneer"
-/datum/alt_title/explorer/junior
+/datum/prototype/alt_title/explorer/junior
title = "Jr. Explorer"
/datum/outfit/job/station/explorer
diff --git a/code/modules/jobs/job_types/station/exploration/field_medic.dm b/code/modules/jobs/job_types/station/exploration/field_medic.dm
index 41871cb9b58..a9de09e2bb3 100644
--- a/code/modules/jobs/job_types/station/exploration/field_medic.dm
+++ b/code/modules/jobs/job_types/station/exploration/field_medic.dm
@@ -1,4 +1,4 @@
-/datum/job/station/field_medic
+/datum/role/job/station/field_medic
id = JOB_ID_FIELD_MEDIC
title = "Field Medic"
economy_payscale = ECONOMY_PAYSCALE_JOB_DANGER
@@ -17,14 +17,14 @@
outfit_type = /datum/outfit/job/station/sar
desc = "A Field medic works as the field doctor of expedition teams."
alt_titles = list(
- "Expedition Medic" = /datum/alt_title/expedition_medic,
- "Search and Rescue" = /datum/alt_title/field_medic/sar
+ "Expedition Medic" = /datum/prototype/alt_title/expedition_medic,
+ "Search and Rescue" = /datum/prototype/alt_title/field_medic/sar
)
-/datum/alt_title/expedition_medic
+/datum/prototype/alt_title/expedition_medic
title = "Expedition Medic"
-/datum/alt_title/field_medic/sar
+/datum/prototype/alt_title/field_medic/sar
title = "Search and Rescue"
/datum/outfit/job/station/sar
diff --git a/code/modules/jobs/job_types/station/exploration/pathfinder.dm b/code/modules/jobs/job_types/station/exploration/pathfinder.dm
index c16ae4499aa..d8fc06998b7 100644
--- a/code/modules/jobs/job_types/station/exploration/pathfinder.dm
+++ b/code/modules/jobs/job_types/station/exploration/pathfinder.dm
@@ -1,4 +1,4 @@
-/datum/job/station/pathfinder
+/datum/role/job/station/pathfinder
id = JOB_ID_PATHFINDER
title = "Pathfinder"
economy_payscale = ECONOMY_PAYSCALE_JOB_SENIOR
@@ -20,18 +20,18 @@
outfit_type = /datum/outfit/job/station/pathfinder
desc = "The Pathfinder's job is to lead and manage expeditions, and is the primary authority on all off-station expeditions."
alt_titles = list(
- "Expedition Lead" = /datum/alt_title/expedition_lead,
- "Exploration Manager" = /datum/alt_title/exploration_manager,
- "Lead Pioneer" = /datum/alt_title/pathfinder/pioneer
+ "Expedition Lead" = /datum/prototype/alt_title/expedition_lead,
+ "Exploration Manager" = /datum/prototype/alt_title/exploration_manager,
+ "Lead Pioneer" = /datum/prototype/alt_title/pathfinder/pioneer
)
-/datum/alt_title/expedition_lead
+/datum/prototype/alt_title/expedition_lead
title = "Expedition Lead"
-/datum/alt_title/exploration_manager
+/datum/prototype/alt_title/exploration_manager
title = "Exploration Manager"
-/datum/alt_title/pathfinder/pioneer
+/datum/prototype/alt_title/pathfinder/pioneer
title = "Lead Pioneer"
/datum/outfit/job/station/pathfinder
diff --git a/code/modules/jobs/job_types/station/medical/chemist.dm b/code/modules/jobs/job_types/station/medical/chemist.dm
index 44f7219245c..56b1267073d 100644
--- a/code/modules/jobs/job_types/station/medical/chemist.dm
+++ b/code/modules/jobs/job_types/station/medical/chemist.dm
@@ -1,4 +1,4 @@
-/datum/job/station/chemist
+/datum/role/job/station/chemist
id = JOB_ID_CHEMIST
title = "Chemist"
flag = CHEMIST
@@ -18,14 +18,14 @@
desc = "A Chemist produces and maintains a stock of basic to advanced chemicals for medical and occasionally research use. \
They are likely to know the use and dangers of many lab-produced chemicals."
alt_titles = list(
- "Pharmacist" = /datum/alt_title/pharmacist,
- "Pharmacologist" = /datum/alt_title/pharmacologist
+ "Pharmacist" = /datum/prototype/alt_title/pharmacist,
+ "Pharmacologist" = /datum/prototype/alt_title/pharmacologist
)
-/datum/alt_title/pharmacist
+/datum/prototype/alt_title/pharmacist
title = "Pharmacist"
title_blurb = "A Pharmacist focuses on the chemical needs of the Medical Department, and often offers to fill crew prescriptions at their discretion."
-/datum/alt_title/pharmacologist
+/datum/prototype/alt_title/pharmacologist
title = "Pharmacologist"
title_blurb = "A Pharmacologist focuses on the chemical needs of the Medical Department, primarily specializing in producing more advanced forms of medicine."
diff --git a/code/modules/jobs/job_types/station/medical/chief_medical_officer.dm b/code/modules/jobs/job_types/station/medical/chief_medical_officer.dm
index a0c4f6c0e9d..13a1352d743 100644
--- a/code/modules/jobs/job_types/station/medical/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/station/medical/chief_medical_officer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/chief_medical_officer
+/datum/role/job/station/chief_medical_officer
id = JOB_ID_CHIEF_MEDICAL_OFFICER
title = "Chief Medical Officer"
economy_payscale = ECONOMY_PAYSCALE_JOB_COMMAND
@@ -32,18 +32,18 @@
transported to Medical for treatment. They are expected to keep the crew informed about threats to their health and safety, and \
about the importance of Suit Sensors."
alt_titles = list (
- "Chief Physician" = /datum/alt_title/cmo/physician,
- "Director of Medicine" = /datum/alt_title/cmo/director,
- "Chief Surgeon" = /datum/alt_title/cmo/surgeon
+ "Chief Physician" = /datum/prototype/alt_title/cmo/physician,
+ "Director of Medicine" = /datum/prototype/alt_title/cmo/director,
+ "Chief Surgeon" = /datum/prototype/alt_title/cmo/surgeon
)
-/datum/alt_title/cmo/physician
+/datum/prototype/alt_title/cmo/physician
title = "Chief Physician"
-/datum/alt_title/cmo/director
+/datum/prototype/alt_title/cmo/director
title = "Director of Medicine"
-/datum/alt_title/cmo/surgeon
+/datum/prototype/alt_title/cmo/surgeon
title = "Chief Surgeon"
/datum/outfit/job/station/chief_medical_officer
diff --git a/code/modules/jobs/job_types/station/medical/geneticist.dm b/code/modules/jobs/job_types/station/medical/geneticist.dm
index 0e29a9b5c5f..188d9dc0a86 100644
--- a/code/modules/jobs/job_types/station/medical/geneticist.dm
+++ b/code/modules/jobs/job_types/station/medical/geneticist.dm
@@ -3,7 +3,7 @@
//////////////////////////////////
// Geneticist
//////////////////////////////////
-/datum/job/station/geneticist
+/datum/role/job/station/geneticist
id = "geneticist"
title = "Geneticist"
flag = GENETICIST
diff --git a/code/modules/jobs/job_types/station/medical/head_nurse.dm b/code/modules/jobs/job_types/station/medical/head_nurse.dm
index ba54a530760..d8597fa4854 100644
--- a/code/modules/jobs/job_types/station/medical/head_nurse.dm
+++ b/code/modules/jobs/job_types/station/medical/head_nurse.dm
@@ -1,4 +1,4 @@
-/datum/job/station/head_nurse
+/datum/role/job/station/head_nurse
title = "Head Nurse"
id = JOB_ID_HEAD_NURSE
flag = HEAD_NURSE
@@ -23,18 +23,18 @@
ideal_character_age = 45
alt_titles = list (
- "Medical Specialist" = /datum/alt_title/medical_specialist,
- "Consultant Physician" = /datum/alt_title/consultant_physician,
+ "Medical Specialist" = /datum/prototype/alt_title/medical_specialist,
+ "Consultant Physician" = /datum/prototype/alt_title/consultant_physician,
)
-/datum/alt_title/medical_specialist
+/datum/prototype/alt_title/medical_specialist
title = "Medical Specialist"
title_blurb = "A Medical Specialist is a senior medical professional with extensive knowledge within a particular field of medicine which \
is expected to perform the standard duties of a medical doctor, as well as offer training, guidance and oversight to both resident \
and attending physicians in all matters, especially when presented with difficult situations within their field of expertise."
title_outfit = /datum/outfit/job/station/medical_doctor
-/datum/alt_title/consultant_physician
+/datum/prototype/alt_title/consultant_physician
title = "Consultant Physician"
title_blurb = "A Consultant Physician is a senior medical professional with extensive training in general medical practice which is expected to perform the \
standard duties of a medical doctor, as well as offer training, guidance and oversight to resident and attending physicians, especially when presented with difficult \
diff --git a/code/modules/jobs/job_types/station/medical/medical_doctor.dm b/code/modules/jobs/job_types/station/medical/medical_doctor.dm
index 6c01af6fd3a..e86407740e1 100644
--- a/code/modules/jobs/job_types/station/medical/medical_doctor.dm
+++ b/code/modules/jobs/job_types/station/medical/medical_doctor.dm
@@ -1,4 +1,4 @@
-/datum/job/station/doctor
+/datum/role/job/station/doctor
id = JOB_ID_MEDICAL_DOCTOR
title = "Medical Doctor"
flag = DOCTOR
@@ -17,52 +17,52 @@
familiar with basic first aid, and a number of accompanying medications, and can generally save, if not cure, a majority of the \
patients they encounter."
alt_titles = list(
- "Surgeon" = /datum/alt_title/surgeon,
- "Emergency Physician" = /datum/alt_title/emergency_physician,
- "Nurse" = /datum/alt_title/nurse,
- "Virologist" = /datum/alt_title/virologist,
- "Medical Resident" = /datum/alt_title/doctor/resident,
- "Medical Intern" = /datum/alt_title/doctor/intern,
- "Orderly" = /datum/alt_title/orderly
+ "Surgeon" = /datum/prototype/alt_title/surgeon,
+ "Emergency Physician" = /datum/prototype/alt_title/emergency_physician,
+ "Nurse" = /datum/prototype/alt_title/nurse,
+ "Virologist" = /datum/prototype/alt_title/virologist,
+ "Medical Resident" = /datum/prototype/alt_title/doctor/resident,
+ "Medical Intern" = /datum/prototype/alt_title/doctor/intern,
+ "Orderly" = /datum/prototype/alt_title/orderly
)
// Medical Doctor Alt Titles
-/datum/alt_title/surgeon
+/datum/prototype/alt_title/surgeon
title = "Surgeon"
title_blurb = "A Surgeon specializes in providing surgical aid to injured patients, up to and including amputation and limb reattachement. They are expected \
to know the ins and outs of anesthesia and surgery."
title_outfit = /datum/outfit/job/station/medical_doctor/surgeon
-/datum/alt_title/orderly
+/datum/prototype/alt_title/orderly
title = "Orderly"
title_blurb = "An Orderly acts as Medbay's general helping hand, assisting any doctor that might need some form of help, as well as handling manual \
and dirty labor around the department."
title_outfit = /datum/outfit/job/station/medical_doctor/nurse
-/datum/alt_title/emergency_physician
+/datum/prototype/alt_title/emergency_physician
title = "Emergency Physician"
title_blurb = "An Emergency Physician is a Medical professional trained for stabilizing and treating severely injured and/or dying patients. \
They are generally the first response for any such individual brought to the Medbay, and can sometimes be expected to help their patients \
make a full recovery."
title_outfit = /datum/outfit/job/station/medical_doctor/emergency_physician
-/datum/alt_title/nurse
+/datum/prototype/alt_title/nurse
title = "Nurse"
title_blurb = "A Nurse acts as a general purpose Doctor's Aide, providing basic care to non-critical patients, and stabilizing critical patients during \
busy periods. They frequently watch the suit sensors console, to help manage the time of other Doctors. In rare occasions, a Nurse can be \
called upon to revive deceased crew members."
title_outfit = /datum/outfit/job/station/medical_doctor/nurse
-/datum/alt_title/virologist
+/datum/prototype/alt_title/virologist
title = "Virologist"
title_blurb = "A Virologist cures active diseases in the crew, and prepares antibodies for possible infections. They also have the skills \
to produce the various types of virus foods or mutagens."
title_outfit = /datum/outfit/job/station/medical_doctor/virologist
-/datum/alt_title/doctor/resident
+/datum/prototype/alt_title/doctor/resident
title = "Medical Resident"
-/datum/alt_title/doctor/intern
+/datum/prototype/alt_title/doctor/intern
title = "Medical Intern"
/datum/outfit/job/station/medical_doctor
diff --git a/code/modules/jobs/job_types/station/medical/paramedic.dm b/code/modules/jobs/job_types/station/medical/paramedic.dm
index 70e17d242ff..6769226bf3b 100644
--- a/code/modules/jobs/job_types/station/medical/paramedic.dm
+++ b/code/modules/jobs/job_types/station/medical/paramedic.dm
@@ -1,4 +1,4 @@
-/datum/job/station/paramedic
+/datum/role/job/station/paramedic
id = JOB_ID_PARAMEDIC
title = "Paramedic"
flag = PARAMEDIC
@@ -16,17 +16,17 @@
desc = "A Paramedic is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their own. \
They may also be called upon to keep patients stable when Medical is busy or understaffed."
alt_titles = list(
- "Emergency Medical Technician" = /datum/alt_title/emt,
- "Medical Responder" = /datum/alt_title/medical_responder
+ "Emergency Medical Technician" = /datum/prototype/alt_title/emt,
+ "Medical Responder" = /datum/prototype/alt_title/medical_responder
)
-/datum/alt_title/emt
+/datum/prototype/alt_title/emt
title = "Emergency Medical Technician"
title_blurb = "An Emergency Medical Technician is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \
own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training."
title_outfit = /datum/outfit/job/station/paramedic/emt
-/datum/alt_title/medical_responder
+/datum/prototype/alt_title/medical_responder
title = "Medical Responder"
title_blurb = "A Medical Responder is primarily concerned with the recovery of patients who are unable to make it to the Medical Department on their \
own. They are capable of keeping a patient stabilized until they reach the hands of someone with more training."
diff --git a/code/modules/jobs/job_types/station/medical/psychiatrist.dm b/code/modules/jobs/job_types/station/medical/psychiatrist.dm
index 703d6ee3a9b..b2fe6ee93ac 100644
--- a/code/modules/jobs/job_types/station/medical/psychiatrist.dm
+++ b/code/modules/jobs/job_types/station/medical/psychiatrist.dm
@@ -1,4 +1,4 @@
-/datum/job/station/psychiatrist
+/datum/role/job/station/psychiatrist
id = JOB_ID_PSYCHIATRIST
title = "Psychiatrist"
flag = PSYCHIATRIST
@@ -16,27 +16,27 @@
desc = "A Psychiatrist provides mental health services to crew members in need. They may also be called upon to determine whatever \
ails the mentally unwell, frequently under Security supervision. They understand the effects of various psychoactive drugs."
alt_titles = list(
- "Psychologist" = /datum/alt_title/psychologist,
- "Psychoanalyst" = /datum/alt_title/psychologist/psychoanalyst,
- "Counselor" = /datum/alt_title/counselor,
- "Therapist" = /datum/alt_title/therapist
+ "Psychologist" = /datum/prototype/alt_title/psychologist,
+ "Psychoanalyst" = /datum/prototype/alt_title/psychologist/psychoanalyst,
+ "Counselor" = /datum/prototype/alt_title/counselor,
+ "Therapist" = /datum/prototype/alt_title/therapist
)
-/datum/alt_title/psychologist
+/datum/prototype/alt_title/psychologist
title = "Psychologist"
title_blurb = "A Psychologist provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \
called upon to determine whatever ails the mentally unwell, frequently under Security supervision."
title_outfit = /datum/outfit/job/station/psychiatrist/psychologist
-/datum/alt_title/psychologist/psychoanalyst
+/datum/prototype/alt_title/psychologist/psychoanalyst
title = "Psychoanalyst"
title_blurb = "A Psychoanalyst provides mental health services to crew members in need, focusing more on therapy than medication. They may also be \
called upon to determine whatever ails the mentally unwell, frequently under Security supervision."
-/datum/alt_title/counselor
+/datum/prototype/alt_title/counselor
title = "Counselor"
-/datum/alt_title/therapist
+/datum/prototype/alt_title/therapist
title = "Therapist"
/datum/outfit/job/station/psychiatrist
diff --git a/code/modules/jobs/job_types/station/offduty/_offduty.dm b/code/modules/jobs/job_types/station/offduty/_offduty.dm
index cf49ea9b540..105212f81f3 100644
--- a/code/modules/jobs/job_types/station/offduty/_offduty.dm
+++ b/code/modules/jobs/job_types/station/offduty/_offduty.dm
@@ -1,5 +1,5 @@
-/datum/job/station/off_duty
- abstract_type = /datum/job/station/off_duty
+/datum/role/job/station/off_duty
+ abstract_type = /datum/role/job/station/off_duty
join_types = JOB_LATEJOIN
timeoff_factor = -1
total_positions = -1
diff --git a/code/modules/jobs/job_types/station/offduty/civillian.dm b/code/modules/jobs/job_types/station/offduty/civillian.dm
index 4695bc26ab4..88a68d4ebf9 100644
--- a/code/modules/jobs/job_types/station/offduty/civillian.dm
+++ b/code/modules/jobs/job_types/station/offduty/civillian.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/civilian
+/datum/role/job/station/off_duty/civilian
id = JOB_ID_OFFDUTY_CIVILLIAN
title = "Off-duty Worker"
selection_color = "#9b633e"
diff --git a/code/modules/jobs/job_types/station/offduty/command.dm b/code/modules/jobs/job_types/station/offduty/command.dm
index 039bdf164c8..b7c3784e6ca 100644
--- a/code/modules/jobs/job_types/station/offduty/command.dm
+++ b/code/modules/jobs/job_types/station/offduty/command.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/command
+/datum/role/job/station/off_duty/command
id = JOB_ID_OFFDUTY_COMMAND
title = "Off-duty Command"
timeoff_factor = -1
diff --git a/code/modules/jobs/job_types/station/offduty/engineering.dm b/code/modules/jobs/job_types/station/offduty/engineering.dm
index fc652722937..c12f203969b 100644
--- a/code/modules/jobs/job_types/station/offduty/engineering.dm
+++ b/code/modules/jobs/job_types/station/offduty/engineering.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/engineering
+/datum/role/job/station/off_duty/engineering
id = JOB_ID_OFFDUTY_ENGINEER
title = "Off-duty Engineer"
selection_color = "#5B4D20"
diff --git a/code/modules/jobs/job_types/station/offduty/exploration.dm b/code/modules/jobs/job_types/station/offduty/exploration.dm
index 782b48af724..a71bb69f8aa 100644
--- a/code/modules/jobs/job_types/station/offduty/exploration.dm
+++ b/code/modules/jobs/job_types/station/offduty/exploration.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/exploration
+/datum/role/job/station/off_duty/exploration
id = JOB_ID_OFFDUTY_EXPLORER
title = "Off-duty Explorer"
selection_color = "#999440"
diff --git a/code/modules/jobs/job_types/station/offduty/medical.dm b/code/modules/jobs/job_types/station/offduty/medical.dm
index 9b18a921504..f69e6448f69 100644
--- a/code/modules/jobs/job_types/station/offduty/medical.dm
+++ b/code/modules/jobs/job_types/station/offduty/medical.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/medical
+/datum/role/job/station/off_duty/medical
id = JOB_ID_OFFDUTY_MEDBAY
title = "Off-duty Medic"
selection_color = "#013D3B"
diff --git a/code/modules/jobs/job_types/station/offduty/science.dm b/code/modules/jobs/job_types/station/offduty/science.dm
index 0fc149cf28f..59f21275ef9 100644
--- a/code/modules/jobs/job_types/station/offduty/science.dm
+++ b/code/modules/jobs/job_types/station/offduty/science.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/science
+/datum/role/job/station/off_duty/science
id = JOB_ID_OFFDUTY_SCIENCE
title = "Off-duty Scientist"
selection_color = "#633D63"
diff --git a/code/modules/jobs/job_types/station/offduty/security.dm b/code/modules/jobs/job_types/station/offduty/security.dm
index a01de547b9f..98c9afa7e48 100644
--- a/code/modules/jobs/job_types/station/offduty/security.dm
+++ b/code/modules/jobs/job_types/station/offduty/security.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/security
+/datum/role/job/station/off_duty/security
id = JOB_ID_OFFDUTY_SECURITY
title = "Off-duty Officer"
selection_color = "#601C1C"
diff --git a/code/modules/jobs/job_types/station/offduty/supply.dm b/code/modules/jobs/job_types/station/offduty/supply.dm
index 7e6dcd7b4fa..cff90498cf1 100644
--- a/code/modules/jobs/job_types/station/offduty/supply.dm
+++ b/code/modules/jobs/job_types/station/offduty/supply.dm
@@ -1,4 +1,4 @@
-/datum/job/station/off_duty/cargo
+/datum/role/job/station/off_duty/cargo
id = JOB_ID_OFFDUTY_CARGO
title = "Off-duty Cargo"
selection_color = "#9b633e"
diff --git a/code/modules/jobs/job_types/station/science/research_director.dm b/code/modules/jobs/job_types/station/science/research_director.dm
index f9d3f840c64..2c9b12f3ef7 100644
--- a/code/modules/jobs/job_types/station/science/research_director.dm
+++ b/code/modules/jobs/job_types/station/science/research_director.dm
@@ -1,4 +1,4 @@
-/datum/job/station/research_director
+/datum/role/job/station/research_director
id = JOB_ID_RESEARCH_DIRECTOR
title = "Research Director"
economy_payscale = ECONOMY_PAYSCALE_JOB_COMMAND
@@ -34,18 +34,18 @@
might originate from Research. The Research Director often has at least passing knowledge of most of the Research department, but \
are encouraged to allow their staff to perform their own duties."
alt_titles = list(
- "Research Supervisor" = /datum/alt_title/research_supervisor,
- "Head of Development" = /datum/alt_title/head_of_development,
- "Head Scientist" = /datum/alt_title/head_scientist
+ "Research Supervisor" = /datum/prototype/alt_title/research_supervisor,
+ "Head of Development" = /datum/prototype/alt_title/head_of_development,
+ "Head Scientist" = /datum/prototype/alt_title/head_scientist
)
-/datum/alt_title/research_supervisor
+/datum/prototype/alt_title/research_supervisor
title = "Research Supervisor"
-/datum/alt_title/head_of_development
+/datum/prototype/alt_title/head_of_development
title = "Head of Development"
-/datum/alt_title/head_scientist
+/datum/prototype/alt_title/head_scientist
title = "Head Scientist"
/datum/outfit/job/station/research_director
diff --git a/code/modules/jobs/job_types/station/science/roboticist.dm b/code/modules/jobs/job_types/station/science/roboticist.dm
index 17ad1bd1e50..9d1dc5ba6df 100644
--- a/code/modules/jobs/job_types/station/science/roboticist.dm
+++ b/code/modules/jobs/job_types/station/science/roboticist.dm
@@ -1,4 +1,4 @@
-/datum/job/station/roboticist
+/datum/role/job/station/roboticist
id = JOB_ID_ROBOTICIST
title = "Roboticist"
flag = ROBOTICIST
@@ -17,22 +17,22 @@
desc = "A Roboticist maintains and repairs the station's synthetics, including crew with prosthetic limbs. \
They can also assist the station by producing simple robots and even pilotable exosuits."
alt_titles = list(
- "Biomechanical Engineer" = /datum/alt_title/biomech,
- "Mechatronic Engineer" = /datum/alt_title/mech_tech,
- "Prosthetists" = /datum/alt_title/prosthetists
+ "Biomechanical Engineer" = /datum/prototype/alt_title/biomech,
+ "Mechatronic Engineer" = /datum/prototype/alt_title/mech_tech,
+ "Prosthetists" = /datum/prototype/alt_title/prosthetists
)
-/datum/alt_title/biomech
+/datum/prototype/alt_title/biomech
title = "Biomechanical Engineer"
title_blurb = "A Biomechanical Engineer primarily works on prosthetics, and the organic parts attached to them. They may have some \
knowledge of the relatively simple surgical procedures used in making cyborgs and attaching prosthesis."
-/datum/alt_title/mech_tech
+/datum/prototype/alt_title/mech_tech
title = "Mechatronic Engineer"
title_blurb = "A Mechatronic Engineer focuses on the construction and maintenance of Exosuits, and should be well versed in their use. \
They may also be called upon to work on synthetics and prosthetics, if needed."
-/datum/alt_title/prosthetists
+/datum/prototype/alt_title/prosthetists
title = "Prosthetists"
title_blurb = "Prosthetists design and fabricate medical supportive devices and measure and fit patients for them. These devices \
include artificial limbs (arms, hands, legs, and feet), braces, and other medical or surgical devices."
diff --git a/code/modules/jobs/job_types/station/science/scientist.dm b/code/modules/jobs/job_types/station/science/scientist.dm
index 0c0e9b669da..296a0d063ee 100644
--- a/code/modules/jobs/job_types/station/science/scientist.dm
+++ b/code/modules/jobs/job_types/station/science/scientist.dm
@@ -1,4 +1,4 @@
-/datum/job/station/scientist
+/datum/role/job/station/scientist
id = JOB_ID_SCIENTIST
title = "Scientist"
flag = SCIENTIST
@@ -20,62 +20,62 @@
the principles and requirements of Research and Development. They may also formulate experiments of their own devising, if \
they find an appropriate topic."
alt_titles = list(
- "Junior Scientist" = /datum/alt_title/scientist/junior,
- "Lab Assistant" = /datum/alt_title/scientist/assistant,
- "Researcher" = /datum/alt_title/scientist/researcher,
- "Xenoarchaeologist" = /datum/alt_title/scientist/xenoarch,
- "Anomalist" = /datum/alt_title/scientist/anomalist, \
- "Phoron Researcher" = /datum/alt_title/scientist/phoron_research,
- "Circuit Designer" = /datum/alt_title/scientist/circuit,
- "Research Field Technician" = /datum/alt_title/scientist/fieldtech,
- "Xenobotanist" = /datum/alt_title/scientist/xenobotanist,
- "Xenobiologist" = /datum/alt_title/scientist/xenobiologist
+ "Junior Scientist" = /datum/prototype/alt_title/scientist/junior,
+ "Lab Assistant" = /datum/prototype/alt_title/scientist/assistant,
+ "Researcher" = /datum/prototype/alt_title/scientist/researcher,
+ "Xenoarchaeologist" = /datum/prototype/alt_title/scientist/xenoarch,
+ "Anomalist" = /datum/prototype/alt_title/scientist/anomalist, \
+ "Phoron Researcher" = /datum/prototype/alt_title/scientist/phoron_research,
+ "Circuit Designer" = /datum/prototype/alt_title/scientist/circuit,
+ "Research Field Technician" = /datum/prototype/alt_title/scientist/fieldtech,
+ "Xenobotanist" = /datum/prototype/alt_title/scientist/xenobotanist,
+ "Xenobiologist" = /datum/prototype/alt_title/scientist/xenobiologist
)
-/datum/alt_title/scientist/junior
+/datum/prototype/alt_title/scientist/junior
title = "Junior Scientist"
title_blurb = "A Junior Scientist is a lower-level member of research staff, whose main purpose is to help scientists with their specialized work in more menial fashion, while also \
learning the specializations in process."
-/datum/alt_title/scientist/assistant
+/datum/prototype/alt_title/scientist/assistant
title = "Lab Assistant"
title_blurb = "A Lab Assistant is a lower-level member of research staff, whose main purpose is to help scientists with their specialized work in more menial fashion, while also \
learning the specializations in process."
-/datum/alt_title/scientist/researcher
+/datum/prototype/alt_title/scientist/researcher
title = "Researcher"
-/datum/alt_title/scientist/xenoarch
+/datum/prototype/alt_title/scientist/xenoarch
title = "Xenoarchaeologist"
title_blurb = "A Xenoarchaeologist enters digsites in search of artifacts of alien origin. These digsites are frequently in vacuum or other inhospitable \
locations, and as such a Xenoarchaeologist should be prepared to handle hostile evironmental conditions."
-/datum/alt_title/scientist/anomalist
+/datum/prototype/alt_title/scientist/anomalist
title = "Anomalist"
title_blurb = "An Anomalist is a Scientist whose expertise is analyzing alien artifacts. They are familar with the most common methods of testing artifact \
function. They work closely with Xenoarchaeologists, or Miners, if either role is present."
-/datum/alt_title/scientist/phoron_research
+/datum/prototype/alt_title/scientist/phoron_research
title = "Phoron Researcher"
title_blurb = "A Phoron Researcher is a specialist in the practical applications of phoron, and has knowledge of its practical uses and dangers. \
Many Phoron Researchers are interested in the combustability and explosive properties of gaseous phoron, as well as the specific hazards \
of working with the substance in that state."
-/datum/alt_title/scientist/circuit
+/datum/prototype/alt_title/scientist/circuit
title = "Circuit Designer"
title_blurb = "A Circuit Designer is a Scientist whose expertise is working with integrated circuits. They are familar with the workings and programming of those devices. \
They work to create various useful devices using the capabilities of integrated circuitry."
-/datum/alt_title/scientist/fieldtech
+/datum/prototype/alt_title/scientist/fieldtech
title = "Research Field Technician"
-/datum/alt_title/scientist/xenobiologist
+/datum/prototype/alt_title/scientist/xenobiologist
title = "Xenobiologist"
title_blurb = "A Xenobiologist studies esoteric lifeforms, usually in the relative safety of their lab. They attempt to find ways to benefit \
from the byproducts of these lifeforms, and their main subject at present is the Giant Slime."
title_outfit = /datum/outfit/job/station/scientist/xenobiologist
-/datum/alt_title/scientist/xenobotanist
+/datum/prototype/alt_title/scientist/xenobotanist
title = "Xenobotanist"
title_blurb = "A Xenobotanist grows and cares for a variety of abnormal, custom made, and frequently dangerous plant life. When the products of these plants \
are both safe and beneficial to the station, they may choose to introduce it to the rest of the crew."
diff --git a/code/modules/jobs/job_types/station/science/senior_researcher.dm b/code/modules/jobs/job_types/station/science/senior_researcher.dm
index e243684fcb9..3ab16d120c4 100644
--- a/code/modules/jobs/job_types/station/science/senior_researcher.dm
+++ b/code/modules/jobs/job_types/station/science/senior_researcher.dm
@@ -1,4 +1,4 @@
-/datum/job/station/senior_researcher
+/datum/role/job/station/senior_researcher
title = "Senior Researcher"
economy_payscale = ECONOMY_PAYSCALE_JOB_SENIOR
id = JOB_ID_SENIOR_RESEARCHER
diff --git a/code/modules/jobs/job_types/station/science/xenobiologist.dm b/code/modules/jobs/job_types/station/science/xenobiologist.dm
index 18f99ba5760..41f6ff6ec68 100644
--- a/code/modules/jobs/job_types/station/science/xenobiologist.dm
+++ b/code/modules/jobs/job_types/station/science/xenobiologist.dm
@@ -2,7 +2,7 @@
//////////////////////////////////
// Xenobiologist
//////////////////////////////////
-/datum/job/station/xenobiologist
+/datum/role/job/station/xenobiologist
title = "Xenobiologist"
flag = XENOBIOLOGIST
departments = list(DEPARTMENT_RESEARCH)
@@ -23,16 +23,16 @@
from the byproducts of these lifeforms, and their main subject at present is the Giant Slime."
alt_titles = list(
- "Xenozoologist" = /datum/alt_title/xenozoologist,
- "Xenoanthropologist" = /datum/alt_title/xenoanthropologist
+ "Xenozoologist" = /datum/prototype/alt_title/xenozoologist,
+ "Xenoanthropologist" = /datum/prototype/alt_title/xenoanthropologist
)
// Xenibiologist Alt Titles
-/datum/alt_title/xenozoologist
+/datum/prototype/alt_title/xenozoologist
title = "Xenozoologist"
title_blurb = "Xenozoologists are well versed in their study of extra-terrestrial life." // Someone make a better blurb please
-/datum/alt_title/xenoanthropologist
+/datum/prototype/alt_title/xenoanthropologist
title = "Xenoanthropologist"
title_blurb = "Xenoanthropologist still heavily focuses their study on alien lifeforms, but their specialty leans more towards fellow sapient beings than simple animals."
*/
diff --git a/code/modules/jobs/job_types/station/science/xenobotanist.dm b/code/modules/jobs/job_types/station/science/xenobotanist.dm
index 34acc4bc57e..a8a166a0a64 100644
--- a/code/modules/jobs/job_types/station/science/xenobotanist.dm
+++ b/code/modules/jobs/job_types/station/science/xenobotanist.dm
@@ -2,7 +2,7 @@
//////////////////////////////////
// Xenobotanist
//////////////////////////////////
-/datum/job/station/xenobotanist
+/datum/role/job/station/xenobotanist
title = "Xenobotanist"
flag = XENOBOTANIST
departments = list(DEPARTMENT_RESEARCH)
@@ -21,13 +21,13 @@
desc = "A Xenobotanist grows and cares for a variety of abnormal, custom made, and frequently dangerous plant life. When the products of these plants \
are both safe and beneficial to the station, they may choose to introduce it to the rest of the crew."
alt_titles = list(
- "Xenohydroponicist" = /datum/alt_title/xenohydroponicist,
- "Xenoflorist" = /datum/alt_title/xenoflorist
+ "Xenohydroponicist" = /datum/prototype/alt_title/xenohydroponicist,
+ "Xenoflorist" = /datum/prototype/alt_title/xenoflorist
)
-/datum/alt_title/xenoflorist
+/datum/prototype/alt_title/xenoflorist
title = "Xenoflorist"
-/datum/alt_title/xenohydroponicist
+/datum/prototype/alt_title/xenohydroponicist
title = "Xenohydroponicist"
*/
diff --git a/code/modules/jobs/job_types/station/security/detective.dm b/code/modules/jobs/job_types/station/security/detective.dm
index 5a1ae18ba6c..c0693affe79 100644
--- a/code/modules/jobs/job_types/station/security/detective.dm
+++ b/code/modules/jobs/job_types/station/security/detective.dm
@@ -1,4 +1,4 @@
-/datum/job/station/detective
+/datum/role/job/station/detective
id = JOB_ID_DETECTIVE
title = "Detective"
flag = DETECTIVE
@@ -17,15 +17,15 @@
desc = "A Detective works to help Security find criminals who have not properly been identified, through interviews and forensic work. \
For crimes only witnessed after the fact, or those with no survivors, they attempt to piece together what they can from pure evidence."
alt_titles = list(
- "Forensic Technician" = /datum/alt_title/detective/forensics_tech,
- "Crime Scene Investigator" = /datum/alt_title/detective/csi
+ "Forensic Technician" = /datum/prototype/alt_title/detective/forensics_tech,
+ "Crime Scene Investigator" = /datum/prototype/alt_title/detective/csi
)
-/datum/alt_title/detective/csi
+/datum/prototype/alt_title/detective/csi
title = "Crime Scene Investigator"
/// Detective Alt Titles
-/datum/alt_title/detective/forensics_tech
+/datum/prototype/alt_title/detective/forensics_tech
title = "Forensic Technician"
title_blurb = "A Forensic Technician works more with hard evidence and labwork than a Detective, but they share the purpose of solving crimes."
title_outfit = /datum/outfit/job/station/detective/forensic
diff --git a/code/modules/jobs/job_types/station/security/head_of_security.dm b/code/modules/jobs/job_types/station/security/head_of_security.dm
index 32d3860f8dc..d7855474377 100644
--- a/code/modules/jobs/job_types/station/security/head_of_security.dm
+++ b/code/modules/jobs/job_types/station/security/head_of_security.dm
@@ -1,4 +1,4 @@
-/datum/job/station/head_of_security
+/datum/role/job/station/head_of_security
id = JOB_ID_HEAD_OF_SECURITY
economy_payscale = ECONOMY_PAYSCALE_JOB_COMMAND
title = "Head of Security"
@@ -32,18 +32,18 @@
keep the other Department Heads, and the rest of the crew, aware of developing situations that may be a threat. If necessary, the HoS may \
perform the duties of absent Security roles, such as distributing gear from the Armory."
alt_titles = list(
- "Security Commander" = /datum/alt_title/hos/commander,
- "Chief of Security" = /datum/alt_title/hos/chief,
- "Defense Director" = /datum/alt_title/hos/director
+ "Security Commander" = /datum/prototype/alt_title/hos/commander,
+ "Chief of Security" = /datum/prototype/alt_title/hos/chief,
+ "Defense Director" = /datum/prototype/alt_title/hos/director
)
-/datum/alt_title/hos/commander
+/datum/prototype/alt_title/hos/commander
title = "Security Commander"
-/datum/alt_title/hos/chief
+/datum/prototype/alt_title/hos/chief
title = "Chief of Security"
-/datum/alt_title/hos/director
+/datum/prototype/alt_title/hos/director
title = "Defense Director"
/datum/outfit/job/station/head_of_security
diff --git a/code/modules/jobs/job_types/station/security/security_officer.dm b/code/modules/jobs/job_types/station/security/security_officer.dm
index d494d103dab..e4e6dbc8b41 100644
--- a/code/modules/jobs/job_types/station/security/security_officer.dm
+++ b/code/modules/jobs/job_types/station/security/security_officer.dm
@@ -1,4 +1,4 @@
-/datum/job/station/officer
+/datum/role/job/station/officer
id = JOB_ID_SECURITY_OFFICER
title = "Security Officer"
flag = OFFICER
@@ -19,21 +19,21 @@
apprehending criminals. A Security Officer is responsible for the health, safety, and processing of any prisoner they arrest. \
No one is above the Law, not Security or Command."
alt_titles = list(
- "Junior Officer" = /datum/alt_title/security_officer/junior,
- "Security Cadet" = /datum/alt_title/security_officer/cadet,
- "Security Guard" = /datum/alt_title/security_officer/guard
+ "Junior Officer" = /datum/prototype/alt_title/security_officer/junior,
+ "Security Cadet" = /datum/prototype/alt_title/security_officer/cadet,
+ "Security Guard" = /datum/prototype/alt_title/security_officer/guard
)
-/datum/alt_title/security_officer/junior
+/datum/prototype/alt_title/security_officer/junior
title = "Junior Officer"
title_blurb = "A Junior Officer is an inexperienced Security Officer. They likely have training, but not experience, and are frequently \
paired off with a more senior co-worker. Junior Officers may also be expected to take over the boring duties of other Officers \
including patrolling the station or maintaining specific posts."
-/datum/alt_title/security_officer/cadet
+/datum/prototype/alt_title/security_officer/cadet
title = "Security Cadet"
-/datum/alt_title/security_officer/guard
+/datum/prototype/alt_title/security_officer/guard
title = "Security Guard"
/datum/outfit/job/station/security_officer
diff --git a/code/modules/jobs/job_types/station/security/warden.dm b/code/modules/jobs/job_types/station/security/warden.dm
index b372238301f..aaa4b61d508 100644
--- a/code/modules/jobs/job_types/station/security/warden.dm
+++ b/code/modules/jobs/job_types/station/security/warden.dm
@@ -1,4 +1,4 @@
-/datum/job/station/warden
+/datum/role/job/station/warden
id = JOB_ID_WARDEN
economy_payscale = ECONOMY_PAYSCALE_JOB_SENIOR
title = "Warden"
@@ -22,14 +22,14 @@
Armoury gear in a crisis, and retrieving it when the crisis has passed. In an emergency, the Warden may be called upon to direct the \
Security Department as a whole."
alt_titles = list(
- "Jailor" = /datum/alt_title/warden/jailor,
- "Dispatch Officer" = /datum/alt_title/warden/dispatch_officer
+ "Jailor" = /datum/prototype/alt_title/warden/jailor,
+ "Dispatch Officer" = /datum/prototype/alt_title/warden/dispatch_officer
)
-/datum/alt_title/warden/jailor
+/datum/prototype/alt_title/warden/jailor
title = "Jailor"
-/datum/alt_title/warden/dispatch_officer
+/datum/prototype/alt_title/warden/dispatch_officer
title = "Dispatch Officer"
/datum/outfit/job/station/warden
diff --git a/code/modules/jobs/job_types/station/service/bartender.dm b/code/modules/jobs/job_types/station/service/bartender.dm
index de6cb66f1bd..b00212a30e0 100644
--- a/code/modules/jobs/job_types/station/service/bartender.dm
+++ b/code/modules/jobs/job_types/station/service/bartender.dm
@@ -1,4 +1,4 @@
-/datum/job/station/bartender
+/datum/role/job/station/bartender
id = JOB_ID_BARTENDER
title = "Bartender"
flag = BARTENDER
@@ -16,18 +16,18 @@
outfit_type = /datum/outfit/job/station/bartender
desc = "A Bartender mixes drinks for the crew. They generally have permission to charge for drinks or deny service to unruly patrons."
alt_titles = list(
- "Barista" = /datum/alt_title/bartender/barista,
- "Barkeeper" = /datum/alt_title/bartender/barkeeper,
- "Barmaid" = /datum/alt_title/bartender/barmaid
+ "Barista" = /datum/prototype/alt_title/bartender/barista,
+ "Barkeeper" = /datum/prototype/alt_title/bartender/barkeeper,
+ "Barmaid" = /datum/prototype/alt_title/bartender/barmaid
)
-/datum/alt_title/bartender/barkeeper
+/datum/prototype/alt_title/bartender/barkeeper
title = "Barkeeper"
-/datum/alt_title/bartender/barmaid
+/datum/prototype/alt_title/bartender/barmaid
title = "Barmaid"
-/datum/alt_title/bartender/barista
+/datum/prototype/alt_title/bartender/barista
title = "Barista"
title_blurb = "A barista mans the Cafe, serving primarily non-alcoholic drinks to the crew. They generally have permission to charge for drinks \
or deny service to unruly patrons."
diff --git a/code/modules/jobs/job_types/station/service/botanist.dm b/code/modules/jobs/job_types/station/service/botanist.dm
index 1b135857eec..ff8626201c4 100644
--- a/code/modules/jobs/job_types/station/service/botanist.dm
+++ b/code/modules/jobs/job_types/station/service/botanist.dm
@@ -1,4 +1,4 @@
-/datum/job/station/hydro
+/datum/role/job/station/hydro
id = JOB_ID_BOTANIST
title = "Botanist"
flag = BOTANIST
@@ -16,9 +16,9 @@
outfit_type = /datum/outfit/job/station/botanist/gardener
desc = "A Botanist grows plants for the Chef and Bartender."
- alt_titles = list("Gardener" = /datum/alt_title/gardener)
+ alt_titles = list("Gardener" = /datum/prototype/alt_title/gardener)
-/datum/alt_title/gardener
+/datum/prototype/alt_title/gardener
title = "Gardener"
title_blurb = "A Gardener may be less professional than their counterparts, and are more likely to tend to the public gardens if they aren't needed elsewhere."
diff --git a/code/modules/jobs/job_types/station/service/chef.dm b/code/modules/jobs/job_types/station/service/chef.dm
index 807d122c000..c6d4e988f10 100644
--- a/code/modules/jobs/job_types/station/service/chef.dm
+++ b/code/modules/jobs/job_types/station/service/chef.dm
@@ -1,4 +1,4 @@
-/datum/job/station/chef
+/datum/role/job/station/chef
id = JOB_ID_CHEF
title = "Chef"
flag = CHEF
@@ -16,24 +16,24 @@
outfit_type = /datum/outfit/job/station/chef
desc = "A Chef cooks food for the crew. They generally have permission to charge for food or deny service to unruly diners."
alt_titles = list(
- "Cook" = /datum/alt_title/chef/cook,
- "Sous-chef" = /datum/alt_title/chef/souschef,
- "Kitchen Worker" = /datum/alt_title/chef/kitchen_worker,
- "Line Cook" = /datum/alt_title/chef/line
+ "Cook" = /datum/prototype/alt_title/chef/cook,
+ "Sous-chef" = /datum/prototype/alt_title/chef/souschef,
+ "Kitchen Worker" = /datum/prototype/alt_title/chef/kitchen_worker,
+ "Line Cook" = /datum/prototype/alt_title/chef/line
)
-/datum/alt_title/chef/souschef
+/datum/prototype/alt_title/chef/souschef
title = "Sous-chef"
-/datum/alt_title/chef/kitchen_worker
+/datum/prototype/alt_title/chef/kitchen_worker
title = "Kitchen Worker"
title_blurb = "A Kitchen Worker has the same duties, though they may be less experienced."
-/datum/alt_title/chef/line
+/datum/prototype/alt_title/chef/line
title = "Line Cook"
// Chef Alt Titles
-/datum/alt_title/chef/cook
+/datum/prototype/alt_title/chef/cook
title = "Cook"
title_blurb = "A Cook has the same duties, though they may be less experienced."
diff --git a/code/modules/jobs/job_types/station/service/janitor.dm b/code/modules/jobs/job_types/station/service/janitor.dm
index 2e26a13d320..bedf376502a 100644
--- a/code/modules/jobs/job_types/station/service/janitor.dm
+++ b/code/modules/jobs/job_types/station/service/janitor.dm
@@ -1,4 +1,4 @@
-/datum/job/station/janitor
+/datum/role/job/station/janitor
id = JOB_ID_JANITOR
title = "Janitor"
flag = JANITOR
@@ -16,22 +16,22 @@
outfit_type = /datum/outfit/job/station/janitor
desc = "A Janitor keeps the station clean, as long as it doesn't interfere with active crime scenes."
alt_titles = list(
- "Custodian" = /datum/alt_title/janitor/custodian,
- "Sanitation Technician" = /datum/alt_title/janitor/tech,
- "Viscera Cleaner" = /datum/alt_title/janitor/gorecleaner,
- "Maid" = /datum/alt_title/janitor/maid
+ "Custodian" = /datum/prototype/alt_title/janitor/custodian,
+ "Sanitation Technician" = /datum/prototype/alt_title/janitor/tech,
+ "Viscera Cleaner" = /datum/prototype/alt_title/janitor/gorecleaner,
+ "Maid" = /datum/prototype/alt_title/janitor/maid
)
-/datum/alt_title/janitor/custodian
+/datum/prototype/alt_title/janitor/custodian
title = "Custodian"
-/datum/alt_title/janitor/tech
+/datum/prototype/alt_title/janitor/tech
title = "Sanitation Technician"
-/datum/alt_title/janitor/gorecleaner
+/datum/prototype/alt_title/janitor/gorecleaner
title = "Viscera Cleaner"
-/datum/alt_title/janitor/maid
+/datum/prototype/alt_title/janitor/maid
title = "Maid"
title_outfit = /datum/outfit/job/station/janitor/maid
diff --git a/code/modules/jobs/job_types/station/silicon/ai.dm b/code/modules/jobs/job_types/station/silicon/ai.dm
index 9bcde1c8e2d..78bae1fe28d 100644
--- a/code/modules/jobs/job_types/station/silicon/ai.dm
+++ b/code/modules/jobs/job_types/station/silicon/ai.dm
@@ -1,4 +1,4 @@
-/datum/job/station/ai
+/datum/role/job/station/ai
id = JOB_ID_AI
title = "AI"
flag = AI
@@ -23,20 +23,20 @@
disallow_jobhop = TRUE
// AI procs
-/datum/job/station/ai/equip(var/mob/living/carbon/human/H)
+/datum/role/job/station/ai/equip(var/mob/living/carbon/human/H)
if(!H)
return 0
return 1
-/datum/job/station/ai/slots_remaining(latejoin)
+/datum/role/job/station/ai/slots_remaining(latejoin)
if(latejoin)
return GLOB.empty_playable_ai_cores.len
return ..()
-/datum/job/station/ai/is_position_available()
+/datum/role/job/station/ai/is_position_available()
return (GLOB.empty_playable_ai_cores.len != 0)
-/datum/job/station/ai/equip_preview(mob/living/carbon/human/H)
+/datum/role/job/station/ai/equip_preview(mob/living/carbon/human/H)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/straight_jacket(H), SLOT_ID_SUIT)
H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), SLOT_ID_HEAD)
return 1
diff --git a/code/modules/jobs/job_types/station/silicon/cyborg.dm b/code/modules/jobs/job_types/station/silicon/cyborg.dm
index 0c51504d071..5e6efb3554f 100644
--- a/code/modules/jobs/job_types/station/silicon/cyborg.dm
+++ b/code/modules/jobs/job_types/station/silicon/cyborg.dm
@@ -1,4 +1,4 @@
-/datum/job/station/cyborg
+/datum/role/job/station/cyborg
id = JOB_ID_CYBORG
title = "Cyborg"
flag = CYBORG
@@ -18,27 +18,27 @@
desc = "A Cyborg is a mobile station synthetic, piloted by a cybernetically preserved brain. It is considered a person, but is still required \
to follow its Laws."
alt_titles = list(
- "Robot" = /datum/alt_title/robot,
- "Drone" = /datum/alt_title/drone
+ "Robot" = /datum/prototype/alt_title/robot,
+ "Drone" = /datum/prototype/alt_title/drone
)
// Cyborg Alt Titles
-/datum/alt_title/robot
+/datum/prototype/alt_title/robot
title = "Robot"
title_blurb = "A Robot is a mobile station synthetic, piloted by an advanced piece of technology called a Positronic Brain. It is considered a person, \
legally, but is required to follow its Laws."
-/datum/alt_title/drone
+/datum/prototype/alt_title/drone
title = "Drone"
title_blurb = "A Drone is a mobile station synthetic, piloted by a simple computer-based AI. As such, it is not a person, but rather an expensive and \
and important piece of station property, and is expected to follow its Laws."
// Cyborg procs
-/datum/job/station/cyborg/equip(var/mob/living/carbon/human/H)
+/datum/role/job/station/cyborg/equip(var/mob/living/carbon/human/H)
if(!H) return 0
return 1
-/datum/job/station/cyborg/equip_preview(mob/living/carbon/human/H)
+/datum/role/job/station/cyborg/equip_preview(mob/living/carbon/human/H)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/cardborg(H), SLOT_ID_SUIT)
H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), SLOT_ID_HEAD)
return 1
diff --git a/code/modules/jobs/job_types/station/supply/cargo_technician.dm b/code/modules/jobs/job_types/station/supply/cargo_technician.dm
index fd63d8b18a3..69b2ebdf5ec 100644
--- a/code/modules/jobs/job_types/station/supply/cargo_technician.dm
+++ b/code/modules/jobs/job_types/station/supply/cargo_technician.dm
@@ -1,4 +1,4 @@
-/datum/job/station/cargo_tech
+/datum/role/job/station/cargo_tech
id = JOB_ID_CARGO_TECHNICIAN
title = "Cargo Technician"
flag = CARGOTECH
@@ -15,9 +15,9 @@
outfit_type = /datum/outfit/job/station/cargo_technician
desc = "A Cargo Technician fills and delivers cargo orders. They are encouraged to return delivered crates to the Cargo Shuttle, \
because Central Command gives a partial refund."
- alt_titles = list("Logistics Specialist" = /datum/alt_title/logi_spec)
+ alt_titles = list("Logistics Specialist" = /datum/prototype/alt_title/logi_spec)
-/datum/alt_title/logi_spec
+/datum/prototype/alt_title/logi_spec
title = "Logistics Specialist"
/datum/outfit/job/station/cargo_technician
diff --git a/code/modules/jobs/job_types/station/supply/quartermaster.dm b/code/modules/jobs/job_types/station/supply/quartermaster.dm
index 2d7ccdd79a7..5cb858c22ff 100644
--- a/code/modules/jobs/job_types/station/supply/quartermaster.dm
+++ b/code/modules/jobs/job_types/station/supply/quartermaster.dm
@@ -1,4 +1,4 @@
-/datum/job/station/quartermaster
+/datum/role/job/station/quartermaster
id = JOB_ID_QUARTERMASTER
title = "Quartermaster"
economy_payscale = ECONOMY_PAYSCALE_JOB_SENIOR
@@ -20,9 +20,9 @@
outfit_type = /datum/outfit/job/station/quartermaster
desc = "The Quartermaster manages the Supply department, checking cargo orders and ensuring supplies get to where they are needed."
- alt_titles = list("Supply Chief" = /datum/alt_title/supply_chief)
+ alt_titles = list("Supply Chief" = /datum/prototype/alt_title/supply_chief)
-/datum/alt_title/supply_chief
+/datum/prototype/alt_title/supply_chief
title = "Supply Chief"
/datum/outfit/job/station/quartermaster
diff --git a/code/modules/jobs/job_types/station/supply/shaft_miner.dm b/code/modules/jobs/job_types/station/supply/shaft_miner.dm
index c7d7c5f501a..d49b84e9606 100644
--- a/code/modules/jobs/job_types/station/supply/shaft_miner.dm
+++ b/code/modules/jobs/job_types/station/supply/shaft_miner.dm
@@ -1,4 +1,4 @@
-/datum/job/station/mining
+/datum/role/job/station/mining
id = JOB_ID_SHAFT_MINER
title = "Shaft Miner"
flag = MINER
@@ -17,15 +17,18 @@
outfit_type = /datum/outfit/job/station/shaft_miner
desc = "A Shaft Miner mines and processes minerals to be delivered to departments that need them."
alt_titles = list(
- "Drill Technician" = /datum/alt_title/drill_tech,
- "Belt Miner" = /datum/alt_title/miner/belt
+ "Drill Technician" = /datum/prototype/alt_title/miner/drill_tech,
+ "Belt Miner" = /datum/prototype/alt_title/miner/belt
)
-/datum/alt_title/drill_tech
+/datum/prototype/alt_title/miner
+ abstract_type = /datum/prototype/alt_title/miner
+
+/datum/prototype/alt_title/miner/drill_tech
title = "Drill Technician"
title_blurb = "A Drill Technician specializes in operating and maintaining the machinery needed to extract ore from veins deep below the surface."
-/datum/alt_title/miner/belt
+/datum/prototype/alt_title/miner/belt
title = "Belt Miner"
/datum/outfit/job/station/shaft_miner
diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm
index c658f29185a..496ef15ba34 100644
--- a/code/modules/jobs/jobs.dm
+++ b/code/modules/jobs/jobs.dm
@@ -61,10 +61,10 @@ var/const/TRADER =(1<<15)
/proc/get_job_datums()
var/list/occupations = list()
- var/list/all_jobs = typesof(/datum/job)
+ var/list/all_jobs = typesof(/datum/role/job)
for(var/A in all_jobs)
- var/datum/job/job = new A()
+ var/datum/role/job/job = new A()
if(!job) continue
occupations += job
@@ -74,7 +74,7 @@ var/const/TRADER =(1<<15)
var/list/jobs = get_job_datums()
var/list/titles = list()
- for(var/datum/job/J in jobs)
+ for(var/datum/role/job/J in jobs)
if(J.title == job)
titles = J.alt_titles
diff --git a/code/modules/jobs/outfit.dm b/code/modules/jobs/outfit.dm
index 178891eb3b7..d01f020292e 100644
--- a/code/modules/jobs/outfit.dm
+++ b/code/modules/jobs/outfit.dm
@@ -17,7 +17,7 @@
var/obj/item/card/id/C = ..()
if(!C)
return
- var/datum/job/J = SSjob.get_job(rank)
+ var/datum/role/job/J = SSjob.get_job(rank)
if(J)
C.access = J.get_access()
if(H.mind)
diff --git a/code/modules/lore_hardcoded/_hardcoded.dm b/code/modules/lore_hardcoded/_hardcoded.dm
index e94d76c1acb..03cf0788052 100644
--- a/code/modules/lore_hardcoded/_hardcoded.dm
+++ b/code/modules/lore_hardcoded/_hardcoded.dm
@@ -9,6 +9,8 @@
var/name = "Unknown"
/// id - **must be unique on subtypes
var/id
+ /// category
+ var/category = "Misc"
/// description/what the player sees
var/desc = "What is this?"
/// subspecies are counted as the master species
diff --git a/code/modules/lore_hardcoded/faction.dm b/code/modules/lore_hardcoded/faction.dm
index f1d99946457..a1ce26d686f 100644
--- a/code/modules/lore_hardcoded/faction.dm
+++ b/code/modules/lore_hardcoded/faction.dm
@@ -1,6 +1,6 @@
/datum/lore/character_background/faction
abstract_type = /datum/lore/character_background/faction
- /// station job types you can play as under this - **typepaths** e.g. /datum/job/station/security_officer, etc
+ /// station job types you can play as under this - **typepaths** e.g. /datum/role/job/station/security_officer, etc
/// if null, you can play as everything
var/list/job_whitelist = list()
/// job blacklist
diff --git a/code/modules/lore_hardcoded/origin.dm b/code/modules/lore_hardcoded/origin.dm
index 5b46658968f..7840da9eb12 100644
--- a/code/modules/lore_hardcoded/origin.dm
+++ b/code/modules/lore_hardcoded/origin.dm
@@ -1,7 +1,5 @@
/datum/lore/character_background/origin
abstract_type = /datum/lore/character_background/origin
- /// category
- var/category = "Misc"
/datum/lore/character_background/origin/check_character_species(datum/character_species/S)
if(S.species_fluff_flags & SPECIES_FLUFF_PICKY_ORIGIN)
diff --git a/code/modules/maps/overmap/space/talon/talon_jobs.dm b/code/modules/maps/overmap/space/talon/talon_jobs.dm
index 827c17e56aa..187cc62a854 100644
--- a/code/modules/maps/overmap/space/talon/talon_jobs.dm
+++ b/code/modules/maps/overmap/space/talon/talon_jobs.dm
@@ -18,7 +18,7 @@
assignable = FALSE
visible = FALSE
-/datum/job/talon_captain
+/datum/role/job/talon_captain
title = "Talon Captain"
flag = TALCAP
department_flag = TALON
@@ -37,12 +37,12 @@
pto_type = null
access = list(access_talon)
minimal_access = list(access_talon)
- alt_titles = list("Talon Commander" = /datum/alt_title/talon_commander)
+ alt_titles = list("Talon Commander" = /datum/prototype/alt_title/talon_commander)
-/datum/alt_title/talon_commander
+/datum/prototype/alt_title/talon_commander
title = "Talon Commander"
-/datum/job/talon_doctor
+/datum/role/job/talon_doctor
title = "Talon Doctor"
flag = TALDOC
department_flag = TALON
@@ -60,13 +60,13 @@
pto_type = null
access = list(access_talon)
minimal_access = list(access_talon)
- alt_titles = list("Talon Medic" = /datum/alt_title/talon_medic)
+ alt_titles = list("Talon Medic" = /datum/prototype/alt_title/talon_medic)
-/datum/alt_title/talon_medic
+/datum/prototype/alt_title/talon_medic
title = "Talon Medic"
-/datum/job/talon_engineer
+/datum/role/job/talon_engineer
title = "Talon Engineer"
flag = TALENG
department_flag = TALON
@@ -84,13 +84,13 @@
pto_type = null
access = list(access_talon)
minimal_access = list(access_talon)
- alt_titles = list("Talon Technician" = /datum/alt_title/talon_tech)
+ alt_titles = list("Talon Technician" = /datum/prototype/alt_title/talon_tech)
-/datum/alt_title/talon_tech
+/datum/prototype/alt_title/talon_tech
title = "Talon Technician"
-/datum/job/talon_pilot
+/datum/role/job/talon_pilot
title = "Talon Pilot"
flag = TALPIL
department_flag = TALON
@@ -108,13 +108,13 @@
pto_type = null
access = list(access_talon)
minimal_access = list(access_talon)
- alt_titles = list("Talon Helmsman" = /datum/alt_title/talon_helmsman)
+ alt_titles = list("Talon Helmsman" = /datum/prototype/alt_title/talon_helmsman)
-/datum/alt_title/talon_helmsman
+/datum/prototype/alt_title/talon_helmsman
title = "Talon Helmsman"
-/datum/job/talon_guard
+/datum/role/job/talon_guard
title = "Talon Guard"
flag = TALSEC
department_flag = TALON
@@ -132,9 +132,9 @@
pto_type = null
access = list(access_talon)
minimal_access = list(access_talon)
- alt_titles = list("Talon Security" = /datum/alt_title/talon_security)
+ alt_titles = list("Talon Security" = /datum/prototype/alt_title/talon_security)
-/datum/alt_title/talon_security
+/datum/prototype/alt_title/talon_security
title = "Talon Security"
diff --git a/code/modules/maps/overmap/space/trade_station/trade_station_jobs.dm b/code/modules/maps/overmap/space/trade_station/trade_station_jobs.dm
index 61e6ff970c8..c9ab597f8a9 100644
--- a/code/modules/maps/overmap/space/trade_station/trade_station_jobs.dm
+++ b/code/modules/maps/overmap/space/trade_station/trade_station_jobs.dm
@@ -7,7 +7,7 @@
assignable = FALSE
visible = FALSE
-/datum/job/trader
+/datum/role/job/trader
title = "Trader"
flag = TRADER
id = JOB_ID_TRADER
@@ -35,15 +35,15 @@
access = list(access_trader)
minimal_access = list(access_trader)
alt_titles = list(
- "Trade Manager" = /datum/alt_title/trade_manager,
- "Merchant" = /datum/alt_title/merchant
+ "Trade Manager" = /datum/prototype/alt_title/trade_manager,
+ "Merchant" = /datum/prototype/alt_title/merchant
)
-/datum/alt_title/trade_manager
+/datum/prototype/alt_title/trade_manager
title = "Trade Manager"
// title_blurb = "A Drill Technician specializes in operating and maintaining the machinery needed to extract ore from veins deep below the surface."
-/datum/alt_title/merchant
+/datum/prototype/alt_title/merchant
title = "Merchant"
/datum/outfit/trade
diff --git a/code/modules/metric/department.dm b/code/modules/metric/department.dm
index 4792434f772..15c1420ed40 100644
--- a/code/modules/metric/department.dm
+++ b/code/modules/metric/department.dm
@@ -34,18 +34,18 @@
// Like before, records are the most reliable way.
var/datum/data/record/R = find_general_record("name", M.real_name)
if(R) // They got a record, now find the job datum.
- var/datum/job/J = SSjob.get_job(R.fields["real_rank"])
+ var/datum/role/job/J = SSjob.get_job(R.fields["real_rank"])
if(istype(J))
return J
// Try the mind.
if(M.mind)
- var/datum/job/J = SSjob.get_job(M.mind.assigned_role)
+ var/datum/role/job/J = SSjob.get_job(M.mind.assigned_role)
if(istype(J))
return J
// Last ditch effort, check for job assigned to the mob itself.
- var/datum/job/J = SSjob.get_job(M.job)
+ var/datum/role/job/J = SSjob.get_job(M.job)
if(istype(J))
return J
@@ -54,7 +54,7 @@
// Feed this proc the name of a job, and it will try to figure out what department they are apart of.
// Improved with the addition of SSjob, which has departments be an actual thing and not a virtual concept.
/datum/metric/proc/role_name_to_department(var/role_name)
- var/datum/job/J = SSjob.get_job(role_name)
+ var/datum/role/job/J = SSjob.get_job(role_name)
if(istype(J))
if(LAZYLEN(J.departments))
return J.departments
@@ -84,13 +84,13 @@
/datum/metric/proc/get_people_with_job(job_type, cutoff = 75)
. = list()
// First, get the name.
- var/datum/job/J = SSjob.job_by_type(job_type)
+ var/datum/role/job/J = SSjob.job_by_type(job_type)
if(!istype(J))
return
// Now find people with the job name.
for(var/M in GLOB.player_list)
- var/datum/job/their_job = guess_job(M)
+ var/datum/role/job/their_job = guess_job(M)
if(!istype(their_job)) // No job was guessed.
continue
if(their_job.title != J.title) // Jobs don't match.
@@ -109,8 +109,8 @@
. = list()
var/list/people_with_jobs = get_people_with_job(job_type, cutoff)
- var/datum/job/J = SSjob.job_by_type(job_type)
- var/datum/alt_title/A = new alt_title_type()
+ var/datum/role/job/J = SSjob.job_by_type(job_type)
+ var/datum/prototype/alt_title/A = new alt_title_type()
for(var/M in people_with_jobs)
if(J.has_alt_title(M, null, A.title))
diff --git a/code/modules/mob/characteristics/helpers.dm b/code/modules/mob/characteristics/helpers.dm
new file mode 100644
index 00000000000..e5f71336afa
--- /dev/null
+++ b/code/modules/mob/characteristics/helpers.dm
@@ -0,0 +1,11 @@
+/**
+ * checks if characteristics system is enabled
+ */
+/proc/characteristics_enabled()
+ return CONFIG_GET(flag/characteristics_enabled)
+
+/**
+ * checks if characteristics system is active
+ */
+/proc/characteristics_active()
+ return CONFIG_GET(flag/characteristics_active)
diff --git a/code/modules/mob/characteristics/holder.dm b/code/modules/mob/characteristics/holder.dm
new file mode 100644
index 00000000000..9ce0b73f00a
--- /dev/null
+++ b/code/modules/mob/characteristics/holder.dm
@@ -0,0 +1,143 @@
+/**
+ * holds characteristics data
+ *
+ * can be just used as a holder datum but can also be used as a 1:1 with a mind
+ * downsides: can only be associated with one mind at a time, for now.
+ * if this belongs to a mind the mind has free reign to qdel it. you have been warned.
+ */
+/datum/characteristics_holder
+ //! ownership
+ /// current mind that holds us; **CAN BE NULL**
+ var/datum/mind/mind
+
+ //! characteristics
+ /// skill ids associated to values
+ var/list/skills
+ /// stat ids associated to values
+ var/list/stats
+ /// talent ids associated to arbitrary metadata, usually just 1 for assoc lookup; said metadata should eval to true in logic.
+ var/list/talents
+ // todo: modifiers
+
+/datum/characteristics_holder/Destroy()
+ if(mind)
+ disassociate_from_mind(mind)
+ return ..()
+
+/datum/characteristics_holder/proc/associate_with_mind(datum/mind/M)
+ if(M.current)
+ associate_with_mob(M.current)
+ if(M.characteristics)
+ stack_trace("mind already had characteristics")
+ M.characteristics = src
+ for(var/id in talents)
+ var/datum/characteristic_talent/talent = resolve_characteristics_talent(id)
+ talent.gain(M, talents[id])
+
+/datum/characteristics_holder/proc/disassociate_from_mind(datum/mind/M)
+ if(M.current)
+ disassociate_from_mob(M.current)
+ if(M.characteristics != src)
+ stack_trace("mind characteristics was not self")
+ for(var/id in talents)
+ var/datum/characteristic_talent/talent = resolve_characteristics_talent(id)
+ talent.lose(M, talents[id])
+ M.characteristics = null
+
+/datum/characteristics_holder/proc/associate_with_mob(mob/M)
+ for(var/id in talents)
+ var/datum/characteristic_talent/talent = resolve_characteristics_talent(id)
+ talent.attach(M, talents[id])
+
+/datum/characteristics_holder/proc/disassociate_from_mob(mob/M)
+ for(var/id in talents)
+ var/datum/characteristic_talent/talent = resolve_characteristics_talent(id)
+ talent.detach(M, talents[id])
+
+/datum/characteristics_holder/proc/set_stat(datum/characteristic_stat/id_or_typepath, val)
+ LAZYINITLIST(stats)
+ stats[ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath] = val
+
+/datum/characteristics_holder/proc/set_skill(datum/characteristic_skill/id_or_typepath, val)
+ LAZYINITLIST(skills)
+ skills[ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath] = val
+
+/datum/characteristics_holder/proc/get_stat(datum/characteristic_stat/id_or_typepath)
+ . = stats?[ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath]
+
+/datum/characteristics_holder/proc/get_skill(datum/characteristic_skill/id_or_typepath)
+ . = skills?[ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath] || CHARACTER_SKILL_UNTRAINED
+
+/datum/characteristics_holder/proc/add_talent(datum/characteristic_talent/id_or_typepath, ...)
+ var/id = ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath
+ if(talents?[id])
+ // do NOT allow overwrite!
+ return FALSE
+ var/datum/characteristic_talent/talent = resolve_characteristics_talent(id)
+ talents[id] = talent.metadata(arglist(args.Copy(2)))
+ if(mind)
+ if(mind.current)
+ talent.attach(mind.current, talents[id])
+ talent.gain(mind, talents[id])
+
+/datum/characteristics_holder/proc/remove_talent(datum/characteristic_talent/id_or_typepath)
+ var/id = ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath
+ if(!talents?[id])
+ return FALSE
+ var/datum/characteristic_talent/talent = resolve_characteristics_talent(id)
+ if(mind)
+ if(mind.current)
+ talent.detach(mind.current, talents[id])
+ talent.lose(mind, talents[id])
+ talents -= id
+ return TRUE
+
+/datum/characteristics_holder/proc/has_talent(datum/characteristic_talent/id_or_typepath)
+ return !!talents[ispath(id_or_typepath)? initial(id_or_typepath.id) : id_or_typepath]
+
+/**
+ * apply a preset to us
+ *
+ * @params
+ * - typepath_or_preset - typepath or preset datum
+ * - overwrite - should we replace everything in us or instead raise / append if needed?
+ */
+/datum/characteristics_holder/proc/apply_preset(datum/characteristic_preset/typepath_or_preset, overwrite = FALSE)
+ if(ispath(typepath_or_preset))
+ typepath_or_preset = resolve_characteristics_preset(typepath_or_preset)
+ if(typepath_or_preset.skills)
+ LAZYINITLIST(skills)
+ if(overwrite)
+ skills = typepath_or_preset.skills.Copy()
+ else
+ for(var/id in typepath_or_preset.skills)
+ skills[id] = max(skills[id], typepath_or_preset.skills[id])
+ if(typepath_or_preset.stats)
+ LAZYINITLIST(stats)
+ if(overwrite)
+ stats = typepath_or_preset.stats.Copy()
+ else
+ for(var/id in typepath_or_preset.stats)
+ var/datum/characteristic_stat/stat = resolve_characteristics_stat(id)
+ stats[id] = stat.greater_value(stats[id], typepath_or_preset.stats[id])
+ if(typepath_or_preset.talents)
+ LAZYINITLIST(talents)
+ for(var/id in typepath_or_preset.talents)
+ if(has_talent(id))
+ continue
+ if(typepath_or_preset.talents[id])
+ add_talent(arglist(list(id) + typepath_or_preset.talents[id]))
+ else
+ add_talent(id)
+ return TRUE
+
+/**
+ * clones
+ */
+/datum/characteristics_holder/proc/clone()
+ RETURN_TYPE(/datum/characteristics_holder)
+ var/datum/characteristics_holder/cloning = new
+ cloning.skills = skills.Copy()
+ cloning.stats = stats.Copy()
+ cloning.talents = talents.Copy()
+ return cloning
diff --git a/code/modules/mob/characteristics/mob.dm b/code/modules/mob/characteristics/mob.dm
new file mode 100644
index 00000000000..358a5f9969d
--- /dev/null
+++ b/code/modules/mob/characteristics/mob.dm
@@ -0,0 +1,98 @@
+//? file contains mob helpers and whatnot
+
+//! direct lookup
+/**
+ * checks if we have a characteristic talent
+ *
+ * @params
+ * - typepath_or_id - typepath or id; prefer typepath during compile time
+ *
+ * @return TRUE/FALSE
+ */
+/mob/proc/has_characteristic_talent(datum/characteristic_talent/typepath_or_id)
+ if(!characteristics_active())
+ return FALSE
+ return mind?.characteristics?.has_talent(typepath_or_id)
+
+/**
+ * checks the value of one of our characteristic stats
+ *
+ * @params
+ * - typepath_or_id - typepath or id; prefer typepath during compile time
+ *
+ * @return raw value
+ */
+/mob/proc/get_characteristic_stat(datum/characteristic_stat/typepath_or_id)
+ if(!characteristics_active())
+ typepath_or_id = resolve_characteristics_stat(typepath_or_id)
+ return typepath_or_id.baseline_value
+ return mind?.characteristics?.get_stat(typepath_or_id)
+
+/**
+ * gets the skill value enum of one of our characteristic skills
+ *
+ * @params
+ * - typepath_or_id - typepath or id; prefer typepath during compile time
+ *
+ * @return skill level
+ */
+/mob/proc/get_characteristic_skill(datum/characteristic_skill/typepath_or_id)
+ if(!characteristics_active())
+ typepath_or_id = resolve_characteristics_stat(typepath_or_id)
+ return typepath_or_id.baseline_value
+ return mind?.characteristics?.get_skill(typepath_or_id)
+
+//! checks
+
+//? no stat check ; stats are raw values
+
+//? no talent check ; talents are boolean for checks
+
+//? skill checks
+
+/**
+ * scales a number with requested skill scaling
+ *
+ * @params
+ * * typepath_or_id - typepath or id of skill
+ * * level - what level is wanted
+ * * constant - constant for scaling
+ * * method - skill scaling enum, see __DEFINES/mobs/characteristics.dm
+ */
+/mob/proc/characteristic_skill_scaling(datum/characteristic_skill/typepath_or_id, level, constant, method)
+ var/diff = level - get_characteristic_skill(typepath_or_id)
+ switch(method)
+ if(SKILL_SCALING_EXPONENTIAL_HARD)
+ return constant * (2 ** diff)
+ if(SKILL_SCALING_EXPONENTIAL_SOFT)
+ return constant * (1.5 ** diff)
+ if(SKILL_SCALING_LINEAR)
+ return diff * constant
+
+/**
+ * checks if we have a skill at a required level.
+ *
+ * @params
+ * * typepath_or_id - typepath or id of skill
+ * * level - what level is needed
+ */
+/mob/proc/characteristic_skill_check(datum/characteristic_skill/typepath_or_id, level)
+ return level <= get_characteristic_skill(typepath_or_id)
+
+/**
+ * gets skill difference
+ *
+ * @params
+ * * typepath_or_id - typepath or id of skill
+ * * level - what level is needed
+ */
+/mob/proc/characteristic_skill_difference(datum/characteristic_skill/typepath_or_id, level)
+ return level - get_characteristic_skill(typepath_or_id)
+
+/**
+ * get or create characteristics holder
+ */
+/mob/proc/characteristics_holder()
+ if(!mind)
+ mind_initialize()
+ return mind.characteristics_holder()
diff --git a/code/modules/mob/characteristics/modifier.dm b/code/modules/mob/characteristics/modifier.dm
new file mode 100644
index 00000000000..10804784a27
--- /dev/null
+++ b/code/modules/mob/characteristics/modifier.dm
@@ -0,0 +1,8 @@
+/**
+ * modifiers applied to characteristics holders temporarily
+ */
+/datum/characteristic_modifier
+
+
+
+// todo: this file isn't done yet.
diff --git a/code/modules/mob/characteristics/presets.dm b/code/modules/mob/characteristics/presets.dm
new file mode 100644
index 00000000000..fa5aa99add9
--- /dev/null
+++ b/code/modules/mob/characteristics/presets.dm
@@ -0,0 +1,55 @@
+GLOBAL_LIST_EMPTY(characteristics_presets)
+
+/**
+ * gets a skill-holder preset
+ *
+ * use typepaths whenever possible for compile time!
+ */
+/proc/resolve_characteristics_preset(datum/characteristic_preset/typepath_or_instance)
+ RETURN_TYPE(/datum/characteristic_preset)
+ if(istype(typepath_or_instance))
+ return typepath_or_instance
+ . = GLOB.characteristics_presets[typepath_or_instance]
+ if(!.)
+ return (GLOB.characteristics_presets[typepath_or_instance] = (new typepath_or_instance))
+
+/**
+ * holds presets for skills/whatont
+ */
+/datum/characteristic_preset
+ /// name for debugging ; optional
+ var/name
+
+ /// skill tpyepaths or ids associated to values
+ var/list/skills
+ /// stat typepaths or ids associated to values
+ var/list/stats
+ /// talent typepaths or ids associated to lists (or null) of what to pass in for arglist in talent add.
+ var/list/talents
+
+/datum/characteristic_preset/New(list/skills = list(), list/stats = list(), list/talents = list())
+ src.skills = skills.Copy()
+ src.stats = stats.Copy()
+ src.talents = talents.Copy()
+ transform()
+
+/**
+ * flatten everything into ids
+ */
+/datum/characteristic_preset/proc/transform()
+ var/datum/characteristic_skill/skillpath_or_id
+ var/datum/characteristic_stat/statpath_or_id
+ var/datum/characteristic_talent/talentpath_or_id
+ for(var/i in 1 to length(skills))
+ skillpath_or_id = skills[i]
+ if(ispath(skillpath_or_id))
+ skills[i] = initial(skillpath_or_id.id)
+ for(var/i in 1 to length(stats))
+ statpath_or_id = stats[i]
+ if(ispath(statpath_or_id))
+ stats[i] = initial(statpath_or_id.id)
+ for(var/i in 1 to length(talents))
+ talentpath_or_id = talents[i]
+ if(ispath(talentpath_or_id))
+ talents[i] = initial(talentpath_or_id.id)
+
diff --git a/code/modules/mob/characteristics/readme.md b/code/modules/mob/characteristics/readme.md
new file mode 100644
index 00000000000..f626f28c33d
--- /dev/null
+++ b/code/modules/mob/characteristics/readme.md
@@ -0,0 +1,45 @@
+# Characteristics
+
+A system to do character stats.
+
+This page is heavily WIP.
+
+## Config
+
+CHARACTERISTICS_ENABLED - defaults to true; without this, preferences won't show these things at all.
+CHARACTERISTICS_ACTIVE - defaults to false; nothing happens without this, everyone gets baseline average.
+
+## Specializations
+
+Special modifiers that increase adaptation towards one archetype
+Should be very broad and encourage character development towards a specific field
+
+Heavily WIP
+
+## Skills
+
+Bay-like enum'd numeric stats with levels from 0 to 6 (basic to professional)
+
+### Recommended Use Cases
+
+WIP
+
+## Stats
+
+Heavily numeric stats that are really low level.
+It's not recommended to use these for "abstract" concepts to prevent minmaxing.
+It's not recommended to overuse these at all to prevent minmaxing.
+
+### Recommended Use Cases
+
+WIP
+
+## Talents
+
+Boolean abilities that potentially have special code handling.
+Allows for tracking mobs, almost like a /datum/element.
+Think Barotrauma talents.
+
+### Recommended Use Cases
+
+WIP
diff --git a/code/modules/mob/characteristics/skill.dm b/code/modules/mob/characteristics/skill.dm
new file mode 100644
index 00000000000..08e2e453b06
--- /dev/null
+++ b/code/modules/mob/characteristics/skill.dm
@@ -0,0 +1,102 @@
+GLOBAL_LIST_INIT(characteristics_skills, _create_characteristics_skills())
+
+/proc/_create_characteristics_skills()
+ . = list()
+ for(var/datum/characteristic_skill/skill in subtypesof(/datum/characteristic_skill))
+ if(is_abstract(skill))
+ continue
+ . = new skill
+ if(isnull(skill.id))
+ stack_trace("null id on [skill.type]")
+ continue
+ if(.[skill.id])
+ stack_trace("collision on id [skill.id] between types [skill.type] and [.[skill.id]:type]")
+ continue
+ .[skill.id] = skill
+
+/**
+ * gets a skill datum
+ *
+ * use typepaths whenever possible for compile time!
+ */
+/proc/resolve_characteristics_skill(datum/characteristic_skill/typepath_or_id)
+ RETURN_TYPE(/datum/characteristic_skill)
+ return GLOB.characteristics_skills[ispath(typepath_or_id)? initial(typepath_or_id.id) : typepath_or_id]
+
+/**
+ * skills - more enum-like numerics/whatnot than boolean-like talents
+ * are held in skill holder
+ *
+ * skills are untrained when unset, and baseline when characteristics are disabled
+ */
+/datum/characteristic_skill
+ abstract_type = /datum/characteristic_skill
+ //? basics
+ /// unique id
+ var/id
+ /// name
+ var/name = "ERROR"
+ /// desc
+ var/desc = "An unknown skill. Someone needs to set this!"
+ /// category - just strings for now, don't need defines yet
+ var/category = "Unsorted"
+
+ //? values
+ /// what to return if characteristics are disabled
+ var/baseline_value = CHARACTER_SKILL_ENUM_MIN
+ /// max skill value
+ var/max_value = CHARACTER_SKILL_ENUM_MAX
+
+ //? costs - these are additive!
+ var/cost_novice = 0
+ var/cost_trained = 0
+ var/cost_experienced = 0
+ var/cost_professional = 0
+
+ var/tmp/total_cost_novice
+ var/tmp/total_cost_trained
+ var/tmp/total_cost_experienced
+ var/tmp/total_cost_professional
+
+ //? descriptions
+ var/desc_untrained = "ERR: NO UNTRAINED DESC"
+ var/desc_novice = "ERR: NO NOVICE DESC"
+ var/desc_trained = "ERR: NO TRAINED DESC"
+ var/desc_experienced = "ERR: NO EXPERIENCED DESC"
+ var/desc_professional = "ERR: NO PROFESSIONAL DESC"
+
+/datum/characteristic_skill/New()
+ compute_caches()
+
+/datum/characteristic_skill/proc/total_cost(level)
+ switch(level)
+ if(CHARACTER_SKILL_UNTRAINED)
+ return 0
+ if(CHARACTER_SKILL_NOVICE)
+ . = total_cost_novice
+ if(CHARACTER_SKILL_TRAINED)
+ . = total_cost_trained
+ if(CHARACTER_SKILL_EXPERIENCED)
+ . = total_cost_experienced
+ if(CHARACTER_SKILL_PROFESSIONAL)
+ . = total_cost_professional
+
+/datum/characteristic_skill/proc/compute_caches()
+ var/total = 0
+ total_cost_novice = round(total, 1)
+ total_cost_trained = round(total, 1)
+ total_cost_experienced = round(total, 1)
+ total_cost_professional = round(total, 1)
+
+/datum/characteristic_skill/proc/level_description(level)
+ switch(level)
+ if(CHARACTER_SKILL_UNTRAINED)
+ return desc_untrained
+ if(CHARACTER_SKILL_NOVICE)
+ return desc_novice
+ if(CHARACTER_SKILL_TRAINED)
+ return desc_trained
+ if(CHARACTER_SKILL_EXPERIENCED)
+ return desc_experienced
+ if(CHARACTER_SKILL_PROFESSIONAL)
+ return desc_professional
diff --git a/code/modules/mob/characteristics/skills/engineering.dm b/code/modules/mob/characteristics/skills/engineering.dm
new file mode 100644
index 00000000000..0af3d807008
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/engineering.dm
@@ -0,0 +1,79 @@
+/datum/characteristic_skill/engineering
+ abstract_type = /datum/characteristic_skill/engineering
+ category = "Engineering"
+
+/**
+ * Electrical Engineering: Wiring, power, etc
+ *
+ * Implementation status: not started
+ */
+/datum/characteristic_skill/engineering/electrical
+ id = "electrical"
+ name = "Electrical Engineering"
+ desc = "How experienced you are with powe, wiring, hacking, etc."
+ desc_untrained = "What even are wires? You should not be caught dead without insulated gloves if you are messing with a panel. You probably will be caught dead if you do it anyways. Wiring interfaces can become shuffled at random."
+ desc_novice = "You have some practice with wiring and electricity. Panels are no longer randomized."
+ desc_trained = "You have general training in electrical maintenance. You can now perform some actions on hacking interfaces without testing for wires, at a random chance depending on the object in question."
+ desc_experienced = "You do a lot of work with electronics. Departmental wire sets now always order the same way for you. More actions can be used without wire pulsing, and some wires reveal their functions entirely."
+ desc_professional = "You are a highly skilled electrician. Wires always render in the same order for you, and you can simply manipulate most lower-end devices without hacking or access if you can get the cover off."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MODERATE
+ cost_experienced = SKILLCOST_INCREMENT_MODERATE
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Construction: Building, Teardowns, etc
+ *
+ * Implementation status: not started
+ */
+/datum/characteristic_skill/engineering/construction
+ id = "construction"
+ name = "Construction"
+ desc = "How good you are at building or breaking things down."
+ desc_untrained = "You need an instruction manual to put together even a table. All construction are slightly slower for you."
+ desc_novice = "You've had some practice building things. Construction is now faster."
+ desc_trained = "You're a handyman of sorts. Construction and deconstruction are now faster. Things will prompt you with what can be used on them if applicable."
+ desc_experienced = "You're a skilled construction engineer. Automatic construction/deconstruction at a boosted speed is now available for you. Lathes are slightly more efficient for you. Construction is more efficient for you."
+ desc_professional = "You're a master builder or ship architect. RCDs are more efficient for you. All speeds and lathe handling boosted. You can build more for less."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MODERATE
+ cost_experienced = SKILLCOST_INCREMENT_MODERATE
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Atmospherics
+ *
+ * Implementation status: not started
+ */
+/datum/characteristic_skill/engineering/atmospherics
+ id = "atmospherics"
+ name = "Atmospherics"
+ desc = "How experienced you are with gas mechanics, atmospherics machinery, etc."
+ desc_untrained = "What's an air network? All you recall from the academy is PV=NRT. All atmospherics operations tend to slow for you."
+ desc_novice = "You've had some practice managing air systems. Doing maintenance is now faster. Air alarms show slightly more information. Using an analyzer can be done at range."
+ desc_trained = "You're an apprentice air technician. Speeds globally increased. You can do basic pipenet tracing with an analyzer rather than T-rays. You can memorize where pipes are after a pulse for a little while."
+ desc_experienced = "You can intuit some information from the room, as well as some air networks with a glance. Speeds globally increased."
+ desc_professional = "You can track multiple pieces of atmospherics machinery with a glance, even while far away. You can see the direction of breaches."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Engines & Ship Systems
+ *
+ * Implementation status: not started
+ */
+/datum/characteristic_skill/engineering/engines
+ id = "engines"
+ name = "Engine & Voidcraft Operation"
+ desc = "How experienced you are with various engines, as well as with systems aboard a ship."
+ desc_untrained = "You probably shouldn't be touching engines."
+ desc_novice = "You've been shown how to set up a basic reactor, tune ship systems, so on and so forth. You can now do more efficient repairs on ship systems."
+ desc_trained = "You've been working on ships, or complex industrial systems for a while. You can check components and efficiencies with a glance."
+ desc_experienced = "You're a long-time reactor or ship technician. You can overclock ship components while near them. You can now see the engine's EER with a quick glance. You are no longer affected by certain engine emissions."
+ desc_professional = "You are a master of everything electromechanical. You can now overclock ship components remotely, as well as perform stronger overclocking on the spot."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
diff --git a/code/modules/mob/characteristics/skills/logistics.dm b/code/modules/mob/characteristics/skills/logistics.dm
new file mode 100644
index 00000000000..5dae0800411
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/logistics.dm
@@ -0,0 +1,39 @@
+/datum/characteristic_skill/logistics
+ abstract_type = /datum/characteristic_skill/logistics
+ category = "Logistics"
+
+/**
+ * Salvage: mining, etc
+ *
+ * Implementation status: not started
+ */
+/datum/characteristic_skill/logistics/salvage
+ id = "salvage"
+ name = "Salvage"
+ desc = "How experienced you are with salvaging and mining. Each level provides additional boosts to mining equipment speed and efficiency."
+ desc_untrained = "Hitting rocks is unskilled labor, and you are the unskilled labor."
+ desc_novice = "You have been mining, or salvagingfor a while. Kinetic weaponry and other tooling now have less recoil."
+ desc_trained = "You are a trained miner. Drills and other equipment you set gain a small speed boost, if configured by you."
+ desc_experienced = "You're a space salvager. Some EVA skill boosts apply to you as well. You gain very slightly higher ore yields while mining."
+ desc_professional = "They said robots can replace you, but clearly, they were wrong. Kinetic tooling can now be used by you with almost no one-handing penalties, and you can use some equipment meant for two people by yourself efficiently."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Logistics: cargo, etc
+ *
+ * Implementation status: not started
+ */
+/datum/characteristic_skill/logistics/cargo
+ id = "cargo"
+ name = "Logistics"
+ desc = "How experienced you are running logistics, deck supply, etc."
+ desc_untrained = "Surely pushing crates and signing off on orders is easy. Right? Right?!"
+ desc_novice = "You've been doing logistics for a while. Crates can now be moved at full speed."
+ desc_trained = "You have been trained in logistics. Crates can now fit more when you pack them. Price scanners and other scanners may now be used at range."
+ desc_experienced = "You're a master at running a shipyard, or logistics deck. You now automatically negate certain penalties for not labelling crates when exporting. Work mechas now move and operates faster for you. You can intuit the prices of most objects at a glance."
+ cost_novice = SKILLCOST_INCREMENT_MODERATE
+ cost_trained = SKILLCOST_INCREMENT_MODERATE
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
diff --git a/code/modules/mob/characteristics/skills/medical.dm b/code/modules/mob/characteristics/skills/medical.dm
new file mode 100644
index 00000000000..6375636617b
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/medical.dm
@@ -0,0 +1,61 @@
+/datum/characteristic_skill/medical
+ abstract_type = /datum/characteristic_skill/medical
+ category = "Medical"
+
+/**
+ * anatomy: surgery / evaluations / whatnot
+ *
+ * implementation status: not started
+ */
+/datum/characteristic_skill/medical/anatomy
+ id = "anatomy"
+ name = "Anatomy"
+ desc = "How well you know the anatomical structures of living things."
+ desc_untrained = "You really don't know much about the inner workings of living things beyond the basics. You cannot do surgery well other than in a perfect suite with holographic assistance from a computer."
+ desc_novice = "You have had a little bit of study into the anatomy of living things. Surgery and dissections are now a bit faster, and you can see a bit more information on wounds with a glance."
+ desc_trained = "You have formal anatomical training. You can now do risk-free surgery without holographic assistance, in a good enough lab."
+ desc_experienced = "You have a decent amount of surgery experience. You now have less penalty from bad environments during surgery, and have some basic bonuses to surgery upon xenobiological lifeforms."
+ desc_professional = "You are a world class surgeon. You can do surgery anywhere, at any place, only risking minor failures, as long as you are careful enough. You are great at avoiding wound infections during surgery and can perform experimental biotuning procedures with ease."
+ cost_novice = SKILLCOST_INCREMENT_MAJOR
+ cost_trained = SKILLCOST_INCREMENT_MAJOR
+ cost_experienced = SKILLCOST_INCREMENT_MODERATE
+ cost_professional = SKILLCOST_INCREMENT_MODERATE
+
+/**
+ * medicine: medical in general
+ *
+ * implementation status: not started
+ */
+/datum/characteristic_skill/medical/medicine
+ id = "medicine"
+ name = "Medicine"
+ desc = "How well you know the nitty gritty of practicing medicine. Each level gives small boosts to tasks like CPR and equipment operation speed."
+ desc_untrained = "You probably have to follow a step by step guide to treat anyone well. You get raw medical information from scanners - great if you practice on your own time, terrible in a pinch."
+ desc_novice = "You have some experience with nursing, or similar. You get slightly more information from medical scanners, as well as when inspecting people visually."
+ desc_trained = "You are a physician in training. You get more information from scanners, and more information on visual inspection. You can administer medicine with ease."
+ desc_experienced = "You have had many years under your belt as a doctor. Many afflictions can be diagnosed at a close examination, and the intrinsics of treatment and triage are second nature to you."
+ desc_professional = "You are a true, tried and tested, doctor. All but the most invasive problems can be determined by you without technological assistance, and you practically have a third sense for injury. Medical speed and efficiency globally increased. You will see visual pings when people are dying of common afflictions."
+ // extremely harsh scaling as this skill is very useful
+ cost_novice = SKILLCOST_INCREMENT_MAJOR
+ cost_trained = SKILLCOST_INCREMENT_MAJOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * chemistry
+ *
+ * implementation status: not started
+ */
+/datum/characteristic_skill/medical/chemistry
+ id = "chemistry"
+ name = "Chemistry"
+ desc = "How well you know chemistry."
+ desc_untrained = "You don't know much about the nitty gritty of chemistry. Mixing chemicals is slow and tedious and you probably need a book with exact recipes."
+ desc_novice = "You've had a few lessons in chemistry. Common chemicals are now second nature to you and can be made easily without much problems."
+ desc_trained = "You have formal, or equivalent, training in chemistry. You can discern some basic things about reagents and mixtures at a glance, and more chemicals are now easily accessible by you."
+ desc_experienced = "You have a lot of experience with chemicals. Some basic chemicals themselves can be discerned at a glance, and metabolic information can be read directly from some scanners."
+ desc_professional = "You have been working with chemicals all your life. Nothing gets past your skills of perception, and you are able to go as far as to determine certain active ingrediants from trace signs alone. Chemistry machines now operate at a higher efficiency when used by you, with more product and less waste."
+ cost_novice = SKILLCOST_INCREMENT_MAJOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MODERATE
+ cost_professional = SKILLCOST_INCREMENT_MODERATE
diff --git a/code/modules/mob/characteristics/skills/misc.dm b/code/modules/mob/characteristics/skills/misc.dm
new file mode 100644
index 00000000000..89688da1edb
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/misc.dm
@@ -0,0 +1,23 @@
+/datum/characteristic_skill/misc
+ abstract_type = /datum/characteristic_skill/misc
+ category = "General"
+
+/**
+ * Atheletics - TBD
+ *
+ * Scaling is harsh due to being a general skill.
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/security/athletics
+ id = "athletics"
+ name = "Athletics"
+ desc = "How skilled you are with gymnastics, athletics, as well as general hand-eye coordination."
+ desc_untrained = "You don't really have any experience with gymnastics or serious fitness."
+ desc_novice = "You have a bit of recreational exercise. Fall damage is slightly decreased, and some minor tasks are slightly faster."
+ desc_trained = "You are a recreational athlete. Fall damage is more decreased, and you get up faster from falls. Your metabolism is slightly more efficient."
+ desc_experienced = "You are practically a professional gymnast. You can withstand minor falls with ease, as long as you are not in heavy equipment, and find yourself to be more resilient to many physical stressors."
+ max_value = CHARACTER_SKILL_EXPERIENCED
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MAJOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
diff --git a/code/modules/mob/characteristics/skills/science.dm b/code/modules/mob/characteristics/skills/science.dm
new file mode 100644
index 00000000000..9452b55f54e
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/science.dm
@@ -0,0 +1,77 @@
+/datum/characteristic_skill/science
+ abstract_type = /datum/characteristic_skill/science
+ category = "Research"
+
+/**
+ * R&D / Anomalies / Tech
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/science/devices
+ id = "devices"
+ name = "Complex Devices"
+ desc = "Your ability to fabricate, utilize, maintain, and analyze complex machinery."
+ desc_untrained = "You can follow the on screen instructions of most consoles. Cool, I guess."
+ desc_novice = "You are a hobbyist gizmo enthusiast. You can probably figure out how to use some machines decently well, even without an interface. Some global speed bonuses are buffed, including lathe speed, to a small extent."
+ desc_trained = "You are a tinkerer by trade, perhaps even a scientist. Bonuses increased. You now have an easier time figuring out truly alien contraptions."
+ desc_experienced = "You've been a scientist or engineer for a long time. Complicated mechanisms are now second nature to you, and you can even overclock some things much like an engineer can."
+ desc_professional = "You are a professional reverse engineer. You can intuit things about alien machinery and technology, and things just work for you."
+ cost_novice = SKILLCOST_INCREMENT_MODERATE
+ cost_trained = SKILLCOST_INCREMENT_MODERATE
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Robolimbs / Cyborgs / Cybernetics
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/science/robotics
+ id = "robotics"
+ name = "Robotics"
+ desc = "Your ability to build and maintain prosthetics, synthetics, and robots in general. Each level gives slight increases in repair rate, as well as fabrication rate for related machinery."
+ desc_untrained = "Building a robot for you is like trying to put together a replica model brick set with no manual. Or with a manual, in this case. No active bonuses."
+ desc_novice = "You have had some experience with synthetics and related technologies. Repair efficiency is boosted as well as speed from here on out."
+ desc_trained = "You are trained to assemble synthetics and robotic platforms with ease. Prosthetic surgeries can be safely done at this level without surgical suites, or skills. Surgeries will be automatically boosted from here on out regardless of surgery skill."
+ desc_experienced = "You have spent a good deal of your life dealing with synthetics. Prosthetic surgeries are further tuned, and cybernetics you install start out optimized. You can now operate cyborg wiring panels without testing for wires. All parts you construct gain temporary bonuses."
+ desc_professional = "You are a master roboticist. You can now overclock prosthetics and cybernetics to boost them for their user for hours, or even days at a time. Prosthetics surgeries are second nature to you, regardless of your anatomical knowledge."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Mechs / Rigsuits
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/science/mecha
+ id = "mecha"
+ name = "Mechatronics"
+ desc = "Your ability to pilot and maintain exosuits, as well as powered hardsuits. Each level gives benefits to repair speed and efficiency (diminishing returns)."
+ desc_untrained = "Piloting a mech is just like a really big hardsuit, right? What even is a hardsuit?"
+ desc_novice = "You have had some practice with mechatronics. You can probably maintain one without a manual now. Hardsuit deployment speed, hardware installation speed, and mecha enter/exit speeds boosted."
+ desc_trained = "You are a trained mechatronic engineer, or an experienced operator. Speeds further boosted. Mecha can now natively strafe at full speed with you at the helm (adds automatic face-cursor mode)."
+ desc_experienced = "You spend a great deal of time working with powered suits. Mecha now suffer less movement cost and recoil with you at the helm. You can salvage more parts out of destroyed mecha."
+ max_value = CHARACTER_SKILL_EXPERIENCED
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MODERATE
+ cost_experienced = SKILLCOST_INCREMENT_MODERATE
+
+/**
+ * biology: xenobiology, genetics, nanoswarms, etc
+ */
+/datum/characteristic_skill/medical/biology
+ id = "biotech"
+ name = "Biotechnology"
+ desc = "How well you understand biology, genetics, xeno-lifeform research, nanoswarms, and anything else relating to weird life sciences."
+ desc_untrained = "You really shouldn't be anywhere near xenobiology, let alone aliens, without a full biosuit."
+ desc_novice = "You have had some training with biohazardous protocol. Biosuits now slow you down less. Radsuits now slow you down less."
+ desc_trained = "You have been working exotic organisms and/or other exotic biotechnologies as an assistant. Genetics scanners are less harmful to you. Slimes deal slightly less damage to you, and find you less threatening."
+ desc_experienced = "You are a trained xenobiologist, geneticist, or bioengineer. You gain innate resistance to virus spread, and now have a slight global stat boost to resistances. Furthermore, you may analyze the function of certain genes faster, at a random chance, and can read information about slimes with a glance."
+ desc_professional = "You are a master biologist-engineer. Slimes rarely attack you. Global speed modifier increased. You can now operate xenobio-botanical and genetics machinery at maximum speed."
+ // pretty busted skill once released
+ cost_novice = SKILLCOST_INCREMENT_MODERATE
+ cost_trained = SKILLCOST_INCREMENT_MODERATE
+ cost_trained = SKILLCOST_INCREMENT_MAJOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
diff --git a/code/modules/mob/characteristics/skills/security.dm b/code/modules/mob/characteristics/skills/security.dm
new file mode 100644
index 00000000000..ff6e146f2b4
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/security.dm
@@ -0,0 +1,41 @@
+/datum/characteristic_skill/security
+ abstract_type = /datum/characteristic_skill/security
+ category = "Security"
+
+/**
+ * Ranged
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/security/ranged
+ id = "ranged"
+ name = "Weapons Expertise"
+ desc = "How skilled you are with assorted weaponry."
+ desc_untrained = "You know how to hold a gun. Not well, though. That's it."
+ desc_novice = "You have had some firearms safety. You can reflexively disable safeties when trying to aim and aiming is a bit easier for you. You know how to handle recoil more naturally now."
+ desc_trained = "You have been using firearms for a while or it is part of your daily job. Your stability has further increased."
+ desc_experienced = "Using a firearm is second nature to you. Your stability is further increased. You can use some alien weaponry by just studying it."
+ desc_professional = "You might have a career as an armorer or gunsmith, or just use guns daily. You have no penalties and can wield weapons with minimal recoil."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * Forensics
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/security/forensics
+ id = "forensics"
+ name = "Forensics"
+ desc = "How skilled you are at forensic examinations and evidence collection."
+ desc_untrained = "You have no experience doing forensics whatsoever. You can probably use a scanner to gather some data with guided evidence, but that's it."
+ desc_novice = "You have been doing forensics for a while. Things are faster for you than before and you can use manual collection methods without a scanner."
+ desc_trained = "You are trained in forensics. You can efficiently gather data from crime scenes, with or without technological assistance."
+ desc_experienced = "You have a career in forensics. You can efficiently determine some sources of evidence with just a glance."
+ desc_professional = "You are a master of forensics. A mere glance is enough to tell you about many kinds of data about something, whether it be be at people or objects around you."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
diff --git a/code/modules/mob/characteristics/skills/service.dm b/code/modules/mob/characteristics/skills/service.dm
new file mode 100644
index 00000000000..b7859041fdd
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/service.dm
@@ -0,0 +1,41 @@
+/datum/characteristic_skill/service
+ abstract_type = /datum/characteristic_skill/service
+ category = "Service"
+
+/**
+ * Cooking
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/service/cooking
+ id = "cooking"
+ name = "Cooking"
+ desc = "How good you are at cooking."
+ desc_untrained = "You will probably need a cookbook to do anything."
+ desc_novice = "You have had some practice cooking. Actions are inherently faster for you now."
+ desc_trained = "You have been cooking for a while, hobby or otherwise. You can now see some nutritional information at a glance."
+ desc_experienced = "You are a professionally trained chef. You now gain double outputs from some recipes at random."
+ desc_professional = "You now have a higher chance of getting doubled outputs."
+ cost_novice = SKILLCOST_INCREMENT_NEGLIGIBLE
+ cost_trained = SKILLCOST_INCREMENT_NEGLIGIBLE
+ cost_experienced = SKILLCOST_INCREMENT_MINOR
+ cost_professional = SKILLCOST_INCREMENT_MINOR
+
+/**
+ * Botany
+ *
+ * Implementation status: Not started
+ */
+/datum/characteristic_skill/service/botany
+ id = "botany"
+ name = "Botany"
+ desc = "How good you are at growing plants."
+ desc_untrained = "You have no experience growing plants."
+ desc_novice = "You have had some practice growing plants. Actions are inherently faster for you now, and you can see some plant stats by just looking at them."
+ desc_trained = "You've been growing or gardening for a while. More stats are now visible, and fertilizing/watering plants is now more efficient."
+ desc_experienced = "You are a professional grower, or a hydroponics worker. Plants tend to grow faster when tended to by you."
+ desc_professional = "You have had extensive research in hydroponics. You now sometimes gain additional yield during harvest, and can determine some plant genes with ease."
+ cost_novice = SKILLCOST_INCREMENT_NEGLIGIBLE
+ cost_trained = SKILLCOST_INCREMENT_NEGLIGIBLE
+ cost_experienced = SKILLCOST_INCREMENT_MINOR
+ cost_professional = SKILLCOST_INCREMENT_MINOR
diff --git a/code/modules/mob/characteristics/skills/voidcraft.dm b/code/modules/mob/characteristics/skills/voidcraft.dm
new file mode 100644
index 00000000000..1b003a3b071
--- /dev/null
+++ b/code/modules/mob/characteristics/skills/voidcraft.dm
@@ -0,0 +1,41 @@
+/datum/characteristic_skill/voidcraft
+ abstract_type = /datum/characteristic_skill/voidcraft
+ category = "General"
+
+/**
+ * EVA
+ *
+ * implementation status: not started
+ */
+/datum/characteristic_skill/voidcraft/eva
+ id = "eva"
+ name = "EVA"
+ desc = "How well you can perform EVA."
+ desc_untrained = "You don't really go into space. While you can handle it, it probably is very uneasy for you."
+ desc_novice = "You have had some basic EVA training. You now slip less often at high speeds, and can operate hardsuits a bit faster."
+ desc_trained = "You have had a decent amount of EVA training. You now move faster in magboots, and always see your jetpack gauge. Hardsuits are now faster."
+ desc_experienced = "You have worked in space for long periods of time. Jetpacks and oxygen supplies last longer than you. Hardsuits are now faster."
+ desc_professional = "Space is second nature to you. EVA equipment bonuses are maximized, and you inherently have the effects of magboots as long as there is support near you."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MINOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
+
+/**
+ * piloting
+ *
+ * implementation status: not started
+ */
+/datum/characteristic_skill/voidcraft/piloting
+ id = "pilot"
+ name = "Piloting"
+ desc = "How well you can pilot voidcraft of various function."
+ desc_untrained = "You don't really fly ships. The only way you manage to pilot is through modern automation and guided instruction."
+ desc_novice = "You have had some practice piloting. You gain a small increase in control and a decrease in response latency."
+ desc_trained = "You are a trained pilot. You gain more control, and can dodge meteors at low speeds, while piloting small craft. Manual landing is now far faster."
+ desc_experienced = "You have been piloting for a while. You can now dodge meteors at faster speeds, or for larger craft. You gain a small boost in engine performance as well. Some ship components can be analyzed by you with a glance."
+ desc_professional = "You are a professional pilot. You gain more boosts in engine performance and control, and can dock ships with ease."
+ cost_novice = SKILLCOST_INCREMENT_MINOR
+ cost_trained = SKILLCOST_INCREMENT_MINOR
+ cost_experienced = SKILLCOST_INCREMENT_MAJOR
+ cost_professional = SKILLCOST_INCREMENT_MAJOR
diff --git a/code/modules/mob/characteristics/stat.dm b/code/modules/mob/characteristics/stat.dm
new file mode 100644
index 00000000000..80e784cf083
--- /dev/null
+++ b/code/modules/mob/characteristics/stat.dm
@@ -0,0 +1,62 @@
+GLOBAL_LIST_INIT(characteristics_stats, _create_characteristics_stats())
+
+/proc/_create_characteristics_stats()
+ . = list()
+ for(var/datum/characteristic_stat/stat in subtypesof(/datum/characteristic_stat))
+ if(is_abstract(stat))
+ continue
+ . = new stat
+ if(isnull(stat.id))
+ stack_trace("null id on [stat.type]")
+ continue
+ if(.[stat.id])
+ stack_trace("collision on id [stat.id] between types [stat.type] and [.[stat.id]:type]")
+ continue
+ .[stat.id] = stat
+
+/**
+ * gets a stat datum
+ *
+ * use typepaths whenever possible for compile time!
+ */
+/proc/resolve_characteristics_stat(datum/characteristic_stat/typepath_or_id)
+ RETURN_TYPE(/datum/characteristic_stat)
+ return GLOB.characteristics_stats[ispath(typepath_or_id)? initial(typepath_or_id.id) : typepath_or_id]
+
+/**
+ * stats - basically raw skills that can theoretically hold anything
+ * you usually don't want players to be able to touch these or minmax them too hard
+ * use skills whenever possible
+ *
+ * stats are null when unset, and baseline when characteristics are disabled
+ */
+/datum/characteristic_stat
+ abstract_type = /datum/characteristic_stat
+ /// unique id
+ var/id
+ /// name
+ var/name = "ERROR"
+ /// description
+ var/desc = "An unknown stat. Someone needs to change this."
+ /// cateogry - just a string, no defines for now, surely no one will typo..
+ var/category = "General"
+ /// datatype
+ var/datatype = CHARACTER_STAT_UNKNOWN
+ /// default value when characteristics are disabled
+ var/baseline_value
+
+/**
+ * get the greater value. this is automatic for numbers, less so for everything else.
+ *
+ * if number, default handling without a proc override is return greater
+ * if bool, default handling is returning TRUE if either is true
+ * else, returns first value.
+ */
+/datum/characteristic_stat/proc/greater_value(a, b)
+ switch(datatype)
+ if(CHARACTER_STAT_NUMERIC)
+ return a > b? a : b
+ if(CHARACTER_STAT_BOOL)
+ return a || b
+ else
+ return a
diff --git a/code/modules/mob/characteristics/stats/gaming.dm b/code/modules/mob/characteristics/stats/gaming.dm
new file mode 100644
index 00000000000..9755607dc53
--- /dev/null
+++ b/code/modules/mob/characteristics/stats/gaming.dm
@@ -0,0 +1,5 @@
+/datum/characteristic_stat/gaming
+ name = "Gaming"
+ desc = "How good you are at playing games. Higher values make arcade games go brrr."
+ datatype = CHARACTER_STAT_NUMERIC
+ baseline_value = 0
diff --git a/code/modules/mob/characteristics/talent.dm b/code/modules/mob/characteristics/talent.dm
new file mode 100644
index 00000000000..519b4cd8a7a
--- /dev/null
+++ b/code/modules/mob/characteristics/talent.dm
@@ -0,0 +1,70 @@
+GLOBAL_LIST_INIT(characteristics_talents, _create_characteristics_talents())
+
+/proc/_create_characteristics_talents()
+ . = list()
+ for(var/datum/characteristic_talent/talent in subtypesof(/datum/characteristic_talent))
+ if(is_abstract(talent))
+ continue
+ . = new talent
+ if(isnull(talent.id))
+ stack_trace("null id on [talent.type]")
+ continue
+ if(.[talent.id])
+ stack_trace("collision on id [talent.id] between types [talent.type] and [.[talent.id]:type]")
+ continue
+ .[talent.id] = talent
+
+/**
+ * gets a talent datum
+ *
+ * use typepaths whenever possible for compile time!
+ */
+/proc/resolve_characteristics_talent(datum/characteristic_talent/typepath_or_id)
+ RETURN_TYPE(/datum/characteristic_talent)
+ return GLOB.characteristics_talents[ispath(typepath_or_id)? initial(typepath_or_id.id) : typepath_or_id]
+
+/**
+ * barotrauma-like talents
+ * these are **global singletons** to better do things like synchronization
+ * make sure to gc your stuff properly on Destroy().
+ *
+ * talents default to not being there when characteristics are disabled
+ */
+/datum/characteristic_talent
+ abstract_type = /datum/characteristic_talent
+ /// unique id
+ var/id
+ /// name
+ var/name = "ERROR"
+ /// desc
+ var/desc = "An unknown talent. Someone needs to set this."
+
+/**
+ * called when we're put into a mind
+ * attach for mobs is called separately by mind
+ */
+/datum/characteristic_talent/proc/gain(datum/mind/M, metadata)
+
+/**
+ * called when we're yanked out of a mind
+ * detach for mobs is called separately by mind
+ */
+/datum/characteristic_talent/proc/lose(datum/mind/M, metadata)
+
+/**
+ * called when we're attaching to a mob
+ */
+/datum/characteristic_talent/proc/attach(mob/M, metadata)
+
+/**
+ * called when we're detaching from a mob
+ */
+/datum/characteristic_talent/proc/detach(mob/M, metadata)
+
+/**
+ * generates initial metadata for when we're being added to a holder
+ *
+ * @return anything that evals to true in logical expressions; defaults to TRUE if unimplemented
+ */
+/datum/characteristic_talent/proc/metadata(...)
+ return TRUE
diff --git a/code/modules/mob/characteristics/talents/placeholder.dm b/code/modules/mob/characteristics/talents/placeholder.dm
new file mode 100644
index 00000000000..5790b56417c
--- /dev/null
+++ b/code/modules/mob/characteristics/talents/placeholder.dm
@@ -0,0 +1 @@
+// no talents exist yet
diff --git a/code/modules/mob/characteristics/ui.dm b/code/modules/mob/characteristics/ui.dm
new file mode 100644
index 00000000000..a9780c089b4
--- /dev/null
+++ b/code/modules/mob/characteristics/ui.dm
@@ -0,0 +1,11 @@
+/datum/characteristics_holder/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
+
+/datum/characteristics_holder/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+
+/datum/characteristics_holder/ui_static_data(mob/user)
+ . = ..()
+
+/datum/characteristics_holder/ui_data(mob/user, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+
diff --git a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm
index 8f2fc6369b1..af1b89e491b 100644
--- a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm
+++ b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm
@@ -89,9 +89,9 @@
if(to_wear_id_type)
var/obj/item/card/id/W = new to_wear_id_type(src)
W.name = "[real_name]'s ID Card"
- var/datum/job/jobdatum
- for(var/jobtype in typesof(/datum/job))
- var/datum/job/J = new jobtype
+ var/datum/role/job/jobdatum
+ for(var/jobtype in typesof(/datum/role/job))
+ var/datum/role/job/J = new jobtype
if(J.title == to_wear_id_job)
jobdatum = J
break
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 3517ec2c2c4..787c91de433 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -682,23 +682,18 @@ default behaviour is:
/mob/living/proc/UpdateDamageIcon()
return
-
/mob/living/proc/Examine_OOC()
set name = "Examine Meta-Info (OOC)"
set category = "OOC"
set src in view()
- // Making it so SSD people have prefs with fallback to original style.
- if(config_legacy.allow_Metadata)
- if(ooc_notes)
- to_chat(usr, "[src]'s Metainfo:
[ooc_notes]")
- else if(client)
- to_chat(usr, "[src]'s Metainfo:
[client.prefs.metadata]")
- else
- to_chat(usr, "[src] does not have any stored infomation!")
- else
- to_chat(usr, "OOC Metadata is not supported by this server!")
- return
+ // Making it so SSD people have prefs with fallback to original style.
+ if(ooc_notes)
+ to_chat(usr, "[src]'s Metainfo:
[ooc_notes]")
+ else if(client)
+ to_chat(usr, "[src]'s Metainfo:
[client.prefs.metadata]")
+ else
+ to_chat(usr, "[src] does not have any stored infomation!")
/mob/living/proc/handle_footstep(turf/T)
return FALSE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 2eec828a346..ef7007d75cb 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -504,8 +504,8 @@
// Try harder to find a key to use
if(!keytouse && key)
keytouse = ckey(key)
- else if(!keytouse && mind?.key)
- keytouse = ckey(mind.key)
+ else if(!keytouse && mind?.ckey)
+ keytouse = mind.ckey
GLOB.respawn_timers[keytouse] = world.time + time
diff --git a/code/modules/mob/new_player/join_menu.dm b/code/modules/mob/new_player/join_menu.dm
index 5517fe5eb75..4a6f499f6eb 100644
--- a/code/modules/mob/new_player/join_menu.dm
+++ b/code/modules/mob/new_player/join_menu.dm
@@ -34,9 +34,9 @@ GLOBAL_DATUM_INIT(join_menu, /datum/join_menu, new)
data["jobs"] = jobs
// collect
- var/list/datum/job/eligible = list()
+ var/list/datum/role/job/eligible = list()
for(var/title in SSjob.name_occupations)
- var/datum/job/J = SSjob.name_occupations[title]
+ var/datum/role/job/J = SSjob.name_occupations[title]
if(!(J.join_types & JOB_LATEJOIN))
continue
if(!IsJobAvailable(J, N))
@@ -44,7 +44,7 @@ GLOBAL_DATUM_INIT(join_menu, /datum/join_menu, new)
eligible += J
// make
- for(var/datum/job/J as anything in eligible) // already type filtered
+ for(var/datum/role/job/J as anything in eligible) // already type filtered
// faction
var/list/faction
if(!jobs[J.faction])
@@ -75,7 +75,7 @@ GLOBAL_DATUM_INIT(join_menu, /datum/join_menu, new)
var/list/ghostroles = list()
data["ghostroles"] = ghostroles
for(var/id in GLOB.ghostroles)
- var/datum/ghostrole/R = GLOB.ghostroles[id]
+ var/datum/role/ghostrole/R = GLOB.ghostroles[id]
// can't afford runtime here
if(!istype(R) || !IsGhostroleAvailable(R, N))
continue
@@ -133,28 +133,28 @@ GLOBAL_DATUM_INIT(join_menu, /datum/join_menu, new)
* checks if job is available
* if not, it shouldn't even show
*/
-/datum/join_menu/proc/IsJobAvailable(datum/job/J, mob/new_player/N)
+/datum/join_menu/proc/IsJobAvailable(datum/role/job/J, mob/new_player/N)
return J.check_client_availability_one(N.client, TRUE, TRUE) == ROLE_AVAILABLE
/**
* checks if ghostrole is available
* if not, it shouldn't even show
*/
-/datum/join_menu/proc/IsGhostroleAvailable(datum/ghostrole/G, mob/new_player/N)
+/datum/join_menu/proc/IsGhostroleAvailable(datum/role/ghostrole/G, mob/new_player/N)
return G.AllowSpawn(N.client)
/**
* return effective title - used for alt titles - JOBS ONLY, not ghostroles
*/
-/datum/join_menu/proc/EffectiveTitle(datum/job/J, mob/new_player/N)
+/datum/join_menu/proc/EffectiveTitle(datum/role/job/J, mob/new_player/N)
return N.client.prefs.get_job_alt_title_name(J) || J.title
/**
* returns effective desc - used for alt titles - JOBS ONLY, not ghostroles
*/
-/datum/join_menu/proc/EffectiveDesc(datum/job/J, mob/new_player/N)
+/datum/join_menu/proc/EffectiveDesc(datum/role/job/J, mob/new_player/N)
var/title = N.client.prefs.get_job_alt_title_name(J)
- var/datum/alt_title/T = J.alt_titles?[title]
+ var/datum/prototype/alt_title/T = J.alt_titles?[title]
return isnull(T)? J.desc : (initial(T.title_blurb) || J.desc)
/datum/join_menu/proc/QueueStatus(mob/new_player/N)
@@ -209,14 +209,14 @@ GLOBAL_DATUM_INIT(join_menu, /datum/join_menu, new)
if(!config_legacy.enter_allowed)
to_chat(usr, SPAN_NOTICE("There is an administrative lock on entering the game."))
return
- var/datum/job/J = SSjob.job_by_id(id)
+ var/datum/role/job/J = SSjob.job_by_id(id)
if(!J)
to_chat(usr, SPAN_WARNING("Failed to find job [id]."))
return
to_chat(usr, SPAN_NOTICE("Attempting to latespawn as [id] ([J.title])."))
N.AttemptLateSpawn(J.title) // todo: remove shim
if("ghostrole")
- var/datum/ghostrole/R = get_ghostrole_datum(id)
+ var/datum/role/ghostrole/R = get_ghostrole_datum(id)
if(!R)
to_chat(usr, SPAN_WARNING("Failed to find ghostrole [R]"))
return
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 4163b2ba68f..322a052bbfa 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -232,6 +232,12 @@
if(href_list["ready"])
if(!SSticker || SSticker.current_state <= GAME_STATE_PREGAME) // Make sure we don't ready up after the round has started
+ var/list/warnings = list()
+ client.prefs.spawn_checks(PREF_COPY_TO_FOR_ROUNDSTART, warnings = warnings)
+ if(length(warnings))
+ to_chat(src, "--- Character Setup Warnings---
- [jointext(warnings, "
- ")]")
+ if(tgui_alert(src, "You do not seem to have your preferences set properly. Are you sure you wish to ready up? Check the chat panel for details.", "Spawn Checks", list("Yes", "No")) != "Yes")
+ return
ready = text2num(href_list["ready"])
else
ready = 0
@@ -468,7 +474,7 @@
if(!config_legacy.enter_allowed)
to_chat(usr, "There is an administrative lock on entering the game!")
return 0
- var/datum/job/J = SSjob.job_by_title(rank)
+ var/datum/role/job/J = SSjob.job_by_title(rank)
var/reason
if((reason = J.check_client_availability_one(client)) != ROLE_AVAILABLE)
to_chat(src, SPAN_WARNING("[rank] is not available: [J.get_availability_reason(client, reason)]"))
@@ -476,9 +482,14 @@
if(!spawn_checks_vr())
return FALSE
var/list/errors = list()
- if(!client.prefs.spawn_checks(PREF_COPY_TO_FOR_LATEJOIN, errors))
+ var/list/warnings = list()
+ if(!client.prefs.spawn_checks(PREF_COPY_TO_FOR_LATEJOIN, errors, warnings))
to_chat(src, SPAN_WARNING("An error has occured while trying to spawn you in:
[errors.Join("
")]"))
return FALSE
+ if(length(warnings))
+ to_chat(src, "--- Character Setup Warnings---
- [jointext(warnings, "
- ")]")
+ if(tgui_alert(src, "You do not seem to have your preferences set properly. Are you sure you wish to join the game?", "Spawn Checks", list("Yes", "No")) != "Yes")
+ return
//Find our spawning point.
var/list/join_props = SSjob.LateSpawn(client, rank)
@@ -558,6 +569,7 @@
if(!spawn_checks_vr())
return FALSE
var/list/errors = list()
+ // warnings ignored for now.
if(!client.prefs.spawn_checks(PREF_COPY_TO_FOR_ROUNDSTART, errors))
to_chat(src, SPAN_WARNING("An error has occured while trying to spawn you in:
[errors.Join("
")]"))
return FALSE
@@ -690,16 +702,6 @@
/mob/new_player/proc/spawn_checks_vr() //Custom spawn checks.
var/pass = TRUE
- //No Flavor Text
- if (config_legacy.require_flavor && client && client.prefs && client.prefs.flavor_texts && !client.prefs.flavor_texts["general"])
- to_chat(src,"Please set your general flavor text to give a basic description of your character. Set it using the 'Set Flavor text' button on the 'General' tab in character setup, and choosing 'General' category.")
- pass = FALSE
-
- //No OOC notes
- if (config_legacy.allow_Metadata && client && client.prefs && (isnull(client.prefs.metadata) || length(client.prefs.metadata) < 15))
- to_chat(src,"Please set informative OOC notes related to ERP preferences. Set them using the 'OOC Notes' button on the 'General' tab in character setup.")
- pass = FALSE
-
//Are they on the VERBOTEN LIST?
if (prevent_respawns.Find(client.prefs.real_name))
to_chat(src,"You've already quit the round as this character. You can't go back now that you've free'd your job slot. Play another character, or wait for the next round.")
diff --git a/code/modules/mob/new_player/skill.dm b/code/modules/mob/new_player/skill.dm
index c076d22bf2e..91addb4739b 100644
--- a/code/modules/mob/new_player/skill.dm
+++ b/code/modules/mob/new_player/skill.dm
@@ -5,12 +5,12 @@ var/global/const
SKILL_EXPERT = 3
SKILL_PROF = 4
-/datum/skill/var
- ID = "none" // ID of the skill, used in code
- name = "None" // Name of the skill
- desc = "Placeholder skill" // Detailed description of the skill
- field = "Misc" // The field under which the skill will be listed
- secondary = 0 // Secondary skills only have two levels and cost significantly less
+/datum/skill
+ var/ID = "none" // ID of the skill, used in code
+ var/name = "None" // Name of the skill
+ var/desc = "Placeholder skill" // Detailed description of the skill
+ var/field = "Misc" // The field under which the skill will be listed
+ var/secondary = 0 // Secondary skills only have two levels and cost significantly less
var/global/list/SKILLS = null
var/list/SKILL_ENGINEER = list("field" = "Engineering", "EVA" = SKILL_BASIC, "construction" = SKILL_ADEPT, "electrical" = SKILL_BASIC, "engines" = SKILL_ADEPT)
@@ -24,18 +24,6 @@ var/global/list/SKILL_PRE = list("Engineer" = SKILL_ENGINEER, "Roboticist" = SKI
name = "Command"
desc = "Your ability to manage and commandeer other crew members."
-/datum/skill/combat
- ID = "combat"
- name = "Close Combat"
- desc = "This skill describes your training in hand-to-hand combat or melee weapon usage. While expertise in this area is rare in the era of firearms, experts still exist among athletes."
- field = "Security"
-
-/datum/skill/weapons
- ID = "weapons"
- name = "Weapons Expertise"
- desc = "This skill describes your expertise with and knowledge of weapons. A low level in this skill implies knowledge of simple weapons, for example tazers and flashes. A high level in this skill implies knowledge of complex weapons, such as grenades, riot shields, pulse rifles or bombs. A low level in this skill is typical for security officers, a high level of this skill is typical for special agents and soldiers."
- field = "Security"
-
/datum/skill/EVA
ID = "EVA"
name = "Extra-vehicular activity"
@@ -43,12 +31,6 @@ var/global/list/SKILL_PRE = list("Engineer" = SKILL_ENGINEER, "Roboticist" = SKI
field = "Engineering"
secondary = 1
-/datum/skill/forensics
- ID = "forensics"
- name = "Forensics"
- desc = "Describes your skill at performing forensic examinations and identifying vital evidence. Does not cover analytical abilities, and as such isn't the only indicator for your investigation skill. Note that in order to perform autopsy, the surgery skill is also required."
- field = "Security"
-
/datum/skill/construction
ID = "construction"
name = "Construction"
diff --git a/code/modules/mob/skillset.dm b/code/modules/mob/skillset.dm
deleted file mode 100644
index c43f866edfd..00000000000
--- a/code/modules/mob/skillset.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-// We don't actually have a skills system, so return max skill for everything.
-/mob/proc/get_skill_value(skill_path)
- return SKILL_EXPERT
-
-// A generic way of modifying success probabilities via skill values. Higher factor means skills have more effect. fail_chance is the chance at SKILL_NONE.
-/mob/proc/skill_fail_chance(skill_path, fail_chance, no_more_fail = SKILL_EXPERT, factor = 1)
- var/points = get_skill_value(skill_path)
- if(points >= no_more_fail)
- return 0
- else
- return fail_chance * 2 ** (factor*(SKILL_BASIC - points))
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 60c484374fe..8b05661d4fe 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -81,7 +81,7 @@
O.add_language(LANGUAGE_ROOTLOCAL, 1)
if(move)
- var/obj/landmark/spawnpoint/S = SSjob.get_latejoin_spawnpoint(job_path = /datum/job/station/ai)
+ var/obj/landmark/spawnpoint/S = SSjob.get_latejoin_spawnpoint(job_path = /datum/role/job/station/ai)
O.forceMove(S.GetSpawnLoc())
S.OnSpawn(O)
diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm
index 25119cd0af0..23c045fce43 100644
--- a/code/modules/modular_computers/file_system/programs/command/card.dm
+++ b/code/modules/modular_computers/file_system/programs/command/card.dm
@@ -194,9 +194,9 @@
if(module.is_centcom)
access = get_centcom_access(t1)
else
- var/datum/job/jobdatum
- for(var/jobtype in typesof(/datum/job))
- var/datum/job/J = new jobtype
+ var/datum/role/job/jobdatum
+ for(var/jobtype in typesof(/datum/role/job))
+ var/datum/role/job/J = new jobtype
if(ckey(J.title) == ckey(t1))
jobdatum = J
break
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index 987f2e5adde..f07473590cc 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -133,7 +133,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
if(brainmob.mind)
brainmob.mind.transfer_to(target)
else
- target.key = brainmob.key
+ target.ckey = brainmob.ckey
..()
/obj/item/organ/internal/brain/proc/get_control_efficiency()
@@ -223,7 +223,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
return FALSE
/// Somebody is using that mind.
if(clonemind.active)
- if(ckey(clonemind.key) != R.ckey)
+ if(clonemind.ckey != R.ckey)
return FALSE
else
for(var/mob/observer/dead/G in GLOB.player_list)
diff --git a/code/modules/overmap/disperser/disperser_console.dm b/code/modules/overmap/disperser/disperser_console.dm
index bb63bcb0e36..302a4cd5826 100644
--- a/code/modules/overmap/disperser/disperser_console.dm
+++ b/code/modules/overmap/disperser/disperser_console.dm
@@ -150,7 +150,7 @@
data["range"] = range
data["next_shot"] = round(get_next_shot_seconds())
data["nopower"] = !data["faillink"] && (!front.powered() || !middle.powered() || !back.powered())
- data["skill"] = user.get_skill_value(core_skill) > skill_offset
+ data["skill"] = TRUE // todo: skills
var/charge = "UNKNOWN ERROR"
if(get_charge_type() == OVERMAP_WEAKNESS_NONE)
@@ -183,7 +183,8 @@
. = TRUE
if("skill_calibration")
- for(var/i = 1 to min(caldigit, usr.get_skill_value(core_skill) - skill_offset))
+ // todo: skills
+ for(var/i in 1 to 2)
calibration[i] = calexpected[i]
. = TRUE
diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm
index 46caf5d7938..cd2a6d70d7a 100644
--- a/code/modules/overmap/overmap_shuttle.dm
+++ b/code/modules/overmap/overmap_shuttle.dm
@@ -9,8 +9,6 @@
var/obj/effect/overmap/visitable/ship/landable/myship //my overmap ship object
category = /datum/shuttle/autodock/overmap
- var/skill_needed = SKILL_BASIC
- var/operator_skill = SKILL_BASIC
/datum/shuttle/autodock/overmap/New(var/_name, var/obj/effect/shuttle_landmark/start_waypoint)
..(_name, start_waypoint)
@@ -52,13 +50,6 @@
/datum/shuttle/autodock/overmap/can_force()
return ..() && can_go()
-/datum/shuttle/autodock/overmap/process_launch()
- if(prob(10*max(0, skill_needed - operator_skill)))
- var/places = get_possible_destinations()
- var/place = pick(places)
- set_destination(places[place])
- ..()
-
/datum/shuttle/autodock/overmap/proc/set_destination(var/obj/effect/shuttle_landmark/A)
if(A != current_location)
next_location = A
diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm
index 63a6c9cd761..cc3a0baddb9 100644
--- a/code/modules/overmap/ships/computers/helm.dm
+++ b/code/modules/overmap/ships/computers/helm.dm
@@ -83,7 +83,6 @@ GLOBAL_LIST_EMPTY(all_waypoints)
// All other cases, move toward direction
else if(speed + acceleration <= speedlimit)
linked.accelerate(direction, accellimit)
- linked.operator_skill = null // If this is on you can't dodge meteors
return
/obj/machinery/computer/ship/helm/ui_interact(mob/user, datum/tgui/ui)
@@ -228,8 +227,6 @@ GLOBAL_LIST_EMPTY(all_waypoints)
if("move")
var/ndir = text2num(params["dir"])
- if(prob(usr.skill_fail_chance(/datum/skill/pilot, 50, linked.skill_needed, factor = 1)))
- ndir = turn(ndir,pick(90,-90))
linked.relaymove(usr, ndir, accellimit)
. = TRUE
diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm
index 5a86c5cc6ef..8141287349f 100644
--- a/code/modules/overmap/ships/computers/shuttle.dm
+++ b/code/modules/overmap/ships/computers/shuttle.dm
@@ -34,10 +34,6 @@
to_chat(usr, "Unable to establish link with the shuttle.")
return TRUE
- if(ismob(usr))
- var/mob/user = usr
- shuttle.operator_skill = user.get_skill_value(/datum/skill/pilot)
-
switch(action)
if("pick")
var/list/possible_d = shuttle.get_possible_destinations()
diff --git a/code/modules/overmap/ships/ship.dm b/code/modules/overmap/ships/ship.dm
index 2622fec68c7..7e9b357db60 100644
--- a/code/modules/overmap/ships/ship.dm
+++ b/code/modules/overmap/ships/ship.dm
@@ -78,7 +78,6 @@
//? todo why tf is this relaymove
/obj/effect/overmap/visitable/ship/relaymove(mob/user, direction, accel_limit)
accelerate(direction, accel_limit)
- operator_skill = user.get_skill_value(/datum/skill/pilot)
/obj/effect/overmap/visitable/ship/get_scan_data(mob/user)
. = ..()
@@ -285,9 +284,6 @@
if(!SSshuttle.overmap_halted)
halted = 0
-/obj/effect/overmap/visitable/ship/proc/get_helm_skill() // Delete this mover operator skill to overmap obj
- return operator_skill
-
/obj/effect/overmap/visitable/ship/populate_sector_objects()
..()
for(var/obj/machinery/computer/ship/S in GLOB.machines)
diff --git a/code/modules/persistence/persistence.dm b/code/modules/persistence/persistence.dm
index 050348378b3..d2d343a0f8a 100644
--- a/code/modules/persistence/persistence.dm
+++ b/code/modules/persistence/persistence.dm
@@ -1,26 +1,3 @@
-/*
-* Returns a byond list that can be passed to the "deserialize" proc
-* to bring a new instance of this atom to its original state
-*
-* If we want to store this info, we can pass it to `json_encode` or some other
-* interface that suits our fancy, to make it into an easily-handled string
-*/
-/datum/proc/serialize()
- var/data = list("type" = "[type]")
- return data
-
-/*
-* This is given the byond list from above, to bring this atom to the state
-* described in the list.
-* This will be called after `New` but before `initialize`, so linking and stuff
-* would probably be handled in `initialize`
-*
-* Also, this should only be called by `list_to_object` in persistence.dm - at least
-* with current plans - that way it can actually initialize the type from the list
-*/
-/datum/proc/deserialize(var/list/data)
- return
-
/atom
// This var isn't actually used for anything, but is present so that
// DM's map reader doesn't forfeit on reading a JSON-serialized map
diff --git a/code/modules/preferences/apply.dm b/code/modules/preferences/apply.dm
index 1644093aed7..3da0f38acf3 100644
--- a/code/modules/preferences/apply.dm
+++ b/code/modules/preferences/apply.dm
@@ -1,7 +1,7 @@
-/datum/preferences/proc/spawn_checks(flags, list/errors)
+/datum/preferences/proc/spawn_checks(flags, list/errors, list/warnings)
. = TRUE
for(var/datum/category_group/player_setup_category/category in player_setup.categories)
- if(!category.spawn_checks(src, flags, errors))
+ if(!category.spawn_checks(src, flags, errors, warnings))
. = FALSE
// todo: at some point we should support nonhuman copy to's better.
diff --git a/code/modules/preferences/migration.dm b/code/modules/preferences/migration.dm
index 6552f07937d..935ccc4e9fd 100644
--- a/code/modules/preferences/migration.dm
+++ b/code/modules/preferences/migration.dm
@@ -63,7 +63,7 @@
var/list/assembled_titles = list()
if(!islist(player_alt_titles))
player_alt_titles = list()
- for(var/datum/job/J as anything in SSjob.occupations)
+ for(var/datum/role/job/J as anything in SSjob.occupations)
switch(J.department_flag)
if(CIVILIAN)
if(job_civilian_high & J.flag)
diff --git a/code/modules/preferences/preference_setup/background/_background.dm b/code/modules/preferences/preference_setup/background/_background.dm
index 9577363a912..8698b50120e 100644
--- a/code/modules/preferences/preference_setup/background/_background.dm
+++ b/code/modules/preferences/preference_setup/background/_background.dm
@@ -16,26 +16,25 @@
sanitize_preference(/datum/category_item/player_setup_item/background/religion)
// do language last
sanitize_preference(/datum/category_item/player_setup_item/background/language)
+ // lastly, do general job titles after
+ sanitize_preference(/datum/category_item/player_setup_item/occupation/alt_titles)
-/datum/preferences/proc/get_background_lore_datums()
+/datum/preferences/proc/all_background_datums()
+ return list(
+ lore_faction_datum(),
+ lore_citizenship_datum(),
+ lore_origin_datum(),
+ lore_religion_datum(),
+ )
+
+/datum/preferences/proc/all_background_ids()
. = list()
- var/datum/lore/character_background/bglore
- bglore = SScharacters.resolve_citizenship(get_preference(/datum/category_item/player_setup_item/background/citizenship))
- if(bglore)
- . += bglore
- bglore = SScharacters.resolve_faction(get_preference(/datum/category_item/player_setup_item/background/faction))
- if(bglore)
- . += bglore
- bglore = SScharacters.resolve_origin(get_preference(/datum/category_item/player_setup_item/background/origin))
- if(bglore)
- . += bglore
- bglore = SScharacters.resolve_religion(get_preference(/datum/category_item/player_setup_item/background/religion))
- if(bglore)
- . += bglore
+ for(var/datum/lore/character_background/bg as anything in all_background_datums())
+ . += bg.id
/datum/preferences/proc/tally_background_economic_factor()
. = 1
- for(var/datum/lore/character_background/bglore as anything in get_background_lore_datums())
+ for(var/datum/lore/character_background/bglore as anything in all_background_datums())
. *= bglore.economy_payscale
// todo: character species when *necessary*
var/datum/species/S = real_species_datum()
diff --git a/code/modules/preferences/preference_setup/background/citizenship.dm b/code/modules/preferences/preference_setup/background/citizenship.dm
index ba6dc6624a7..f7794bdace0 100644
--- a/code/modules/preferences/preference_setup/background/citizenship.dm
+++ b/code/modules/preferences/preference_setup/background/citizenship.dm
@@ -6,9 +6,17 @@
/datum/category_item/player_setup_item/background/citizenship/content(datum/preferences/prefs, mob/user, data)
. = list()
var/list/datum/lore/character_background/citizenship/available = SScharacters.available_citizenships(prefs.character_species_id())
+ var/list/categories = list()
+ for(var/datum/lore/character_background/citizenship/O as anything in available)
+ LAZYADD(categories[O.category], O)
var/datum/lore/character_background/citizenship/current = SScharacters.resolve_citizenship(data)
. += ""
. += "Citizenship
"
+ if(length(categories) > 1)
+ for(var/category in categories)
+ . += (category == current.category)? "[category] " : href_simple(prefs, "category", "[category]", category)
+ . += " "
+ . += "
"
for(var/datum/lore/character_background/citizenship/O in available)
if(O == current)
. += "[O.name]"
@@ -33,6 +41,16 @@
write(prefs, id)
prefs.sanitize_background_lore() // update
return PREFERENCES_REFRESH
+ if("category")
+ var/cat = params["category"]
+ var/list/datum/lore/character_background/citizenship/citizenships = SScharacters.available_citizenships(prefs.character_species_id(), cat)
+ if(!length(citizenships))
+ to_chat(user, SPAN_WARNING("No citizenships in that category have been found; this might be an error."))
+ return PREFERENCES_NOACTION
+ var/datum/lore/character_background/citizenship/first = citizenships[1]
+ write(prefs, first.id)
+ prefs.sanitize_background_lore() // update
+ return PREFERENCES_REFRESH
return ..()
/datum/category_item/player_setup_item/background/citizenship/filter_data(datum/preferences/prefs, data, list/errors)
@@ -48,7 +66,7 @@
M.add_language(id)
return TRUE
-/datum/category_item/player_setup_item/background/citizenship/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/background/citizenship/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
var/datum/lore/character_background/citizenship/current = SScharacters.resolve_citizenship(data)
if(!current?.check_species_id(prefs.character_species_id()))
errors?.Add("Invalid citizenship for your current species.")
@@ -68,4 +86,5 @@
return get_character_data(CHARACTER_DATA_CITIZENSHIP)
/datum/preferences/proc/lore_citizenship_datum()
+ RETURN_TYPE(/datum/lore/character_background/citizenship)
return SScharacters.resolve_citizenship(lore_citizenship_id())
diff --git a/code/modules/preferences/preference_setup/background/faction.dm b/code/modules/preferences/preference_setup/background/faction.dm
index 7125559f2ce..9ab719397a3 100644
--- a/code/modules/preferences/preference_setup/background/faction.dm
+++ b/code/modules/preferences/preference_setup/background/faction.dm
@@ -7,9 +7,17 @@
/datum/category_item/player_setup_item/background/faction/content(datum/preferences/prefs, mob/user, data)
. = list()
var/list/datum/lore/character_background/faction/available = SScharacters.available_factions(prefs.character_species_id(), prefs.lore_origin_id(), prefs.lore_citizenship_id())
+ var/list/categories = list()
+ for(var/datum/lore/character_background/faction/O as anything in available)
+ LAZYADD(categories[O.category], O)
var/datum/lore/character_background/faction/current = SScharacters.resolve_faction(data)
. += ""
. += "Faction
"
+ if(length(categories) > 1)
+ for(var/category in categories)
+ . += (category == current.category)? "[category] " : href_simple(prefs, "category", "[category]", category)
+ . += " "
+ . += "
"
for(var/datum/lore/character_background/faction/O in available)
if(O == current)
. += "[O.name]"
@@ -32,8 +40,19 @@
to_chat(user, SPAN_WARNING("[prefs.character_species_name()] cannot pick this faction."))
return PREFERENCES_NOACTION
write(prefs, id)
+ prefs.sanitize_background_lore() // update
GLOB.join_menu?.update_static_data(user)
return PREFERENCES_REFRESH_UPDATE_PREVIEW
+ if("category")
+ var/cat = params["category"]
+ var/list/datum/lore/character_background/faction/factions = SScharacters.available_factions(prefs.character_species_id(), category = cat)
+ if(!length(factions))
+ to_chat(user, SPAN_WARNING("No factions in that category have been found; this might be an error."))
+ return PREFERENCES_NOACTION
+ var/datum/lore/character_background/faction/first = factions[1]
+ write(prefs, first.id)
+ prefs.sanitize_background_lore() // update
+ return PREFERENCES_REFRESH
return ..()
/datum/category_item/player_setup_item/background/faction/filter_data(datum/preferences/prefs, data, list/errors)
@@ -56,7 +75,7 @@
M.add_language(id)
return TRUE
-/datum/category_item/player_setup_item/background/faction/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/background/faction/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
var/datum/lore/character_background/faction/current = SScharacters.resolve_faction(data)
if(!current?.check_species_id(prefs.character_species_id()))
errors?.Add("Invalid faction for your current species.")
@@ -85,7 +104,8 @@
return get_character_data(CHARACTER_DATA_FACTION)
/datum/preferences/proc/lore_faction_datum()
- return get_character_data(CHARACTER_DATA_FACTION)
+ RETURN_TYPE(/datum/lore/character_background/faction)
+ return SScharacters.resolve_faction(get_character_data(CHARACTER_DATA_FACTION))
-/datum/preferences/proc/lore_faction_job_check(datum/job/J)
- return SScharacters.resolve_faction(get_character_data(CHARACTER_DATA_FACTION))?.check_job_id(J.id)
+/datum/preferences/proc/lore_faction_job_check(datum/role/job/J)
+ return lore_faction_datum()?.check_job_id(J.id)
diff --git a/code/modules/preferences/preference_setup/background/language.dm b/code/modules/preferences/preference_setup/background/language.dm
index 2a9ca48c579..aebe9ad0d5f 100644
--- a/code/modules/preferences/preference_setup/background/language.dm
+++ b/code/modules/preferences/preference_setup/background/language.dm
@@ -58,7 +58,7 @@
for(var/id in data)
H.add_language(id)
-/datum/category_item/player_setup_item/background/language/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/background/language/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
if(length(data) > prefs.extraneous_languages_max())
errors?.Add(SPAN_WARNING("You have selected too many extra languages for your species and culture."))
return FALSE
diff --git a/code/modules/preferences/preference_setup/background/origin.dm b/code/modules/preferences/preference_setup/background/origin.dm
index 6f431e50d40..4def82ff72c 100644
--- a/code/modules/preferences/preference_setup/background/origin.dm
+++ b/code/modules/preferences/preference_setup/background/origin.dm
@@ -12,10 +12,11 @@
var/datum/lore/character_background/origin/current = SScharacters.resolve_origin(data)
. += ""
. += "Origin
"
- for(var/category in categories)
- . += (category == current.category)? "[category] " : href_simple(prefs, "category", "[category]", category)
- . += " "
- . += "
"
+ if(length(categories) > 1)
+ for(var/category in categories)
+ . += (category == current.category)? "[category] " : href_simple(prefs, "category", "[category]", category)
+ . += " "
+ . += "
"
for(var/datum/lore/character_background/origin/O in categories[current.category])
if(O == current)
. += "[O.name]"
@@ -38,6 +39,7 @@
to_chat(user, SPAN_WARNING("[prefs.character_species_name()] cannot pick this origin."))
return PREFERENCES_NOACTION
write(prefs, id)
+ prefs.sanitize_background_lore() // update
return PREFERENCES_REFRESH
if("category")
var/cat = params["category"]
@@ -64,7 +66,7 @@
M.add_language(id)
return TRUE
-/datum/category_item/player_setup_item/background/origin/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/background/origin/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
var/datum/lore/character_background/origin/current = SScharacters.resolve_origin(data)
if(!current?.check_species_id(prefs.character_species_id()))
errors?.Add("Invalid origin for your current species.")
@@ -84,4 +86,5 @@
return get_character_data(CHARACTER_DATA_ORIGIN)
/datum/preferences/proc/lore_origin_datum()
+ RETURN_TYPE(/datum/lore/character_background/origin)
return SScharacters.resolve_origin(lore_origin_id())
diff --git a/code/modules/preferences/preference_setup/background/religion.dm b/code/modules/preferences/preference_setup/background/religion.dm
index 4d75fe4dbfa..c790a8b6585 100644
--- a/code/modules/preferences/preference_setup/background/religion.dm
+++ b/code/modules/preferences/preference_setup/background/religion.dm
@@ -6,9 +6,17 @@
/datum/category_item/player_setup_item/background/religion/content(datum/preferences/prefs, mob/user, data)
. = list()
var/list/datum/lore/character_background/religion/available = SScharacters.available_religions(prefs.character_species_id())
+ var/list/categories = list()
+ for(var/datum/lore/character_background/religion/O as anything in available)
+ LAZYADD(categories[O.category], O)
var/datum/lore/character_background/religion/current = SScharacters.resolve_religion(data)
. += ""
. += "Religion
"
+ if(length(categories) > 1)
+ for(var/category in categories)
+ . += (category == current.category)? "[category] " : href_simple(prefs, "category", "[category]", category)
+ . += " "
+ . += "
"
for(var/datum/lore/character_background/religion/O in available)
if(O == current)
. += "[O.name]"
@@ -31,6 +39,17 @@
to_chat(user, SPAN_WARNING("[prefs.character_species_name()] cannot pick this religion."))
return PREFERENCES_NOACTION
write(prefs, id)
+ prefs.sanitize_background_lore() // update
+ return PREFERENCES_REFRESH
+ if("category")
+ var/cat = params["category"]
+ var/list/datum/lore/character_background/religion/religions = SScharacters.available_religions(prefs.character_species_id(), cat)
+ if(!length(religions))
+ to_chat(user, SPAN_WARNING("No religions in that category have been found; this might be an error."))
+ return PREFERENCES_NOACTION
+ var/datum/lore/character_background/religion/first = religions[1]
+ write(prefs, first.id)
+ prefs.sanitize_background_lore() // update
return PREFERENCES_REFRESH
return ..()
@@ -47,7 +66,7 @@
M.add_language(id)
return TRUE
-/datum/category_item/player_setup_item/background/religion/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/background/religion/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
var/datum/lore/character_background/religion/current = SScharacters.resolve_religion(data)
if(!current?.check_species_id(prefs.character_species_id()))
errors?.Add("Invalid religion for your current species.")
@@ -67,4 +86,6 @@
return get_character_data(CHARACTER_DATA_RELIGION)
/datum/preferences/proc/lore_religion_datum()
+ RETURN_TYPE(/datum/lore/character_background/religion)
return SScharacters.resolve_religion(lore_religion_id())
+
diff --git a/code/modules/preferences/preference_setup/background/species.dm b/code/modules/preferences/preference_setup/background/species.dm
index 340a9c0f99e..ff853fe6b60 100644
--- a/code/modules/preferences/preference_setup/background/species.dm
+++ b/code/modules/preferences/preference_setup/background/species.dm
@@ -24,7 +24,7 @@
. += "[CS.desc]"
. += ""
-/datum/category_item/player_setup_item/background/char_species/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/background/char_species/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
var/datum/character_species/CS = SScharacters.resolve_character_species(data)
if((CS.species_spawn_flags & SPECIES_SPAWN_RESTRICTED) && !(flags & PREF_COPY_TO_NO_CHECK_SPECIES))
errors?.Add(SPAN_WARNING("[CS.name] is a restricted species. You cannot join as this as most normal roles."))
@@ -113,5 +113,5 @@
/datum/preferences/proc/real_species_name()
return SScharacters.resolve_species_id(get_character_data(CHARACTER_DATA_REAL_SPECIES)).name
-/datum/preferences/proc/character_species_job_check(datum/job/J)
+/datum/preferences/proc/character_species_job_check(datum/role/job/J)
return TRUE // todo
diff --git a/code/modules/preferences/preference_setup/general/01_basic.dm b/code/modules/preferences/preference_setup/general/01_basic.dm
index e81a27e5e96..683f6d92d48 100644
--- a/code/modules/preferences/preference_setup/general/01_basic.dm
+++ b/code/modules/preferences/preference_setup/general/01_basic.dm
@@ -81,9 +81,8 @@
. += "Pronouns: [gender2text(pref.identifying_gender)]
"
. += "Age: [pref.age]
"
. += "Spawn Point: [pref.spawnpoint]
"
- if(config_legacy.allow_Metadata)
- . += "OOC Notes: Edit
"
- . = jointext(.,null)
+ . += "OOC Notes: Edit
"
+ . = jointext(., null)
/datum/category_item/player_setup_item/general/basic/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["rename"])
@@ -146,13 +145,18 @@
return PREFERENCES_REFRESH
else if(href_list["metadata"])
- var/new_metadata = sanitize(input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , html_decode(pref.metadata)) as message, extra = 0)
+ var/new_metadata = sanitize(input(user, "Enter any information you'd like others to see in terms of roleplay preferences (including any ERP consent / preference information). This information is considered OOC, unlike 'Flavor Text'.", "OOC Notes" , html_decode(pref.metadata)) as message, extra = 0)
if(new_metadata && CanUseTopic(user))
pref.metadata = new_metadata
return PREFERENCES_REFRESH
return ..()
+/datum/category_item/player_setup_item/general/basic/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
+ . = ..()
+ if(!length(prefs.metadata))
+ warnings += "Missing OOC Notes - See Character Setup for information."
+
/datum/category_item/player_setup_item/general/basic/proc/get_genders()
var/datum/species/S = pref.real_species_datum()
var/list/possible_genders = S.genders
diff --git a/code/modules/preferences/preference_setup/general/06_flavor.dm b/code/modules/preferences/preference_setup/general/06_flavor.dm
index 6b615844c36..123a5f02bad 100644
--- a/code/modules/preferences/preference_setup/general/06_flavor.dm
+++ b/code/modules/preferences/preference_setup/general/06_flavor.dm
@@ -64,11 +64,11 @@
switch(href_list["flavor_text"])
if("open")
if("general")
- var/msg = sanitize(input(usr,"Give a general description of your character. This will be shown regardless of clothings.","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0, max_length = 8192)
+ var/msg = sanitize(input(usr,"Give a general description of your character. This will be shown regardless of clothings. Flavor text is what someone can see IC with a glance, please do not include OOC things like personality / backstory!","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0, max_length = 8192)
if(CanUseTopic(user))
pref.flavor_texts[href_list["flavor_text"]] = msg
else
- var/msg = sanitize(input(usr,"Set the flavor text for your [href_list["flavor_text"]].","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0, max_length = 8192)
+ var/msg = sanitize(input(usr,"Set the flavor text for your [href_list["flavor_text"]]. Flavor text is what someone can see IC with a glance, please do not include OOC things like personality / backstory!","Flavor Text",html_decode(pref.flavor_texts[href_list["flavor_text"]])) as message, extra = 0, max_length = 8192)
if(CanUseTopic(user))
pref.flavor_texts[href_list["flavor_text"]] = msg
SetFlavorText(user)
@@ -78,11 +78,11 @@
switch(href_list["flavour_text_robot"])
if("open")
if("Default")
- var/msg = sanitize(input(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting.","Flavour Text",html_decode(pref.flavour_texts_robot["Default"])) as message, extra = 0, max_length = 8192)
+ var/msg = sanitize(input(usr,"Set the default flavour text for your robot. It will be used for any module without individual setting. Flavor text is what someone can see IC with a glance, please do not include OOC things like personality / backstory!","Flavour Text",html_decode(pref.flavour_texts_robot["Default"])) as message, extra = 0, max_length = 8192)
if(CanUseTopic(user))
pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg
else
- var/msg = sanitize(input(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this empty, default flavour text will be used for this module.","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]])) as message, extra = 0, max_length = 8192)
+ var/msg = sanitize(input(usr,"Set the flavour text for your robot with [href_list["flavour_text_robot"]] module. If you leave this empty, default flavour text will be used for this module. Flavor text is what someone can see IC with a glance, please do not include OOC things like personality / backstory!","Flavour Text",html_decode(pref.flavour_texts_robot[href_list["flavour_text_robot"]])) as message, extra = 0, max_length = 8192)
if(CanUseTopic(user))
pref.flavour_texts_robot[href_list["flavour_text_robot"]] = msg
SetFlavourTextRobot(user)
@@ -90,6 +90,11 @@
return ..()
+/datum/category_item/player_setup_item/general/flavor/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
+ . = ..()
+ if(!length(prefs.flavor_texts["general"]))
+ warnings += "Missing general flavor text - See Character Setup for information."
+
/datum/category_item/player_setup_item/general/flavor/proc/SetFlavorText(mob/user)
var/HTML = ""
HTML += ""
diff --git a/code/modules/preferences/preference_setup/occupation/occupation.dm b/code/modules/preferences/preference_setup/occupation/occupation.dm
index 103a9f7bc0f..7d23babbdc3 100644
--- a/code/modules/preferences/preference_setup/occupation/occupation.dm
+++ b/code/modules/preferences/preference_setup/occupation/occupation.dm
@@ -6,6 +6,7 @@
/datum/category_item/player_setup_item/occupation
is_global = FALSE
+ load_order = PREFERENCE_LOAD_ORDER_OCCUPATIONS
/**
* save format: list(job id = priority)
@@ -18,7 +19,7 @@
var/list/jobs = sanitize_islist(data)
var/highest
for(var/id in jobs)
- var/datum/job/J = SSjob.job_by_id(id)
+ var/datum/role/job/J = SSjob.job_by_id(id)
if(!J)
jobs -= id
continue
@@ -97,7 +98,7 @@
#undef END_COLUMN
#undef START_COLUMN
-/datum/category_item/player_setup_item/occupation/jobs/proc/render_job(datum/preferences/prefs, datum/job/J, current_priority, assistant_selected)
+/datum/category_item/player_setup_item/occupation/jobs/proc/render_job(datum/preferences/prefs, datum/role/job/J, current_priority, assistant_selected)
. = list()
. += ""
// left side
@@ -147,7 +148,7 @@
/**
* return null if allowed, otherwise return error to display
*/
-/datum/category_item/player_setup_item/occupation/jobs/proc/check_job(datum/preferences/prefs, datum/job/J, current_priority)
+/datum/category_item/player_setup_item/occupation/jobs/proc/check_job(datum/preferences/prefs, datum/role/job/J, current_priority)
var/client/C = pref.client
if(!C)
return null
@@ -164,20 +165,20 @@
prefs.set_job_priority(job_id, level)
return PREFERENCES_REFRESH_UPDATE_PREVIEW
if("title")
- var/datum/job/J = SSjob.job_by_id(params["title"])
+ var/datum/role/job/J = SSjob.job_by_id(params["title"])
if(!J)
return PREFERENCES_NOACTION
- var/title = input(user, "Choose a title for [J.title].", "Choose Title", prefs.get_job_alt_title_name(J)) as null|anything in (J.alt_titles | J.title)
+ var/title = input(user, "Choose a title for [J.title].", "Choose Title", prefs.get_job_alt_title_name(J)) as null|anything in prefs.available_alt_titles(J)
if(!title)
return PREFERENCES_NOACTION
prefs.set_job_title(params["title"], title)
return PREFERENCES_REFRESH_UPDATE_PREVIEW
if("help")
- var/datum/job/J = SSjob.job_by_id(params["help"])
+ var/datum/role/job/J = SSjob.job_by_id(params["help"])
var/list/built = list("")
built += "[J.title]"
built += "Purpose: [J.desc]"
- built += "Alternative titles: [english_list(J.alt_titles)]"
+ built += "Alternative titles (faction): [english_list(prefs.available_alt_titles(J))]"
if(J.supervisors)
built += "You answer to [J.supervisors], normally."
if(J.departments)
@@ -205,6 +206,13 @@
/datum/category_item/player_setup_item/occupation/jobs/default_value(randomizing)
return list()
+/datum/preferences/proc/available_alt_titles(datum/role/job/J)
+ RETURN_TYPE(/list)
+ return J.alt_title_query(all_background_datums())
+
+/datum/preferences/proc/check_alt_title(datum/role/job/J, alt_title)
+ return J.alt_title_check(alt_title, all_background_datums())
+
/**
* display is done by jobs; this datum only handles data filtering
*
@@ -216,15 +224,21 @@
/datum/category_item/player_setup_item/occupation/alt_titles/filter_data(datum/preferences/prefs, data, list/errors)
var/list/jobs = sanitize_islist(data)
+ // check the ones we have to ensure compliance
for(var/id in jobs)
- var/datum/job/J = SSjob.job_by_id(id)
+ var/datum/role/job/J = SSjob.job_by_id(id)
if(!J)
jobs -= id
continue
var/title = jobs[id]
- if(!J.alt_titles[title])
+ if(!prefs.check_alt_title(J, title))
jobs -= id
+ // check the ones we don't and are strict titles
+ for(var/datum/role/job/J as anything in SSjob.all_jobs())
+ if(!J.strict_titles || !isnull(jobs[J.id]))
continue
+ // this will always have atleast one
+ jobs[J.id] = prefs.available_alt_titles(J)[1]
return jobs
/datum/category_item/player_setup_item/occupation/alt_titles/default_value(randomizing)
@@ -250,12 +264,12 @@
)
return sanitize_inlist(data, static_list, JOB_ALTERNATIVE_BE_ASSISTANT)
-/datum/preferences/proc/get_job_priority(datum/job/J)
+/datum/preferences/proc/get_job_priority(datum/role/job/J)
var/list/jobs = get_character_data(CHARACTER_DATA_JOBS)
return jobs[J.id]
-/datum/preferences/proc/get_job_alt_title_name(datum/job/J)
- RETURN_TYPE(/datum/alt_title)
+/datum/preferences/proc/get_job_alt_title_name(datum/role/job/J)
+ RETURN_TYPE(/datum/prototype/alt_title)
var/list/titles = get_character_data(CHARACTER_DATA_ALT_TITLES)
return titles[J.id] || J.title
@@ -277,7 +291,7 @@
continue
.[id] = priorities[id]
-/datum/preferences/proc/effective_job_priority(datum/job/J)
+/datum/preferences/proc/effective_job_priority(datum/role/job/J)
if(!lore_faction_job_check(J))
return JOB_PRIORITY_NEVER
var/list/jobs = get_character_data(CHARACTER_DATA_JOBS)
@@ -287,7 +301,7 @@
* gets effective job priority of a job for current slot; used for
* roundstart procs. returns JOB_PRIORITY_NEVER if we can't be said job.
*/
-/client/proc/effective_job_priority(datum/job/J)
+/client/proc/effective_job_priority(datum/role/job/J)
if(J.check_client_availability_one(src) != ROLE_AVAILABLE)
return JOB_PRIORITY_NEVER
return prefs?.effective_job_priority(J)
@@ -302,7 +316,7 @@
RETURN_TYPE(/list)
. = list()
var/list/priorities = sanitize_islist(prefs.get_character_data(CHARACTER_DATA_JOBS))
- for(var/datum/job/J as anything in SSjob.all_jobs())
+ for(var/datum/role/job/J as anything in SSjob.all_jobs())
if(J.check_client_availability_one(src) != ROLE_AVAILABLE)
continue
var/id = J.id
@@ -332,7 +346,7 @@
return get_character_data(CHARACTER_DATA_OVERFLOW_MODE)
/datum/preferences/proc/set_job_priority(id, priority)
- var/datum/job/J = SSjob.job_by_id(id)
+ var/datum/role/job/J = SSjob.job_by_id(id)
if(!J)
return
if(priority < JOB_PRIORITY_NEVER || priority > JOB_PRIORITY_HIGH)
@@ -345,7 +359,7 @@
set_character_data(CHARACTER_DATA_JOBS, current)
/datum/preferences/proc/set_job_title(id, title)
- var/datum/job/J = SSjob.job_by_id(id)
+ var/datum/role/job/J = SSjob.job_by_id(id)
if(!J)
return
var/list/current = get_character_data(CHARACTER_DATA_ALT_TITLES)
@@ -353,7 +367,7 @@
// reset
current -= id
else
- if(!J.alt_titles[title])
+ if(!J.alt_titles?[title])
return
current[id] = title
set_character_data(CHARACTER_DATA_ALT_TITLES, current)
diff --git a/code/modules/preferences/preference_setup/preference_category_new.dm b/code/modules/preferences/preference_setup/preference_category_new.dm
index 2015a233a3f..d1174acb4e7 100644
--- a/code/modules/preferences/preference_setup/preference_category_new.dm
+++ b/code/modules/preferences/preference_setup/preference_category_new.dm
@@ -1,9 +1,9 @@
/datum/category_group/player_setup_category
-/datum/category_group/player_setup_category/proc/spawn_checks(datum/preferences/prefs, flags, list/errors)
+/datum/category_group/player_setup_category/proc/spawn_checks(datum/preferences/prefs, flags, list/errors, list/warnings)
. = TRUE
for(var/datum/category_item/player_setup_item/preference in items)
- if(!preference.spawn_checks(prefs, prefs.get_character_data(preference.save_key), flags, errors))
+ if(!preference.spawn_checks(prefs, prefs.get_character_data(preference.save_key), flags, errors, warnings))
. = FALSE
// todo: multi stage random character generation
diff --git a/code/modules/preferences/preference_setup/preference_item_new.dm b/code/modules/preferences/preference_setup/preference_item_new.dm
index 9d39263394e..92e2cf4d898 100644
--- a/code/modules/preferences/preference_setup/preference_item_new.dm
+++ b/code/modules/preferences/preference_setup/preference_item_new.dm
@@ -33,9 +33,16 @@
* called to check for errors; if non null, players get showed this while spawning and the
* spawn is blocked.
*
- * put reasons into errors
+ * @params
+ * * prefs - preferences datum of spawning mob
+ * * data - current effective data
+ * * flags - preferences copy/apply to flags
+ * * errors - put error reasons to show to user
+ * * warnings - put warnings to show to user; if these exist AND they can spawn, they'll be warned but allowed to spawn if they confirm.
+ *
+ * @return TRUE/FALSE if we should be allowd to spawn
*/
-/datum/category_item/player_setup_item/proc/spawn_checks(datum/preferences/prefs, data, flags, list/errors)
+/datum/category_item/player_setup_item/proc/spawn_checks(datum/preferences/prefs, data, flags, list/errors, list/warnings)
return TRUE
/**
diff --git a/code/modules/preferences/preference_setup/skills/skills.dm b/code/modules/preferences/preference_setup/skills/skills.dm
index 992c97d762e..cd0b36a3286 100644
--- a/code/modules/preferences/preference_setup/skills/skills.dm
+++ b/code/modules/preferences/preference_setup/skills/skills.dm
@@ -1,8 +1,39 @@
-// This file has been UNTICKED on purpose, as it is not in use.
+// TODO: this file is still commented out, because we will not even *SHOW* it until skills implementation is near critical mass.
+
+/datum/category_group/player_setup_category/skills
+ name = "Skills"
+ sort_order = 3.5
+ category_item_type = /datum/category_item/player_setup_item/skills
/datum/category_item/player_setup_item/skills
name = "Skills"
sort_order = 1
+ save_key = CHARACTER_DATA_SKILLS
+ is_global = FALSE
+
+/datum/category_item/player_setup_item/skills/content(datum/preferences/prefs, mob/user, data)
+ . = ..()
+
+/datum/category_item/player_setup_item/skills/act(datum/preferences/prefs, mob/user, action, list/params)
+ switch(action)
+ if("set")
+
+ if("reset")
+
+ if("preset")
+
+ if("details")
+
+ return ..()
+
+/datum/category_item/player_setup_item/skills/filter_data(datum/preferences/prefs, data, list/errors)
+ var/list/skill_data = sanitize_islist(data)
+
+
+/datum/category_item/player_setup_item/skills/copy_to_mob(datum/preferences/prefs, mob/M, data, flags)
+ . = ..()
+
+#warn impl above
/datum/category_item/player_setup_item/skills/load_character(var/savefile/S)
S["skills"] >> pref.skills
diff --git a/code/modules/preferences/preference_setup/vore/08_traits.dm b/code/modules/preferences/preference_setup/vore/08_traits.dm
index eb51170b1c4..20e790067e6 100644
--- a/code/modules/preferences/preference_setup/vore/08_traits.dm
+++ b/code/modules/preferences/preference_setup/vore/08_traits.dm
@@ -119,7 +119,7 @@
S.blood_color = pref.blood_color
if(pref.real_species_id() == SPECIES_ID_CUSTOM)
- if(flags & PREF_COPY_TO_IS_SPAWNING)
+ if(PREF_COPYING_TO_CHECK_IS_SPAWNING(flags))
//Statistics for this would be nice
var/english_traits = english_list(S.traits, and_text = ";", comma_text = ";")
log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet
diff --git a/code/modules/preferences/preferences_setup.dm b/code/modules/preferences/preferences_setup.dm
index 1165733c127..a549cd7a7a4 100644
--- a/code/modules/preferences/preferences_setup.dm
+++ b/code/modules/preferences/preferences_setup.dm
@@ -197,9 +197,9 @@
if(!equip_preview_mob)
return
- var/datum/job/previewJob = SSjob.job_by_id(preview_job_id())
+ var/datum/role/job/previewJob = SSjob.job_by_id(preview_job_id())
- if((equip_preview_mob & EQUIP_PREVIEW_LOADOUT) && !(previewJob && (equip_preview_mob & EQUIP_PREVIEW_JOB) && (previewJob.type == /datum/job/station/ai || previewJob.type == /datum/job/station/cyborg)))
+ if((equip_preview_mob & EQUIP_PREVIEW_LOADOUT) && !(previewJob && (equip_preview_mob & EQUIP_PREVIEW_JOB) && (previewJob.type == /datum/role/job/station/ai || previewJob.type == /datum/role/job/station/cyborg)))
var/list/equipped_slots = list()
for(var/thing in gear)
var/datum/gear/G = gear_datums[thing]
diff --git a/code/modules/resleeving/infocore_records.dm b/code/modules/resleeving/infocore_records.dm
index db79d51f4b6..05579a117ad 100644
--- a/code/modules/resleeving/infocore_records.dm
+++ b/code/modules/resleeving/infocore_records.dm
@@ -39,7 +39,7 @@
//The mind!
mind_ref = mind
mindname = mind.name
- ckey = ckey(mind.key)
+ ckey = mind.ckey
cryo_at = 0
diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm
index bd529a0b1f4..2fc654096ef 100644
--- a/code/modules/resleeving/machines.dm
+++ b/code/modules/resleeving/machines.dm
@@ -585,7 +585,7 @@
occupant.confused = max(occupant.confused, confuse_amount)
occupant.eye_blurry = max(occupant.eye_blurry, blur_amount)
- if(occupant.mind && occupant.original_player && ckey(occupant.mind.key) != occupant.original_player)
+ if(occupant.mind && occupant.original_player && occupant.mind.ckey != occupant.original_player)
log_and_message_admins("is now a cross-sleeved character. Body originally belonged to [occupant.real_name]. Mind is now [occupant.mind.name].",occupant)
if(original_occupant)
diff --git a/code/modules/roles/role.dm b/code/modules/roles/role.dm
new file mode 100644
index 00000000000..af9a40bc7f8
--- /dev/null
+++ b/code/modules/roles/role.dm
@@ -0,0 +1,6 @@
+/**
+ * WIP
+ * TODO: unified role system
+ */
+/datum/role
+ abstract_type = /datum/role
diff --git a/code/modules/tgui/modules/ntos-only/cardmod.dm b/code/modules/tgui/modules/ntos-only/cardmod.dm
index 31ee07e01ac..60c3ad015cb 100644
--- a/code/modules/tgui/modules/ntos-only/cardmod.dm
+++ b/code/modules/tgui/modules/ntos-only/cardmod.dm
@@ -202,9 +202,9 @@
if(is_centcom)
access = get_centcom_access(t1)
else
- var/datum/job/jobdatum
- for(var/jobtype in typesof(/datum/job))
- var/datum/job/J = new jobtype
+ var/datum/role/job/jobdatum
+ for(var/jobtype in typesof(/datum/role/job))
+ var/datum/role/job/J = new jobtype
if(ckey(J.title) == ckey(t1))
jobdatum = J
break
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index ae3ba1edf6f..c4e5c7832eb 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -103,6 +103,7 @@
// #include "pills.dm"
// #include "plantgrowth_tests.dm"
// #include "projectiles.dm"
+#include "prototypes.dm"
// #include "reagent_id_typos.dm"
// #include "reagent_mod_expose.dm"
// #include "reagent_mod_procs.dm"
diff --git a/code/modules/unit_tests/mob/_mob.dm b/code/modules/unit_tests/mob/_mob.dm
index 4221cf340e1..250eab27bed 100644
--- a/code/modules/unit_tests/mob/_mob.dm
+++ b/code/modules/unit_tests/mob/_mob.dm
@@ -1,2 +1,3 @@
+#include "characteristics.dm"
#include "inventory.dm"
#include "sprite_accessories.dm"
diff --git a/code/modules/unit_tests/mob/characteristics.dm b/code/modules/unit_tests/mob/characteristics.dm
new file mode 100644
index 00000000000..e547a4dee69
--- /dev/null
+++ b/code/modules/unit_tests/mob/characteristics.dm
@@ -0,0 +1,2 @@
+/datum/unit_test/characteristics/Run()
+ // todo: uniqueness on ids, but not yet
diff --git a/code/modules/unit_tests/prototypes.dm b/code/modules/unit_tests/prototypes.dm
new file mode 100644
index 00000000000..193a6af2bf0
--- /dev/null
+++ b/code/modules/unit_tests/prototypes.dm
@@ -0,0 +1,18 @@
+/datum/unit_test/prototypes/Run()
+ var/list/id_cache = list()
+ var/list/type_cache = list()
+ for(var/datum/prototype/instance as anything in subtypesof(/datum/prototype))
+ if(initial(instance.abstract_type) == instance)
+ continue
+ // lazy is ignored
+ instance = new instance
+ if(instance.anonymous && instance.identifier)
+ Fail("[instance.type]: has identifier but is marked anonymous")
+ type_cache[instance] = instance
+ if(!instance.uid)
+ Fail("[instance.type]: no uid")
+ else if(id_cache[instance.uid])
+ Fail("[instance.type]: collides on uid [instance.uid] with [id_cache[instance.uid]:type].")
+ else
+ id_cache[instance.uid] = instance
+
diff --git a/config/entries/skills.txt b/config/entries/skills.txt
new file mode 100644
index 00000000000..2a9b2206c61
--- /dev/null
+++ b/config/entries/skills.txt
@@ -0,0 +1,5 @@
+## Do we have the characteristics system at all?
+CHARACTERISTICS_ENABLED
+
+## Are skills in use?
+# CHARACTERISTICS_ACTIVE
diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm
index b644dea06c9..4e15f9507a8 100644
--- a/maps/~map_system/maps.dm
+++ b/maps/~map_system/maps.dm
@@ -137,7 +137,7 @@ var/list/all_maps = list()
if(!map_levels)
map_levels = station_levels.Copy()
if(!allowed_jobs || !allowed_jobs.len)
- allowed_jobs = subtypesof(/datum/job)
+ allowed_jobs = subtypesof(/datum/role/job)
// Gets the current time on a current zlevel, and returns a time datum
/datum/map/proc/get_zlevel_time(var/z)
|