diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 05f310eeef2..29954c9a75b 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -614,11 +614,36 @@ #define GRADIENT_APPLIES_TO_HAIR (1<<0) #define GRADIENT_APPLIES_TO_FACIAL_HAIR (1<<1) +// Height defines +// - They are numbers so you can compare height values (x height < y height) +// - They do not start at 0 for futureproofing +// - They skip numbers for futureproofing as well +// Otherwise they are completely arbitrary +#define HUMAN_HEIGHT_DWARF 2 +#define HUMAN_HEIGHT_SHORTEST 4 +#define HUMAN_HEIGHT_SHORT 6 +#define HUMAN_HEIGHT_MEDIUM 8 +#define HUMAN_HEIGHT_TALL 10 +#define HUMAN_HEIGHT_TALLEST 12 + +/// Assoc list of all heights, cast to strings, to """"tuples""""" +/// The first """tuple""" index is the upper body offset +/// The second """tuple""" index is the lower body offset +GLOBAL_LIST_INIT(human_heights_to_offsets, list( + "[HUMAN_HEIGHT_DWARF]" = list(-5, -4), + "[HUMAN_HEIGHT_SHORTEST]" = list(-2, -1), + "[HUMAN_HEIGHT_SHORT]" = list(-1, -1), + "[HUMAN_HEIGHT_MEDIUM]" = list(0, 0), + "[HUMAN_HEIGHT_TALL]" = list(1, 1), + "[HUMAN_HEIGHT_TALLEST]" = list(2, 2), +)) + // Mob Overlays Indexes /// Total number of layers for mob overlays -// SKYRAT EDIT CHANGE BEGIN - ORIGINAL: #define TOTAL_LAYERS 33 -#define TOTAL_LAYERS 39 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; -// SKYRAT EDIT CHANGE END +/// KEEP THIS UP-TO-DATE OR SHIT WILL BREAK +/// Also consider updating layers_to_offset +#define TOTAL_LAYERS 39 // SKYRAT EDIT CHANGE - ORIGINAL: #define TOTAL_LAYERS 33 + /// Mutations layer - Tk headglows, cold resistance glow, etc #define MUTATIONS_LAYER 39 // SKYRAT EDIT CHANGE - ORIGINAL: 33 /// Mutantrace features (tail when looking south) that must appear behind the body parts @@ -648,9 +673,9 @@ #define NIPPLES_LAYER 26 #define BANDAGE_LAYER 25 //SKYRAT EDIT ADDITION END -/// ID card layer (might be deprecated) -#define ID_LAYER 24 /// ID card layer +#define ID_LAYER 24 +/// ID card layer (might be deprecated) #define ID_CARD_LAYER 23 /// Layer for bodyparts that should appear above every other bodypart - Currently only used for hands #define BODYPARTS_HIGH_LAYER 22 @@ -697,6 +722,49 @@ /// Fire layer when you're on fire #define FIRE_LAYER 1 +#define UPPER_BODY "upper body" +#define LOWER_BODY "lower body" +#define NO_MODIFY "do not modify" + +/// Used for human height overlay adjustments +/// Certain standing overlay layers shouldn't have a filter applied and should instead just offset by a pixel y +/// This list contains all the layers that must offset, with its value being whether it's a part of the upper half of the body (TRUE) or not (FALSE) +GLOBAL_LIST_INIT(layers_to_offset, list( + // Weapons commonly cross the middle of the sprite so they get cut in half by the filter + "[HANDS_LAYER]" = LOWER_BODY, + // Very tall hats will get cut off by filter + "[HEAD_LAYER]" = UPPER_BODY, + // Hair will get cut off by filter + "[HAIR_LAYER]" = UPPER_BODY, + // Long belts (sabre sheathe) will get cut off by filter + "[BELT_LAYER]" = LOWER_BODY, + // Everything below looks fine with or without a filter, so we can skip it and just offset + // (In practice they'd be fine if they got a filter but we can optimize a bit by not.) + "[GLASSES_LAYER]" = UPPER_BODY, + "[ABOVE_BODY_FRONT_GLASSES_LAYER]" = UPPER_BODY, // currently unused + "[ABOVE_BODY_FRONT_HEAD_LAYER]" = UPPER_BODY, // only used for head stuff + "[GLOVES_LAYER]" = LOWER_BODY, + "[HALO_LAYER]" = UPPER_BODY, // above the head + "[HANDCUFF_LAYER]" = LOWER_BODY, + "[ID_CARD_LAYER]" = UPPER_BODY, // unused + "[ID_LAYER]" = UPPER_BODY, + "[FACEMASK_LAYER]" = UPPER_BODY, + // These two are cached, and have their appearance shared(?), so it's safer to just not touch it + "[MUTATIONS_LAYER]" = NO_MODIFY, + "[FRONT_MUTATIONS_LAYER]" = NO_MODIFY, + // These DO get a filter, I'm leaving them here as reference, + // to show how many filters are added at a glance + // BACK_LAYER (backpacks are big) + // BODYPARTS_HIGH_LAYER (arms) + // BODY_ADJ_LAYER (external organs like wings) + // BODY_BEHIND_LAYER (external organs like wings) + // BODY_FRONT_LAYER (external organs like wings) + // DAMAGE_LAYER (full body) + // FIRE_LAYER (full body) + // UNIFORM_LAYER (full body) + // WOUND_LAYER (full body) +)) + //Bitflags for the layers an external organ can draw on (organs can be drawn on multiple layers) /// Draws organ on the BODY_FRONT_LAYER #define EXTERNAL_FRONT (1 << 1) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 40aeb1efe52..9e42adcda2b 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -56,6 +56,10 @@ */ var/list/cooldowns + + /// List for handling persistent filters. + var/list/filter_data + #ifdef REFERENCE_TRACKING var/running_find_references var/last_find_references = 0 @@ -279,3 +283,108 @@ /datum/proc/GenerateTag() datum_flags |= DF_USE_TAG +/** Add a filter to the datum. + * This is on datum level, despite being most commonly / primarily used on atoms, so that filters can be applied to images / mutable appearances. + * Can also be used to assert a filter's existence. I.E. update a filter regardless if it exists or not. + * + * Arguments: + * * name - Filter name + * * priority - Priority used when sorting the filter. + * * params - Parameters of the filter. + */ +/datum/proc/add_filter(name, priority, list/params) + LAZYINITLIST(filter_data) + var/list/copied_parameters = params.Copy() + copied_parameters["priority"] = priority + filter_data[name] = copied_parameters + update_filters() + +/// Reapplies all the filters. +/datum/proc/update_filters() + ASSERT(isatom(src) || istype(src, /image)) + var/atom/atom_cast = src // filters only work with images or atoms. + atom_cast.filters = null + filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) + for(var/filter_raw in filter_data) + var/list/data = filter_data[filter_raw] + var/list/arguments = data.Copy() + arguments -= "priority" + atom_cast.filters += filter(arglist(arguments)) + UNSETEMPTY(filter_data) + +/obj/item/update_filters() + . = ..() + update_item_action_buttons() + +/** Update a filter's parameter to the new one. If the filter doesnt exist we won't do anything. + * + * Arguments: + * * name - Filter name + * * new_params - New parameters of the filter + * * overwrite - TRUE means we replace the parameter list completely. FALSE means we only replace the things on new_params. + */ +/datum/proc/modify_filter(name, list/new_params, overwrite = FALSE) + var/filter = get_filter(name) + if(!filter) + return + if(overwrite) + filter_data[name] = new_params + else + for(var/thing in new_params) + filter_data[name][thing] = new_params[thing] + update_filters() + +/** Update a filter's parameter and animate this change. If the filter doesnt exist we won't do anything. + * Basically a [datum/proc/modify_filter] call but with animations. Unmodified filter parameters are kept. + * + * Arguments: + * * name - Filter name + * * new_params - New parameters of the filter + * * time - time arg of the BYOND animate() proc. + * * easing - easing arg of the BYOND animate() proc. + * * loop - loop arg of the BYOND animate() proc. + */ +/datum/proc/transition_filter(name, list/new_params, time, easing, loop) + var/filter = get_filter(name) + if(!filter) + return + animate(filter, new_params, time = time, easing = easing, loop = loop) + modify_filter(name, new_params) + +/// Updates the priority of the passed filter key +/datum/proc/change_filter_priority(name, new_priority) + if(!filter_data || !filter_data[name]) + return + + filter_data[name]["priority"] = new_priority + update_filters() + +/// Returns the filter associated with the passed key +/datum/proc/get_filter(name) + ASSERT(isatom(src) || istype(src, /image)) + if(filter_data && filter_data[name]) + var/atom/atom_cast = src // filters only work with images or atoms. + return atom_cast.filters[filter_data.Find(name)] + +/// Returns the indice in filters of the given filter name. +/// If it is not found, returns null. +/datum/proc/get_filter_index(name) + return filter_data?.Find(name) + +/// Removes the passed filter, or multiple filters, if supplied with a list. +/datum/proc/remove_filter(name_or_names) + if(!filter_data) + return + + var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) + + for(var/name in names) + if(filter_data[name]) + filter_data -= name + update_filters() + +/datum/proc/clear_filters() + ASSERT(isatom(src) || istype(src, /image)) + var/atom/atom_cast = src // filters only work with images or atoms. + filter_data = null + atom_cast.filters = null diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index 9c77ec8e721..9e76300a3f8 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -114,7 +114,7 @@ difficulty = 16 instability = 5 conflicts = list(/datum/mutation/human/gigantism) - locked = TRUE // Default intert species for now, so locked from regular pool. + locked = TRUE // Default intert species for now, so locked from regular pool. /datum/mutation/human/dwarfism/on_acquiring(mob/living/carbon/human/owner) if(..()) @@ -126,10 +126,6 @@ return // SKYRAT EDIT END ADD_TRAIT(owner, TRAIT_DWARF, GENETIC_MUTATION) - var/matrix/new_transform = matrix() - new_transform.Scale(1, 0.8) - owner.transform = new_transform.Multiply(owner.transform) - passtable_on(owner, GENETIC_MUTATION) owner.visible_message(span_danger("[owner] suddenly shrinks!"), span_notice("Everything around you seems to grow..")) /datum/mutation/human/dwarfism/on_losing(mob/living/carbon/human/owner) @@ -142,10 +138,6 @@ // SKYRAT EDIT END REMOVE_TRAIT(owner, TRAIT_DWARF, GENETIC_MUTATION) - var/matrix/new_transform = matrix() - new_transform.Scale(1, 1.25) - owner.transform = new_transform.Multiply(owner.transform) - passtable_off(owner, GENETIC_MUTATION) owner.visible_message(span_danger("[owner] suddenly grows!"), span_notice("Everything around you seems to shrink..")) //Clumsiness has a very large amount of small drawbacks depending on item. diff --git a/code/game/atoms.dm b/code/game/atoms.dm index e22bd50a06e..e083c4aeea7 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -74,8 +74,6 @@ ///Last fingerprints to touch this atom var/fingerprintslast - var/list/filter_data //For handling persistent filters - //List of datums orbiting this atom var/datum/component/orbiter/orbiters @@ -1622,101 +1620,6 @@ /atom/proc/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) return -/** Add a filter to the atom. - * Can also be used to assert a filter's existence. I.E. update a filter regardless if it exists or not. - * - * Arguments: - * * name - Filter name - * * priority - Priority used when sorting the filter. - * * params - Parameters of the filter. - */ -/atom/proc/add_filter(name, priority, list/params) - LAZYINITLIST(filter_data) - var/list/copied_parameters = params.Copy() - copied_parameters["priority"] = priority - filter_data[name] = copied_parameters - update_filters() - -/atom/proc/update_filters() - filters = null - filter_data = sortTim(filter_data, GLOBAL_PROC_REF(cmp_filter_data_priority), TRUE) - for(var/f in filter_data) - var/list/data = filter_data[f] - var/list/arguments = data.Copy() - arguments -= "priority" - filters += filter(arglist(arguments)) - UNSETEMPTY(filter_data) - -/** Update a filter's parameter and animate this change. If the filter doesnt exist we won't do anything. - * Basically a [atom/proc/modify_filter] call but with animations. Unmodified filter parameters are kept. - * - * Arguments: - * * name - Filter name - * * new_params - New parameters of the filter - * * time - time arg of the BYOND animate() proc. - * * easing - easing arg of the BYOND animate() proc. - * * loop - loop arg of the BYOND animate() proc. - */ -/atom/proc/transition_filter(name, list/new_params, time, easing, loop) - var/filter = get_filter(name) - if(!filter) - return - animate(filter, new_params, time = time, easing = easing, loop = loop) - modify_filter(name, new_params) - -/** Update a filter's parameter to the new one. If the filter doesnt exist we won't do anything. - * - * Arguments: - * * name - Filter name - * * new_params - New parameters of the filter - * * overwrite - TRUE means we replace the parameter list completely. FALSE means we only replace the things on new_params. - */ -/atom/proc/modify_filter(name, list/new_params, overwrite = FALSE) - var/filter = get_filter(name) - if(!filter) - return - if(overwrite) - filter_data[name] = new_params - else - for(var/thing in new_params) - filter_data[name][thing] = new_params[thing] - update_filters() - -/atom/proc/change_filter_priority(name, new_priority) - if(!filter_data || !filter_data[name]) - return - - filter_data[name]["priority"] = new_priority - update_filters() - -/obj/item/update_filters() - . = ..() - update_item_action_buttons() - -/atom/proc/get_filter(name) - if(filter_data && filter_data[name]) - return filters[filter_data.Find(name)] - -/// Returns the indice in filters of the given filter name. -/// If it is not found, returns null. -/atom/proc/get_filter_index(name) - return filter_data?.Find(name) - -/atom/proc/remove_filter(name_or_names) - if(!filter_data) - return - - var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) - - for(var/name in names) - if(filter_data[name]) - filter_data -= name - update_filters() - -/atom/proc/clear_filters() - filter_data = null - filters = null - /atom/proc/intercept_zImpact(list/falling_movables, levels = 1) SHOULD_CALL_PARENT(TRUE) . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, falling_movables, levels) diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm index c220f593629..24199de0f26 100644 --- a/code/modules/mining/lavaland/tendril_loot.dm +++ b/code/modules/mining/lavaland/tendril_loot.dm @@ -863,7 +863,7 @@ living_scanned.set_jitter_if_lower(100 SECONDS) to_chat(living_scanned, span_warning("You've been staggered!")) living_scanned.add_filter("scan", 2, list("type" = "outline", "color" = COLOR_YELLOW, "size" = 1)) - addtimer(CALLBACK(living_scanned, TYPE_PROC_REF(/atom/, remove_filter), "scan"), 30 SECONDS) + addtimer(CALLBACK(living_scanned, TYPE_PROC_REF(/datum, remove_filter), "scan"), 30 SECONDS) owner.playsound_local(get_turf(owner), 'sound/magic/smoke.ogg', 50, TRUE) owner.balloon_alert(owner, "[living_scanned] scanned") diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 214091d2241..830434351b7 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -457,8 +457,6 @@ /mob/living/carbon/proc/update_hud_back(obj/item/I) return - - //Overlays for the worn overlay so you can overlay while you overlay //eg: ammo counters, primed grenade flashing, etc. //"icon_file" is used automatically for inhands etc. to make sure it gets the right inhand file diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 379e958dbab..332801e931c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -732,6 +732,26 @@ return 1 ..() +/mob/living/carbon/human/vv_edit_var(var_name, var_value) + if(var_name == NAMEOF(src, mob_height)) + var/static/list/heights = list( + HUMAN_HEIGHT_SHORTEST, + HUMAN_HEIGHT_SHORT, + HUMAN_HEIGHT_MEDIUM, + HUMAN_HEIGHT_TALL, + HUMAN_HEIGHT_TALLEST + ) + if(!(var_value in heights)) + return + + . = set_mob_height(var_value) + + if(!isnull(.)) + datum_flags |= DF_VAR_EDITED + return + + return ..() + /mob/living/carbon/human/vv_get_dropdown() . = ..() VV_DROPDOWN_OPTION("", "---------") diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 0fc321a0715..807133e1327 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -90,3 +90,6 @@ /// When an braindead player has their equipment fiddled with, we log that info here for when they come back so they know who took their ID while they were DC'd for 30 seconds var/list/afk_thefts + + /// Height of the mob + VAR_PROTECTED/mob_height = HUMAN_HEIGHT_MEDIUM diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index edfae450cf7..5d8786db3d5 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -257,3 +257,33 @@ if (preference.is_randomizable()) preference.apply_to_human(src, preference.create_random_value(preferences)) + +/** + * Setter for mob height + * + * Exists so that the update is done immediately + * + * Returns TRUE if changed, FALSE otherwise + */ +/mob/living/carbon/human/proc/set_mob_height(new_height) + if(mob_height == new_height) + return FALSE + if(new_height == HUMAN_HEIGHT_DWARF) + CRASH("Don't set height to dwarf height directly, use dwarf trait") + + mob_height = new_height + regenerate_icons() + return TRUE + +/** + * Getter for mob height + * + * Mainly so that dwarfism can adjust height without needing to override existing height + * + * Returns a mob height num + */ +/mob/living/carbon/human/proc/get_mob_height() + if(HAS_TRAIT(src, TRAIT_DWARF)) + return HUMAN_HEIGHT_DWARF + + return mob_height diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index 8240f58bee1..7ad23b7d679 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -784,8 +784,15 @@ generate/load female uniform sprites matching all previously decided variables mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT EDIT ADDITION - Taur-friendly suits and uniforms */ - -/obj/item/proc/build_worn_icon(default_layer = 0, default_icon_file = null, isinhands = FALSE, female_uniform = NO_FEMALE_UNIFORM, override_state = null, override_file = null, mutant_styles = NONE) // SKYRAT EDIT - Further outfit modification for outfits (added `mutant_styles` argument) +/obj/item/proc/build_worn_icon( + default_layer = 0, + default_icon_file = null, + isinhands = FALSE, + female_uniform = NO_FEMALE_UNIFORM, + override_state = null, + override_file = null, + mutant_styles = NONE, +) // SKYRAT EDIT - Further outfit modification for outfits (added `mutant_styles` argument) //Find a valid icon_state from variables+arguments var/t_state @@ -817,6 +824,25 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E //eg: ammo counters, primed grenade flashes, etc. var/list/worn_overlays = worn_overlays(standing, isinhands, file2use, mutant_styles) // SKYRAT EDIT CHANGE - ORIGINAL: var/list/worn_overlays = worn_overlays(standing, isinhands) if(worn_overlays?.len) + if(!isinhands && default_layer && ishuman(loc)) + var/mob/living/carbon/human/human_loc = loc + if(human_loc.get_mob_height() != HUMAN_HEIGHT_MEDIUM) + var/string_form_layer = num2text(default_layer) + var/offset_amount = GLOB.layers_to_offset[string_form_layer] + if(isnull(offset_amount)) + // Worn overlays don't get batched in with standing overlays because they are overlay overlays + // ...So we need to apply human height here as well + for(var/mutable_appearance/applied_appearance as anything in worn_overlays) + if(isnull(applied_appearance)) + continue + human_loc.apply_height_filters(applied_appearance) + + else + for(var/mutable_appearance/applied_appearance in worn_overlays) + if(isnull(applied_appearance)) + continue + human_loc.apply_height_offsets(applied_appearance, offset_amount) + standing.overlays.Add(worn_overlays) standing = center_image(standing, isinhands ? inhand_x_dimension : worn_x_dimension, isinhands ? inhand_y_dimension : worn_y_dimension) @@ -921,4 +947,90 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E update_worn_head() update_worn_mask() +// Hooks into human apply overlay so that we can modify all overlays applied through standing overlays to our height system. +// Some of our overlays will be passed through a displacement filter to make our mob look taller or shorter. +// Some overlays can't be displaced as they're too close to the edge of the sprite or cross the middle point in a weird way. +// So instead we have to pass them through an offset, which is close enough to look good. +/mob/living/carbon/human/apply_overlay(cache_index) + if(get_mob_height() == HUMAN_HEIGHT_MEDIUM) + return ..() + + var/raw_applied = overlays_standing[cache_index] + var/string_form_index = num2text(cache_index) + var/offset_amount = GLOB.layers_to_offset[string_form_index] + if(isnull(offset_amount)) + if(islist(raw_applied)) + for(var/mutable_appearance/applied_appearance as anything in raw_applied) + if(isnull(applied_appearance)) + continue + apply_height_filters(applied_appearance) + else if(!isnull(raw_applied)) + apply_height_filters(raw_applied) + else + if(islist(raw_applied)) + for(var/mutable_appearance/applied_appearance as anything in raw_applied) + if(isnull(applied_appearance)) + continue + apply_height_offsets(applied_appearance, offset_amount) + else if(!isnull(raw_applied)) + apply_height_offsets(raw_applied, offset_amount) + + return ..() + +/** + * Used in some circumstances where appearances can get cut off from the mob sprite from being too tall + * + * upper_torso is to specify whether the appearance is locate in the upper half of the mob rather than the lower half, + * higher up things (hats for example) need to be offset more due to the location of the filter displacement + */ +/mob/living/carbon/human/proc/apply_height_offsets(mutable_appearance/appearance, upper_torso) + var/height_to_use = num2text(get_mob_height()) + var/final_offset = 0 + switch(upper_torso) + if(UPPER_BODY) + final_offset = GLOB.human_heights_to_offsets[height_to_use][1] + if(LOWER_BODY) + final_offset = GLOB.human_heights_to_offsets[height_to_use][2] + else + return + + appearance.pixel_y += final_offset + return appearance + +/** + * Applies a filter to an appearance according to mob height + */ +/mob/living/carbon/human/proc/apply_height_filters(mutable_appearance/appearance) + var/static/icon/cut_torso_mask = icon('icons/effects/cut.dmi', "Cut1") + var/static/icon/cut_legs_mask = icon('icons/effects/cut.dmi', "Cut2") + var/static/icon/lenghten_torso_mask = icon('icons/effects/cut.dmi', "Cut3") + var/static/icon/lenghten_legs_mask = icon('icons/effects/cut.dmi', "Cut4") + + appearance.remove_filter(list( + "Cut_Torso", + "Cut_Legs", + "Lenghten_Legs", + "Lenghten_Torso", + "Gnome_Cut_Torso", + "Gnome_Cut_Legs", + )) + + switch(get_mob_height()) + // Don't set this one directly, use TRAIT_DWARF + if(HUMAN_HEIGHT_DWARF) + appearance.add_filter("Gnome_Cut_Torso", 1, displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 2)) + appearance.add_filter("Gnome_Cut_Legs", 1, displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 3)) + if(HUMAN_HEIGHT_SHORTEST) + appearance.add_filter("Cut_Torso", 1, displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 1)) + appearance.add_filter("Cut_Legs", 1, displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 1)) + if(HUMAN_HEIGHT_SHORT) + appearance.add_filter("Cut_Legs", 1, displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 1)) + if(HUMAN_HEIGHT_TALL) + appearance.add_filter("Lenghten_Legs", 1, displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 1)) + if(HUMAN_HEIGHT_TALLEST) + appearance.add_filter("Lenghten_Torso", 1, displacement_map_filter(lenghten_torso_mask, x = 0, y = 0, size = 1)) + appearance.add_filter("Lenghten_Legs", 1, displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 1)) + + return appearance + #undef RESOLVE_ICON_STATE diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index 4c6824712eb..44a377c2b3d 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -2,6 +2,7 @@ . = ..() RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_UNKNOWN), SIGNAL_REMOVETRAIT(TRAIT_UNKNOWN)), PROC_REF(on_unknown_trait)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DWARF), SIGNAL_REMOVETRAIT(TRAIT_DWARF)), PROC_REF(on_dwarf_trait)) /// Gaining or losing [TRAIT_UNKNOWN] updates our name and our sechud /mob/living/carbon/human/proc/on_unknown_trait(datum/source) @@ -9,3 +10,15 @@ name = get_visible_name() sec_hud_set_ID() + +/// Gaining or losing [TRAIT_DWARF] updates our height +/mob/living/carbon/human/proc/on_dwarf_trait(datum/source) + SIGNAL_HANDLER + + // We need to regenerate everything for height + regenerate_icons() + // Toggle passtable + if(HAS_TRAIT(src, TRAIT_DWARF)) + passtable_on(src, TRAIT_DWARF) + else + passtable_off(src, TRAIT_DWARF) diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index 0b11d4616db..dec55a3a044 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -518,7 +518,7 @@ playsound(src, 'sound/items/modsuit/ballin.ogg', 100, TRUE, frequency = -1) mod.wearer.base_pixel_y = 0 animate(mod.wearer, animate_time, pixel_y = mod.wearer.base_pixel_y) - addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/atom, remove_filter), list("mod_ball", "mod_blur", "mod_outline")), animate_time) + addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/datum, remove_filter), list("mod_ball", "mod_blur", "mod_outline")), animate_time) REMOVE_TRAIT(mod.wearer, TRAIT_LAVA_IMMUNE, MOD_TRAIT) REMOVE_TRAIT(mod.wearer, TRAIT_HANDS_BLOCKED, MOD_TRAIT) REMOVE_TRAIT(mod.wearer, TRAIT_FORCED_STANDING, MOD_TRAIT) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index c01f42cab05..e9bc7c6b203 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -1474,7 +1474,7 @@ playsound(get_turf(drinker), 'sound/effects/supermatter.ogg', 150, TRUE) drinker.add_filter("singulo_rays", 1, ray_filter) animate(drinker.get_filter("singulo_rays"), offset = 10, time = 1.5 SECONDS, loop = -1) - addtimer(CALLBACK(drinker, TYPE_PROC_REF(/atom/, remove_filter), "singulo_rays"), 1.5 SECONDS) + addtimer(CALLBACK(drinker, TYPE_PROC_REF(/datum, remove_filter), "singulo_rays"), 1.5 SECONDS) drinker.emote("burp") return ..() diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_dwarf.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_dwarf.png index e0f8dee542b..cc097e68068 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_dwarf.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_dwarf.png differ diff --git a/icons/effects/cut.dmi b/icons/effects/cut.dmi new file mode 100644 index 00000000000..0252704d2bb Binary files /dev/null and b/icons/effects/cut.dmi differ diff --git a/modular_skyrat/modules/xenos_skyrat_redo/code/xeno_types/rouny.dm b/modular_skyrat/modules/xenos_skyrat_redo/code/xeno_types/rouny.dm index b5d1378eb2a..cf5f4e6ca09 100644 --- a/modular_skyrat/modules/xenos_skyrat_redo/code/xeno_types/rouny.dm +++ b/modular_skyrat/modules/xenos_skyrat_redo/code/xeno_types/rouny.dm @@ -79,7 +79,7 @@ owner.visible_message(span_danger("[owner] effortlessly dodges the projectile!"), span_userdanger("You dodge the projectile!")) playsound(get_turf(owner), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE) owner.add_filter(RUNNER_BLUR_EFFECT, 2, gauss_blur_filter(5)) - addtimer(CALLBACK(owner, TYPE_PROC_REF(/atom, remove_filter), RUNNER_BLUR_EFFECT), 0.5 SECONDS) + addtimer(CALLBACK(owner, TYPE_PROC_REF(/datum, remove_filter), RUNNER_BLUR_EFFECT), 0.5 SECONDS) return BULLET_ACT_FORCE_PIERCE /mob/living/carbon/alien/adult/skyrat/runner/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE)