diff --git a/code/datums/mind.dm b/code/datums/mind/_mind.dm
similarity index 59%
rename from code/datums/mind.dm
rename to code/datums/mind/_mind.dm
index 1fac0e75d4b..84985943388 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind/_mind.dm
@@ -30,15 +30,20 @@
*/
/datum/mind
+ /// Key of the mob
var/key
- var/name //replaces mob/var/original_name
- var/ghostname //replaces name for observers name if set
+ /// The name linked to this mind
+ var/name
+ /// replaces name for observers name if set
+ var/ghostname
+ /// Current mob this mind datum is attached to
var/mob/living/current
+ /// Is this mind active?
var/active = FALSE
- ///a list of /datum/memories. assoc type of memory = memory datum. only one type of memory will be stored, new ones of the same type overriding the last.
+ /// a list of /datum/memories. assoc type of memory = memory datum. only one type of memory will be stored, new ones of the same type overriding the last.
var/list/memories = list()
- ///reference to the memory panel tgui
+ /// reference to the memory panel tgui
var/datum/memory_panel/memory_panel
/// Job datum indicating the mind's role. This should always exist after initialization, as a reference to a singleton.
@@ -46,11 +51,14 @@
var/special_role
var/list/restricted_roles = list()
+ /// Martial art on this mind
var/datum/martial_art/martial_art
var/static/default_martial_art = new/datum/martial_art
- var/miming = FALSE // Mime's vow of silence
+ /// Mime's vow of silence
+ var/miming = FALSE
+ /// List of antag datums on this mind
var/list/antag_datums
- ///this mind's ANTAG_HUD should have this icon_state
+ /// this mind's ANTAG_HUD should have this icon_state
var/antag_hud_icon_state = null
///this mind's antag HUD
var/datum/atom_hud/alternate_appearance/basic/antagonist_hud/antag_hud = null
@@ -61,11 +69,12 @@
var/datum/language_holder/language_holder
var/unconvertable = FALSE
var/late_joiner = FALSE
- ///has this mind ever been an AI
+ /// has this mind ever been an AI
var/has_ever_been_ai = FALSE
var/last_death = 0
- var/force_escaped = FALSE // Set by Into The Sunset command of the shuttle manipulator
+ /// Set by Into The Sunset command of the shuttle manipulator
+ var/force_escaped = FALSE
var/list/learned_recipes //List of learned recipe TYPES.
@@ -187,298 +196,11 @@
/datum/mind/proc/set_original_character(new_original_character)
original_character = WEAKREF(new_original_character)
-/datum/mind/proc/init_known_skills()
- for (var/type in GLOB.skill_types)
- known_skills[type] = list(SKILL_LEVEL_NONE, 0)
-
-///Return the amount of EXP needed to go to the next level. Returns 0 if max level
-/datum/mind/proc/exp_needed_to_level_up(skill)
- var/lvl = update_skill_level(skill)
- if (lvl >= length(SKILL_EXP_LIST)) //If we're already past the last exp threshold
- return 0
- return SKILL_EXP_LIST[lvl+1] - known_skills[skill][SKILL_EXP]
-
-///Adjust experience of a specific skill
-/datum/mind/proc/adjust_experience(skill, amt, silent = FALSE, force_old_level = 0)
- var/datum/skill/S = GetSkillRef(skill)
- var/old_level = force_old_level ? force_old_level : known_skills[skill][SKILL_LVL] //Get current level of the S skill
- experience_multiplier = initial(experience_multiplier)
- for(var/key in experience_multiplier_reasons)
- experience_multiplier += experience_multiplier_reasons[key]
- known_skills[skill][SKILL_EXP] = max(0, known_skills[skill][SKILL_EXP] + amt*experience_multiplier) //Update exp. Prevent going below 0
- known_skills[skill][SKILL_LVL] = update_skill_level(skill)//Check what the current skill level is based on that skill's exp
- if(silent)
- return
- if(known_skills[skill][SKILL_LVL] > old_level)
- S.level_gained(src, known_skills[skill][SKILL_LVL], old_level)
- else if(known_skills[skill][SKILL_LVL] < old_level)
- S.level_lost(src, known_skills[skill][SKILL_LVL], old_level)
-
-///Set experience of a specific skill to a number
-/datum/mind/proc/set_experience(skill, amt, silent = FALSE)
- var/old_level = known_skills[skill][SKILL_EXP]
- known_skills[skill][SKILL_EXP] = amt
- adjust_experience(skill, 0, silent, old_level) //Make a call to adjust_experience to handle updating level
-
-///Set level of a specific skill
-/datum/mind/proc/set_level(skill, newlevel, silent = FALSE)
- var/oldlevel = get_skill_level(skill)
- var/difference = SKILL_EXP_LIST[newlevel] - SKILL_EXP_LIST[oldlevel]
- adjust_experience(skill, difference, silent)
-
-///Check what the current skill level is based on that skill's exp
-/datum/mind/proc/update_skill_level(skill)
- var/i = 0
- for (var/exp in SKILL_EXP_LIST)
- i ++
- if (known_skills[skill][SKILL_EXP] >= SKILL_EXP_LIST[i])
- continue
- return i - 1 //Return level based on the last exp requirement that we were greater than
- return i //If we had greater EXP than even the last exp threshold, we return the last level
-
-///Gets the skill's singleton and returns the result of its get_skill_modifier
-/datum/mind/proc/get_skill_modifier(skill, modifier)
- var/datum/skill/S = GetSkillRef(skill)
- return S.get_skill_modifier(modifier, known_skills[skill][SKILL_LVL])
-
-///Gets the player's current level number from the relevant skill
-/datum/mind/proc/get_skill_level(skill)
- return known_skills[skill][SKILL_LVL]
-
-///Gets the player's current exp from the relevant skill
-/datum/mind/proc/get_skill_exp(skill)
- return known_skills[skill][SKILL_EXP]
-
-/datum/mind/proc/get_skill_level_name(skill)
- var/level = get_skill_level(skill)
- return SSskills.level_names[level]
-
-/datum/mind/proc/print_levels(user)
- var/list/shown_skills = list()
- for(var/i in known_skills)
- if(known_skills[i][SKILL_LVL] > SKILL_LEVEL_NONE) //Do we actually have a level in this?
- shown_skills += i
- if(!length(shown_skills))
- to_chat(user, span_notice("You don't seem to have any particularly outstanding skills."))
- return
- var/msg = "[span_info("Your skills")]\n"
- for(var/i in shown_skills)
- var/datum/skill/the_skill = i
- msg += "[initial(the_skill.name)] - [get_skill_level_name(the_skill)]\n"
- msg += ""
- to_chat(user, examine_block(msg))
-
/datum/mind/proc/set_death_time()
SIGNAL_HANDLER
last_death = world.time
-// Datum antag mind procs
-
-/datum/mind/proc/add_antag_datum(datum_type_or_instance, team)
- if(!datum_type_or_instance)
- return
- var/datum/antagonist/A
- if(!ispath(datum_type_or_instance))
- A = datum_type_or_instance
- if(!istype(A))
- return
- else
- A = new datum_type_or_instance()
- //Choose snowflake variation if antagonist handles it
- var/datum/antagonist/S = A.specialization(src)
- if(S && S != A)
- qdel(A)
- A = S
- if(!A.can_be_owned(src))
- qdel(A)
- return
- A.owner = src
- LAZYADD(antag_datums, A)
- A.create_team(team)
- var/datum/team/antag_team = A.get_team()
- if(antag_team)
- antag_team.add_member(src)
- INVOKE_ASYNC(A, /datum/antagonist.proc/on_gain)
- log_game("[key_name(src)] has gained antag datum [A.name]([A.type]).")
- handle_exploitables() //SKYRAT EDIT ADDITION - EXPLOITABLES MENU
- return A
-
-/datum/mind/proc/remove_antag_datum(datum_type)
- if(!datum_type)
- return
- var/datum/antagonist/A = has_antag_datum(datum_type)
- if(A)
- A.on_removal()
-
- return TRUE
-
-
-/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
- for(var/a in antag_datums)
- var/datum/antagonist/A = a
- A.on_removal()
-
-/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
- if(!datum_type)
- return
- for(var/a in antag_datums)
- var/datum/antagonist/A = a
- if(check_subtypes && istype(A, datum_type))
- return A
- else if(A.type == datum_type)
- return A
-
-/*
- Removes antag type's references from a mind.
- objectives, uplinks, powers etc are all handled.
-*/
-
-/datum/mind/proc/remove_changeling()
- var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
- if(C)
- remove_antag_datum(/datum/antagonist/changeling)
- special_role = null
-
-/datum/mind/proc/remove_traitor()
- remove_antag_datum(/datum/antagonist/traitor)
-
-/datum/mind/proc/remove_nukeop()
- var/datum/antagonist/nukeop/nuke = has_antag_datum(/datum/antagonist/nukeop,TRUE)
- if(nuke)
- remove_antag_datum(nuke.type)
- special_role = null
-
-/datum/mind/proc/remove_wizard()
- remove_antag_datum(/datum/antagonist/wizard)
- special_role = null
-
-/datum/mind/proc/remove_rev()
- var/datum/antagonist/rev/rev = has_antag_datum(/datum/antagonist/rev)
- if(rev)
- remove_antag_datum(rev.type)
- special_role = null
-
-
-/datum/mind/proc/remove_antag_equip()
- var/list/Mob_Contents = current.get_contents()
- for(var/obj/item/I in Mob_Contents)
- var/datum/component/uplink/O = I.GetComponent(/datum/component/uplink) //Todo make this reset signal
- if(O)
- O.unlock_code = null
-
-/// Remove the antagonists that should not persist when being borged
-/datum/mind/proc/remove_antags_for_borging()
- remove_antag_datum(/datum/antagonist/cult)
-
- var/datum/antagonist/rev/revolutionary = has_antag_datum(/datum/antagonist/rev)
- revolutionary?.remove_revolutionary(borged = TRUE)
-
-/**
- * ## give_uplink
- *
- * A mind proc for giving anyone an uplink.
- * arguments:
- * * silent: if this should send a message to the mind getting the uplink. traitors do not use this silence, but the silence var on their antag datum.
- * * antag_datum: the antag datum of the uplink owner, for storing it in antag memory. optional!
- */
-/datum/mind/proc/give_uplink(silent = FALSE, datum/antagonist/antag_datum)
- if(!current)
- return
- var/mob/living/carbon/human/traitor_mob = current
- if (!istype(traitor_mob))
- return
-
- var/list/all_contents = traitor_mob.get_all_contents()
- var/obj/item/modular_computer/tablet/pda/PDA = locate() in all_contents
- var/obj/item/radio/R = locate() in all_contents
- var/obj/item/pen/P
-
- if (PDA) // Prioritize PDA pen, otherwise the pocket protector pens will be chosen, which causes numerous ahelps about missing uplink
- P = locate() in PDA
- if (!P) // If we couldn't find a pen in the PDA, or we didn't even have a PDA, do it the old way
- P = locate() in all_contents
-
- var/obj/item/uplink_loc
- var/implant = FALSE
-
- var/uplink_spawn_location = traitor_mob.client?.prefs?.read_preference(/datum/preference/choiced/uplink_location)
- switch (uplink_spawn_location)
- if(UPLINK_PDA)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = R
- if(!uplink_loc)
- uplink_loc = P
- if(UPLINK_RADIO)
- uplink_loc = R
- if(!uplink_loc)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = P
- if(UPLINK_PEN)
- uplink_loc = P
- if(UPLINK_IMPLANT)
- implant = TRUE
-
- if(!uplink_loc) // We've looked everywhere, let's just implant you
- implant = TRUE
-
- if(implant)
- var/obj/item/implant/uplink/starting/new_implant = new(traitor_mob)
- new_implant.implant(traitor_mob, null, silent = TRUE)
- if(!silent)
- to_chat(traitor_mob, span_boldnotice("Your Syndicate Uplink has been cunningly implanted in you, for a small TC fee. Simply trigger the uplink to access it."))
- return new_implant
-
- . = uplink_loc
- var/unlock_text
- var/datum/component/uplink/new_uplink = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
- if(!new_uplink)
- CRASH("Uplink creation failed.")
- new_uplink.setup_unlock_code()
- new_uplink.uplink_handler.owner = traitor_mob.mind
- new_uplink.uplink_handler.assigned_role = traitor_mob.mind.assigned_role.title
- new_uplink.uplink_handler.assigned_species = traitor_mob.dna.species.id
- if(uplink_loc == R)
- unlock_text = "Your Uplink is cunningly disguised as your [R.name]. Simply dial the frequency [format_frequency(new_uplink.unlock_code)] to unlock its hidden features."
- else if(uplink_loc == PDA)
- unlock_text = "Your Uplink is cunningly disguised as your [PDA.name]. Simply enter the code \"[new_uplink.unlock_code]\" into the ring tone selection to unlock its hidden features."
- else if(uplink_loc == P)
- unlock_text = "Your Uplink is cunningly disguised as your [P.name]. Simply twist the top of the pen [english_list(new_uplink.unlock_code)] from its starting position to unlock its hidden features."
- new_uplink.unlock_text = unlock_text
- if(!silent)
- to_chat(traitor_mob, span_boldnotice(unlock_text))
- if(antag_datum)
- antag_datum.antag_memory += new_uplink.unlock_note + "
"
-
-
-//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
-
-/datum/mind/proc/enslave_mind_to_creator(mob/living/creator)
- if(IS_CULTIST(creator))
- add_antag_datum(/datum/antagonist/cult)
-
- else if(IS_REVOLUTIONARY(creator))
- var/datum/antagonist/rev/converter = creator.mind.has_antag_datum(/datum/antagonist/rev,TRUE)
- converter.add_revolutionary(src,FALSE)
-
- else if(IS_NUKE_OP(creator))
- var/datum/antagonist/nukeop/converter = creator.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
- var/datum/antagonist/nukeop/N = new()
- N.send_to_spawnpoint = FALSE
- N.nukeop_outfit = null
- add_antag_datum(N,converter.nuke_team)
-
- enslaved_to = creator
-
- current.faction |= creator.faction
- creator.faction |= current.faction
-
- if(creator.mind.special_role)
- message_admins("[ADMIN_LOOKUPFLW(current)] has been created by [ADMIN_LOOKUPFLW(creator)], an antagonist.")
- to_chat(current, span_userdanger("Despite your creator's current allegiances, your true master remains [creator.real_name]. If their loyalties change, so do yours. This will never change unless your creator's body is destroyed."))
-
/datum/mind/Topic(href, href_list)
if(!check_rights(R_ADMIN))
return
@@ -737,74 +459,6 @@
usr = current
traitor_panel()
-
-/datum/mind/proc/get_all_objectives()
- var/list/all_objectives = list()
- for(var/datum/antagonist/A in antag_datums)
- all_objectives |= A.objectives
- return all_objectives
-
-/datum/mind/proc/announce_objectives()
- var/obj_count = 1
- to_chat(current, span_notice("Your current objectives:"))
- for(var/datum/objective/objective as anything in get_all_objectives())
- to_chat(current, "[objective.objective_name] #[obj_count]: [objective.explanation_text]")
- obj_count++
-
-/datum/mind/proc/find_syndicate_uplink(check_unlocked)
- var/list/L = current.get_all_contents()
- for (var/i in L)
- var/atom/movable/I = i
- var/datum/component/uplink/found_uplink = I.GetComponent(/datum/component/uplink)
- if(!found_uplink || (check_unlocked && found_uplink.locked))
- continue
- return found_uplink
-
-/**
-* Checks to see if the mind has an accessible uplink (their own, if they are a traitor; any unlocked uplink otherwise),
-* and gives them a fallback spell if no uplink was found
-*/
-/datum/mind/proc/try_give_equipment_fallback()
- var/uplink_exists
- var/datum/antagonist/traitor/traitor_datum = has_antag_datum(/datum/antagonist/traitor)
- if(traitor_datum)
- uplink_exists = traitor_datum.uplink_ref
- if(!uplink_exists)
- uplink_exists = find_syndicate_uplink(check_unlocked = TRUE)
- if(!uplink_exists && !(locate(/datum/action/special_equipment_fallback) in current.actions))
- var/datum/action/special_equipment_fallback/fallback = new(src)
- fallback.Grant(current)
-
-/datum/mind/proc/take_uplink()
- qdel(find_syndicate_uplink())
-
-/datum/mind/proc/make_traitor()
- if(!(has_antag_datum(/datum/antagonist/traitor)))
- add_antag_datum(/datum/antagonist/traitor)
-
-/datum/mind/proc/make_changeling()
- var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
- if(!C)
- C = add_antag_datum(/datum/antagonist/changeling)
- special_role = ROLE_CHANGELING
- return C
-
-
-/datum/mind/proc/make_wizard()
- if(has_antag_datum(/datum/antagonist/wizard))
- return
- set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
- special_role = ROLE_WIZARD
- add_antag_datum(/datum/antagonist/wizard)
-
-
-/datum/mind/proc/make_rev()
- var/datum/antagonist/rev/head/head = new()
- head.give_flash = TRUE
- head.give_hud = TRUE
- add_antag_datum(head)
- special_role = ROLE_REV_HEAD
-
/datum/mind/proc/transfer_martial_arts(mob/living/new_character)
if(!ishuman(new_character))
return
@@ -814,6 +468,11 @@
else
martial_art.teach(new_character)
+/datum/mind/proc/has_martialart(string)
+ if(martial_art && martial_art.id == string)
+ return martial_art
+ return FALSE
+
/datum/mind/proc/get_ghost(even_if_they_cant_reenter, ghosts_with_clients)
for(var/mob/dead/observer/G in (ghosts_with_clients ? GLOB.player_list : GLOB.dead_mob_list))
if(G.mind == src)
@@ -827,27 +486,6 @@
if(G)
G.reenter_corpse()
-/// Sets our can_hijack to the fastest speed our antag datums allow.
-/datum/mind/proc/get_hijack_speed()
- . = 0
- for(var/datum/antagonist/A in antag_datums)
- . = max(., A.hijack_speed())
-
-/datum/mind/proc/has_objective(objective_type)
- for(var/datum/antagonist/A in antag_datums)
- for(var/O in A.objectives)
- if(istype(O,objective_type))
- return TRUE
-
-/mob/proc/sync_mind()
- mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist)
- mind.active = TRUE //indicates that the mind is currently synced with a client
-
-/datum/mind/proc/has_martialart(string)
- if(martial_art && martial_art.id == string)
- return martial_art
- return FALSE
-
///Adds addiction points to the specified addiction
/datum/mind/proc/add_addiction_points(type, amount)
LAZYSET(addiction_points, type, min(LAZYACCESS(addiction_points, type) + amount, MAX_ADDICTION_POINTS))
@@ -871,44 +509,12 @@
assigned_role = new_role
+/mob/proc/sync_mind()
+ mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist)
+ mind.active = TRUE //indicates that the mind is currently synced with a client
+
/mob/dead/new_player/sync_mind()
return
/mob/dead/observer/sync_mind()
return
-
-
-//Initialisation procs
-/mob/proc/mind_initialize()
- if(mind)
- mind.key = key
-
- else
- mind = new /datum/mind(key)
- SSticker.minds += mind
- if(!mind.name)
- mind.name = real_name
- mind.set_current(src)
-
-/mob/living/carbon/mind_initialize()
- ..()
- last_mind = mind
-
-
-//AI
-/mob/living/silicon/ai/mind_initialize()
- . = ..()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/ai))
-
-
-//BORG
-/mob/living/silicon/robot/mind_initialize()
- . = ..()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/cyborg))
-
-
-//PAI
-/mob/living/silicon/pai/mind_initialize()
- . = ..()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/personal_ai))
- mind.special_role = ""
diff --git a/code/datums/mind/antag.dm b/code/datums/mind/antag.dm
new file mode 100644
index 00000000000..c1b10555baa
--- /dev/null
+++ b/code/datums/mind/antag.dm
@@ -0,0 +1,281 @@
+// Datum antag mind procs
+/datum/mind/proc/add_antag_datum(datum_type_or_instance, team)
+ if(!datum_type_or_instance)
+ return
+ var/datum/antagonist/A
+ if(!ispath(datum_type_or_instance))
+ A = datum_type_or_instance
+ if(!istype(A))
+ return
+ else
+ A = new datum_type_or_instance()
+ //Choose snowflake variation if antagonist handles it
+ var/datum/antagonist/S = A.specialization(src)
+ if(S && S != A)
+ qdel(A)
+ A = S
+ if(!A.can_be_owned(src))
+ qdel(A)
+ return
+ A.owner = src
+ LAZYADD(antag_datums, A)
+ A.create_team(team)
+ var/datum/team/antag_team = A.get_team()
+ if(antag_team)
+ antag_team.add_member(src)
+ INVOKE_ASYNC(A, /datum/antagonist.proc/on_gain)
+ log_game("[key_name(src)] has gained antag datum [A.name]([A.type]).")
+ handle_exploitables() //SKYRAT EDIT ADDITION - EXPLOITABLES MENU
+ return A
+
+/datum/mind/proc/remove_antag_datum(datum_type)
+ if(!datum_type)
+ return
+ var/datum/antagonist/A = has_antag_datum(datum_type)
+ if(A)
+ A.on_removal()
+ return TRUE
+
+
+/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
+ for(var/a in antag_datums)
+ var/datum/antagonist/A = a
+ A.on_removal()
+
+/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
+ if(!datum_type)
+ return
+ for(var/a in antag_datums)
+ var/datum/antagonist/A = a
+ if(check_subtypes && istype(A, datum_type))
+ return A
+ else if(A.type == datum_type)
+ return A
+
+/*
+ Removes antag type's references from a mind.
+ objectives, uplinks, powers etc are all handled.
+*/
+
+/datum/mind/proc/remove_changeling()
+ var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
+ if(C)
+ remove_antag_datum(/datum/antagonist/changeling)
+ special_role = null
+
+/datum/mind/proc/remove_traitor()
+ remove_antag_datum(/datum/antagonist/traitor)
+
+/datum/mind/proc/remove_nukeop()
+ var/datum/antagonist/nukeop/nuke = has_antag_datum(/datum/antagonist/nukeop,TRUE)
+ if(nuke)
+ remove_antag_datum(nuke.type)
+ special_role = null
+
+/datum/mind/proc/remove_wizard()
+ remove_antag_datum(/datum/antagonist/wizard)
+ special_role = null
+
+/datum/mind/proc/remove_rev()
+ var/datum/antagonist/rev/rev = has_antag_datum(/datum/antagonist/rev)
+ if(rev)
+ remove_antag_datum(rev.type)
+ special_role = null
+
+
+/datum/mind/proc/remove_antag_equip()
+ var/list/Mob_Contents = current.get_contents()
+ for(var/obj/item/I in Mob_Contents)
+ var/datum/component/uplink/O = I.GetComponent(/datum/component/uplink) //Todo make this reset signal
+ if(O)
+ O.unlock_code = null
+
+/// Remove the antagonists that should not persist when being borged
+/datum/mind/proc/remove_antags_for_borging()
+ remove_antag_datum(/datum/antagonist/cult)
+
+ var/datum/antagonist/rev/revolutionary = has_antag_datum(/datum/antagonist/rev)
+ revolutionary?.remove_revolutionary(borged = TRUE)
+
+/**
+ * ## give_uplink
+ *
+ * A mind proc for giving anyone an uplink.
+ * arguments:
+ * * silent: if this should send a message to the mind getting the uplink. traitors do not use this silence, but the silence var on their antag datum.
+ * * antag_datum: the antag datum of the uplink owner, for storing it in antag memory. optional!
+ */
+/datum/mind/proc/give_uplink(silent = FALSE, datum/antagonist/antag_datum)
+ if(!current)
+ return
+ var/mob/living/carbon/human/traitor_mob = current
+ if (!istype(traitor_mob))
+ return
+
+ var/list/all_contents = traitor_mob.get_all_contents()
+ var/obj/item/modular_computer/tablet/pda/PDA = locate() in all_contents
+ var/obj/item/radio/R = locate() in all_contents
+ var/obj/item/pen/P
+
+ if (PDA) // Prioritize PDA pen, otherwise the pocket protector pens will be chosen, which causes numerous ahelps about missing uplink
+ P = locate() in PDA
+ if (!P) // If we couldn't find a pen in the PDA, or we didn't even have a PDA, do it the old way
+ P = locate() in all_contents
+
+ var/obj/item/uplink_loc
+ var/implant = FALSE
+
+ var/uplink_spawn_location = traitor_mob.client?.prefs?.read_preference(/datum/preference/choiced/uplink_location)
+ switch (uplink_spawn_location)
+ if(UPLINK_PDA)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = R
+ if(!uplink_loc)
+ uplink_loc = P
+ if(UPLINK_RADIO)
+ uplink_loc = R
+ if(!uplink_loc)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = P
+ if(UPLINK_PEN)
+ uplink_loc = P
+ if(UPLINK_IMPLANT)
+ implant = TRUE
+
+ if(!uplink_loc) // We've looked everywhere, let's just implant you
+ implant = TRUE
+
+ if(implant)
+ var/obj/item/implant/uplink/starting/new_implant = new(traitor_mob)
+ new_implant.implant(traitor_mob, null, silent = TRUE)
+ if(!silent)
+ to_chat(traitor_mob, span_boldnotice("Your Syndicate Uplink has been cunningly implanted in you, for a small TC fee. Simply trigger the uplink to access it."))
+ return new_implant
+
+ . = uplink_loc
+ var/unlock_text
+ var/datum/component/uplink/new_uplink = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
+ if(!new_uplink)
+ CRASH("Uplink creation failed.")
+ new_uplink.setup_unlock_code()
+ new_uplink.uplink_handler.owner = traitor_mob.mind
+ new_uplink.uplink_handler.assigned_role = traitor_mob.mind.assigned_role.title
+ new_uplink.uplink_handler.assigned_species = traitor_mob.dna.species.id
+ if(uplink_loc == R)
+ unlock_text = "Your Uplink is cunningly disguised as your [R.name]. Simply dial the frequency [format_frequency(new_uplink.unlock_code)] to unlock its hidden features."
+ else if(uplink_loc == PDA)
+ unlock_text = "Your Uplink is cunningly disguised as your [PDA.name]. Simply enter the code \"[new_uplink.unlock_code]\" into the ring tone selection to unlock its hidden features."
+ else if(uplink_loc == P)
+ unlock_text = "Your Uplink is cunningly disguised as your [P.name]. Simply twist the top of the pen [english_list(new_uplink.unlock_code)] from its starting position to unlock its hidden features."
+ new_uplink.unlock_text = unlock_text
+ if(!silent)
+ to_chat(traitor_mob, span_boldnotice(unlock_text))
+ if(antag_datum)
+ antag_datum.antag_memory += new_uplink.unlock_note + "
"
+
+/// Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
+/datum/mind/proc/enslave_mind_to_creator(mob/living/creator)
+ if(IS_CULTIST(creator))
+ add_antag_datum(/datum/antagonist/cult)
+
+ else if(IS_REVOLUTIONARY(creator))
+ var/datum/antagonist/rev/converter = creator.mind.has_antag_datum(/datum/antagonist/rev,TRUE)
+ converter.add_revolutionary(src,FALSE)
+
+ else if(IS_NUKE_OP(creator))
+ var/datum/antagonist/nukeop/converter = creator.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
+ var/datum/antagonist/nukeop/N = new()
+ N.send_to_spawnpoint = FALSE
+ N.nukeop_outfit = null
+ add_antag_datum(N,converter.nuke_team)
+
+
+ enslaved_to = creator
+
+ current.faction |= creator.faction
+ creator.faction |= current.faction
+
+ if(creator.mind.special_role)
+ message_admins("[ADMIN_LOOKUPFLW(current)] has been created by [ADMIN_LOOKUPFLW(creator)], an antagonist.")
+ to_chat(current, span_userdanger("Despite your creator's current allegiances, your true master remains [creator.real_name]. If their loyalties change, so do yours. This will never change unless your creator's body is destroyed."))
+
+/datum/mind/proc/get_all_objectives()
+ var/list/all_objectives = list()
+ for(var/datum/antagonist/A in antag_datums)
+ all_objectives |= A.objectives
+ return all_objectives
+
+/datum/mind/proc/announce_objectives()
+ var/obj_count = 1
+ to_chat(current, span_notice("Your current objectives:"))
+ for(var/datum/objective/objective as anything in get_all_objectives())
+ to_chat(current, "[objective.objective_name] #[obj_count]: [objective.explanation_text]")
+ obj_count++
+
+/datum/mind/proc/find_syndicate_uplink(check_unlocked)
+ var/list/L = current.get_all_contents()
+ for (var/i in L)
+ var/atom/movable/I = i
+ var/datum/component/uplink/found_uplink = I.GetComponent(/datum/component/uplink)
+ if(!found_uplink || (check_unlocked && found_uplink.locked))
+ continue
+ return found_uplink
+
+/**
+* Checks to see if the mind has an accessible uplink (their own, if they are a traitor; any unlocked uplink otherwise),
+* and gives them a fallback spell if no uplink was found
+*/
+/datum/mind/proc/try_give_equipment_fallback()
+ var/uplink_exists
+ var/datum/antagonist/traitor/traitor_datum = has_antag_datum(/datum/antagonist/traitor)
+ if(traitor_datum)
+ uplink_exists = traitor_datum.uplink_ref
+ if(!uplink_exists)
+ uplink_exists = find_syndicate_uplink(check_unlocked = TRUE)
+ if(!uplink_exists && !(locate(/datum/action/special_equipment_fallback) in current.actions))
+ var/datum/action/special_equipment_fallback/fallback = new(src)
+ fallback.Grant(current)
+
+/datum/mind/proc/take_uplink()
+ qdel(find_syndicate_uplink())
+
+/datum/mind/proc/make_traitor()
+ if(!(has_antag_datum(/datum/antagonist/traitor)))
+ add_antag_datum(/datum/antagonist/traitor)
+
+/datum/mind/proc/make_changeling()
+ var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
+ if(!C)
+ C = add_antag_datum(/datum/antagonist/changeling)
+ special_role = ROLE_CHANGELING
+ return C
+
+
+/datum/mind/proc/make_wizard()
+ if(has_antag_datum(/datum/antagonist/wizard))
+ return
+ set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
+ special_role = ROLE_WIZARD
+ add_antag_datum(/datum/antagonist/wizard)
+
+
+/datum/mind/proc/make_rev()
+ var/datum/antagonist/rev/head/head = new()
+ head.give_flash = TRUE
+ head.give_hud = TRUE
+ add_antag_datum(head)
+ special_role = ROLE_REV_HEAD
+
+/// Sets our can_hijack to the fastest speed our antag datums allow.
+/datum/mind/proc/get_hijack_speed()
+ . = 0
+ for(var/datum/antagonist/A in antag_datums)
+ . = max(., A.hijack_speed())
+
+/datum/mind/proc/has_objective(objective_type)
+ for(var/datum/antagonist/A in antag_datums)
+ for(var/O in A.objectives)
+ if(istype(O,objective_type))
+ return TRUE
diff --git a/code/datums/mind/initialization.dm b/code/datums/mind/initialization.dm
new file mode 100644
index 00000000000..ed4b732de31
--- /dev/null
+++ b/code/datums/mind/initialization.dm
@@ -0,0 +1,34 @@
+//Initialisation procs
+/mob/proc/mind_initialize()
+ if(mind)
+ mind.key = key
+
+ else
+ mind = new /datum/mind(key)
+ SSticker.minds += mind
+ if(!mind.name)
+ mind.name = real_name
+ mind.set_current(src)
+
+/mob/living/carbon/mind_initialize()
+ ..()
+ last_mind = mind
+
+
+//AI
+/mob/living/silicon/ai/mind_initialize()
+ . = ..()
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/ai))
+
+
+//BORG
+/mob/living/silicon/robot/mind_initialize()
+ . = ..()
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/cyborg))
+
+
+//PAI
+/mob/living/silicon/pai/mind_initialize()
+ . = ..()
+ mind.set_assigned_role(SSjob.GetJobType(/datum/job/personal_ai))
+ mind.special_role = ""
diff --git a/code/datums/mind/skills.dm b/code/datums/mind/skills.dm
new file mode 100644
index 00000000000..5847236435b
--- /dev/null
+++ b/code/datums/mind/skills.dm
@@ -0,0 +1,80 @@
+/datum/mind/proc/init_known_skills()
+ for (var/type in GLOB.skill_types)
+ known_skills[type] = list(SKILL_LEVEL_NONE, 0)
+
+///Return the amount of EXP needed to go to the next level. Returns 0 if max level
+/datum/mind/proc/exp_needed_to_level_up(skill)
+ var/lvl = update_skill_level(skill)
+ if (lvl >= length(SKILL_EXP_LIST)) //If we're already past the last exp threshold
+ return 0
+ return SKILL_EXP_LIST[lvl+1] - known_skills[skill][SKILL_EXP]
+
+///Adjust experience of a specific skill
+/datum/mind/proc/adjust_experience(skill, amt, silent = FALSE, force_old_level = 0)
+ var/datum/skill/S = GetSkillRef(skill)
+ var/old_level = force_old_level ? force_old_level : known_skills[skill][SKILL_LVL] //Get current level of the S skill
+ experience_multiplier = initial(experience_multiplier)
+ for(var/key in experience_multiplier_reasons)
+ experience_multiplier += experience_multiplier_reasons[key]
+ known_skills[skill][SKILL_EXP] = max(0, known_skills[skill][SKILL_EXP] + amt*experience_multiplier) //Update exp. Prevent going below 0
+ known_skills[skill][SKILL_LVL] = update_skill_level(skill)//Check what the current skill level is based on that skill's exp
+ if(silent)
+ return
+ if(known_skills[skill][SKILL_LVL] > old_level)
+ S.level_gained(src, known_skills[skill][SKILL_LVL], old_level)
+ else if(known_skills[skill][SKILL_LVL] < old_level)
+ S.level_lost(src, known_skills[skill][SKILL_LVL], old_level)
+
+///Set experience of a specific skill to a number
+/datum/mind/proc/set_experience(skill, amt, silent = FALSE)
+ var/old_level = known_skills[skill][SKILL_EXP]
+ known_skills[skill][SKILL_EXP] = amt
+ adjust_experience(skill, 0, silent, old_level) //Make a call to adjust_experience to handle updating level
+
+///Set level of a specific skill
+/datum/mind/proc/set_level(skill, newlevel, silent = FALSE)
+ var/oldlevel = get_skill_level(skill)
+ var/difference = SKILL_EXP_LIST[newlevel] - SKILL_EXP_LIST[oldlevel]
+ adjust_experience(skill, difference, silent)
+
+///Check what the current skill level is based on that skill's exp
+/datum/mind/proc/update_skill_level(skill)
+ var/i = 0
+ for (var/exp in SKILL_EXP_LIST)
+ i ++
+ if (known_skills[skill][SKILL_EXP] >= SKILL_EXP_LIST[i])
+ continue
+ return i - 1 //Return level based on the last exp requirement that we were greater than
+ return i //If we had greater EXP than even the last exp threshold, we return the last level
+
+///Gets the skill's singleton and returns the result of its get_skill_modifier
+/datum/mind/proc/get_skill_modifier(skill, modifier)
+ var/datum/skill/S = GetSkillRef(skill)
+ return S.get_skill_modifier(modifier, known_skills[skill][SKILL_LVL])
+
+///Gets the player's current level number from the relevant skill
+/datum/mind/proc/get_skill_level(skill)
+ return known_skills[skill][SKILL_LVL]
+
+///Gets the player's current exp from the relevant skill
+/datum/mind/proc/get_skill_exp(skill)
+ return known_skills[skill][SKILL_EXP]
+
+/datum/mind/proc/get_skill_level_name(skill)
+ var/level = get_skill_level(skill)
+ return SSskills.level_names[level]
+
+/datum/mind/proc/print_levels(user)
+ var/list/shown_skills = list()
+ for(var/i in known_skills)
+ if(known_skills[i][SKILL_LVL] > SKILL_LEVEL_NONE) //Do we actually have a level in this?
+ shown_skills += i
+ if(!length(shown_skills))
+ to_chat(user, span_notice("You don't seem to have any particularly outstanding skills."))
+ return
+ var/msg = "[span_info("Your skills")]\n"
+ for(var/i in shown_skills)
+ var/datum/skill/the_skill = i
+ msg += "[initial(the_skill.name)] - [get_skill_level_name(the_skill)]\n"
+ msg += ""
+ to_chat(user, examine_block(msg))
diff --git a/tgstation.dme b/tgstation.dme
index 0ba26f4fa7b..b45c5e67909 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -668,7 +668,6 @@
#include "code\datums\http.dm"
#include "code\datums\hud.dm"
#include "code\datums\map_config.dm"
-#include "code\datums\mind.dm"
#include "code\datums\minigames_menu.dm"
#include "code\datums\mood.dm"
#include "code\datums\movement_detector.dm"
@@ -1194,6 +1193,10 @@
#include "code\datums\memory\memory.dm"
#include "code\datums\memory\tattoo_kit.dm"
#include "code\datums\mergers\_merger.dm"
+#include "code\datums\mind\_mind.dm"
+#include "code\datums\mind\antag.dm"
+#include "code\datums\mind\initialization.dm"
+#include "code\datums\mind\skills.dm"
#include "code\datums\mocking\client.dm"
#include "code\datums\mood_events\_mood_event.dm"
#include "code\datums\mood_events\area_events.dm"