mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-01-25 00:41:48 +00:00
Body accessory system
This commit adds a modular system for selectable body accessories. These accessories are currently split into two primary groups, "/body_accessory/body" and "/body_accessory/tail", for "body" sprites and "tail" sprites respectively. (also known as a subtype just for vorestation taur sprites) Basically, this allows for selectable tails. This commit doesn't include any subtypes, which will cause the startup hook to fail. The only subtypes this actually includes is a snake 'body'. Admins can select any tail, regardless of species restriction. Admin-only subtypes are simply defined by not adding any species to the whitelist. Note, as this system uses the body color system, admins may also use the body colors at any time regardless of race.
This commit is contained in:
@@ -127,6 +127,7 @@ datum/preferences
|
||||
var/species = "Human"
|
||||
var/language = "None" //Secondary language
|
||||
|
||||
var/body_accessory = null
|
||||
|
||||
var/speciesprefs = 0//I hate having to do this, I really do (Using this for oldvox code, making names universal I guess
|
||||
|
||||
@@ -185,17 +186,17 @@ datum/preferences
|
||||
|
||||
// jukebox volume
|
||||
var/volume = 100
|
||||
|
||||
|
||||
// BYOND membership
|
||||
var/unlock_content = 0
|
||||
|
||||
|
||||
/datum/preferences/New(client/C)
|
||||
b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
|
||||
if(istype(C))
|
||||
if(!IsGuestKey(C.key))
|
||||
unlock_content = C.IsByondMember()
|
||||
if(unlock_content)
|
||||
max_save_slots = MAX_SAVE_SLOTS_MEMBER
|
||||
max_save_slots = MAX_SAVE_SLOTS_MEMBER
|
||||
var/loaded_preferences_successfully = load_preferences(C)
|
||||
if(loaded_preferences_successfully)
|
||||
if(load_character(C))
|
||||
@@ -364,10 +365,14 @@ datum/preferences
|
||||
dat += "<br><b>Eyes</b><br>"
|
||||
dat += "<a href='?_src_=prefs;preference=eyes;task=input'>Change Color</a> <font face='fixedsys' size='3' color='#[num2hex(r_eyes, 2)][num2hex(g_eyes, 2)][num2hex(b_eyes, 2)]'><table style='display:inline;' bgcolor='#[num2hex(r_eyes, 2)][num2hex(g_eyes, 2)][num2hex(b_eyes)]'><tr><td>__</td></tr></table></font><br>"
|
||||
|
||||
if(species == "Unathi" || species == "Tajaran" || species == "Skrell" || species == "Slime People" || species == "Vulpkanin")
|
||||
if(species == "Unathi" || species == "Tajaran" || species == "Skrell" || species == "Slime People" || species == "Vulpkanin" || body_accessory_by_species[species] || check_rights(R_ADMIN, 1, user)) //admins can always fuck with this, because they are admins
|
||||
dat += "<br><b>Body Color</b><br>"
|
||||
dat += "<a href='?_src_=prefs;preference=skin;task=input'>Change Color</a> <font face='fixedsys' size='3' color='#[num2hex(r_skin, 2)][num2hex(g_skin, 2)][num2hex(b_skin, 2)]'><table style='display:inline;' bgcolor='#[num2hex(r_skin, 2)][num2hex(g_skin, 2)][num2hex(b_skin)]'><tr><td>__</td></tr></table></font>"
|
||||
|
||||
if(body_accessory_by_species[species] || check_rights(R_ADMIN, 1, user))
|
||||
dat += "<br><b>Body Accessory</b><br>"
|
||||
dat += "Accessory: <a href='?_src_=prefs;preference=body_accessory;task=input'>[body_accessory ? "[body_accessory]" : "None"]</a><br>"
|
||||
|
||||
dat += "</td></tr></table><hr><center>"
|
||||
|
||||
if (1) // General Preferences
|
||||
@@ -385,7 +390,7 @@ datum/preferences
|
||||
dat += "<b>Ghost radio:</b> <a href='?_src_=prefs;preference=ghost_radio'><b>[(toggles & CHAT_GHOSTRADIO) ? "Nearest Speakers" : "All Chatter"]</b></a><br>"
|
||||
if(config.allow_Metadata)
|
||||
dat += "<b>OOC notes:</b> <a href='?_src_=prefs;preference=metadata;task=input'><b>Edit</b></a><br>"
|
||||
|
||||
|
||||
if(user.client)
|
||||
if(user.client.holder)
|
||||
dat += "<b>Adminhelp sound:</b> "
|
||||
@@ -393,7 +398,7 @@ datum/preferences
|
||||
|
||||
if(check_rights(R_ADMIN,0))
|
||||
dat += "<b>OOC:</b> <span style='border: 1px solid #161616; background-color: [ooccolor ? ooccolor : normal_ooc_colour];'> </span> <a href='?_src_=prefs;preference=ooccolor;task=input'><b>Change</b></a><br>"
|
||||
|
||||
|
||||
if(unlock_content)
|
||||
dat += "<b>BYOND Membership Publicity:</b> <a href='?_src_=prefs;preference=publicity'><b>[(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]</b></a><br>"
|
||||
|
||||
@@ -1160,10 +1165,27 @@ datum/preferences
|
||||
|
||||
valid_hairstyles[hairstyle] = hair_styles_list[hairstyle]
|
||||
|
||||
var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles
|
||||
var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in valid_hairstyles
|
||||
if(new_h_style)
|
||||
h_style = new_h_style
|
||||
|
||||
if("body_accessory")
|
||||
var/list/possible_body_accessories = list()
|
||||
if(check_rights(R_ADMIN, 1, user))
|
||||
possible_body_accessories = body_accessory_by_name.Copy()
|
||||
else
|
||||
for(var/B in body_accessory_by_name)
|
||||
var/datum/body_accessory/accessory = body_accessory_by_name[B]
|
||||
if(!istype(accessory))
|
||||
possible_body_accessories += "None" //the only null entry should be the "None" option
|
||||
continue
|
||||
if(species in accessory.allowed_species)
|
||||
possible_body_accessories += B
|
||||
|
||||
var/new_body_accessory = input(user, "Choose your body accessory:", "Character Preference") as null|anything in possible_body_accessories
|
||||
if(new_body_accessory)
|
||||
body_accessory = new_body_accessory
|
||||
|
||||
if("facial")
|
||||
var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as color|null
|
||||
if(new_facial)
|
||||
@@ -1225,7 +1247,7 @@ datum/preferences
|
||||
s_tone = 35 - max(min( round(new_s_tone), 220),1)
|
||||
|
||||
if("skin")
|
||||
if(species == "Unathi" || species == "Tajaran" || species == "Skrell" || species == "Slime People"|| species == "Vulpkanin")
|
||||
if(species == "Unathi" || species == "Tajaran" || species == "Skrell" || species == "Slime People"|| species == "Vulpkanin" || body_accessory_by_species[species] || check_rights(R_ADMIN, 1, user))
|
||||
var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference") as color|null
|
||||
if(new_skin)
|
||||
r_skin = hex2num(copytext(new_skin, 2, 4))
|
||||
@@ -1360,7 +1382,7 @@ datum/preferences
|
||||
if("publicity")
|
||||
if(unlock_content)
|
||||
toggles ^= MEMBER_PUBLIC
|
||||
|
||||
|
||||
if("gender")
|
||||
if(gender == MALE)
|
||||
gender = FEMALE
|
||||
@@ -1549,6 +1571,9 @@ datum/preferences
|
||||
character.underwear = underwear
|
||||
character.undershirt = undershirt
|
||||
|
||||
if(body_accessory)
|
||||
character.body_accessory = body_accessory_by_name["[body_accessory]"]
|
||||
|
||||
if(backbag > 4 || backbag < 1)
|
||||
backbag = 1 //Same as above
|
||||
character.backbag = backbag
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
rlimb_data = params2list(query.item[51])
|
||||
nanotrasen_relation = query.item[52]
|
||||
speciesprefs = text2num(query.item[53])
|
||||
body_accessory = query.item[54]
|
||||
|
||||
//Sanitize
|
||||
metadata = sanitize_text(metadata, initial(metadata))
|
||||
@@ -267,7 +268,8 @@
|
||||
organ_data='[organlist]',
|
||||
rlimb_data='[rlimblist]',
|
||||
nanotrasen_relation='[nanotrasen_relation]',
|
||||
speciesprefs='[speciesprefs]'
|
||||
speciesprefs='[speciesprefs]',
|
||||
body_accessory='[body_accessory]'
|
||||
WHERE ckey='[C.ckey]'
|
||||
AND slot='[default_slot]'"}
|
||||
)
|
||||
@@ -295,7 +297,8 @@
|
||||
job_karma_high, job_karma_med, job_karma_low,
|
||||
flavor_text, med_record, sec_record, gen_record,
|
||||
player_alt_titles, be_special,
|
||||
disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs)
|
||||
disabilities, organ_data, rlimb_data, nanotrasen_relation, speciesprefs,
|
||||
body_accessory)
|
||||
VALUES
|
||||
('[C.ckey]', '[default_slot]', '[sql_sanitize_text(metadata)]', '[sql_sanitize_text(real_name)]', '[be_random_name]','[gender]',
|
||||
'[age]', '[sql_sanitize_text(species)]', '[sql_sanitize_text(language)]',
|
||||
@@ -312,7 +315,8 @@
|
||||
'[job_karma_high]', '[job_karma_med]', '[job_karma_low]',
|
||||
'[sql_sanitize_text(flavor_text)]', '[sql_sanitize_text(med_record)]', '[sql_sanitize_text(sec_record)]', '[sql_sanitize_text(gen_record)]',
|
||||
'[playertitlelist]', '[be_special]',
|
||||
'[disabilities]', '[organlist]', '[rlimblist]', '[nanotrasen_relation]', '[speciesprefs]')
|
||||
'[disabilities]', '[organlist]', '[rlimblist]', '[nanotrasen_relation]', '[speciesprefs]',
|
||||
'[body_accessory]')
|
||||
"}
|
||||
)
|
||||
|
||||
|
||||
79
code/modules/mob/living/carbon/human/body_accessories.dm
Normal file
79
code/modules/mob/living/carbon/human/body_accessories.dm
Normal file
@@ -0,0 +1,79 @@
|
||||
var/global/list/body_accessory_by_name = list("None" = null)
|
||||
|
||||
/hook/startup/proc/initalize_body_accessories()
|
||||
//each accessory subtype needs it's own loop
|
||||
for(var/type in subtypesof(/datum/body_accessory/body))
|
||||
var/datum/body_accessory/ac1 = new type
|
||||
if(istype(ac1))
|
||||
body_accessory_by_name["[ac1.name]"] = ac1
|
||||
|
||||
for(var/type in subtypesof(/datum/body_accessory/tail))
|
||||
var/datum/body_accessory/ac2 = new type
|
||||
if(istype(ac2))
|
||||
body_accessory_by_name["[ac2.name]"] = ac2
|
||||
|
||||
if(body_accessory_by_name.len)
|
||||
if(initialize_body_accessory_by_species())
|
||||
return 1
|
||||
|
||||
return 0 //fail if no bodies are found
|
||||
|
||||
var/global/list/body_accessory_by_species = list("None" = null)
|
||||
|
||||
/proc/initialize_body_accessory_by_species()
|
||||
for(var/B in body_accessory_by_name)
|
||||
var/datum/body_accessory/accessory = body_accessory_by_name[B]
|
||||
if(!istype(accessory)) continue
|
||||
for(var/A in accessory.allowed_species)
|
||||
body_accessory_by_species[A] += accessory
|
||||
|
||||
if(body_accessory_by_species.len)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/datum/body_accessory
|
||||
var/name = "default"
|
||||
|
||||
var/icon = null
|
||||
var/icon_state = ""
|
||||
|
||||
var/animated_icon = null
|
||||
var/animated_icon_state = ""
|
||||
|
||||
var/blend_mode = null
|
||||
|
||||
var/list/pixel_offsets = list("x" = 0, "y" = 0)
|
||||
|
||||
var/list/allowed_species = list()
|
||||
|
||||
/datum/body_accessory/proc/try_restrictions(var/mob/living/carbon/human/H)
|
||||
return 1
|
||||
|
||||
/datum/body_accessory/proc/get_pixel_x(var/mob/living/carbon/human/H)
|
||||
return pixel_offsets["x"]
|
||||
|
||||
/datum/body_accessory/proc/get_pixel_y(var/mob/living/carbon/human/H)
|
||||
return pixel_offsets["y"]
|
||||
|
||||
|
||||
//Bodies
|
||||
/datum/body_accessory/body
|
||||
blend_mode = ICON_MULTIPLY
|
||||
|
||||
/datum/body_accessory/body/snake
|
||||
name = "Snake"
|
||||
|
||||
icon = 'icons/mob/body_accessory_64.dmi'
|
||||
icon_state = "naga"
|
||||
|
||||
pixel_offsets = list("x" = -16, "y" = 0)
|
||||
|
||||
//Tails
|
||||
/datum/body_accessory/tail
|
||||
blend_mode = ICON_ADD
|
||||
|
||||
/datum/body_accessory/tail/try_restrictions(var/mob/living/carbon/human/H)
|
||||
if(!H.wear_suit || !(H.wear_suit.flags_inv & HIDETAIL) && !istype(H.wear_suit, /obj/item/clothing/suit/space))
|
||||
return 1
|
||||
return 0
|
||||
@@ -81,4 +81,6 @@
|
||||
var/lastFart = 0 // Toxic fart cooldown.
|
||||
|
||||
var/fire_dmi = 'icons/mob/OnFire.dmi'
|
||||
var/fire_sprite = "Standing"
|
||||
var/fire_sprite = "Standing"
|
||||
|
||||
var/datum/body_accessory/body_accessory = null
|
||||
@@ -337,7 +337,7 @@ var/global/list/damage_icon_parts = list()
|
||||
stand_icon.Blend(lips, ICON_OVERLAY)
|
||||
|
||||
//tail
|
||||
update_tail_showing(0)
|
||||
update_tail_layer(0)
|
||||
|
||||
|
||||
//HAIR OVERLAY
|
||||
@@ -756,12 +756,12 @@ var/global/list/damage_icon_parts = list()
|
||||
|
||||
overlays_standing[SUIT_LAYER] = standing
|
||||
|
||||
update_tail_showing(0)
|
||||
update_tail_layer(0)
|
||||
|
||||
else
|
||||
overlays_standing[SUIT_LAYER] = null
|
||||
|
||||
update_tail_showing(0)
|
||||
update_tail_layer(0)
|
||||
|
||||
update_collar(0)
|
||||
|
||||
@@ -896,10 +896,20 @@ var/global/list/damage_icon_parts = list()
|
||||
overlays_standing[L_HAND_LAYER] = null
|
||||
if(update_icons) update_icons()
|
||||
|
||||
/mob/living/carbon/human/proc/update_tail_showing(var/update_icons=1)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/update_tail_layer(var/update_icons=1)
|
||||
overlays_standing[TAIL_LAYER] = null
|
||||
|
||||
if(species.tail && species.bodyflags & HAS_TAIL)
|
||||
if(body_accessory)
|
||||
if(body_accessory.try_restrictions(src))
|
||||
var/icon/accessory_s = new/icon("icon" = body_accessory.icon, "icon_state" = body_accessory.icon_state)
|
||||
accessory_s.Blend(rgb(r_skin, g_skin, b_skin), body_accessory.blend_mode)
|
||||
|
||||
overlays_standing[TAIL_LAYER] = image(accessory_s, "pixel_x" = body_accessory.get_pixel_x(src), "pixel_y" = body_accessory.get_pixel_y(src))
|
||||
|
||||
|
||||
else if(species.tail && species.bodyflags & HAS_TAIL) //no tailless tajaran
|
||||
if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL) && !istype(wear_suit, /obj/item/clothing/suit/space))
|
||||
var/icon/tail_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[species.tail]_s")
|
||||
tail_s.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD)
|
||||
@@ -913,7 +923,14 @@ var/global/list/damage_icon_parts = list()
|
||||
/mob/living/carbon/human/proc/start_tail_wagging(var/update_icons=1)
|
||||
overlays_standing[TAIL_LAYER] = null
|
||||
|
||||
if(species.tail && species.bodyflags & HAS_TAIL)
|
||||
if(body_accessory)
|
||||
if(body_accessory.animated_icon && body_accessory.animated_icon_state)
|
||||
var/icon/accessory_s = new/icon("icon" = body_accessory.animated_icon, "icon_state" = body_accessory.animated_icon_state)
|
||||
accessory_s.Blend(rgb(r_skin, g_skin, b_skin), body_accessory.blend_mode)
|
||||
|
||||
overlays_standing[TAIL_LAYER] = image(accessory_s, "pixel_x" = body_accessory.get_pixel_x(src), "pixel_y" = body_accessory.get_pixel_y(src))
|
||||
|
||||
else if(species.tail && species.bodyflags & HAS_TAIL)
|
||||
var/icon/tailw_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[species.tail]w_s")
|
||||
tailw_s.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD)
|
||||
|
||||
@@ -925,14 +942,11 @@ var/global/list/damage_icon_parts = list()
|
||||
/mob/living/carbon/human/proc/stop_tail_wagging(var/update_icons=1)
|
||||
overlays_standing[TAIL_LAYER] = null
|
||||
|
||||
if(species.tail && species.bodyflags & HAS_TAIL)
|
||||
var/icon/tail_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[species.tail]_s")
|
||||
tail_s.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD)
|
||||
update_tail_layer(update_icons) //just trigger a full update for normal stationary sprites
|
||||
|
||||
overlays_standing[TAIL_LAYER] = image(tail_s)
|
||||
|
||||
if(update_icons)
|
||||
update_icons()
|
||||
/mob/living/carbon/human/handle_transform_change()
|
||||
..()
|
||||
update_tail_layer()
|
||||
|
||||
//Adds a collar overlay above the helmet layer if the suit has one
|
||||
// Suit needs an identically named sprite in icons/mob/collar.dmi
|
||||
|
||||
@@ -28,4 +28,8 @@
|
||||
|
||||
if(changed)
|
||||
animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT)
|
||||
floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
|
||||
handle_transform_change()
|
||||
floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
|
||||
|
||||
/mob/living/carbon/proc/handle_transform_change()
|
||||
return
|
||||
Reference in New Issue
Block a user