mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-01-06 15:32:52 +00:00
Blob Maintenance: Stairs are Hard (#9146)
* Fix: Blobs respect gravity if not otherwise prevented. Tweak: Blobs do not directly interact with stairs Tweak: Blobs, when expanding against a stair's middle structure, can grow up Z-levels if the turf allows. Tweak: Blobs, if controlled by a player, may grow upward or downward, if the turfs allow. Fix: Blob Overmind is no longer affected by gravity. * Fringe case deleted blobs are null-loc'd for GC so they are not stuck in-map. Railing checks properly function.
This commit is contained in:
@@ -86,6 +86,10 @@ GLOBAL_LIST_EMPTY(all_blobs)
|
||||
overmind.blob_type.on_emp(src, severity)
|
||||
|
||||
/obj/structure/blob/proc/pulsed()
|
||||
if(QDELETED(src))
|
||||
loc = null
|
||||
overmind = null
|
||||
return
|
||||
if(pulse_timestamp <= world.time)
|
||||
consume_tile()
|
||||
if(heal_timestamp <= world.time)
|
||||
@@ -96,6 +100,7 @@ GLOBAL_LIST_EMPTY(all_blobs)
|
||||
if(overmind)
|
||||
faction = overmind.blob_type.faction
|
||||
overmind.blob_type.on_pulse(src)
|
||||
fall() // Make sure to obey gravity.
|
||||
return TRUE //we did it, we were pulsed!
|
||||
return FALSE //oh no we failed
|
||||
|
||||
@@ -169,6 +174,11 @@ GLOBAL_LIST_EMPTY(all_blobs)
|
||||
make_blob = FALSE
|
||||
T.blob_act(src) //hit the turf if it is
|
||||
|
||||
if((locate(/obj/structure/stairs/middle) in T) && (locate(/obj/structure/stairs/bottom) in get_turf(src))) // If we're growing against stairs, check for an open space above to grow 'up' the stairs.
|
||||
var/turf/TAbove = GetAbove(src)
|
||||
if(TAbove.CanZPass(src, UP)) // Can we pass through the turf above us to enter the tile? If so, swap T to that, we're going upstairs.
|
||||
T = TAbove
|
||||
|
||||
for(var/atom/A in T)
|
||||
if(!A.CanPass(src, T)) //is anything in the turf impassable
|
||||
make_blob = FALSE
|
||||
@@ -192,6 +202,21 @@ GLOBAL_LIST_EMPTY(all_blobs)
|
||||
else
|
||||
blob_attack_animation(T, controller) //if we can't, animate that we attacked
|
||||
|
||||
/obj/structure/blob/fall_impact(var/atom/hit_atom, var/damage_min = 0, var/damage_max = 10, var/silent = FALSE, var/planetary = FALSE)
|
||||
..()
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, 1)
|
||||
consume_tile()
|
||||
|
||||
/obj/structure/blob/can_fall() // Despite popular belief, the blob's belief in its ability to fly doesn't matter. (Unless something else is holding it up)
|
||||
if(overmind)
|
||||
for(var/direction in cardinal)
|
||||
var/turf/other_T = get_step(get_turf(src), direction)
|
||||
if(other_T)
|
||||
var/obj/structure/blob/B = locate(/obj/structure/blob/shield) in other_T
|
||||
if(B && B.overmind)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/blob/proc/do_slide_animation(var/obj/structure/blob/B, var/turf/T, var/expand_reaction)
|
||||
set waitfor = FALSE
|
||||
sleep(1) // To have the slide animation work.
|
||||
@@ -201,6 +226,9 @@ GLOBAL_LIST_EMPTY(all_blobs)
|
||||
B.density = initial(B.density)
|
||||
B.forceMove(T)
|
||||
B.update_icon()
|
||||
|
||||
B.fall()
|
||||
|
||||
if(B.overmind && expand_reaction)
|
||||
B.overmind.blob_type.on_expand(src, B, T, B.overmind)
|
||||
|
||||
@@ -417,6 +445,7 @@ GLOBAL_LIST_EMPTY(all_blobs)
|
||||
qdel(src)
|
||||
else
|
||||
update_icon()
|
||||
fall()
|
||||
|
||||
/obj/effect/temporary_effect/blob_attack
|
||||
name = "blob"
|
||||
|
||||
@@ -144,7 +144,7 @@ var/global/list/blob_cores = list()
|
||||
// overmind.update_health_hud()
|
||||
pulse_area(overmind, 15, BLOB_CORE_PULSE_RANGE, BLOB_CORE_EXPAND_RANGE)
|
||||
for(var/obj/structure/blob/normal/B in range(1, src))
|
||||
if(prob(5))
|
||||
if(B.overmind == overmind && prob(5))
|
||||
B.change_to(/obj/structure/blob/shield/core, overmind)
|
||||
|
||||
overmind.blob_type.on_core_process(src)
|
||||
|
||||
@@ -62,6 +62,7 @@ var/global/list/overminds = list()
|
||||
if(B && B.overmind == src)
|
||||
B.overmind = null
|
||||
B.update_icon() //reset anything that was ours
|
||||
B.fall() // No longer held up by whatever blob horror the core had going on (if any), fall like a really gross rock.
|
||||
|
||||
for(var/BLO in blob_mobs)
|
||||
var/mob/living/simple_mob/blob/spore/BM = BLO
|
||||
@@ -165,3 +166,6 @@ var/global/list/overminds = list()
|
||||
|
||||
log_say(message, src)
|
||||
return 1
|
||||
|
||||
/mob/observer/blob/can_fall()
|
||||
return FALSE
|
||||
|
||||
@@ -199,8 +199,24 @@
|
||||
if(B && B.overmind == src)
|
||||
break
|
||||
|
||||
// Only player-blobs can currently move up or down at will. AI-blobs can only fall downward.
|
||||
if(!B) // No cardinals, check if it can creep down from above or build up from below.
|
||||
var/list/T_ZChecks = list()
|
||||
|
||||
T_ZChecks["UP"] = GetAbove(T)
|
||||
T_ZChecks["DOWN"]= GetBelow(T)
|
||||
|
||||
for(var/zdir in T_ZChecks)
|
||||
var/turf/T_check = T_ZChecks[zdir]
|
||||
if(!T_check)
|
||||
continue
|
||||
B = locate(/obj/structure/blob) in T_check
|
||||
if(B && T.CanZPass(B, (zdir == "UP") ? DOWN : UP)) // Can the blob [B] from [zdir] pass into the target turf by moving in the opposite direction. IE, A blob above enter by moving downward, or reverse.
|
||||
break
|
||||
B = null // If both checks fail, B must be nulled for the next check.
|
||||
|
||||
if(!B)
|
||||
to_chat(src, "<span class='warning'>There is no blob cardinally adjacent to the target tile!</span>")
|
||||
to_chat(src, "<span class='warning'>There is no blob cardinally or vertically adjacent to the target tile!</span>")
|
||||
return
|
||||
|
||||
if(!can_buy(4))
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/obj/structure/stairs/Initialize()
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
|
||||
/obj/structure/stairs/LateInitialize()
|
||||
..()
|
||||
if(check_integrity())
|
||||
@@ -21,13 +21,13 @@
|
||||
|
||||
// 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:
|
||||
// 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,
|
||||
/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)
|
||||
|
||||
@@ -94,14 +94,14 @@
|
||||
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)
|
||||
@@ -121,7 +121,7 @@
|
||||
|
||||
// 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))
|
||||
if(istype(M) && istype(T) && ..(src, M, T, O))
|
||||
return TRUE
|
||||
|
||||
// Else, we have to look in other directions
|
||||
@@ -130,12 +130,12 @@
|
||||
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
|
||||
|
||||
@@ -146,21 +146,24 @@
|
||||
use_stairs(AM, oldloc)
|
||||
..()
|
||||
|
||||
/obj/structure/stairs/bottom/use_stairs(var/atom/movable/AM, var/atom/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
|
||||
|
||||
|
||||
if(AM.has_buckled_mobs()) // Similarly, the rider entering the turf will bring along whatever they're buckled to
|
||||
return
|
||||
|
||||
if(istype(AM, /obj/structure/blob)) // Blobs can't go down stairs normally. They kind of.. roll down them, when spreading. (See: They just fall.)
|
||||
return
|
||||
|
||||
var/list/atom/movable/pulling = list() // Will also include grabbed mobs
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
@@ -176,7 +179,7 @@
|
||||
pulling |= L.pulling
|
||||
for(var/obj/item/grab/G in list(L.l_hand, L.r_hand))
|
||||
pulling |= G.affecting
|
||||
|
||||
|
||||
// If the stairs aren't broken, go up.
|
||||
if(check_integrity())
|
||||
AM.dir = src.dir
|
||||
@@ -188,7 +191,7 @@
|
||||
|
||||
// Move to Top
|
||||
AM.forceMove(get_turf(top))
|
||||
|
||||
|
||||
// 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)
|
||||
P.forceMove(get_turf(top)) // Just bring it along directly, no fussing with animation timing
|
||||
@@ -203,18 +206,21 @@
|
||||
if(isobserver(AM)) // Ghosts have their own methods for going up and down
|
||||
return
|
||||
|
||||
if(istype(AM, /obj/structure/blob)) // Blobs can't go down stairs normally. They kind of.. roll down them, when spreading. (See: They just fall.)
|
||||
return
|
||||
|
||||
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(L.has_buckled_mobs())
|
||||
return
|
||||
|
||||
if(L.buckled)
|
||||
L.buckled.forceMove(get_turf(top))
|
||||
|
||||
|
||||
// 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)
|
||||
var/atom/movable/P = L.pulling
|
||||
@@ -225,7 +231,7 @@
|
||||
G.affecting.forceMove(get_turf(top))
|
||||
|
||||
L.forceMove(get_turf(top))
|
||||
|
||||
|
||||
if(L.client)
|
||||
L.client.Process_Grab()
|
||||
else
|
||||
@@ -259,10 +265,10 @@
|
||||
|
||||
// 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/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) && \
|
||||
@@ -285,7 +291,7 @@
|
||||
// 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))
|
||||
@@ -297,11 +303,11 @@
|
||||
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
|
||||
@@ -345,7 +351,7 @@
|
||||
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) && \
|
||||
@@ -381,11 +387,11 @@
|
||||
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
|
||||
|
||||
@@ -407,16 +413,19 @@
|
||||
// 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
|
||||
|
||||
|
||||
if(AM.has_buckled_mobs()) // Similarly, the rider entering the turf will bring along whatever they're buckled to
|
||||
return
|
||||
|
||||
if(istype(AM, /obj/structure/blob)) // Blobs can't go down stairs normally. They kind of.. roll down them, when spreading. (See: They just fall.)
|
||||
return
|
||||
|
||||
var/list/atom/movable/pulling = list() // Will also include grabbed mobs
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
@@ -432,7 +441,7 @@
|
||||
pulling |= L.pulling
|
||||
for(var/obj/item/grab/G in list(L.l_hand, L.r_hand))
|
||||
pulling |= G.affecting
|
||||
|
||||
|
||||
// If the stairs aren't broken, go up.
|
||||
if(check_integrity())
|
||||
AM.dir = turn(src.dir, 180)
|
||||
@@ -442,7 +451,7 @@
|
||||
|
||||
// Move to Top
|
||||
AM.forceMove(get_turf(bottom))
|
||||
|
||||
|
||||
// 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)
|
||||
P.forceMove(get_turf(bottom)) // Just bring it along directly, no fussing with animation timing
|
||||
@@ -457,18 +466,21 @@
|
||||
if(isobserver(AM)) // Ghosts have their own methods for going up and down
|
||||
return
|
||||
|
||||
if(istype(AM, /obj/structure/blob)) // Blobs can't go down stairs normally. They kind of.. roll down them, when spreading. (See: They just fall.)
|
||||
return
|
||||
|
||||
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(L.has_buckled_mobs())
|
||||
return
|
||||
|
||||
if(L.buckled)
|
||||
L.buckled.forceMove(get_turf(bottom))
|
||||
|
||||
|
||||
// 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)
|
||||
var/atom/movable/P = L.pulling
|
||||
@@ -477,7 +489,7 @@
|
||||
|
||||
for(var/obj/item/grab/G in list(L.l_hand, L.r_hand))
|
||||
G.affecting.forceMove(get_turf(bottom))
|
||||
|
||||
|
||||
L.forceMove(get_turf(bottom))
|
||||
|
||||
if(L.client)
|
||||
@@ -498,14 +510,14 @@
|
||||
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
|
||||
@@ -521,7 +533,7 @@
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user