diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index d6f110b0d38..aedaa972ab4 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -540,3 +540,14 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE)))
#define IGNORE_TARGET_LOC_CHANGE (1<<1)
#define IGNORE_HELD_ITEM (1<<2)
#define IGNORE_INCAPACITATED (1<<3)
+
+// Floating status defines. Used for the floating animation loop code.
+///The mob will never ever bob up and down when floating. (eg. Colossus, winged flying humanoids)
+#define NEVER_FLOATING_ANIM -1
+///Isn't currently bobbing.
+#define NO_FLOATING_ANIM 0
+///Is currently bobbing.
+#define HAS_FLOATING_ANIM 1
+///The bobbing animation has been halted and will be updated by a timed callback.
+#define UPDATE_FLOATING_ANIM 2
+
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 3a89453eee9..3621d0c0a8d 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -9,14 +9,14 @@
target.status_traits = list(); \
_L = target.status_traits; \
_L[trait] = list(source); \
- SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait)); \
+ SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
} else { \
_L = target.status_traits; \
if (_L[trait]) { \
_L[trait] |= list(source); \
} else { \
_L[trait] = list(source); \
- SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait)); \
+ SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
} \
} \
} while (0)
@@ -37,7 +37,7 @@
};\
if (!length(_L[trait])) { \
_L -= trait; \
- SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait)); \
+ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
}; \
if (!length(_L)) { \
target.status_traits = null \
@@ -53,7 +53,7 @@
_L[_T] &= _S;\
if (!length(_L[_T])) { \
_L -= _T; \
- SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T)); \
+ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T), _T); \
}; \
};\
if (!length(_L)) { \
@@ -219,6 +219,35 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_KNOW_CYBORG_WIRES "know_cyborg_wires"
#define TRAIT_KNOW_ENGI_WIRES "know_engi_wires"
+///Movement type traits for movables.
+
+/**
+ * Registers movement trait signals on the movable the first time the macro is used on it,
+ * These are necessary to add and remove bit flags, as well as the floating animation.
+ * Overall it's a better alternative than doing so on init for every movable.
+ */
+#define ADD_MOVE_TRAIT(AM, trait, source)\
+ if(!AM.has_movement_type_signals){\
+ if(!GLOB.movement_type_trait_add_signals){\
+ GLOB.movement_type_trait_add_signals = list();\
+ GLOB.movement_type_trait_remove_signals = list();\
+ for(var/_trait in GLOB.movement_type_trait_to_flag){\
+ GLOB.movement_type_trait_add_signals += SIGNAL_ADDTRAIT(_trait);\
+ GLOB.movement_type_trait_remove_signals += SIGNAL_REMOVETRAIT(_trait)\
+ };\
+ };\
+ AM.RegisterSignal(AM, GLOB.movement_type_trait_add_signals, /atom/movable/.proc/on_movement_type_trait_gain);\
+ AM.RegisterSignal(AM, GLOB.movement_type_trait_remove_signals, /atom/movable/.proc/on_movement_type_trait_loss);\
+ AM.has_movement_type_signals = TRUE\
+ };\
+ ADD_TRAIT(AM, trait, source)
+
+#define TRAIT_MOVE_GROUND "move_ground"
+#define TRAIT_MOVE_FLYING "move_flying"
+#define TRAIT_MOVE_VENTCRAWLING "move_ventcrawling"
+#define TRAIT_MOVE_FLOATING "move_floating"
+#define TRAIT_MOVE_UNSTOPPABLE "move_unstoppable"
+
//non-mob traits
/// Used for limb-based paralysis, where replacing the limb will fix it.
#define TRAIT_PARALYSIS "paralysis"
@@ -357,6 +386,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define SLEEPING_CARP_TRAIT "sleeping_carp"
#define MADE_UNCLONEABLE "made-uncloneable"
#define TIMESTOP_TRAIT "timestop"
+#define LIFECANDLE_TRAIT "lifecandle"
+#define VENTCRAWLING_TRAIT "ventcrawling"
+#define SPECIES_FLIGHT_TRAIT "species-flight"
+#define FROSTMINER_ENRAGE_TRAIT "frostminer-enrage"
+#define NO_GRAVITY_TRAIT "no-gravity"
+#define LEAPER_BUBBLE_TRAIT "leaper-bubble"
#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name
#define SKILLCHIP_TRAIT "skillchip"
#define PULLED_WHILE_SOFTCRIT_TRAIT "pulled-while-softcrit"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index b502cfdaa6b..c40146daba9 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -168,6 +168,13 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/atom = list(
"TRAIT_KEEP_TOGETHER" = TRAIT_KEEP_TOGETHER
+ ),
+ /atom/movable = list(
+ "TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND,
+ "TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING,
+ "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
+ "TRAIT_MOVE_FLOATING" = TRAIT_MOVE_FLOATING,
+ "TRAIT_MOVE_UNSTOPPABLE" = TRAIT_MOVE_UNSTOPPABLE
)
))
@@ -180,3 +187,13 @@ GLOBAL_LIST(trait_name_map)
for(var/tname in GLOB.traits_by_type[key])
var/val = GLOB.traits_by_type[key][tname]
.[val] = tname
+
+GLOBAL_LIST_INIT(movement_type_trait_to_flag, list(
+ TRAIT_MOVE_GROUND = GROUND,
+ TRAIT_MOVE_FLYING = FLYING,
+ TRAIT_MOVE_VENTCRAWLING = VENTCRAWLING,
+ TRAIT_MOVE_FLOATING = FLOATING,
+ TRAIT_MOVE_UNSTOPPABLE = UNSTOPPABLE
+ ))
+GLOBAL_LIST(movement_type_trait_add_signals)
+GLOBAL_LIST(movement_type_trait_remove_signals)
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index e5d6b0bbd08..ee6cb105451 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -85,6 +85,7 @@ SUBSYSTEM_DEF(throwing)
/datum/thrownthing/Destroy()
SSthrowing.processing -= thrownthing
+ thrownthing.floating_anim_check()
thrownthing.throwing = null
thrownthing = null
target = null
diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm
index b6275ac7393..bd00761fea8 100644
--- a/code/datums/components/caltrop.dm
+++ b/code/datums/components/caltrop.dm
@@ -29,7 +29,7 @@
return
//move these next two down a level if you add more mobs to this.
- if(H.is_flying() || H.is_floating()) //check if they are able to pass over us
+ if(H.movement_type & (FLOATING|FLYING)) //check if they are able to pass over us
return //gravity checking only our parent would prevent us from triggering they're using magboots / other gravity assisting items that would cause them to still touch us.
if(H.buckled) //if they're buckled to something, that something should be checked instead.
return
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
index e0a96bc6217..481173731f3 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))
+ if(is_type_in_typecache(AM, forbidden_types) || AM.throwing || (AM.movement_type & (FLOATING|FLYING)))
return FALSE
//Flies right over the chasm
if(ismob(AM))
@@ -78,8 +78,6 @@
var/mob/buckled_to = M.buckled
if((!ismob(M.buckled) || (buckled_to.buckled != M)) && !droppable(M.buckled))
return FALSE
- if(M.is_flying())
- return FALSE
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
if(istype(H.belt, /obj/item/wormhole_jaunter))
diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm
index 5f9aa29d083..ce7dc1ed78f 100644
--- a/code/datums/components/slippery.dm
+++ b/code/datums/components/slippery.dm
@@ -18,7 +18,7 @@
SIGNAL_HANDLER
var/mob/victim = AM
- if(istype(victim) && !victim.is_flying() && victim.slip(knockdown_time, parent, lube_flags, paralyze_time, force_drop_items) && callback)
+ if(istype(victim) && !(victim.movement_type & FLYING) && victim.slip(knockdown_time, parent, lube_flags, paralyze_time, force_drop_items) && callback)
callback.Invoke(victim)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 6c83bc2000e..3d5a3f20f28 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -34,8 +34,25 @@
var/list/client_mobs_in_contents // This contains all the client mobs within this container
var/list/acted_explosions //for explosion dodging
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
- ///In case you have multiple types, you automatically use the most useful one. IE: Skating on ice, flippers on water, flying over chasm/space, etc. Should only be changed through setMovetype()
+
+ /**
+ * In case you have multiple types, you automatically use the most useful one.
+ * IE: Skating on ice, flippers on water, flying over chasm/space, etc.
+ * Should be added/removed through the ADD_MOVE_TRAIT and REMOVE_TRAIT (and variant) macros.
+ */
var/movement_type = GROUND
+ /// Whether the movable has movement_type signals registered or not. See the ADD_MOVE_TRAIT macro on __DEFINES/traits.dm
+ var/has_movement_type_signals = FALSE
+ /// Whether the movable is floating, not floating or is queued for update.
+ var/floating_anim_status = NO_FLOATING_ANIM
+ /**
+ * Stores the timer id for the floating anim update.
+ * Used to avoid the timed event from being overridden by others that would run sooner.
+ */
+ var/floating_halt_timerid
+ /// Stores the timer id for the next half of the floating anim loop
+ var/floating_anim_timerid
+
var/atom/movable/pulling
var/grab_state = 0
var/throwforce = 0
@@ -210,6 +227,15 @@
if(NAMEOF(src, glide_size))
set_glide_size(var_value)
. = TRUE
+ if(NAMEOF(src, floating_anim_status))
+ if(var_value != floating_anim_status)
+ switch(var_value)
+ if(HAS_FLOATING_ANIM)
+ floating_anim_status = HAS_FLOATING_ANIM
+ do_floating_anim()
+ else
+ halt_floating_anim(var_value)
+ . = TRUE
if(!isnull(.))
datum_flags |= DF_VAR_EDITED
@@ -593,14 +619,22 @@
var/atom/movable/AM = item
AM.onTransitZ(old_z,new_z)
+/// Called when movement_type trait is added to the mob.
+/atom/movable/proc/on_movement_type_trait_gain(datum/source, trait)
+ SIGNAL_HANDLER
+ var/old_movement_type = movement_type
+ movement_type |= GLOB.movement_type_trait_to_flag[trait]
+ if(!(old_movement_type & (FLOATING|FLYING)) && (trait == TRAIT_MOVE_FLYING || trait == TRAIT_MOVE_FLOATING))
+ floating_anim_check()
-///Proc to modify the movement_type and hook behavior associated with it changing.
-/atom/movable/proc/setMovetype(newval)
- if(movement_type == newval)
- return
- . = movement_type
- movement_type = newval
-
+/// Called when a movement_type trait is removed from the mob.
+/atom/movable/proc/on_movement_type_trait_loss(datum/source, trait)
+ SIGNAL_HANDLER
+ var/flag = GLOB.movement_type_trait_to_flag[trait]
+ if(!(initial(movement_type) & flag))
+ movement_type &= ~(GLOB.movement_type_trait_to_flag[trait])
+ if(trait == TRAIT_MOVE_FLYING || trait == TRAIT_MOVE_FLOATING && !(movement_type & (FLOATING|FLYING)))
+ halt_floating_anim(NO_FLOATING_ANIM)
/**
* Called whenever an object moves and by mobs when they attempt to move themselves through space
@@ -740,6 +774,7 @@
if(pulledby)
pulledby.stop_pulling()
+ halt_floating_anim(NO_FLOATING_ANIM, animate = !spin)
throwing = TT
if(spin)
SpinAnimation(5, 1)
@@ -827,6 +862,7 @@
if(A == src)
return //don't do an animation if attacking self
+ halt_floating_anim(animate = FALSE)
var/pixel_x_diff = 0
var/pixel_y_diff = 0
var/turn_dir = 1
@@ -902,18 +938,40 @@
acted_explosions += ex_id
return TRUE
-//TODO: Better floating
-/atom/movable/proc/float(on)
- if(throwing)
- return
- if(on && !(movement_type & FLOATING))
- animate(src, pixel_y = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
- animate(pixel_y = -2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
- setMovetype(movement_type | FLOATING)
- else if (!on && (movement_type & FLOATING))
- animate(src, pixel_y = base_pixel_y, time = 10)
- setMovetype(movement_type & ~FLOATING)
+///The bouncing animation loop that stops once halt_floating_anim is called.
+/atom/movable/proc/do_floating_anim(shift = 2)
+ if(floating_anim_status == HAS_FLOATING_ANIM)
+ animate(src, pixel_y = pixel_y + shift, time = 1 SECONDS)
+ floating_anim_timerid = addtimer(CALLBACK(src, .proc/do_floating_anim, -shift), 1.1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
+///Restarts the floating animation if conditions are met.
+/atom/movable/proc/floating_anim_check(timed = FALSE)
+ if(timed)
+ floating_halt_timerid = null
+ if(floating_anim_status == HAS_FLOATING_ANIM || floating_anim_status == NEVER_FLOATING_ANIM || floating_halt_timerid)
+ return
+ if(throwing || !(movement_type & (FLOATING|FLYING)))
+ floating_anim_status = NO_FLOATING_ANIM
+ else
+ floating_anim_status = HAS_FLOATING_ANIM
+ do_floating_anim()
+
+/// Stops the floating anim. If the update arg is TRUE, floating_anim_check(TRUE) will be a invoked after a set time indicated by the timer arg.
+/atom/movable/proc/halt_floating_anim(new_status = UPDATE_FLOATING_ANIM, timer = 1 SECONDS, animate = TRUE)
+ if(floating_anim_status == HAS_FLOATING_ANIM)
+ if(animate)
+ animate(src, pixel_y = base_pixel_y, time = 1 SECONDS)
+ else
+ pixel_y = base_pixel_y
+ if(new_status == UPDATE_FLOATING_ANIM)
+ if(!floating_halt_timerid || timeleft(floating_halt_timerid) < timer)
+ floating_halt_timerid = addtimer(CALLBACK(src, .proc/floating_anim_check, TRUE), timer, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE|TIMER_NO_HASH_WAIT)
+ else if(floating_anim_timerid)
+ deltimer(floating_anim_timerid)
+ floating_anim_timerid = null
+
+ if(floating_anim_status != NEVER_FLOATING_ANIM)
+ floating_anim_status = new_status
/* Language procs
* Unless you are doing something very specific, these are the ones you want to use.
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 5187ca21789..10029c7318a 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -100,6 +100,8 @@
if(!check_loc && M.loc != loc)
M.forceMove(loc)
+ if(anchored)
+ M.halt_floating_anim(NO_FLOATING_ANIM)
M.set_buckled(src)
M.setDir(dir)
buckled_mobs |= M
@@ -138,6 +140,8 @@
buckled_mob.clear_alert("buckled")
buckled_mob.set_glide_size(DELAY_TO_GLIDE_SIZE(buckled_mob.total_multiplicative_slowdown()))
buckled_mobs -= buckled_mob
+ if(anchored)
+ buckled_mob.floating_anim_check()
SEND_SIGNAL(src, COMSIG_MOVABLE_UNBUCKLE, buckled_mob, force)
post_unbuckle_mob(.)
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 0470fdcca56..63b5a209cc0 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -355,7 +355,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
/obj/item/shard/Crossed(atom/movable/AM)
if(isliving(AM))
var/mob/living/L = AM
- if(!(L.is_flying() || L.is_floating() || L.buckled))
+ if(!(L.movement_type & (FLYING|FLOATING)) || L.buckled)
playsound(src, 'sound/effects/glass_step.ogg', HAS_TRAIT(L, TRAIT_LIGHT_STEP) ? 30 : 50, TRUE)
return ..()
diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm
index 3d4e0307ece..0fd34857efc 100644
--- a/code/game/objects/items/tanks/jetpack.dm
+++ b/code/game/objects/items/tanks/jetpack.dm
@@ -78,7 +78,7 @@
return
if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet
return
- if(!(user.is_flying() || user.is_floating()) || user.buckled)//You don't want use jet in gravity or while buckled.
+ if(!(user.movement_type & FLOATING) || user.buckled)//You don't want use jet in gravity or while buckled.
return
if(user.pulledby)//You don't must use jet if someone pull you
return
diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm
index 3a0cb6bd6db..d4ef0b6c3e3 100644
--- a/code/game/objects/structures/life_candle.dm
+++ b/code/game/objects/structures/life_candle.dm
@@ -30,15 +30,19 @@
return
if(!user.mind)
return
+ var/old_len = linked_minds.len
if(user.mind in linked_minds)
user.visible_message("[user] reaches out and pinches the flame of [src].", "You sever the connection between yourself and [src].")
linked_minds -= user.mind
+ if(old_len)
+ REMOVE_TRAIT(src, TRAIT_MOVE_FLOATING, LIFECANDLE_TRAIT)
else
user.visible_message("[user] touches [src]. It seems to respond to [user.p_their()] presence!", "You create a connection between you and [src].")
linked_minds |= user.mind
+ if(!old_len)
+ ADD_MOVE_TRAIT(src, TRAIT_MOVE_FLOATING, LIFECANDLE_TRAIT)
update_icon()
- float(linked_minds.len)
if(linked_minds.len)
START_PROCESSING(SSobj, src)
set_light(lit_luminosity)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 2a89c6c2342..97d09c63d9b 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1475,7 +1475,11 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/source = "adminabuse"
switch(add_or_remove)
if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it.
- ADD_TRAIT(D,chosen_trait,source)
+ if(GLOB.movement_type_trait_to_flag[chosen_trait]) //big damn exception.
+ var/atom/movable/AM = D
+ ADD_MOVE_TRAIT(AM, chosen_trait,source)
+ else
+ ADD_TRAIT(D,chosen_trait,source)
if("Remove")
var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific")
if(!specific)
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 3c8e4fc0f57..8c5543d05e5 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -375,7 +375,6 @@
custom_premium_price = PAYCHECK_EASY * 1.6
custom_price = PAYCHECK_EASY * 1.6
-
/obj/item/clothing/shoes/kindle_kicks
name = "Kindle Kicks"
desc = "They'll sure kindle something in you, and it's not childhood nostalgia..."
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
index 6c004bbc074..de9b8968e08 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
@@ -86,9 +86,3 @@
visible_message("[src] smashes into [hit_atom]!", "[src] smashes into [hit_atom]!")
Paralyze(40, ignore_canstun = TRUE)
-/mob/living/carbon/alien/humanoid/float(on)
- if(leaping)
- return
- ..()
-
-
diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm
index 7f65b79ddca..a4c53b77053 100644
--- a/code/modules/mob/living/carbon/carbon_movement.dm
+++ b/code/modules/mob/living/carbon/carbon_movement.dm
@@ -59,17 +59,18 @@
if(!usable_legs && !(movement_type & (FLYING | FLOATING)))
ADD_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
-
-/mob/living/carbon/setMovetype(newval)
+/// Called when movement_type trait is added to the mob.
+/mob/living/carbon/on_movement_type_trait_gain(datum/source, trait)
+ var/old_movetype = movement_type
. = ..()
- if(isnull(.))
- return
- if(!(. & (FLYING | FLOATING)))
- if(movement_type & (FLYING | FLOATING)) //From not flying to flying.
- remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
- REMOVE_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
- else if(!(movement_type & (FLYING | FLOATING))) //From flying to no longer flying.
+ if((trait == TRAIT_MOVE_FLOATING || trait == TRAIT_MOVE_FLYING) && !(old_movetype & (FLYING | FLOATING)))
+ remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
+ REMOVE_TRAIT(src, TRAIT_FLOORED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
+ REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LACKING_LOCOMOTION_APPENDAGES_TRAIT)
+
+/mob/living/carbon/on_movement_type_trait_loss(datum/source, trait)
+ . = ..()
+ if((trait == TRAIT_MOVE_FLOATING || trait == TRAIT_MOVE_FLYING) && !(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/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index b44b7ed2683..859b5432b87 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -21,8 +21,8 @@
resize = RESIZE_DEFAULT_SIZE
if(changed)
+ halt_floating_anim(animate = FALSE)
animate(src, transform = ntransform, time = (lying_prev == 0 || lying_angle == 0) ? 2 : 0, pixel_y = final_pixel_y, dir = final_dir, easing = (EASE_IN|EASE_OUT))
- setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
/mob/living/carbon
var/list/overlays_standing[TOTAL_LAYERS]
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index c0f4a19ac18..53cd91f3c8c 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -901,11 +901,6 @@
/mob/living/carbon/human/is_literate()
return TRUE
-/mob/living/carbon/human/update_gravity(has_gravity,override = 0)
- if(dna?.species) //prevents a runtime while a human is being monkeyfied
- override = dna.species.override_float
- ..()
-
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
if(blood && (NOBLOOD in dna.species.species_traits) && !HAS_TRAIT(src, TRAIT_TOXINLOVER))
if(message)
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 3de8c9d5fed..459b0f2de1e 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -168,8 +168,6 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/obj/item/organ/appendix/mutantappendix = /obj/item/organ/appendix
///Forces an item into this species' hands. Only an honorary mutantthing because this is not an organ and not loaded in the same way, you've been warned to do your research.
var/obj/item/mutanthands
- ///Allows the species to not give a single F about gravity. Used by wings.
- var/override_float = FALSE
///Bitflag that controls what in game ways something can select this species as a spawnable source, such as magic mirrors. See [mob defines][code/__DEFINES/mobs.dm] for possible sources.
var/changesource_flags = NONE
@@ -2080,18 +2078,19 @@ GLOBAL_LIST_EMPTY(roundstart_races)
//UNSAFE PROC, should only be called through the Activate or other sources that check for CanFly
/datum/species/proc/ToggleFlight(mob/living/carbon/human/H)
- if(!(H.movement_type & FLYING))
+ if(!HAS_TRAIT_FROM(H, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT))
stunmod *= 2
speedmod -= 0.35
- H.setMovetype(H.movement_type | FLYING)
- override_float = TRUE
+ H.halt_floating_anim(NEVER_FLOATING_ANIM)
+ ADD_MOVE_TRAIT(H, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT)
passtable_on(H, SPECIES_TRAIT)
H.OpenWings()
else
stunmod *= 0.5
speedmod += 0.35
- H.setMovetype(H.movement_type & ~FLYING)
- override_float = FALSE
+ H.floating_anim_status = initial(H.floating_anim_status)
+ H.floating_anim_check()
+ REMOVE_TRAIT(H, TRAIT_MOVE_FLYING, SPECIES_FLIGHT_TRAIT)
passtable_off(H, SPECIES_TRAIT)
H.CloseWings()
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index da74b491ebc..0a99e840305 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -4,9 +4,6 @@
/mob/living/proc/Life(times_fired)
set waitfor = FALSE
- if((movement_type & FLYING) && !(movement_type & FLOATING)) //TODO: Better floating
- float(on = TRUE)
-
if (client)
var/turf/T = get_turf(src)
if(!T)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 50168b3b8a5..4a71393d5ba 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -9,6 +9,8 @@
diag_hud.add_to_hud(src)
faction += "[REF(src)]"
GLOB.mob_living_list += src
+ if(movement_type & (FLYING|FLOATING) && floating_anim_status != NEVER_FLOATING_ANIM)
+ floating_anim_check()
/mob/living/prepare_huds()
..()
@@ -942,10 +944,11 @@
/mob/living/proc/get_visible_name()
return name
-/mob/living/update_gravity(has_gravity, override)
+/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")
@@ -954,24 +957,32 @@
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)
else
throw_alert("gravity", /atom/movable/screen/alert/weightless)
- if(!override && !is_flying())
- float(!has_gravity)
+ if(!was_weightless)
+ ADD_MOVE_TRAIT(src, TRAIT_MOVE_FLOATING, NO_GRAVITY_TRAIT)
-/mob/living/float(on)
- if(throwing)
+/mob/living/floating_anim_check(timed = FALSE)
+ if(timed)
+ floating_halt_timerid = null
+ if(floating_anim_status == HAS_FLOATING_ANIM || floating_anim_status == NEVER_FLOATING_ANIM || floating_halt_timerid)
return
- var/fixed = 0
- if(anchored || (buckled?.anchored))
- fixed = 1
- if(on && !(movement_type & FLOATING) && !fixed)
- animate(src, pixel_y = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
- animate(pixel_y = -2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
- setMovetype(movement_type | FLOATING)
- else if(((!on || fixed) && (movement_type & FLOATING)))
- animate(src, pixel_y = base_pixel_y + body_position_pixel_y_offset, time = 1 SECONDS)
- setMovetype(movement_type & ~FLOATING)
+ if(throwing || !(movement_type & (FLOATING|FLYING)) || buckled?.anchored)
+ floating_anim_status = NO_FLOATING_ANIM
+ else
+ floating_anim_status = HAS_FLOATING_ANIM
+ do_floating_anim()
+
+/mob/living/halt_floating_anim(new_status = UPDATE_FLOATING_ANIM, timer = 1 SECONDS, animate = TRUE)
+ if(floating_anim_status == HAS_FLOATING_ANIM)
+ if(animate)
+ animate(src, pixel_y = base_pixel_y + body_position_pixel_y_offset, time = 1 SECONDS)
+ else
+ pixel_y = base_pixel_y + body_position_pixel_y_offset
+ floating_anim_status = NO_FLOATING_ANIM //only stops the parent call from affecting nullifying our pixel y offset.
+ ..()
// 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)
@@ -1062,6 +1073,7 @@
step_towards(src,S)
/mob/living/proc/do_jitter_animation(jitteriness)
+ halt_floating_anim(TRUE, 1.5 SECONDS, FALSE) //the time of the jitter animation plus 0.1
var/amplitude = min(4, (jitteriness/100) + 1)
var/pixel_x_diff = rand(-amplitude, amplitude)
var/pixel_y_diff = rand(-amplitude/3, amplitude/3)
@@ -1069,7 +1081,6 @@
var/final_pixel_y = base_pixel_y + body_position_pixel_y_offset
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff , time = 2, loop = 6)
animate(pixel_x = final_pixel_x , pixel_y = final_pixel_y , time = 2)
- setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure to restart it in next life().
/mob/living/proc/get_temperature(datum/gas_mixture/environment)
var/loc_temp = environment ? environment.temperature : T0C
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index bdde3a402ee..afa1058ee8c 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -401,7 +401,6 @@
if(!used_item)
used_item = get_active_held_item()
..()
- setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
/**
* Does a slap animation on an atom
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
index cb5d9e39b82..dc0ce6b72b9 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -81,8 +81,8 @@
/obj/structure/leaper_bubble/Initialize()
. = ..()
- INVOKE_ASYNC(src, /atom/movable.proc/float, TRUE)
QDEL_IN(src, 100)
+ ADD_MOVE_TRAIT(src, TRAIT_MOVE_FLOATING, LEAPER_BUBBLE_TRAIT)
/obj/structure/leaper_bubble/Destroy()
new /obj/effect/temp_visual/leaper_projectile_impact(get_turf(src))
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 1d86c07b2c3..5ef26fb72fd 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -42,6 +42,7 @@
ranged = TRUE
pixel_x = -32
base_pixel_x = -32
+ floating_anim_status = NEVER_FLOATING_ANIM //we don't want this guy to float, messes up his animations
del_on_death = TRUE
gps_name = "Angelic Signal"
achievement_type = /datum/award/achievement/boss/colossus_kill
@@ -250,14 +251,6 @@
AT.pixel_y += random_y
return ..()
-/mob/living/simple_animal/hostile/megafauna/colossus/float(on) //we don't want this guy to float, messes up his animations
- if(throwing)
- return
- if(on && !(movement_type & FLOATING))
- setMovetype(movement_type | FLOATING)
- else if(!on && (movement_type & FLOATING))
- setMovetype(movement_type & ~FLOATING)
-
/obj/projectile/colossus
name ="death bolt"
icon_state= "chronobolt"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm
index ad6d89ee28d..4dd2f2d0174 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm
@@ -256,7 +256,7 @@ Difficulty: Extremely Hard
for(var/mob/living/L in viewers(src))
shake_camera(L, 3, 2)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
- setMovetype(movement_type | FLYING)
+ ADD_MOVE_TRAIT(src, TRAIT_MOVE_FLYING, FROSTMINER_ENRAGE_TRAIT)
enraging = FALSE
adjustHealth(-maxHealth)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index e4f244777ef..397808a0849 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -449,7 +449,9 @@
new i(loc)
/mob/living/simple_animal/death(gibbed)
- movement_type &= ~FLYING
+ if(!HAS_TRAIT(src, TRAIT_MOVE_FLYING)) //Has no extrinsic source of flight
+ movement_type &= ~FLYING
+ halt_floating_anim(NO_FLOATING_ANIM)
if(nest)
nest.spawned_mobs -= src
nest = null
@@ -507,7 +509,9 @@
icon = initial(icon)
icon_state = icon_living
density = initial(density)
- setMovetype(initial(movement_type))
+ if(initial(movement_type) & (FLYING)) //regain its intrisic flight
+ movement_type |= FLYING
+ floating_anim_check()
/mob/living/simple_animal/proc/make_babies() // <3 <3 <3
diff --git a/code/modules/mob/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm
index 4585247d389..60dfe364c50 100644
--- a/code/modules/mob/living/ventcrawling.dm
+++ b/code/modules/mob/living/ventcrawling.dm
@@ -93,15 +93,14 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, typecacheof(list(
A.pipe_vision_img.plane = ABOVE_HUD_PLANE
client.images += A.pipe_vision_img
pipes_shown += A.pipe_vision_img
- setMovetype(movement_type | VENTCRAWLING)
-
+ ADD_MOVE_TRAIT(src, TRAIT_MOVE_VENTCRAWLING, VENTCRAWLING_TRAIT)
/mob/living/proc/remove_ventcrawl()
if(client)
for(var/image/current_image in pipes_shown)
client.images -= current_image
pipes_shown.len = 0
- setMovetype(movement_type & ~VENTCRAWLING)
+ REMOVE_TRAIT(src, TRAIT_MOVE_VENTCRAWLING, VENTCRAWLING_TRAIT)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 550e57e4051..35b8e591ba6 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1291,13 +1291,13 @@
/mob/proc/set_nutrition(change) //Seriously fuck you oldcoders.
nutrition = max(0, change)
-
-/mob/setMovetype(newval) //Set the movement type of the mob and update it's movespeed
+/mob/on_movement_type_trait_gain(datum/source, trait)
. = ..()
- if(isnull(.))
- return
update_movespeed(FALSE)
+/mob/on_movement_type_trait_loss(datum/source, trait)
+ . = ..()
+ update_movespeed(FALSE)
/mob/proc/update_equipment_speed_mods()
var/speedies = equipped_speed_mods()
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 12fb1279b47..4402ea86c5c 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -441,14 +441,6 @@
message_admins("No ghosts were willing to take control of [ADMIN_LOOKUPFLW(M)])")
return FALSE
-///Is the mob a flying mob
-/mob/proc/is_flying()
- return (movement_type & FLYING)
-
-///Is the mob a floating mob
-/mob/proc/is_floating()
- return (movement_type & FLOATING)
-
///Clicks a random nearby mob with the source from this mob
/mob/proc/click_random_mob()
var/list/nearby_mobs = list()
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 564173d14ee..1de54cdea32 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -898,7 +898,7 @@
if(!isliving(AM))
return
var/mob/living/L = AM
- if(istype(L) && !(L.is_flying() || L.is_floating() || L.buckled))
+ if(!(L.movement_type & (FLYING|FLOATING)) || L.buckled)
playsound(src, 'sound/effects/glass_step.ogg', HAS_TRAIT(L, TRAIT_LIGHT_STEP) ? 30 : 50, TRUE)
if(status == LIGHT_BURNED || status == LIGHT_OK)
shatter()
diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm
index f2f2b1ec6ed..67175891ff1 100644
--- a/code/modules/surgery/organs/augments_chest.dm
+++ b/code/modules/surgery/organs/augments_chest.dm
@@ -181,7 +181,7 @@
return
if(!isturf(owner.loc))//You can't use jet in nowhere or in mecha/closet
return
- if(!(owner.is_flying() || owner.is_floating()) || owner.buckled)//You don't want use jet in gravity or while buckled.
+ if(!(owner.movement_type & FLOATING) || owner.buckled)//You don't want use jet in gravity or while buckled.
return
if(owner.pulledby)//You don't must use jet if someone pull you
return