/mob/verb/up() set name = "Move Upwards" set category = "IC" if(zMove(UP)) to_chat(src, "You move upwards.") /mob/verb/down() set name = "Move Down" set category = "IC" if(zMove(DOWN)) to_chat(src, "You move down.") /mob/proc/zMove(direction) if(eyeobj) return eyeobj.zMove(direction) if(!can_ztravel()) to_chat(src, "You lack means of travel in that direction.") return var/turf/start = loc if(!istype(start)) to_chat(src, "You are unable to move from here.") return 0 var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) if(!destination) to_chat(src, "There is nothing of interest in this direction.") return 0 if(!start.CanZPass(src, direction)) to_chat(src, "\The [start] is in the way.") return 0 if(!destination.CanZPass(src, direction)) to_chat(src, "\The [destination] blocks your way.") return 0 var/area/area = get_area(src) if(direction == UP && area.has_gravity() && !can_overcome_gravity()) var/obj/structure/lattice/lattice = locate() in destination.contents if(lattice) var/pull_up_time = max(5 SECONDS + (src.movement_delay() * 10), 1) to_chat(src, "You grab \the [lattice] and start pulling yourself upward...") destination.audible_message("You hear something climbing up \the [lattice].") if(do_after(src, pull_up_time)) to_chat(src, "You pull yourself up.") else to_chat(src, "You gave up on pulling yourself up.") return 0 else to_chat(src, "Gravity stops you from moving upward.") return 0 for(var/atom/A in destination) if(!A.CanPass(src, start, 1.5, 0)) to_chat(src, "\The [A] blocks you.") return 0 if(!Move(destination)) return 0 return 1 /mob/proc/can_overcome_gravity() return FALSE /mob/living/carbon/human/can_overcome_gravity() return species && species.can_overcome_gravity(src) /mob/observer/zMove(direction) var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) if(destination) forceMove(destination) else to_chat(src, "There is nothing of interest in this direction.") /mob/observer/eye/zMove(direction) var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) if(destination) setLoc(destination) else to_chat(src, "There is nothing of interest in this direction.") /mob/proc/can_ztravel() return 0 /mob/observer/can_ztravel() return 1 /mob/living/carbon/human/can_ztravel() if(incapacitated()) return 0 if(Process_Spacemove()) return 1 if(Check_Shoegrip()) //scaling hull with magboots for(var/turf/simulated/T in trange(1,src)) if(T.density) return 1 /mob/living/silicon/robot/can_ztravel() if(incapacitated() || is_dead()) return 0 if(Process_Spacemove()) //Checks for active jetpack return 1 for(var/turf/simulated/T in trange(1,src)) //Robots get "magboots" if(T.density) return 1 // TODO - Leshana Experimental //Execution by grand piano! /atom/movable/proc/get_fall_damage() return 42 //If atom stands under open space, it can prevent fall, or not /atom/proc/can_prevent_fall(var/atom/movable/mover, var/turf/coming_from) return (!CanPass(mover, coming_from)) //////////////////////////// //FALLING STUFF //Holds fall checks that should not be overriden by children /atom/movable/proc/fall() if(!isturf(loc)) return var/turf/below = GetBelow(src) if(!below) return var/turf/T = loc if(!T.CanZPass(src, DOWN) || !below.CanZPass(src, DOWN)) return // No gravity in space, apparently. var/area/area = get_area(src) if(!area.has_gravity()) return if(throwing) return if(can_fall()) // We spawn here to let the current move operation complete before we start falling. fall() is normally called from // Entered() which is part of Move(), by spawn()ing we let that complete. But we want to preserve if we were in client movement // or normal movement so other move behavior can continue. var/mob/M = src var/is_client_moving = (ismob(M) && M.client && M.client.moving) spawn(0) if(is_client_moving) M.client.moving = 1 handle_fall(below) if(is_client_moving) M.client.moving = 0 // TODO - handle fall on damage! //For children to override /atom/movable/proc/can_fall() if(anchored) return FALSE return TRUE /obj/effect/can_fall() return FALSE /obj/effect/decal/cleanable/can_fall() return TRUE // These didn't fall anyways but better to nip this now just incase. /atom/movable/lighting_overlay/can_fall() return FALSE // Mechas are anchored, so we need to override. /obj/mecha/can_fall() return TRUE /obj/item/pipe/can_fall() . = ..() if(anchored) return FALSE var/turf/below = GetBelow(src) if((locate(/obj/structure/disposalpipe/up) in below) || locate(/obj/machinery/atmospherics/pipe/zpipe/up in below)) return FALSE /mob/living/carbon/human/can_fall() if(..()) return species.can_fall(src) /mob/living/simple_animal/parrot/can_fall() // Poly can fly. return FALSE /mob/living/simple_animal/hostile/carp/can_fall() // So can carp apparently. return FALSE // Check if this atom prevents things standing on it from falling. Return TRUE to allow the fall. /obj/proc/CanFallThru(atom/movable/mover as mob|obj, turf/target as turf) return TRUE // Things that prevent objects standing on them from falling into turf below /obj/structure/catwalk/CanFallThru(atom/movable/mover as mob|obj, turf/target as turf) if(target.z < z) return FALSE // TODO - Technically should be density = 1 and flags |= ON_BORDER if(!isturf(mover.loc)) return FALSE // Only let loose floor items fall. No more snatching things off people's hands. else return TRUE // So you'll slam when falling onto a catwalk /obj/structure/catwalk/CheckFall(var/atom/movable/falling_atom) return falling_atom.fall_impact(src) /obj/structure/lattice/CanFallThru(atom/movable/mover as mob|obj, turf/target as turf) if(target.z >= z) return TRUE // We don't block sideways or upward movement. else if(istype(mover) && mover.checkpass(PASSGRILLE)) return TRUE // Anything small enough to pass a grille will pass a lattice if(!isturf(mover.loc)) return FALSE // Only let loose floor items fall. No more snatching things off people's hands. else return FALSE // TODO - Technically should be density = 1 and flags |= ON_BORDER // So you'll slam when falling onto a grille /obj/structure/lattice/CheckFall(var/atom/movable/falling_atom) if(istype(falling_atom) && falling_atom.checkpass(PASSGRILLE)) return FALSE return falling_atom.fall_impact(src) // Actually process the falling movement and impacts. /atom/movable/proc/handle_fall(var/turf/landing) var/turf/oldloc = loc // Check if there is anything in our turf we are standing on to prevent falling. for(var/obj/O in loc) if(!O.CanFallThru(src, landing)) return FALSE // See if something in turf below prevents us from falling into it. 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) 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) /atom/movable/proc/special_fall_handle(var/atom/A) return FALSE /mob/living/carbon/human/special_fall_handle(var/atom/A) if(species) return species.fall_impact_special(src, A) return FALSE /atom/movable/proc/find_fall_target(var/turf/oldloc, var/turf/landing) if(isopenspace(oldloc)) oldloc.visible_message("\The [src] falls down through \the [oldloc]!", "You hear something falling through the air.") // If the turf has density, we give it first dibs if (landing.density && landing.CheckFall(src)) return landing // First hit objects in the turf! for(var/atom/movable/A in landing) if(A != src && A.CheckFall(src)) return A // If none of them stopped us, then hit the turf itself if(landing.CheckFall(src)) return landing /mob/living/carbon/human/find_fall_target(var/turf/landing) if(species) var/atom/A = species.find_fall_target_special(src, landing) if(A) return A return ..() //CheckFall landing.fall_impact(src) // ## THE FALLING PROCS ### // Called on everything that falling_atom might hit. Return TRUE if you're handling it so find_fall_target() will stop checking. /atom/proc/CheckFall(var/atom/movable/falling_atom) if(density && !(flags & ON_BORDER)) return TRUE // If you are hit: how is it handled. // Return TRUE if the generic fall_impact should be called // Return FALSE if you handled it yourself or if there's no effect from hitting you /atom/proc/check_impact(var/atom/movable/falling_atom) if(density && !(flags & ON_BORDER)) return TRUE // By default all turfs are gonna let you hit them regardless of density. /turf/CheckFall(var/atom/movable/falling_atom) return TRUE /turf/check_impact(var/atom/movable/falling_atom) return TRUE // Obviously you can't really hit open space. /turf/simulated/open/CheckFall(var/atom/movable/falling_atom) return FALSE /turf/simulated/open/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 /mob/observer/dead/check_impact() return FALSE // Called by CheckFall when we actually hit something. Various Vars will be described below // hit_atom is the thing we fall on // damage_min is the smallest amount of damage a thing (currently only mobs and mechs) will take from falling // damage_max is the largest amount of damage a thing (currently only mobs and mechs) will take from falling. // If silent is True, the proc won't play sound or give a message. // If planetary is True, it's harder to stop the fall damage /atom/movable/proc/fall_impact(var/atom/hit_atom, var/damage_min = 0, var/damage_max = 10, var/silent = FALSE, var/planetary = FALSE) if(!silent) visible_message("\The [src] falls from above and slams into \the [hit_atom]!", "You hear something slam into \the [hit_atom].") // Take damage from falling and hitting the ground /mob/living/fall_impact(var/turf/landing, var/damage_min = 0, var/damage_max = 30, var/silent = FALSE, var/planetary = FALSE) if(planetary && src.CanParachute()) if(!silent) visible_message("\The [src] glides in from above and lands on \the [landing]!", \ "You land on \the [landing]!", \ "You hear something land \the [landing].") return else if(!planetary && src.softfall) // Falling one floor and falling one atmosphere are very different things if(!silent) visible_message("\The [src] falls from above and lands on \the [landing]!", \ "You land on \the [landing]!", \ "You hear something land \the [landing].") return else if(!silent) if(planetary) visible_message("\A [src] falls out of the sky and crashes into \the [landing]!", \ " You fall out of the skiy and crash into \the [landing]!", \ "You hear something slam into \the [landing].") var/turf/T = get_turf(landing) explosion(T, 0, 1, 2) else visible_message("\The [src] falls from above and slams into \the [landing]!", \ "You fall off and hit \the [landing]!", \ "You hear something slam into \the [landing].") playsound(loc, "punch", 25, 1, -1) if(planetary) //Since the planetary fall damage is calibrated for humans, we need to up this a bit damage_min *= 2 damage_max *= 2 adjustBruteLoss(rand(damage_min, damage_max)) return return /mob/living/carbon/human/fall_impact(var/turf/landing, var/damage_min = 0, var/damage_max = 10, var/silent = FALSE, var/planetary = FALSE) if(planetary && src.CanParachute()) if(!silent) visible_message("\The [src] glides in from above and lands on \the [landing]!", \ "You land on \the [landing]!", \ "You hear something land \the [landing].") return else if(!planetary && src.softfall) // Falling one floor and falling one atmosphere are very different things if(!silent) visible_message("\The [src] falls from above and lands on \the [landing]!", \ "You land on \the [landing]!", \ "You hear something land \the [landing].") return else if(!silent) if(planetary) visible_message("\A [src] falls out of the sky and crashes into \the [landing]!", \ " You fall out of the skiy and crash into \the [landing]!", \ "You hear something slam into \the [landing].") var/turf/T = get_turf(landing) explosion(T, 0, 1, 2) else visible_message("\The [src] falls from above and slams into \the [landing]!", \ "You fall off and hit \the [landing]!", \ "You hear something slam into \the [landing].") playsound(loc, "punch", 25, 1, -1) // Because wounds heal rather quickly, 10 should be enough to discourage jumping off but not be enough to ruin you, at least for the first time. apply_damage(rand(damage_min, damage_max), BRUTE, BP_HEAD) apply_damage(rand(damage_min, damage_max), BRUTE, BP_TORSO) apply_damage(rand(damage_min, damage_max), BRUTE, BP_GROIN) apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_LEG) apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_LEG) apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_FOOT) apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_FOOT) apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_ARM) apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_ARM) apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_HAND) apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_HAND) Weaken(4) updatehealth() return return //Checks if the mob is allowed to survive a fall from space /mob/living/proc/CanParachute() return parachuting //For humans, this needs to be a wee bit more complicated /mob/living/carbon/human/CanParachute() //Certain slots don't really need to be checked for parachute ability, i.e. pockets, ears, etc. If this changes, just add them to the loop, I guess? //This is done in Priority Order, so items lower down the list don't call handleParachute() unless they're actually used. if(back && back.isParachute()) back.handleParachute() return TRUE if(s_store && s_store.isParachute()) back.handleParachute() return TRUE if(belt && belt.isParachute()) back.handleParachute() return TRUE if(wear_suit && wear_suit.isParachute()) back.handleParachute() return TRUE if(w_uniform && w_uniform.isParachute()) back.handleParachute() return TRUE else return parachuting //For human falling code //Using /obj instead of /obj/item because I'm not sure what all humans can pick up or wear /obj var/parachute = FALSE /obj/proc/isParachute() return parachute //This is what makes the parachute items know they've been used. //I made it /atom/movable so it can be retooled for other things (mobs, mechs, etc), though it's only currently called in human/CanParachute(). /atom/movable/proc/handleParachute() return //Mech Code /obj/mecha/handle_fall(var/turf/landing) // First things first, break any lattice var/obj/structure/lattice/lattice = locate(/obj/structure/lattice, loc) if(lattice) // Lattices seem a bit too flimsy to hold up a massive exosuit. lattice.visible_message("\The [lattice] collapses under the weight of \the [src]!") qdel(lattice) // Then call parent to have us actually fall return ..() /obj/mecha/fall_impact(var/atom/hit_atom, var/damage_min = 15, var/damage_max = 30, var/silent = FALSE, var/planetary = FALSE) // Tell the pilot that they just dropped down with a superheavy mecha. if(occupant) to_chat(occupant, "\The [src] crashed down onto \the [hit_atom]!") // Anything on the same tile as the landing tile is gonna have a bad day. for(var/mob/living/L in hit_atom.contents) L.visible_message("\The [src] crushes \the [L] as it lands on them!") L.adjustBruteLoss(rand(70, 100)) L.Weaken(8) // Now to hurt the mech. take_damage(rand(damage_min, damage_max)) // And hurt the floor. if(istype(hit_atom, /turf/simulated/floor)) var/turf/simulated/floor/ground = hit_atom ground.break_tile()