diff --git a/code/__DEFINES/dcs/signals/signals_movable.dm b/code/__DEFINES/dcs/signals/signals_movable.dm
index 5b3ae553ddadc..6bcfbde38a980 100644
--- a/code/__DEFINES/dcs/signals/signals_movable.dm
+++ b/code/__DEFINES/dcs/signals/signals_movable.dm
@@ -69,8 +69,8 @@
// /datum/element/movetype_handler signals
/// Called when the floating anim has to be temporarily stopped and restarted later: (timer)
#define COMSIG_PAUSE_FLOATING_ANIM "pause_floating_anim"
-/// From base of datum/element/movetype_handler/on_movement_type_trait_gain: (flag)
+/// From base of datum/element/movetype_handler/on_movement_type_trait_gain: (flag, old_movement_type)
#define COMSIG_MOVETYPE_FLAG_ENABLED "movetype_flag_enabled"
-/// From base of datum/element/movetype_handler/on_movement_type_trait_loss: (flag)
+/// From base of datum/element/movetype_handler/on_movement_type_trait_loss: (flag, old_movement_type)
#define COMSIG_MOVETYPE_FLAG_DISABLED "movetype_flag_disabled"
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 342b99743fd63..04e7d943ddb6e 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -141,6 +141,10 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define FLOATING (1<<3)
#define PHASING (1<<4) //! When moving, will Bump()/Cross() everything, but won't be stopped.
#define THROWN (1<<5) //! while an atom is being thrown
+#define UPSIDE_DOWN (1<<6) /// The mob is walking on the ceiling. Or is generally just, upside down.
+
+/// Combination flag for movetypes which, for all intents and purposes, mean the mob is not touching the ground
+#define MOVETYPES_NOT_TOUCHING_GROUND (FLYING|FLOATING|UPSIDE_DOWN)
//! ## Fire and Acid stuff, for resistance_flags
#define LAVA_PROOF (1<<0)
diff --git a/code/__DEFINES/gravity.dm b/code/__DEFINES/gravity.dm
index eba2af77e122a..a638b22be3dd6 100644
--- a/code/__DEFINES/gravity.dm
+++ b/code/__DEFINES/gravity.dm
@@ -12,7 +12,18 @@
/// Singularity is stage 6 (11x11)
#define STAGE_SIX 11 //! From supermatter shard
-/// Anything above this is high gravity, anything below no grav until negative gravity
-#define STANDARD_GRAVITY 1
+/**
+ * The point where gravity is negative enough to pull you upwards.
+ * That means walking checks for a ceiling instead of a floor, and you can fall "upwards"
+ *
+ * This should only be possible on multi-z maps because it works like shit on maps that aren't.
+ */
+#define NEGATIVE_GRAVITY -1
+
+#define STANDARD_GRAVITY 1 //Anything above this is high gravity, anything below no grav until negative gravity
/// The gravity strength threshold for high gravity damage.
-#define GRAVITY_DAMAGE_TRESHOLD 3
+#define GRAVITY_DAMAGE_THRESHOLD 3
+/// The scaling factor for high gravity damage.
+#define GRAVITY_DAMAGE_SCALING 0.5
+/// The maximum [BRUTE] damage a mob can take from high gravity per second.
+#define GRAVITY_DAMAGE_MAXIMUM 1.5
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 7385f71578e02..dd6121db10c3d 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -317,4 +317,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// The person with this trait always appears as 'unknown'.
#define TRAIT_UNKNOWN "unknown"
+/// We are ignoring gravity
+#define TRAIT_IGNORING_GRAVITY "ignores_gravity"
+/// We have some form of forced gravity acting on us
+#define TRAIT_FORCED_GRAVITY "forced_gravity"
+#define TRAIT_MOVE_UPSIDE_DOWN "move_upside_down"
+#define TRAIT_NEGATES_GRAVITY "negates_gravity"
+
// END TRAIT DEFINES
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index fea059205dc72..d52b459750be9 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -133,3 +133,6 @@
#define NO_GRAVITY_TRAIT "no-gravity"
#define LIFECANDLE_TRAIT "lifecandle"
#define LEAPER_BUBBLE_TRAIT "leaper-bubble"
+#define NEGATIVE_GRAVITY_TRAIT "negative-gravity"
+/// Sources for TRAIT_IGNORING_GRAVITY
+#define IGNORING_GRAVITY_NEGATION "ignoring_gravity_negation"
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 2d121bb1829d5..8d75b40d3dd9d 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -153,7 +153,8 @@ DEFINE_BITFIELD(movement_type, list(
"FLYING" = FLYING,
"VENTCRAWLING" = VENTCRAWLING,
"FLOATING" = FLOATING,
- "PHASING" = PHASING
+ "PHASING" = PHASING,
+ "UPSIDE_DOWN" = UPSIDE_DOWN,
))
DEFINE_BITFIELD(mat_container_flags, list(
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index bb40b3836fbf8..aab4c39b3d4b4 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -178,7 +178,11 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NORADDAMAGE" = TRAIT_NORADDAMAGE,
"TRAIT_MOBILE" = TRAIT_MOBILE,
"INSTANT_DO_AFTER" = INSTANT_DO_AFTER,
- "TRAIT_UNKNOWN" = TRAIT_UNKNOWN
+ "TRAIT_UNKNOWN" = TRAIT_UNKNOWN,
+ "TRAIT_IGNORING_GRAVITY" = TRAIT_IGNORING_GRAVITY,
+ "TRAIT_FORCED_GRAVITY" = TRAIT_FORCED_GRAVITY,
+ "TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN,
+ "TRAIT_NEGATES_GRAVITY" = TRAIT_NEGATES_GRAVITY
),
/obj/item/integrated_circuit = list(
"TRAIT_COMPONENT_MMI" = TRAIT_COMPONENT_MMI,
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index dd5f5e8722411..51325b3bab772 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -248,6 +248,11 @@ If you're feeling frisky, examine yourself and click the underlined item to pull
var/mob/living/carbon/M = usr
return M.help_shake_act(M)
+/atom/movable/screen/alert/negative
+ name = "Negative Gravity"
+ desc = "You're getting pulled upwards. While you won't have to worry about falling down anymore, you may accidentally fall upwards!"
+ icon_state = "negative"
+
/atom/movable/screen/alert/weightless
name = "Weightless"
desc = "Gravity has ceased affecting you, and you're floating around aimlessly. You'll need something large and heavy, like a \
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index add3bf18afaa8..d2d8fba411651 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -55,6 +55,10 @@ SUBSYSTEM_DEF(mapping)
var/datum/space_level/empty_space
var/num_of_res_levels = 1
+ ///shows the default gravity value for each z level. recalculated when gravity generators change.
+ ///List in the form: list(z level num = max generator gravity in that z level OR the gravity level trait)
+ var/list/gravity_by_z_level = list()
+
/datum/controller/subsystem/mapping/PreInit()
..()
#ifdef FORCE_MAP
@@ -126,6 +130,7 @@ SUBSYSTEM_DEF(mapping)
generate_station_area_list()
transit = add_new_zlevel("Transit/Reserved", list(ZTRAIT_RESERVED = TRUE))
initialize_reserved_level(transit.z_value)
+ calculate_default_z_level_gravities()
return SS_INIT_SUCCESS
/datum/controller/subsystem/mapping/fire(resumed)
@@ -617,6 +622,9 @@ GLOBAL_LIST_EMPTY(the_station_areas)
/// - Adds to z_list, and builds its area turfs
/datum/controller/subsystem/mapping/proc/manage_z_level(datum/space_level/new_z, filled_with_space, contain_turfs = TRUE)
z_list += new_z
+
+ gravity_by_z_level.len += 1
+
if(contain_turfs)
build_area_turfs(new_z.z_value, filled_with_space)
@@ -632,6 +640,10 @@ GLOBAL_LIST_EMPTY(the_station_areas)
var/area/our_area = to_contain.loc
our_area.contained_turfs += to_contain
+/datum/controller/subsystem/mapping/proc/calculate_default_z_level_gravities()
+ for(var/z_level in 1 to length(z_list))
+ calculate_z_level_gravity(z_level)
+
/datum/controller/subsystem/mapping/proc/generate_z_level_linkages()
for(var/z_level in 1 to length(z_list))
generate_linkages_for_z_level(z_level)
@@ -650,3 +662,16 @@ GLOBAL_LIST_EMPTY(the_station_areas)
multiz_levels[z_level] = new /list(LARGEST_Z_LEVEL_INDEX)
multiz_levels[z_level][Z_LEVEL_UP] = !!z_above
multiz_levels[z_level][Z_LEVEL_DOWN] = !!z_below
+
+/datum/controller/subsystem/mapping/proc/calculate_z_level_gravity(z_level_number)
+ if(!isnum(z_level_number) || z_level_number < 1)
+ return FALSE
+
+ var/max_gravity = 0
+
+ for(var/obj/machinery/gravity_generator/main/grav_gen as anything in GLOB.gravity_generators["[z_level_number]"])
+ max_gravity = max(grav_gen.setting, max_gravity)
+
+ max_gravity = max_gravity || level_trait(z_level_number, ZTRAIT_GRAVITY) || 0 //just to make sure no nulls
+ gravity_by_z_level[z_level_number] = max_gravity
+ return max_gravity
diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm
index 67e76e0592e17..ff2c7611bc549 100644
--- a/code/datums/components/caltrop.dm
+++ b/code/datums/components/caltrop.dm
@@ -55,7 +55,7 @@
if(!(flags & CALTROP_BYPASS_SHOES) && (H.shoes || feetCover))
return
- if((H.movement_type & (FLYING|FLOATING)) || (H.body_position == LYING_DOWN)|| H.buckled)
+ if((H.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || (H.body_position == LYING_DOWN)|| H.buckled)
return
var/damage = rand(min_damage, max_damage)
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
index c439bcee66ce0..3db67bab6209a 100644
--- a/code/datums/components/chasm.dm
+++ b/code/datums/components/chasm.dm
@@ -69,7 +69,7 @@
return FALSE
if(!isliving(AM) && !isobj(AM))
return FALSE
- if(is_type_in_typecache(AM, forbidden_types) || AM.throwing || (AM.movement_type & (FLOATING|FLYING)))
+ if(is_type_in_typecache(AM, forbidden_types) || AM.throwing || (AM.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return FALSE
//Flies right over the chasm
if(ismob(AM))
diff --git a/code/datums/components/conveyor_movement.dm b/code/datums/components/conveyor_movement.dm
index 43fb9979a2cc9..6ec3f30830731 100644
--- a/code/datums/components/conveyor_movement.dm
+++ b/code/datums/components/conveyor_movement.dm
@@ -24,7 +24,7 @@
source.delay = speed //We use the default delay
if(living_parent)
var/mob/living/moving_mob = parent
- if((moving_mob.movement_type & (FLOATING|FLYING)) && !moving_mob.stat)
+ if((moving_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !moving_mob.stat)
return MOVELOOP_SKIP_STEP
var/atom/movable/moving_parent = parent
if(moving_parent.anchored || !moving_parent.has_gravity())
diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm
index 3b244d5e16f8a..39907f144d6d0 100644
--- a/code/datums/components/slippery.dm
+++ b/code/datums/components/slippery.dm
@@ -34,7 +34,7 @@
if(!isliving(arrived))
return
var/mob/living/victim = arrived
- if(!(victim.movement_type & (FLOATING|FLYING)) && victim.slip(knockdown_time, parent, lube_flags, paralyze_time, force_drop_items) && callback)
+ if(!(victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && victim.slip(knockdown_time, parent, lube_flags, paralyze_time, force_drop_items) && callback)
callback.Invoke(victim)
/datum/component/slippery/UnregisterFromParent()
diff --git a/code/datums/components/spikes.dm b/code/datums/components/spikes.dm
index be078a73721c7..a2a2daac40a68 100644
--- a/code/datums/components/spikes.dm
+++ b/code/datums/components/spikes.dm
@@ -58,7 +58,7 @@
if(ishuman(C))
var/mob/living/carbon/human/H = C
var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET))
- if((H.movement_type & (FLOATING|FLYING)) || H.body_position == LYING_DOWN || H.buckled || H.shoes || feetCover)
+ if((H.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || H.body_position == LYING_DOWN || H.buckled || H.shoes || feetCover)
prick(H, 0.5)
else
prick(H, 2)
diff --git a/code/datums/components/squashable.dm b/code/datums/components/squashable.dm
index f9159614909fd..9091ef34e981d 100644
--- a/code/datums/components/squashable.dm
+++ b/code/datums/components/squashable.dm
@@ -53,7 +53,7 @@
if(isliving(crossing_movable))
var/mob/living/crossing_mob = crossing_movable
- if(crossing_mob.mob_size > MOB_SIZE_SMALL && !(crossing_mob.movement_type & (FLOATING|FLYING)))
+ if(crossing_mob.mob_size > MOB_SIZE_SMALL && !(crossing_mob.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
if(HAS_TRAIT(crossing_mob, TRAIT_PACIFISM))
crossing_mob.visible_message("[crossing_mob] carefully steps over [parent_as_living].", "You carefully step over [parent_as_living] to avoid hurting it.")
return
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index 8757402c7b2e9..9c1d694be9bc9 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -94,7 +94,7 @@
return
if(istype(arrived, /obj/effect/dummy/phased_mob)) //don't squeek if they're in a phased/jaunting container.
return
- if(arrived.movement_type & (FLYING|FLOATING) || !arrived.has_gravity())
+ if(arrived.movement_type & MOVETYPES_NOT_TOUCHING_GROUND || !arrived.has_gravity())
return
var/atom/current_parent = parent
if(isturf(current_parent?.loc))
diff --git a/code/datums/elements/forced_gravity.dm b/code/datums/elements/forced_gravity.dm
index 17f2651dc485c..51a0c270e77fa 100644
--- a/code/datums/elements/forced_gravity.dm
+++ b/code/datums/elements/forced_gravity.dm
@@ -4,7 +4,7 @@
var/gravity
var/ignore_space
-/datum/element/forced_gravity/Attach(datum/target, gravity=1, ignore_space=FALSE)
+/datum/element/forced_gravity/Attach(datum/target, gravity=1, ignore_space=FALSE, can_override = FALSE)
. = ..()
if(!isatom(target))
return ELEMENT_INCOMPATIBLE
@@ -12,21 +12,26 @@
src.gravity = gravity
src.ignore_space = ignore_space
- RegisterSignal(target, COMSIG_ATOM_HAS_GRAVITY, PROC_REF(gravity_check))
+ RegisterSignal(target, COMSIG_ATOM_HAS_GRAVITY, PROC_REF(gravity_check), override = can_override)
if(isturf(target))
- RegisterSignal(target, COMSIG_TURF_HAS_GRAVITY, PROC_REF(turf_gravity_check))
+ RegisterSignal(target, COMSIG_TURF_HAS_GRAVITY, PROC_REF(turf_gravity_check), override = can_override)
+
+ ADD_TRAIT(target, TRAIT_FORCED_GRAVITY, REF(src))
/datum/element/forced_gravity/Detach(datum/source, force)
. = ..()
var/static/list/signals_b_gone = list(COMSIG_ATOM_HAS_GRAVITY, COMSIG_TURF_HAS_GRAVITY)
UnregisterSignal(source, signals_b_gone)
+ REMOVE_TRAIT(source, TRAIT_FORCED_GRAVITY, REF(src))
/datum/element/forced_gravity/proc/gravity_check(datum/source, turf/location, list/gravs)
SIGNAL_HANDLER
- if(!ignore_space && isspaceturf(location))
- return
+ if(!ignore_space && location.force_no_gravity)
+ return FALSE
gravs += gravity
+ return TRUE
+
/datum/element/forced_gravity/proc/turf_gravity_check(datum/source, atom/checker, list/gravs)
SIGNAL_HANDLER
- return gravity_check(null, source, gravs)
+ gravity_check(null, source, gravs)
diff --git a/code/datums/elements/movetype_handler.dm b/code/datums/elements/movetype_handler.dm
index 3e648d01c4f5a..b33800738307e 100644
--- a/code/datums/elements/movetype_handler.dm
+++ b/code/datums/elements/movetype_handler.dm
@@ -52,7 +52,9 @@
if(!(source.movement_type & (FLOATING|FLYING)) && (trait == TRAIT_MOVE_FLYING || trait == TRAIT_MOVE_FLOATING) && !paused_floating_anim_atoms[source] && !HAS_TRAIT(source, TRAIT_NO_FLOATING_ANIM))
DO_FLOATING_ANIM(source)
source.movement_type |= flag
- SEND_SIGNAL(source, COMSIG_MOVETYPE_FLAG_ENABLED, flag)
+ if((trait == TRAIT_MOVE_FLYING || trait == TRAIT_MOVE_FLOATING) && !(source.movement_type & (FLOATING|FLYING)))
+ stop_floating(source)
+ SEND_SIGNAL(source, COMSIG_MOVETYPE_FLAG_DISABLED, flag)
/// Called when a movement type trait is removed from the movable. Disables the relative bitflag if it wasn't there in the compile-time bitfield.
/datum/element/movetype_handler/proc/on_movement_type_trait_loss(atom/movable/source, trait)
@@ -60,10 +62,14 @@
var/flag = GLOB.movement_type_trait_to_flag[trait]
if(initial(source.movement_type) & flag)
return
+ var/old_state = source.movement_type
source.movement_type &= ~flag
- if((trait == TRAIT_MOVE_FLYING || trait == TRAIT_MOVE_FLOATING) && !(source.movement_type & (FLOATING|FLYING)))
+ if((old_state & (FLOATING|FLYING)) && !(source.movement_type & (FLOATING|FLYING)))
stop_floating(source)
- SEND_SIGNAL(source, COMSIG_MOVETYPE_FLAG_DISABLED, flag)
+ var/turf/pitfall = source.loc //Things that don't fly fall in open space.
+ if(istype(pitfall))
+ pitfall.zFall(source)
+ SEND_SIGNAL(source, COMSIG_MOVETYPE_FLAG_DISABLED, flag, old_state)
/// Called when the TRAIT_NO_FLOATING_ANIM trait is added to the movable. Stops it from bobbing up and down.
/datum/element/movetype_handler/proc/on_no_floating_anim_trait_gain(atom/movable/source, trait)
diff --git a/code/datums/proximity_monitor/fields/gravity.dm b/code/datums/proximity_monitor/fields/gravity.dm
index ccac71a6d8508..b55a6521c32ed 100644
--- a/code/datums/proximity_monitor/fields/gravity.dm
+++ b/code/datums/proximity_monitor/fields/gravity.dm
@@ -9,7 +9,7 @@
/datum/proximity_monitor/advanced/gravity/setup_field_turf(turf/T)
. = ..()
- T.AddElement(/datum/element/forced_gravity, gravity_value)
+ T.AddElement(/datum/element/forced_gravity, gravity_value, can_override = TRUE)
modified_turfs[T] = gravity_value
/datum/proximity_monitor/advanced/gravity/cleanup_field_turf(turf/T)
diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm
index 9762499693560..61bf86a2c3c12 100644
--- a/code/datums/weather/weather_types/floor_is_lava.dm
+++ b/code/datums/weather/weather_types/floor_is_lava.dm
@@ -35,6 +35,6 @@
return
if(!L.client) //Only sentient people are going along with it!
return
- if(L.movement_type & (FLOATING|FLYING))
+ if(L.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return
L.adjustFireLoss(3)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 9aa198d1ae78c..674d8de01175e 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1897,6 +1897,12 @@ CREATION_TEST_IGNORE_SUBTYPES(/atom)
* Sends signals [COMSIG_ATOM_HAS_GRAVITY] and [COMSIG_TURF_HAS_GRAVITY], both can force gravity with
* the forced gravity var.
*
+ * HEY JACKASS, LISTEN
+ * IF YOU ADD SOMETHING TO THIS PROC, MAKE SURE /mob/living ACCOUNTS FOR IT
+ *
+ * Living mobs treat gravity in an event based manner. We've decomposed this proc into different checks
+ * for them to use. If you add more to it, make sure you do that, or things will behave strangely
+ *
* Gravity situations:
* * No gravity if you're not in a turf
* * No gravity if this atom is in is a space turf
@@ -1909,32 +1915,25 @@ CREATION_TEST_IGNORE_SUBTYPES(/atom)
if(!isturf(gravity_turf))
gravity_turf = get_turf(src)
- if(!gravity_turf)
+ if(!gravity_turf)//no gravity in nullspace
return FALSE
var/list/forced_gravity = list()
SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, gravity_turf, forced_gravity)
- if(!length(forced_gravity))
- SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
+ SEND_SIGNAL(gravity_turf, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
if(length(forced_gravity))
- var/max_grav
- for(var/i in forced_gravity)
- max_grav = max(max_grav, i)
- return max_grav
+ var/positive_grav = max(forced_gravity)
+ var/negative_grav = min(min(forced_gravity), 0) //negative grav needs to be below or equal to 0
- if(!gravity_turf.check_gravity()) // Turf never has gravity
- return FALSE
- var/area/A = get_area(gravity_turf)
- if(A.has_gravity) // Areas which always has gravity
- return TRUE
- else if(SSmapping.level_trait(gravity_turf.z, ZTRAIT_GRAVITY)) // If the z-level always has gravity
- return TRUE
- else if(GLOB.gravity_generators["[gravity_turf.get_virtual_z_level()]"]) // If there's a gravity generator on our z level
- var/max_grav = 0
- for(var/obj/machinery/gravity_generator/main/G in GLOB.gravity_generators["[gravity_turf.get_virtual_z_level()]"])
- max_grav = max(G.setting,max_grav)
- return max_grav
- return FALSE
+ //our gravity is sum of the most massive positive and negative numbers returned by the signal
+ //so that adding two forced_gravity elements with an effect size of 1 each doesnt add to 2 gravity
+ //but negative force gravity effects can cancel out positive ones
+
+ return (positive_grav + negative_grav)
+
+ var/area/turf_area = gravity_turf.loc
+
+ return !gravity_turf.force_no_gravity && (SSmapping.gravity_by_z_level[gravity_turf.z] || turf_area.has_gravity)
/*
* Called when something made out of plasma is exposed to high temperatures.
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index b860bda0a83f1..7940d16e74694 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -125,7 +125,7 @@
/obj/effect/mine/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
- if(!isturf(loc) || AM.throwing || (AM.movement_type & (FLYING | FLOATING)) || !AM.has_gravity() || triggered)
+ if(!isturf(loc) || AM.throwing || (AM.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || !AM.has_gravity() || triggered)
return
if(ismob(AM))
checksmartmine(AM)
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index 1c16677c2f466..bb2e99f70d127 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -215,7 +215,7 @@
. = ..()
update_icon()
var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(spring_trap),
+ COMSIG_ATOM_ENTERED = PROC_REF(trap_stepped_on),
)
AddElement(/datum/element/connect_loc, loc_connections)
@@ -241,41 +241,53 @@
update_appearance()
playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
-/obj/item/restraints/legcuffs/beartrap/proc/spring_trap(datum/source, AM as mob|obj)
+/obj/item/restraints/legcuffs/beartrap/proc/trap_stepped_on(datum/source, atom/movable/entering, ...)
SIGNAL_HANDLER
- if(armed && isturf(loc))
- if(isliving(AM))
- var/mob/living/L = AM
- var/snap = TRUE
- if(istype(L.buckled, /obj/vehicle))
- var/obj/vehicle/ridden_vehicle = L.buckled
- if(!ridden_vehicle.are_legs_exposed) //close the trap without injuring/trapping the rider if their legs are inside the vehicle at all times.
- close_trap()
- ridden_vehicle.visible_message("[ridden_vehicle] triggers \the [src].")
-
- if(L.movement_type & (FLYING|FLOATING)) //don't close the trap if they're flying/floating over it.
- snap = FALSE
-
- var/def_zone = BODY_ZONE_CHEST
- if(snap && iscarbon(L))
- var/mob/living/carbon/C = L
- if(C.body_position == STANDING_UP)
- def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- if(!C.legcuffed && C.num_legs >= 2) //beartrap can't cuff your leg if there's already a beartrap or legcuffs, or you don't have two legs.
- C.legcuffed = src
- forceMove(C)
- C.update_equipment_speed_mods()
- C.update_inv_legcuffed()
- SSblackbox.record_feedback("tally", "handcuffs", 1, type)
- else if(snap && isanimal(L))
- var/mob/living/simple_animal/SA = L
- if(SA.mob_size <= MOB_SIZE_TINY) //don't close the trap if they're as small as a mouse.
- snap = FALSE
- if(snap)
- close_trap()
- L.visible_message("[L] triggers \the [src].", \
- "You trigger \the [src]!")
- L.apply_damage(trap_damage, BRUTE, def_zone)
+
+ spring_trap(entering)
+
+/**
+ * Tries to spring the trap on the target movable.
+ *
+ * This proc is safe to call without knowing if the target is valid or if the trap is armed.
+ *
+ * Does not trigger on tiny mobs.
+ * If ignore_movetypes is FALSE, does not trigger on floating / flying / etc. mobs.
+ */
+/obj/item/restraints/legcuffs/beartrap/proc/spring_trap(atom/movable/target, ignore_movetypes = FALSE, hit_prone = FALSE)
+ if(!armed || !isturf(loc) || !isliving(target))
+ return
+
+ var/mob/living/victim = target
+ if(istype(victim.buckled, /obj/vehicle))
+ var/obj/vehicle/ridden_vehicle = victim.buckled
+ if(!ridden_vehicle.are_legs_exposed) //close the trap without injuring/trapping the rider if their legs are inside the vehicle at all times.
+ close_trap()
+ ridden_vehicle.visible_message("[ridden_vehicle] triggers \the [src].")
+ return
+
+ //don't close the trap if they're as small as a mouse
+ if(victim.mob_size <= MOB_SIZE_TINY)
+ return
+ if(!ignore_movetypes && (victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
+ return
+
+ close_trap()
+ if(ignore_movetypes)
+ victim.visible_message("\The [src] ensnares [victim]!", \
+ "\The [src] ensnares you!")
+ else
+ victim.visible_message("[victim] triggers \the [src].", \
+ "You trigger \the [src]!")
+ var/def_zone = BODY_ZONE_CHEST
+ if(iscarbon(victim) && (victim.body_position == STANDING_UP || hit_prone))
+ var/mob/living/carbon/carbon_victim = victim
+ def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ if(!carbon_victim.legcuffed && carbon_victim.num_legs >= 2) //beartrap can't cuff your leg if there's already a beartrap or legcuffs, or you don't have two legs.
+ INVOKE_ASYNC(carbon_victim, TYPE_PROC_REF(/mob/living/carbon, equip_to_slot), src, ITEM_SLOT_LEGCUFFED)
+ SSblackbox.record_feedback("tally", "handcuffs", 1, type)
+
+ victim.apply_damage(trap_damage, BRUTE, def_zone)
/obj/item/restraints/legcuffs/beartrap/energy
name = "energy snare"
@@ -296,7 +308,7 @@
qdel(src)
/obj/item/restraints/legcuffs/beartrap/energy/attack_hand(mob/user, list/modifiers)
- spring_trap(null, user)
+ spring_trap(user)
return ..()
/obj/item/restraints/legcuffs/beartrap/energy/cyborg
@@ -371,7 +383,7 @@
/obj/item/restraints/legcuffs/bola/energy/ensnare(mob/living/carbon/C)
var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy/cyborg(get_turf(C))
- B.spring_trap(null, C)
+ B.spring_trap(C, ignore_movetypes = TRUE)
qdel(src)
/obj/item/restraints/legcuffs/bola/energy/emp_act(severity)
diff --git a/code/game/objects/items/stacks/sheets/mineral/glass.dm b/code/game/objects/items/stacks/sheets/mineral/glass.dm
index 80f392130647d..3602fc2b74d28 100644
--- a/code/game/objects/items/stacks/sheets/mineral/glass.dm
+++ b/code/game/objects/items/stacks/sheets/mineral/glass.dm
@@ -295,7 +295,7 @@
SIGNAL_HANDLER
if(isliving(AM))
var/mob/living/L = AM
- if(!(L.movement_type & (FLYING|FLOATING)) || L.buckled)
+ if(!(L.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || L.buckled)
playsound(src, 'sound/effects/glass_step.ogg', HAS_TRAIT(L, TRAIT_LIGHT_STEP) ? 30 : 50, TRUE)
/obj/item/shard/plasma
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index f48bcdf352da5..37987a431deb6 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -84,6 +84,7 @@
m180.Turn(180)
animate(L, transform = m180, time = 3)
L.pixel_y = L.base_pixel_y + PIXEL_Y_OFFSET_LYING
+ ADD_TRAIT(user, TRAIT_MOVE_UPSIDE_DOWN, REF(src))
else if (has_buckled_mobs())
for(var/mob/living/L in buckled_mobs)
user_unbuckle_mob(L, user)
@@ -129,6 +130,7 @@
m180.Turn(180)
animate(M, transform = m180, time = 3)
M.pixel_y = M.base_pixel_y + PIXEL_Y_OFFSET_LYING
+ REMOVE_TRAIT(M, TRAIT_MOVE_UPSIDE_DOWN, REF(src))
M.adjustBruteLoss(30)
src.visible_message("[M] falls free of [src]!")
unbuckle_mob(M,force=1)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index cd6f467120bcc..ca961d47a5197 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -84,7 +84,7 @@
/obj/structure/railing/CanPass(atom/movable/mover, border_dir)
. = ..()
if(border_dir & dir)
- return . || mover.throwing || mover.movement_type & (FLYING | FLOATING)
+ return . || mover.throwing || mover.movement_type & MOVETYPES_NOT_TOUCHING_GROUND
return TRUE
/obj/structure/railing/proc/on_exit(datum/source, atom/movable/leaving, direction)
@@ -102,7 +102,7 @@
if (leaving.throwing)
return
- if (leaving.movement_type & (PHASING | FLYING | FLOATING))
+ if (leaving.movement_type & (PHASING | MOVETYPES_NOT_TOUCHING_GROUND))
return
if (leaving.move_force >= MOVE_FORCE_EXTREMELY_STRONG)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 544cb9177e98b..1a657d3bc2c98 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -317,7 +317,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/structure/table)
check_break(M)
/obj/structure/table/glass/proc/check_break(mob/living/M)
- if(M.has_gravity() && M.mob_size > MOB_SIZE_SMALL && !(M.movement_type & (FLOATING|FLYING)))
+ if(M.has_gravity() && M.mob_size > MOB_SIZE_SMALL && !(M.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
table_shatter(M)
/obj/structure/table/glass/proc/table_shatter(mob/living/victim)
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index 6b0f5a24a69ef..d6a6ae2dd1d22 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -99,7 +99,7 @@ CREATION_TEST_IGNORE_SELF(/turf/open)
/turf/open/indestructible/sound/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
- if(istype(arrived) && !(arrived.movement_type & (FLYING|FLOATING)))
+ if(istype(arrived) && !(arrived.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
playsound(src,sound,50,1)
/turf/open/indestructible/necropolis
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index a31ddf285f808..104b7d6dadd28 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -125,7 +125,7 @@
else if (isliving(thing))
. = 1
var/mob/living/L = thing
- if(L.movement_type & (FLOATING|FLYING))
+ if(L.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
continue //YOU'RE FLYING OVER IT
var/buckle_check = L.buckled
if(isobj(buckle_check))
diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm
index 4cef4cfdf32af..005628458fbd8 100644
--- a/code/game/turfs/open/openspace.dm
+++ b/code/game/turfs/open/openspace.dm
@@ -31,6 +31,13 @@ CREATION_TEST_IGNORE_SUBTYPES(/turf/open/openspace)
/turf/open/openspace/airless
initial_gas_mix = AIRLESS_ATMOS
+/turf/open/openspace/Initialize(mapload)
+ . = ..()
+ var/area/our_area = loc
+ if(istype(our_area, /area/space))
+ force_no_gravity = TRUE
+ return INITIALIZE_HINT_LATELOAD
+
/turf/open/openspace/can_have_cabling()
if(locate(/obj/structure/lattice/catwalk, src))
return TRUE
diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm
index d4e29778c1bab..54672809445a6 100644
--- a/code/game/turfs/open/space/space.dm
+++ b/code/game/turfs/open/space/space.dm
@@ -38,6 +38,8 @@
z_eventually_space = TRUE
vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf.
+ force_no_gravity = TRUE
+
/turf/open/space/basic/New() //Do not convert to Initialize
//This is used to optimize the map loader
return
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 94342afcd56a3..a1992909487a4 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -72,6 +72,9 @@ CREATION_TEST_IGNORE_SELF(/turf)
/// See __DEFINES/construction.dm for RCD_MEMORY_*.
var/rcd_memory
+ ///whether or not this turf forces movables on it to have no gravity (unless they themselves have forced gravity)
+ var/force_no_gravity = FALSE
+
///Icon-smoothing variable to map a diagonal wall corner with a fixed underlay.
var/list/fixed_underlay = null
diff --git a/code/modules/antagonists/clock_cult/traps/receivers/skewer.dm b/code/modules/antagonists/clock_cult/traps/receivers/skewer.dm
index d9d66ea55c9f4..b66d84885dc28 100644
--- a/code/modules/antagonists/clock_cult/traps/receivers/skewer.dm
+++ b/code/modules/antagonists/clock_cult/traps/receivers/skewer.dm
@@ -27,7 +27,7 @@
var/target_stabbed = FALSE
density = TRUE
for(var/mob/living/M in get_turf(src))
- if(M.incorporeal_move || M.movement_type & (FLOATING|FLYING))
+ if(M.incorporeal_move || M.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
continue
if(buckle_mob(M, TRUE))
target_stabbed = TRUE
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index 39399d9e8064a..94f2af29bb758 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -4,6 +4,7 @@
GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/obj/machinery/field/containment,
/obj/machinery/power/supermatter_crystal,
+ /obj/machinery/gravity_generator,
/obj/machinery/doomsday_device,
/obj/machinery/nuclearbomb,
/obj/machinery/nuclearbomb/selfdestruct,
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 4769f01272687..db98df285b98e 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -175,7 +175,7 @@
if(armed)
if(ismob(AM))
var/mob/MM = AM
- if(!(MM.movement_type & (FLOATING|FLYING)))
+ if(!(MM.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
if(ishuman(AM))
var/mob/living/carbon/H = AM
if(H.m_intent == MOVE_INTENT_RUN)
diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm
index 6e3a364ab8ff3..a1cecec5fe1f1 100644
--- a/code/modules/clothing/shoes/magboots.dm
+++ b/code/modules/clothing/shoes/magboots.dm
@@ -12,6 +12,16 @@
equip_delay_other = 70
resistance_flags = FIRE_PROOF
+/obj/item/clothing/shoes/magboots/equipped(mob/user, slot)
+ . = ..()
+ if(slot & ITEM_SLOT_FEET)
+ update_gravity_trait(user)
+ else
+ REMOVE_TRAIT(user, TRAIT_NEGATES_GRAVITY, type)
+
+/obj/item/clothing/shoes/magboots/dropped(mob/user)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_NEGATES_GRAVITY, type)
/obj/item/clothing/shoes/magboots/verb/toggle()
set name = "Toggle Magboots"
@@ -22,7 +32,7 @@
attack_self(usr)
-/obj/item/clothing/shoes/magboots/attack_self(mob/user)
+/obj/item/clothing/shoes/magboots/attack_self(mob/living/user)
if(magpulse)
clothing_flags &= ~NOSLIP
slowdown = SHOES_SLOWDOWN
@@ -31,9 +41,8 @@
slowdown = slowdown_active
magpulse = !magpulse
icon_state = "[magboot_state][magpulse]"
- to_chat(user, "You [magpulse ? "enable" : "disable"] the mag-pulse traction system.")
- user.update_inv_shoes() //so our mob-overlays update
- user.update_gravity(user.has_gravity())
+ update_gravity_trait(user)
+ user.refresh_gravity()
update_action_buttons()
/obj/item/clothing/shoes/magboots/negates_gravity()
@@ -43,6 +52,13 @@
. = ..()
. += "Its mag-pulse traction system appears to be [magpulse ? "enabled" : "disabled"]."
+///Adds/removes the gravity negation trait from the wearer depending on if the magpulse system is turned on.
+/obj/item/clothing/shoes/magboots/proc/update_gravity_trait(mob/user)
+ if(magpulse)
+ ADD_TRAIT(user, TRAIT_NEGATES_GRAVITY, type)
+ else
+ REMOVE_TRAIT(user, TRAIT_NEGATES_GRAVITY, type)
+
/obj/item/clothing/shoes/magboots/advance
desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer."
@@ -69,6 +85,7 @@
clothing_flags = NOSLIP
/obj/item/clothing/shoes/magboots/commando/attack_self(mob/user) //Code for the passive no-slip of the commando magboots to always apply, kind of a shit code solution though.
+ . = ..()
if(magpulse)
slowdown = SHOES_SLOWDOWN
else
@@ -77,7 +94,6 @@
icon_state = "[magboot_state][magpulse]"
to_chat(user, "You [magpulse ? "enable" : "disable"] the mag-pulse traction system.")
user.update_inv_shoes()
- user.update_gravity(user.has_gravity())
update_action_buttons()
/obj/item/clothing/shoes/magboots/crushing
diff --git a/code/modules/mapping/space_management/zlevel_manager.dm b/code/modules/mapping/space_management/zlevel_manager.dm
index d43aedc2a225d..7e7c8e188e887 100644
--- a/code/modules/mapping/space_management/zlevel_manager.dm
+++ b/code/modules/mapping/space_management/zlevel_manager.dm
@@ -27,6 +27,7 @@
var/datum/space_level/S = new z_type(new_z, name, traits, orbital_body_type)
manage_z_level(S, filled_with_space = TRUE, contain_turfs = contain_turfs)
generate_linkages_for_z_level(new_z)
+ calculate_z_level_gravity(new_z)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, S)
return S
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 5ce3b199f2e90..04313e3f0c07c 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -32,6 +32,9 @@
GLOB.new_player_list -= src
return ..()
+/mob/dead/new_player/mob_negates_gravity()
+ return TRUE //no need to calculate if they have gravity.
+
/mob/dead/new_player/prepare_huds()
return
diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm
index b776e3bf4bc43..a4e01f2cd0d6f 100644
--- a/code/modules/mob/living/carbon/carbon_movement.dm
+++ b/code/modules/mob/living/carbon/carbon_movement.dm
@@ -1,6 +1,6 @@
/mob/living/carbon/slip(knockdown_amount, obj/O, lube, paralyze, force_drop)
- if(movement_type & (FLYING|FLOATING))
+ if(movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
return FALSE
if((lube & NO_SLIP_ON_CATWALK) && (locate(/obj/structure/lattice/catwalk) in get_turf(src)))
return FALSE
@@ -63,9 +63,9 @@
REMOVE_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
-/mob/living/carbon/on_movement_type_flag_disabled(datum/source, flag)
+/mob/living/carbon/on_movement_type_flag_disabled(datum/source, flag, old_movement_type)
. = ..()
- if(flag & (FLYING | FLOATING) && !(movement_type & (FLYING | FLOATING)))
+ if(old_movement_type & (FLYING | FLOATING) && !(movement_type & (FLYING | FLOATING)))
var/limbless_slowdown = 0
if(usable_legs < default_num_legs)
limbless_slowdown += (default_num_legs - usable_legs) * 3
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 12961b8d82daa..d3050b5df6689 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -12,6 +12,11 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
. = ..()
remove_from_all_data_huds()
+
+// We don't want your dummy floating up and down in the preference menu.
+/mob/living/carbon/human/dummy/mob_negates_gravity()
+ return TRUE
+
/mob/living/carbon/human/dummy/prepare_data_huds()
return
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index a4f6a35ff6439..06d5bf93df7b0 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -2391,6 +2391,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(isturf(H.loc))
var/turf/T = H.loc
T.Entered(H)
+ H.refresh_gravity()
///Calls the DMI data for a custom icon for a given bodypart from the Species Datum.
/datum/species/proc/get_custom_icons(var/part)
diff --git a/code/modules/mob/living/init_signals.dm b/code/modules/mob/living/init_signals.dm
index b3608f641cd33..1c1b9752b350b 100644
--- a/code/modules/mob/living/init_signals.dm
+++ b/code/modules/mob/living/init_signals.dm
@@ -41,6 +41,16 @@
RegisterSignal(src, COMSIG_MOVETYPE_FLAG_ENABLED, PROC_REF(on_movement_type_flag_enabled))
RegisterSignal(src, COMSIG_MOVETYPE_FLAG_DISABLED, PROC_REF(on_movement_type_flag_disabled))
+ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_NEGATES_GRAVITY), SIGNAL_REMOVETRAIT(TRAIT_NEGATES_GRAVITY)), PROC_REF(on_negate_gravity))
+ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_IGNORING_GRAVITY), SIGNAL_REMOVETRAIT(TRAIT_IGNORING_GRAVITY)), PROC_REF(on_ignore_gravity))
+ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_FORCED_GRAVITY), SIGNAL_REMOVETRAIT(TRAIT_FORCED_GRAVITY)), PROC_REF(on_force_gravity))
+ // We hook for forced grav changes from our turf and ourselves
+ var/static/list/loc_connections = list(
+ SIGNAL_ADDTRAIT(TRAIT_FORCED_GRAVITY) = PROC_REF(on_loc_force_gravity),
+ SIGNAL_REMOVETRAIT(TRAIT_FORCED_GRAVITY) = PROC_REF(on_loc_force_gravity),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
///Called when TRAIT_KNOCKEDOUT is added to the mob.
/mob/living/proc/on_knockedout_trait_gain(datum/source)
SIGNAL_HANDLER
@@ -63,6 +73,29 @@
SIGNAL_HANDLER
REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, TRAIT_DEATHCOMA)
+/// Called when [TRAIT_NEGATES_GRAVITY] is gained or lost
+/mob/living/proc/on_negate_gravity(datum/source)
+ SIGNAL_HANDLER
+ if(!isgroundlessturf(loc))
+ if(HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY))
+ ADD_TRAIT(src, TRAIT_IGNORING_GRAVITY, IGNORING_GRAVITY_NEGATION)
+ else
+ REMOVE_TRAIT(src, TRAIT_IGNORING_GRAVITY, IGNORING_GRAVITY_NEGATION)
+
+/// Called when [TRAIT_IGNORING_GRAVITY] is gained or lost
+/mob/living/proc/on_ignore_gravity(datum/source)
+ SIGNAL_HANDLER
+ refresh_gravity()
+
+/// Called when [TRAIT_FORCED_GRAVITY] is gained or lost
+/mob/living/proc/on_force_gravity(datum/source)
+ SIGNAL_HANDLER
+ refresh_gravity()
+
+/// Called when our loc's [TRAIT_FORCED_GRAVITY] is gained or lost
+/mob/living/proc/on_loc_force_gravity(datum/source)
+ SIGNAL_HANDLER
+ refresh_gravity()
///Called when TRAIT_IMMOBILIZED is added to the mob.
/mob/living/proc/on_immobilized_trait_gain(datum/source)
@@ -178,11 +211,11 @@
clear_alert("succumb")
///From [element/movetype_handler/on_movement_type_trait_gain()]
-/mob/living/proc/on_movement_type_flag_enabled(datum/source, trait)
+/mob/living/proc/on_movement_type_flag_enabled(datum/source, trait, flag, old_movement_type)
SIGNAL_HANDLER
update_movespeed(FALSE)
///From [element/movetype_handler/on_movement_type_trait_loss()]
-/mob/living/proc/on_movement_type_flag_disabled(datum/source, trait)
+/mob/living/proc/on_movement_type_flag_disabled(datum/source, trait, flag, old_movement_type)
SIGNAL_HANDLER
update_movespeed(FALSE)
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index b98d460e29a1f..0be85eaa01ca3 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -36,14 +36,7 @@
if(environment)
handle_environment(environment)
- //Handle gravity
- var/gravity = has_gravity()
- update_gravity(gravity)
-
- if(gravity > STANDARD_GRAVITY)
- if(!get_filter("gravity"))
- add_filter("gravity",1,list("type"="motion_blur", "x"=0, "y"=0))
- handle_high_gravity(gravity)
+ handle_gravity(delta_time, times_fired)
if(stat != DEAD)
handle_traits(delta_time) // eye, ear, brain damages
@@ -117,9 +110,20 @@
/mob/living/proc/update_damage_hud()
return
-/mob/living/proc/handle_high_gravity(gravity)
- if(gravity >= GRAVITY_DAMAGE_TRESHOLD) //Aka gravity values of 3 or more
- var/grav_stregth = gravity - GRAVITY_DAMAGE_TRESHOLD
- adjustBruteLoss(min(grav_stregth,3))
+/mob/living/proc/handle_gravity(seconds_per_tick, times_fired)
+ if(gravity_state > STANDARD_GRAVITY)
+ handle_high_gravity(gravity_state, seconds_per_tick, times_fired)
+
+/mob/living/proc/gravity_animate()
+ if(!get_filter("gravity"))
+ add_filter("gravity",1,list("type"="motion_blur", "x"=0, "y"=0))
+ animate(get_filter("gravity"), y = 1, time = 10, loop = -1)
+ animate(y = 0, time = 10)
+
+/mob/living/proc/handle_high_gravity(gravity, seconds_per_tick, times_fired)
+ if(gravity < GRAVITY_DAMAGE_THRESHOLD) //Aka gravity values of 3 or more
+ return
+ var/grav_strength = gravity - GRAVITY_DAMAGE_THRESHOLD
+ adjustBruteLoss(min(GRAVITY_DAMAGE_SCALING * grav_strength, GRAVITY_DAMAGE_MAXIMUM) * seconds_per_tick)
#undef BODYTEMP_DIVISOR
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 58f66906a0bb1..00771145a2264 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -24,6 +24,7 @@
// it prevents 'GLOB.poi_list' being glitched. without this, it will show xeno(or some mobs) twice in orbit panel.
//color correction
RegisterSignal(src, COMSIG_MOVABLE_ENTERED_AREA, PROC_REF(apply_color_correction))
+ gravity_setup()
/mob/living/ComponentInitialize()
. = ..()
@@ -955,25 +956,51 @@
/mob/living/proc/get_visible_name()
return name
-/mob/living/update_gravity(has_gravity)
- . = ..()
- if(!SSticker.HasRoundStarted())
- return
- var/was_weightless = alerts["gravity"] && istype(alerts["gravity"], /atom/movable/screen/alert/weightless)
- if(has_gravity)
- if(has_gravity == 1)
- clear_alert("gravity")
- else
- if(has_gravity >= GRAVITY_DAMAGE_TRESHOLD)
- throw_alert("gravity", /atom/movable/screen/alert/veryhighgravity)
- else
- throw_alert("gravity", /atom/movable/screen/alert/highgravity)
- if(was_weightless)
- REMOVE_TRAIT(src, TRAIT_MOVE_FLOATING, NO_GRAVITY_TRAIT)
+/mob/living/proc/update_gravity(gravity)
+ // Handle movespeed stuff
+ var/speed_change = max(0, gravity - STANDARD_GRAVITY)
+ if(speed_change)
+ add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gravity, multiplicative_slowdown=speed_change)
else
- throw_alert("gravity", /atom/movable/screen/alert/weightless)
- if(!was_weightless)
- ADD_TRAIT(src, TRAIT_MOVE_FLOATING, NO_GRAVITY_TRAIT)
+ remove_movespeed_modifier(/datum/movespeed_modifier/gravity)
+
+ // Time to add/remove gravity alerts. sorry for the mess it's gotta be fast
+ var/atom/movable/screen/alert/gravity_alert = alerts["gravity"]
+ switch(gravity)
+ if(-INFINITY to NEGATIVE_GRAVITY)
+ if(!istype(gravity_alert, /atom/movable/screen/alert/negative))
+ throw_alert("gravity", /atom/movable/screen/alert/negative)
+ ADD_TRAIT(src, TRAIT_MOVE_UPSIDE_DOWN, NEGATIVE_GRAVITY_TRAIT)
+ var/matrix/flipped_matrix = transform
+ flipped_matrix.b = -flipped_matrix.b
+ flipped_matrix.e = -flipped_matrix.e
+ animate(src, transform = flipped_matrix, pixel_y = pixel_y+4, time = 0.5 SECONDS, easing = EASE_OUT, flags = ANIMATION_PARALLEL)
+ base_pixel_y += 4
+ if(NEGATIVE_GRAVITY + 0.01 to 0)
+ if(!istype(gravity_alert, /atom/movable/screen/alert/weightless))
+ throw_alert("gravity", /atom/movable/screen/alert/weightless)
+ ADD_TRAIT(src, TRAIT_MOVE_FLOATING, NO_GRAVITY_TRAIT)
+ if(0.01 to STANDARD_GRAVITY)
+ if(gravity_alert)
+ clear_alert("gravity")
+ if(STANDARD_GRAVITY + 0.01 to GRAVITY_DAMAGE_THRESHOLD - 0.01)
+ throw_alert("gravity", /atom/movable/screen/alert/highgravity)
+ if(GRAVITY_DAMAGE_THRESHOLD to INFINITY)
+ throw_alert("gravity", /atom/movable/screen/alert/veryhighgravity)
+
+ // If we had no gravity alert, or the same alert as before, go home
+ if(!gravity_alert || alerts["gravity"] == gravity_alert)
+ return
+ // By this point we know that we do not have the same alert as we used to
+ if(istype(gravity_alert, /atom/movable/screen/alert/weightless))
+ REMOVE_TRAIT(src, TRAIT_MOVE_FLOATING, NO_GRAVITY_TRAIT)
+ if(istype(gravity_alert, /atom/movable/screen/alert/negative))
+ REMOVE_TRAIT(src, TRAIT_MOVE_UPSIDE_DOWN, NEGATIVE_GRAVITY_TRAIT)
+ var/matrix/flipped_matrix = transform
+ flipped_matrix.b = -flipped_matrix.b
+ flipped_matrix.e = -flipped_matrix.e
+ animate(src, transform = flipped_matrix, pixel_y = pixel_y-4, time = 0.5 SECONDS, easing = EASE_OUT, flags = ANIMATION_PARALLEL)
+ base_pixel_y -= 4
// The src mob is trying to strip an item from someone
// Override if a certain type of mob should be behave differently when stripping items (can't, for example)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 7e5917bde22ba..c7b849c0c4b69 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -169,3 +169,6 @@
///The x amount a mob's sprite should be offset due to the current position they're in
var/body_position_pixel_y_offset = 0
+ /// What our current gravity state is. Used to avoid duplicate animates and such
+ var/gravity_state = null
+
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
index eaf9b84962334..25b8815ef6b45 100644
--- a/code/modules/mob/living/living_movement.dm
+++ b/code/modules/mob/living/living_movement.dm
@@ -1,8 +1,60 @@
-/mob/living/Moved()
+/mob/living/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
update_turf_movespeed(loc)
update_looking_move()
+ if(HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY))
+ if(!isgroundlessturf(loc))
+ ADD_TRAIT(src, TRAIT_IGNORING_GRAVITY, IGNORING_GRAVITY_NEGATION)
+ else
+ REMOVE_TRAIT(src, TRAIT_IGNORING_GRAVITY, IGNORING_GRAVITY_NEGATION)
+ var/turf/old_turf = get_turf(old_loc)
+ var/turf/new_turf = get_turf(src)
+ // If we're moving to/from nullspace, refresh
+ // Easier then adding nullchecks to all this shit, and technically right since a null turf means nograv
+ if(isnull(old_turf) || isnull(new_turf))
+ if(!QDELING(src))
+ refresh_gravity()
+ return
+ // If the turf gravity has changed, then it's possible that our state has changed, so update
+ if(HAS_TRAIT(old_turf, TRAIT_FORCED_GRAVITY) != HAS_TRAIT(new_turf, TRAIT_FORCED_GRAVITY) || new_turf.force_no_gravity != old_turf.force_no_gravity)
+ refresh_gravity()
+
+ // Going to do area gravity checking here
+ var/area/old_area = get_area(old_turf)
+ var/area/new_area = get_area(new_turf)
+ // If the area gravity has changed, then it's possible that our state has changed, so update
+ if(old_area.has_gravity != new_area.has_gravity)
+ refresh_gravity()
+
+/mob/living/onTransitZ(old_z, new_z, same_z_layer, notify_contents)
+ . = ..()
+
+ if(!old_z || !new_z || SSmapping.gravity_by_z_level[old_z] != SSmapping.gravity_by_z_level[new_z])
+ refresh_gravity()
+
+/// Living Mob use event based gravity
+/// We check here to ensure we haven't dropped any gravity changes
+/mob/living/proc/gravity_setup()
+ on_negate_gravity(src)
+ refresh_gravity()
+
+/// Handles gravity effects. Call if something about our gravity has potentially changed!
+/mob/living/proc/refresh_gravity()
+ var/old_grav_state = gravity_state
+ gravity_state = has_gravity()
+ if(gravity_state == old_grav_state)
+ return
+
+ update_gravity(gravity_state)
+
+ if(gravity_state > STANDARD_GRAVITY)
+ gravity_animate()
+ else if(old_grav_state > STANDARD_GRAVITY)
+ remove_filter("gravity")
+
+/mob/living/mob_negates_gravity()
+ return HAS_TRAIT_FROM(src, TRAIT_IGNORING_GRAVITY, IGNORING_GRAVITY_NEGATION)
/mob/living/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index 81e5ff91e12a8..aacc8ed8cb00d 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -3,8 +3,6 @@
return
//Being dead doesn't mean your temperature never changes
- update_gravity(has_gravity())
-
handle_status_effects(delta_time)
handle_traits(delta_time)
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
index c9bee4c254854..6ded2491d4a6e 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
@@ -58,7 +58,7 @@
/obj/projectile/mega_arachnid/on_hit(atom/target, blocked = FALSE)
if(iscarbon(target) && blocked < 100)
var/obj/item/restraints/legcuffs/beartrap/mega_arachnid/B = new /obj/item/restraints/legcuffs/beartrap/mega_arachnid(get_turf(target))
- B.spring_trap(null, target)
+ B.spring_trap(target)
return ..()
/obj/item/restraints/legcuffs/beartrap/mega_arachnid
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index b089c1bde7df3..289f149f9561e 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -354,6 +354,9 @@
continue
return rebound
+/mob/has_gravity(turf/gravity_turf)
+ return mob_negates_gravity() || ..()
+
/**
* Does this mob ignore gravity
*/
@@ -372,14 +375,6 @@
/mob/proc/slip(knockdown, paralyze, forcedrop, w_amount, obj/O, lube)
return
-/// Update the gravity status of this mob
-/mob/proc/update_gravity(has_gravity, override=FALSE)
- var/speed_change = max(0, has_gravity - STANDARD_GRAVITY)
- if(!speed_change)
- remove_movespeed_modifier(/datum/movespeed_modifier/gravity)
- else
- add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gravity, multiplicative_slowdown=speed_change)
-
//bodypart selection verbs - Cyberboss
//8:repeated presses toggles through head - eyes - mouth
//4: r-arm 5: chest 6: l-arm
diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm
index 7a4e3ba20d20b..c6f604069e801 100644
--- a/code/modules/movespeed/modifiers/mobs.dm
+++ b/code/modules/movespeed/modifiers/mobs.dm
@@ -83,6 +83,7 @@
/datum/movespeed_modifier/limbless
variable = TRUE
movetypes = GROUND
+ blacklisted_movetypes = FLOATING|FLYING
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/simplemob_varspeed
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 4f91bdd2f00c7..1106020078cd1 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -187,6 +187,20 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
// Interaction
+/obj/machinery/gravity_generator/main/examine(mob/user)
+ . = ..()
+ if(!(machine_stat & BROKEN))
+ return
+ switch(broken_state)
+ if(GRAV_NEEDS_SCREWDRIVER)
+ . += "The entire frame is barely holding together, the screws need to be refastened."
+ if(GRAV_NEEDS_WELDING)
+ . += "There's lots of broken seals on the framework, it could use some welding."
+ if(GRAV_NEEDS_PLASTEEL)
+ . += "Some of this damaged plating needs full replacement. 10 plasteel> should be enough."
+ if(GRAV_NEEDS_WRENCH)
+ . += "The new plating just needs to be bolted into place now."
+
// Fixing the gravity generator.
/obj/machinery/gravity_generator/main/attackby(obj/item/I, mob/user, params)
switch(broken_state)
@@ -365,11 +379,13 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
/obj/machinery/gravity_generator/main/proc/shake_everyone()
var/turf/T = get_turf(src)
var/sound/alert_sound = sound('sound/effects/alert.ogg')
- for(var/i in GLOB.mob_list)
- var/mob/M = i
+ for(var/mobs in GLOB.mob_list)
+ var/mob/M = mobs
if(M.get_virtual_z_level() != get_virtual_z_level() && !(ztrait && SSmapping.level_trait(z, ztrait) && SSmapping.level_trait(M.z, ztrait)))
continue
- M.update_gravity(M.has_gravity())
+ if(isliving(M))
+ var/mob/living/grav_update = M
+ grav_update.refresh_gravity()
if(M.client)
shake_camera(M, 15, 1)
M.playsound_local(T, null, 100, 1, 0.5, S = alert_sound)
@@ -399,6 +415,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
GLOB.gravity_generators["[theZ]"] |= src
else
GLOB.gravity_generators["[theZ]"] -= src
+ SSmapping.calculate_z_level_gravity(z)
/obj/machinery/gravity_generator/main/proc/change_setting(value)
if(value != setting)
diff --git a/code/modules/projectiles/projectile/energy/net_snare.dm b/code/modules/projectiles/projectile/energy/net_snare.dm
index 04e496aa21786..5a3bdb9df2b5a 100644
--- a/code/modules/projectiles/projectile/energy/net_snare.dm
+++ b/code/modules/projectiles/projectile/energy/net_snare.dm
@@ -83,7 +83,7 @@
new/obj/item/restraints/legcuffs/beartrap/energy(get_turf(loc))
else if(iscarbon(target))
var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy(get_turf(target))
- B.spring_trap(null, target)
+ B.spring_trap(target)
. = ..()
/obj/projectile/energy/trap/on_range()
@@ -104,7 +104,7 @@
qdel(src)
if(iscarbon(target))
var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy/cyborg(get_turf(target))
- B.spring_trap(null, target)
+ B.spring_trap(target)
QDEL_IN(src, 10)
. = ..()
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 0b6df82332fec..b9c133c5fbdb3 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -61,8 +61,8 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/docking_port)
else
return QDEL_HINT_LETMELIVE
-/obj/docking_port/has_gravity(turf/T)
- return FALSE
+/obj/docking_port/has_gravity(turf/current_turf)
+ return TRUE
/obj/docking_port/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, armour_penetration = 0)
return
diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi
index dc9f39b99ec45..13d032eeee32e 100644
Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