Files
Aurora.3/code/modules/multiz/movement.dm
T
BatrachophrenoandGitHub af6c74d917 Verb organization stuff (#21131)
Currently, only Preferences Verbs use subcategories. As a result, all
other Verb tabs are only organized alphabetically, meaning that a single
mechanic (ie. languages) has its governing Verbs scattered rather than
grouped together.

Verb organization by Tab handles broad organization - IC, OOC, Objects,
etc.- highly effectively but requires the user to click-navigate to
other Tabs to access any other verbs.

Verb organization within a Tab can be handled by A.) subcategories and
B.) Verb name prefixes (such as Earphones - *****). The first option is
the clearest and cleanest, but consumes additional vertical space. The
second option conserves vertical space at the cost of visual clarity.

This PR attempts to reorganize Verbs by their Tabs, Subcategories, and
prefixes with the following priorities in mind:
- Minimal disruption; any radical change must be justified (don't move
shit around just for the sake of moving shit around.)
- Verbs which share common functions (such as Languages) should be
grouped together.
- Maintainability. Contributors should not need to memorize an
excessively convoluted Subcategories list; Subcategories must be
intuitive and simple.
- Subcategories should be used judiciously when grouping Verbs to
conserve vertical space. Example: Instead of creating a Subcategory for
Emotes, just rename Audible Emote and Visual Emote to Emote (Audible)
and Emote (Visual).
- Subcategories should highlight uncommon or unusual verbs when
reasonable. Example: There are several animal husbandry-related Verbs,
and because most characters will not have access to those verbs
regularly, we can be less conservative with space (because they'll be
gone most of the time) for the sake of emphasizing their availability.

This PR covers the IC, OOC, and Object Tabs.

Subcategories:
OOC.Chat (Chat functions)
- AOOC
- LOOC
- Devsay (moved from Special Verbs)
- Dsay (moved from Special Verbs)
- OOC

OOC.Debug (Fix shit)
- Fit viewport
- Fix chat
- Refresh TGUI

OOC.Round (Round information)
- Check Gamemode Probability
- Check Round Info
- Custom Event Info
- Vote

IC.Antag (Antag verbs w/o their own tab)
- Invite to the Loyalists
- Invite to the Revolutionaries
- Set Ambition

