diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 2e1066dc0b..a084e29ffa 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -372,14 +372,17 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
var/obj/item/organ/external/O = character.organs_by_name[N]
O.markings.Cut()
+ var/priority = 0
for(var/M in pref.body_markings)
+ priority += 1
var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[M]
var/mark_color = "[pref.body_markings[M]]"
for(var/BP in mark_datum.body_parts)
var/obj/item/organ/external/O = character.organs_by_name[BP]
if(O)
- O.markings[M] = list("color" = mark_color, "datum" = mark_datum)
+ O.markings[M] = list("color" = mark_color, "datum" = mark_datum, "priority" = priority)
+ character.markings_len = priority
var/list/last_descriptors = list()
if(islist(pref.body_descriptors))
diff --git a/code/modules/mob/living/carbon/human/human_defines_vr.dm b/code/modules/mob/living/carbon/human/human_defines_vr.dm
index 69a008029c..39c4f11cbc 100644
--- a/code/modules/mob/living/carbon/human/human_defines_vr.dm
+++ b/code/modules/mob/living/carbon/human/human_defines_vr.dm
@@ -9,6 +9,7 @@
var/sensorpref = 5 //Suit sensor loadout pref
var/wings_hidden = FALSE
var/tail_hidden = FALSE
+ var/markings_len = 0 //mostly an arbitrary number
/mob/living/carbon/human/proc/shadekin_get_energy()
var/datum/species/shadekin/SK = species
diff --git a/code/modules/mob/living/carbon/human/human_helpers_vr.dm b/code/modules/mob/living/carbon/human/human_helpers_vr.dm
index b8e9d75b30..14ed52bd8c 100644
--- a/code/modules/mob/living/carbon/human/human_helpers_vr.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers_vr.dm
@@ -7,3 +7,116 @@ var/static/icon/ingame_hud_med_vr = icon('icons/mob/hud_med_vr.dmi')
hud_list[STATUS_R_HUD] = gen_hud_image(ingame_hud_vr, src, plane = PLANE_CH_STATUS_R)
hud_list[BACKUP_HUD] = gen_hud_image(ingame_hud_vr, src, plane = PLANE_CH_BACKUP)
hud_list[VANTAG_HUD] = gen_hud_image(ingame_hud_vr, src, plane = PLANE_CH_VANTAG)
+
+/mob/living/carbon/human/proc/remove_marking(var/datum/sprite_accessory/marking/mark_datum)
+ if (!mark_datum)
+ return FALSE
+ var/successful = FALSE
+ for(var/BP in mark_datum.body_parts)
+ var/obj/item/organ/external/O = organs_by_name[BP]
+ if(O)
+ successful = O.markings.Remove(mark_datum.name) || successful
+ if (successful)
+ markings_len -= 1
+ update_dna()
+ update_icons_body()
+ return TRUE
+ return FALSE
+
+/mob/living/carbon/human/proc/add_marking(var/datum/sprite_accessory/marking/mark_datum, var/mark_color = "#000000")
+ if (!mark_datum)
+ return FALSE
+ var/success = FALSE
+ for(var/BP in mark_datum.body_parts)
+ var/obj/item/organ/external/O = organs_by_name[BP]
+ if(O)
+ success = TRUE
+ O.markings[mark_datum.name] = list("color" = mark_color, "datum" = mark_datum, "priority" = markings_len + 1)
+ if (success)
+ markings_len += 1
+ update_dna()
+ update_icons_body()
+ return success
+
+/mob/living/carbon/human/proc/change_priority_of_marking(var/datum/sprite_accessory/marking/mark_datum, var/move_down, var/swap = TRUE) //move_down should be true/false
+ if (!mark_datum)
+ return FALSE
+ var/change = move_down ? 1 : -1
+ var/success = FALSE
+ for(var/BP in mark_datum.body_parts)
+ var/obj/item/organ/external/O = organs_by_name[BP]
+ if(O)
+ var/index = O.markings.Find(mark_datum.name)
+ if (!index)
+ continue
+ var/change_from = O.markings[mark_datum.name]["priority"]
+ if (change_from == clamp(change_from + change, 1, markings_len))
+ continue
+ if (!success)
+ success = TRUE
+ change_priority_marking_to_priority(change_from + change, change_from)
+ O.markings[mark_datum.name]["priority"] = clamp(change_from + change, 1, markings_len)
+ if ((move_down && index == O.markings.len) || (!move_down && index == 1))
+ continue
+ if (O.markings[O.markings[index + change]]["priority"] == change_from)
+ moveElement(O.markings, index, index+(move_down ? 2 : -1))
+ if (success)
+ update_dna()
+ update_icons_body()
+ return TRUE
+
+/mob/living/carbon/human/proc/change_priority_marking_to_priority(var/priority, var/to_priority)
+ for (var/obj/item/organ/external/O in organs)
+ for (var/marking in O.markings)
+ if (O.markings[marking]["priority"] == priority)
+ O.markings[marking]["priority"] = to_priority
+
+/mob/living/carbon/human/proc/change_marking_color(var/datum/sprite_accessory/marking/mark_datum, var/mark_color = "#000000")
+ if (!mark_datum)
+ return FALSE
+ var/success = FALSE
+ for(var/BP in mark_datum.body_parts)
+ var/obj/item/organ/external/O = organs_by_name[BP]
+ if(O && O.markings[mark_datum.name] && O.markings[mark_datum.name]["color"] != mark_color)
+ success = TRUE
+ O.markings[mark_datum.name]["color"] = mark_color
+ if (success)
+ update_dna()
+ update_icons_body()
+ return success
+
+/mob/living/carbon/human/proc/get_prioritised_markings()
+ var/list/markings = list()
+ var/list/priorities = list()
+ for(var/obj/item/organ/external/O in organs)
+ if(O.markings?.len)
+ for (var/marking in O.markings)
+ var/priority = num2text(O.markings[marking]["priority"])
+ if (markings[priority])
+ markings[priority] |= list("[marking]" = O.markings[marking]["color"]) //yes I know technically you could have a limb that was attached that has the same marking as another limb with a different color but I'm too tired
+ else
+ priorities |= O.markings[marking]["priority"]
+ markings[priority] = list("[marking]" = O.markings[marking]["color"])
+ var/list/sorted = list()
+ while (priorities.len > 0)
+ var/priority = min(priorities)
+ priorities.Remove(priority)
+ priority = num2text(priority)
+ for (var/marking in markings[priority])
+ if (!(marking in sorted))
+ sorted[marking] = markings[priority][marking]
+ del(markings)
+ del(priorities)
+ markings_len = sorted.len
+ //todo - add an autofixing thing for having markings with the same priorities as another, and for having markings that should have the same priorities across bodyparts, but don't
+ //does not really need to happen, that kinda thing will only happen when putting another person's limb onto your own body
+ return sorted
+
+
+
+
+
+
+
+
+
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
index d6d7e5f386..a90d69689b 100644
--- a/code/modules/tgui/modules/appearance_changer.dm
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -26,6 +26,7 @@
var/list/valid_earstyles = list()
var/list/valid_tailstyles = list()
var/list/valid_wingstyles = list()
+ var/list/markings = null
/datum/tgui_module/appearance_changer/New(
var/host,
@@ -174,7 +175,7 @@
update_dna()
changed_hook(APPEARANCECHANGER_CHANGED_EYES)
return 1
- // VOREStation Add - Ears/Tails/Wings
+ // VOREStation Add - Ears/Tails/Wings/Markings
if("ear")
if(can_change(APPEARANCE_ALL_HAIR))
var/datum/sprite_accessory/ears/instance = locate(params["ref"])
@@ -277,6 +278,40 @@
owner.update_wing_showing()
changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR)
return 1
+ if("marking")
+ if(can_change(APPEARANCE_ALL_HAIR))
+ var/todo = params["todo"]
+ var/name_marking = params["name"]
+ switch (todo)
+ if (0) //delete
+ if (name_marking)
+ var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking]
+ if (target.remove_marking(mark_datum))
+ changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE)
+ return TRUE
+ if (1) //add
+ var/list/usable_markings = markings.Copy() ^ body_marking_styles_list.Copy()
+ var/new_marking = tgui_input_list(usr, "Choose a body marking:", "New Body Marking", usable_markings)
+ if(new_marking && can_still_topic(usr, state))
+ var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[new_marking]
+ if (target.add_marking(mark_datum))
+ changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE)
+ return TRUE
+ if (2) //move up
+ var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking]
+ if (target.change_priority_of_marking(mark_datum, FALSE))
+ return TRUE
+ if (3) //move down
+ var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking]
+ if (target.change_priority_of_marking(mark_datum, TRUE))
+ return TRUE
+ if (4) //color
+ var/current = markings[name_marking] ? markings[name_marking] : "#000000"
+ var/marking_color = input(usr, "Please select marking color", "Marking color", current) as color|null
+ if(marking_color && can_still_topic(usr, state))
+ var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking]
+ if (target.change_marking_color(mark_datum, marking_color))
+ return TRUE
// VOREStation Add End
return FALSE
@@ -370,6 +405,11 @@
data["ear_style"] = target.ear_style
data["tail_style"] = target.tail_style
data["wing_style"] = target.wing_style
+ var/list/markings_data[0]
+ markings = target.get_prioritised_markings()
+ for (var/marking in markings)
+ markings_data[++markings_data.len] = list("marking_name" = marking, "marking_color" = markings[marking])
+ data["markings"] = markings_data
// VOREStation Add End
data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR)
diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger.js b/tgui/packages/tgui/interfaces/AppearanceChanger.js
index 088306b969..23fd8d8ebb 100644
--- a/tgui/packages/tgui/interfaces/AppearanceChanger.js
+++ b/tgui/packages/tgui/interfaces/AppearanceChanger.js
@@ -18,6 +18,7 @@ export const AppearanceChanger = (props, context) => {
ear_style,
tail_style,
wing_style,
+ markings,
change_race,
change_gender,
change_eye_color,
@@ -128,6 +129,9 @@ export const AppearanceChanger = (props, context) => {
setTabIndex(7)}>
Wing
+ setTabIndex(8)}>
+ Markings
+
) : null}
{change_facial_hair ? (
@@ -145,6 +149,7 @@ export const AppearanceChanger = (props, context) => {
{change_hair && tabIndex === 5 ? : null}
{change_hair && tabIndex === 6 ? : null}
{change_hair && tabIndex === 7 ? : null}
+ {change_hair && tabIndex === 8 ? : null}
@@ -383,3 +388,28 @@ const AppearanceChangerWings = (props, context) => {
);
};
+
+const AppearanceChangerMarkings = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const { markings } = data;
+
+ return (
+
+
+
+
+ {markings.map((m) => (
+
+
+
+ ))}
+
+
+ );
+};