mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-22 04:28:33 +01:00
Merge pull request #9454 from VOREStation/upstream-merge-7783
[MIRROR] Reworks stairs to be sensible
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 ..()
|
||||
|
||||
Reference in New Issue
Block a user