diff --git a/code/datums/elements/waddling.dm b/code/datums/elements/waddling.dm
index ef2e73b67a5..d85129b070b 100644
--- a/code/datums/elements/waddling.dm
+++ b/code/datums/elements/waddling.dm
@@ -19,7 +19,8 @@
Waddle(target)
/datum/element/waddling/proc/Waddle(atom/movable/target)
- animate(target, pixel_z = 4, time = 0)
+ var/current_pixel_z = target.pixel_z
+ animate(target, pixel_z = target.pixel_z + 4, time = 0)
var/prev_trans = matrix(target.transform)
- animate(pixel_z = 0, transform = turn(target.transform, pick(-12, 0, 12)), time = 2)
- animate(pixel_z = 0, transform = prev_trans, time = 0)
+ animate(pixel_z = target.pixel_z - 4, transform = turn(target.transform, pick(-12, 0, 12)), time = 2)
+ animate(pixel_z = current_pixel_z, transform = prev_trans, time = 0)
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index c2763b987df..1b358fe1acf 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -565,6 +565,8 @@
if(isspaceturf(get_turf(user))) // Can't fall onto nothing.
return
+ user.pixel_z = initial(user.pixel_z)
+
if(user.m_intent == MOVE_INTENT_RUN)
user.Weaken(10 SECONDS)
else
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 47ba86c7220..0397bc662de 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -32,6 +32,8 @@
return ..()
/obj/structure/Destroy()
+ if(climbable)
+ structure_gone(src)
if(SSticker)
GLOB.cameranet.updateVisibility(src)
if(smooth)
@@ -49,6 +51,9 @@
if(!..())
return FALSE
+ if(climbable)
+ structure_gone(old)
+
if(creates_cover)
if(isturf(old))
REMOVE_TRAIT(old, TRAIT_TURF_COVERED, UNIQUE_TRAIT_SOURCE(src))
@@ -56,7 +61,6 @@
ADD_TRAIT(loc, TRAIT_TURF_COVERED, UNIQUE_TRAIT_SOURCE(src))
return TRUE
-
/obj/structure/has_prints()
return TRUE
@@ -79,6 +83,32 @@
do_climb(usr)
+/obj/structure/proc/animate_jumping_off(mob/living/user)
+ if(!user.flying && user.mob_has_gravity())
+ var/delay = user.movement_delay()/4
+ sleep(delay)
+ animate(user, pixel_z = initial(user.pixel_z), time = 3, easing = BACK_EASING|EASE_IN)
+
+/obj/structure/proc/animate_climb(mob/living/user)
+ if(!istype(user))
+ return
+ if(!user.checkpass(PASSTABLE) && !user.flying && user.mob_size > MOB_SIZE_SMALL)
+ var/delay = user.movement_delay()/2
+ sleep(delay)
+ animate(user, pixel_z = 16, time = 1, easing = LINEAR_EASING)
+ if(user.floating)
+ user.float(TRUE)
+
+/obj/structure/Uncrossed(atom/movable/mover)
+ . = ..()
+ if(!istype(mover, /mob/living))
+ return
+ if(climbable)
+ var/turf/T = get_turf(mover)
+ var/obj/structure/other_structure = locate(/obj/structure) in T
+ if(!other_structure?.climbable)
+ animate_jumping_off(mover)
+
/obj/structure/MouseDrop_T(atom/movable/dropping, mob/user, params)
. = ..()
if(!. && dropping == user)
@@ -94,18 +124,28 @@
return T
return null
-/obj/structure/proc/do_climb(var/mob/living/user)
+/obj/structure/proc/climb_check(mob/living/user)
+ if(user.mob_size == MOB_SIZE_SMALL)
+ return FALSE
+ if(user.flying)
+ return FALSE
if(!can_touch(user) || !climbable)
return FALSE
var/blocking_object = density_check()
if(blocking_object)
- to_chat(user, "You cannot climb [src], as it is blocked by \a [blocking_object]!")
+ to_chat(user, span_warning("You cannot climb [src], as it is blocked by \a [blocking_object]!"))
return FALSE
-
var/turf/T = src.loc
- if(!T || !istype(T)) return FALSE
+ if(!T || !istype(T))
+ return FALSE
- usr.visible_message("[user] starts climbing onto \the [src]!")
+ return TRUE
+
+/obj/structure/proc/do_climb(mob/living/user)
+ if(!climb_check(user))
+ return FALSE
+
+ user.visible_message(span_warning("[user] starts climbing onto \the [src]!"))
climber = user
if(!do_after(user, 50, target = src))
climber = null
@@ -115,12 +155,13 @@
climber = null
return FALSE
- usr.loc = get_turf(src)
+ user.loc = get_turf(src)
+ animate_climb(user)
+
if(get_turf(user) == get_turf(src))
- usr.visible_message("[user] climbs onto \the [src]!")
+ user.visible_message(span_warning("[user] climbs onto \the [src]!"))
clumse_stuff(climber)
-
climber = null
return TRUE
@@ -165,49 +206,65 @@
AM.force /= force_mult
AM.throwforce /= force_mult
+/obj/structure/proc/get_fall_damage(mob/living/L)
+ if(prob(25))
+
+ var/damage = rand(15,30)
+ var/mob/living/carbon/human/H = L
+ if(!istype(H))
+ to_chat(H, span_warning("You land heavily!"))
+ L.adjustBruteLoss(damage)
+ return
+
+ var/obj/item/organ/external/affecting
+
+ switch(pick(list("ankle","wrist","head","knee","elbow")))
+ if("ankle")
+ affecting = H.get_organ(pick(BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_R_FOOT))
+ if("knee")
+ affecting = H.get_organ(pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
+ if("wrist")
+ affecting = H.get_organ(pick(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND))
+ if("elbow")
+ affecting = H.get_organ(pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ if("head")
+ affecting = H.get_organ(BODY_ZONE_HEAD)
+
+ if(affecting)
+ to_chat(L, span_warning("You land heavily on your [affecting.name]!"))
+ affecting.receive_damage(damage, 0)
+ if(affecting.parent)
+ affecting.parent.add_autopsy_data("Misadventure", damage)
+ else
+ to_chat(H, span_warning("You land heavily!"))
+ H.adjustBruteLoss(damage)
+
+ H.UpdateDamageIcon()
+
+/obj/structure/proc/structure_gone(atom/location)
+ for(var/mob/living/carbon/human/H in get_turf(location))
+ H.pixel_z = initial(H.pixel_z)
+ if(H.lying || H.mob_size <= MOB_SIZE_SMALL)
+ continue
+ to_chat(H, span_warning("You stop feeling \the [src] beneath your feet."))
+ if(H.m_intent == MOVE_INTENT_WALK)
+ H.Weaken(3 SECONDS)
+ if(H.m_intent == MOVE_INTENT_RUN)
+ H.Weaken(10 SECONDS)
+ get_fall_damage(H)
/obj/structure/proc/structure_shaken()
for(var/mob/living/M in get_turf(src))
- if(M.lying) return //No spamming this on people.
+ if(M.lying)
+ continue //No spamming this on people.
M.Weaken(10 SECONDS)
- to_chat(M, "You topple as \the [src] moves under you!")
-
- if(prob(25))
-
- var/damage = rand(15,30)
- var/mob/living/carbon/human/H = M
- if(!istype(H))
- to_chat(H, "You land heavily!")
- M.adjustBruteLoss(damage)
- return
-
- var/obj/item/organ/external/affecting
-
- switch(pick(list("ankle","wrist","head","knee","elbow")))
- if("ankle")
- affecting = H.get_organ(pick(BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_R_FOOT))
- if("knee")
- affecting = H.get_organ(pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
- if("wrist")
- affecting = H.get_organ(pick(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND))
- if("elbow")
- affecting = H.get_organ(pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
- if("head")
- affecting = H.get_organ(BODY_ZONE_HEAD)
-
- if(affecting)
- to_chat(M, "You land heavily on your [affecting.name]!")
- affecting.receive_damage(damage, 0)
- if(affecting.parent)
- affecting.parent.add_autopsy_data("Misadventure", damage)
- else
- to_chat(H, "You land heavily!")
- H.adjustBruteLoss(damage)
-
- H.UpdateDamageIcon()
+ to_chat(M, span_warning("You topple as \the [src] moves under you!"))
+
+ get_fall_damage(M)
+
return
/obj/structure/proc/can_touch(mob/living/user)
@@ -229,14 +286,14 @@
. = ..()
if(!(resistance_flags & INDESTRUCTIBLE))
if(resistance_flags & ON_FIRE)
- . += "It's on fire!"
+ . += span_warning("It's on fire!")
if(broken)
- . += "It appears to be broken."
+ . += span_notice("It appears to be broken.")
var/examine_status = examine_status(user)
if(examine_status)
. += examine_status
if(climbable)
- . += "You can Click-Drag someone to [src] to put them on the table after a short delay."
+ . += span_info("You can Click-Drag someone to [src] to put them on the structure after a short delay.")
/obj/structure/proc/examine_status(mob/user) //An overridable proc, mostly for falsewalls.
var/healthpercent = (obj_integrity/max_integrity) * 100
@@ -247,7 +304,7 @@
. += "It appears heavily damaged."
if(0 to 25)
if(!broken)
- . += "It's falling apart!"
+ . += span_warning("It's falling apart!")
/obj/structure/proc/prevents_buckled_mobs_attacking()
return FALSE
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
index 3e3781c55c9..d1e033de241 100644
--- a/code/game/objects/structures/fence.dm
+++ b/code/game/objects/structures/fence.dm
@@ -67,6 +67,31 @@
return TRUE
return FALSE
+/obj/structure/fence/do_climb(mob/living/user)
+ if(!climb_check(user))
+ return FALSE
+
+ user.visible_message("[user] starts climbing onto \the [src]!")
+ climber = user
+ if(!do_after(user, CLIMB_TIME, target = src))
+ climber = null
+ return FALSE
+
+ if(!can_touch(user) || !climbable)
+ climber = null
+ return FALSE
+
+ user.loc = get_turf(src)
+
+ if(get_turf(user) == get_turf(src))
+ user.visible_message("[user] climbs onto \the [src]!")
+
+ clumse_stuff(climber)
+
+ climber = null
+
+ return TRUE
+
/*
Shock user with probability prb (if all connections & power are working)
Returns TRUE if shocked, FALSE otherwise
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index 34b3f63fb2b..1822c032169 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -138,16 +138,53 @@
return TRUE
return FALSE
+/obj/structure/railing/proc/hopping(mob/living/user)
+ if(!istype(user))
+ return
+ var/delay = user.movement_delay()/2
+ sleep(delay)
+ animate(user, pixel_z = 10, time = 3, easing = CIRCULAR_EASING|EASE_OUT)
+ delay = user.movement_delay()/4
+ sleep(delay)
+ animate(user, pixel_z = initial(user.pixel_z), time = 3, easing = CIRCULAR_EASING|EASE_OUT)
+ if(user.floating)
+ user.float(TRUE)
+
/obj/structure/railing/do_climb(mob/living/user)
+ if(!climb_check(user))
+ return FALSE
+
var/initial_mob_loc = get_turf(user)
- . = ..()
- if(.)
- currently_climbed = TRUE
- if(initial_mob_loc != get_turf(src)) // If we are on the railing, we want to move in the same dir as the railing. Otherwise we get put on the railing
- currently_climbed = FALSE
- return
- user.Move(get_step(user, dir), TRUE)
+
+ user.visible_message("[user] starts climbing onto \the [src]!")
+ climber = user
+ if(!do_after(user, 50, target = src))
+ climber = null
+ return FALSE
+
+ if(!can_touch(user) || !climbable)
+ climber = null
+ return FALSE
+
+ user.loc = get_turf(src)
+ hopping(user)
+
+ if(get_turf(user) == get_turf(src))
+ user.visible_message("[user] climbs onto \the [src]!")
+
+ clumse_stuff(climber)
+
+ climber = null
+
+ currently_climbed = TRUE
+
+ if(initial_mob_loc != get_turf(src)) // If we are on the railing, we want to move in the same dir as the railing. Otherwise we get put on the railing
currently_climbed = FALSE
+ return TRUE
+ user.loc = get_step(user, dir)
+ currently_climbed = FALSE
+
+ return TRUE
/obj/structure/railing/proc/can_be_rotated(mob/user)
if(anchored)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 9364329513e..dc81f0dd761 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -137,6 +137,10 @@
var/mob/living/user = AM
clumse_stuff(user)
+/obj/structure/table/climb_check(mob/living/user)
+ . = ..()
+ if(user.checkpass(PASSTABLE))
+ return FALSE
/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0)
if(height == 0)
@@ -147,14 +151,14 @@
var/mob/living/M = mover
if(M.flying)
return TRUE
+ var/obj/structure/table/other_table = locate(/obj/structure/table) in get_turf(mover)
+ var/obj/structure/other_object = locate(/obj/structure) in get_turf(mover)
+ if(other_object?.climbable && !other_table?.flipped)
+ return TRUE
if(istype(mover) && mover.checkpass(PASSTABLE))
return TRUE
if(mover.throwing)
return TRUE
- if(length(get_atoms_of_type(get_turf(mover), /obj/structure/table) - mover))
- var/obj/structure/table/other_table = locate(/obj/structure/table) in get_turf(mover)
- if(!other_table.flipped)
- return TRUE
if(flipped)
if(get_dir(loc, target) == dir)
return !density
@@ -487,6 +491,7 @@
debris -= AM
if(istype(AM, /obj/item/shard))
AM.throw_impact(L)
+ L.pixel_z = initial(L.pixel_z)
L.Weaken(10 SECONDS)
qdel(src)
@@ -732,6 +737,9 @@
if(OldLoc != held.loc)
held_items -= held_uid
continue
+ if(istype(held, /mob/living))
+ held.pixel_z = 16
+ held.glide_size = glide_size
held.forceMove(NewLoc)
@@ -791,12 +799,24 @@
. = ..()
. += "It's held together by a couple of bolts."
+/obj/structure/rack/climb_check(mob/living/user)
+ . = ..()
+ if(user.checkpass(PASSTABLE))
+ return FALSE
/obj/structure/rack/CanPass(atom/movable/mover, turf/target, height=0)
if(height==0)
return TRUE
if(!density) //Because broken racks -Agouri |TODO: SPRITE!|
return TRUE
+ if(ismob(mover))
+ var/mob/living/M = mover
+ if(M.flying)
+ return TRUE
+ var/obj/structure/table/other_table = locate(/obj/structure/table) in get_turf(mover)
+ var/obj/structure/other_object = locate(/obj/structure) in get_turf(mover)
+ if(other_object?.climbable && !other_table?.flipped)
+ return TRUE
if(istype(mover) && mover.checkpass(PASSTABLE))
return TRUE
if(mover.throwing)
@@ -865,6 +885,7 @@
desc = "Made with the skulls of the fallen."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "minibar"
+ climbable = TRUE
/obj/structure/rack/skeletal_bar/left
icon_state = "minibar_left"
@@ -872,6 +893,12 @@
/obj/structure/rack/skeletal_bar/right
icon_state = "minibar_right"
+/obj/structure/rack/skeletal_bar/MouseDrop_T(atom/movable/dropping, mob/user, params)
+ . = ..()
+ if(!. && dropping == user)
+ do_climb(user)
+ return TRUE
+
/obj/structure/rack/gunrack
name = "gun rack"
desc = "A gun rack for storing guns."