diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 69f20b7d8f6..2c906a34868 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -515,3 +515,6 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE)))
#define ANON_DISABLED "" //so it's falsey
#define ANON_RANDOMNAMES "Random Default"
#define ANON_EMPLOYEENAMES "Employees"
+
+/// Possible value of [/atom/movable/buckle_lying]. If set to a different (positive-or-zero) value than this, the buckling thing will force a lying angle on the buckled.
+#define NO_BUCKLE_LYING -1
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 25d2cd2990c..266b55ce4c4 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -209,6 +209,10 @@
#define SENTIENCE_BOSS 5
//Mob AI Status
+#define POWER_RESTORATION_OFF 0
+#define POWER_RESTORATION_START 1
+#define POWER_RESTORATION_SEARCH_APC 2
+#define POWER_RESTORATION_APC_FOUND 3
//Hostile simple animals
//If you add a new status, be sure to add a list for it to the simple_animals global in _globalvars/lists/mobs.dm
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index bba30c2cb72..4e498c20cd5 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -81,6 +81,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FLOORED "floored" //Prevents standing or staying up on its own.
/// Prevents usage of manipulation appendages (picking, holding or using items, manipulating storage).
#define TRAIT_HANDS_BLOCKED "handsblocked"
+/// Inability to access UI hud elements. Turned into a trait from [MOBILITY_UI] to be able to track sources.
+#define TRAIT_UI_BLOCKED "uiblocked"
+/// Inability to pull things. Turned into a trait from [MOBILITY_PULL] to be able to track sources.
+#define TRAIT_PULL_BLOCKED "pullblocked"
/// Abstract condition that prevents movement if being pulled and might be resisted against. Handcuffs and straight jackets, basically.
#define TRAIT_RESTRAINED "restrained"
#define TRAIT_INCAPACITATED "incapacitated"
@@ -292,6 +296,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define MAPPING_HELPER_TRAIT "mapping-helper" //obtained from mapping helper
/// Trait associated to wearing a suit
#define SUIT_TRAIT "suit"
+/// Trait associated to lying down (having a [lying_angle] of a different value than zero).
+#define LYING_DOWN_TRAIT "lying-down"
+/// Trait associated to lacking electrical power.
+#define POWER_LACK_TRAIT "power-lack"
// unique trait sources, still defines
#define CLONING_POD_TRAIT "cloning-pod"
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index ded403262ef..f57925ba461 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -230,6 +230,7 @@
if(!.)
return
ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+ ADD_TRAIT(owner, TRAIT_HANDS_BLOCKED, TRAIT_STATUS_EFFECT(id))
owner.update_mobility()
/datum/status_effect/grouped/stasis/tick()
@@ -237,6 +238,7 @@
/datum/status_effect/grouped/stasis/on_remove()
REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+ REMOVE_TRAIT(owner, TRAIT_HANDS_BLOCKED, TRAIT_STATUS_EFFECT(id))
owner.update_mobility()
update_time_of_death()
return ..()
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 28d807459a9..bb3db37c6e1 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -1,6 +1,7 @@
/atom/movable
var/can_buckle = FALSE
- var/buckle_lying = -1 //bed-like behaviour, forces mob.lying = buckle_lying if != -1
+ /// Bed-like behaviour, forces mob.lying = buckle_lying if not set to [NO_BUCKLE_LYING].
+ var/buckle_lying = NO_BUCKLE_LYING
var/buckle_requires_restraints = FALSE //require people to be handcuffed before being able to buckle. eg: pipes
var/list/mob/living/buckled_mobs = null //list()
var/max_buckled_mobs = 1
diff --git a/code/game/objects/structures/manned_turret.dm b/code/game/objects/structures/manned_turret.dm
index 4ca79754e22..be832d087ca 100644
--- a/code/game/objects/structures/manned_turret.dm
+++ b/code/game/objects/structures/manned_turret.dm
@@ -9,7 +9,7 @@
anchored = FALSE
density = TRUE
max_integrity = 100
- buckle_lying = FALSE
+ buckle_lying = 0
layer = ABOVE_MOB_LAYER
var/view_range = 2.5
var/cooldown = 0
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index e2aa6ad4242..5cbb76503b6 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -552,7 +552,7 @@
smoothing_groups = null
canSmoothWith = null
can_buckle = 1
- buckle_lying = -1
+ buckle_lying = NO_BUCKLE_LYING
buckle_requires_restraints = TRUE
var/mob/living/carbon/human/patient = null
var/obj/machinery/computer/operating/computer = null
diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm
index 6f5d5463cfb..35959589b50 100644
--- a/code/game/objects/structures/target_stake.dm
+++ b/code/game/objects/structures/target_stake.dm
@@ -7,7 +7,7 @@
flags_1 = CONDUCT_1
can_buckle = TRUE
max_buckled_mobs = 1
- buckle_lying = FALSE
+ buckle_lying = 0
var/obj/item/target/pinned_target
/obj/structure/target_stake/Destroy()
diff --git a/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm b/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm
index 5520b640b7e..67f24a3d92d 100644
--- a/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm
+++ b/code/modules/atmospherics/machinery/pipes/heat_exchange/he_pipes.dm
@@ -2,7 +2,7 @@
var/minimum_temperature_difference = 20
var/thermal_conductivity = WINDOW_HEAT_TRANSFER_COEFFICIENT
color = "#404040"
- buckle_lying = -1
+ buckle_lying = NO_BUCKLE_LYING
var/icon_temperature = T20C //stop small changes in temperature causing icon refresh
resistance_flags = LAVA_PROOF | FIRE_PROOF
diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm
index 558a08b4063..e2889f6a040 100644
--- a/code/modules/atmospherics/machinery/pipes/pipes.dm
+++ b/code/modules/atmospherics/machinery/pipes/pipes.dm
@@ -12,7 +12,7 @@
//Buckling
can_buckle = TRUE
buckle_requires_restraints = TRUE
- buckle_lying = -1
+ buckle_lying = NO_BUCKLE_LYING
/obj/machinery/atmospherics/pipe/New()
add_atom_colour(pipe_color, FIXED_COLOUR_PRIORITY)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index d239ee6459b..e5e052fe995 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -542,12 +542,17 @@
if(dna)
dna.real_name = real_name
-/mob/living/carbon/update_mobility()
+
+/mob/living/carbon/set_lying_angle(new_lying)
. = ..()
- if(!(mobility_flags & MOBILITY_STAND))
- add_movespeed_modifier(/datum/movespeed_modifier/carbon_crawling)
- else
- remove_movespeed_modifier(/datum/movespeed_modifier/carbon_crawling)
+ if(isnull(.))
+ return
+ switch(lying_angle)
+ if(90, 270)
+ add_movespeed_modifier(/datum/movespeed_modifier/carbon_crawling)
+ else
+ remove_movespeed_modifier(/datum/movespeed_modifier/carbon_crawling)
+
//Updates the mob's health from bodyparts and mob damage variables
/mob/living/carbon/updatehealth()
@@ -1281,7 +1286,5 @@
if(.)
if(!handcuffed)
REMOVE_TRAIT(src, TRAIT_RESTRAINED, HANDCUFFED_TRAIT)
- REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, HANDCUFFED_TRAIT)
else if(handcuffed)
ADD_TRAIT(src, TRAIT_RESTRAINED, HANDCUFFED_TRAIT)
- ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, HANDCUFFED_TRAIT)
diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm
index 7367995a613..7f65b79ddca 100644
--- a/code/modules/mob/living/carbon/carbon_movement.dm
+++ b/code/modules/mob/living/carbon/carbon_movement.dm
@@ -64,11 +64,22 @@
. = ..()
if(isnull(.))
return
- if(. & !(FLYING | FLOATING))
+ if(!(. & (FLYING | FLOATING)))
if(movement_type & (FLYING | FLOATING)) //From not flying to flying.
+ remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
REMOVE_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- else if(!(movement_type & (FLYING | FLOATING)) && !usable_legs) //From flying to no longer flying.
- ADD_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- if(!usable_hands)
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
+ else if(!(movement_type & (FLYING | FLOATING))) //From flying to no longer flying.
+ 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)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index edf9be57b88..1b128824f11 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -149,7 +149,6 @@
update_inv_w_uniform()
if(wear_suit.breakouttime) //when equipping a straightjacket
ADD_TRAIT(src, TRAIT_RESTRAINED, SUIT_TRAIT)
- ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, SUIT_TRAIT)
stop_pulling() //can't pull if restrained
update_action_buttons_icon() //certain action buttons will no longer be usable.
update_inv_wear_suit()
@@ -206,7 +205,6 @@
dropItemToGround(s_store, TRUE) //It makes no sense for your suit storage to stay on you if you drop your suit.
if(wear_suit.breakouttime) //when unequipping a straightjacket
REMOVE_TRAIT(src, TRAIT_RESTRAINED, SUIT_TRAIT)
- REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, SUIT_TRAIT)
drop_all_held_items() //suit is restraining
update_action_buttons_icon() //certain action buttons may be usable again.
wear_suit = null
diff --git a/code/modules/mob/living/init_signals.dm b/code/modules/mob/living/init_signals.dm
index dd4c88b3355..586ff858031 100644
--- a/code/modules/mob/living/init_signals.dm
+++ b/code/modules/mob/living/init_signals.dm
@@ -1,4 +1,4 @@
-///Called on /mob/living/Initialize(), for the mob to register to relevant signals.
+/// Called on [/mob/living/Initialize()], for the mob to register to relevant signals.
/mob/living/proc/register_init_signals()
RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_KNOCKEDOUT), .proc/on_knockedout_trait_gain)
RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_KNOCKEDOUT), .proc/on_knockedout_trait_loss)
@@ -9,9 +9,24 @@
RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED), .proc/on_immobilized_trait_gain)
RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_IMMOBILIZED), .proc/on_immobilized_trait_loss)
+ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_FLOORED), .proc/on_floored_trait_gain)
+ RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_FLOORED), .proc/on_floored_trait_loss)
+
RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED), .proc/on_handsblocked_trait_gain)
RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_HANDS_BLOCKED), .proc/on_handsblocked_trait_loss)
+ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_UI_BLOCKED), .proc/on_ui_blocked_trait_gain)
+ RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_UI_BLOCKED), .proc/on_ui_blocked_trait_loss)
+
+ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_PULL_BLOCKED), .proc/on_pull_blocked_trait_gain)
+ RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_PULL_BLOCKED), .proc/on_pull_blocked_trait_loss)
+
+ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED), .proc/on_incapacitated_trait_gain)
+ RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_INCAPACITATED), .proc/on_incapacitated_trait_loss)
+
+ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_RESTRAINED), .proc/on_restrained_trait_gain)
+ RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_RESTRAINED), .proc/on_restrained_trait_loss)
+
RegisterSignal(src, list(
SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION),
SIGNAL_REMOVETRAIT(TRAIT_CRITICAL_CONDITION),
@@ -21,48 +36,132 @@
), .proc/update_succumb_action)
-///Called when TRAIT_KNOCKEDOUT is added to the mob.
+/// Called when [TRAIT_KNOCKEDOUT] is added to the mob.
/mob/living/proc/on_knockedout_trait_gain(datum/source)
+ SIGNAL_HANDLER
if(stat < UNCONSCIOUS)
set_stat(UNCONSCIOUS)
-///Called when TRAIT_KNOCKEDOUT is removed from the mob.
+/// Called when [TRAIT_KNOCKEDOUT] is removed from the mob.
/mob/living/proc/on_knockedout_trait_loss(datum/source)
+ SIGNAL_HANDLER
if(stat <= UNCONSCIOUS)
update_stat()
-///Called when TRAIT_DEATHCOMA is added to the mob.
+/// Called when [TRAIT_DEATHCOMA] is added to the mob.
/mob/living/proc/on_deathcoma_trait_gain(datum/source)
+ SIGNAL_HANDLER
ADD_TRAIT(src, TRAIT_KNOCKEDOUT, TRAIT_DEATHCOMA)
-///Called when TRAIT_DEATHCOMA is removed from the mob.
+/// Called when [TRAIT_DEATHCOMA] is removed from the mob.
/mob/living/proc/on_deathcoma_trait_loss(datum/source)
+ SIGNAL_HANDLER
REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, TRAIT_DEATHCOMA)
-///Called when TRAIT_IMMOBILIZED is added to the mob.
+/// Called when [TRAIT_IMMOBILIZED] is added to the mob.
/mob/living/proc/on_immobilized_trait_gain(datum/source)
+ SIGNAL_HANDLER
mobility_flags &= ~MOBILITY_MOVE
-///Called when TRAIT_IMMOBILIZED is removed from the mob.
+/// Called when [TRAIT_IMMOBILIZED] is removed from the mob.
/mob/living/proc/on_immobilized_trait_loss(datum/source)
+ SIGNAL_HANDLER
mobility_flags |= MOBILITY_MOVE
-///Called when TRAIT_HANDS_BLOCKED is added to the mob.
+/// Called when [TRAIT_FLOORED] is added to the mob.
+/mob/living/proc/on_floored_trait_gain(datum/source)
+ SIGNAL_HANDLER
+ if(buckled && buckled.buckle_lying != NO_BUCKLE_LYING)
+ return // Handled by the buckle.
+ mobility_flags &= ~MOBILITY_STAND
+ if(lying_angle == 0) //force them on the ground
+ set_lying_angle(pick(90, 270))
+ on_fall()
+
+
+/// Called when [TRAIT_FLOORED] is removed from the mob.
+/mob/living/proc/on_floored_trait_loss(datum/source)
+ SIGNAL_HANDLER
+ mobility_flags |= MOBILITY_STAND
+ if(!resting)
+ get_up()
+
+
+/// Called when [TRAIT_HANDS_BLOCKED] is added to the mob.
/mob/living/proc/on_handsblocked_trait_gain(datum/source)
+ SIGNAL_HANDLER
mobility_flags &= ~(MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_STORAGE)
drop_all_held_items()
+ ADD_TRAIT(src, TRAIT_UI_BLOCKED, TRAIT_HANDS_BLOCKED)
+ ADD_TRAIT(src, TRAIT_PULL_BLOCKED, TRAIT_HANDS_BLOCKED)
-///Called when TRAIT_HANDS_BLOCKED is removed from the mob.
+/// Called when [TRAIT_HANDS_BLOCKED] is removed from the mob.
/mob/living/proc/on_handsblocked_trait_loss(datum/source)
+ SIGNAL_HANDLER
mobility_flags |= (MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_STORAGE)
+ REMOVE_TRAIT(src, TRAIT_UI_BLOCKED, TRAIT_HANDS_BLOCKED)
+ REMOVE_TRAIT(src, TRAIT_PULL_BLOCKED, TRAIT_HANDS_BLOCKED)
-/// Called when traits that alter succumbing are added/removed.
-/// Will show or hide the succumb alert prompt.
+/// Called when [TRAIT_UI_BLOCKED] is added to the mob.
+/mob/living/proc/on_ui_blocked_trait_gain(datum/source)
+ SIGNAL_HANDLER
+ mobility_flags &= ~(MOBILITY_UI)
+ unset_machine()
+
+/// Called when [TRAIT_UI_BLOCKED] is removed from the mob.
+/mob/living/proc/on_ui_blocked_trait_loss(datum/source)
+ SIGNAL_HANDLER
+ mobility_flags |= MOBILITY_UI
+
+
+/// Called when [TRAIT_PULL_BLOCKED] is added to the mob.
+/mob/living/proc/on_pull_blocked_trait_gain(datum/source)
+ SIGNAL_HANDLER
+ mobility_flags &= ~(MOBILITY_PULL)
+ if(pulling)
+ stop_pulling()
+
+/// Called when [TRAIT_PULL_BLOCKED] is removed from the mob.
+/mob/living/proc/on_pull_blocked_trait_loss(datum/source)
+ SIGNAL_HANDLER
+ mobility_flags |= MOBILITY_PULL
+
+
+/// Called when [TRAIT_INCAPACITATED] is added to the mob.
+/mob/living/proc/on_incapacitated_trait_gain(datum/source)
+ SIGNAL_HANDLER
+ ADD_TRAIT(src, TRAIT_UI_BLOCKED, TRAIT_INCAPACITATED)
+ ADD_TRAIT(src, TRAIT_PULL_BLOCKED, TRAIT_INCAPACITATED)
+
+/// Called when [TRAIT_INCAPACITATED] is removed from the mob.
+/mob/living/proc/on_incapacitated_trait_loss(datum/source)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_UI_BLOCKED, TRAIT_INCAPACITATED)
+ REMOVE_TRAIT(src, TRAIT_PULL_BLOCKED, TRAIT_INCAPACITATED)
+
+
+/// Called when [TRAIT_RESTRAINED] is added to the mob.
+/mob/living/proc/on_restrained_trait_gain(datum/source)
+ SIGNAL_HANDLER
+ ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, TRAIT_RESTRAINED)
+
+/// Called when [TRAIT_RESTRAINED] is removed from the mob.
+/mob/living/proc/on_restrained_trait_loss(datum/source)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, TRAIT_RESTRAINED)
+
+
+/**
+ * Called when traits that alter succumbing are added/removed.
+ *
+ * Will show or hide the succumb alert prompt.
+ */
/mob/living/proc/update_succumb_action()
+ SIGNAL_HANDLER
if (CAN_SUCCUMB(src))
throw_alert("succumb", /obj/screen/alert/succumb)
else
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 589c510eb14..2fe067f0265 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -385,7 +385,7 @@
death()
/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_stasis = FALSE)
- if(stat || HAS_TRAIT(src, TRAIT_INCAPACITATED) || (!ignore_restraints && (HAS_TRAIT(src, TRAIT_RESTRAINED) || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE))) || (!ignore_stasis && IS_IN_STASIS(src)))
+ if(HAS_TRAIT(src, TRAIT_INCAPACITATED) || (!ignore_restraints && (HAS_TRAIT(src, TRAIT_RESTRAINED) || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE))) || (!ignore_stasis && IS_IN_STASIS(src)))
return TRUE
/mob/living/canUseStorage()
@@ -427,30 +427,43 @@
/mob/proc/get_contents()
+
/mob/living/proc/lay_down()
set name = "Rest"
set category = "IC"
- if(!resting)
- set_resting(TRUE, FALSE)
- else
- if(do_after(src, 10, target = src))
- set_resting(FALSE, FALSE)
- else
- to_chat(src, "You fail to get up!")
+ set_resting(!resting, FALSE)
///Proc to hook behavior to the change of value in the resting variable.
-/mob/living/proc/set_resting(rest, silent = TRUE)
- if(rest == resting)
+/mob/living/proc/set_resting(new_resting, silent = TRUE)
+ if(new_resting == resting)
return
- if(!silent)
- if(rest)
- to_chat(src, "You are now resting.")
+ . = resting
+ resting = new_resting
+ if(new_resting)
+ if(lying_angle == 90 || lying_angle == 270)
+ if(!silent)
+ to_chat(src, "You will now try to stay lying down on the floor.")
+ else if(buckled && buckled.buckle_lying != NO_BUCKLE_LYING)
+ if(!silent)
+ to_chat(src, "You will now lay down as soon as you are able to.")
else
- to_chat(src, "You get up.")
- . = rest
- resting = rest
+ if(!silent)
+ to_chat(src, "You lay down.")
+ set_lying_down()
+ else
+ if(lying_angle == 0)
+ if(!silent)
+ to_chat(src, "You will now try to remain standing up.")
+ else if(HAS_TRAIT(src, TRAIT_FLOORED) || (buckled && buckled.buckle_lying != NO_BUCKLE_LYING))
+ if(!silent)
+ to_chat(src, "You will now stand up as soon as you are able to.")
+ else
+ if(!silent)
+ to_chat(src, "You stand up.")
+ get_up()
+
update_resting()
@@ -459,6 +472,31 @@
update_mobility()
+/mob/living/proc/get_up()
+ set waitfor = FALSE
+ var/static/datum/callback/rest_checks = CALLBACK(src, .proc/rest_checks_callback)
+ if(!do_mob(src, src, 2 SECONDS, uninterruptible = TRUE, extra_checks = rest_checks))
+ return
+ if(resting || lying_angle == 0 || HAS_TRAIT(src, TRAIT_FLOORED))
+ return
+ set_lying_angle(0)
+
+
+/mob/living/proc/rest_checks_callback()
+ if(resting || lying_angle == 0 || HAS_TRAIT(src, TRAIT_FLOORED))
+ return FALSE
+ return TRUE
+
+
+/mob/living/proc/set_lying_down(new_lying_angle)
+ if(buckled && buckled.buckle_lying == 0)
+ return
+ if(!new_lying_angle)
+ set_lying_angle(pick(90, 270))
+ else
+ set_lying_angle(new_lying_angle)
+
+
//Recursive function to find everything a mob is holding. Really shitty proc tbh.
/mob/living/get_contents()
var/list/ret = list()
@@ -1177,53 +1215,9 @@
return ..() && !(buckled && buckled.buckle_prevents_pull)
-//Updates lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
-//Robots, animals and brains have their own version so don't worry about them
+//Updates lying and icons on robots, animals and brains. Needs to be refactored to use traits and update based on events.
/mob/living/proc/update_mobility()
- var/canstand_involuntary = !HAS_TRAIT(src, TRAIT_FLOORED)
- var/canstand = canstand_involuntary && !resting
-
- if(buckled && buckled.buckle_lying != -1)
- if(buckled.buckle_lying != 0)
- mobility_flags &= ~MOBILITY_STAND
- else
- mobility_flags |= MOBILITY_STAND
- set_lying_angle(buckled.buckle_lying)
- else if(!canstand)
- mobility_flags &= ~MOBILITY_STAND
- if(lying_angle == 0) //force them on the ground
- set_lying_angle(pick(90, 270))
- if(!canstand_involuntary)
- on_fall()
- else
- mobility_flags |= MOBILITY_STAND
- set_lying_angle(0)
-
- if(lying_angle != 0 || HAS_TRAIT(src, TRAIT_HANDS_BLOCKED) || incapacitated())
- mobility_flags &= ~(MOBILITY_UI|MOBILITY_PULL)
- else
- mobility_flags |= MOBILITY_UI|MOBILITY_PULL
-
- if(!(mobility_flags & MOBILITY_PULL))
- if(pulling)
- stop_pulling()
- if(!(mobility_flags & MOBILITY_UI))
- unset_machine()
-
- // Movespeed mods based on arms/legs quantity
- if(movement_type & (FLYING | FLOATING))
- remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
- else
- var/limbless_slowdown = 0
- // These checks for <2 should be swapped out for something else if we ever end up with a species with more than 2
- if(usable_legs < default_num_legs)
- limbless_slowdown += (default_num_legs * 3) - (usable_legs * 3)
- if(!usable_legs && usable_hands < default_num_hands)
- limbless_slowdown += (default_num_hands * 3) - (usable_hands * 3)
- 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)
+ return
///Called when mob changes from a standing position into a prone while lacking the ability to stand up at the moment, through update_mobility()
@@ -1437,13 +1431,19 @@
if(lying_angle != 0) //We are not standing up.
if(layer == initial(layer)) //to avoid things like hiding larvas.
layer = LYING_MOB_LAYER //so mob lying always appear behind standing mobs
- if(. == 0) //We became prone and were not before. We lose density and stop bumping passable dense things.
- density = FALSE
+ if(. == 0) // We became prone and were not before.
+ ADD_TRAIT(src, TRAIT_UI_BLOCKED, LYING_DOWN_TRAIT)
+ ADD_TRAIT(src, TRAIT_PULL_BLOCKED, LYING_DOWN_TRAIT)
+ density = FALSE // We lose density and stop bumping passable dense things.
+ if(HAS_TRAIT(src, TRAIT_FLOORED) && !(dir & (NORTH|SOUTH)))
+ setDir(pick(NORTH, SOUTH)) // We are and look helpless.
else //We are prone.
if(layer == LYING_MOB_LAYER)
layer = initial(layer)
if(.) //We were prone before, so we become dense and things can bump into us again.
density = initial(density)
+ REMOVE_TRAIT(src, TRAIT_UI_BLOCKED, LYING_DOWN_TRAIT)
+ REMOVE_TRAIT(src, TRAIT_PULL_BLOCKED, LYING_DOWN_TRAIT)
/**
@@ -1601,6 +1601,7 @@
switch(.) //Previous stat.
if(CONSCIOUS)
if(stat >= UNCONSCIOUS)
+ ADD_TRAIT(src, TRAIT_INCAPACITATED, TRAIT_KNOCKEDOUT)
ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, TRAIT_KNOCKEDOUT)
ADD_TRAIT(src, TRAIT_FLOORED, UNCONSCIOUS_TRAIT)
@@ -1619,6 +1620,7 @@
switch(stat) //Current stat.
if(CONSCIOUS)
if(. >= UNCONSCIOUS)
+ REMOVE_TRAIT(src, TRAIT_INCAPACITATED, TRAIT_KNOCKEDOUT)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, TRAIT_KNOCKEDOUT)
REMOVE_TRAIT(src, TRAIT_FLOORED, UNCONSCIOUS_TRAIT)
@@ -1655,11 +1657,21 @@
if(buckled)
if(!.)
ADD_TRAIT(src, TRAIT_IMMOBILIZED, BUCKLED_TRAIT)
- if(buckled.buckle_lying)
- ADD_TRAIT(src, TRAIT_FLOORED, BUCKLED_TRAIT)
- else if(.)
+ switch(buckled.buckle_lying)
+ if(NO_BUCKLE_LYING) // The buckle doesn't force a lying angle.
+ REMOVE_TRAIT(src, TRAIT_FLOORED, BUCKLED_TRAIT)
+ return
+ if(0) // Forcing to a standing position.
+ REMOVE_TRAIT(src, TRAIT_FLOORED, BUCKLED_TRAIT)
+ else // Forcing to a lying position.
+ ADD_TRAIT(src, TRAIT_FLOORED, BUCKLED_TRAIT)
+ set_lying_angle(buckled.buckle_lying)
+ else if(.) // We unbuckled from something.
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, BUCKLED_TRAIT)
REMOVE_TRAIT(src, TRAIT_FLOORED, BUCKLED_TRAIT)
+ var/atom/movable/old_buckled = .
+ if(old_buckled.buckle_lying == 0 && resting) // The buckle forced us to stay up (like a chair) and our preference is set to resting...
+ set_lying_down() // ...so let's drop on the ground.
/mob/living/set_pulledby(new_pulledby)
@@ -1707,6 +1719,23 @@
. = usable_legs
usable_legs = new_value
+ if(new_value > .) // Gained leg usage.
+ 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)
+
+ if(usable_legs < default_num_legs)
+ var/limbless_slowdown = (default_num_legs - usable_legs) * 3
+ if(!usable_legs && usable_hands < default_num_hands)
+ limbless_slowdown += (default_num_hands - usable_hands) * 3
+ add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/limbless, multiplicative_slowdown = limbless_slowdown)
+ else
+ remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
+
///Proc to modify the value of num_hands and hook behavior associated to this event.
/mob/living/proc/set_num_hands(new_value)
@@ -1723,6 +1752,12 @@
. = 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)
+
+
/// Whether or not this mob will escape from storages while being picked up/held.
/mob/living/proc/will_escape_storage()
return FALSE
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 7ce45c53153..1ef24f3fb43 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -34,7 +34,7 @@
var/list/network = list("ss13")
var/obj/machinery/camera/current
var/list/connected_robots = list()
- var/aiRestorePowerRoutine = 0
+ var/aiRestorePowerRoutine = POWER_RESTORATION_OFF
var/requires_power = POWER_REQ_ALL
var/can_be_carded = TRUE
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list(), "Burglar"=list())
@@ -170,6 +170,9 @@
builtInCamera = new (src)
builtInCamera.network = list("ss13")
+ ADD_TRAIT(src, TRAIT_PULL_BLOCKED, ROUNDSTART_TRAIT)
+
+
/mob/living/silicon/ai/key_down(_key, client/user)
if(findtext(_key, "numpad")) //if it's a numpad number, we can convert it to just the number
_key = _key[7] //strings, lists, same thing really
@@ -798,10 +801,6 @@
/mob/living/silicon/ai/can_buckle()
return FALSE
-/mob/living/silicon/ai/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_stasis = FALSE)
- if(aiRestorePowerRoutine)
- return TRUE
- return ..()
/mob/living/silicon/ai/canUseTopic(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE)
if(control_disabled || incapacitated())
@@ -1032,3 +1031,16 @@
/mob/living/silicon/ai/zMove(dir, feedback = FALSE)
. = eyeobj.zMove(dir, feedback)
+
+
+/// Proc to hook behavior to the changes of the value of [aiRestorePowerRoutine].
+/mob/living/silicon/ai/proc/setAiRestorePowerRoutine(new_value)
+ if(new_value == aiRestorePowerRoutine)
+ return
+ . = aiRestorePowerRoutine
+ aiRestorePowerRoutine = new_value
+ if(aiRestorePowerRoutine)
+ if(!.)
+ ADD_TRAIT(src, TRAIT_INCAPACITATED, POWER_LACK_TRAIT)
+ else if(.)
+ REMOVE_TRAIT(src, TRAIT_INCAPACITATED, POWER_LACK_TRAIT)
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index c40dfd67fd5..1996b816b14 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -1,8 +1,3 @@
-#define POWER_RESTORATION_OFF 0
-#define POWER_RESTORATION_START 1
-#define POWER_RESTORATION_SEARCH_APC 2
-#define POWER_RESTORATION_APC_FOUND 3
-
/mob/living/silicon/ai/Life()
if (stat == DEAD)
return
@@ -110,7 +105,7 @@
T = get_turf(src)
if(isspaceturf(T))
to_chat(src, "Unable to verify! No power connection detected!")
- aiRestorePowerRoutine = POWER_RESTORATION_SEARCH_APC
+ setAiRestorePowerRoutine(POWER_RESTORATION_SEARCH_APC)
return
to_chat(src, "Connection verified. Searching for APC in power network.")
sleep(50)
@@ -131,7 +126,7 @@
to_chat(src, "Unable to locate APC!")
else
to_chat(src, "Lost connection with the APC!")
- aiRestorePowerRoutine = POWER_RESTORATION_SEARCH_APC
+ setAiRestorePowerRoutine(POWER_RESTORATION_SEARCH_APC)
return
if(AIarea.power_equip)
if(!isspaceturf(T))
@@ -152,7 +147,7 @@
apc_override = 1
theAPC.ui_interact(src)
apc_override = 0
- aiRestorePowerRoutine = POWER_RESTORATION_APC_FOUND
+ setAiRestorePowerRoutine(POWER_RESTORATION_APC_FOUND)
sleep(50)
theAPC = null
@@ -162,19 +157,14 @@
to_chat(src, "Alert cancelled. Power has been restored.")
else
to_chat(src, "Alert cancelled. Power has been restored without our assistance.")
- aiRestorePowerRoutine = POWER_RESTORATION_OFF
+ setAiRestorePowerRoutine(POWER_RESTORATION_OFF)
set_blindness(0)
update_sight()
/mob/living/silicon/ai/proc/ai_lose_power()
disconnect_shell()
- aiRestorePowerRoutine = POWER_RESTORATION_START
+ setAiRestorePowerRoutine(POWER_RESTORATION_START)
blind_eyes(1)
update_sight()
to_chat(src, "You've lost power!")
addtimer(CALLBACK(src, .proc/start_RestorePowerRoutine), 20)
-
-#undef POWER_RESTORATION_OFF
-#undef POWER_RESTORATION_START
-#undef POWER_RESTORATION_SEARCH_APC
-#undef POWER_RESTORATION_APC_FOUND
diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm
index 6f8c212ce8c..78dd47bab20 100644
--- a/code/modules/power/singularity/emitter.dm
+++ b/code/modules/power/singularity/emitter.dm
@@ -375,7 +375,7 @@
icon_state_on = "protoemitter_+a"
icon_state_underpowered = "protoemitter_+u"
can_buckle = TRUE
- buckle_lying = FALSE
+ buckle_lying = 0
var/view_range = 4.5
var/datum/action/innate/protoemitter/firing/auto