diff --git a/SQL/migrate-2023/V011__skills_education.sql b/SQL/migrate-2023/V011__skills_education.sql
new file mode 100644
index 00000000000..4ba8a8996f1
--- /dev/null
+++ b/SQL/migrate-2023/V011__skills_education.sql
@@ -0,0 +1,8 @@
+--
+-- Implemented in PR #?????.
+-- Add education and skills.
+--
+
+ALTER TABLE `ss13_characters` ADD COLUMN `education` VARCHAR(48) DEFAULT NULL AFTER `origin`;
+ALTER TABLE `ss13_characters` ADD COLUMN `skills` VARCHAR(256) DEFAULT NULL AFTER `education`;
+ALTER TABLE `ss13_characters` DROP COLUMN `home_system`;
diff --git a/aurorastation.dme b/aurorastation.dme
index 759cb38da8d..8614dd5c5b5 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -129,6 +129,7 @@
#include "code\__DEFINES\shuttle.dm"
#include "code\__DEFINES\si.dm"
#include "code\__DEFINES\singletons.dm"
+#include "code\__DEFINES\skills.dm"
#include "code\__DEFINES\smart_token_bucket.dm"
#include "code\__DEFINES\solar.dm"
#include "code\__DEFINES\sound.dm"
@@ -363,6 +364,7 @@
#include "code\controllers\subsystems\records.dm"
#include "code\controllers\subsystems\responseteam.dm"
#include "code\controllers\subsystems\runechat.dm"
+#include "code\controllers\subsystems\skills.dm"
#include "code\controllers\subsystems\skybox.dm"
#include "code\controllers\subsystems\sound_loops.dm"
#include "code\controllers\subsystems\sounds.dm"
@@ -537,6 +539,13 @@
#include "code\datums\repositories\singletons.dm"
#include "code\datums\repositories\sound_channels.dm"
#include "code\datums\repositories\unique.dm"
+#include "code\datums\skills\_skill_categories.dm"
+#include "code\datums\skills\_skills.dm"
+#include "code\datums\skills\combat\combat.dm"
+#include "code\datums\skills\everyday\service.dm"
+#include "code\datums\skills\occupational\engineering.dm"
+#include "code\datums\skills\occupational\medical.dm"
+#include "code\datums\skills\occupational\science.dm"
#include "code\datums\state_machine\state.dm"
#include "code\datums\state_machine\transition.dm"
#include "code\datums\tips\tips.dm"
@@ -1699,6 +1708,13 @@
#include "code\modules\background\citizenship\tajara.dm"
#include "code\modules\background\citizenship\unathi.dm"
#include "code\modules\background\citizenship\vaurca.dm"
+#include "code\modules\background\education\_education.dm"
+#include "code\modules\background\education\engineering.dm"
+#include "code\modules\background\education\medical.dm"
+#include "code\modules\background\education\misc.dm"
+#include "code\modules\background\education\science.dm"
+#include "code\modules\background\education\security.dm"
+#include "code\modules\background\education\service.dm"
#include "code\modules\background\origins\_origins.dm"
#include "code\modules\background\origins\origins\diona\biesel.dm"
#include "code\modules\background\origins\origins\diona\coalition.dm"
@@ -1864,6 +1880,7 @@
#include "code\modules\client\preference_setup\occupation\occupation.dm"
#include "code\modules\client\preference_setup\origin\origin.dm"
#include "code\modules\client\preference_setup\other\01_incidents.dm"
+#include "code\modules\client\preference_setup\skills\skills.dm"
#include "code\modules\client\verbs\ping.dm"
#include "code\modules\clothing\chameleon.dm"
#include "code\modules\clothing\clothing.dm"
@@ -2830,6 +2847,7 @@
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
#include "code\modules\mob\living\simple_animal\hostile\toy\mech.dm"
#include "code\modules\mob\living\simple_animal\mechanical\mechanical.dm"
+#include "code\modules\mob\skills\skills.dm"
#include "code\modules\modular_computers\_description.dm"
#include "code\modules\modular_computers\laptop_vendor.dm"
#include "code\modules\modular_computers\computers\modular_computer\core.dm"
diff --git a/code/__DEFINES/skills.dm b/code/__DEFINES/skills.dm
new file mode 100644
index 00000000000..f9a6a4c7759
--- /dev/null
+++ b/code/__DEFINES/skills.dm
@@ -0,0 +1,32 @@
+/// You don't know anything about this subject.
+#define SKILL_LEVEL_UNFAMILIAR 1
+/// You're familiar with this subject, either by reading into it or by doing some courses.
+#define SKILL_LEVEL_FAMILIAR 2
+/// You've been formally trained in this subject. Typically, this is the minimum level for a job.
+#define SKILL_LEVEL_TRAINED 3
+/// You have some training and a good amount of experience in this subject.
+#define SKILL_LEVEL_PROFESSIONAL 4
+
+/// An everyday skill is a skill that can be picked up normally. Think like mixing a drink, growing a plant, cooking, and so on.
+#define SKILL_CATEGORY_EVERYDAY "Everyday"
+ #define SKILL_SUBCATEGORY_SERVICE "Service"
+ #define SKILL_SUBCATEGORY_MUNDANE "Mundane"
+
+/// Occupational skills are the skills necessary for you to do your job to a minimum degree.
+#define SKILL_CATEGORY_OCCUPATIONAL "Occupational"
+ #define SKILL_SUBCATEGORY_MEDICAL "Medical"
+ #define SKILL_SUBCATEGORY_ENGINEERING "Engineering"
+ #define SKILL_SUBCATEGORY_SCIENCE "Science"
+
+///A combat skill is a skill that has a direct effect in combat. These have an increased cost.
+#define SKILL_CATEGORY_COMBAT "Combat"
+ #define SKILL_SUBCATEGORY_RANGED "Ranged"
+ #define SKILL_SUBCATEGORY_MELEE "Melee"
+
+#define BASE_SKILL_POINTS_COMBAT 4
+#define BASE_SKILL_POINTS_OCCUPATIONAL 8
+#define BASE_SKILL_POINTS_EVERYDAY 8
+
+#define SKILL_DIFFICULTY_MODIFIER_EASY 1
+#define SKILL_DIFFICULTY_MODIFIER_MEDIUM 2
+#define SKILL_DIFFICULTY_MODIFIER_HARD 4
diff --git a/code/controllers/subsystems/skills.dm b/code/controllers/subsystems/skills.dm
new file mode 100644
index 00000000000..b15d4e9936e
--- /dev/null
+++ b/code/controllers/subsystems/skills.dm
@@ -0,0 +1,34 @@
+SUBSYSTEM_DEF(skills)
+ name = "Skills"
+ flags = SS_NO_FIRE
+
+ /// This is essentially the list we use to read skills in the character setup.
+ var/list/skill_tree = list()
+ /// A map of all the skill levels to their definition.
+ var/list/skill_level_map = list(
+ "Unfamiliar" = "You don't know anything about this subject.",
+ "Familiar" = "You're familiar with this subject, either by reading into it or by doing some courses.",
+ "Trained" = "You've been formally trained in this subject. Typically, this is the minimum level for a job.",
+ "Professional" = "You have a lot of training and a good amount of experience in this subject."
+ )
+
+/datum/controller/subsystem/skills/Initialize()
+ // Initialize the skill category lists first.
+ // This creates linked lists as follows: "Science" -> empty list
+ for(var/singleton/skill_category/skill_category in GET_SINGLETON_SUBTYPE_LIST(/singleton/skill_category))
+ skill_tree[skill_category] = list()
+
+ // Now, initialize all the skills.
+ // What actually goes on here: we want a tree that we can traverse programmatically.
+ // To do that, we first of all make empty lists above with all the categories (they're singletons so we can easily iterate over them).
+ // Next, we add the empty subcategory lists if they're not present. At this point, the tree would look like "Combat" -> "Melee" -> empty list
+ // After that's done, if our skill is not present, add it to the empty list of the subcategory.
+ for(var/singleton/skill/skill in GET_SINGLETON_SUBTYPE_LIST(/singleton/skill))
+ var/singleton/skill_category/skill_category = GET_SINGLETON(skill.category)
+ if(!(skill.subcategory in skill_tree[skill_category]))
+ skill_tree[skill_category] |= skill.subcategory
+ skill_tree[skill_category][skill.subcategory] = list()
+
+ if(!(skill in skill_tree[skill_category][skill.subcategory]))
+ skill_tree[skill_category][skill.subcategory] |= skill
+ return SS_INIT_SUCCESS
diff --git a/code/datums/skills/_skill_categories.dm b/code/datums/skills/_skill_categories.dm
new file mode 100644
index 00000000000..683a62477b1
--- /dev/null
+++ b/code/datums/skills/_skill_categories.dm
@@ -0,0 +1,29 @@
+/singleton/skill_category
+ /// The name of the skill category.
+ var/name
+ /// The description and purpose of the skill category, displayed in the info tab.
+ var/desc
+ /// The hex colour of the skill category. Shows as background on the skills tab.
+ var/color
+ /// The number of base points a character gets in this category.
+ var/base_skill_points
+
+/singleton/skill_category/proc/calculate_skill_points(datum/species/species, age, singleton/origin_item/culture, singleton/origin_item/origin)
+ var/list/species_modifiers = species.modify_skill_points(src, age)
+ return base_skill_points * species_modifiers[name]
+
+/singleton/skill_category/everyday
+ name = SKILL_CATEGORY_EVERYDAY
+ desc = "An everyday skill is a skill that can be picked up normally. Think like mixing a drink, growing a plant, cooking, and so on."
+ base_skill_points = BASE_SKILL_POINTS_EVERYDAY
+
+/singleton/skill_category/occupational
+ name = SKILL_CATEGORY_OCCUPATIONAL
+ desc = "Occupational skills are the skills necessary for you to do your job. They typically lock certain aspects of your department if you aren't proficient enough."
+ base_skill_points = BASE_SKILL_POINTS_OCCUPATIONAL
+
+/singleton/skill_category/combat
+ name = SKILL_CATEGORY_COMBAT
+ desc = "A combat skill is a skill that has a direct effect in combat. These have an increased cost."
+ base_skill_points = BASE_SKILL_POINTS_COMBAT
+
diff --git a/code/datums/skills/_skills.dm b/code/datums/skills/_skills.dm
new file mode 100644
index 00000000000..1a9a558417a
--- /dev/null
+++ b/code/datums/skills/_skills.dm
@@ -0,0 +1,49 @@
+/singleton/skill
+ /// The displayed name of the skill.
+ var/name
+ /// A description of what this skill entails.
+ var/description
+ /// A detailed description of what a character should expect with their current level in this skill. Assoc list of skill level to string.
+ var/list/skill_level_descriptions = list(
+ SKILL_LEVEL_UNFAMILIAR = "You are clueless.",
+ SKILL_LEVEL_FAMILIAR = "You have read up on the subject or have prior real experience.",
+ SKILL_LEVEL_TRAINED = "You have received some degree of official training on the subject, whether through certifications or courses.",
+ SKILL_LEVEL_PROFESSIONAL = "You are an expert in this field, devoting many years of study or practice to it."
+ )
+ /// The maximum level someone with no education can reach in this skill. Typically, this should be FAMILIAR on occupational skills.
+ /// If null, then there is no cap.
+ var/uneducated_skill_cap
+ /// The maximum level this skill can reach.
+ var/maximum_level = SKILL_LEVEL_TRAINED
+ /// The category of this skill. Used for sorting, typically.
+ var/category
+ /// The sub-category of this skill. Used to better sort skills.
+ var/subcategory
+ /// The modifier for how difficult the skill is. Each level costs this much * the level.
+ var/skill_difficulty_modifier = SKILL_DIFFICULTY_MODIFIER_MEDIUM
+
+/**
+ * Returns the maximum level a character can have in this skill depending on education.
+ */
+/singleton/skill/proc/get_maximum_level(var/singleton/education/education)
+ if(!istype(education))
+ crash_with("SKILL: Invalid [education] fed to get_maximum_level!")
+
+ // If there is no uneducated skill cap, it means we can always pick the maximum level.
+ if(!uneducated_skill_cap)
+ return maximum_level
+
+ // Otherwise, we need to check the education...
+ if(type in education.skills)
+ return education.skills[type]
+
+
+ return uneducated_skill_cap
+
+/**
+ * Returns the cost of this skill, modified by its difficulty modifier.
+ */
+/singleton/skill/proc/get_cost(level)
+ if(level == SKILL_LEVEL_UNFAMILIAR) //thanks byond for not supporting index 0
+ return 0
+ return skill_difficulty_modifier * level
diff --git a/code/datums/skills/combat/combat.dm b/code/datums/skills/combat/combat.dm
new file mode 100644
index 00000000000..4e36eaef643
--- /dev/null
+++ b/code/datums/skills/combat/combat.dm
@@ -0,0 +1,25 @@
+/singleton/skill/unarmed_combat
+ name = "Unarmed Combat"
+ description = "Unarmed combat represents your training in hand-to-hand combat, or without a weapon."
+ skill_level_descriptions = list(
+ SKILL_LEVEL_UNFAMILIAR = "You have rarely, if ever, fought someone in your life. You are likely to crack under pressure, not land punches, and are physically \
+ a pushover in a real fight. Being shoved, grabbed, or moved is likely to be very dangerous for you.",
+ SKILL_LEVEL_FAMILIAR = "You have some experience throwing punches. You are no stranger to a close-quarters fight, though anyone with real training is likely to \
+ overwhelm you. You are not as likely to miss punches, and you are physically less likely to suffer being shoved, grabbed, or moved.",
+ SKILL_LEVEL_TRAINED = "You have been trained, whether by being in the military, taking close-quarters-combat classes or simply through experience, to both keep \
+ calm in a close-quarters fight and also fight well. You suffer no maluses to your close-quarters combat.",
+ )
+ category = /singleton/skill_category/combat
+ subcategory = SKILL_SUBCATEGORY_MELEE
+
+/singleton/skill/armed_combat
+ name = "Armed Combat"
+ description = "zomboid time"
+ category = /singleton/skill_category/combat
+ subcategory = SKILL_SUBCATEGORY_MELEE
+
+/singleton/skill/firearms
+ name = "Firearms"
+ description = "using guns. split this into close arms/longarms/special arms?"
+ category = /singleton/skill_category/combat
+ subcategory = SKILL_SUBCATEGORY_RANGED
diff --git a/code/datums/skills/everyday/service.dm b/code/datums/skills/everyday/service.dm
new file mode 100644
index 00000000000..47ac4cdc720
--- /dev/null
+++ b/code/datums/skills/everyday/service.dm
@@ -0,0 +1,20 @@
+/singleton/skill/mixing
+ name = "Mixing"
+ description = "valhalla skill"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ category = /singleton/skill_category/everyday
+ subcategory = SKILL_SUBCATEGORY_SERVICE
+
+/singleton/skill/cooking
+ name = "Cooking"
+ description = "Cooking Mama"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ category = /singleton/skill_category/everyday
+ subcategory = SKILL_SUBCATEGORY_SERVICE
+
+/singleton/skill/gardening
+ name = "Gardening"
+ description = "this is boring as shit"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ category = /singleton/skill_category/everyday
+ subcategory = SKILL_SUBCATEGORY_SERVICE
diff --git a/code/datums/skills/occupational/engineering.dm b/code/datums/skills/occupational/engineering.dm
new file mode 100644
index 00000000000..91da1afcf75
--- /dev/null
+++ b/code/datums/skills/occupational/engineering.dm
@@ -0,0 +1,35 @@
+/singleton/skill/electrical_engineering
+ name = "Electrical Engineering"
+ description = "Electrical engineering has to do with anything that involves wires and electricity. This includes things such as hacking doors, machines, but also \
+ laying down wires, or repairing wiring damage in prosthetics."
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_ENGINEERING
+
+/singleton/skill/mechanical_engineering
+ name = "Mechanical Engineering"
+ description = "Mechanical engineering has to do with general construction of objects, walls, windows, and so on. It is also necessary for the usage of heavy machinery \
+ such as emitters."
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_ENGINEERING
+
+/singleton/skill/atmospherics_systems
+ name = "Atmospherics Systems"
+ description = "Atmospherics systems involves the usage of atmospherics tooling and machinery, such as powered pumps, certain settings on air alarms, pipe wrenches, \
+ pipe layers, and pipe construction."
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_ENGINEERING
+
+/singleton/skill/reactor_systems
+ name = "Reactor Systems"
+ description = "Reactor systems envelops anything used for reactors, such as the computers and gyrotrons for the INDRA. It is also necessary to correctly interpret information \
+ from reactor monitoring programs."
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_ENGINEERING
diff --git a/code/datums/skills/occupational/medical.dm b/code/datums/skills/occupational/medical.dm
new file mode 100644
index 00000000000..7acaa54cf10
--- /dev/null
+++ b/code/datums/skills/occupational/medical.dm
@@ -0,0 +1,37 @@
+/singleton/skill/medicine
+ name = "Medicine"
+ description ="how to use health analyzers, ATKs, syringes"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_MEDICAL
+
+/singleton/skill/surgery
+ name = "Surgery"
+ description = "the one everyone wants to do in medical to be the protag"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_MEDICAL
+
+/singleton/skill/pharmacology
+ name = "Pharmacology"
+ description = "why are you playing pharma LOL"
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_MEDICAL
+
+/singleton/skill/anatomy
+ name = "Anatomy"
+ description = "this one lets you know what's wrong with people"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_MEDICAL
+
+/singleton/skill/forensics
+ name = "Forensics"
+ description = "forensics shit i guess"
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_MEDICAL
diff --git a/code/datums/skills/occupational/science.dm b/code/datums/skills/occupational/science.dm
new file mode 100644
index 00000000000..e02f8a361fb
--- /dev/null
+++ b/code/datums/skills/occupational/science.dm
@@ -0,0 +1,37 @@
+/singleton/skill/research
+ name = "Research"
+ description = "it's generic research shit, using the protolathe, destructive analyzers, etc"
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_SCIENCE
+
+/singleton/skill/robotics
+ name = "Robotics"
+ description = "Robotics is well you know what the fuck it is man"
+ maximum_level = SKILL_LEVEL_PROFESSIONAL
+ uneducated_skill_cap = SKILL_LEVEL_FAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_SCIENCE
+
+/singleton/skill/xenobotany
+ name = "Xenobotany"
+ description = "Xenobotany is the creation and study of new or alien genomes of plants. It is necessary to be able to properly splice and process them."
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_SCIENCE
+
+/singleton/skill/archaeology
+ name = "Xenoarchaeology"
+ description = "Xenoarchaeology is the study of alien civilizations, artifacts, architecture, and so on. It is necessary for the unearthing and cataloguing of \
+ alien artifacts."
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_SCIENCE
+
+/singleton/skill/xenobiology
+ name = "Xenobiology"
+ description = "Xenobiology is the study of the research and cataloguing of alien lifeforms. It is necessary not only for the proper detailing of \
+ alien creatures, but also for their processing, such as with slimes."
+ uneducated_skill_cap = SKILL_LEVEL_UNFAMILIAR
+ category = /singleton/skill_category/occupational
+ subcategory = SKILL_SUBCATEGORY_SCIENCE
diff --git a/code/modules/background/education/_education.dm b/code/modules/background/education/_education.dm
new file mode 100644
index 00000000000..b4fadedcf6e
--- /dev/null
+++ b/code/modules/background/education/_education.dm
@@ -0,0 +1,18 @@
+/singleton/education
+ /// The name of this education type.
+ var/name
+ /// The description of this education type. It should ideally match what's on the Aurora wiki, but from an IC point of view.
+ var/description
+ /// Age requirement for this education. Should match the job this is intended for. This doesn't need to be here per se, but it helps to filter results.
+ var/list/minimum_character_age
+ /// The jobs this education type allows you to access.
+ var/list/jobs
+ /// The given skills for this education type. Linked list of skill type to level.
+ var/list/skills
+ /// Species that CANNOT take this education, if necessary. This is a list of names, must match what's in the species pref variable.
+ /// Empty list means no restrictions.
+ var/list/species_restriction
+
+/singleton/education/high_school
+ name = "High School Diploma"
+ description = "Your character has a high school diploma."
diff --git a/code/modules/background/education/engineering.dm b/code/modules/background/education/engineering.dm
new file mode 100644
index 00000000000..508d6355020
--- /dev/null
+++ b/code/modules/background/education/engineering.dm
@@ -0,0 +1,86 @@
+/singleton/education/mechanical_engineering_degree
+ name = "Mechanical Engineering Degree"
+ description = "You are at least 25 years of age, with a Bachelor's degree in Mechanical Engineering. You specialize in constructing structural systems, lathing, \
+ and the more manual pleasures of engineering, such as welding and wrenching."
+ jobs = list("Engineer")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/atmospherics_systems = SKILL_LEVEL_TRAINED,
+ /singleton/skill/reactor_systems = SKILL_LEVEL_TRAINED
+ )
+
+/singleton/education/electrical_engineering
+ name = "Electrical Engineering Degree"
+ description = "You are at least 25 years of age, with a Bachelor's degree in Electrical Engineering. You specialize in cutting wires, electronic circuits, and other \
+ electrical systems."
+ jobs = list("Engineer")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/atmospherics_systems = SKILL_LEVEL_TRAINED,
+ /singleton/skill/reactor_systems = SKILL_LEVEL_TRAINED
+ )
+
+/singleton/education/atmospherics_engineer
+ name = "Atmospherics Systems Degree"
+ description = "You are at least 25 years of age, with a Bachelor's degree in Atmospherics Systems. You specialize in everything to do with atmospherics systems, \
+ whether that's the delivery of gases, usage of atmospherics machines, or simply how to use a pipe wrench."
+ jobs = list("Atmospherics Technician")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/atmospherics_systems = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/reactor_systems = SKILL_LEVEL_TRAINED
+ )
+
+
+/singleton/education/reactors_engineer
+ name = "Reactor Systems Degree"
+ description = "You are at least 25 years of age, with a Bachelor's degree in Reactor Systems. You specialize in everything to do with reactors systems, \
+ whether you are looking at a Supermatter, an INDRA reactor, or a combustion chamber."
+ jobs = list("Engineer")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/atmospherics_systems = SKILL_LEVEL_TRAINED,
+ /singleton/skill/reactor_systems = SKILL_LEVEL_PROFESSIONAL
+ )
+
+/singleton/education/experienced_engineer
+ name = "Engineering Certification"
+ description = "You are at least 25 years of age. You may not have an Engineering degree, but you had enough experience for the Conglomerate to validate it instead \
+ of a degree. You do not have the same specialization as your fellow Engineers with a degree, making up for it by being a jack of all trades. \
+ You could probably fix a car, whereas they might not be able to."
+ jobs = list("Engineer")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_TRAINED,
+ /singleton/skill/atmospherics_systems = SKILL_LEVEL_TRAINED,
+ /singleton/skill/reactor_systems = SKILL_LEVEL_TRAINED
+ )
diff --git a/code/modules/background/education/medical.dm b/code/modules/background/education/medical.dm
new file mode 100644
index 00000000000..58db4c6991d
--- /dev/null
+++ b/code/modules/background/education/medical.dm
@@ -0,0 +1,75 @@
+/singleton/education/surgical_degree
+ name = "MD, Surgery Track"
+ description = "You are 30 years of age or older, with an applicable MD from accredited school and you have completed 2 years of residency at an \
+ accredited hospital or clinic."
+ jobs = list("Surgeon")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 30,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/surgery = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/medicine = SKILL_LEVEL_TRAINED,
+ /singleton/skill/anatomy = SKILL_LEVEL_TRAINED
+ )
+
+/singleton/education/physician_degree
+ name = "MD, Physician Track"
+ description = "You are at least 30 years of age, with an applicable MD from an accredited school and you have completed 2 years of residency at an \
+ accredited hospital or clinic."
+ jobs = list("Physician")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 30,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/surgery = SKILL_LEVEL_TRAINED,
+ /singleton/skill/medicine = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/anatomy = SKILL_LEVEL_TRAINED
+ )
+
+/singleton/education/pharmacist_degree
+ name = "Doctor of Pharmacy"
+ description = "You are at least 25 years of age, with an applicable Masters from an accredited school, along with 2 years of residency at an \
+ accredited hospital or clinic."
+ jobs = list("Pharmacist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 30,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/medicine = SKILL_LEVEL_TRAINED,
+ /singleton/skill/anatomy = SKILL_LEVEL_TRAINED
+ )
+
+/singleton/education/psychologist_degree
+ name = "Psychology PhD"
+ description = "You are at least 30 years of age, with a PhD from an accredited university in an applicable field."
+ jobs = list("Psychologist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/pharmacology = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/medicine = SKILL_LEVEL_TRAINED,
+ /singleton/skill/anatomy = SKILL_LEVEL_TRAINED
+ )
+
+/singleton/education/paramedic
+ name = "Paramedic Certification"
+ description = "You are at least 18 years of age, with a Paramedic certification."
+ jobs = list("Paramedic")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 18,
+ SPECIES_SKRELL = 55,
+ SPECIES_SKRELL_AXIORI = 55
+ )
+ skills = list(
+ /singleton/skill/medicine = SKILL_LEVEL_TRAINED,
+ /singleton/skill/anatomy = SKILL_LEVEL_TRAINED
+ )
diff --git a/code/modules/background/education/misc.dm b/code/modules/background/education/misc.dm
new file mode 100644
index 00000000000..d2986659294
--- /dev/null
+++ b/code/modules/background/education/misc.dm
@@ -0,0 +1,19 @@
+/singleton/education/finance
+ name = "Finance Degree"
+ description = "You are at least 21 years of age. You graduated in a field related to finance, whether that is Business Management or something else. \
+ You can count very well, and you have a statistician's eye - especially for credits."
+ minimum_character_age = list(
+ SPECIES_HUMAN = 21,
+ SPECIES_SKRELL = 55,
+ SPECIES_SKRELL_AXIORI = 55
+ )
+
+/singleton/education/arts
+ name = "Humanities Degree"
+ description = "You are at least 21 years of age. You graduated in a field related to the humanities, whether that is music, arts, linguistics, or a bachelor's in \
+ psychology. You are likely very well-read on foreign cultures and on the human mind."
+ minimum_character_age = list(
+ SPECIES_HUMAN = 21,
+ SPECIES_SKRELL = 55,
+ SPECIES_SKRELL_AXIORI = 55
+ )
diff --git a/code/modules/background/education/science.dm b/code/modules/background/education/science.dm
new file mode 100644
index 00000000000..218ff30e23e
--- /dev/null
+++ b/code/modules/background/education/science.dm
@@ -0,0 +1,81 @@
+/singleton/education/research_and_development
+ name = "Research & Development Degree"
+ description = "You are at least 30 years of age, with a PhD in an applicable field for Research and Development. This may range from a Firearms Engineering degree \
+ to a Bluespace Engineering degree or even Aerospace Engineering. Space is the limit for your research."
+ jobs = list("Scientist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 30,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/research = SKILL_LEVEL_PROFESSIONAL
+ )
+
+/singleton/education/robotics_masters
+ name = "Robotics Master's"
+ description = "You are at least 25 years of age, with a Master's in Robotics. Your specialization is in building and repairing IPCs and other smaller robots, though \
+ you are also capable of building exoskeletons and mechs. You're also proficient with some more basic engineering skills, though you prefer the \
+ theoretical aspect and robots in general."
+ jobs = list("Roboticist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/research = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/robotics = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/surgery = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_FAMILIAR,
+ )
+
+/singleton/education/mechatronics_masters
+ name = "Mechatronics Master's"
+ description = "You are at least 25 years of age, with a Master's in Mechatronics. Your specialization is with building large human-sized exoskeletons and mechs, though \
+ you've also learnt how to repair IPCs and simpler robots as well. You're more proficient with the mechanical aspects of engineering as well."
+ jobs = list("Roboticist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/research = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/robotics = SKILL_LEVEL_TRAINED,
+ /singleton/skill/surgery = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/electrical_engineering = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/mechanical_engineering = SKILL_LEVEL_PROFESSIONAL
+ )
+
+/singleton/education/xenobotany_degree
+ name = "Xenobotany Degree"
+ description = "You are at least 30 years of age, with a PhD in Xenobotany. Your specialization is with discovering, sequencing, and creating alien flora... though \
+ you can also grow some potatoes in your spare time."
+ jobs = list("Xenobotanist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 30,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/research = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/gardening = SKILL_LEVEL_PROFESSIONAL,
+ /singleton/skill/xenobotany = SKILL_LEVEL_PROFESSIONAL
+ )
+
+
+/singleton/education/xenobiology_degree
+ name = "Xenobiology Degree"
+ description = "You are at least 30 years of age, with a PhD in Xenobiology. Your specialization is with discovering and cataloguing alien animals."
+ jobs = list("Xenobiologist")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 30,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/research = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/xenobiology = SKILL_LEVEL_PROFESSIONAL
+ )
diff --git a/code/modules/background/education/security.dm b/code/modules/background/education/security.dm
new file mode 100644
index 00000000000..ed51254aaba
--- /dev/null
+++ b/code/modules/background/education/security.dm
@@ -0,0 +1,24 @@
+/singleton/education/forensics_degree
+ name = "Forensics Science Degree"
+ description = "You are 25 years of age or older, with a degree in Forensics Science. You specialize in the medical procedures required to understand why someone died."
+ jobs = list("Investigator")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 25,
+ SPECIES_SKRELL = 60,
+ SPECIES_SKRELL_AXIORI = 60
+ )
+ skills = list(
+ /singleton/skill/surgery = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/medicine = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/anatomy = SKILL_LEVEL_FAMILIAR,
+ /singleton/skill/forensics = SKILL_LEVEL_PROFESSIONAL
+ )
+
+/singleton/education/protagonist
+ name = "Protagonist Degree"
+ description = "you are the protagonist of aurora"
+ skills = list(
+ /singleton/skill/unarmed_combat = SKILL_LEVEL_TRAINED,
+ /singleton/skill/armed_combat = SKILL_LEVEL_TRAINED,
+ /singleton/skill/firearms = SKILL_LEVEL_TRAINED
+ )
diff --git a/code/modules/background/education/service.dm b/code/modules/background/education/service.dm
new file mode 100644
index 00000000000..33c2327f559
--- /dev/null
+++ b/code/modules/background/education/service.dm
@@ -0,0 +1,67 @@
+/singleton/education/mixing
+ name = "Mixing License"
+ description = "You paid for and successfully attained an Idris mixing license, making you officially a specialist in mixing cocktails, mocktails, and whatever else. \
+ Time to mix drinks and save lives."
+ jobs = list("Bartender")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 18,
+ SPECIES_SKRELL = 50,
+ SPECIES_SKRELL_AXIORI = 50
+ )
+ skills = list(
+ /singleton/skill/mixing = SKILL_LEVEL_PROFESSIONAL,
+ )
+
+/singleton/education/cooking_degree
+ name = "Culinary Arts Degree"
+ description = "You obtained a degree in Culinary Arts, making you an artist at cooking. Pancakes, steaks, and cultural food - you've learnt about how to cook it all."
+ jobs = list("Chef")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 18,
+ SPECIES_SKRELL = 50,
+ SPECIES_SKRELL_AXIORI = 50
+ )
+ skills = list(
+ /singleton/skill/cooking = SKILL_LEVEL_PROFESSIONAL,
+ )
+
+/singleton/education/cooking_certification
+ name = "Culinary Certification"
+ description = "You obtained an Idris certification to work as a cook. You won't be as good as a professional chef, but you can pour your soul out into a good breakfast."
+ jobs = list("Chef")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 18,
+ SPECIES_SKRELL = 50,
+ SPECIES_SKRELL_AXIORI = 50
+ )
+ skills = list(
+ /singleton/skill/cooking = SKILL_LEVEL_TRAINED,
+ )
+
+/singleton/education/hydroponics_degree
+ name = "Hydroponics Degree"
+ description = "You obtained a degree to work as a hydroponicist or gardener. Your degree covered both manual and hydroponics gardening of just about every plant known to your species, \
+ alongside plants that are more typical to other cultures in the Spur."
+ jobs = list("Gardener")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 18,
+ SPECIES_SKRELL = 50,
+ SPECIES_SKRELL_AXIORI = 50
+ )
+ skills = list(
+ /singleton/skill/gardening = SKILL_LEVEL_PROFESSIONAL,
+ )
+
+/singleton/education/hydroponics_certification
+ name = "Hydroponics Certification"
+ description = "You obtained an Idris certification to work as a hydroponicist or gardener. Although you might not be as much of an expert as someone with a Hydroponics degree, \
+ you can still plant just about everything if you give it your all."
+ jobs = list("Gardener")
+ minimum_character_age = list(
+ SPECIES_HUMAN = 18,
+ SPECIES_SKRELL = 50,
+ SPECIES_SKRELL_AXIORI = 50
+ )
+ skills = list(
+ /singleton/skill/gardening = SKILL_LEVEL_TRAINED,
+ )
diff --git a/code/modules/background/origins/_origins.dm b/code/modules/background/origins/_origins.dm
index 4004d45da5a..d54a029677a 100644
--- a/code/modules/background/origins/_origins.dm
+++ b/code/modules/background/origins/_origins.dm
@@ -1,11 +1,15 @@
/singleton/origin_item
var/name = "generic origin item"
var/desc = DESC_PARENT
- var/important_information //Big red text. Should only be used if not following it would incur a bwoink.
+ /// Big red text. Should only be used if not following it would incur a bwoink.
+ var/important_information
+ /// A list of the origin traits given by this culture item.
var/list/origin_traits = list()
/// Format for the following list: "Characters from this origin: [list entry], [list entry]."
/// One list item per trait.
var/list/origin_traits_descriptions = list()
+ /// A list of skills given by this origin. Assoc list of skill singleton type to level.
+ var/list/given_skills = list()
/singleton/origin_item/culture
name = "generic culture"
diff --git a/code/modules/client/preference_setup/origin/origin.dm b/code/modules/client/preference_setup/origin/origin.dm
index 378230ebbfd..02ab8293337 100644
--- a/code/modules/client/preference_setup/origin/origin.dm
+++ b/code/modules/client/preference_setup/origin/origin.dm
@@ -70,27 +70,32 @@
var/datum/species/S = GLOB.all_species[pref.species]
if(!istext(pref.culture) || !ispath(text2path(pref.culture), /singleton/origin_item/culture))
var/singleton/origin_item/culture/CI = S.possible_cultures[1]
- pref.culture = "[CI]"
+ pref.culture = "[CI.type]"
+
var/singleton/origin_item/culture/our_culture = GET_SINGLETON(text2path(pref.culture))
if(!istext(pref.origin) || !ispath(text2path(pref.origin), /singleton/origin_item/origin))
var/singleton/origin_item/origin/OI = pick(our_culture.possible_origins)
- pref.origin = "[OI]"
+ pref.origin = "[OI.type]"
else
var/singleton/origin_item/origin/origin_check = text2path(pref.origin)
if(!(origin_check in our_culture.possible_origins))
to_client_chat(SPAN_WARNING("Your origin has been reset due to it being incompatible with your culture!"))
var/singleton/origin_item/origin/OI = pick(our_culture.possible_origins)
- pref.origin = "[OI]"
+ pref.origin = "[OI.type]"
+
var/singleton/origin_item/origin/our_origin = GET_SINGLETON(text2path(pref.origin))
if(!(pref.citizenship in our_origin.possible_citizenships))
to_client_chat(SPAN_WARNING("Your previous citizenship is invalid for this origin! Resetting."))
pref.citizenship = our_origin.possible_citizenships[1]
+
if(!(pref.religion in our_origin.possible_religions))
to_client_chat(SPAN_WARNING("Your previous religion is invalid for this origin! Resetting."))
pref.religion = our_origin.possible_religions[1]
+
if(!(pref.accent in our_origin.possible_accents))
to_client_chat(SPAN_WARNING("Your previous accent is invalid for this origin! Resetting."))
pref.accent = our_origin.possible_accents[1]
+
pref.economic_status = sanitize_inlist(pref.economic_status, ECONOMIC_POSITIONS, initial(pref.economic_status))
/datum/category_item/player_setup_item/origin/content(var/mob/user)
@@ -114,6 +119,7 @@
if(OR.important_information)
dat += "
- [OR.important_information]"
dat += "
[skill_category.name] ([calculate_remaining_skill_points(skill_category)] points remaining)" + dat += " | |||
---|---|---|---|
[subcategory] |