From bad46d86008c1dee6b4efa16889e04192edcfd39 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Fri, 3 Apr 2026 09:32:09 -0500 Subject: [PATCH] Walking in snow leaves snowprints, snowstorms clear them (#95569) ## About The Pull Request image Walking through snow or sand leaves behind a trail of prints similar to that of bloody footprints (they use the old sprites, in fact). Like bloody footprints, you can examine them to get a hint at what left the prints (shoe type or leg type if barefoot). When weather comes through (snow storm or sand storm), all prints will be cleared off. During a raging snow or sand storm, you won't leave prints at all. Snowprints can be filled with snow to remove them as well. Some equipment or effects will stop you from leaving snowprints (ski shoes, ice shoes*, heretic's cloak of shadows). Also you obviously don't leave prints if you're flying or crawling or riding something. **We don't have snow shoes* ## Why It's Good For The Game Initially I made this as a cute little detail, but I realized it has far reaching implications for play on Icebox (and many, many sandbox applications) On Icebox, any average joe can run into the wastes and become untraceable, which makes it almost impossible to track down antagonists. Snowprints... are a method for tracking people who run off into the snow. But you can play around it - You can make fake trails and retrace your steps. You can wait for a snowstorm to clear your tracks. You can even fill in your tracks with snow if you're patient. Alternatively, acquiring snow shoes means you leave no trail... or you could learn to fly, crawl through the snow, use a vehicle, so on. If you're assigned to assassinate a miner, you can follow their snowprint trail as they head into the wastes. However, this may only works early in the round, as the caverns have no snowstorm to clear prints - as miners come and go, it will become harder to follow trails. Speaking of miners, they can follow their co-workers trail if they get injured, or look for prints to see if a ruin has been looted. Similarly if a crew member falls down to the mining layers, they could find a miner's trail to follow it back to the station. And If you're a paramedic and someone was wounded out in the wastes, you might be able to find their trail to track them down. But you can't dilly-dally, as the storm will wipe away any chance of that. Ultimately I think a small detail like this has some big potential for emergent gameplay which could be cool. My intent is not to affect antagonists too negatively, so more tools to avoid leaving prints (snow shoes) could be added. ## Changelog :cl: Melbert add: Walking in snow or sand leaves behind prints. You can examine the turf to see what left the prints, like with bloodprints. add: Snowstorms or sandstorms clear away any footprints left behind in the snow or sand. add: Some equipment or effects, like skis or snowshoes, prevent you from leaving prints in the snow. add: You can fill in snowprints with snow. /:cl: --- code/__DEFINES/traits/declarations.dm | 3 + code/_globalvars/traits/_traits.dm | 1 + code/_globalvars/traits/admin_tooling.dm | 1 + code/game/turfs/open/_open.dm | 146 +++++++++++++++--- code/game/turfs/open/asteroid.dm | 81 +++++++++- .../turfs/open/floor/plating/misc_plating.dm | 6 +- code/game/turfs/open/sand.dm | 28 ++++ code/game/turfs/open/snow.dm | 30 ++++ .../antagonists/heretic/magic/shadow_cloak.dm | 4 +- code/modules/clothing/shoes/boots.dm | 2 +- code/modules/clothing/shoes/wheelys.dm | 1 + .../mecha/equipment/tools/mining_tools.dm | 2 +- icons/effects/footprints.dmi | Bin 4204 -> 5087 bytes 13 files changed, 269 insertions(+), 36 deletions(-) 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 175b0c17f66024e3aeef29ceb438f37a3ef8c190..9c17931691853ef83bfe8cfc842cc18974dd3d27 100644 GIT binary patch literal 5087 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 literal 4204 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