diff --git a/code/__DEFINES/loadout.dm b/code/__DEFINES/loadout.dm
index 94059c0880..767894050d 100644
--- a/code/__DEFINES/loadout.dm
+++ b/code/__DEFINES/loadout.dm
@@ -54,3 +54,16 @@
//donator items
#define LOADOUT_CATEGORY_DONATOR "Donator"
+
+//how many prosthetics can we have
+#define MAXIMUM_LOADOUT_PROSTHETICS 2
+
+//what limbs can be amputated or be prosthetic
+#define LOADOUT_ALLOWED_LIMB_TARGETS list(BODY_ZONE_L_ARM,BODY_ZONE_R_ARM,BODY_ZONE_L_LEG,BODY_ZONE_R_LEG)
+
+//options for modifiying limbs
+#define LOADOUT_LIMB_NORMAL "Normal"
+#define LOADOUT_LIMB_PROSTHETIC "Prosthetic"
+#define LOADOUT_LIMB_AMPUTATED "Amputated"
+
+#define LOADOUT_LIMBS list(LOADOUT_LIMB_NORMAL,LOADOUT_LIMB_PROSTHETIC,LOADOUT_LIMB_AMPUTATED) //you can amputate your legs/arms though
\ No newline at end of file
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 71c2b2e13b..09d4aadf50 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -279,7 +279,8 @@ GLOBAL_LIST_INIT(unlocked_mutant_parts, list("horns", "insect_fluff"))
//parts in either of the above two lists that require a second option that allows them to be coloured
GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "deco_wings" = "wings_color", "horns" = "horns_color"))
-//species ids that have greyscale sprites
+//body ids that have greyscale sprites
GLOBAL_LIST_INIT(greyscale_limb_types, list("human","moth","lizard","pod","plant","jelly","slime","golem","lum","stargazer","mush","ethereal","snail","c_golem","b_golem","mammal","xeno","ipc","insect","synthliz","avian","aquatic"))
-//species ids that need snowflake coloring applied
+//body ids that have prosthetic sprites
+GLOBAL_LIST_INIT(prosthetic_limb_types, list("xion","bishop","cybersolutions","grayson","hephaestus","nanotrasen","talon"))
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 0bc4e6665a..9af86653f4 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -396,6 +396,7 @@ SUBSYSTEM_DEF(ticker)
SSjob.EquipRank(N, player.mind.assigned_role, 0)
if(CONFIG_GET(flag/roundstart_traits) && ishuman(N.new_character))
SSquirks.AssignQuirks(N.new_character, N.client, TRUE, TRUE, SSjob.GetJob(player.mind.assigned_role), FALSE, N)
+ N.client.prefs.post_copy_to(player)
CHECK_TICK
if(captainless)
for(var/mob/dead/new_player/N in GLOB.player_list)
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 68b5d6f987..322fba0a33 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -211,24 +211,27 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
H.gain_trauma(T, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/paraplegic/on_spawn()
- if(quirk_holder.buckled) // Handle late joins being buckled to arrival shuttle chairs.
- quirk_holder.buckled.unbuckle_mob(quirk_holder)
+ if(quirk_holder.client)
+ var/modified_limbs = quirk_holder.client.prefs.modified_limbs
+ if(!(modified_limbs[BODY_ZONE_L_LEG] == LOADOUT_LIMB_AMPUTATED && modified_limbs[BODY_ZONE_R_LEG] == LOADOUT_LIMB_AMPUTATED && !isjellyperson(quirk_holder)))
+ if(quirk_holder.buckled) // Handle late joins being buckled to arrival shuttle chairs.
+ quirk_holder.buckled.unbuckle_mob(quirk_holder)
- var/turf/T = get_turf(quirk_holder)
- var/obj/structure/chair/spawn_chair = locate() in T
+ var/turf/T = get_turf(quirk_holder)
+ var/obj/structure/chair/spawn_chair = locate() in T
- var/obj/vehicle/ridden/wheelchair/wheels = new(T)
- if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
- wheels.setDir(spawn_chair.dir)
+ var/obj/vehicle/ridden/wheelchair/wheels = new(T)
+ if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
+ wheels.setDir(spawn_chair.dir)
- wheels.buckle_mob(quirk_holder)
+ wheels.buckle_mob(quirk_holder)
- // During the spawning process, they may have dropped what they were holding, due to the paralysis
- // So put the things back in their hands.
+ // During the spawning process, they may have dropped what they were holding, due to the paralysis
+ // So put the things back in their hands.
- for(var/obj/item/I in T)
- if(I.fingerprintslast == quirk_holder.ckey)
- quirk_holder.put_in_hands(I)
+ for(var/obj/item/I in T)
+ if(I.fingerprintslast == quirk_holder.ckey)
+ quirk_holder.put_in_hands(I)
/datum/quirk/poor_aim
name = "Poor Aim"
@@ -244,42 +247,6 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
mob_trait = TRAIT_PROSOPAGNOSIA
medical_record_text = "Patient suffers from prosopagnosia and cannot recognize faces."
-/datum/quirk/prosthetic_limb
- name = "Prosthetic Limb"
- desc = "An accident caused you to lose one of your limbs. Because of this, you now have a random prosthetic!"
- value = -1
- var/slot_string = "limb"
-
-/datum/quirk/prosthetic_limb/on_spawn()
- var/mob/living/carbon/human/H = quirk_holder
- var/limb_slot
- if(HAS_TRAIT(H, TRAIT_PARA))//Prevent paraplegic legs being replaced
- limb_slot = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
- else
- limb_slot = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- var/obj/item/bodypart/old_part = H.get_bodypart(limb_slot)
- var/obj/item/bodypart/prosthetic
- switch(limb_slot)
- if(BODY_ZONE_L_ARM)
- prosthetic = new/obj/item/bodypart/l_arm/robot/surplus(quirk_holder)
- slot_string = "left arm"
- if(BODY_ZONE_R_ARM)
- prosthetic = new/obj/item/bodypart/r_arm/robot/surplus(quirk_holder)
- slot_string = "right arm"
- if(BODY_ZONE_L_LEG)
- prosthetic = new/obj/item/bodypart/l_leg/robot/surplus(quirk_holder)
- slot_string = "left leg"
- if(BODY_ZONE_R_LEG)
- prosthetic = new/obj/item/bodypart/r_leg/robot/surplus(quirk_holder)
- slot_string = "right leg"
- prosthetic.replace_limb(H)
- qdel(old_part)
- H.regenerate_icons()
-
-/datum/quirk/prosthetic_limb/post_add()
- to_chat(quirk_holder, "Your [slot_string] has been replaced with a surplus prosthetic. It is fragile and will easily come apart under duress. Additionally, \
- you need to use a welding tool and cables to repair it, instead of bruise packs and ointment.")
-
/datum/quirk/insanity
name = "Reality Dissociation Syndrome"
desc = "You suffer from a severe disorder that causes very vivid hallucinations. Mindbreaker toxin can suppress its effects, and you are immune to mindbreaker's hallucinogenic properties. This is not a license to grief."
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index 33349cee67..7e14fb9d35 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -272,4 +272,3 @@
icon_state = "skub"
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("skubbed")
-
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 51e027522c..cff3040926 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -163,6 +163,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
)
var/custom_speech_verb = "default" //if your say_mod is to be something other than your races
var/custom_tongue = "default" //if your tongue is to be something other than your races
+ var/modified_limbs = list() //prosthetic/amputated limbs
var/chosen_limb_id //body sprite selected to load for the users limbs, null means default, is sanitized when loaded
/// Security record note section
@@ -429,12 +430,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Gender:[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]
"
if(gender != NEUTER && pref_species.sexes)
dat += "Body Model:[features["body_model"] == MALE ? "Masculine" : "Feminine"]
"
+ dat += "Limb Modification:
"
+ dat += "Modify Limbs
"
+ for(var/modification in modified_limbs)
+ if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
+ dat += "[modification]: [modified_limbs[modification][2]]
"
+ else
+ dat += "[modification]: [modified_limbs[modification][1]]
"
+ dat += "
"
dat += "Species:[pref_species.name]
"
dat += "Custom Species Name:[custom_species ? custom_species : "None"]
"
dat += "Random Body:Randomize!
"
dat += "Always Random Body:[be_random_body ? "Yes" : "No"]
"
dat += "
Cycle background:[bgstate]
"
-
var/use_skintones = pref_species.use_skintones
if(use_skintones)
dat += APPEARANCE_CATEGORY_COLUMN
@@ -1235,6 +1243,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
for(var/V in all_quirks)
var/datum/quirk/T = SSquirks.quirks[V]
bal -= initial(T.value)
+ for(var/modification in modified_limbs)
+ if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
+ return bal + 1 //max 1 point regardless of how many prosthetics
return bal
/datum/preferences/proc/GetPositiveQuirkCount()
@@ -1492,6 +1503,29 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("cycle_bg")
bgstate = next_list_item(bgstate, bgstate_options)
+ if("modify_limbs")
+ var/limb_type = input(user, "Choose the limb to modify:", "Character Preference") as null|anything in LOADOUT_ALLOWED_LIMB_TARGETS
+ if(limb_type)
+ var/modification_type = input(user, "Choose the modification to the limb:", "Character Preference") as null|anything in LOADOUT_LIMBS
+ if(modification_type)
+ if(modification_type == LOADOUT_LIMB_PROSTHETIC)
+ var/prosthetic_type = input(user, "Choose the type of prosthetic", "Character Preference") as null|anything in (list("prosthetic") + GLOB.prosthetic_limb_types)
+ if(prosthetic_type)
+ var/number_of_prosthetics = 0
+ for(var/modification in modified_limbs)
+ if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC)
+ number_of_prosthetics += 1
+ if(number_of_prosthetics >= MAXIMUM_LOADOUT_PROSTHETICS && !(limb_type in modified_limbs && modified_limbs[limb_type][1] == LOADOUT_LIMB_PROSTHETIC))
+ to_chat(user, "You can only have up to two prosthetic limbs!")
+ else
+ //save the actual prosthetic data
+ modified_limbs[limb_type] = list(modification_type, prosthetic_type)
+ else
+ if(modification_type == LOADOUT_LIMB_NORMAL)
+ modified_limbs -= limb_type
+ else
+ modified_limbs[limb_type] = list(modification_type)
+
if("underwear")
var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_list
if(new_underwear)
@@ -2478,7 +2512,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ShowChoices(user)
return 1
-/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE)
+/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE, initial_spawn = FALSE)
if(be_random_name)
real_name = pref_species.random_name(gender)
@@ -2573,6 +2607,35 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(custom_speech_verb != "default")
character.dna.species.say_mod = custom_speech_verb
+ //limb stuff, only done when initially spawning in
+ if(initial_spawn)
+ //delete any existing prosthetic limbs to make sure no remnant prosthetics are left over
+ for(var/obj/item/bodypart/part in character.bodyparts)
+ if(part.status == BODYPART_ROBOTIC)
+ qdel(part)
+ character.regenerate_limbs() //regenerate limbs so now you only have normal limbs
+ for(var/modified_limb in modified_limbs)
+ var/modification = modified_limbs[modified_limb][1]
+ var/obj/item/bodypart/old_part = character.get_bodypart(modified_limb)
+ if(modification == LOADOUT_LIMB_PROSTHETIC)
+ var/obj/item/bodypart/new_limb
+ switch(modified_limb)
+ if(BODY_ZONE_L_ARM)
+ new_limb = new/obj/item/bodypart/l_arm/robot/surplus(character)
+ if(BODY_ZONE_R_ARM)
+ new_limb = new/obj/item/bodypart/r_arm/robot/surplus(character)
+ if(BODY_ZONE_L_LEG)
+ new_limb = new/obj/item/bodypart/l_leg/robot/surplus(character)
+ if(BODY_ZONE_R_LEG)
+ new_limb = new/obj/item/bodypart/r_leg/robot/surplus(character)
+ var/prosthetic_type = modified_limbs[modified_limb][2]
+ if(prosthetic_type != "prosthetic") //lets just leave the old sprites as they are
+ new_limb.icon = file("icons/mob/augmentation/cosmetic_prosthetic/[prosthetic_type].dmi")
+ new_limb.replace_limb(character)
+ qdel(old_part)
+
+ if(length(modified_limbs))
+ character.regenerate_icons()
SEND_SIGNAL(character, COMSIG_HUMAN_PREFS_COPIED_TO, src, icon_updates, roundstart_checks)
@@ -2581,6 +2644,18 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.update_body()
character.update_hair()
+/datum/preferences/proc/post_copy_to(mob/living/carbon/human/character)
+ //if no legs, and not a paraplegic or a slime, give them a free wheelchair
+ if(modified_limbs[BODY_ZONE_L_LEG] == LOADOUT_LIMB_AMPUTATED && modified_limbs[BODY_ZONE_R_LEG] == LOADOUT_LIMB_AMPUTATED && !character.has_quirk(/datum/quirk/paraplegic) && !isjellyperson(character))
+ if(character.buckled)
+ character.buckled.unbuckle_mob(character)
+ var/turf/T = get_turf(character)
+ var/obj/structure/chair/spawn_chair = locate() in T
+ var/obj/vehicle/ridden/wheelchair/wheels = new(T)
+ if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
+ wheels.setDir(spawn_chair.dir)
+ wheels.buckle_mob(character)
+
/datum/preferences/proc/get_default_name(name_id)
switch(name_id)
if("human")
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 8591d77e50..5856a7b6e1 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -514,6 +514,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["scars3"] >> scars_list["3"]
S["scars4"] >> scars_list["4"]
S["scars5"] >> scars_list["5"]
+ var/limbmodstr
+ S["modified_limbs"] >> limbmodstr
+ if(length(limbmodstr))
+ modified_limbs = safe_json_decode(limbmodstr)
+ else
+ modified_limbs = list()
S["chosen_limb_id"] >> chosen_limb_id
S["hide_ckey"] >> hide_ckey //saved per-character
@@ -845,8 +851,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_ooc_notes"], features["ooc_notes"])
- WRITE_FILE(S["chosen_limb_id"], chosen_limb_id)
-
//Custom names
for(var/custom_name_id in GLOB.preferences_custom_names)
var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
@@ -874,6 +878,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["scars3"] , scars_list["3"])
WRITE_FILE(S["scars4"] , scars_list["4"])
WRITE_FILE(S["scars5"] , scars_list["5"])
+ if(islist(modified_limbs))
+ WRITE_FILE(S["modified_limbs"] , safe_json_encode(modified_limbs))
+ WRITE_FILE(S["chosen_limb_id"], chosen_limb_id)
//gear loadout
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 1238b37919..0feceaf1a8 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -80,6 +80,9 @@
if(mind_traits)
for(var/t in mind_traits)
ADD_TRAIT(H.mind, t, JOB_TRAIT)
+ if(/datum/quirk/paraplegic in blacklisted_quirks)
+ H.regenerate_limbs() //if you can't be a paraplegic, attempt to regenerate limbs to stop amputated limb selection
+ H.set_resting(FALSE, TRUE) //they probably shouldn't be on the floor because they had no legs then suddenly had legs
/datum/job/proc/announce(mob/living/carbon/human/H)
if(head_announce)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 768c4c943e..312449064f 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -414,6 +414,8 @@
give_magic(humanc)
if(GLOB.curse_of_madness_triggered)
give_madness(humanc, GLOB.curse_of_madness_triggered)
+ if(humanc.client)
+ humanc.client.prefs.post_copy_to(humanc)
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
@@ -567,7 +569,7 @@
client.prefs.scars_list["[cur_scar_index]"] = valid_scars
client.prefs.save_character()
- client.prefs.copy_to(H)
+ client.prefs.copy_to(H, initial_spawn = TRUE)
H.dna.update_dna_identity()
if(mind)
if(transfer_after)
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index 9a0d9f9242..b77fae68c8 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -41,7 +41,7 @@
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
// Apply the Dummy's preview background first so we properly layer everything else on top of it.
mannequin.add_overlay(mutable_appearance('modular_citadel/icons/ui/backgrounds.dmi', bgstate, layer = SPACE_LAYER))
- copy_to(mannequin)
+ copy_to(mannequin, initial_spawn = TRUE)
if(previewJob && equip_job)
mannequin.job = previewJob.title
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 2f5c94b784..ed1ba3852f 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -18,6 +18,9 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/mob/living/carbon/human/dummy/Life()
return
+/mob/living/carbon/human/dummy/update_mobility()
+ return
+
/mob/living/carbon/human/dummy/proc/wipe_state()
delete_equipment()
icon_render_key = null
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/bishop.dmi b/icons/mob/augmentation/cosmetic_prosthetic/bishop.dmi
new file mode 100644
index 0000000000..8641ef2d3c
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/bishop.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/cybersolutions.dmi b/icons/mob/augmentation/cosmetic_prosthetic/cybersolutions.dmi
new file mode 100644
index 0000000000..eeed0d0c01
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/cybersolutions.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/grayson.dmi b/icons/mob/augmentation/cosmetic_prosthetic/grayson.dmi
new file mode 100644
index 0000000000..9ed6c5f06f
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/grayson.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/hephaestus.dmi b/icons/mob/augmentation/cosmetic_prosthetic/hephaestus.dmi
new file mode 100644
index 0000000000..4bb6b72fd0
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/hephaestus.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/nanotrasen.dmi b/icons/mob/augmentation/cosmetic_prosthetic/nanotrasen.dmi
new file mode 100644
index 0000000000..18c7738ba3
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/nanotrasen.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/talon.dmi b/icons/mob/augmentation/cosmetic_prosthetic/talon.dmi
new file mode 100644
index 0000000000..9e4b93fb54
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/talon.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/ward.dmi b/icons/mob/augmentation/cosmetic_prosthetic/ward.dmi
new file mode 100644
index 0000000000..b2622ef6a1
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/ward.dmi differ
diff --git a/icons/mob/augmentation/cosmetic_prosthetic/xion.dmi b/icons/mob/augmentation/cosmetic_prosthetic/xion.dmi
new file mode 100644
index 0000000000..823205742a
Binary files /dev/null and b/icons/mob/augmentation/cosmetic_prosthetic/xion.dmi differ