From 3ee80a35867b00a5363b042ba2d617e40a98e9bd Mon Sep 17 00:00:00 2001 From: Tigercat2000 Date: Sat, 5 Sep 2015 17:20:28 -0700 Subject: [PATCH] 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. --- SQL/paradise_schema.sql | 1 + code/modules/client/preferences.dm | 43 ++++++++-- code/modules/client/preferences_mysql.dm | 10 ++- .../living/carbon/human/body_accessories.dm | 79 ++++++++++++++++++ .../mob/living/carbon/human/human_defines.dm | 4 +- .../mob/living/carbon/human/update_icons.dm | 40 ++++++--- .../modules/mob/living/carbon/update_icons.dm | 6 +- icons/mob/body_accessory_64.dmi | Bin 0 -> 863 bytes paradise.dme | 1 + 9 files changed, 157 insertions(+), 27 deletions(-) create mode 100644 code/modules/mob/living/carbon/human/body_accessories.dm create mode 100644 icons/mob/body_accessory_64.dmi diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index c6045cd835e..f36d62f7339 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -78,6 +78,7 @@ CREATE TABLE `characters` ( `rlimb_data` mediumtext NOT NULL, `nanotrasen_relation` varchar(45) NOT NULL, `speciesprefs` int(1) NOT NULL, + `body_accessory` mediumtext NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=18747 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 338d57dd616..785e4718617 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -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 += "
Eyes
" dat += "Change Color
__

" - 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 += "
Body Color
" dat += "Change Color
__
" + if(body_accessory_by_species[species] || check_rights(R_ADMIN, 1, user)) + dat += "
Body Accessory
" + dat += "Accessory: [body_accessory ? "[body_accessory]" : "None"]
" + dat += "
" if (1) // General Preferences @@ -385,7 +390,7 @@ datum/preferences dat += "Ghost radio: [(toggles & CHAT_GHOSTRADIO) ? "Nearest Speakers" : "All Chatter"]
" if(config.allow_Metadata) dat += "OOC notes: Edit
" - + if(user.client) if(user.client.holder) dat += "Adminhelp sound: " @@ -393,7 +398,7 @@ datum/preferences if(check_rights(R_ADMIN,0)) dat += "OOC:     Change
" - + if(unlock_content) dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" @@ -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 diff --git a/code/modules/client/preferences_mysql.dm b/code/modules/client/preferences_mysql.dm index 96e51448c6d..f89a76d7689 100644 --- a/code/modules/client/preferences_mysql.dm +++ b/code/modules/client/preferences_mysql.dm @@ -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]') "} ) diff --git a/code/modules/mob/living/carbon/human/body_accessories.dm b/code/modules/mob/living/carbon/human/body_accessories.dm new file mode 100644 index 00000000000..4ad65f05ffb --- /dev/null +++ b/code/modules/mob/living/carbon/human/body_accessories.dm @@ -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 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index bc10d54b1c8..38523536807 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -81,4 +81,6 @@ var/lastFart = 0 // Toxic fart cooldown. var/fire_dmi = 'icons/mob/OnFire.dmi' - var/fire_sprite = "Standing" \ No newline at end of file + var/fire_sprite = "Standing" + + var/datum/body_accessory/body_accessory = null \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 2e7a5e2be23..1fb591ba8a3 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -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 diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index 02fe01c39a1..e22ad5e66dc 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -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(). \ No newline at end of file + 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 \ No newline at end of file diff --git a/icons/mob/body_accessory_64.dmi b/icons/mob/body_accessory_64.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4dfe1fc23a9d540660ec0bea90c5812786874dc7 GIT binary patch literal 863 zcmV-l1EBngP)5H4+R@R`r>Ca4xVVRhhJt{Aj*g9bdU~#|u8@z9a&mHngoL!S zv!tY?tgNj5l+kAZ0004WQchCV=-0C=2JR&a84 z_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a?DJ)I5Sc+(=$qd9AhJ{;*!LYR3K9+ zFEKq)iHkEOv#1!Phl?|<00073Nkl+MhCLNGnsr>d3C6RCC~%zT*u!akk4y1Kf$y1Kf$ezDGn zu~x%jtOo!ca03{IGN*LjfI84W066~Zc7W61ORV0;ln*5N&ETK9d2m+)A_#XvuB@N7yuLK&-vl*z8 zK=KJ7H2_916*dq^3FcWxKB@^Od)-|FYA}^HFz(5p1eE~U1}5^;-vO>P2Ye)`zX#d? za=tVG*1+up$Q1yL<8?mhN;FmiAj@;UG{6Ng&IPC>UkT{S$HOfci@rkc2{c_Bz_Uy) zGB3$z0!BEhHlGR3Nf*)WcKayHSA?UQzm2=Py1Kf$y8bf_rXPzK97aA0etNg(e-$yf zs4E#WZtAy>hIY4MT(NC&sJ z2Cq(sjB%#r1>^CG13)~*=-UB&WCM6w7lxPva0xW6=mSxVi0uG~F_QW|>=FcW&j3uj zJs)g26>Q1`t@mJck`Hz%YPu)nT4jLkQ?Re+g3a42kOJ&V!oEn{60!h{yX|PCvyCNU z+q+=(nxA~^s`PsxTk49OrP~6k^qLo^7XZj5eMTo+4#{@Pz!ob@bF_0Uzr6t?uz0hz zjgPz`6Z7Xz3SW7Ok>?R@qoA