Merge pull request #9454 from VOREStation/upstream-merge-7783

[MIRROR] Reworks stairs to be sensible
This commit is contained in:
Novacat
2021-02-27 13:02:59 -05:00
committed by GitHub
27 changed files with 377820 additions and 64642 deletions
-379
View File
@@ -74,214 +74,6 @@
usr.visible_message("<b>[src]</b> points to [A]")
return 1
/*one proc, four uses
swapping: if it's 1, the mobs are trying to switch, if 0, non-passive is pushing passive
default behaviour is:
- non-passive mob passes the passive version
- passive mob checks to see if its mob_bump_flag is in the non-passive's mob_bump_flags
- if si, the proc returns
*/
/mob/living/proc/can_move_mob(var/mob/living/swapped, swapping = 0, passive = 0)
if(!swapped)
return 1
if(!passive)
return swapped.can_move_mob(src, swapping, 1)
else
var/context_flags = 0
if(swapping)
context_flags = swapped.mob_swap_flags
else
context_flags = swapped.mob_push_flags
if(!mob_bump_flag) //nothing defined, go wild
return 1
if(mob_bump_flag & context_flags)
return 1
return 0
/mob/living/Bump(atom/movable/AM)
if(now_pushing || !loc || buckled == AM)
return
now_pushing = 1
if (istype(AM, /mob/living))
var/mob/living/tmob = AM
//Even if we don't push/swap places, we "touched" them, so spread fire
spread_fire(tmob)
for(var/mob/living/M in range(tmob, 1))
if(tmob.pinned.len || ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == 0)) || locate(/obj/item/weapon/grab, tmob.grabbed_by.len)) )
if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restrained, you cannot push past</span>")
now_pushing = 0
return
if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == 0) )
if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restraining [M], you cannot push past</span>")
now_pushing = 0
return
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
var/can_swap = 1
if(loc.density || tmob.loc.density)
can_swap = 0
if(can_swap)
for(var/atom/movable/A in loc)
if(A == src)
continue
if(!A.CanPass(tmob, loc))
can_swap = 0
if(!can_swap) break
if(can_swap)
for(var/atom/movable/A in tmob.loc)
if(A == tmob)
continue
if(!A.CanPass(src, tmob.loc))
can_swap = 0
if(!can_swap) break
//Leaping mobs just land on the tile, no pushing, no anything.
if(status_flags & LEAPING)
loc = tmob.loc
status_flags &= ~LEAPING
now_pushing = 0
return
if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && can_swap && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
var/turf/oldloc = loc
//VOREstation Edit - Begin
//check bumpnom chance, if it's a simplemob that's doing the bumping
var/mob/living/simple_mob/srcsimp = src
if(istype(srcsimp))
if(srcsimp.tryBumpNom(tmob))
now_pushing = 0
return
//if it's a simplemob being bumped, and the above didn't make them start getting bumpnommed, they get a chance to bumpnom
var/mob/living/simple_mob/tmobsimp = tmob
if(istype(tmobsimp))
if(tmobsimp.tryBumpNom(src))
now_pushing = 0
return
//VOREstation Edit - End
forceMove(tmob.loc)
//VOREstation Edit - Begin
// In case of micros, we don't swap positions; instead occupying the same square!
if (handle_micro_bump_helping(tmob))
now_pushing = 0
return
// TODO - Check if we need to do something about the slime.UpdateFeed() we are skipping below.
// VOREStation Edit - End
tmob.forceMove(oldloc)
now_pushing = 0
return
//VOREStation Edit - Begin
else if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && canmove && can_swap && handle_micro_bump_helping(tmob))
forceMove(tmob.loc)
now_pushing = 0
return
//VOREStation Edit - End
if(!can_move_mob(tmob, 0, 0))
now_pushing = 0
return
if(a_intent == I_HELP || src.restrained())
now_pushing = 0
return
// VOREStation Edit - Begin
// Plow that nerd.
if(ishuman(tmob))
var/mob/living/carbon/human/H = tmob
if(H.species.lightweight == 1 && prob(50))
H.visible_message("<span class='warning'>[src] bumps into [H], knocking them off balance!</span>")
H.Weaken(5)
now_pushing = 0
return
// Handle grabbing, stomping, and such of micros!
if(handle_micro_bump_other(tmob)) return
// VOREStation Edit - End
if(istype(tmob, /mob/living/carbon/human) && (FAT in tmob.mutations))
if(prob(40) && !(FAT in src.mutations))
to_chat(src, "<span class='danger'>You fail to push [tmob]'s fat ass out of the way.</span>")
now_pushing = 0
return
if(tmob.r_hand && istype(tmob.r_hand, /obj/item/weapon/shield/riot))
if(prob(99))
now_pushing = 0
return
if(tmob.l_hand && istype(tmob.l_hand, /obj/item/weapon/shield/riot))
if(prob(99))
now_pushing = 0
return
if(!(tmob.status_flags & CANPUSH))
now_pushing = 0
return
tmob.LAssailant = src
now_pushing = 0
. = ..()
if (!istype(AM, /atom/movable) || AM.anchored)
//VOREStation Edit - object-specific proc for running into things
if(((confused || is_blind()) && stat == CONSCIOUS && prob(50) && m_intent=="run") || flying)
AM.stumble_into(src)
//VOREStation Edit End
/* VOREStation Removal - See above
if(confused && prob(50) && m_intent=="run")
Weaken(2)
playsound(src, "punch", 25, 1, -1)
visible_message("<span class='warning'>[src] [pick("ran", "slammed")] into \the [AM]!</span>")
src.apply_damage(5, BRUTE)
to_chat(src, "<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>")
*/ // VOREStation Removal End
return
if (!now_pushing)
if(isobj(AM))
var/obj/I = AM
if(!can_pull_size || can_pull_size < I.w_class)
return
now_pushing = 1
var/t = get_dir(src, AM)
if (istype(AM, /obj/structure/window))
for(var/obj/structure/window/win in get_step(AM,t))
now_pushing = 0
return
var/turf/T = AM.loc
var/turf/T2 = get_step(AM,t)
if(!T2) // Map edge
now_pushing = 0
return
var/move_time = movement_delay(loc, t)
move_time = DS2NEARESTTICK(move_time)
if(AM.Move(T2, t, move_time))
Move(T, t, move_time)
if(ishuman(AM) && AM:grabbed_by)
for(var/obj/item/weapon/grab/G in AM:grabbed_by)
step(G:assailant, get_dir(G:assailant, AM))
G.adjust_position()
now_pushing = 0
/mob/living/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /obj/structure/blob) && faction == "blob") //Blobs should ignore things on their faction.
return TRUE
return ..()
// Called when something steps onto us. This allows for mulebots and vehicles to run things over. <3
/mob/living/Crossed(var/atom/movable/AM) // Transplanting this from /mob/living/carbon/human/Crossed()
if(AM == src || AM.is_incorporeal()) // We're not going to run over ourselves or ghosts
return
if(istype(AM, /mob/living/bot/mulebot))
var/mob/living/bot/mulebot/MB = AM
MB.runOver(src)
if(istype(AM, /obj/vehicle))
var/obj/vehicle/V = AM
V.RunOver(src)
/mob/living/verb/succumb()
set hidden = 1
if ((src.health < 0 && src.health > (5-src.getMaxHealth()))) // Health below Zero but above 5-away-from-death, as before, but variable
@@ -682,36 +474,6 @@ default behaviour is:
// ++++ROCKDTBEN++++ MOB PROCS //END
// Applies direct "cold" damage while checking protection against the cold.
/mob/living/proc/inflict_cold_damage(amount)
amount *= 1 - get_cold_protection(50) // Within spacesuit protection.
if(amount > 0)
adjustFireLoss(amount)
// Ditto, but for "heat".
/mob/living/proc/inflict_heat_damage(amount)
amount *= 1 - get_heat_protection(10000) // Within firesuit protection.
if(amount > 0)
adjustFireLoss(amount)
// and one for electricity because why not
/mob/living/proc/inflict_shock_damage(amount)
electrocute_act(amount, null, 1 - get_shock_protection(), pick(BP_HEAD, BP_TORSO, BP_GROIN))
// also one for water (most things resist it entirely, except for slimes)
/mob/living/proc/inflict_water_damage(amount)
amount *= 1 - get_water_protection()
if(amount > 0)
adjustToxLoss(amount)
// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents)
/mob/living/proc/inflict_poison_damage(amount)
if(isSynthetic())
return
amount *= 1 - get_poison_protection()
if(amount > 0)
adjustToxLoss(amount)
/mob/proc/get_contents()
@@ -762,50 +524,6 @@ default behaviour is:
return 1
return 0
/mob/living/proc/can_inject()
return 1
/mob/living/proc/get_organ_target()
var/mob/shooter = src
var/t = shooter:zone_sel.selecting
if ((t in list( O_EYES, O_MOUTH )))
t = BP_HEAD
var/obj/item/organ/external/def_zone = ran_zone(t)
return def_zone
// heal ONE external organ, organ gets randomly selected from damaged ones.
/mob/living/proc/heal_organ_damage(var/brute, var/burn)
adjustBruteLoss(-brute)
adjustFireLoss(-burn)
src.updatehealth()
// damage ONE external organ, organ gets randomly selected from damaged ones.
/mob/living/proc/take_organ_damage(var/brute, var/burn, var/emp=0)
if(status_flags & GODMODE) return 0 //godmode
adjustBruteLoss(brute)
adjustFireLoss(burn)
src.updatehealth()
// heal MANY external organs, in random order
/mob/living/proc/heal_overall_damage(var/brute, var/burn)
adjustBruteLoss(-brute)
adjustFireLoss(-burn)
src.updatehealth()
// damage MANY external organs, in random order
/mob/living/proc/take_overall_damage(var/brute, var/burn, var/used_weapon = null)
if(status_flags & GODMODE) return 0 //godmode
adjustBruteLoss(brute)
adjustFireLoss(burn)
src.updatehealth()
/mob/living/proc/restore_all_organs()
return
/mob/living/proc/revive()
rejuvenate()
if(buckled)
@@ -903,103 +621,6 @@ default behaviour is:
return
// Almost all of this handles pulling movables behind us
/mob/living/Move(atom/newloc, direct, movetime)
if(buckled && buckled.loc != newloc) //not updating position
if(!buckled.anchored && buckled.buckle_movable)
return buckled.Move(newloc, direct)
else
return 0
var/atom/movable/pullee = pulling
// Prior to our move it's already too far away
if(pullee && get_dist(src, pullee) > 1)
stop_pulling()
// Shenanigans! Pullee closed into locker for eg.
if(pullee && !isturf(pullee.loc) && pullee.loc != loc)
stop_pulling()
// Can't pull with no hands
if(restrained())
stop_pulling()
// Will move our mob (probably)
. = ..() // Moved() called at this point if successful
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //seperated from our puller and not in the middle of a diagonal move
pulledby.stop_pulling()
if(s_active && !(s_active in contents) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much.
s_active.close(src)
/mob/living/proc/dragged(var/mob/living/dragger, var/oldloc)
var/area/A = get_area(src)
if(lying && !buckled && pull_damage() && A.has_gravity() && (prob(getBruteLoss() * 200 / maxHealth)))
adjustBruteLoss(2)
visible_message("<span class='danger'>\The [src]'s [isSynthetic() ? "state" : "wounds"] worsen terribly from being dragged!</span>")
/mob/living/Moved(var/atom/oldloc, direct, forced, movetime)
. = ..()
handle_footstep(loc)
// Begin VOREstation edit
if(is_shifted)
is_shifted = FALSE
pixel_x = default_pixel_x
pixel_y = default_pixel_y
// End VOREstation edit
if(pulling) // we were pulling a thing and didn't lose it during our move.
var/pull_dir = get_dir(src, pulling)
if(pulling.anchored || !isturf(pulling.loc))
stop_pulling()
else if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
// If it is too far away or across z-levels from old location, stop pulling.
if(get_dist(pulling.loc, oldloc) > 1 || pulling.loc.z != oldloc?.z)
stop_pulling()
// living might take damage from drags
else if(isliving(pulling))
var/mob/living/M = pulling
M.dragged(src, oldloc)
pulling.Move(oldloc, 0, movetime) // the pullee tries to reach our previous position
if(pulling && get_dist(src, pulling) > 1) // the pullee couldn't keep up
stop_pulling()
if(!isturf(loc))
return
else if(lastarea?.has_gravity == 0)
inertial_drift()
//VOREStation Edit Start
else if(flying)
inertial_drift()
make_floating(1)
//VOREStation Edit End
else if(!isspace(loc))
inertia_dir = 0
make_floating(0)
/mob/living/proc/inertial_drift()
if(x > 1 && x < (world.maxx) && y > 1 && y < (world.maxy))
if(Process_Spacemove(1))
inertia_dir = 0
return
var/locthen = loc
spawn(5)
if(!anchored && !pulledby && loc == locthen)
var/stepdir = inertia_dir ? inertia_dir : last_move
if(!stepdir)
return
var/turf/T = get_step(src, stepdir)
if(!T)
return
Move(T, stepdir, 5)
/mob/living/proc/handle_footstep(turf/T)
return FALSE
/mob/living/verb/resist()
set name = "Resist"
set category = "IC"
+71 -1
View File
@@ -571,4 +571,74 @@
if(confused)
accuracy_penalty += 45
return accuracy_penalty
return accuracy_penalty
// Applies direct "cold" damage while checking protection against the cold.
/mob/living/proc/inflict_cold_damage(amount)
amount *= 1 - get_cold_protection(50) // Within spacesuit protection.
if(amount > 0)
adjustFireLoss(amount)
// Ditto, but for "heat".
/mob/living/proc/inflict_heat_damage(amount)
amount *= 1 - get_heat_protection(10000) // Within firesuit protection.
if(amount > 0)
adjustFireLoss(amount)
// and one for electricity because why not
/mob/living/proc/inflict_shock_damage(amount)
electrocute_act(amount, null, 1 - get_shock_protection(), pick(BP_HEAD, BP_TORSO, BP_GROIN))
// also one for water (most things resist it entirely, except for slimes)
/mob/living/proc/inflict_water_damage(amount)
amount *= 1 - get_water_protection()
if(amount > 0)
adjustToxLoss(amount)
// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents)
/mob/living/proc/inflict_poison_damage(amount)
if(isSynthetic())
return
amount *= 1 - get_poison_protection()
if(amount > 0)
adjustToxLoss(amount)
/mob/living/proc/can_inject()
return 1
/mob/living/proc/get_organ_target()
var/mob/shooter = src
var/t = shooter:zone_sel.selecting
if ((t in list( O_EYES, O_MOUTH )))
t = BP_HEAD
var/obj/item/organ/external/def_zone = ran_zone(t)
return def_zone
// heal ONE external organ, organ gets randomly selected from damaged ones.
/mob/living/proc/heal_organ_damage(var/brute, var/burn)
adjustBruteLoss(-brute)
adjustFireLoss(-burn)
src.updatehealth()
// damage ONE external organ, organ gets randomly selected from damaged ones.
/mob/living/proc/take_organ_damage(var/brute, var/burn, var/emp=0)
if(status_flags & GODMODE) return 0 //godmode
adjustBruteLoss(brute)
adjustFireLoss(burn)
src.updatehealth()
// heal MANY external organs, in random order
/mob/living/proc/heal_overall_damage(var/brute, var/burn)
adjustBruteLoss(-brute)
adjustFireLoss(-burn)
src.updatehealth()
// damage MANY external organs, in random order
/mob/living/proc/take_overall_damage(var/brute, var/burn, var/used_weapon = null)
if(status_flags & GODMODE) return 0 //godmode
adjustBruteLoss(brute)
adjustFireLoss(burn)
src.updatehealth()
/mob/living/proc/restore_all_organs()
return
+299 -1
View File
@@ -18,4 +18,302 @@
if(!n.is_safe_to_enter(src))
to_chat(src, span("warning", "\The [n] is dangerous to move into."))
return FALSE // In case any code wants to know if movement happened.
return ..() // Parent call should make the mob move.
return ..() // Parent call should make the mob move.
/*one proc, four uses
swapping: if it's 1, the mobs are trying to switch, if 0, non-passive is pushing passive
default behaviour is:
- non-passive mob passes the passive version
- passive mob checks to see if its mob_bump_flag is in the non-passive's mob_bump_flags
- if si, the proc returns
*/
/mob/living/proc/can_move_mob(var/mob/living/swapped, swapping = 0, passive = 0)
if(!swapped)
return 1
if(!passive)
return swapped.can_move_mob(src, swapping, 1)
else
var/context_flags = 0
if(swapping)
context_flags = swapped.mob_swap_flags
else
context_flags = swapped.mob_push_flags
if(!mob_bump_flag) //nothing defined, go wild
return 1
if(mob_bump_flag & context_flags)
return 1
return 0
/mob/living/Bump(atom/movable/AM)
if(now_pushing || !loc || buckled == AM)
return
now_pushing = 1
if (istype(AM, /mob/living))
var/mob/living/tmob = AM
//Even if we don't push/swap places, we "touched" them, so spread fire
spread_fire(tmob)
for(var/mob/living/M in range(tmob, 1))
if(tmob.pinned.len || ((M.pulling == tmob && ( tmob.restrained() && !( M.restrained() ) && M.stat == 0)) || locate(/obj/item/weapon/grab, tmob.grabbed_by.len)) )
if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restrained, you cannot push past</span>")
now_pushing = 0
return
if( tmob.pulling == M && ( M.restrained() && !( tmob.restrained() ) && tmob.stat == 0) )
if ( !(world.time % 5) )
to_chat(src, "<span class='warning'>[tmob] is restraining [M], you cannot push past</span>")
now_pushing = 0
return
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
var/can_swap = 1
if(loc.density || tmob.loc.density)
can_swap = 0
if(can_swap)
for(var/atom/movable/A in loc)
if(A == src)
continue
if(!A.CanPass(tmob, loc))
can_swap = 0
if(!can_swap) break
if(can_swap)
for(var/atom/movable/A in tmob.loc)
if(A == tmob)
continue
if(!A.CanPass(src, tmob.loc))
can_swap = 0
if(!can_swap) break
//Leaping mobs just land on the tile, no pushing, no anything.
if(status_flags & LEAPING)
loc = tmob.loc
status_flags &= ~LEAPING
now_pushing = 0
return
if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && can_swap && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
var/turf/oldloc = loc
//VOREstation Edit - Begin
//check bumpnom chance, if it's a simplemob that's doing the bumping
var/mob/living/simple_mob/srcsimp = src
if(istype(srcsimp))
if(srcsimp.tryBumpNom(tmob))
now_pushing = 0
return
//if it's a simplemob being bumped, and the above didn't make them start getting bumpnommed, they get a chance to bumpnom
var/mob/living/simple_mob/tmobsimp = tmob
if(istype(tmobsimp))
if(tmobsimp.tryBumpNom(src))
now_pushing = 0
return
//VOREstation Edit - End
forceMove(tmob.loc)
//VOREstation Edit - Begin
// In case of micros, we don't swap positions; instead occupying the same square!
if (handle_micro_bump_helping(tmob))
now_pushing = 0
return
// TODO - Check if we need to do something about the slime.UpdateFeed() we are skipping below.
// VOREStation Edit - End
tmob.forceMove(oldloc)
now_pushing = 0
return
//VOREStation Edit - Begin
else if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && canmove && can_swap && handle_micro_bump_helping(tmob))
forceMove(tmob.loc)
now_pushing = 0
return
//VOREStation Edit - End
if(!can_move_mob(tmob, 0, 0))
now_pushing = 0
return
if(a_intent == I_HELP || src.restrained())
now_pushing = 0
return
// VOREStation Edit - Begin
// Plow that nerd.
if(ishuman(tmob))
var/mob/living/carbon/human/H = tmob
if(H.species.lightweight == 1 && prob(50))
H.visible_message("<span class='warning'>[src] bumps into [H], knocking them off balance!</span>")
H.Weaken(5)
now_pushing = 0
return
// Handle grabbing, stomping, and such of micros!
if(handle_micro_bump_other(tmob)) return
// VOREStation Edit - End
if(istype(tmob, /mob/living/carbon/human) && (FAT in tmob.mutations))
if(prob(40) && !(FAT in src.mutations))
to_chat(src, "<span class='danger'>You fail to push [tmob]'s fat ass out of the way.</span>")
now_pushing = 0
return
if(tmob.r_hand && istype(tmob.r_hand, /obj/item/weapon/shield/riot))
if(prob(99))
now_pushing = 0
return
if(tmob.l_hand && istype(tmob.l_hand, /obj/item/weapon/shield/riot))
if(prob(99))
now_pushing = 0
return
if(!(tmob.status_flags & CANPUSH))
now_pushing = 0
return
tmob.LAssailant = src
now_pushing = 0
. = ..()
if (!istype(AM, /atom/movable) || AM.anchored)
//VOREStation Edit - object-specific proc for running into things
if(((confused || is_blind()) && stat == CONSCIOUS && prob(50) && m_intent=="run") || flying)
AM.stumble_into(src)
//VOREStation Edit End
/* VOREStation Removal - See above
if(confused && prob(50) && m_intent=="run")
Weaken(2)
playsound(src, "punch", 25, 1, -1)
visible_message("<span class='warning'>[src] [pick("ran", "slammed")] into \the [AM]!</span>")
src.apply_damage(5, BRUTE)
to_chat(src, "<span class='warning'>You just [pick("ran", "slammed")] into \the [AM]!</span>")
*/ // VOREStation Removal End
return
if (!now_pushing)
if(isobj(AM))
var/obj/I = AM
if(!can_pull_size || can_pull_size < I.w_class)
return
now_pushing = 1
var/t = get_dir(src, AM)
if (istype(AM, /obj/structure/window))
for(var/obj/structure/window/win in get_step(AM,t))
now_pushing = 0
return
step(AM, t)
if(ishuman(AM) && AM:grabbed_by)
for(var/obj/item/weapon/grab/G in AM:grabbed_by)
step(G:assailant, get_dir(G:assailant, AM))
G.adjust_position()
now_pushing = 0
/mob/living/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /obj/structure/blob) && faction == "blob") //Blobs should ignore things on their faction.
return TRUE
return ..()
// Called when something steps onto us. This allows for mulebots and vehicles to run things over. <3
/mob/living/Crossed(var/atom/movable/AM) // Transplanting this from /mob/living/carbon/human/Crossed()
if(AM == src || AM.is_incorporeal()) // We're not going to run over ourselves or ghosts
return
if(istype(AM, /mob/living/bot/mulebot))
var/mob/living/bot/mulebot/MB = AM
MB.runOver(src)
if(istype(AM, /obj/vehicle))
var/obj/vehicle/V = AM
V.RunOver(src)
// Almost all of this handles pulling movables behind us
/mob/living/Move(atom/newloc, direct, movetime)
if(buckled && buckled.loc != newloc) //not updating position
if(!buckled.anchored && buckled.buckle_movable)
return buckled.Move(newloc, direct)
else
return 0
var/atom/movable/pullee = pulling
// Prior to our move it's already too far away
if(pullee && get_dist(src, pullee) > 1)
stop_pulling()
// Shenanigans! Pullee closed into locker for eg.
if(pullee && !isturf(pullee.loc) && pullee.loc != loc)
stop_pulling()
// Can't pull with no hands
if(restrained())
stop_pulling()
// Will move our mob (probably)
. = ..() // Moved() called at this point if successful
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //seperated from our puller and not in the middle of a diagonal move
pulledby.stop_pulling()
if(s_active && !(s_active in contents) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much.
s_active.close(src)
/mob/living/proc/dragged(var/mob/living/dragger, var/oldloc)
var/area/A = get_area(src)
if(lying && !buckled && pull_damage() && A.has_gravity() && (prob(getBruteLoss() * 200 / maxHealth)))
adjustBruteLoss(2)
visible_message("<span class='danger'>\The [src]'s [isSynthetic() ? "state" : "wounds"] worsen terribly from being dragged!</span>")
/mob/living/Moved(var/atom/oldloc, direct, forced, movetime)
. = ..()
handle_footstep(loc)
// Begin VOREstation edit
if(is_shifted)
is_shifted = FALSE
pixel_x = default_pixel_x
pixel_y = default_pixel_y
// End VOREstation edit
if(pulling) // we were pulling a thing and didn't lose it during our move.
var/pull_dir = get_dir(src, pulling)
if(pulling.anchored || !isturf(pulling.loc))
stop_pulling()
else if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
// If it is too far away or across z-levels from old location, stop pulling.
if(get_dist(pulling.loc, oldloc) > 1 || pulling.loc.z != oldloc?.z)
stop_pulling()
// living might take damage from drags
else if(isliving(pulling))
var/mob/living/M = pulling
M.dragged(src, oldloc)
pulling.Move(oldloc, 0, movetime) // the pullee tries to reach our previous position
if(pulling && get_dist(src, pulling) > 1) // the pullee couldn't keep up
stop_pulling()
if(!isturf(loc))
return
else if(lastarea?.has_gravity == 0)
inertial_drift()
//VOREStation Edit Start
else if(flying)
inertial_drift()
make_floating(1)
//VOREStation Edit End
else if(!isspace(loc))
inertia_dir = 0
make_floating(0)
/mob/living/proc/inertial_drift()
if(x > 1 && x < (world.maxx) && y > 1 && y < (world.maxy))
if(Process_Spacemove(1))
inertia_dir = 0
return
var/locthen = loc
spawn(5)
if(!anchored && !pulledby && loc == locthen)
var/stepdir = inertia_dir ? inertia_dir : last_move
if(!stepdir)
return
var/turf/T = get_step(src, stepdir)
if(!T)
return
Move(T, stepdir, 5)
/mob/living/proc/handle_footstep(turf/T)
return FALSE
@@ -1,12 +1,8 @@
//////////////////////////////
//Contents: Ladders, Stairs.//
//////////////////////////////
/obj/structure/ladder
name = "ladder"
desc = "A ladder. You can climb it up and down."
icon_state = "ladder01"
icon = 'icons/obj/structures.dmi'
icon = 'icons/obj/structures/multiz.dmi'
density = 0
opacity = 0
anchored = 1
@@ -125,65 +121,3 @@
/obj/structure/ladder/updown
allowed_directions = UP|DOWN
icon_state = "ladder11"
/obj/structure/stairs
name = "Stairs"
desc = "Stairs leading to another deck. Not too useful if the gravity goes out."
icon = 'icons/obj/stairs.dmi'
density = 0
opacity = 0
anchored = 1
flags = ON_BORDER
layer = STAIRS_LAYER
/obj/structure/stairs/Initialize()
. = ..()
for(var/turf/turf in locs)
var/turf/simulated/open/above = GetAbove(turf)
if(!above)
warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])")
return qdel(src)
if(!istype(above))
above.ChangeTurf(/turf/simulated/open)
/obj/structure/stairs/CheckExit(atom/movable/mover as mob|obj, turf/target as turf)
if(get_dir(loc, target) == dir && upperStep(mover.loc))
return FALSE
. = ..()
/obj/structure/stairs/Bumped(atom/movable/A)
// This is hackish but whatever.
var/turf/target = get_step(GetAbove(A), dir)
if(target.Enter(A, src)) // Pass src to be ignored to avoid infinate loop
A.forceMove(target)
if(isliving(A))
var/mob/living/L = A
if(L.pulling && !L.pulling.anchored)
L.pulling.forceMove(target)
/obj/structure/stairs/proc/upperStep(var/turf/T)
return (T == loc)
/obj/structure/stairs/CanPass(obj/mover, turf/source, height, airflow)
return airflow || !density
// type paths to make mapping easier.
/obj/structure/stairs/north
dir = NORTH
bound_height = 64
bound_y = -32
pixel_y = -32
/obj/structure/stairs/south
dir = SOUTH
bound_height = 64
/obj/structure/stairs/east
dir = EAST
bound_width = 64
bound_x = -32
pixel_x = -32
/obj/structure/stairs/west
dir = WEST
bound_width = 64
+4 -20
View File
@@ -358,24 +358,16 @@
for(var/atom/A in landing)
if(!A.CanPass(src, src.loc, 1, 0))
return FALSE
// TODO - Stairs should operate thru a different mechanism, not falling, to allow side-bumping.
// Now lets move there!
if(!Move(landing))
return 1
// Detect if we made a silent landing.
if(locate(/obj/structure/stairs) in landing)
if(isliving(src))
var/mob/living/L = src
if(L.pulling)
L.pulling.forceMove(landing)
return 1
else
var/atom/A = find_fall_target(oldloc, landing)
if(special_fall_handle(A) || !A || !A.check_impact(src))
return
fall_impact(A)
var/atom/A = find_fall_target(oldloc, landing)
if(special_fall_handle(A) || !A || !A.check_impact(src))
return
fall_impact(A)
/atom/movable/proc/special_fall_handle(var/atom/A)
return FALSE
@@ -446,14 +438,6 @@
/turf/space/check_impact(var/atom/movable/falling_atom)
return FALSE
// We return 1 without calling fall_impact in order to provide a soft landing. So nice.
// Note this really should never even get this far
/obj/structure/stairs/CheckFall(var/atom/movable/falling_atom)
return TRUE
/obj/structure/stairs/check_impact(var/atom/movable/falling_atom)
return FALSE
// Can't fall onto ghosts
/mob/observer/dead/CheckFall()
return FALSE
+556
View File
@@ -0,0 +1,556 @@
#define STAIR_MOVE_DELAY 10 // Animation delay for non-living objects moving up/down stairs
/obj/structure/stairs
name = "Stairs"
desc = "Stairs leading to another deck. Not too useful if the gravity goes out."
icon = 'icons/obj/structures/multiz.dmi'
icon_state = "stair"
opacity = 0
density = 0
anchored = 1
layer = STAIRS_LAYER
/obj/structure/stairs/Initialize()
if(check_integrity())
update_icon()
return INITIALIZE_HINT_NORMAL
// Returns TRUE if the stairs are a complete and connected unit, FALSE if a piece is missing or obstructed
// Will attempt to reconnect broken pieces
// Parameters:
// - B1: Loc of bottom stair
// - B2: Loc of middle stair
// - T1: Openspace over bottom stair
// - T2: Loc of top stair, over middle stair
/obj/structure/stairs/proc/check_integrity(var/obj/structure/stairs/bottom/B = null,
var/obj/structure/stairs/middle/M = null,
var/obj/structure/stairs/top/T = null,
var/turf/simulated/open/O = null)
// Base cases: Something is missing!
// The parent type doesn't know enough about the positional relations to find neighbors, only evaluate if they're connected
if(!istype(B) || !istype(M) || !istype(T) || !istype(O))
return FALSE
// Case 1: In working order
if(B.top == T && M.bottom == B && T.bottom == B && \
get_turf(M) == get_step(B, B.dir) && O == GetAbove(B) && get_turf(T) == GetAbove(M))
return TRUE
// Case 2: The top is linked to someone else
if(istype(T.bottom) && T.bottom != B)
return FALSE
// Case 3: The bottom is linked to someone else
if(istype(B.top) && B.top != T)
return FALSE
// Case 4: They're unlinked
B.dir = get_dir(get_turf(B), get_turf(M))
B.top = T
B.middle = M
T.dir = B.dir
T.middle = M
T.bottom = B
M.dir = B.dir
M.top = T
M.bottom = B
return TRUE
// Used to actually move stuff up/down stairs. Removed from Crossed for special cases
/obj/structure/stairs/proc/use_stairs(var/atom/movable/AM, var/atom/oldloc)
return
//////////////////////////////////////////////////////////////////////
// Bottom piece that you step ontor //////////////////////////////////
//////////////////////////////////////////////////////////////////////
/obj/structure/stairs/bottom
icon_state = "stair_l"
var/obj/structure/stairs/top/top = null
var/obj/structure/stairs/middle/middle = null
/obj/structure/stairs/bottom/Initialize()
if(!GetAbove(src))
warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])")
return INITIALIZE_HINT_QDEL
. = ..()
/obj/structure/stairs/bottom/Destroy()
if(top)
top.bottom = null
if(middle)
middle.bottom = null
..()
// These are necessarily fairly similar, but because the positional relations are different, we have to copy-pasta a fair bit
/obj/structure/stairs/bottom/check_integrity(var/obj/structure/stairs/bottom/B = null,
var/obj/structure/stairs/middle/M = null,
var/obj/structure/stairs/top/T = null,
var/turf/simulated/open/O = null)
// In the case where we're provided all the pieces, just try connecting them.
// In order: all exist, they are appropriately adjacent, and they can connect
if(istype(B) && istype(M) && istype(T) && istype(O) && \
B.Adjacent(M) && (GetBelow(O) == get_turf(B)) && T.Adjacent(O) && \
..())
return TRUE
// If we're already configured, just check those
else if(istype(top) && istype(middle))
O = locate(/turf/simulated/open) in GetAbove(src)
if(..(src, middle, top, O))
return TRUE
var/turf/B2 = get_step(src, src.dir)
O = GetAbove(src)
var/turf/T2 = GetAbove(B2)
// T1 is the same regardless of B1's dir, so we can enforce it here
if(!istype(O))
return FALSE
T = locate(/obj/structure/stairs/top) in T2
M = locate(/obj/structure/stairs/middle) in B2
// If you set the dir, that's the dir it *wants* to connect in. It only chooses the others if that doesn't work
// Everything is simply linked in our original direction
if(istype(M) && istype(T) && ..(src, M, T, O))
return TRUE
// Else, we have to look in other directions
for(var/dir in cardinal - src.dir)
B2 = get_step(src, dir)
T2 = GetAbove(B2)
if(!istype(B2) || !istype(T2))
continue
T = locate(/obj/structure/stairs/top) in T2
M = locate(/obj/structure/stairs/middle) in B2
if(..(src, M, T, O))
return TRUE
// Out of the dir check, we have no valid neighbors, and thus are not complete.
return FALSE
/obj/structure/stairs/bottom/Crossed(var/atom/movable/AM, var/atom/oldloc)
use_stairs(AM, oldloc)
/obj/structure/stairs/bottom/use_stairs(var/atom/movable/AM, var/atom/oldloc)
// If we're coming from the top of the stairs, don't trap us in an infinite staircase
// Or if we fell down the openspace
if((top in oldloc) || oldloc == GetAbove(src))
return
if(isobserver(AM)) // Ghosts have their own methods for going up and down
return
if(AM.pulledby) // Animating the movement of pulled things is handled when the puller goes up the stairs
return
var/animation_delay = STAIR_MOVE_DELAY // Default value
var/list/atom/movable/pulling = list() // Will also include grabbed mobs
if(isliving(AM))
var/mob/living/L = AM
if(L.grabbed_by.len) // Same as pulledby, whoever's holding you will keep you from going down stairs.
return
// If the object has a measurable movement delay, use that
animation_delay = L.movement_delay()
// If the object is pulling or grabbing anything, we'll want to move those too. A grab chain may be disrupted in doing so.
if(L.pulling && !L.pulling.anchored)
to_world("adding [L.pulling] to pulling")
pulling |= L.pulling
for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand))
to_world("adding [G.affecting] to pulling")
pulling |= G.affecting
// If the stairs aren't broken, go up.
if(check_integrity())
AM.dir = src.dir
// Animate moving onto M
switch(src.dir)
if(NORTH)
animate(AM, AM.pixel_y += 32, time = animation_delay)
if(SOUTH)
animate(AM, AM.pixel_y += -32, time = animation_delay)
if(EAST)
animate(AM, AM.pixel_x += 32, time = animation_delay)
if(WEST)
animate(AM, AM.pixel_x += -32, time = animation_delay)
// Bring the pulled/grabbed object(s) along behind us
for(var/atom/movable/P in pulling)
to_world("attempting to move [P]")
P.forceMove(get_turf(src)) // They will move onto the turf but won't get past the check earlier in crossed. Aligns animation more cleanly
switch(src.dir)
if(NORTH)
animate(P, P.pixel_y += 32, time = animation_delay)
if(SOUTH)
animate(P, P.pixel_y += -32, time = animation_delay)
if(EAST)
animate(P, P.pixel_x += 32, time = animation_delay)
if(WEST)
animate(P, P.pixel_x += -32, time = animation_delay)
// Go up the stairs
spawn(animation_delay)
// Move to Top
AM.forceMove(get_turf(top))
// Animate moving from O to T
switch(src.dir)
if(NORTH)
AM.pixel_y -= 64
animate(AM, AM.pixel_y += 32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
if(SOUTH)
AM.pixel_y -= -64
animate(AM, AM.pixel_y += -32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
if(EAST)
AM.pixel_x -= 64
animate(AM, AM.pixel_x += 32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
if(WEST)
AM.pixel_x -= -64
animate(AM, AM.pixel_x += -32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
// If something is being pulled, bring it along directly to avoid the mob being torn away from it due to movement delays
for(var/atom/movable/P in pulling)
spawn(animation_delay)
switch(src.dir)
if(NORTH)
P.pixel_y -= 32
if(SOUTH)
P.pixel_y -= -32
if(EAST)
P.pixel_x -= 32
if(WEST)
P.pixel_x -= -32
P.forceMove(get_turf(top)) // Just bring it along directly, no fussing with animation timing
if(isliving(P))
var/mob/living/L = P
if(L.client)
L.client.Process_Grab() // Update any miscellanous grabs, possibly break grab-chains
return TRUE
//////////////////////////////////////////////////////////////////////
// Middle piece that you are animated onto/off of ////////////////////
//////////////////////////////////////////////////////////////////////
/obj/structure/stairs/middle
icon_state = "stair_u"
opacity = TRUE
density = TRUE // Too high to simply step up on
climbable = TRUE // But they can be climbed if the bottom is out
var/obj/structure/stairs/top/top = null
var/obj/structure/stairs/bottom/bottom = null
/obj/structure/stairs/middle/Initialize()
if(!GetAbove(src))
warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])")
return INITIALIZE_HINT_QDEL
. = ..()
/obj/structure/stairs/middle/Destroy()
if(top)
top.middle = null
if(bottom)
bottom.middle = null
..()
// These are necessarily fairly similar, but because the positional relations are different, we have to copy-pasta a fair bit
/obj/structure/stairs/middle/check_integrity(var/obj/structure/stairs/bottom/B = null,
var/obj/structure/stairs/middle/M = null,
var/obj/structure/stairs/top/T = null,
var/turf/simulated/open/O = null)
// In the case where we're provided all the pieces, just try connecting them.
// In order: all exist, they are appropriately adjacent, and they can connect
if(istype(B) && istype(M) && istype(T) && istype(O) && \
B.Adjacent(M) && (GetBelow(O) == B.loc) && T.Adjacent(O) && \
..())
return TRUE
else if(istype(top) && istype(bottom))
O = locate(/turf/simulated/open) in GetAbove(bottom)
if(..(bottom, src, top, O))
return TRUE
var/turf/B1 = get_step(src, turn(src.dir, 180))
O = GetAbove(B1)
var/turf/T2 = GetAbove(src)
B = locate(/obj/structure/stairs/bottom) in B1
T = locate(/obj/structure/stairs/top) in T2
// Top is static for Middle stair, if it's invalid we can't do much
if(!istype(T))
return FALSE
// If you set the dir, that's the dir it *wants* to connect in. It only chooses the others if that doesn't work
// Everything is simply linked in our original direction
if(istype(B1) && istype(T2) && istype(O) && ..(B, src, T, O))
return TRUE
// Else, we have to look in other directions
for(var/dir in cardinal - src.dir)
B1 = get_step(src, turn(dir, 180))
O = GetAbove(B1)
if(!istype(B1) || !istype(O))
continue
B = locate(/obj/structure/stairs/bottom) in B1
if(..(B, src, T, O))
return TRUE
// The middle stair has some further special logic, in that it can be climbed, and so is technically valid if only the top exists
// T is enforced by a prior if
T.middle = src
src.top = T
src.dir = T.dir
return TRUE
/obj/structure/stairs/middle/MouseDrop_T(mob/target, mob/user)
. = ..()
if(check_integrity())
do_climb(user)
user.forceMove(get_turf(top)) // You can't really drag things when you have to climb up the gap in the stairs yourself
/obj/structure/stairs/middle/Bumped(mob/user)
if(check_integrity() && bottom && (bottom in get_turf(user))) // Bottom must be enforced because the middle stairs don't actually need the bottom
user.forceMove(get_turf(top))
//////////////////////////////////////////////////////////////////////
// Top piece that you step onto //////////////////////////////////////
//////////////////////////////////////////////////////////////////////
/obj/structure/stairs/top
icon_state = "stair_l" // Darker, marginally less contrast w/ openspace
var/obj/structure/stairs/middle/middle = null
var/obj/structure/stairs/bottom/bottom = null
/obj/structure/stairs/top/Initialize()
if(!GetBelow(src))
warning("Stair created without level below: ([loc.x], [loc.y], [loc.z])")
return INITIALIZE_HINT_QDEL
. = ..()
/obj/structure/stairs/top/Destroy()
if(middle)
middle.top = null
if(bottom)
bottom.top = null
..()
// These are necessarily fairly similar, but because the positional relations are different, we have to copy-pasta a fair bit
/obj/structure/stairs/top/check_integrity(var/obj/structure/stairs/bottom/B = null,
var/obj/structure/stairs/middle/M = null,
var/obj/structure/stairs/top/T = null,
var/turf/simulated/open/O = null)
// In the case where we're provided all the pieces, just try connecting them.
// In order: all exist, they are appropriately adjacent, and they can connect
if(istype(B) && istype(M) && istype(T) && istype(O) && \
B.Adjacent(M) && (GetBelow(O) == B.loc) && T.Adjacent(O) && \
(. = ..()))
return
else if(istype(middle) && istype(bottom))
O = locate(/turf/simulated/open) in GetAbove(bottom)
if(..(bottom, middle, src, O))
return TRUE
O = get_step(src, turn(src.dir, 180))
var/turf/B1 = GetBelow(O)
var/turf/B2 = GetBelow(src)
B = locate(/obj/structure/stairs/bottom) in B1
M = locate(/obj/structure/stairs/middle) in B2
// Middle stair is static for Top stair, so if it's invalid we can't do much
if(!istype(M))
return FALSE
// If you set the dir, that's the dir it *wants* to connect in. It only chooses the others if that doesn't work
// Everything is simply linked in our original direction
if(istype(B) && istype(O) && (. = ..(B, M, src, O)))
return
// Else, we have to look in other directions
for(var/dir in cardinal - src.dir)
O = get_step(src, turn(dir, 180))
B1 = GetBelow(O)
if(!istype(B1) || !istype(O))
continue
B = locate(/obj/structure/stairs/bottom) in B1
if((. = ..(B, M, src, O)))
return
// Out of the dir check, we have no valid neighbors, and thus are not complete. `.` was set by ..()
return
/obj/structure/stairs/top/Crossed(var/atom/movable/AM, var/atom/oldloc)
use_stairs(AM, oldloc)
. = ..()
/obj/structure/stairs/top/use_stairs(var/atom/movable/AM, var/atom/oldloc)
// If we're coming from the bottom of the stairs, don't trap us in an infinite staircase
// Or if we climb up the middle
if((bottom in oldloc) || oldloc == GetBelow(src))
return
if(isobserver(AM)) // Ghosts have their own methods for going up and down
return
if(AM.pulledby) // Animating the movement of pulled things is handled when the puller goes up the stairs
return
var/animation_delay = STAIR_MOVE_DELAY // Default value
var/list/atom/movable/pulling = list() // Will also include grabbed mobs
if(isliving(AM))
var/mob/living/L = AM
if(L.grabbed_by.len) // Same as pulledby, whoever's holding you will keep you from going down stairs.
return
// If the object has a measurable movement delay, use that
animation_delay = L.movement_delay()
// If the object is pulling or grabbing anything, we'll want to move those too. A grab chain may be disrupted in doing so.
if(L.pulling && !L.pulling.anchored)
to_world("adding [L.pulling] to pulling")
pulling |= L.pulling
for(var/obj/item/weapon/grab/G in list(L.l_hand, L.r_hand))
to_world("adding [G.affecting] to pulling")
pulling |= G.affecting
// If the stairs aren't broken, go up.
if(check_integrity())
AM.dir = turn(src.dir, 180)
// Animate moving onto M
switch(src.dir)
if(NORTH)
animate(AM, AM.pixel_y -= 32, time = animation_delay) // Incrementing/decrementing to preserve prior values
if(SOUTH)
animate(AM, AM.pixel_y -= -32, time = animation_delay)
if(EAST)
animate(AM, AM.pixel_x -= 32, time = animation_delay)
if(WEST)
animate(AM, AM.pixel_x -= -32, time = animation_delay)
// Bring the pulled/grabbed object(s) along behind us
for(var/atom/movable/P in pulling)
to_world("attempting to move [P]")
P.forceMove(get_turf(src)) // They will move onto the turf but won't get past the check earlier in crossed. Aligns animation more cleanly
switch(src.dir)
if(NORTH)
animate(P, P.pixel_y -= 32, time = animation_delay)
if(SOUTH)
animate(P, P.pixel_y -= -32, time = animation_delay)
if(EAST)
animate(P, P.pixel_x -= 32, time = animation_delay)
if(WEST)
animate(P, P.pixel_x -= -32, time = animation_delay)
// Go up the stairs
spawn(animation_delay)
// Move to Top
AM.forceMove(get_turf(bottom))
// Animate moving from O to T
switch(src.dir)
if(NORTH)
AM.pixel_y += 64
animate(AM, AM.pixel_y -= 32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
if(SOUTH)
AM.pixel_y += -64
animate(AM, AM.pixel_y -= -32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
if(EAST)
AM.pixel_x += 64
animate(AM, AM.pixel_x -= 32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
if(WEST)
AM.pixel_x += -64
animate(AM, AM.pixel_x -= -32, time = animation_delay)//, easing = SINE_EASING | EASE_OUT)
// If something is being pulled, bring it along directly to avoid the mob being torn away from it due to movement delays
for(var/atom/movable/P in pulling)
spawn(animation_delay)
switch(src.dir)
if(NORTH)
P.pixel_y += 32
if(SOUTH)
P.pixel_y += -32
if(EAST)
P.pixel_x += 32
if(WEST)
P.pixel_x += -32
P.forceMove(get_turf(bottom)) // Just bring it along directly, no fussing with animation timing
if(isliving(P))
var/mob/living/L = P
if(L.client)
L.client.Process_Grab() // Update any miscellanous grabs, possibly break grab-chains
return TRUE
// Mapping pieces, placed at the bottommost part of the stairs
/obj/structure/stairs/spawner
name = "Stairs spawner"
icon = 'icons/obj/structures/stairs_64x64.dmi'
icon_state = ""
/obj/structure/stairs/spawner/Initialize()
. = ..()
var/turf/B1 = get_step(get_turf(src), turn(dir, 180))
var/turf/B2 = get_turf(src)
var/turf/T1 = GetAbove(B1)
var/turf/T2 = GetAbove(B2)
if(!istype(B1) || !istype(B2))
warning("Stair created at invalid loc: ([loc.x], [loc.y], [loc.z])")
return INITIALIZE_HINT_QDEL
if(!istype(T1) || !istype(T2))
warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])")
return INITIALIZE_HINT_QDEL
// Spawn the stairs
// Railings sold separately
var/turf/simulated/open/O = T1
var/obj/structure/stairs/top/T = new(T2)
var/obj/structure/stairs/middle/M = new(B2)
var/obj/structure/stairs/bottom/B = new(B1)
if(!isopenspace(O))
O = new(O)
B.dir = dir
M.dir = dir
T.dir = dir
B.check_integrity(B, M, T, O)
return INITIALIZE_HINT_QDEL
// For ease of spawning. While you *can* spawn the base type and set its dir, this is useful for adminbus and a little bit quicker to map in
/obj/structure/stairs/spawner/north
dir = NORTH
bound_height = 64
bound_y = -32
pixel_y = -32
/obj/structure/stairs/spawner/south
dir = SOUTH
bound_height = 64
/obj/structure/stairs/spawner/east
dir = EAST
bound_width = 64
bound_x = -32
pixel_x = -32
/obj/structure/stairs/spawner/west
dir = WEST
bound_width = 64
+10 -4
View File
@@ -55,8 +55,16 @@
GLOB.turf_entered_event.register(T, src, .proc/BelowOpenUpdated)
GLOB.turf_exited_event.register(T, src, .proc/BelowOpenUpdated)
/turf/simulated/open/Entered(var/atom/movable/mover)
/turf/simulated/open/Entered(var/atom/movable/mover, var/atom/oldloc)
. = ..()
// Going down stairs from the topstair piece
var/obj/structure/stairs/top/T = locate(/obj/structure/stairs/top) in oldloc
if(T && mover.dir == turn(T.dir, 180) && T.check_integrity())
T.use_stairs(mover, oldloc)
return
mover.fall()
/turf/simulated/open/proc/BelowOpenUpdated(turf/T, atom/movable/AM, old_loc)
@@ -187,6 +195,4 @@
for(var/obj/O in contents)
if(!O.CanFallThru(L, GetBelow(src)))
return TRUE // Can't fall through this, like lattice or catwalk.
if(!locate(/obj/structure/stairs) in GetBelow(src))
return FALSE // Falling on stairs is safe.
return ..()
return ..()