Rewriting multiz movement to be gooder (#2464)

Intent: implement all of the features from #2442 while also unfucking the multiz movement files to be more readable and not recurse.

To that end, this PR does the following:

All multiz travel is now arbitrated by SSfalling. This will eliminate the need for recursion without relying on timers. Timers would be a bit scary.
The call chain for multiz movement now looks like this (in terms of overwritable procs):
can_fall() (Can lead into fall_impact() & fall_collateral() if returns FALSE)
fall_through() IF current block is open space ELSE fall_impact() & fall_collateral()
Removed almost every istype(src, A) check in the movement.dm file by exercising proper parenting and call chains.
Documented and standardized the contents of multiz/movement.dm. Because this is an API we're going to be relying on very heavily, I'd like to get this right before we move on.
A lot of minor tweaks, like swapping usr over to the applicable mob, and so on.
This commit is contained in:
skull132
2017-05-27 02:14:35 +03:00
committed by GitHub
parent e71299699e
commit 755ce490db
16 changed files with 598 additions and 235 deletions
+4 -1
View File
@@ -23,7 +23,7 @@
#define TIMER_NO_HASH_WAIT 0x10
//number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
#define TIMER_NO_INVOKE_WARNING 600
#define TIMER_NO_INVOKE_WARNING 600
// -- SSatoms stuff --
// SSatoms Initialization state.
@@ -60,3 +60,6 @@
// -- SSopenturf --
#define CHECK_OO_EXISTENCE(OO) if (OO && !istype(OO.loc, /turf/simulated/open)) { qdel(OO); }
#define UPDATE_OO_IF_PRESENT CHECK_OO_EXISTENCE(bound_overlay); if (bound_overlay) { update_oo(); }
// -- SSfalling --
#define ADD_FALLING_ATOM(atom) if (!atom.multiz_falling) { atom.multiz_falling = 1; SSfalling.falling[atom] = 0; }
+107
View File
@@ -0,0 +1,107 @@
#define REMOVE_AND_CONTINUE \
falling -= victim; \
victim.multiz_falling = 0; \
if (MC_TICK_CHECK) return; \
continue;
/var/datum/controller/subsystem/falling/SSfalling
/datum/controller/subsystem/falling
name = "Falling"
flags = SS_NO_INIT | SS_TICKER | SS_KEEP_TIMING
wait = 1
var/list/falling = list()
var/list/currentrun
/datum/controller/subsystem/falling/New()
NEW_SS_GLOBAL(SSfalling)
/datum/controller/subsystem/falling/stat_entry()
..("F:[falling.len]")
/datum/controller/subsystem/falling/fire(resumed = 0)
if (!resumed)
currentrun = falling.Copy()
var/list/curr = currentrun
while (curr.len)
var/atom/movable/victim = curr[curr.len]
curr.len--
if (QDELETED(victim))
falling -= victim
if (MC_TICK_CHECK)
return
continue
// The call_fall checks that are executed for every atom forever. These
// should not be overwritten/there shouldn't be a need to overwrite them.
// For specialty conditions, edit CanZPass and can_fall procs.
if (!isturf(victim.loc))
REMOVE_AND_CONTINUE
// Get the below turf.
var/turf/below = GetBelow(victim)
if (!below)
REMOVE_AND_CONTINUE
// Check if we can fall through the current tile and onto the next one.
if (!victim.loc:CanZPass(victim, DOWN) || !below.CanZPass(victim, DOWN))
REMOVE_AND_CONTINUE
// Check if the victim's current position is affected by gravity.
if (!victim.loc.loc:has_gravity())
REMOVE_AND_CONTINUE
// Thrown objects don't fall, generally speaking.
if (victim.throwing)
REMOVE_AND_CONTINUE
// can_fall check to see if we can fall. Current position is accessible
// via src.loc, destination by the param. So should be customizable enough.
if (!victim.can_fall(below))
// In case the stop is called in a situation when we're already falling.
// The still want to call fall_impact, due to the fact they've fallen.
if (falling[victim])
victim.fall_impact(falling[victim], TRUE)
victim.fall_collateral(falling[victim], TRUE)
REMOVE_AND_CONTINUE
// Iterate the falling counter. This is how many levels the mob has fallen
// thus far.
falling[victim]++
// Open turfs. Handle falling through them.
// Invokes fall_through() after the atom is moved to
// its new destination this cycle. Immediately invokes fall_impact and
// fall_collateral if the next turf is not open space.
if (istype(victim.loc, /turf/simulated/open))
victim.forceMove(below)
if (istype(victim.loc, /turf/simulated/open))
victim.fall_through()
else
// This is a lookahead. It removes any lag from being moved onto
// the destination turf, and calling fall_impact.
victim.fall_impact(falling[victim], FALSE)
victim.fall_collateral(falling[victim], FALSE)
victim.multiz_falling = 0
falling -= victim
if (MC_TICK_CHECK)
return
continue
// This shouldn't actually happen. But for safety, here it is.
victim.fall_impact(falling[victim], FALSE)
victim.fall_collateral(falling[victim], FALSE)
victim.multiz_falling = 0
falling -= victim
if (MC_TICK_CHECK)
return
#undef REMOVE_AND_CONTINUE
+2 -1
View File
@@ -11,6 +11,7 @@
max_temperature = 30000
infra_luminosity = 8
force = 40
w_class = 25
var/defence = 0
var/def_boost = 15
wreckage = /obj/effect/decal/mecha_wreckage/durand
@@ -72,4 +73,4 @@
..()
if (href_list["toggle_defence_mode"])
src.defence_mode()
return
return
+2 -1
View File
@@ -3,6 +3,7 @@
name = "Gygax"
icon_state = "gygax"
initial_icon = "gygax"
w_class = 15
step_in = 3
dir_in = 1 //Facing North.
health = 300
@@ -97,4 +98,4 @@
..()
if (href_list["toggle_leg_overload"])
src.overload()
return
return
+2
View File
@@ -20,6 +20,7 @@
add_req_access = 0
internal_damage_threshold = 25
force = 45
w_class = 35
max_equip = 4
/obj/mecha/combat/marauder/seraph
@@ -40,6 +41,7 @@
name = "Mauler"
icon_state = "mauler"
initial_icon = "mauler"
w_class = 40
operation_req_access = list(access_syndicate)
wreckage = /obj/effect/decal/mecha_wreckage/mauler
+6 -5
View File
@@ -19,6 +19,7 @@
name = "Mecha"
desc = "Exosuit"
icon = 'icons/mecha/mecha.dmi'
w_class = 20
density = 1 //Dense. To raise the heat.
opacity = 1 ///opaque. Menacing.
anchored = 1 //no pulling around.
@@ -117,7 +118,7 @@
loc.Entered(src)
mechas_list += src //global mech list
narrator_message(FIRSTRUN)
spark_system = bind_spark(src, 2)
/obj/mecha/Destroy()
@@ -666,7 +667,7 @@
if(istype(Proj, /obj/item/projectile/beam/pulse))
ignore_threshold = 1
src.hit_damage(Proj.damage, Proj.check_armour, is_melee=0)
if(prob(25))
if(prob(25))
spark_system.queue()
src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold)
@@ -757,7 +758,7 @@
//////////////////////
/obj/mecha/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/mecha_parts/mecha_equipment))
var/obj/item/mecha_parts/mecha_equipment/E = W
spawn()
@@ -1928,9 +1929,9 @@
return icon_state
/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(!damage)
return 0
+2 -1
View File
@@ -3,6 +3,7 @@
name = "Odysseus"
icon_state = "odysseus"
initial_icon = "odysseus"
w_class = 15
step_in = 2
max_temperature = 15000
health = 120
@@ -126,4 +127,4 @@
ME = new /obj/item/mecha_parts/mecha_equipment/tool/sleeper
ME.attach(src)
ME = new /obj/item/mecha_parts/mecha_equipment/tool/syringe_gun
ME.attach(src)
ME.attach(src)
+2 -1
View File
@@ -3,6 +3,7 @@
name = "Hover Pod"
icon_state = "engineering_pod"
initial_icon = "engineering_pod"
w_class = 10
internal_damage_threshold = 80
step_in = 4
step_energy_drain = 10
@@ -51,7 +52,7 @@
ion_trail.start()
if (stabilization_enabled)
return 1
return ..()
//these three procs overriden to play different sounds
+4 -1
View File
@@ -22,12 +22,15 @@ proc/log_and_message_admins_many(var/list/mob/users, var/message)
message_admins("[english_list(user_keys)] [message]")
proc/admin_attack_log(var/mob/attacker, var/mob/victim, var/attacker_message, var/victim_message, var/admin_message)
var/jmp_link = ""
if(victim)
victim.attack_log += text("\[[time_stamp()]\] <font color='orange'>[key_name(attacker)] - [victim_message]</font>")
jmp_link = " (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[victim.x];Y=[victim.y];Z=[victim.z]'>JMP</a>)"
if(attacker)
attacker.attack_log += text("\[[time_stamp()]\] <font color='red'>[key_name(victim)] - [attacker_message]</font>")
jmp_link = " (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[attacker.x];Y=[attacker.y];Z=[attacker.z]'>JMP</a>)"
msg_admin_attack("[key_name(attacker)] [admin_message] [key_name(victim)] (INTENT: [attacker? uppertext(attacker.a_intent) : "N/A"]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[attacker.x];Y=[attacker.y];Z=[attacker.z]'>JMP</a>)",ckey=key_name(attacker),ckey_target=key_name(victim))
msg_admin_attack("[key_name(attacker)] [admin_message] [key_name(victim)] (INTENT: [attacker? uppertext(attacker.a_intent) : "N/A"])[jmp_link]",ckey=key_name(attacker),ckey_target=key_name(victim))
proc/admin_attacker_log_many_victims(var/mob/attacker, var/list/mob/victims, var/attacker_message, var/victim_message, var/admin_message)
if(!victims || !victims.len)
+1
View File
@@ -3,6 +3,7 @@
icon = 'icons/obj/mining.dmi'
icon_state = "ore1"
w_class = 2
throwforce = 10
var/datum/geosample/geologic_data
var/material
@@ -46,7 +46,6 @@
var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks.
var/possession_candidate // Can be possessed by ghosts if unplayed.
var/z_levels_fallen = 0 //For z-level damage stacking.
var/list/stomach_contents //This is moved here from carbon defines
var/composition_reagent
+345 -125
View File
@@ -1,3 +1,10 @@
/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"
@@ -5,6 +12,9 @@
if(zMove(UP))
to_chat(usr, "<span class='notice'>You move upwards.</span>")
/**
* Verb for the mob to move down a z-level if possible.
*/
/mob/verb/down()
set name = "Move Down"
set category = "IC"
@@ -12,62 +22,117 @@
if(zMove(DOWN))
to_chat(usr, "<span class='notice'>You move down.</span>")
/**
* 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)
if(eyeobj)
// In the case of an active eyeobj, move that instead.
if (eyeobj)
return eyeobj.zMove(direction)
if(!can_ztravel())
to_chat(usr, "<span class='warning'>You lack means of travel in that direction.</span>")
return
// Check if we can actually travel a Z-level.
if (!can_ztravel())
to_chat(src, "<span class='warning'>You lack means of travel in that direction.</span>")
return FALSE
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(!destination)
to_chat(usr, "<span class='notice'>There is nothing of interest in this direction.</span>")
return 0
to_chat(src, "<span class='notice'>There is nothing of interest in this direction.</span>")
return FALSE
var/turf/start = get_turf(src)
if(!start.CanZPass(src, direction))
to_chat(usr, "<span class='warning'>\The [start] is in the way.</span>")
return 0
to_chat(src, "<span class='warning'>\The [start] is in the way.</span>")
return FALSE
if(!destination.CanZPass(src, direction))
to_chat(usr, "<span class='warning'>You bump against \the [destination].</span>")
return 0
to_chat(src, "<span class='warning'>You bump against \the [destination].</span>")
return FALSE
var/area/area = get_area(src)
if(direction == UP && area.has_gravity && !usr.CanAvoidGravity())
to_chat(usr, "<span class='warning'>Gravity stops you from moving upward.</span>")
return 0
for(var/atom/A in destination)
if(!A.CanPass(src, start, 1.5, 0))
to_chat(usr, "<span class='warning'>\The [A] blocks you.</span>")
return 0
// 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 class='warning'>Gravity stops you from moving upward.</span>")
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 class='warning'>\The [A] blocks you.</span>")
return FALSE
// Actually move.
Move(destination)
return 1
return TRUE
/mob/eye/zMove(direction)
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(destination)
setLoc(destination)
else
to_chat(usr, "<span class='notice'>There is nothing of interest in this direction.</span>")
to_chat(owner, "<span class='notice'>There is nothing of interest in this direction.</span>")
/mob/dead/observer/zMove(direction)
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(destination)
forceMove(destination)
else
to_chat(usr, "<span class='notice'>There is nothing of interest in this direction.</span>")
to_chat(src, "<span class='notice'>There is nothing of interest in this direction.</span>")
/mob/living/carbon/human/bst/zMove(direction)
var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
if(destination)
forceMove(destination)
else
to_chat(usr, "<span class='notice'>There is nothing of interest in this direction.</span>")
to_chat(src, "<span class='notice'>There is nothing of interest in this direction.</span>")
/**
* @brief Used to determine whether or not a given mob can override gravity when
* 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()
return FALSE
/mob/dead/observer/can_ztravel()
return TRUE
/mob/living/carbon/human/can_ztravel()
if(incapacitated())
return FALSE
if(Allow_Spacemove())
return TRUE
if(Check_Shoegrip()) //scaling hull with magboots
for(var/turf/simulated/T in trange(1,src))
if(T.density)
return TRUE
/mob/living/silicon/robot/can_ztravel()
if(incapacitated() || is_dead())
return FALSE
if(Allow_Spacemove()) //Checks for active jetpack
return TRUE
for(var/turf/simulated/T in trange(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.
@@ -78,6 +143,8 @@
/mob/proc/CanAvoidGravity()
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/weapon/tank/jetpack/thrust = GetJetpack(src)
@@ -95,75 +162,36 @@
return ..()
/mob/proc/can_ztravel()
return 0
/mob/dead/observer/can_ztravel()
return 1
/mob/living/carbon/human/can_ztravel()
if(incapacitated())
return 0
if(Allow_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(Allow_Spacemove()) //Checks for active jetpack
return 1
for(var/turf/simulated/T in trange(1,src)) //Robots get "magboots"
if(T.density)
return 1
//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())
handle_fall(below)
//For children to override
/atom/movable/proc/can_fall()
/**
* 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.
*
* @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)
// Anchored things don't fall.
if(anchored)
return FALSE
if(locate(/obj/structure/lattice, loc))
// Lattices and stairs stop things from falling.
if(locate(/obj/structure/lattice, loc) || locate(/obj/structure/stairs, loc))
return FALSE
// See if something prevents us from falling.
var/turf/below = GetBelow(src)
for(var/atom/A in below)
if(!A.CanPass(src, src.loc))
return FALSE
// True otherwise.
return TRUE
/obj/effect/can_fall()
@@ -172,18 +200,23 @@
/obj/effect/decal/cleanable/can_fall()
return TRUE
/obj/item/pipe/can_fall()
var/turf/simulated/open/below = loc
below = below.below
/obj/item/pipe/can_fall(turf/below)
. = ..()
if(anchored)
return FALSE
if((locate(/obj/structure/disposalpipe/up) in below) || locate(/obj/machinery/atmospherics/pipe/zpipe/up in below))
return FALSE
// Only things that stop mechas are atoms that, well, stop them.
// Lattices and stairs get crushed in fall_through.
/obj/mecha/can_fall(turf/below)
// See if something prevents us from falling.
for(var/atom/A in below)
if(!A.CanPass(src, src.loc))
return FALSE
// True otherwise.
return TRUE
/mob/living/carbon/human/can_fall()
// Special condition for jetpack mounted folk!
if (!restrained())
@@ -195,6 +228,9 @@
return ..()
/mob/living/carbon/human/bst/can_fall()
return FALSE
/mob/living/silicon/robot/can_fall()
var/obj/item/weapon/tank/jetpack/thrust = GetJetpack(src)
@@ -203,46 +239,95 @@
return ..()
/atom/movable/proc/handle_fall(var/turf/landing)
Move(landing)
if(locate(/obj/structure/stairs) in landing)
return 1
/**
* 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.")
if(istype(landing, /turf/simulated/open))
visible_message("\The [src] falls from the level above through \the [landing]!", "You hear a whoosh of displaced air.")
else if(!istype(landing, /turf/space))
visible_message("\The [src] falls from the level above and slams onto \the [landing]!", "You hear something slam onto the floor.")
/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/handle_fall(var/turf/landing)
if(..())
return 1
/obj/mecha/fall_through()
var/obj/structure/lattice/L = locate() in loc
if (L)
visible_message("<span class='danger'>\The [src] crushes \the [L] with its weight!</span>")
qdel(L)
var/area/area1 = get_area(landing)
if(!area1.has_gravity())
return 1
var/obj/structure/stairs/S = locate() in loc
if (S)
visible_message("<span class='danger'>\The [src] crushes \the [S] with its weight!</span>")
qdel(S)
if(istype(landing, /turf/simulated/open))
z_levels_fallen++ // Don't damage them but keep track of this.
return 1
/**
* 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)
// No gravity, stop falling into spess!
var/area/area = get_area(src)
if (istype(loc, /turf/space) || (area && !area.has_gravity))
return FALSE
if(istype(landing, /turf/space))
z_levels_fallen = 0 // turns out they didn't hit anything solid. Lucky them.
return 1
visible_message("\The [src] falls and lands on \the [loc]!", "You hear a thud!")
if(!z_levels_fallen) // Checks if they only fell down one floor.
z_levels_fallen = 1
return TRUE
if(!istype(src, /mob/living/carbon/human))
var/damage = 30*z_levels_fallen
apply_damage(rand(0, damage), BRUTE)
apply_damage(rand(0, damage), BRUTE)
apply_damage(rand(0, damage), BRUTE)
z_levels_fallen = 0
// Mobs take damage if they fall!
/mob/living/fall_impact(levels_fallen, stopped_early = FALSE)
// No gravity, stop falling into spess!
var/area/area = get_area(src)
if (istype(loc, /turf/space) || (area && !area.has_gravity))
return FALSE
/mob/living/carbon/human/handle_fall(var/turf/landing)
if(..())
return
var/damage = 30*species.fall_mod*z_levels_fallen
visible_message("\The [src] falls and lands on \the [loc]!",
"With a loud thud, you land on \the [loc]!", "You hear a thud!")
var/damage = 30 * levels_fallen
apply_damage(rand(0, damage), BRUTE)
apply_damage(rand(0, damage), BRUTE)
apply_damage(rand(0, 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)
playsound(src.loc, "sound/weapons/punch[rand(1, 4)].ogg", 75, 1)
if(51 to INFINITY)
playsound(src.loc, "sound/weapons/heavysmash.ogg", 100, 1)
else
playsound(src.loc, "sound/weapons/genhit1.ogg", 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)
// 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]!",
"With a loud thud, you land on \the [loc]!", "You hear a thud!")
var/damage = 25 * species.fall_mod * levels_fallen
if(prob(20)) //landed on their head
apply_damage(rand(0, damage), BRUTE, "head")
@@ -265,13 +350,148 @@
apply_damage(rand(0, (damage/2)), BRUTE, "l_foot")
if(prob(50))
apply_damage(rand(0, (damage/2)), BRUTE, "groin")
apply_damage(rand(0, damage), BRUTE, "chest")
apply_damage(rand(0, damage), BRUTE, "chest")
Weaken(rand(0, damage/2))
z_levels_fallen = 0 // reset their fallen variable.
updatehealth()
/mob/living/carbon/human/bst/can_fall()
// 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, "sound/weapons/punch[rand(1, 4)].ogg", 75, 1)
if(51 to INFINITY)
playsound(src.loc, "sound/weapons/heavysmash.ogg", 100, 1)
else
playsound(src.loc, "sound/weapons/genhit1.ogg", 75, 1)
else
playsound(src.loc, "sound/weapons/smash.ogg", 75, 1)
return TRUE
/mob/living/carbon/human/bst/fall_impact()
return FALSE
/mob/living/carbon/human/bst/handle_fall(turf/landing)
return
/obj/mecha/fall_impact(levels_fallen, stopped_early = FALSE)
. = ..()
if (!.)
return
var/damage = 40 * levels_fallen
take_damage(rand(0, damage))
take_damage(rand(0, damage))
take_damage(rand(0, damage))
take_damage(rand(0, damage))
playsound(loc, "sound/effects/bang.ogg", 100, 1)
playsound(loc, "sound/effects/bamf.ogg", 100, 1)
/obj/vehicle/fall_impact(levels_fallen, stopped_early = FALSE)
. = ..()
if (!.)
return
var/damage = 35 * levels_fallen
health -= (rand(0, damage) * brute_dam_coeff)
health -= (rand(0, damage) * brute_dam_coeff)
health -= (rand(0, damage) * brute_dam_coeff)
health -= (rand(0, 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)
// 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]
var/speed = levels_fallen * throw_speed
var/mass = (weight / THROWNOBJ_KNOCKBACK_DIVISOR) + density + opacity
var/momentum = speed * mass
var/damage = round(fall_force * (speed / THROWNOBJ_KNOCKBACK_DIVISOR) * momentum)
var/miss_chance = max(15 * (levels_fallen - 1), 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
H.apply_damage(rand(damage / 2, damage), BRUTE, "head")
H.apply_damage(damage, BRUTE, "chest")
if (damage >= THROWNOBJ_KNOCKBACK_DIVISOR)
H.Weaken(rand(damage / 4, damage / 2))
else
L.apply_damage(damage, BRUTE)
L.apply_damage(rand(damage / 2, damage), BRUTE)
L.visible_message("<span class='danger'>\The [L] had \the [src] fall onto \him!</span>",
"<span class='danger'>You had \the [src] fall onto you and strike you!</span>")
admin_attack_log((ismob(src) ? src : null), L, "fell onto", "was fallen on by", "fell ontop of")
playsound(L.loc, "sound/waepons/genhit[rand(1, 3)].ogg", 75, 1)
return L
/mob/fall_collateral(levels_fallen, stopped_early = FALSE)
. = ..()
if (.)
to_chat(src, "<span class='danger'>You fell ontop of \the [.]!</span>")
/**
* 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)
+44 -44
View File
@@ -1,29 +1,29 @@
////////////////////////////
// parent class for pipes //
////////////////////////////
obj/machinery/atmospherics/pipe/zpipe
icon = 'icons/obj/structures.dmi'
icon_state = "up"
/obj/machinery/atmospherics/pipe/zpipe
icon = 'icons/obj/structures.dmi'
icon_state = "up"
name = "upwards pipe"
desc = "A pipe segment to connect upwards."
name = "upwards pipe"
desc = "A pipe segment to connect upwards."
volume = 70
volume = 70
dir = SOUTH
initialize_directions = SOUTH
dir = SOUTH
initialize_directions = SOUTH
var/minimum_temperature_difference = 300
var/thermal_conductivity = 0 //WALL_HEAT_TRANSFER_COEFFICIENT No
var/minimum_temperature_difference = 300
var/thermal_conductivity = 0 //WALL_HEAT_TRANSFER_COEFFICIENT No
var/maximum_pressure = 70*ONE_ATMOSPHERE
var/fatigue_pressure = 55*ONE_ATMOSPHERE
alert_pressure = 55*ONE_ATMOSPHERE
var/maximum_pressure = 70*ONE_ATMOSPHERE
var/fatigue_pressure = 55*ONE_ATMOSPHERE
alert_pressure = 55*ONE_ATMOSPHERE
level = 1
level = 1
obj/machinery/atmospherics/pipe/zpipe/New()
/obj/machinery/atmospherics/pipe/zpipe/New()
..()
switch(dir)
if(SOUTH)
@@ -48,13 +48,13 @@ obj/machinery/atmospherics/pipe/zpipe/New()
invisibility = i ? 101 : 0
update_icon()
obj/machinery/atmospherics/pipe/up/process()
/obj/machinery/atmospherics/pipe/up/process()
if(!parent) //This should cut back on the overhead calling build_network thousands of times per cycle
..()
else
. = PROCESS_KILL
obj/machinery/atmospherics/pipe/zpipe/check_pressure(pressure)
/obj/machinery/atmospherics/pipe/zpipe/check_pressure(pressure)
var/datum/gas_mixture/environment = loc.return_air()
var/pressure_difference = pressure - environment.return_pressure()
@@ -69,34 +69,34 @@ obj/machinery/atmospherics/pipe/zpipe/check_pressure(pressure)
else return 1
obj/machinery/atmospherics/pipe/zpipe/proc/burst()
/obj/machinery/atmospherics/pipe/zpipe/proc/burst()
src.visible_message("<span class='warning'>\The [src] bursts!</span>");
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)
var/datum/effect/effect/system/smoke_spread/smoke = new
smoke.set_up(1,0, src.loc, 0)
smoke.start()
qdel(src) // NOT qdel.
qdel(src) // Yes QDel.
obj/machinery/atmospherics/pipe/zpipe/proc/normalize_dir()
/obj/machinery/atmospherics/pipe/zpipe/proc/normalize_dir()
if(dir==3)
set_dir(1)
else if(dir==12)
set_dir(4)
obj/machinery/atmospherics/pipe/zpipe/Destroy()
/obj/machinery/atmospherics/pipe/zpipe/Destroy()
if(node1)
node1.disconnect(src)
if(node2)
node2.disconnect(src)
return ..()
obj/machinery/atmospherics/pipe/zpipe/pipeline_expansion()
/obj/machinery/atmospherics/pipe/zpipe/pipeline_expansion()
return list(node1, node2)
obj/machinery/atmospherics/pipe/zpipe/update_icon()
/obj/machinery/atmospherics/pipe/zpipe/update_icon()
return
obj/machinery/atmospherics/pipe/zpipe/disconnect(obj/machinery/atmospherics/reference)
/obj/machinery/atmospherics/pipe/zpipe/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
if(istype(node1, /obj/machinery/atmospherics/pipe))
qdel(parent)
@@ -111,14 +111,14 @@ obj/machinery/atmospherics/pipe/zpipe/disconnect(obj/machinery/atmospherics/refe
/////////////////////////
// the elusive up pipe //
/////////////////////////
obj/machinery/atmospherics/pipe/zpipe/up
icon = 'icons/obj/structures.dmi'
icon_state = "up"
/obj/machinery/atmospherics/pipe/zpipe/up
icon = 'icons/obj/structures.dmi'
icon_state = "up"
name = "upwards pipe"
desc = "A pipe segment to connect upwards."
name = "upwards pipe"
desc = "A pipe segment to connect upwards."
obj/machinery/atmospherics/pipe/zpipe/up/initialize()
/obj/machinery/atmospherics/pipe/zpipe/up/initialize()
normalize_dir()
var/node1_dir
@@ -149,14 +149,14 @@ obj/machinery/atmospherics/pipe/zpipe/up/initialize()
// and the down pipe //
///////////////////////
obj/machinery/atmospherics/pipe/zpipe/down
icon = 'icons/obj/structures.dmi'
icon_state = "down"
/obj/machinery/atmospherics/pipe/zpipe/down
icon = 'icons/obj/structures.dmi'
icon_state = "down"
name = "downwards pipe"
desc = "A pipe segment to connect downwards."
name = "downwards pipe"
desc = "A pipe segment to connect downwards."
obj/machinery/atmospherics/pipe/zpipe/down/initialize()
/obj/machinery/atmospherics/pipe/zpipe/down/initialize()
normalize_dir()
var/node1_dir
@@ -187,7 +187,7 @@ obj/machinery/atmospherics/pipe/zpipe/down/initialize()
// supply/scrubbers //
///////////////////////
obj/machinery/atmospherics/pipe/zpipe/up/scrubbers
/obj/machinery/atmospherics/pipe/zpipe/up/scrubbers
icon_state = "up-scrubbers"
name = "upwards scrubbers pipe"
desc = "A scrubbers pipe segment to connect upwards."
@@ -195,7 +195,7 @@ obj/machinery/atmospherics/pipe/zpipe/up/scrubbers
icon_connect_type = "-scrubbers"
color = PIPE_COLOR_RED
obj/machinery/atmospherics/pipe/zpipe/up/supply
/obj/machinery/atmospherics/pipe/zpipe/up/supply
icon_state = "up-supply"
name = "upwards supply pipe"
desc = "A supply pipe segment to connect upwards."
@@ -203,7 +203,7 @@ obj/machinery/atmospherics/pipe/zpipe/up/supply
icon_connect_type = "-supply"
color = PIPE_COLOR_BLUE
obj/machinery/atmospherics/pipe/zpipe/down/scrubbers
/obj/machinery/atmospherics/pipe/zpipe/down/scrubbers
icon_state = "down-scrubbers"
name = "downwards scrubbers pipe"
desc = "A scrubbers pipe segment to connect downwards."
@@ -211,7 +211,7 @@ obj/machinery/atmospherics/pipe/zpipe/down/scrubbers
icon_connect_type = "-scrubbers"
color = PIPE_COLOR_RED
obj/machinery/atmospherics/pipe/zpipe/down/supply
/obj/machinery/atmospherics/pipe/zpipe/down/supply
icon_state = "down-supply"
name = "downwards supply pipe"
desc = "A supply pipe segment to connect downwards."
@@ -220,12 +220,12 @@ obj/machinery/atmospherics/pipe/zpipe/down/supply
color = PIPE_COLOR_BLUE
// Colored misc. pipes
obj/machinery/atmospherics/pipe/zpipe/up/cyan
/obj/machinery/atmospherics/pipe/zpipe/up/cyan
color = PIPE_COLOR_CYAN
obj/machinery/atmospherics/pipe/zpipe/down/cyan
/obj/machinery/atmospherics/pipe/zpipe/down/cyan
color = PIPE_COLOR_CYAN
obj/machinery/atmospherics/pipe/zpipe/up/red
/obj/machinery/atmospherics/pipe/zpipe/up/red
color = PIPE_COLOR_RED
obj/machinery/atmospherics/pipe/zpipe/down/red
/obj/machinery/atmospherics/pipe/zpipe/down/red
color = PIPE_COLOR_RED
+38 -38
View File
@@ -131,47 +131,47 @@
opacity = 0
anchored = 1
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/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)
Uncross(atom/movable/A)
if(A.dir == dir)
// This is hackish but whatever.
var/turf/target = get_step(GetAbove(A), dir)
var/turf/source = A.loc
if(target.Enter(A, source))
A.loc = target
target.Entered(A, source)
return 0
return 1
/obj/structure/stairs/Uncross(atom/movable/A)
if(A.dir == dir)
// This is hackish but whatever.
var/turf/target = get_step(GetAbove(A), dir)
var/turf/source = A.loc
if(target.Enter(A, source))
A.loc = target
target.Entered(A, source)
return FALSE
return TRUE
CanPass(obj/mover, turf/source, height, airflow)
return airflow || !density
/obj/structure/stairs/CanPass(obj/mover, turf/source, height, airflow)
return airflow || !density
// type paths to make mapping easier.
north
dir = NORTH
bound_height = 64
bound_y = -32
pixel_y = -32
// type paths to make mapping easier.
/obj/structure/stairs/north
dir = NORTH
bound_height = 64
bound_y = -32
pixel_y = -32
south
dir = SOUTH
bound_height = 64
/obj/structure/stairs/south
dir = SOUTH
bound_height = 64
east
dir = EAST
bound_width = 64
bound_x = -32
pixel_x = -32
/obj/structure/stairs/east
dir = EAST
bound_width = 64
bound_x = -32
pixel_x = -32
west
dir = WEST
bound_width = 64
/obj/structure/stairs/west
dir = WEST
bound_width = 64
+38 -16
View File
@@ -1,18 +1,32 @@
/**
* Used to check wether or not an atom can pass through a turf.
*
* @param A The atom that's moving either up or down from this turf or to it.
* @param direction The direction of the atom's movement in relation to its
* current position.
*
* @return TRUE if A can pass in the movement direction, FALSE if not.
*/
/turf/proc/CanZPass(atom/A, direction)
if(z == A.z) //moving FROM this turf
return direction == UP //can't go below
else
if(direction == UP) //on a turf below, trying to enter
return 0
return FALSE
if(direction == DOWN) //on a turf above, trying to enter
return !density
/turf/simulated/open/CanZPass(atom, direction)
return 1
return TRUE
/turf/space/CanZPass(atom, direction)
return 1
return TRUE
/**
* Open turf class.
*
* All atoms are able to pass through this, and also to see under it.
*/
/turf/simulated/open
name = "open space"
icon = 'icons/turf/space.dmi'
@@ -39,15 +53,6 @@
return ..()
/turf/simulated/open/proc/is_above_space()
var/turf/T = GetBelow(src)
while (T && T.is_hole)
if (istype(T, /turf/space))
return TRUE
T = GetBelow(T)
return FALSE
/turf/simulated/open/Destroy()
SSopenturf.openspace_turfs -= src
SSopenturf.queued_turfs -= src
@@ -63,6 +68,20 @@
below = null
return ..()
/**
* Used to check wether or not the specific open turf eventually leads into spess.
*
* @return TRUE if the turfs/holes eventually lead into space. FALSE otherwise.
*/
/turf/simulated/open/proc/is_above_space()
var/turf/T = GetBelow(src)
while (T && T.is_hole)
if (istype(T, /turf/space))
return TRUE
T = GetBelow(T)
return FALSE
/turf/simulated/open/airless
oxygen = 0
nitrogen = 0
@@ -77,12 +96,15 @@
SSopenturf.openspace_turfs += src
update()
/**
* Updates the turf with open turf's variables and basically resets it properly.
*/
/turf/simulated/open/proc/update()
below = GetBelow(src)
below.above = src
levelupdate()
for(var/atom/movable/A in src)
A.fall()
for (var/atom/movable/A in src)
ADD_FALLING_ATOM(A)
update_icon()
/turf/simulated/open/update_dirt()
@@ -90,7 +112,7 @@
/turf/simulated/open/Entered(atom/movable/mover)
..()
mover.fall()
ADD_FALLING_ATOM(mover)
update_icon()
// override to make sure nothing is hidden
@@ -142,4 +164,4 @@
//Most things use is_plating to test if there is a cover tile on top (like regular floors)
/turf/simulated/open/is_plating()
return 1
return TRUE