Files
MrMelbertandGitHub 21ea64aec5 Height and minor bodypart overlay refactor (#96570)
## About The Pull Request

### Main changes

Height is no longer applied in `apply_overlay`

There is now a proc titled `apply_height()` which is passed an
appearance and a body area, and handles either applying a filter or
adjusting the offset of the appearance up or down

`apply_height` is now called directly when applying item appearances
(ie, `update_worn_x`)
`apply_height` is also called directly in `get_limb_icon` (as height is
included in limb render keys).

### Other changes

Bodypart overlays were cleaned up a bit. You can now apply and remove
bodypart overlays directly with just the typepath, which is a bit more
convenient.

Bodypart textures were split into a separate type. Previously, textures
relied on insertion order to be "correctly" added (any bodypart overlays
added later would not be modified by the bodypart texture). Now

Fixed a bug with cybernetics while I was there. They reskin by changing
DMI so they needed to have their DMI included in their render keys.

## Why It's Good For The Game

This allows us to be more specific and less wasteful about applying
height filters and whatnot - We can now specify whether certain overlays
are offset or given a filter.

For example: In the past, horns and frills were filtered solely because
they were attached to the head and the head was filtered.
We couldn't independently say "Offsets the horns and frills, they don't
need filters".
But now, not only are we able to say "rather than filter the head, just
apply an offset", we can also say "horns and frills should be offset
rather than filtered".

TL;DR fixes the issue where horns or cat ears are cut off by height
filters, yippee.

## Changelog

🆑 Melbert
fix: Cybernetic reskinning should break less. 
fix: Horns and cat ears should be cut off less by height.
fix: Bodypart textures should apply more consistently. 
refactor: Mutant parts like moth wings, lizard tails, cat eats, etc.
have been refactored a tiny bit, report any oddities.
refactor: Bodypart textures were refactored a tiny bit, report any
oddities.
refactor: Refactored the way height works, report anything weird looking
things involving that.
/🆑
2026-06-27 10:28:43 +02:00

77 lines
2.9 KiB
Plaintext

/// A datum for controlling how to position items on an unusually offset body part
/// For instance if you have an asymmetrical head, hats might need to be offset to one side
/datum/worn_feature_offset
/// Owner of mob we are attached to, could be null on a severed limb
var/mob/living/carbon/owner
/// What are we attached to
var/obj/item/bodypart/attached_part
/// Key used to identify what this offset applies to
var/feature_key
/// Offsets to apply on the x axis for each direction
var/list/offset_x
/// Offsets to apply on the y axis for each direction
var/list/offset_y
/datum/worn_feature_offset/New(
obj/item/bodypart/attached_part,
feature_key,
list/offset_x = list("south" = 0),
list/offset_y = list("south" = 0),
)
attached_part.feature_offsets[feature_key] = src
src.attached_part = attached_part
src.feature_key = feature_key
src.offset_x = offset_x
src.offset_y = offset_y
if (length(offset_x) <= 1 && length(offset_y) <= 1)
return // We don't need to do any extra signal handling
changed_owner(owner, attached_part.owner)
RegisterSignal(attached_part, COMSIG_BODYPART_CHANGED_OWNER, PROC_REF(changed_owner))
/datum/worn_feature_offset/Destroy(force)
attached_part.feature_offsets -= feature_key
attached_part = null
changed_owner(null, null)
return ..()
/// Returns the current offset which should be used for this feature
/datum/worn_feature_offset/proc/get_offset() as /list
var/current_dir = owner ? owner.dir : SOUTH
if(ISDIAGONALDIR(current_dir))
current_dir = current_dir & (EAST|WEST)
current_dir = dir2text(current_dir)
var/x = length(offset_x) ? ((current_dir in offset_x) ? offset_x[current_dir] : offset_x["south"]) : 0
var/y = length(offset_y) ? ((current_dir in offset_y) ? offset_y[current_dir] : offset_y["south"]) : 0
return list("x" = x, "y" = y)
/// Applies the current offset to a provided overlay image
/datum/worn_feature_offset/proc/apply_offset(image/overlay)
var/list/offset = get_offset()
overlay.pixel_w += offset["x"]
overlay.pixel_z += offset["y"]
/// When the owner of the bodypart changes, update our signal registrations
/datum/worn_feature_offset/proc/changed_owner(obj/item/bodypart/part, mob/living/new_owner, mob/living/old_owner)
SIGNAL_HANDLER
if(isnull(old_owner))
old_owner = owner
owner = new_owner
if (!isnull(old_owner))
UnregisterSignal(old_owner, list(COMSIG_ATOM_POST_DIR_CHANGE, COMSIG_QDELETING))
if (!isnull(new_owner))
RegisterSignal(new_owner, COMSIG_ATOM_POST_DIR_CHANGE, PROC_REF(on_dir_change))
RegisterSignal(new_owner, COMSIG_QDELETING, PROC_REF(on_owner_deleted))
/// If the owner is deleted, stop updating
/datum/worn_feature_offset/proc/on_owner_deleted(mob/living/host)
SIGNAL_HANDLER
owner = null
/// When we change direction, re-apply the offset
/datum/worn_feature_offset/proc/on_dir_change(mob/living/carbon/owner, olddir, newdir)
SIGNAL_HANDLER
if(olddir != newdir)
owner.update_features(feature_key)