diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
index 267b328fcf37..38d0e6035e89 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -151,3 +151,7 @@
/// Sent at the very end of human character setup
#define COMSIG_HUMAN_CHARACTER_SETUP "after_human_setup"
+
+/// from /datum/status_effect/limp/proc/check_step()
+#define COMSIG_CARBON_LIMPING "mob_limp_check"
+ #define COMPONENT_CANCEL_LIMP (1<<0)
diff --git a/code/__DEFINES/living.dm b/code/__DEFINES/living.dm
index d256bb7de448..fe79f7064cf1 100644
--- a/code/__DEFINES/living.dm
+++ b/code/__DEFINES/living.dm
@@ -66,6 +66,11 @@
/// The trait that determines if someone has the robotic limb reattachment quirk.
#define TRAIT_ROBOTIC_LIMBATTACHMENT "trait_robotic_limbattachment"
+/// Mob can walk despite having two disabled/missing legs so long as they have two of this trait.
+/// Kind of jank, refactor at a later day when I can think of a better solution.
+/// Just be sure to call update_limbless_locomotion() after applying / removal
+#define TRAIT_NO_LEG_AID "no_leg_aid"
+
#define COLOR_BLOOD "#c90000"
// Used in ready menu anominity
diff --git a/code/datums/status_effects/wound_effects.dm b/code/datums/status_effects/wound_effects.dm
index f7d640a6d1cf..afda360acbdd 100644
--- a/code/datums/status_effects/wound_effects.dm
+++ b/code/datums/status_effects/wound_effects.dm
@@ -70,6 +70,9 @@
if(!owner.client || owner.body_position == LYING_DOWN || !owner.has_gravity() || (owner.movement_type & (FLYING|FLOATING)) || forced || owner.buckled)
return
+ if(SEND_SIGNAL(owner, COMSIG_CARBON_LIMPING, (next_leg || right || left)) & COMPONENT_CANCEL_LIMP)
+ return
+
// less limping while we have determination still
var/determined_mod = owner.has_status_effect(/datum/status_effect/determined) ? 0.5 : 1
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index c33d482d345c..e97c3d9ba498 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -329,6 +329,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("ore box", /obj/structure/ore_box, 4, time = 5 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_CONTAINERS),\
new/datum/stack_recipe("wooden crate", /obj/structure/closet/crate/wooden, 6, time = 5 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_FURNITURE),\
new/datum/stack_recipe("baseball bat", /obj/item/melee/baseball_bat, 5, time = 1.5 SECONDS, check_density = FALSE, category = CAT_WEAPON_MELEE),\
+ new/datum/stack_recipe("wooden crutch", /obj/item/cane/crutch/wood, 5, time = 1.5 SECONDS, check_density = FALSE, category = CAT_WEAPON_MELEE),\
new/datum/stack_recipe("loom", /obj/structure/loom, 10, time = 1.5 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_TOOLS), \
new/datum/stack_recipe("mortar", /obj/item/reagent_containers/cup/mortar, 3, check_density = FALSE, category = CAT_CHEMISTRY), \
new/datum/stack_recipe("firebrand", /obj/item/match/firebrand, 2, time = 10 SECONDS, check_density = FALSE, category = CAT_TOOLS), \
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 8209c47536e1..a8dd52a274b9 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -519,6 +519,28 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE)
return COMPONENT_NO_DEFAULT_MESSAGE
+/obj/item/cane/crutch
+ name = "medical crutch"
+ desc = "A medical crutch used by people missing a leg. Not all that useful if you're missing both of them, though."
+ icon = 'icons/obj/weapons/staff.dmi'
+ icon_state = "crutch_med"
+ inhand_icon_state = "crutch_med"
+ lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
+ force = 12
+ throwforce = 8
+ w_class = WEIGHT_CLASS_BULKY
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.5)
+ attack_verb_continuous = list("bludgeons", "whacks", "thrashes")
+ attack_verb_simple = list("bludgeon", "whack", "thrash")
+
+/obj/item/cane/crutch/wood
+ name = "wooden crutch"
+ desc = "A handmade crutch. Also makes a decent bludgeon if you need it."
+ icon_state = "crutch_wood"
+ inhand_icon_state = "crutch_wood"
+ custom_materials = list(/datum/material/wood = SMALL_MATERIAL_AMOUNT * 0.5)
+
/obj/item/staff
name = "wizard staff"
desc = "Apparently a staff used by the wizard."
diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm
index 02e04557f420..df20ee846a79 100644
--- a/code/game/turfs/closed/walls.dm
+++ b/code/game/turfs/closed/walls.dm
@@ -51,6 +51,7 @@
return
leaner.start_leaning(src)
+// NON-MODULE CHANGE START
/mob/living/proc/start_leaning(turf/closed/wall/wall)
var/new_y = base_pixel_y + pixel_y
var/new_x = base_pixel_x + pixel_x
@@ -65,7 +66,7 @@
new_x -= LEANING_OFFSET
animate(src, 0.2 SECONDS, pixel_x = new_x, pixel_y = new_y)
- add_traits(list(TRAIT_UNDENSE, TRAIT_EXPANDED_FOV), LEANING_TRAIT)
+ add_traits(list(TRAIT_UNDENSE, TRAIT_EXPANDED_FOV, TRAIT_NO_LEG_AID), LEANING_TRAIT)
visible_message(
span_notice("[src] leans against [wall]."),
span_notice("You lean against [wall]."),
@@ -75,9 +76,16 @@
COMSIG_LIVING_DISARM_HIT,
COMSIG_LIVING_GET_PULLED,
COMSIG_MOVABLE_TELEPORTING,
- COMSIG_ATOM_DIR_CHANGE,
+ COMSIG_LIVING_RESIST,
), PROC_REF(stop_leaning))
+ RegisterSignal(src, COMSIG_ATOM_POST_DIR_CHANGE, PROC_REF(stop_leaning_dir))
update_fov()
+ update_limbless_locomotion()
+
+/mob/living/proc/stop_leaning_dir(datum/source, old_dir, new_dir)
+ SIGNAL_HANDLER
+ if(new_dir != old_dir)
+ stop_leaning()
/mob/living/proc/stop_leaning()
SIGNAL_HANDLER
@@ -86,11 +94,14 @@
COMSIG_LIVING_DISARM_HIT,
COMSIG_LIVING_GET_PULLED,
COMSIG_MOVABLE_TELEPORTING,
- COMSIG_ATOM_DIR_CHANGE,
+ COMSIG_ATOM_POST_DIR_CHANGE,
+ COMSIG_LIVING_RESIST,
))
animate(src, 0.2 SECONDS, pixel_x = base_pixel_x, pixel_y = base_pixel_y)
- remove_traits(list(TRAIT_UNDENSE, TRAIT_EXPANDED_FOV), LEANING_TRAIT)
+ remove_traits(list(TRAIT_UNDENSE, TRAIT_EXPANDED_FOV, TRAIT_NO_LEG_AID), LEANING_TRAIT)
update_fov()
+ update_limbless_locomotion()
+// NON-MODULE CHANGE END
/turf/closed/wall/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm
index 1a5b79bd86ab..5331a891362b 100644
--- a/code/modules/mob/living/carbon/carbon_movement.dm
+++ b/code/modules/mob/living/carbon/carbon_movement.dm
@@ -33,52 +33,25 @@
has_momentum = FALSE
// NON-MODULE CHANGE END
-/mob/living/carbon/set_usable_legs(new_value)
- . = ..()
- if(isnull(.))
- return
- if(. == 0)
- if(usable_legs != 0) //From having no usable legs to having some.
- REMOVE_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- else if(usable_legs == 0 && !(movement_type & (FLYING | FLOATING))) //From having usable legs to no longer having them.
- ADD_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- if(!usable_hands)
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
-
-
+// NON-MODULE CHANGE START
/mob/living/carbon/set_usable_hands(new_value)
. = ..()
if(isnull(.))
return
if(. == 0)
REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, LACKING_MANIPULATION_APPENDAGES_TRAIT)
- if(usable_hands != 0) //From having no usable hands to having some.
- REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
else if(usable_hands == 0 && default_num_hands > 0) //From having usable hands to no longer having them.
ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, LACKING_MANIPULATION_APPENDAGES_TRAIT)
- if(!usable_legs && !(movement_type & (FLYING | FLOATING)))
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
/mob/living/carbon/on_movement_type_flag_enabled(datum/source, flag, old_movement_type)
. = ..()
if(movement_type & (FLYING | FLOATING) && !(old_movement_type & (FLYING | FLOATING)))
- remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
- remove_traits(list(TRAIT_FLOORED, TRAIT_IMMOBILIZED), LACKING_LOCOMOTION_APPENDAGES_TRAIT)
+ update_limbless_locomotion()
+ update_limbless_movespeed_mod()
/mob/living/carbon/on_movement_type_flag_disabled(datum/source, flag, old_movement_type)
. = ..()
if(old_movement_type & (FLYING | FLOATING) && !(movement_type & (FLYING | FLOATING)))
- var/limbless_slowdown = 0
- if(usable_legs < default_num_legs)
- limbless_slowdown += (default_num_legs - usable_legs) * 3
- if(!usable_legs)
- ADD_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- if(usable_hands < default_num_hands)
- limbless_slowdown += (default_num_hands - usable_hands) * 3
- if(!usable_hands)
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- if(limbless_slowdown)
- add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/limbless, multiplicative_slowdown = limbless_slowdown)
- else
- remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
+ update_limbless_locomotion()
+ update_limbless_movespeed_mod()
+// NON-MODULE CHANGE END
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 6b28e33c06b4..5fc589f278d9 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -166,7 +166,7 @@
damage_text = "limp and lifeless"
else
damage_text = (body_part.brute_dam >= body_part.burn_dam) ? body_part.heavy_brute_msg : body_part.heavy_burn_msg
- msg += "[capitalize(t_his)] [body_part.name] is [damage_text]!\n"
+ msg += "[capitalize(t_his)] [body_part.plaintext_zone] is [damage_text]!\n"
//stores missing limbs
var/l_limbs_missing = 0
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 11d84be746a9..c910ba0f54ea 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2269,6 +2269,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
num_legs = new_value
+// NON-MODULE CHANGE START
///Proc to modify the value of usable_legs and hook behavior associated to this event.
/mob/living/proc/set_usable_legs(new_value)
if(usable_legs == new_value)
@@ -2277,19 +2278,23 @@ GLOBAL_LIST_EMPTY(fire_appearances)
stack_trace("[src] had set_usable_legs() called on them with a negative value!")
new_value = 0
- . = usable_legs
+ var/old_value = usable_legs
usable_legs = new_value
- if(new_value > .) // Gained leg usage.
+ update_limbless_locomotion()
+ update_limbless_movespeed_mod()
+
+ return old_value
+
+/// Updates whether the mob is floored or immobilized based on how many limbs they have or are missing.
+/mob/living/proc/update_limbless_locomotion()
+ if(usable_legs > 0 || (movement_type & (FLYING|FLOATING)) || COUNT_TRAIT_SOURCES(src, TRAIT_NO_LEG_AID) >= 2)
REMOVE_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- else if(!(movement_type & (FLYING | FLOATING))) //Lost leg usage, not flying.
- if(!usable_legs)
- ADD_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- if(!usable_hands)
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
-
- update_limbless_movespeed_mod()
+ return
+ ADD_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
+ if(usable_hands == 0)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
/// Updates the mob's movespeed based on how many limbs they have or are missing.
/mob/living/proc/update_limbless_movespeed_mod()
@@ -2305,6 +2310,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/limbless, multiplicative_slowdown = limbless_slowdown)
else
remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
+// NON-MODULE CHANGE END
///Proc to modify the value of num_hands and hook behavior associated to this event.
/mob/living/proc/set_num_hands(new_value)
@@ -2314,18 +2320,24 @@ GLOBAL_LIST_EMPTY(fire_appearances)
num_hands = new_value
+// NON-MODULE CHANGE START
///Proc to modify the value of usable_hands and hook behavior associated to this event.
/mob/living/proc/set_usable_hands(new_value)
if(usable_hands == new_value)
return
- . = usable_hands
+ if(new_value < 0) // Sanity check
+ stack_trace("[src] had set_usable_hands() called on them with a negative value!")
+ new_value = 0
+
+ var/old_value = usable_hands
usable_hands = new_value
- if(new_value > .) // Gained hand usage.
- REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- else if(!(movement_type & (FLYING | FLOATING)) && !usable_hands && !usable_legs) //Lost a hand, not flying, no hands left, no legs.
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
+ if(usable_legs < default_num_legs)
+ update_limbless_locomotion()
+ update_limbless_movespeed_mod()
+ return old_value
+// NON-MODULE CHANGE END
/// Whether or not this mob will escape from storages while being picked up/held.
/mob/living/proc/will_escape_storage()
diff --git a/icons/mob/inhands/weapons/melee_lefthand.dmi b/icons/mob/inhands/weapons/melee_lefthand.dmi
index 79a362a00900..b2a0cd8a8d3a 100644
Binary files a/icons/mob/inhands/weapons/melee_lefthand.dmi and b/icons/mob/inhands/weapons/melee_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/melee_righthand.dmi b/icons/mob/inhands/weapons/melee_righthand.dmi
index 222f8955396c..b04ce88aa257 100644
Binary files a/icons/mob/inhands/weapons/melee_righthand.dmi and b/icons/mob/inhands/weapons/melee_righthand.dmi differ
diff --git a/icons/obj/weapons/staff.dmi b/icons/obj/weapons/staff.dmi
index da97e484df96..88668654a75f 100644
Binary files a/icons/obj/weapons/staff.dmi and b/icons/obj/weapons/staff.dmi differ
diff --git a/maplestation.dme b/maplestation.dme
index d01fed6c5662..06937bed9c94 100644
--- a/maplestation.dme
+++ b/maplestation.dme
@@ -6259,6 +6259,7 @@
#include "maplestation_modules\code\modules\client\preferences\loadout_preference.dm"
#include "maplestation_modules\code\modules\client\preferences\multiline_preferences.dm"
#include "maplestation_modules\code\modules\client\preferences\name_preferences.dm"
+#include "maplestation_modules\code\modules\client\preferences\paraplegic_aid.dm"
#include "maplestation_modules\code\modules\client\preferences\ready_anominity.dm"
#include "maplestation_modules\code\modules\client\preferences\runechat_color.dm"
#include "maplestation_modules\code\modules\client\preferences\sound_frequency.dm"
diff --git a/maplestation_modules/code/datums/components/limbless_aid.dm b/maplestation_modules/code/datums/components/limbless_aid.dm
index fcd0be2f8ff0..9b035914afbb 100644
--- a/maplestation_modules/code/datums/components/limbless_aid.dm
+++ b/maplestation_modules/code/datums/components/limbless_aid.dm
@@ -1,9 +1,9 @@
/// Attach to items that help mobs missing limbs move faster when held.
/datum/component/limbless_aid
/// What slot flags must the parent item have to provide the bonus?
- var/required_slot = ITEM_SLOT_HANDS
+ var/required_slot
/// How much should the movespeed be modified?
- var/movespeed_mod = 0.5
+ var/movespeed_mod
/datum/component/limbless_aid/Initialize(required_slot = ITEM_SLOT_HANDS, movespeed_mod = 0.5)
if(!isitem(parent))
@@ -15,6 +15,7 @@
/datum/component/limbless_aid/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_drop))
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examined))
var/obj/item/item_parent = parent
if(isliving(item_parent.loc))
@@ -22,13 +23,19 @@
on_equip(parent, wearer, wearer.get_slot_by_item(parent))
/datum/component/limbless_aid/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
+ UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ATOM_EXAMINE))
var/obj/item/item_parent = parent
if(isliving(item_parent.loc))
- var/mob/living/wearer = item_parent.loc
- UnregisterSignal(wearer, COMSIG_LIVING_LIMBLESS_MOVESPEED_UPDATE)
- wearer.update_limbless_movespeed_mod()
+ on_drop(item_parent, item_parent.loc)
+
+/datum/component/limbless_aid/proc/examined(obj/item/source, mob/living/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ examine_list += span_info("It will support your weight, allowing you to move faster with a wounded, disabled, or missing leg.")
+ examine_list += span_info("Holding two will allow you to walk despite having two missing or disabled legs.")
+ examine_list += span_info("Resisting will brace you, allowing you to stand on one support, \
+ despite having two missing or disabled legs. Moving will cancel this effect.")
/datum/component/limbless_aid/proc/on_equip(obj/item/source, mob/living/user, slot)
SIGNAL_HANDLER
@@ -36,16 +43,88 @@
if(!(slot & required_slot))
return
+ add_support(user)
+
+/datum/component/limbless_aid/proc/add_support(mob/living/user)
+ ADD_TRAIT(user, TRAIT_NO_LEG_AID, "[REF(src)]_aid")
RegisterSignal(user, COMSIG_LIVING_LIMBLESS_MOVESPEED_UPDATE, PROC_REF(modify_movespeed), override = TRUE)
+ RegisterSignal(user, COMSIG_CARBON_LIMPING, PROC_REF(limp_check), override = TRUE)
+ RegisterSignal(user, COMSIG_LIVING_RESIST, PROC_REF(self_brace), override = TRUE)
+ user.update_limbless_locomotion()
user.update_limbless_movespeed_mod()
/datum/component/limbless_aid/proc/on_drop(obj/item/source, mob/living/user)
SIGNAL_HANDLER
+ lose_support(user)
+
+/datum/component/limbless_aid/proc/lose_support(mob/living/user)
+ REMOVE_TRAIT(user, TRAIT_NO_LEG_AID, "[REF(src)]_aid")
+ un_self_brace(user)
UnregisterSignal(user, COMSIG_LIVING_LIMBLESS_MOVESPEED_UPDATE)
+ UnregisterSignal(user, COMSIG_CARBON_LIMPING)
+ UnregisterSignal(user, COMSIG_LIVING_RESIST)
+ user.update_limbless_locomotion()
user.update_limbless_movespeed_mod()
/datum/component/limbless_aid/proc/modify_movespeed(mob/living/source, list/modifiers)
SIGNAL_HANDLER
- modifiers += movespeed_mod
+ var/obj/item/bodypart/leg = get_braced_leg(source)
+ if(isnull(leg) || leg.bodypart_disabled)
+ modifiers += movespeed_mod
+
+/datum/component/limbless_aid/proc/limp_check(mob/living/source, obj/item/bodypart/next_leg)
+ SIGNAL_HANDLER
+
+ var/obj/item/bodypart/leg = get_braced_leg(source)
+ if(isnull(leg) || leg == next_leg)
+ return COMPONENT_CANCEL_LIMP
+
+#define IS_RIGHT_ARM(index) (index % 2 == 0)
+
+/// Checks what side the item is equipped on
+/datum/component/limbless_aid/proc/get_braced_leg(mob/living/who)
+ if(required_slot & ITEM_SLOT_HANDS)
+ // note this is backwards intentionally:
+ // right arm braces the left leg, and left arm braces right leg
+ var/side = IS_RIGHT_ARM(who.get_held_index_of_item(parent)) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG
+ return who.get_bodypart(side)
+
+ return null // unimplemented
+
+#undef IS_RIGHT_ARM
+
+/datum/component/limbless_aid/proc/self_brace(mob/living/source)
+ SIGNAL_HANDLER
+
+ INVOKE_ASYNC(src, PROC_REF(self_brace_async), source)
+
+/datum/component/limbless_aid/proc/un_self_brace(mob/living/source)
+ REMOVE_TRAIT(source, TRAIT_NO_LEG_AID, "[REF(src)]_brace")
+ UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
+
+/datum/component/limbless_aid/proc/self_brace_async(mob/living/source)
+ if((required_slot & ITEM_SLOT_HANDS) && parent != source.get_active_held_item())
+ return
+ if(HAS_TRAIT_FROM(source, TRAIT_NO_LEG_AID, "[REF(src)]_brace"))
+ return
+ if(DOING_INTERACTION_WITH_TARGET(source, source))
+ return
+ // lying down is a lot harder to get up from
+ if(!do_after(source, (source.body_position == LYING_DOWN ? 2.4 SECONDS : 0.8 SECONDS), source))
+ return
+
+ source.balloon_alert(source, "braced")
+ ADD_TRAIT(source, TRAIT_NO_LEG_AID, "[REF(src)]_brace")
+ RegisterSignal(source, COMSIG_MOVABLE_MOVED, PROC_REF(brace_moved))
+ source.update_limbless_locomotion()
+
+/datum/component/limbless_aid/proc/brace_moved(mob/living/source, atom/old_loc)
+ SIGNAL_HANDLER
+
+ if(source.loc == old_loc)
+ return
+
+ un_self_brace(source)
+ source.update_limbless_locomotion()
diff --git a/maplestation_modules/code/modules/client/preferences/paraplegic_aid.dm b/maplestation_modules/code/modules/client/preferences/paraplegic_aid.dm
new file mode 100644
index 000000000000..5d5fcbfffbc3
--- /dev/null
+++ b/maplestation_modules/code/modules/client/preferences/paraplegic_aid.dm
@@ -0,0 +1,87 @@
+#define WHEELCHAIR_PREFERENCE "Wheelchair"
+#define CRUTCHES_MED_PREFERENCE "Crutches (Medical)"
+#define CRUTCHES_WOOD_PREFERENCE "Crutches (Wooden)"
+#define NONE_PREFERENCE "None"
+
+/// Preference for paraplegics to choose how they get around.
+/datum/preference/choiced/paraplegic_aid
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "paraplegic_aid"
+ savefile_identifier = PREFERENCE_CHARACTER
+ can_randomize = FALSE
+ should_generate_icons = TRUE
+
+/datum/preference/choiced/paraplegic_aid/create_default_value()
+ return WHEELCHAIR_PREFERENCE
+
+/datum/preference/choiced/paraplegic_aid/init_possible_values()
+ return list(WHEELCHAIR_PREFERENCE, CRUTCHES_MED_PREFERENCE, CRUTCHES_WOOD_PREFERENCE, NONE_PREFERENCE)
+
+/datum/preference/choiced/paraplegic_aid/icon_for(value)
+ switch(value)
+ if(WHEELCHAIR_PREFERENCE)
+ return icon(/obj/item/wheelchair::icon, /obj/item/wheelchair::icon_state)
+ if(CRUTCHES_MED_PREFERENCE)
+ return icon(/obj/item/cane/crutch::icon, /obj/item/cane/crutch::icon_state)
+ if(CRUTCHES_WOOD_PREFERENCE)
+ return icon(/obj/item/cane/crutch/wood::icon, /obj/item/cane/crutch/wood::icon_state)
+ if(NONE_PREFERENCE)
+ return icon('icons/hud/screen_gen.dmi', "x")
+
+ return icon('icons/effects/random_spawners.dmi', "questionmark")
+
+/datum/preference/choiced/paraplegic_aid/is_accessible(datum/preferences/preferences)
+ if(!..(preferences))
+ return FALSE
+
+ return /datum/quirk/paraplegic::name in preferences.all_quirks
+
+/datum/preference/choiced/paraplegic_aid/apply_to_human(mob/living/carbon/human/target, value)
+ return
+
+/datum/quirk_constant_data/paraplegic
+ associated_typepath = /datum/quirk/paraplegic
+ customization_options = list(/datum/preference/choiced/paraplegic_aid)
+
+// Overrides paraplegic normal add unique to do our own thing
+/datum/quirk/paraplegic/add_unique(client/client_source)
+
+ var/wheelchair_type = client_source?.prefs?.read_preference(/datum/preference/choiced/paraplegic_aid) || NONE_PREFERENCE
+
+ switch(wheelchair_type)
+ if(WHEELCHAIR_PREFERENCE)
+ // Handle late joins being buckled to arrival shuttle chairs.
+ quirk_holder.buckled?.unbuckle_mob(quirk_holder)
+
+ var/turf/holder_turf = get_turf(quirk_holder)
+ var/obj/structure/chair/spawn_chair = locate() in holder_turf
+
+ var/obj/vehicle/ridden/wheelchair/wheels
+ // More than 5k score? you unlock the gamer wheelchair.
+ if(client_source?.get_award_status(/datum/award/score/hardcore_random) >= 5000)
+ wheels = new /obj/vehicle/ridden/wheelchair/gold(holder_turf)
+ else
+ wheels = new /obj/vehicle/ridden/wheelchair(holder_turf)
+
+ // Makes spawning on the arrivals shuttle more consistent looking
+ if(spawn_chair)
+ wheels.setDir(spawn_chair.dir)
+
+ 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.
+ for(var/obj/item/dropped_item in holder_turf)
+ if(dropped_item.fingerprintslast == quirk_holder.ckey)
+ quirk_holder.put_in_hands(dropped_item)
+
+ if(CRUTCHES_MED_PREFERENCE, CRUTCHES_WOOD_PREFERENCE)
+ var/crutch_type = wheelchair_type == CRUTCHES_MED_PREFERENCE ? /obj/item/cane/crutch : /obj/item/cane/crutch/wood
+ var/turf/holder_turf = get_turf(quirk_holder)
+ for(var/hand in quirk_holder.held_items)
+ quirk_holder.put_in_hands(new crutch_type(holder_turf))
+
+#undef WHEELCHAIR_PREFERENCE
+#undef CRUTCHES_MED_PREFERENCE
+#undef CRUTCHES_WOOD_PREFERENCE
+#undef NONE_PREFERENCE
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/paraplegic_aid.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/paraplegic_aid.tsx
new file mode 100644
index 000000000000..1a46a4c4b9cd
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/paraplegic_aid.tsx
@@ -0,0 +1,6 @@
+import { FeatureIconnedDropdownInput, FeatureWithIcons } from '../base';
+
+export const paraplegic_aid: FeatureWithIcons = {
+ name: 'Paraplegia Aid',
+ component: FeatureIconnedDropdownInput,
+};