diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 69af372eb87..3428e73a600 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -357,6 +357,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Stops all slipping and sliding from occurring
#define TRAIT_NO_SLIP_ALL "noslip_all"
+/// Stops the mob from leaving prints in the snow
+#define TRAIT_NO_SNOWPRINTS "no_snowprints"
+
/// Unlinks gliding from movement speed, meaning that there will be a delay between movements rather than a single move movement between tiles
#define TRAIT_NO_GLIDE "no_glide"
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 0e3607877a3..813d7d13e9e 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -457,6 +457,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_SLIP_ICE" = TRAIT_NO_SLIP_ICE,
"TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE,
"TRAIT_NO_SLIP_WATER" = TRAIT_NO_SLIP_WATER,
+ "TRAIT_NO_SNOWPRINTS" = TRAIT_NO_SNOWPRINTS,
"TRAIT_NO_SOUL" = TRAIT_NO_SOUL,
"TRAIT_NO_SPLIT_PERSONALITY" = TRAIT_NO_SPLIT_PERSONALITY,
"TRAIT_NO_STAGGER" = TRAIT_NO_STAGGER,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index c2c929746f4..0b1e2f2ba84 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -205,6 +205,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_NO_SLIP_ICE" = TRAIT_NO_SLIP_ICE,
"TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE,
"TRAIT_NO_SLIP_WATER" = TRAIT_NO_SLIP_WATER,
+ "TRAIT_NO_SNOWPRINTS" = TRAIT_NO_SNOWPRINTS,
"TRAIT_NO_SOUL" = TRAIT_NO_SOUL,
"TRAIT_NO_SPLIT_PERSONALITY" = TRAIT_NO_SPLIT_PERSONALITY,
"TRAIT_NO_STAGGER" = TRAIT_NO_STAGGER,
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index 9948c3ea26c..9a2f293bbde 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -26,6 +26,17 @@
/// Custom destination for mirages
var/destination_y
+ /// If TRUE, humans walking in and out of this turf will leave behind footprints, applied as a generic overlay
+ var/leave_footprints = FALSE
+ /// Lazylist of all shoe types that have walked over this turf
+ VAR_PRIVATE/list/footprint_shoe_types
+ /// Lazylist of all species that have walked over this turf
+ VAR_PRIVATE/list/footprint_species_types
+ /// All dirs from which footprints have entered this turf
+ VAR_PRIVATE/footprint_entrance_dirs = NONE
+ /// All dirs from which footprints have exited this turf
+ VAR_PRIVATE/footprint_exit_dirs = NONE
+
/// Returns a list of every turf state considered "broken".
/// Will be randomly chosen if a turf breaks at runtime.
/turf/open/proc/broken_states()
@@ -40,50 +51,100 @@
if(isnull(damaged_dmi) || broken)
return FALSE
broken = TRUE
- update_appearance()
+ if(leave_footprints)
+ clear_footprints() // calls update_appearance() for us
+ else
+ update_appearance()
return TRUE
/turf/open/burn_tile()
if(isnull(damaged_dmi) || burnt)
return FALSE
burnt = TRUE
- update_appearance()
+ if(leave_footprints)
+ clear_footprints() // calls update_appearance() for us
+ else
+ update_appearance()
return TRUE
/turf/open/update_overlays()
- if(isnull(damaged_dmi))
- return ..()
-
. = ..()
-
if(broken)
- var/mutable_appearance/broken_appearance = mutable_appearance(damaged_dmi, pick(broken_states()))
+ if(isnull(damaged_dmi))
+ stack_trace("[type] has broken set to TRUE but doesn't have a damaged_dmi set.")
+ else
+ var/mutable_appearance/broken_appearance = mutable_appearance(damaged_dmi, pick(broken_states()))
- if(smoothing_flags && !smooth_broken)
- var/matrix/translation = new
- translation.Translate(-LARGE_TURF_SMOOTHING_X_OFFSET, -LARGE_TURF_SMOOTHING_Y_OFFSET)
- broken_appearance.transform = translation
+ if(smoothing_flags && !smooth_broken)
+ var/matrix/translation = new
+ translation.Translate(-LARGE_TURF_SMOOTHING_X_OFFSET, -LARGE_TURF_SMOOTHING_Y_OFFSET)
+ broken_appearance.transform = translation
- . += broken_appearance
+ . += broken_appearance
else if(burnt)
- var/list/burnt_states = burnt_states()
- var/mutable_appearance/burnt_appearance
- if(burnt_states.len)
- burnt_appearance = mutable_appearance(damaged_dmi, pick(burnt_states))
+ if(isnull(damaged_dmi))
+ stack_trace("[type] has burnt set to TRUE but doesn't have a damaged_dmi set.")
else
- burnt_appearance = mutable_appearance(damaged_dmi, pick(broken_states()))
+ var/list/burnt_states = burnt_states()
+ var/mutable_appearance/burnt_appearance
+ if(burnt_states.len)
+ burnt_appearance = mutable_appearance(damaged_dmi, pick(burnt_states))
+ else
+ burnt_appearance = mutable_appearance(damaged_dmi, pick(broken_states()))
- if(smoothing_flags && !smooth_burnt)
- var/matrix/translation = new
- translation.Translate(-LARGE_TURF_SMOOTHING_X_OFFSET, -LARGE_TURF_SMOOTHING_Y_OFFSET)
- burnt_appearance.transform = translation
+ if(smoothing_flags && !smooth_burnt)
+ var/matrix/translation = new
+ translation.Translate(-LARGE_TURF_SMOOTHING_X_OFFSET, -LARGE_TURF_SMOOTHING_Y_OFFSET)
+ burnt_appearance.transform = translation
- . += burnt_appearance
+ . += burnt_appearance
+
+ if(leave_footprints && (footprint_entrance_dirs || footprint_exit_dirs))
+ var/static/list/footprint_cache = list()
+
+ var/icon_state_to_use = "footprint"
+ if(LAZYACCESS(footprint_species_types, BODYPART_ID_DIGITIGRADE))
+ icon_state_to_use += "_claw"
+ else if(LAZYACCESS(footprint_species_types, SPECIES_MONKEY))
+ icon_state_to_use += "_paw"
+
+ for(var/print_dir in GLOB.cardinals)
+ if(footprint_entrance_dirs & print_dir)
+ var/enter_state = "entered-[icon_state_to_use]-[print_dir]"
+ var/image/enter_overlay = footprint_cache[enter_state]
+ if(!enter_overlay)
+ enter_overlay = image('icons/effects/footprints.dmi', "[icon_state_to_use]1", dir = print_dir)
+ enter_overlay.color = COLOR_DARK
+ enter_overlay.alpha *= 0.2
+ footprint_cache[enter_state] = enter_overlay
+ . += enter_overlay
+
+ if(footprint_exit_dirs & print_dir)
+ var/exit_state = "exited-[icon_state_to_use]-[print_dir]"
+ var/image/exit_overlay = footprint_cache[exit_state]
+ if(!exit_overlay)
+ exit_overlay = image('icons/effects/footprints.dmi', "[icon_state_to_use]2", dir = print_dir)
+ exit_overlay.color = COLOR_DARK
+ exit_overlay.alpha *= 0.2
+ footprint_cache[exit_state] = exit_overlay
+ . += exit_overlay
/turf/open/examine_descriptor(mob/user)
return "floor"
+/turf/open/examine(mob/user)
+ . = ..()
+ if(leave_footprints && (footprint_entrance_dirs || footprint_exit_dirs) && (LAZYLEN(footprint_shoe_types) || LAZYLEN(footprint_species_types)))
+ . += "You recognise the footprints as belonging to:"
+ for(var/obj/item/clothing/shoes/sole as anything in footprint_shoe_types)
+ var/article = initial(sole.article) || (initial(sole.gender) == PLURAL ? "Some" : "A")
+ . += "[icon2html(initial(sole.icon), user, initial(sole.icon_state))] [article] [initial(sole.name)]."
+
+ for(var/species in footprint_species_types)
+ var/datum/species/species_type = GLOB.species_list[species]
+ . += "• Some [species_type ? format_text(species_type::plural_form) : "unknown"] feet."
+
//direction is direction of travel of A
/turf/open/zPassIn(direction)
if(direction != DOWN)
@@ -130,11 +191,46 @@
if(destination_x || destination_y || destination_z)
return TRUE
+/// Add the passed mob's footprint to the turf with the given movement direction
+/turf/open/proc/add_footprint(mob/living/carbon/human/walker, movement_direction)
+ if(walker.body_position != STANDING_UP || walker.buckled || (walker.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
+ return
+
+ if(walker.loc == src)
+ footprint_entrance_dirs |= movement_direction
+ else
+ footprint_exit_dirs |= movement_direction
+
+ if(walker.shoes)
+ LAZYOR(footprint_shoe_types, walker.shoes.type)
+
+ else
+ // we can't actually get which leg is the "front foot" so we'll just guess
+ var/front_foot = prob(50) ? BODY_ZONE_R_LEG : BODY_ZONE_L_LEG
+ var/backup_foot = front_foot == BODY_ZONE_R_LEG ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG
+ var/obj/item/bodypart/leg/walking_on = walker.get_bodypart(front_foot) || walker.get_bodypart(backup_foot)
+ if(isnull(walking_on))
+ return
+ LAZYOR(footprint_species_types, walking_on.limb_id)
+
+ update_appearance()
+
+/// Clear all footprints on the turf
+/turf/open/proc/clear_footprints()
+ footprint_entrance_dirs = NONE
+ footprint_exit_dirs = NONE
+ LAZYNULL(footprint_shoe_types)
+ LAZYNULL(footprint_species_types)
+ update_appearance()
+
/turf/open/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if(!arrived || src != arrived.loc)
return
+ if(old_loc && leave_footprints && !broken && !burnt && ishuman(arrived))
+ add_footprint(arrived, get_dir(old_loc, src))
+
if(!destination_z || !destination_x || !destination_y || arrived.pulledby || arrived.currently_z_moving)
return
@@ -166,6 +262,12 @@
var/turf/target_turf = get_step(current_pull.pulledby.loc, REVERSE_DIR(current_pull.pulledby.dir)) || current_pull.pulledby.loc
current_pull.zMove(null, target_turf, ZMOVE_ALLOW_BUCKLED)
current_pull = current_pull.pulling
+
+/turf/open/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone && direction && leave_footprints && !broken && !burnt && isturf(gone.loc) && ishuman(gone))
+ add_footprint(gone, direction)
+
/**
* Replace an open turf with another open turf while avoiding the pitfall of replacing plating with a floor tile, leaving a hole underneath.
* This replaces the current turf if it is plating and is passed plating, is tile and is passed tile.
diff --git a/code/game/turfs/open/asteroid.dm b/code/game/turfs/open/asteroid.dm
index 4e131d0bf98..9675f0c45d3 100644
--- a/code/game/turfs/open/asteroid.dm
+++ b/code/game/turfs/open/asteroid.dm
@@ -1,5 +1,7 @@
/**********************Asteroid**************************/
+#define DIG_SHEET_AMOUNT 5
+
/turf/open/misc/asteroid //floor piece
gender = PLURAL
name = "asteroid sand"
@@ -86,14 +88,12 @@
/// Drops itemstack when dug and changes icon
/turf/open/misc/asteroid/proc/getDug()
- if(dug || broken)
- return
- dug = TRUE
- broken = TRUE
- new dig_result(src, 5)
+ if(!break_tile())
+ return FALSE
+ new dig_result(src, DIG_SHEET_AMOUNT)
if(prob(worm_chance))
new /obj/item/food/bait/worm(src)
- update_appearance()
+ return TRUE
/// If the user can dig the turf
/turf/open/misc/asteroid/proc/can_dig(mob/user)
@@ -101,6 +101,7 @@
return TRUE
if(user)
balloon_alert(user, "already excavated!")
+ return FALSE
///Refills the previously dug tile
/turf/open/misc/asteroid/proc/refill_dug()
@@ -141,9 +142,11 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
dig_result = /obj/item/stack/ore/glass/basalt
/turf/open/misc/asteroid/basalt/getDug()
+ . = ..()
+ if(!.)
+ return
set_light(0)
GLOB.dug_up_basalt |= src
- return ..()
/turf/open/misc/asteroid/basalt/Destroy()
GLOB.dug_up_basalt -= src
@@ -214,6 +217,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
bullet_sizzle = TRUE
bullet_bounce_sound = null
dig_result = /obj/item/stack/sheet/mineral/snow
+ leave_footprints = TRUE
/turf/open/misc/asteroid/snow/burn_tile()
if(!burnt)
@@ -227,6 +231,67 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
/turf/open/misc/asteroid/snow/burnt_states()
return list("snow_dug")
+/turf/open/misc/asteroid/snow/add_footprint(mob/living/carbon/human/walker, movement_direction)
+ if(HAS_TRAIT(walker, TRAIT_NO_SNOWPRINTS))
+ return
+ // skip the special logic if the level doesn't naturally have snowstorms
+ if(!SSmapping.level_trait(z, ZTRAIT_SNOWSTORM))
+ return ..()
+
+ // if an active snow storm affecting this turf is currently in its main or wind down stage, skip footprint creation
+ for(var/datum/weather/snow_storm/active_weather in SSweather.processing)
+ if(active_weather.stage != MAIN_STAGE && active_weather.stage != WIND_DOWN_STAGE)
+ continue
+ if(!(loc in active_weather.impacted_areas))
+ continue
+ return
+
+ . = ..()
+ // when a snow storm enters its main stage, clear all of our footprints
+ for(var/snow_type in typesof(/datum/weather/snow_storm))
+ RegisterSignal(SSdcs, COMSIG_WEATHER_START(snow_type), PROC_REF(snow_clear_footprints), override = TRUE)
+
+/turf/open/misc/asteroid/snow/proc/snow_clear_footprints(datum/source, datum/weather/storm)
+ SIGNAL_HANDLER
+
+ if(!(loc in storm.impacted_areas))
+ return
+
+ clear_footprints()
+ for(var/snow_type in typesof(/datum/weather/snow_storm))
+ UnregisterSignal(SSdcs, COMSIG_WEATHER_START(snow_type))
+
+/turf/open/misc/asteroid/snow/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/stack/sheet/mineral/snow))
+ return ..()
+
+ if(dug)
+ if(tool.use(DIG_SHEET_AMOUNT))
+ user.visible_message(
+ span_notice("[user] packs [src] back in."),
+ span_notice("You pack [src] back in."),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
+ refill_dug()
+ return ITEM_INTERACT_SUCCESS
+
+ to_chat(user, "You don't have enough [tool.name] to fill the hole.")
+ return ITEM_INTERACT_BLOCKING
+
+ if(footprint_entrance_dirs || footprint_exit_dirs)
+ if(tool.use(1))
+ user.visible_message(
+ span_notice("[user] fills in the footprints in [src]."),
+ span_notice("You fill in the footprints in [src]."),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
+ clear_footprints()
+ return ITEM_INTERACT_SUCCESS
+
+ return NONE
+
+ return NONE
+
/turf/open/misc/asteroid/snow/icemoon
baseturfs = /turf/open/openspace/icemoon
initial_gas_mix = ICEMOON_DEFAULT_ATMOS
@@ -259,6 +324,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
clawfootstep = FOOTSTEP_HARD_CLAW
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
damaged_dmi = null
+ leave_footprints = FALSE
/turf/open/misc/asteroid/snow/ice/break_tile()
return FALSE
@@ -327,3 +393,4 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
initial_gas_mix = "co2=173.4;n2=135.1;plasma=229.8;TEMP=351.9"
planetary_atmos = TRUE
+#undef DIG_SHEET_AMOUNT
diff --git a/code/game/turfs/open/floor/plating/misc_plating.dm b/code/game/turfs/open/floor/plating/misc_plating.dm
index 2b6fe12a158..750f47ecf11 100644
--- a/code/game/turfs/open/floor/plating/misc_plating.dm
+++ b/code/game/turfs/open/floor/plating/misc_plating.dm
@@ -20,14 +20,14 @@
base_icon_state = "alienpod1"
tiled_turf = FALSE
rust_resistance = RUST_RESISTANCE_ORGANIC
- // Not actually broken, just should never break...yeah.
- broken = TRUE
- damaged_dmi = null
/turf/open/floor/plating/abductor/Initialize(mapload)
. = ..()
icon_state = "alienpod[rand(1,9)]"
+/turf/open/floor/plating/abductor/break_tile()
+ return FALSE
+
/turf/open/floor/plating/abductor2
name = "alien plating"
icon_state = "alienplating"
diff --git a/code/game/turfs/open/sand.dm b/code/game/turfs/open/sand.dm
index 71ffa6be613..c57d9b047bd 100644
--- a/code/game/turfs/open/sand.dm
+++ b/code/game/turfs/open/sand.dm
@@ -9,6 +9,7 @@
clawfootstep = FOOTSTEP_SAND
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
rust_resistance = RUST_RESISTANCE_REINFORCED
+ leave_footprints = TRUE
/turf/open/misc/beach/Initialize(mapload)
. = ..()
@@ -19,6 +20,33 @@
GLOB.preset_fish_sources[fish_source].spawn_reward_from_explosion(src, severity)
return FALSE
+/turf/open/misc/beach/add_footprint(mob/living/carbon/human/walker, movement_direction)
+ if(!SSmapping.level_trait(z, ZTRAIT_SANDSTORM))
+ return ..()
+
+ // if an active sand storm affecting this turf is currently in its main or wind down stage, skip footprint creation
+ for(var/datum/weather/sand_storm/active_weather in SSweather.processing)
+ if(active_weather.stage != MAIN_STAGE && active_weather.stage != WIND_DOWN_STAGE)
+ continue
+ if(!(loc in active_weather.impacted_areas))
+ continue
+ return
+
+ . = ..()
+ // when a sand storm enters its main stage, clear all of our footprints
+ for(var/sand_type in typesof(/datum/weather/sand_storm))
+ RegisterSignal(SSdcs, COMSIG_WEATHER_START(sand_type), PROC_REF(sand_clear_footprints), override = TRUE)
+
+/turf/open/misc/beach/proc/sand_clear_footprints(datum/source, datum/weather/storm)
+ SIGNAL_HANDLER
+
+ if(!(loc in storm.impacted_areas))
+ return
+
+ clear_footprints()
+ for(var/sand_type in typesof(/datum/weather/sand_storm))
+ UnregisterSignal(SSdcs, COMSIG_WEATHER_START(sand_type))
+
/turf/open/misc/beach/sand
gender = PLURAL
name = "sand"
diff --git a/code/game/turfs/open/snow.dm b/code/game/turfs/open/snow.dm
index 5c34a91038e..58d2d0912eb 100644
--- a/code/game/turfs/open/snow.dm
+++ b/code/game/turfs/open/snow.dm
@@ -13,6 +13,7 @@
barefootstep = FOOTSTEP_SAND
clawfootstep = FOOTSTEP_SAND
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
+ leave_footprints = TRUE
/turf/open/misc/snow/Initialize(mapload)
. = ..()
@@ -21,6 +22,35 @@
/turf/open/misc/snow/broken_states()
return list("snow_dug")
+/turf/open/misc/snow/add_footprint(mob/living/carbon/human/walker, movement_direction)
+ if(HAS_TRAIT(walker, TRAIT_NO_SNOWPRINTS))
+ return
+ // skip the special logic if the level doesn't naturally have snowstorms
+ if(!SSmapping.level_trait(z, ZTRAIT_SNOWSTORM))
+ return ..()
+
+ // if an active snow storm affecting this turf is currently in its main or wind down stage, skip footprint creation
+ for(var/datum/weather/snow_storm/active_weather in SSweather.processing)
+ if(active_weather.stage != MAIN_STAGE && active_weather.stage != WIND_DOWN_STAGE)
+ continue
+ if(!(loc in active_weather.impacted_areas))
+ continue
+ return
+ . = ..()
+ // when a snow storm enters its main stage, clear all of our footprints
+ for(var/snow_type in typesof(/datum/weather/snow_storm))
+ RegisterSignal(SSdcs, COMSIG_WEATHER_START(snow_type), PROC_REF(snow_clear_footprints), override = TRUE)
+
+/turf/open/misc/snow/proc/snow_clear_footprints(datum/source, datum/weather/storm)
+ SIGNAL_HANDLER
+
+ if(!(loc in storm.impacted_areas))
+ return
+
+ clear_footprints()
+ for(var/snow_type in typesof(/datum/weather/snow_storm))
+ UnregisterSignal(SSdcs, COMSIG_WEATHER_START(snow_type))
+
/turf/open/misc/snow/actually_safe
slowdown = 0
planetary_atmos = FALSE
diff --git a/code/modules/antagonists/heretic/magic/shadow_cloak.dm b/code/modules/antagonists/heretic/magic/shadow_cloak.dm
index 825e1874c78..0ecd355ae98 100644
--- a/code/modules/antagonists/heretic/magic/shadow_cloak.dm
+++ b/code/modules/antagonists/heretic/magic/shadow_cloak.dm
@@ -141,7 +141,7 @@
animate(cloak_image, alpha = 255, 0.2 SECONDS)
owner.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, id, cloak_image)
// Add the relevant traits and modifiers
- owner.add_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
+ owner.add_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS, TRAIT_NO_SNOWPRINTS), TRAIT_STATUS_EFFECT(id))
owner.add_movespeed_modifier(/datum/movespeed_modifier/shadow_cloak)
owner.add_actionspeed_modifier(/datum/actionspeed_modifier/shadow_cloak)
// Register signals to cause effects
@@ -157,7 +157,7 @@
owner.remove_alt_appearance(id)
QDEL_NULL(cloak_image)
// Remove traits and modifiers
- owner.remove_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS), TRAIT_STATUS_EFFECT(id))
+ owner.remove_traits(list(TRAIT_UNKNOWN_APPEARANCE, TRAIT_UNKNOWN_VOICE, TRAIT_SILENT_FOOTSTEPS, TRAIT_NO_SNOWPRINTS), TRAIT_STATUS_EFFECT(id))
owner.remove_movespeed_modifier(/datum/movespeed_modifier/shadow_cloak)
owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/shadow_cloak)
// Clear signals
diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm
index 17957869608..492a9800572 100644
--- a/code/modules/clothing/shoes/boots.dm
+++ b/code/modules/clothing/shoes/boots.dm
@@ -105,7 +105,7 @@
desc = "A pair of winter boots with special grips on the bottom, designed to prevent slipping on frozen surfaces."
icon_state = "iceboots"
inhand_icon_state = null
- clothing_traits = list(TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE)
+ clothing_traits = list(TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE, TRAIT_NO_SNOWPRINTS)
// A pair of ice boots intended for general crew EVA use - see EVA winter coat for comparison.
/obj/item/clothing/shoes/winterboots/ice_boots/eva
diff --git a/code/modules/clothing/shoes/wheelys.dm b/code/modules/clothing/shoes/wheelys.dm
index 63c72ad71ff..2282de5bc29 100644
--- a/code/modules/clothing/shoes/wheelys.dm
+++ b/code/modules/clothing/shoes/wheelys.dm
@@ -89,3 +89,4 @@
wheels = /obj/vehicle/ridden/scooter/skateboard/wheelys/skishoes
custom_premium_price = PAYCHECK_CREW * 1.6
custom_price = PAYCHECK_CREW * 1.6
+ clothing_traits = list(TRAIT_NO_SNOWPRINTS)
diff --git a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
index f8e49cf0949..a693fe33ab1 100644
--- a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
@@ -178,7 +178,7 @@
/turf/open/misc/asteroid/drill_act(obj/item/mecha_parts/mecha_equipment/drill/drill)
for(var/turf/open/misc/asteroid/floor in range(1, drill.chassis))
- if((get_dir(drill.chassis, floor) & drill.chassis.dir) && !floor.dug)
+ if((get_dir(drill.chassis, floor) & drill.chassis.dir) && floor.can_dig())
floor.getDug()
drill.log_message("Drilled through [src]", LOG_MECHA)
drill.move_ores()
diff --git a/icons/effects/footprints.dmi b/icons/effects/footprints.dmi
index 175b0c17f66..9c179316918 100644
Binary files a/icons/effects/footprints.dmi and b/icons/effects/footprints.dmi differ