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 00000000000..4dfe1fc23a9 Binary files /dev/null and b/icons/mob/body_accessory_64.dmi differ diff --git a/paradise.dme b/paradise.dme index b17128a95b3..7e36a94c82d 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1324,6 +1324,7 @@ #include "code\modules\mob\living\carbon\brain\posibrain.dm" #include "code\modules\mob\living\carbon\brain\say.dm" #include "code\modules\mob\living\carbon\human\appearance.dm" +#include "code\modules\mob\living\carbon\human\body_accessories.dm" #include "code\modules\mob\living\carbon\human\death.dm" #include "code\modules\mob\living\carbon\human\emote.dm" #include "code\modules\mob\living\carbon\human\examine.dm"