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:
Tigercat2000
2015-09-05 17:20:28 -07:00
parent 789794e0e1
commit 3ee80a3586
9 changed files with 157 additions and 27 deletions

View File

@@ -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];'>&nbsp;&nbsp;&nbsp;</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

View File

@@ -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]')
"}
)

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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