diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index 796654d2062..4e52090f848 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -388,6 +388,8 @@
if(malf_datum)
malf_datum.add_law_zero()
+ if(!isnull(the_brainmob.client))
+ ai_mob.set_gender(the_brainmob.client)
if(core_mmi.force_replace_ai_name)
ai_mob.fully_replace_character_name(ai_mob.name, core_mmi.replacement_ai_name())
ai_mob.posibrain_inside = core_mmi.braintype == "Android"
diff --git a/code/modules/client/preferences/silicon_gender.dm b/code/modules/client/preferences/silicon_gender.dm
new file mode 100644
index 00000000000..65d7b27b582
--- /dev/null
+++ b/code/modules/client/preferences/silicon_gender.dm
@@ -0,0 +1,37 @@
+#define SILICON_MALE "He/Him"
+#define SILICON_FEMALE "She/Her"
+#define SILICON_PLURAL "They/Them"
+#define SILICON_NEUTER "It/Its"
+
+/datum/preference/choiced/silicon_gender
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "silicon_gender"
+ var/static/use_character_gender = "Use character gender"
+ ///Used to convert the read value of this preference into a gender
+ var/static/list/pronouns_to_genders = list(
+ "[SILICON_MALE]" = MALE,
+ "[SILICON_FEMALE]" = FEMALE,
+ "[SILICON_PLURAL]" = PLURAL,
+ "[SILICON_NEUTER]" = NEUTER,
+ )
+
+/datum/preference/choiced/silicon_gender/init_possible_values()
+ return list(
+ use_character_gender,
+ SILICON_MALE,
+ SILICON_FEMALE,
+ SILICON_PLURAL,
+ SILICON_NEUTER,
+ )
+
+/datum/preference/choiced/silicon_gender/create_default_value()
+ return SILICON_NEUTER
+
+/datum/preference/choiced/silicon_gender/apply_to_human(mob/living/carbon/human/target, value)
+ return
+
+#undef SILICON_MALE
+#undef SILICON_FEMALE
+#undef SILICON_PLURAL
+#undef SILICON_NEUTER
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index 2264a914c1b..58774b5aedf 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -31,6 +31,8 @@
if(!R.connected_ai)
R.TryConnectToAI()
var/mob/living/silicon/ai/ai_spawn = spawned
+ if(player_client)
+ ai_spawn.set_gender(player_client)
ai_spawn.log_current_laws()
diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm
index 7b1e40fe1d6..99d370c2082 100644
--- a/code/modules/jobs/job_types/cyborg.dm
+++ b/code/modules/jobs/job_types/cyborg.dm
@@ -26,9 +26,10 @@
. = ..()
if(!iscyborg(spawned))
return
- spawned.gender = NEUTER
var/mob/living/silicon/robot/robot_spawn = spawned
robot_spawn.notify_ai(AI_NOTIFICATION_NEW_BORG)
+ if(player_client)
+ robot_spawn.set_gender(player_client)
if(!robot_spawn.connected_ai) // Only log if there's no Master AI
robot_spawn.log_current_laws()
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 6a9b24a4b66..4e5e2dd3c96 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -51,6 +51,7 @@
if(client)
INVOKE_ASYNC(src, PROC_REF(apply_pref_name), /datum/preference/name/ai, client)
INVOKE_ASYNC(src, PROC_REF(apply_pref_hologram_display), client)
+ set_gender(client)
INVOKE_ASYNC(src, PROC_REF(set_core_display_icon))
@@ -373,6 +374,7 @@
the_mmi.brainmob.name = src.real_name
the_mmi.brainmob.real_name = src.real_name
the_mmi.brainmob.container = the_mmi
+ the_mmi.brainmob.gender = src.gender
var/has_suicided_trait = HAS_TRAIT(src, TRAIT_SUICIDED)
the_mmi.brainmob.set_suicide(has_suicided_trait)
diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm
index 13dc53ff2a8..9e58ee04d44 100644
--- a/code/modules/mob/living/silicon/ai/examine.dm
+++ b/code/modules/mob/living/silicon/ai/examine.dm
@@ -1,27 +1,27 @@
/mob/living/silicon/ai/examine(mob/user)
. = list()
if(stat == DEAD)
- . += span_deadsay("It appears to be powered-down.")
- . += span_notice("Its floor bolts are [is_anchored ? "tightened" : "loose"].")
+ . += span_deadsay("[p_They()] appear[p_s()] to be powered-down.")
+ . += span_notice("[p_Their()] floor bolts are [is_anchored ? "tightened" : "loose"].")
if(is_anchored)
if(!opened)
if(!emagged)
- . += span_notice("Its access panel is [stat == DEAD ? "damaged" : "closed and locked"], but could be pried open.")
+ . += span_notice("[p_Their()] access panel is [stat == DEAD ? "damaged" : "closed and locked"], but could be pried open.")
else
- . += span_warning("Its access panel lock is sparking, the cover can be pried open.")
+ . += span_warning("[p_Their()] access panel lock is sparking, the cover can be pried open.")
else
- . += span_notice("Its neural network connection could be cut, its access panel cover can be pried back into place.")
+ . += span_notice("[p_Their()] neural network connection could be cut, the access panel cover can be pried back into place.")
if(stat != DEAD)
if (getBruteLoss())
if (getBruteLoss() < 30)
- . += span_warning("It looks slightly dented.")
+ . += span_warning("[p_They()] look[p_s()] slightly dented.")
else
- . += span_warning("It looks severely dented!")
+ . += span_warning("[p_They()] look[p_s()] severely dented!")
if (getFireLoss())
if (getFireLoss() < 30)
- . += span_warning("It looks slightly charred.")
+ . += span_warning("[p_They()] look[p_s()] slightly charred.")
else
- . += span_warning("Its casing is melted and heat-warped!")
+ . += span_warning("[p_Their()] casing is melted and heat-warped!")
if(deployed_shell)
. += "The wireless networking light is blinking."
else if (!shunted && !client)
diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm
index 3091881c56f..388835b7a9a 100644
--- a/code/modules/mob/living/silicon/robot/examine.dm
+++ b/code/modules/mob/living/silicon/robot/examine.dm
@@ -4,47 +4,47 @@
. += "[desc]"
var/model_name = model ? "\improper [model.name]" : "\improper Default"
- . += "It is currently \a [model_name]-type cyborg."
+ . += "[p_Theyre()] currently \a [model_name]-type cyborg."
var/obj/act_module = get_active_held_item()
if(act_module)
- . += "It is holding [icon2html(act_module, user)] \a [act_module]."
+ . += "[p_Theyre()] holding [icon2html(act_module, user)] \a [act_module]."
. += get_status_effect_examinations()
if (getBruteLoss())
if (getBruteLoss() < maxHealth*0.5)
- . += span_warning("It looks slightly dented.")
+ . += span_warning("[p_They()] look[p_s()] slightly dented.")
else
- . += span_boldwarning("It looks severely dented!")
+ . += span_boldwarning("[p_They()] look[p_s()] severely dented!")
if (getFireLoss() || getToxLoss())
var/overall_fireloss = getFireLoss() + getToxLoss()
if (overall_fireloss < maxHealth * 0.5)
- . += span_warning("It looks slightly charred.")
+ . += span_warning("[p_They()] look[p_s()] slightly charred.")
else
- . += span_boldwarning("It looks severely burnt and heat-warped!")
+ . += span_boldwarning("[p_They()] look[p_s()] severely burnt and heat-warped!")
if (health < -maxHealth*0.5)
- . += span_warning("It looks barely operational.")
+ . += span_warning("[p_They()] look[p_s()] barely operational.")
if (fire_stacks < 0)
- . += span_warning("It's covered in water.")
+ . += span_warning("[p_Theyre()] covered in water.")
else if (fire_stacks > 0)
- . += span_warning("It's coated in something flammable.")
+ . += span_warning("[p_Theyre()] coated in something flammable.")
if(opened)
- . += span_warning("Its cover is open and the power cell is [cell ? "installed" : "missing"].")
+ . += span_warning("[p_Their()] cover is open and the power cell is [cell ? "installed" : "missing"].")
else
- . += "Its cover is closed[locked ? "" : ", and looks unlocked"]."
+ . += "[p_Their()] cover is closed[locked ? "" : ", and looks unlocked"]."
if(cell && cell.charge <= 0)
- . += span_warning("Its battery indicator is blinking red!")
+ . += span_warning("[p_Their()] battery indicator is blinking red!")
switch(stat)
if(CONSCIOUS)
if(shell)
- . += "It appears to be an [deployed ? "active" : "empty"] AI shell."
+ . += "[p_They()] appear[p_s()] to be an [deployed ? "active" : "empty"] AI shell."
else if(!client)
- . += "It appears to be in stand-by mode." //afk
+ . += "[p_They()] appear[p_s()] to be in stand-by mode." //afk
if(SOFT_CRIT, UNCONSCIOUS, HARD_CRIT)
- . += span_warning("It doesn't seem to be responding.")
+ . += span_warning("[p_They()] do[p_es()]n't seem to be responding.")
if(DEAD)
- . += span_deadsay("It looks like its system is corrupted and requires a reset.")
+ . += span_deadsay("[p_They()] look[p_s()] like its system is corrupted and requires a reset.")
. += ..()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 7b38a5df990..6fab744907e 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -75,6 +75,9 @@
mmi.update_appearance()
setup_default_name()
+ if(mmi.brainmob)
+ gender = mmi.brainmob.gender
+
aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
@@ -887,6 +890,8 @@
builtInCamera.c_tag = real_name //update the camera name too
mainframe = AI
deployed = TRUE
+ if(AI.client)
+ set_gender(AI.client)
set_connected_ai(mainframe)
mainframe.connected_robots |= src
lawupdate = TRUE
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 1937cd3d4a5..01ec2806a0f 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -94,6 +94,16 @@
GLOB.silicon_mobs -= src
return ..()
+///Sets cyborg gender from preferences. Expects a client.
+/mob/living/silicon/proc/set_gender(client/player_client)
+ var/silicon_pronouns = player_client.prefs.read_preference(/datum/preference/choiced/silicon_gender)
+ if(silicon_pronouns == /datum/preference/choiced/silicon_gender::use_character_gender)
+ gender = player_client.prefs.read_preference(/datum/preference/choiced/gender)
+ return
+ var/silicon_gender = /datum/preference/choiced/silicon_gender::pronouns_to_genders[silicon_pronouns]
+ if(!isnull(silicon_gender))
+ gender = silicon_gender
+
/mob/living/silicon/proc/on_silicon_shocked(datum/source, shock_damage, shock_source, siemens_coeff, flags)
SIGNAL_HANDLER
for(var/mob/living/living_mob in buckled_mobs)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 2ac1dc56c1a..17da7290366 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -144,7 +144,8 @@
ADD_TRAIT(src, TRAIT_NO_TRANSFORM, PERMANENT_TRANSFORMATION_TRAIT)
var/mob/living/silicon/robot/new_borg = new /mob/living/silicon/robot(loc)
- new_borg.gender = gender
+ if(client)
+ new_borg.set_gender(client)
new_borg.SetInvisibility(INVISIBILITY_NONE)
if(client?.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME)
diff --git a/tgstation.dme b/tgstation.dme
index ce6faac8792..4ddfc80f51d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3912,6 +3912,7 @@
#include "code\modules\client\preferences\scarred_eye.dm"
#include "code\modules\client\preferences\screentips.dm"
#include "code\modules\client\preferences\security_department.dm"
+#include "code\modules\client\preferences\silicon_gender.dm"
#include "code\modules\client\preferences\skin_tone.dm"
#include "code\modules\client\preferences\sounds.dm"
#include "code\modules\client\preferences\species.dm"
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/silicon_gender.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/silicon_gender.tsx
new file mode 100644
index 00000000000..c585bfc1c06
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/silicon_gender.tsx
@@ -0,0 +1,7 @@
+import { FeatureChoiced } from '../base';
+import { FeatureDropdownInput } from '../dropdowns';
+
+export const silicon_gender: FeatureChoiced = {
+ name: 'Gender (Silicon)',
+ component: FeatureDropdownInput,
+};