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, +};