IC.Critters (More critter stuff than you'd expect there to be)
- Befriend Carp (Previous name 'Become Friends')
- Befriend Cat (Previous name 'Become Friends')
- Befriend Dog
- Befriend Ives
- Name Alien Species
- Name Animal

IC.Language (Language-related stuff)
- Check Default Language
- Check Known Languages
- Set Default Language

IC.Maneuver (Movement, positioning, etc.)
- Adjust walk speed
- Do Pushup
- Face Direction
- Look Down
- Look Up
- Move Downwards
- Move Upwards
- Rest

Object.Equipped (Equipped object stuff, mostly for drip)
- Adjust Badge
- Adjust Bag Straps (Backpacks)
- Adjust Goggles
- Adjust Mask
- Adjust Welding Goggles
- Adjust Welding Mask
- Change Glasses Layer
- Change Pants Layer
- Change Wrist Radio Layer
- Change Wristwear Layer
- Check Time
- Flip Badge
- Flip Belt
- Flip Eyepatch
- Flip Hat
- Flip ID card (ID card)
- Flip Radio (Clip-on radio)
- Flip Wristwear
- Fold Collar
- Lock Antenna(e)
- Point At Watch
- Raise Shroud
- Roll Up Cape Mantle
- Roll Up/Down Jumpsuit
- Roll Up/Down Sleeves
- Switch Belt Layer
- Switch ID Layer
- Switch Lanyard Layer
- Switch Shoe Layer
- Toggle Aviators (Like a dozen variants)
- Toggle Ceremonial Garment Lights
- Toggle Coat Buttons
- Toggle Coat Zipper
- Toggle Hair Coverage (For hats helmets etc)
- Toggle Hair Coverage (FOR BABY CARP!!!!!)
- Toggle Helmet Camera
- Toggle Hood
- Toggle Lyodsuit Mask
- Toggle Poncho Tail Coverage
- Toggle Shirt Buttons
- Toggle Suit Sensors
- Toggle Visor (Pilot helmet)
- Toggle Waistcoat Buttons
- Transform Holoclothing
- Voidsuit - Eject Suit Cooler
- Voidsuit - Eject Tank
- Voidsuit - Toggle Helmet

Object.Held (If you're HOLDING it, its probably relevant to you rn)
- Activate Held Object (Silicons; moved from IC)
- Alter Beacon's Signal (Radio beacon)
- Change Bite Size (Utensils)
- Change Pen Colour
- Choose Colour (Paint sprayer)
- Choose Decal (Paint sprayer)
- Choose Direction (Paint sprayer)
- Choose Preset Colour (Paint sprayer)
- Describe Prototype
- Draw Boot Knife
- Empty Bee Net
- Empty Spray Bottle
- Holster
- Name Gun
- Name Prototype
- Plant Flag (Flags)
- Print Plant Report
- Remove Chopsticks
- Remove Top
- Set Detector High-Bound (Light meter)
- Set Detector Low-Bound (Light meter)
- Set Hailer Message (Hailer)
- Set Timer (Timer igniter)
- Set Valve Pressure (Pneumatic cannon)
- Show Held Item
- Spin Cylinder (Revolvers)
- Switch Verbosity (Health analyzer)
- Toggle Chainsaw Power (Chainsaw)
- Toggle Flashlight Brightness (Flashlights)
- Toggle Gun Safety
- Toggle Hazard Vest
- Toggle Pinpointer Mode (Pinpointer)
- Toggle Mister (Backpack water tank. Goes into/out of hands)
- Twist Cap
- Use Scope
- Wield Pick/Drill
- Wield Two-Handed Weapon (Two-handed weapons)

Object.Cards (I fucking hate cards!!!)
- Deck - Deal
- Deck - Draw
- Deck - Pick
- Hand - Pick
- Turn Hand Into Deck

Object.Earphones (Enough here to separate out)
- Eject Music Cartridge
- Change Volume
- Next Song
- Pause/Unpause
- Play/Stop
- Previous Song

Object.Jetpack (This is probably really important to see quickly)
- Toggle Jetpack
- Toggle Jetpack Stabilization

Object.Tape Recorder (Enough here to separate out)
- Start Recording
- Stop Recording
- Clear Memory
- Playback Memory
- Print Transcript
- Eject Portable Storage

**IC tab example:**
<img width="1061" height="277" alt="Screenshot 2025-08-09 101813"
src="https://github.com/user-attachments/assets/a2afe92d-34fd-4160-bf68-b459a4195e26"
/>
**Objects tab example:**
<img width="1100" height="472" alt="Screenshot 2025-08-09 101807"
src="https://github.com/user-attachments/assets/a36e4c76-594e-4569-93ba-ffcda57e8760"
/>
2025-08-11 15:12:56 +00:00

848 lines
28 KiB
Plaintext

/atom/movable
/** Used to check wether or not an atom is being handled by SSfalling. */
var/tmp/multiz_falling = 0
/**
* Verb for the mob to move up a z-level if possible.
*/
/mob/verb/up()
set name = "Move Upwards"
set category = "IC.Maneuver"
if(zMove(UP))
visible_message(SPAN_NOTICE("[src] has moved upwards."), SPAN_NOTICE("You move upwards."))
/**
* Verb for the mob to move down a z-level if possible.
*/
/mob/verb/down()
set name = "Move Downwards"
set category = "IC.Maneuver"
if(zMove(DOWN))
visible_message(SPAN_NOTICE("[src] has moved downwards."), SPAN_NOTICE("You move downwards."))
/**
* Used to check if a mob can move up or down a Z-level and to then actually do the move.
*
* @param direction The direction in which we're moving. Expects defines UP or DOWN.
*
* @return TRUE if the mob has been successfully moved a Z-level.
* FALSE otherwise.
*/
/mob/proc/zMove(direction)
// In the case of an active eyeobj, move that instead.
if (eyeobj)
return eyeobj.zMove(direction)
// Check if we can actually travel a Z-level.
if (!can_ztravel(direction))
to_chat(src, SPAN_WARNING("You lack means of travel in that direction."))
return FALSE
var/turf/T = get_turf(src)
var/turf/destination = (direction == UP) ? GET_TURF_ABOVE(T) : GET_TURF_BELOW(T)
if(!destination)
to_chat(src, SPAN_NOTICE("There is nothing of interest in this direction."))
return FALSE
if(incorporeal_move == INCORPOREAL_BSTECH)
forceMove(destination)
return TRUE
var/turf/start = get_turf(src)
if(!start.CanZPass(src, direction))
to_chat(src, SPAN_WARNING("\The [start.GetZPassBlocker()] is in the way."))
return FALSE
if(!destination.CanZPass(src, direction))
to_chat(src, SPAN_WARNING("\The [destination.GetZPassBlocker()] is in the way!"))
return FALSE
var/area/area = get_area(src)
// If we want to move up,but the current area has gravity. Invoke CanAvoidGravity()
// to check if this move is possible.
if(direction == UP && area.has_gravity() && !CanAvoidGravity())
to_chat(src, SPAN_WARNING("Gravity stops you from moving upward."))
return FALSE
// Check for blocking atoms at the destination.
for (var/atom/A in destination)
if (!A.CanPass(src, start, 1.5, 0))
to_chat(src, SPAN_WARNING("\The [A] blocks you."))
return FALSE
if(buckled_to && istype(buckled_to, /obj/vehicle))
var/obj/vehicle/car = buckled_to
if(car.flying)
buckled_to.Move(destination)
return TRUE
// Actually move.
Move(destination)
return TRUE
/mob/living/carbon/human/zMove(direction)
if(istype(loc, /mob/living/heavy_vehicle))
var/mob/living/heavy_vehicle/mech = loc
mech.zMove(direction)
return
. = ..()
if(.)
for(var/obj/item/grab/G in list(l_hand, r_hand))
if(G.state >= GRAB_NECK) //strong grip
if(G.affecting && !(G.affecting.buckled_to))
G.affecting.Move(get_turf(src))
visible_message(SPAN_WARNING("[src] pulls [G.affecting] [direction & UP ? "upwards" : "downwards"]!"))
/mob/living/zMove(direction)
if (is_ventcrawling)
var/obj/machinery/atmospherics/pipe/zpipe/P = loc
if (istype(P) && P.can_z_crawl(src, direction))
return P.handle_z_crawl(src, direction)
return ..()
/mob/abstract/eye/zMove(direction)
var/turf/T = get_turf(src)
var/turf/destination = (direction == UP) ? GET_TURF_ABOVE(T) : GET_TURF_BELOW(T)
if(destination)
setLoc(destination)
else
to_chat(owner, SPAN_NOTICE("There is nothing of interest in this direction."))
// Both observers and storytellers depend on this to be able to move through z-levels freely!
/mob/abstract/ghost/zMove(direction)
var/turf/T = get_turf(src)
var/turf/destination = (direction == UP) ? GET_TURF_ABOVE(T) : GET_TURF_BELOW(T)
if(destination)
forceMove(destination)
else
to_chat(src, SPAN_NOTICE("There is nothing of interest in this direction."))
/**
* An initial check for Z-level travel. Called relatively early in mob/proc/zMove.
*
* Useful for overwriting and special conditions for STOPPING z-level transit.
*
* @return TRUE if the mob can move a Z-level of its own volition.
* FALSE otherwise.
*/
/mob/proc/can_ztravel(var/direction)
if(incorporeal_move == INCORPOREAL_BSTECH)
return TRUE
return FALSE
/mob/abstract/ghost/observer/can_ztravel(var/direction)
return TRUE
/mob/living/carbon/human/can_ztravel(var/direction)
if(incapacitated())
return FALSE
if(incorporeal_move == INCORPOREAL_BSTECH)
return TRUE
if(Allow_Spacemove())
return TRUE
for(var/turf/simulated/T in RANGE_TURFS(1,src))
if(T.density)
if(Check_Shoegrip(FALSE))
return TRUE
/mob/living/carbon/human/proc/climb(var/direction, var/turf/source, var/climb_bonus)
var/turf/destination
if(direction == UP)
destination = GET_TURF_ABOVE(source)
else
destination = GET_TURF_BELOW(source)
if(!destination)
return
if(stat || paralysis || stunned || weakened || lying || restrained() || buckled_to)
return
if(destination.density)
to_chat(src, SPAN_NOTICE("There is something obstructing your destination!"))
return
for(var/obj/O in destination)
if(O.density)
to_chat(src, SPAN_NOTICE("There is something obstructing your destination!"))
return
visible_message(SPAN_NOTICE("The [src] begins to climb [(direction == UP) ? "upwards" : "downwards"]."),
SPAN_NOTICE("You begin to climb [(direction == UP) ? "upwards" : "downwards"]."))
var/climb_chance = 50
var/climb_speed = 45 SECONDS
var/will_succeed = FALSE
var/turf/stack_turf = get_turf(src) //turf upon which obejcts must be stacked upon to gain vantage
var/speed_bonus = 0
if(direction == DOWN)
stack_turf = destination
if(species && !species.natural_climbing)
for(var/obj/O in stack_turf)
if(O.w_class >= 4.0 || O.anchored) //if an object is anchored it's stable footing
climb_chance = min(100, climb_chance + O.w_class) //large items increase your reach
speed_bonus = min(15, speed_bonus + 1)
else
climb_chance = max(0, climb_chance - O.w_class) //small items destabilize your footing
speed_bonus = max(0, speed_bonus - 1)
if(climb_bonus)
climb_chance = min(100, climb_chance + climb_bonus)
else
climb_chance = 100
if(species && species.climb_coeff)
climb_speed = round(max(1, (species.climb_coeff * climb_speed) - speed_bonus), 1)
if(prob(climb_chance))
will_succeed = TRUE
if(do_after(src, climb_speed))
if(will_succeed)
visible_message(SPAN_NOTICE("\The [src] climbs [(direction == UP) ? "upwards" : "downwards"]."),
SPAN_NOTICE("You climb [(direction == UP) ? "upwards" : "downwards"]."))
forceMove(destination)
return
else
visible_message(SPAN_WARNING("\The [src] slips and falls as they climb [(direction == UP) ? "upwards" : "downwards"]!"),
SPAN_DANGER("You slip and fall as you climb [(direction == UP) ? "upwards" : "downwards"]!"))
if(direction == DOWN)
Move(destination)
fall_impact(1, damage_mod = min(1, max(0.2, ((100-climb_chance)/100) - 0.2)))
/mob/living/carbon/human/proc/climb_check(var/success, var/climb_chance, var/speed, var/direction, var/turf/destination) //purely for immersion and variety
if((last_special < world.time) && !success) //if you will succeed you can't fail
last_special = world.time + speed/10
if(prob(100 - climb_chance)) //The worse you are the sooner you'll fail.
visible_message(SPAN_WARNING("\The [src] slips and falls as they climb [(direction == UP) ? "upwards" : "downwards"]!"),
SPAN_DANGER("You slip and fall as you climb [(direction == UP) ? "upwards" : "downwards"]!"))
if(direction == DOWN)
Move(destination)
fall_impact(1, damage_mod = min(1, max(0.2, ((100-climb_chance)/100) - 0.2)))
return 0
return 1
/mob/living/silicon/robot/can_ztravel(var/direction)
if(incapacitated() || is_dead())
return FALSE
if(Allow_Spacemove()) //Checks for active jetpack
return TRUE
for(var/turf/simulated/T in RANGE_TURFS(1,src)) //Robots get "magboots"
if(T.density)
return TRUE
/**
* Used to determine whether or not a given mob can override gravity when
* attempting to Z-move UP.
*
* Returns FALSE in standard mob cases. Exists for carbon/human and other child overrides.
*
* @return TRUE if the mob can Z-move up despite gravity.
* FALSE otherwise.
*/
/mob/proc/CanAvoidGravity()
if(status_flags & NOFALL || incorporeal_move == INCORPOREAL_BSTECH)
return TRUE
return FALSE
// Humans and borgs have jetpacks which allows them to override gravity! Or rather,
// they can have them. So we override and check.
/mob/living/carbon/human/CanAvoidGravity()
if (!restrained())
var/obj/item/tank/jetpack/thrust = GetJetpack(src)
if (thrust && !lying && thrust.allow_thrust(0.01, src))
return TRUE
if(buckled_to && istype(buckled_to, /obj/vehicle))
var/obj/vehicle/car = buckled_to
if(car.flying)
return TRUE
return ..()
/mob/living/silicon/robot/CanAvoidGravity()
var/obj/item/tank/jetpack/thrust = GetJetpack(src)
if (thrust && thrust.allow_thrust(0.02, src))
return TRUE
return ..()
/**
* An overridable proc used by SSfalling to determine whether or not an atom
* should continue falling to the next level, or stop processing and be caught
* in midair, effectively. One of the ways to make things never fall is to make
* this return FALSE.
*
* If the mob has fallen and is stopped amidst a fall by this, fall_impact is
* invoked with the second argument being TRUE. As opposed to the default value, FALSE.
*
* @param below The turf that the mob is expected to end up at.
* @param dest The tile we're presuming the mob to be at for this check. Default
* value is src.loc, (src. is important there!) but this is used for magboot lookahead
* checks it turf/simulated/open/Enter().
*
* @return TRUE if the atom can continue falling in its present situation.
* FALSE if it should stop falling and not invoke fall_through or fall_impact
* this cycle.
*/
/atom/movable/proc/can_fall(turf/below, turf/simulated/open/dest = src.loc)
if (!istype(dest) || !dest.is_hole)
return FALSE
// Anchored things don't fall.
if(anchored)
return FALSE
// Lattices, ladders, and stairs stop things from falling.
if(locate(/obj/structure/lattice, dest) || locate(/obj/structure/stairs, dest))
return FALSE
if(ismob(src) && locate(/obj/structure/ladder, dest)) //hmmm how is this locker just floating here?
return FALSE
// The var/climbers API is implemented here.
if (LAZYLEN(dest.climbers) && (src in dest.climbers))
return FALSE
// See if something prevents us from falling.
for (var/atom/A in below)
if(!A.CanPass(src, dest))
return FALSE
// True otherwise.
return TRUE
/obj/effect/can_fall()
return FALSE
/obj/effect/decal/cleanable/can_fall()
return TRUE
/obj/item/pipe/can_fall(turf/below, turf/simulated/open/dest = src.loc)
. = ..()
if((locate(/obj/structure/disposalpipe/up) in below) || (locate(/obj/machinery/atmospherics/pipe/zpipe/up) in below))
return FALSE
/mob/can_fall()
if(status_flags & NOFALL || incorporeal_move == INCORPOREAL_BSTECH)
return FALSE
return ..()
/mob/living/heavy_vehicle/can_ztravel(var/direction)
if(legs)
if(legs.hover && legs.motivator.is_functional())
if(get_cell().charge < ((legs.power_use * CELLRATE) / 2))
return FALSE
return TRUE
return FALSE
/mob/living/heavy_vehicle/CanAvoidGravity()
if(can_ztravel())
return TRUE
return FALSE
/mob/living/heavy_vehicle/can_fall(turf/below, turf/simulated/open/dest = src.loc)
// The var/climbers API is implemented here.
if (LAZYLEN(dest.climbers) && (src in dest.climbers))
return FALSE
if (!dest.is_hole)
return FALSE
// See if something prevents us from falling.
for(var/atom/A in below)
if(!A.CanPass(src, dest))
return FALSE
// Hover thrusters
if(legs)
if(legs.hover && legs.motivator.is_functional())
get_cell().use((legs.power_use * CELLRATE) / 2)
return FALSE
// True otherwise.
return TRUE
// Only things that stop mechas are atoms that, well, stop them.
// Lattices and stairs get crushed in fall_through.
/mob/living/carbon/human/can_fall(turf/below, turf/simulated/open/dest = src.loc)
// Special condition for jetpack mounted folk!
if(!restrained())
var/obj/item/tank/jetpack/thrust = GetJetpack(src)
if(thrust && thrust.stabilization_on &&\
!lying && thrust.allow_thrust(0.01, src))
return FALSE
for(var/grab in grabbed_by)
var/obj/item/grab/G = grab
if(G.state >= GRAB_AGGRESSIVE)
return FALSE
return ..()
/mob/abstract/eye/can_fall()
return FALSE
/mob/living/silicon/robot/can_fall(turf/below, turf/simulated/open/dest = src.loc)
var/obj/item/tank/jetpack/thrust = GetJetpack(src)
if (thrust && thrust.stabilization_on && thrust.allow_thrust(0.02, src))
return FALSE
return ..()
/**
* Invoked by SSfalling when an atom is moved one open turf to another via falling.
*
* src.loc can be assumed to be of type /turf/simulated/open.
*/
/atom/movable/proc/fall_through()
visible_message("\The [src] falls from the level above through \the [loc]!",
"You hear a whoosh of displaced air.")
/mob/fall_through()
visible_message("\The [src] falls from the level above through \the [loc]!",
"You fall through \the [loc]!", "You hear a whoosh of displaced air.")
/mob/living/heavy_vehicle/fall_through()
var/obj/structure/lattice/L = locate() in loc
if (L)
visible_message(SPAN_DANGER("\The [src] crushes \the [L] with its weight!"))
qdel(L)
var/obj/structure/stairs/S = locate() in loc
if (S)
visible_message(SPAN_DANGER("\The [src] crushes \the [S] with its weight!"))
qdel(S)
/**
* Invoked when an atom has landed on a tile through which they can no longer fall.
*
* src.loc now contains the final and updated position of the atom.
*
* @param levels_fallen How many Z-levels the atom has fallen before landing
* on its current loc.
* @param stopped_early TRUE if the fall was stopped by can_fall.
* FALSE if the fall was stopped by the fact that the atom
* was no longer on an open turf.
*
* @return TRUE if the proc ran completely. FALSE otherwise. Used to determine
* if child procs should continue running or not, really.
*/
/atom/movable/proc/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
// No gravity, stop falling into spess!
var/area/area = get_area(src)
if (istype(loc, /turf/space) || (area && !area.has_gravity()))
return FALSE
visible_message("\The [src] falls and lands on \the [loc]!", "You hear a thud!")
return TRUE
// Mobs take damage if they fall!
/mob/living/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
// No gravity, stop falling into spess!
var/area/area = get_area(src)
if (istype(loc, /turf/space) || (area && !area.has_gravity()))
return FALSE
if(status_flags & GODMODE) // Godmode
visible_message(SPAN_NOTICE("\The [src] lands flawlessly on their legs, bending their knee to the floor. They promptly stand up."))
playsound(src.loc, /singleton/sound_category/swing_hit_sound, 50, 1)
return FALSE
visible_message("\The [src] falls and lands on \the [loc]!",
"With a loud thud, you land on \the [loc]!", "You hear a thud!")
var/z_velocity = 5*(levels_fallen**2)
var/damage = ((60 + z_velocity) + rand(-20,20)) * damage_mod
apply_damage(damage, DAMAGE_BRUTE)
// The only piece of duplicate code. I was so close. Soooo close. :ree:
if(!isSynthetic())
switch(damage)
if(-INFINITY to 10)
playsound(src.loc, 'sound/weapons/bladeslice.ogg', 50, 1)
if(11 to 50)
var/sound_to_play = pick(list('sound/weapons/punch1.ogg', 'sound/weapons/punch2.ogg', 'sound/weapons/punch3.ogg', 'sound/weapons/punch4.ogg'))
playsound(src.loc, sound_to_play, 75, 1)
if(51 to INFINITY)
playsound(src.loc, 'sound/weapons/heavysmash.ogg', 100, 1)
else
playsound(src.loc, /singleton/sound_category/swing_hit_sound, 75, 1)
else
playsound(src.loc, 'sound/weapons/smash.ogg', 75, 1)
return TRUE
/mob/living/carbon/human/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
// No gravity, stop falling into spess!
var/area/area = get_area(src)
if (istype(loc, /turf/space) || (area && !area.has_gravity()))
return FALSE
var/obj/item/rig/rig = get_rig()
if (istype(rig))
for (var/obj/item/rig_module/actuators/A in rig.installed_modules)
if (A.active && rig.check_power_cost(src, 10, A, 0))
visible_message(SPAN_NOTICE("\The [src] lands flawlessly with [src.get_pronoun("his")] [rig.name]."), SPAN_NOTICE("You land flawlessly with your [rig.name]."), SPAN_NOTICE("You hear an electric <i>*whirr*</i> right after a loud slam!"))
return FALSE
if(status_flags & GODMODE) // Godmode
visible_message(SPAN_NOTICE("\The [src] lands flawlessly on their legs, bending their knee to the floor. They promptly stand up."))
playsound(src.loc, /singleton/sound_category/swing_hit_sound, 50, 1)
return FALSE
var/combat_roll = 1
if(lying)
combat_roll = 0.8 //If you're sleeping, you take less damage because your body is less rigid. It's science 'n shit.
if(!sleeping)
combat_roll = 0.6 //Combat roll!
visible_message(SPAN_NOTICE("\The [src] tucks into a roll as they hit \the [loc]!"),
SPAN_NOTICE("You tuck into a roll as you hit \the [loc], minimizing damage!"))
var/aug_mod = 1
var/obj/item/organ/internal/augment/suspension/suspension = internal_organs_by_name[BP_AUG_SUSPENSION]
if(suspension && !suspension.is_broken())
aug_mod = suspension.suspension_mod
suspension.take_damage(10)
var/z_velocity = 5*(levels_fallen**2)
var/damage = (((40 * species.fall_mod) + z_velocity) + rand(-20,20)) * combat_roll * damage_mod * aug_mod
var/limb_damage = rand(0,damage/2)
if(prob(30) && combat_roll >= 1) //landed on their legs
var/left_damage = rand(0,damage/2)
var/right_damage = rand(0,damage/2)
var/leftf_damage = rand(0,damage/4)
var/rightf_damage = rand(0,damage/4)
var/groin_damage = rand(0,damage/4)
apply_damage(left_damage, DAMAGE_BRUTE, BP_L_LEG)
apply_damage(right_damage, DAMAGE_BRUTE, BP_R_LEG)
if(prob(50))
apply_damage(leftf_damage, DAMAGE_BRUTE, BP_R_FOOT)
if(prob(50))
apply_damage(leftf_damage, DAMAGE_BRUTE, BP_L_FOOT)
if(prob(50))
apply_damage(groin_damage, DAMAGE_BRUTE, BP_GROIN)
visible_message(SPAN_WARNING("\The [src] falls and lands directly on their legs!"),
SPAN_DANGER("You land on your feet, and the impact brings you to your knees."))
if(prob(20))
var/obj/item/organ/external/l_foot = get_organ(BP_L_FOOT)
var/obj/item/organ/external/r_foot = get_organ(BP_R_FOOT)
if(prob(50) && l_foot && l_foot.dislocated != -1)
fall_message("left ankle", "bends unnaturally")
l_foot.dislocate(TRUE)
else if(r_foot && r_foot.dislocated != -1)
fall_message("right ankle", "bends unnaturally")
r_foot.dislocate(TRUE)
else if(prob(15))
var/obj/item/organ/external/l_leg = get_organ(BP_L_LEG)
var/obj/item/organ/external/r_leg = get_organ(BP_R_LEG)
if(prob(50) && l_leg && l_leg.dislocated != -1)
fall_message("left knee", "caves in")
l_leg.dislocate(TRUE)
else if(r_leg && r_leg.dislocated != -1)
fall_message("right knee", "caves in")
l_leg.dislocate(TRUE)
limb_damage = left_damage + right_damage + leftf_damage + rightf_damage + groin_damage
else if(prob(30) && combat_roll >= 1) //landed on their arms
var/left_damage = rand(0,damage/4)
var/right_damage = rand(0,damage/4)
var/lefth_damage = rand(0,damage/4)
var/righth_damage = rand(0,damage/4)
apply_damage(left_damage, DAMAGE_BRUTE, BP_L_ARM)
apply_damage(right_damage, DAMAGE_BRUTE, BP_R_ARM)
if(prob(50))
apply_damage(lefth_damage, DAMAGE_BRUTE, BP_R_HAND)
if(prob(50))
apply_damage(righth_damage, DAMAGE_BRUTE, BP_L_HAND)
limb_damage = left_damage + right_damage + lefth_damage + righth_damage
visible_message(SPAN_WARNING("\The [src] falls and lands arms first!"),
SPAN_DANGER("You brace your fall with your arms, hitting \the [loc] with a loud thud."), "You hear a thud!")
if(prob(20))
var/obj/item/organ/external/l_hand = get_organ(BP_L_HAND)
var/obj/item/organ/external/r_hand = get_organ(BP_R_HAND)
if(prob(50) && l_hand && l_hand.dislocated != -1)
fall_message("left wrist", "bends unnaturally")
l_hand.dislocate(TRUE)
else if(r_hand && r_hand.dislocated != -1)
fall_message("right wrist", "bends unnaturally")
r_hand.dislocate(TRUE)
else if(prob(15))
var/obj/item/organ/external/l_arm = get_organ(BP_L_ARM)
var/obj/item/organ/external/r_arm = get_organ(BP_R_ARM)
if(prob(50) && l_arm && l_arm.dislocated != -1)
fall_message("left elbow", "caves in")
l_arm.dislocate(TRUE)
else if(r_arm && r_arm.dislocated != -1)
fall_message("right elbow", "caves in")
r_arm.dislocate(TRUE)
else if(prob(30) && combat_roll >= 1)//landed on their head
apply_damage(limb_damage, DAMAGE_BRUTE, BP_HEAD)
visible_message(SPAN_WARNING("\The [src] falls and lands on their face!"),
SPAN_DANGER("With a loud thud, you land on your head. Hard."), "You hear a thud!")
var/obj/item/organ/external/head = get_organ(BP_HEAD)
if(prob(20) && head && head.dislocated != -1)
fall_message("jaw", "cracks loose")
head.dislocate(TRUE)
else
limb_damage = 0
if(combat_roll >= 0.5)
visible_message("\The [src] falls and lands on \the [loc]!",
"With a loud thud, you land on \the [loc]!", "You hear a thud!")
if(!limb_damage)
apply_damage(damage, DAMAGE_BRUTE, BP_CHEST)
Weaken(rand(damage/4, damage/2))
updatehealth()
// Humans can be synthetic. Never forgetti.
if(!isSynthetic())
switch(damage)
if(-INFINITY to 10)
playsound(src.loc, 'sound/weapons/bladeslice.ogg', 50, 1)
if(11 to 50)
playsound(src.loc, /singleton/sound_category/punch_sound, 75, 1)
if(51 to INFINITY)
playsound(src.loc, 'sound/weapons/heavysmash.ogg', 100, 1)
else
playsound(src.loc, /singleton/sound_category/swing_hit_sound, 75, 1)
else
playsound(src.loc, 'sound/weapons/smash.ogg', 75, 1)
// Stats.
SSstatistics.IncrementSimpleStat("openturf_human_falls")
addtimer(CALLBACK(src, PROC_REF(post_fall_death_check)), 2 MINUTES, TIMER_UNIQUE | TIMER_OVERRIDE)
return TRUE
/mob/living/carbon/human/proc/fall_message(var/location, var/descriptor)
visible_message(SPAN_WARNING("There's a sickening popping noise as [src]'s [location] [descriptor]!"),
SPAN_DANGER("Grueling pain shoots through your mind as your [location] [descriptor]!"))
/mob/living/carbon/human/proc/post_fall_death_check()
if (stat == DEAD)
SSstatistics.IncrementSimpleStat("openturf_human_deaths")
/obj/vehicle/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1)
. = ..()
if (!.)
return
var/z_velocity = 5*(levels_fallen**2)
var/damage = ((60 + z_velocity) + rand(-20,20)) * damage_mod
if(istype(loc, /turf/simulated/floor/exoplanet/asteroid))
damage /= 2
health -= (damage * brute_dam_coeff)
playsound(loc, 'sound/effects/clang.ogg', 75, 1)
/**
* Used to handle damage dealing for objects post fall. Why is it separated from
* fall_impact? Because putting this into fall_impact would make the procs a huge
* mess of snowflake istype(src, x) checks. And I'm trying to avoid this by making
* the system quite atomic.
*
* @param levels_fallen How many Z-levels the atom has fallen before landing
* on its current loc.
* @param stopped_early TRUE if the fall was stopped by can_fall.
* FALSE if the fall was stopped by the fact that the atom
* was no longer on an open turf.
*
* @return The /mob/living that was hit. null if no mob was hit.
*/
/atom/movable/proc/fall_collateral(levels_fallen, stopped_early = FALSE, armor_penetration = 0)
// No gravity, stop falling into spess!
var/area/area = get_area(src)
if (istype(loc, /turf/space) || (area && !area.has_gravity()))
return null
var/list/fall_specs = fall_get_specs(levels_fallen)
var/weight = fall_specs[1]
var/fall_force = fall_specs[2]
if(weight >= 3 && fall_force <= 5) //Necessary because some big large obj's do not have a defined throw_force (mechs, lockers, pianos, etc)
fall_force = throw_range
var/speed = ((levels_fallen-1) + throw_speed) / THROWFORCE_SPEED_DIVISOR
var/mass = weight + density + opacity //1
var/momentum = speed * mass //8
if(weight <= 10) //Keeps damages sane.
momentum = momentum / THROWNOBJ_KNOCKBACK_DIVISOR
var/damage = round(fall_force * momentum) //64
var/miss_chance = max(10 * (levels_fallen), 0)
if (prob(miss_chance))
return null
if (damage < 1)
return null
var/mob/living/L = null
// Can't use locate due to the if check.
for (var/mob/living/ll in loc)
// in contents check exists for vehicles, mechas, etcetera.
if (ll != src && !(ll in contents))
L = ll
break
if (!L)
return null
if (ishuman(L))
var/mob/living/carbon/human/H = L
var/cranial_damage = rand(0,damage/2)
H.apply_damage(cranial_damage, DAMAGE_BRUTE, BP_HEAD, armor_pen = cranial_damage + armor_penetration)
var/new_damage = damage - cranial_damage
H.apply_damage(new_damage, DAMAGE_BRUTE, BP_CHEST, armor_pen = new_damage + armor_penetration)
if (damage >= THROWNOBJ_KNOCKBACK_DIVISOR)
H.Weaken(rand(damage / 4, damage / 2))
else
L.apply_damage(damage, DAMAGE_BRUTE)
L.visible_message(SPAN_DANGER("\The [L] had \the [src] fall onto [src.get_pronoun("him")]!"),
SPAN_DANGER("You had \the [src] fall onto you and strike you!"))
if(istype(src, /mob/living/heavy_vehicle))
var/mob/living/heavy_vehicle/HV = src
admin_attack_log((HV.pilots.len ? HV.pilots[1] : null), L, "fell onto", "was fallen on by", "fell ontop of")
else
admin_attack_log((ismob(src) ? src : null), L, "fell onto", "was fallen on by", "fell ontop of")
var/sound_to_play = pick(list('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg'))
playsound(L.loc, sound_to_play, 75, 1)
return L
/mob/fall_collateral(levels_fallen, stopped_early = FALSE)
. = ..()
if (.)
to_chat(src, SPAN_DANGER("You fell ontop of \the [.]!"))
/obj/fall_collateral(levels_fallen, stopped_early = FALSE, armor_penetration)
. = ..(levels_fallen, stopped_early, src.armor_penetration)
/**
* Helper proc for customizing which attributes should be used in fall damage
* calculations. Allows for greater control over the damage. (Drop pods, anyone?)
*
* @param levels_fallen How many Z-levels the atom has fallen before landing
* on its current loc.
*
* @return A two entity list: list(weight, fall_force)
*/
/atom/movable/proc/fall_get_specs(levels_fallen)
return list(1, throw_range)
/obj/fall_get_specs(levels_fallen)
return list(w_class, throwforce)
/mob/fall_get_specs(levels_fallen)
return list(mob_size, throw_range)
/mob/living
var/atom/movable/z_observer/z_eye
/atom/movable/z_observer
name = ""
simulated = FALSE
anchored = TRUE
mouse_opacity = FALSE
/// The mob that this observer belongs to.
var/mob/living/owner
/// The physical open-space turf we are watching.
var/turf/target_turf
/atom/movable/z_observer/Initialize(mapload, var/mob/living/user, var/turf/given_turf)
. = ..()
owner = user
target_turf = given_turf
follow()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(follow))
/atom/movable/z_observer/proc/follow()
/atom/movable/z_observer/z_up/follow()
forceMove(get_step(owner, UP))
if(isturf(src.loc))
var/turf/T = src.loc
if(T && TURF_IS_MIMICING(T))
return
owner.reset_view(null)
owner.z_eye = null
qdel(src)
/atom/movable/z_observer/z_down/follow()
var/turf/down_step = get_step(target_turf, DOWN)
/// If we move down more than 1 step, don't move down again.
if((GET_Z(owner) - down_step.z) < 2)
forceMove(down_step)
if(owner.Adjacent(target_turf) && (get_dir(owner, target_turf) & owner.dir))
return
owner.reset_view(null)
owner.z_eye = null
qdel(src)
/atom/movable/z_observer/Destroy()
UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
owner = null
target_turf = null
. = ..()
/atom/movable/z_observer/can_fall()
return FALSE
/atom/movable/z_observer/ex_act()
SHOULD_CALL_PARENT(FALSE)
return
/atom/movable/z_observer/singularity_act()
return
/atom/movable/z_observer/singularity_pull()
return
/atom/movable/z_observer/singuloCanEat()
return