diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 2a542d85378..504dc89e3a7 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -278,6 +278,8 @@
#define COMSIG_MOVABLE_DISPOSING "movable_disposing"
///called when the movable sucessfully has it's anchored var changed, from base atom/movable/set_anchored(): (value)
#define COMSIG_MOVABLE_SET_ANCHORED "movable_set_anchored"
+///from base of atom/movable/setGrabState(): (newstate)
+#define COMSIG_MOVABLE_SET_GRAB_STATE "living_set_grab_state"
// /mob signals
@@ -364,6 +366,8 @@
#define COMSIG_LIVING_REVIVE "living_revive"
///from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regen_limbs"
+///from base of mob/living/set_buckled(): (new_buckled)
+#define COMSIG_LIVING_SET_BUCKLED "living_set_buckled"
///sent from borg recharge stations: (amount, repairs)
#define COMSIG_PROCESS_BORGCHARGER_OCCUPANT "living_charge"
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 73137890942..60153034da0 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -77,6 +77,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
//mob traits
#define TRAIT_KNOCKEDOUT "knockedout" //Forces the user to stay unconscious.
+#define TRAIT_IMMOBILIZED "immobilized" //Prevents voluntary movement.
#define TRAIT_INCAPACITATED "incapacitated"
#define TRAIT_BLIND "blind"
#define TRAIT_MUTE "mute"
@@ -274,6 +275,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define CRIT_HEALTH_TRAIT "crit_health"
#define OXYLOSS_TRAIT "oxyloss"
#define TURF_TRAIT "turf"
+#define BUCKLED_TRAIT "buckled" //trait associated to being buckled
+#define CHOKEHOLD_TRAIT "chokehold" //trait associated to being held in a chokehold
+#define RESTING_TRAIT "resting" //trait associated to resting
// unique trait sources, still defines
#define CLONING_POD_TRAIT "cloning-pod"
@@ -316,3 +320,5 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TIMESTOP_TRAIT "timestop"
#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name
#define SKILLCHIP_TRAIT "skillchip"
+#define PULLED_WHILE_SOFTCRIT_TRAIT "pulled-while-softcrit"
+#define LOCKED_BORG_TRAIT "locked-borg"
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
index ab000892ba4..680ab83e99a 100644
--- a/code/datums/components/chasm.dm
+++ b/code/datums/components/chasm.dm
@@ -110,8 +110,7 @@
if (isliving(AM))
var/mob/living/L = AM
L.notransform = TRUE
- L.Stun(200)
- L.resting = TRUE
+ L.Paralyze(20 SECONDS)
var/oldtransform = AM.transform
var/oldcolor = AM.color
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index a31203acd62..3a13359f572 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -33,19 +33,33 @@
if(!.)
return
ADD_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id))
+ ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
/datum/status_effect/incapacitating/stun/on_remove()
REMOVE_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id))
+ REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
return ..()
//KNOCKDOWN
/datum/status_effect/incapacitating/knockdown
id = "knockdown"
+
//IMMOBILIZED
/datum/status_effect/incapacitating/immobilized
id = "immobilized"
+/datum/status_effect/incapacitating/immobilized/on_apply()
+ . = ..()
+ if(!.)
+ return
+ ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+
+/datum/status_effect/incapacitating/immobilized/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+ return ..()
+
+
/datum/status_effect/incapacitating/paralyzed
id = "paralyzed"
@@ -54,9 +68,11 @@
if(!.)
return
ADD_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id))
+ ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
/datum/status_effect/incapacitating/paralyzed/on_remove()
REMOVE_TRAIT(owner, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(id))
+ REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
return ..()
//UNCONSCIOUS
@@ -163,12 +179,19 @@
if(.)
update_time_of_death()
owner.reagents?.end_metabolization(owner, FALSE)
- owner.update_mobility()
+
+/datum/status_effect/grouped/stasis/on_apply()
+ . = ..()
+ if(!.)
+ return
+ ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+ owner.update_mobility()
/datum/status_effect/grouped/stasis/tick()
update_time_of_death()
/datum/status_effect/grouped/stasis/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
owner.update_mobility()
update_time_of_death()
return ..()
diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm
index 454bdb395b6..78ceaf3bd87 100644
--- a/code/datums/status_effects/gas.dm
+++ b/code/datums/status_effects/gas.dm
@@ -6,6 +6,16 @@
var/icon/cube
var/can_melt = TRUE
+/datum/status_effect/freon/on_apply()
+ . = ..()
+ if(!.)
+ return
+ ADD_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+
+/datum/status_effect/freon/on_remove()
+ REMOVE_TRAIT(owner, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(id))
+ return ..()
+
/obj/screen/alert/status_effect/freon
name = "Frozen Solid"
desc = "You're frozen inside an ice cube, and cannot move! You can still do stuff, like shooting. Resist out of the cube!"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 3e9eade3782..3d6dcfc3167 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -224,7 +224,7 @@
log_combat(AM, AM.pulledby, "pulled from", src)
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
- AM.pulledby = src
+ AM.set_pulledby(src)
setGrabState(state)
if(ismob(AM))
var/mob/M = AM
@@ -236,14 +236,23 @@
/atom/movable/proc/stop_pulling()
if(pulling)
- pulling.pulledby = null
+ pulling.set_pulledby(null)
var/mob/living/ex_pulled = pulling
+ setGrabState(GRAB_PASSIVE)
pulling = null
- setGrabState(0)
if(isliving(ex_pulled))
var/mob/living/L = ex_pulled
L.update_mobility()// mob gets up if it was lyng down in a chokehold
+
+///Reports the event of the change in value of the pulledby variable.
+/atom/movable/proc/set_pulledby(new_pulledby)
+ if(new_pulledby == pulledby)
+ return FALSE //null signals there was a change, be sure to return FALSE if none happened here.
+ . = pulledby
+ pulledby = new_pulledby
+
+
/atom/movable/proc/Move_Pulled(atom/A)
if(!pulling)
return
@@ -969,7 +978,20 @@
* This exists to act as a hook for behaviour
*/
/atom/movable/proc/setGrabState(newstate)
+ if(newstate == grab_state)
+ return
+ SEND_SIGNAL(src, COMSIG_MOVABLE_SET_GRAB_STATE, newstate)
+ . = grab_state
grab_state = newstate
+ switch(.) //Previous state.
+ if(GRAB_PASSIVE, GRAB_AGGRESSIVE)
+ if(grab_state >= GRAB_NECK)
+ ADD_TRAIT(pulling, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT)
+ switch(grab_state) //Current state.
+ if(GRAB_PASSIVE, GRAB_AGGRESSIVE)
+ if(. >= GRAB_NECK)
+ REMOVE_TRAIT(pulling, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT)
+
/obj/item/proc/do_pickup_animation(atom/target)
set waitfor = FALSE
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 504bcf37bae..cb5a5879f17 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -94,7 +94,7 @@
M.forceMove(loc)
M.buckling = null
- M.buckled = src
+ M.set_buckled(src)
M.setDir(dir)
buckled_mobs |= M
M.update_mobility()
@@ -114,7 +114,7 @@
/atom/movable/proc/unbuckle_mob(mob/living/buckled_mob, force=FALSE)
if(istype(buckled_mob) && buckled_mob.buckled == src && (buckled_mob.can_unbuckle() || force))
. = buckled_mob
- buckled_mob.buckled = null
+ buckled_mob.set_buckled(null)
buckled_mob.set_anchored(initial(buckled_mob.anchored))
buckled_mob.update_mobility()
buckled_mob.clear_alert("buckled")
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index f8a3a5f846f..88d18b11c72 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -304,8 +304,7 @@
log_game("[key_name(user)] has put the MMI/posibrain of [key_name(M.brainmob)] into a cyborg shell at [AREACOORD(src)]")
if(!locomotion)
- O.lockcharge = TRUE
- O.update_mobility()
+ O.set_lockcharge(TRUE)
to_chat(O, "Error: Servo motors unresponsive.")
else
@@ -342,8 +341,7 @@
forceMove(O)
O.robot_suit = src
if(!locomotion)
- O.lockcharge = TRUE
- O.update_mobility()
+ O.set_lockcharge(TRUE)
else if(istype(W, /obj/item/pen))
to_chat(user, "You need to use a multitool to name [src]!")
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 72639c20ce0..fbe2fe73096 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -528,11 +528,11 @@
step_towards(user, T2)
T1 = get_turf(user)
if(T1 == T2)
- user.resting = TRUE //so people can jump into crates without slamming the lid on their head
+ user.set_resting(TRUE) //so people can jump into crates without slamming the lid on their head
if(!close(user))
to_chat(user, "You can't get [src] to close!")
- user.resting = FALSE
+ user.set_resting(FALSE)
return
- user.resting = FALSE
+ user.set_resting(FALSE)
togglelock(user)
T1.visible_message("[user] dives into [src]!")
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index 1f970b121a0..f9486a89776 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -79,7 +79,8 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
if(isliving(A))
var/mob/living/M = A
M.Paralyze(320) // Keep them from moving during the duration of the extraction
- M.buckled = 0 // Unbuckle them to prevent anchoring problems
+ if(M.buckled)
+ M.buckled.unbuckle_mob(M, TRUE) // Unbuckle them to prevent anchoring problems
else
A.set_anchored(TRUE)
A.density = FALSE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 92f836bae15..aa09f11d51b 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -267,7 +267,7 @@
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
- AM.pulledby = src
+ AM.set_pulledby(src)
SEND_SIGNAL(src, COMSIG_LIVING_START_PULL, AM, state, force)
@@ -451,19 +451,26 @@
else
to_chat(src, "You fail to get up!")
+
+///Proc to hook behavior to the change of value in the resting variable.
/mob/living/proc/set_resting(rest, silent = TRUE)
+ if(rest == resting)
+ return
if(!silent)
if(rest)
to_chat(src, "You are now resting.")
else
to_chat(src, "You get up.")
+ . = rest
resting = rest
update_resting()
+
/mob/living/proc/update_resting()
update_rest_hud_icon()
update_mobility()
+
//Recursive function to find everything a mob is holding. Really shitty proc tbh.
/mob/living/get_contents()
var/list/ret = list()
@@ -1197,10 +1204,9 @@
var/has_legs = get_num_legs()
var/has_arms = get_num_arms()
var/paralyzed = IsParalyzed()
- var/stun = IsStun()
var/knockdown = IsKnockdown()
var/ignore_legs = get_leg_ignore()
- var/canmove = !IsImmobilized() && !stun && stat_conscious && !paralyzed && !buckled && (!stat_softcrit || !pulledby) && !chokehold && !IsFrozen() && !IS_IN_STASIS(src) && (has_arms || ignore_legs || has_legs)
+ var/canmove = !HAS_TRAIT(src, TRAIT_IMMOBILIZED) && (has_arms || ignore_legs || has_legs)
if(canmove)
mobility_flags |= MOBILITY_MOVE
else
@@ -1231,7 +1237,7 @@
- var/canitem = !paralyzed && !stun && stat_conscious && !chokehold && !restrained && has_arms
+ var/canitem = !paralyzed && !IsStun() && stat_conscious && !chokehold && !restrained && has_arms
if(canitem)
mobility_flags |= (MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_STORAGE)
else
@@ -1627,12 +1633,54 @@
if(isnull(.))
return
switch(.) //Previous stat.
+ if(CONSCIOUS)
+ if(stat >= UNCONSCIOUS)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
+ if(SOFT_CRIT)
+ if(stat >= UNCONSCIOUS)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT) //adding trait sources should come before removing to avoid unnecessary updates
+ if(pulledby)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT)
if(UNCONSCIOUS)
cure_blind(UNCONSCIOUS_BLIND)
switch(stat) //Current stat.
+ if(CONSCIOUS)
+ if(. >= UNCONSCIOUS)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
+ if(SOFT_CRIT)
+ if(pulledby)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT) //adding trait sources should come before removing to avoid unnecessary updates
+ if(. >= UNCONSCIOUS)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT)
if(UNCONSCIOUS)
become_blind(UNCONSCIOUS_BLIND)
+
+///Reports the event of the change in value of the buckled variable.
+/mob/living/proc/set_buckled(new_buckled)
+ if(new_buckled == buckled)
+ return
+ SEND_SIGNAL(src, COMSIG_LIVING_SET_BUCKLED, new_buckled)
+ . = buckled
+ buckled = new_buckled
+ if(buckled)
+ if(!.)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, BUCKLED_TRAIT)
+ else if(.)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, BUCKLED_TRAIT)
+
+
+/mob/living/set_pulledby(new_pulledby)
+ . = ..()
+ if(. == FALSE) //null is a valid value here, we only want to return if FALSE is explicitly passed.
+ return
+ if(pulledby)
+ if(!. && stat == SOFT_CRIT)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT)
+ else if(. && stat == SOFT_CRIT)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT)
+
+
/// Only defined for carbons who can wear masks and helmets, we just assume other mobs have visible faces
/mob/living/proc/is_face_visible()
return TRUE
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index afad167f231..d51a332655c 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -93,7 +93,7 @@
cut_overlay(fire_overlay)
/mob/living/silicon/robot/update_mobility()
- if(stat || buckled || lockcharge)
+ if(HAS_TRAIT(src, TRAIT_IMMOBILIZED))
mobility_flags &= ~MOBILITY_MOVE
else
mobility_flags = MOBILITY_FLAGS_DEFAULT
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 70f46207645..5132a310251 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -464,8 +464,7 @@
connected_ai.connected_robots -= src
src.connected_ai = null
lawupdate = FALSE
- lockcharge = FALSE
- mobility_flags |= MOBILITY_FLAGS_DEFAULT
+ set_lockcharge(FALSE)
scrambledcodes = TRUE
//Disconnect it's camera so it's not so easily tracked.
if(!QDELETED(builtInCamera))
@@ -487,17 +486,31 @@
W.attack_self(src)
-/mob/living/silicon/robot/proc/SetLockdown(state = 1)
+/mob/living/silicon/robot/proc/SetLockdown(state = TRUE)
// They stay locked down if their wire is cut.
if(wires.is_cut(WIRE_LOCKDOWN))
- state = 1
+ state = TRUE
if(state)
throw_alert("locked", /obj/screen/alert/locked)
else
clear_alert("locked")
- lockcharge = state
+ set_lockcharge(state)
+
+
+///Reports the event of the change in value of the lockcharge variable.
+/mob/living/silicon/robot/proc/set_lockcharge(new_lockcharge)
+ if(new_lockcharge == lockcharge)
+ return
+ . = lockcharge
+ lockcharge = new_lockcharge
+ if(lockcharge)
+ if(!.)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT)
+ else if(.)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT)
update_mobility()
+
/mob/living/silicon/robot/proc/SetEmagged(new_state)
emagged = new_state
module.rebuild_modules()
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 78e282f15c9..9afe35bc144 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -527,8 +527,17 @@
else
..()
+
+/mob/living/simple_animal/update_resting()
+ if(resting)
+ ADD_TRAIT(src, TRAIT_IMMOBILIZED, RESTING_TRAIT)
+ else
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, RESTING_TRAIT)
+ return ..()
+
+
/mob/living/simple_animal/update_mobility(value_otherwise = TRUE)
- if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || IsParalyzed() || IsStun() || IsKnockdown() || IsParalyzed() || stat || resting)
+ if(HAS_TRAIT_NOT_FROM(src, TRAIT_IMMOBILIZED, BUCKLED_TRAIT))
drop_all_held_items()
mobility_flags = NONE
else if(buckled)