From bde2f58c72f790ecbc01dd8986978fd4b96ff91b Mon Sep 17 00:00:00 2001
From: Arturlang <24881678+Arturlang@users.noreply.github.com>
Date: Sat, 20 Jun 2026 02:25:46 +0300
Subject: [PATCH] pref for seethrough limbs (#5805)
## About The Pull Request
adds a pref for making limbs partially-seethrough, i kept options for
head and body as disabled prefs because why not
## Why It's Good For The Game
some people want this aspect of customization, could be extra useful for
slimepeople too, or holographic aspects, since it can be partial invis,
does not prevent their hitboxes from working since it's alpha changes
though
## Proof Of Testing
## Changelog
:cl:
add: pref for limb transparency
/:cl:
---
code/__DEFINES/bodyparts.dm | 3 +
.../mob/living/carbon/carbon_update_icons.dm | 6 ++
code/modules/surgery/bodyparts/_bodyparts.dm | 23 +++++-
...s__datum_species_jelly_roundstartslime.png | Bin 3177 -> 3183 bytes
.../bodyparts/code/_mutant_bodyparts.dm | 4 +
.../modules/client/preferences/limb_alpha.dm | 72 ++++++++++++++++++
tgstation.dme | 1 +
.../bubbers/limb_alpha.tsx | 53 +++++++++++++
8 files changed, 159 insertions(+), 3 deletions(-)
create mode 100644 modular_zubbers/code/modules/client/preferences/limb_alpha.dm
create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/bubbers/limb_alpha.tsx
diff --git a/code/__DEFINES/bodyparts.dm b/code/__DEFINES/bodyparts.dm
index 04947265cbe..82f153b5568 100644
--- a/code/__DEFINES/bodyparts.dm
+++ b/code/__DEFINES/bodyparts.dm
@@ -1,6 +1,9 @@
///The standard amount of bodyparts a carbon has. Currently 6, HEAD/L_ARM/R_ARM/CHEST/L_LEG/R_LEG
#define BODYPARTS_DEFAULT_MAXIMUM 6
+// The lowest alpha a dropped limb's sprite will render at, unlike a attached limb which can be 0
+#define LIMB_DROPPED_MIN_ALPHA 40 /// BUBBER EDIT ADDITION
+
/// Limb Health
/// The max damage a limb can take before it stops taking damage.
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 03fe07df0c5..dd401d18b5b 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -543,6 +543,10 @@
. += draw_color
if(is_invisible)
. += "invisible"
+ // BUBBER EDIT ADDITION START - per-limb alpha
+ if(limb_alpha != 255)
+ . += "alpha_[limb_alpha]"
+ // BUBBER EDIT ADDITION END
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner, is_husked))
continue
@@ -571,6 +575,8 @@
. += body_zone
if(is_invisible)
. += "invisible"
+ if(limb_alpha != 255)
+ . += "alpha_[limb_alpha]" // BUBBER EDIT ADDITION - per-limb alpha
. += "[LAZYLEN(blood_dna_info) ? get_color_from_blood_list(blood_dna_info) : BLOOD_COLOR_RED]"
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner, TRUE))
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 9cd19e026b7..1bccd9bc15f 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -1216,6 +1216,14 @@
if(owner_species && owner_species.specific_alpha != 255)
alpha = owner_species.specific_alpha
+ // BUBBER EDIT ADDITION START - per-limb alpha.
+ // preference (stored in dna.features) overrides it for this specific zone if present over species alpha
+ limb_alpha = owner_species?.specific_alpha || 255
+ var/limb_alpha_key = "limb_alpha_[body_zone]"
+ if(limb_alpha_key in human_owner.dna.features)
+ limb_alpha = human_owner.dna.features[limb_alpha_key]
+ // BUBBER EDIT ADDITION END
+
if(body_zone in owner_species.body_markings)
markings = LAZYCOPY(owner_species.body_markings[body_zone])
if(aux_zone && (aux_zone in owner_species.body_markings))
@@ -1327,12 +1335,19 @@
var/image/limb = image(used_icon, used_state, -BODYPARTS_LAYER, dir = image_dir)
var/image/aux = null
+ // BUBBER EDIT ADDITION - per-limb alpha. While worn the chosen alpha is used verbatim (0 = invisible),
+ // while dropped it is floored so the limb stays visible and pickup-able on the ground.
+ var/used_alpha = dropped ? max(limb_alpha, LIMB_DROPPED_MIN_ALPHA) : limb_alpha
+ limb.alpha = used_alpha
+ // BUBBER EDIT ADDITION END
+
icon_exists_or_scream(limb.icon, limb.icon_state) //Prints a stack trace on the first failure of a given iconstate.
. += limb
if(aux_zone) //Hand shit
aux = image(limb.icon, "[limb_id]_[aux_zone]", -aux_layer, dir = image_dir)
+ aux.alpha = used_alpha // BUBBER EDIT ADDITION - per-limb alpha
. += aux
if(dropped && dmg_overlay_type)
@@ -1356,10 +1371,12 @@
update_draw_color()
if(draw_color)
- var/limb_color = alpha != 255 ? "[draw_color][num2hex(alpha, 2)]" : "[draw_color]" // SKYRAT EDIT ADDITION - Alpha values on limbs. We check if the limb is attached and if the owner has an alpha value to append
- limb.color = limb_color // SKYRAT EDIT CHANGE - ORIGINAL: limb.color = "[draw_color]"
+ // BUBBER EDIT CHANGE - per-limb transparency is now handled via limb.alpha (see above) rather than
+ // baking an alpha channel into the draw color. ORIGINAL SKYRAT: limb.color = "[draw_color][num2hex(alpha, 2)]"
+ limb.color = "[draw_color]"
if(aux_zone)
- aux.color = limb_color // SKYRAT EDIT CHANGE - ORIGINAL: aux.color = "[draw_color]"
+ aux.color = "[draw_color]"
+ // BUBBER EDIT END
var/atom/location = loc || owner || src
if(blocks_emissive != EMISSIVE_BLOCK_NONE)
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_roundstartslime.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_roundstartslime.png
index 7d2fb75e560d624be78b18100b255f48fc44f159..6e82d16590142bf8a1ea55360e05ff2182cb59cc 100644
GIT binary patch
delta 3058
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
AVgLXD
delta 3052
zcmV2vmr1AV^9kuX@h(U
zMG^vZMc@7r@9PUG8WBE*MwP#>5CEVt=n<9tyjwbW(Rx7OkL{|Uss0EGc!Djv{-f!!
zl%N6E_yo6m?f?KfXNsu#sSMy_XjDat!w?kk1d1+~$psT9K`0OafCd1r|7iB85$a3M
zt7grVAW|HLhJWTJIVq16hiPv2+`*k*8?`rj(<(t4KU*S$EL04JVPNa)ll%J$p-5gs
zD~<~CxOlel!TtdtzlP=}IoBg-KxKePaTusqf!s)PjkV)Zbny
zzY93L#4SxmfRCY3^2fVTl6#bGEq&;*8YJgYzZ
zLdm^0b+ps)x()ll0EB`mPGv&D6xas_aP5>sPa5zxK*4d_rl%6xj#ON$Rg=apnllf8
zqw$uGHh+W#i!>U;RVoGeLSwj!+0qD&x3{;G5lG{`y}dDRdexDF-*1<@~bs>pW(Rr6~k4$lb$hNW4P+kHGz+zQ6-O?gL2^bno0p05dIiEjp+Ad
z+47sa>OjhA`FR`Q=&E97x}OlYtTrSbScntH4&j5fsmc$E3KZ)%Y=$;d2U70|BM^Sy
zE#Zi{I1Y)&R44QFs;$vyP$w@~x*8UwOd?P9zq+W`RYUV8&FX((QKeJegFDp0)g
z`hQl)q@j5C+8YjmlkLb}wTq!@9Z!DWE#XK^h((p&>{MT+HzP427QS1;srg6T3)CCi
znZK2M2+0gFRL>j*07zztVKh~ttE&t4UK=C}qS^O%=(1q`sTuXg7A8t2dTdYN^1tzy
z_t^3q*G*3EX)EhccCi%y(0xe7&ucRznSUY1g|aFDfcd9pNM?w!G-(0!)yuIoX#raf
z02j)turz4_jHVWzP2|s?v}7^LE|wz1DniblgKT|>RfN`yrAS(`n3~^Z7pOP3Gcsu?
z+qhd+8;qtF_Bnq3TmX*78%-^^Wwo)-GHEF4jqQ)s6}bHR>RJFm_Kr7Uu$z&+<9|(r
zREV6O0YG0}OU3Wf0EEy*8t*m3~GR~y3_u9%jO!pJw=zpqWhWp(}a5)Bq4q6dy?@>m_NKlbi$iycua;hg?
zfP1gWD`Zk<&r!nOgU&f|nLN__x{5DF6vtFO~uT5))#v?b*H9r{2yEfFV{940bd2
z#VqH|&-+9uJb0S&()Kb%*Psng`Z#u73->1Bt95w*08;+kYhtAJa$btZ_CiM+Vq&^P(f{3r9jV*p@n>dJ@SBLiPb
z+A=DBs=WX)0D;lef}zi&vG{St55S3&%ME~PxRb
zW&D`|06)K}w4&U?}gcj_oM9jupPl{nxPkU*@_ekM}etVNbdrc7rFx)Z#uz%aSM|~>v6hAKym?8{dWRfr^lKRGGh(!X_H~zXCB<~o_U`Epj
zg#YY$En7Ame|=*!Ry?0b#m~zCLrDOjMq3T>3yV;rtseG$u}Hv2G%pa&s0gu3{-_8s
zjOHdPe(LD({~FxR512Jm0>JYTVNdHp?E%~nH5X%TjV0DoVh
zMpJTLfk{@d1?q
zxWccU0_H_Ng_h3S=&I9?Bcw
z18NTt3Z|grdQ@catojB3h~SLImX0>a)7NdL)`BPG>3{1sTRPgD$^;W9Av7=u_JIMn
zWKtQR9QKUa(g=+(!q6a`F+I}O*N5Kzel;+586rcudi(p;f{ByR_H!4e1w5fChdqNv
z7VmX{PG@3n?y5uU@7u99bv1h~Cz&B8r@^Cg2+9YVyh0}7<@GE#;5e8mDkw%sg$U8_
z$710^DSt0rco{&aGchD4f8)9ds+9$7U3$qHR*`XD_r}Bk_n2`E;mk|^nk~;blZu#G
zQZ`?Jj}b=f0y>?E>D_NZ;<1&;%`HaGo`dk7Y=`$`JG#fWAusuBHe!5_88@nV0mETv
zMip+<%*+WESWnu=K~O8R>kshb>Jdt$ztT}If!$u`Vr;1^nj2G5wg{e
z2!E`$vJOU5%Y*C?=NJI__s0_4!H?)dbORiD1lR$m@oG5mo(4&5p6h2$-vr2$T6m9XLG
zTUHwz8k;*9i|9TvDvzIlOd84tCOr)j^nU~#!BCkr6qUzMAh=D4xr4E6cerJ>q3mKQ
zw3T%@nN{Larpqzlqf41ydvlZw;E278znYBDK`WkH`aI0HY_Q&R^ud9`ZhTdkhps^z
zqQ3UnN*Lk6)0C(F`mKWGCm82D`n&A+0hsD{A9`&$nhWos+1?M5NAS_5OqXkc5q}Rv
z$P}^{N@hjsYIJJLQCM^e01zbf0|2~Km_8=KQAqvOmc|Q9&_WO?_NYhsOb+Q(F!INO*ZY8zLtLNJ(4f(beF|{O=g90c6rpv~}1J
z6(L4V?M(o5A#&>QH~<7DE<#}9qJQ5+RM=>0fxXwpHlEfC-8vN>y7(BYe;;sUfRId)
z;q;)zW90|@mf)^VR35;!J{O)I{D#9&PV!9ag>FTLxG{OFwFiEFQ}Nk>{bRBfCU`4z
z3q!cA)*b+kd;wZHZjAvFi4?EAz7+s)`14QM`>{mU&fQMjhd=++Ejex(z-4SUv4mrt
zA28Z_xYY%Qi^)HT$c{-jXm0Ogq9Po-&!PDOw6fd_jt-L(008;NzG0GISmaS7lzdap
uq-}TskIGk|g?h^`0000 = {
+ name: 'Limb Transparency: Head',
+ description:
+ 'How opaque your head is. 0 is fully invisible while worn; dropped limbs stay faintly visible so they can be picked up.',
+ component: FeatureSliderInput,
+};
+
+export const limb_alpha_chest: Feature = {
+ name: 'Limb Transparency: Chest',
+ description:
+ 'How opaque your chest is. 0 is fully invisible while worn; dropped limbs stay faintly visible so they can be picked up.',
+ component: FeatureSliderInput,
+};
+
+export const limb_alpha_l_arm: Feature = {
+ name: 'Limb Transparency: Left Arm',
+ description: 'How opaque your left arm is.',
+ component: FeatureSliderInput,
+};
+
+export const limb_alpha_r_arm: Feature = {
+ name: 'Limb Transparency: Right Arm',
+ description: 'How opaque your right arm is.',
+ component: FeatureSliderInput,
+};
+
+export const limb_alpha_l_leg: Feature = {
+ name: 'Limb Transparency: Left Leg',
+ description: 'How opaque your left leg is.',
+ component: FeatureSliderInput,
+};
+
+export const limb_alpha_r_leg: Feature = {
+ name: 'Limb Transparency: Right Leg',
+ description: 'How opaque your right leg is.',
+ component: FeatureSliderInput,
+};