Character Setup Rework (#17576)

* Character Setup Rework

Little tweaks

Species selection done~

Merk randomize body button

Body color and eye color

Merk more replaced code

Convert robolimbs to tgui

Add a warning if flavor text/ooc notes are too short

Custom preview icons for species selector!

A sidequest that only took 8
hours. Also add digitigrade and blood type.

Remove unused body_descriptor system completely

Finish the general tab~

Reorganization to prepare for loadout page creation

* Start of work on the loadout screen

* Only send the data that's actually selected

* Get rid of these ugly ../../../..

* Retype this to avoid conflicts

* Holy shit why did this work this way

* Finish loadout screen

* Add copy loadout function

* Finish occupation page

* Move Special Roles into general tab

* Fix path conflict

* Move size prefs to general tab

* Convert jukebox and volume settings to datum/preference

* Fix a little mergery fuckery in loadout

* Migrate jukebox to new range

* Fix TabbedMenu tabs

* Fix wordwrap for loadout screen

* Kill the vore tab, just traits left to convert

* Convert custom messages

* Convert custom species name

* Convert blood color and reagents

* Move icobase to tgui

* Finished

* This can fuck off too

* Fix a few bugs

* Update index.tsx

* initial for emote sound mode switch

* Make show_roles actually work, hide fluff items

* Fix not being able to select species

* Add emote_sound_mode to body tab

* Fix runtime when no active gear list is present

* Add a save notification to character setup

* Switch to floating

* Add more search bars

* Whoops forgot to add this

---------

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
ShadowLarkens
2025-05-24 18:20:51 -07:00
committed by GitHub
parent 44d1ce470e
commit 31e8e009db
106 changed files with 8770 additions and 4739 deletions
@@ -1,124 +0,0 @@
/datum/category_item/player_setup_item/antagonism/basic
name = "Basic"
sort_order = 1
var/datum/paiCandidate/candidate
/datum/category_item/player_setup_item/antagonism/basic/load_character(list/save_data)
pref.exploit_record = save_data["exploit_record"]
pref.antag_faction = save_data["antag_faction"]
pref.antag_vis = save_data["antag_vis"]
/datum/category_item/player_setup_item/antagonism/basic/save_character(list/save_data)
save_data["exploit_record"] = pref.exploit_record
save_data["antag_faction"] = pref.antag_faction
save_data["antag_vis"] = pref.antag_vis
/datum/category_item/player_setup_item/antagonism/basic/load_preferences(datum/json_savefile/savefile)
if(!candidate)
candidate = new()
var/preference_mob = preference_mob()
if(!preference_mob)// No preference mob - this happens when we're called from client/New() before it calls ..() (via datum/preferences/New())
spawn()
preference_mob = preference_mob()
if(!preference_mob)
return
candidate.savefile_load(preference_mob)
return
candidate.savefile_load(preference_mob)
/datum/category_item/player_setup_item/antagonism/basic/save_preferences(datum/json_savefile/savefile)
if(!candidate)
return
if(!preference_mob())
return
candidate.savefile_save(preference_mob())
/datum/category_item/player_setup_item/antagonism/basic/sanitize_character()
if(!pref.antag_faction) pref.antag_faction = "None"
if(!pref.antag_vis) pref.antag_vis = "Hidden"
// Moved from /datum/preferences/proc/copy_to()
/datum/category_item/player_setup_item/antagonism/basic/copy_to_mob(var/mob/living/carbon/human/character)
character.exploit_record = pref.exploit_record
character.antag_faction = pref.antag_faction
character.antag_vis = pref.antag_vis
/datum/category_item/player_setup_item/antagonism/basic/content(var/mob/user)
. += "Faction: <a href='byond://?src=\ref[src];antagfaction=1'>[pref.antag_faction]</a><br/>"
. += "Visibility: <a href='byond://?src=\ref[src];antagvis=1'>[pref.antag_vis]</a><br/>"
. += span_bold("Uplink Type:") + " <a href='byond://?src=\ref[src];antagtask=1'>[pref.read_preference(/datum/preference/choiced/uplinklocation)]</a>"
. +="<br>"
. +=span_bold("Exploitable information:") + "<br>"
if(jobban_isbanned(user, "Records"))
. += span_bold("You are banned from using character records.") + "<br>"
else
. +="<a href='byond://?src=\ref[src];exploitable_record=1'>[TextPreview(pref.exploit_record,40)]</a><br>"
. += "<br>"
. += span_bold("pAI:") + "<br>"
if(!candidate)
log_debug("[user] pAI prefs have a null candidate var.")
return .
. += "Name: <a href='byond://?src=\ref[src];option=name'>[candidate.name ? candidate.name : "None Set"]</a><br>"
. += "Description: <a href='byond://?src=\ref[src];option=desc'>[candidate.description ? TextPreview(candidate.description, 40) : "None Set"]</a><br>"
. += "Role: <a href='byond://?src=\ref[src];option=role'>[candidate.role ? TextPreview(candidate.role, 40) : "None Set"]</a><br>"
. += "OOC Comments: <a href='byond://?src=\ref[src];option=ooc'>[candidate.comments ? TextPreview(candidate.comments, 40) : "None Set"]</a><br>"
/datum/category_item/player_setup_item/antagonism/basic/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["uplinklocation"])
var/new_uplinklocation = tgui_input_list(user, "Choose your uplink location:", "Character Preference", GLOB.uplink_locations, pref.read_preference(/datum/preference/choiced/uplinklocation))
if(new_uplinklocation && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/choiced/uplinklocation, new_uplinklocation)
return TOPIC_REFRESH
if(href_list["exploitable_record"])
var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0)
if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user))
pref.exploit_record = exploitmsg
return TOPIC_REFRESH
if(href_list["antagfaction"])
var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.antag_faction = raw_choice
else
pref.antag_faction = choice
return TOPIC_REFRESH
if(href_list["antagvis"])
var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
else
pref.antag_vis = choice
return TOPIC_REFRESH
if(href_list["option"])
var/t
switch(href_list["option"])
if("name")
t = sanitizeName(tgui_input_text(user, "Enter a name for your pAI", "Global Preference", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN, 1)
if(t && CanUseTopic(user))
candidate.name = t
if("desc")
t = tgui_input_text(user, "Enter a description for your pAI", "Global Preference", html_decode(candidate.description), multiline = TRUE, prevent_enter = TRUE)
if(!isnull(t) && CanUseTopic(user))
candidate.description = sanitize(t)
if("role")
t = tgui_input_text(user, "Enter a role for your pAI", "Global Preference", html_decode(candidate.role))
if(!isnull(t) && CanUseTopic(user))
candidate.role = sanitize(t)
if("ooc")
t = tgui_input_text(user, "Enter any OOC comments", "Global Preference", html_decode(candidate.comments), multiline = TRUE, prevent_enter = TRUE)
if(!isnull(t) && CanUseTopic(user))
candidate.comments = sanitize(t)
return TOPIC_REFRESH
return ..()
@@ -1,64 +0,0 @@
var/global/list/special_roles = list( //keep synced with the defines BE_* in setup.dm --rastaf
//some autodetection here.
// Change these to 0 if the equivalent mode is disabled for whatever reason!
"traitor" = 0, // 0
"operative" = 0, // 1
"changeling" = 0, // 2
"wizard" = 0, // 3
"malf AI" = 0, // 4
"revolutionary" = 0, // 5
"alien candidate" = 0, // 6
"positronic brain" = 1, // 7
"cultist" = 0, // 8
"renegade" = 0, // 9
"ninja" = 0, // 10
"raider" = 0, // 11
"diona" = 0, // 12
"mutineer" = 0, // 13
"loyalist" = 0, // 14
"pAI candidate" = 1, // 15
//VOREStation Add
"lost drone" = 1, // 16
"maint pred" = 1, // 17
"maint lurker" = 1, // 18
"morph" = 1, // 19
"corgi" = 1, // 20
"cursed sword" = 1, // 21
"Ship Survivor" = 1, // 22
//VOREStation Add End
)
/datum/category_item/player_setup_item/antagonism/candidacy
name = "Candidacy"
sort_order = 2
/datum/category_item/player_setup_item/antagonism/candidacy/load_character(list/save_data)
pref.be_special = save_data["be_special"]
/datum/category_item/player_setup_item/antagonism/candidacy/save_character(list/save_data)
save_data["be_special"] = pref.be_special
/datum/category_item/player_setup_item/antagonism/candidacy/sanitize_character()
pref.be_special = sanitize_integer(pref.be_special, 0, 16777215, initial(pref.be_special)) //VOREStation Edit - 24 bits of support
/datum/category_item/player_setup_item/antagonism/candidacy/content(var/mob/user)
if(jobban_isbanned(user, JOB_SYNDICATE))
. += span_bold("You are banned from antagonist roles.")
pref.be_special = 0
else
var/n = 0
for (var/i in special_roles)
if(special_roles[i]) //if mode is available on the server
if(jobban_isbanned(user, i) || (i == "positronic brain" && jobban_isbanned(user, JOB_AI) && jobban_isbanned(user, JOB_CYBORG)) || (i == "pAI candidate" && jobban_isbanned(user, JOB_PAI)))
. += span_bold("Be [i]:") + " " + span_red(span_bold(" \[BANNED]")) + "<br>"
else
. += span_bold("Be [i]:") + " <a href='byond://?src=\ref[src];be_special=[n]'>" + span_bold("[pref.be_special&(1<<n) ? "Yes" : "No"]") + "</a><br>"
n++
/datum/category_item/player_setup_item/antagonism/candidacy/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["be_special"])
var/num = text2num(href_list["be_special"])
pref.be_special ^= (1<<num)
return TOPIC_REFRESH
return ..()
@@ -1,7 +1,14 @@
#define AUTOHISS_OFF 0
#define AUTOHISS_BASIC 1
#define AUTOHISS_FULL 2
/datum/preferences
var/biological_gender = MALE
var/identifying_gender = MALE
var/vore_egg_type = "Egg" //The egg type they have.
var/autohiss = "Full" // VOREStation Add: Whether we have Autohiss on. I'm hijacking the egg panel bc this one has a shitton of unused space.
/datum/preferences/proc/set_biological_gender(var/gender)
biological_gender = gender
identifying_gender = gender
@@ -11,16 +18,20 @@
sort_order = 1
/datum/category_item/player_setup_item/general/basic/load_character(list/save_data)
pref.real_name = save_data["real_name"]
pref.nickname = save_data["nickname"]
pref.biological_gender = save_data["gender"]
pref.identifying_gender = save_data["id_gender"]
pref.real_name = save_data["real_name"]
pref.nickname = save_data["nickname"]
pref.biological_gender = save_data["gender"]
pref.identifying_gender = save_data["id_gender"]
pref.vore_egg_type = save_data["vore_egg_type"]
pref.autohiss = save_data["autohiss"]
/datum/category_item/player_setup_item/general/basic/save_character(list/save_data)
save_data["real_name"] = pref.real_name
save_data["nickname"] = pref.nickname
save_data["gender"] = pref.biological_gender
save_data["id_gender"] = pref.identifying_gender
save_data["vore_egg_type"] = pref.vore_egg_type
save_data["autohiss"] = pref.autohiss
/datum/category_item/player_setup_item/general/basic/sanitize_character()
pref.biological_gender = sanitize_inlist(pref.biological_gender, get_genders(), pick(get_genders()))
@@ -29,6 +40,8 @@
if(!pref.real_name)
pref.real_name = random_name(pref.identifying_gender, pref.species)
pref.nickname = sanitize_name(pref.nickname)
pref.vore_egg_type = sanitize_inlist(pref.vore_egg_type, GLOB.global_vore_egg_types, initial(pref.vore_egg_type))
pref.autohiss = sanitize_inlist(pref.autohiss, list("None", "Basic", "Full"), initial(pref.autohiss))
// Moved from /datum/preferences/proc/copy_to()
/datum/category_item/player_setup_item/general/basic/copy_to_mob(var/mob/living/carbon/human/character)
@@ -50,174 +63,232 @@
character.gender = pref.biological_gender
character.identifying_gender = pref.identifying_gender
/datum/category_item/player_setup_item/general/basic/content()
. = list()
. += span_bold("Name:") + " "
. += "<a href='byond://?src=\ref[src];rename=1'><b>[pref.real_name]</b></a><br>"
. += "<a href='byond://?src=\ref[src];random_name=1'>Randomize Name</A><br>"
. += "<a href='byond://?src=\ref[src];always_random_name=1'>Always Random Name: [pref.read_preference(/datum/preference/toggle/human/name_is_always_random) ? "Yes" : "No"]</a><br>"
. += span_bold("Nickname:") + " "
. += "<a href='byond://?src=\ref[src];nickname=1'><b>[pref.nickname]</b></a>"
. += "(<a href='byond://?src=\ref[src];reset_nickname=1'>Clear</A>)"
. += "<br>"
. += span_bold("Biological Sex:") + " <a href='byond://?src=\ref[src];bio_gender=1'><b>[gender2text(pref.biological_gender)]</b></a><br>"
. += span_bold("Pronouns:") + " <a href='byond://?src=\ref[src];id_gender=1'><b>[genders_to_pronoun_set[pref.identifying_gender]]</b></a><br>"
. += span_bold("Age:") + " <a href='byond://?src=\ref[src];age=1'>[pref.read_preference(/datum/preference/numeric/human/age)]</a> <b>Birthday:</b> <a href='byond://?src=\ref[src];bday_month=1'>[pref.read_preference(/datum/preference/numeric/human/bday_month)]</a><b>/</b><a href='byond://?src=\ref[src];bday_day=1'>[pref.read_preference(/datum/preference/numeric/human/bday_day)]</a> - <b>Announce?:</b> <a href='byond://?src=\ref[src];bday_announce=1'>[pref.read_preference(/datum/preference/toggle/human/bday_announce) ? "Yes" : "Disabled"]</a><br>"
. += span_bold("Spawn Point:") + " <a href='byond://?src=\ref[src];spawnpoint=1'>[pref.read_preference(/datum/preference/choiced/living/spawnpoint)]</a><br>"
if(CONFIG_GET(flag/allow_metadata))
. += span_bold("OOC Notes: <a href='byond://?src=\ref[src];edit_ooc_notes=1'>Edit</a><a href='byond://?src=\ref[src];edit_ooc_note_favs=1'>Favs</a><a href='byond://?src=\ref[src];edit_ooc_note_likes=1'>Likes</a><a href='byond://?src=\ref[src];edit_ooc_note_maybes=1'>Maybes</a><a href='byond://?src=\ref[src];edit_ooc_note_dislikes=1'>Dislikes</a>") + "<br>"
. += "Detailed field or short list system? <a href='byond://?src=\ref[src];edit_ooc_note_style=1'>[pref.read_preference(/datum/preference/toggle/living/ooc_notes_style) ? "Lists" : "Fields"]</a><br><br>"
. = jointext(.,null)
character.vore_egg_type = pref.vore_egg_type
// VOREStation Add
if(pref.client) // Safety, just in case so we don't runtime.
if(!pref.autohiss)
pref.client.autohiss_mode = AUTOHISS_FULL
else
switch(pref.autohiss)
if("Full")
pref.client.autohiss_mode = AUTOHISS_FULL
if("Basic")
pref.client.autohiss_mode = AUTOHISS_BASIC
if("Off")
pref.client.autohiss_mode = AUTOHISS_OFF
/datum/category_item/player_setup_item/general/basic/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["rename"])
var/raw_name = tgui_input_text(user, "Choose your character's name:", "Character Name")
if (!isnull(raw_name) && CanUseTopic(user))
var/new_name = sanitize_name(raw_name, pref.species, is_FBP())
if(new_name)
pref.real_name = new_name
return TOPIC_REFRESH
else
to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ."))
return TOPIC_NOACTION
/datum/category_item/player_setup_item/general/basic/tgui_data(mob/user)
var/list/data = ..()
else if(href_list["random_name"])
pref.real_name = random_name(pref.identifying_gender, pref.species)
return TOPIC_REFRESH
data["real_name"] = pref.real_name
data["be_random_name"] = pref.read_preference(/datum/preference/toggle/human/name_is_always_random)
data["nickname"] = pref.nickname
data["biological_sex"] = gender2text(pref.biological_gender)
data["identifying_gender"] = gender2text(pref.identifying_gender)
data["age"] = pref.read_preference(/datum/preference/numeric/human/age)
data["bday_month"] = pref.read_preference(/datum/preference/numeric/human/bday_month)
data["bday_day"] = pref.read_preference(/datum/preference/numeric/human/bday_day)
data["bday_announce"] = pref.read_preference(/datum/preference/toggle/human/bday_announce)
data["spawnpoint"] = pref.read_preference(/datum/preference/choiced/living/spawnpoint)
data["ooc_notes_length"] = LAZYLEN(pref.read_preference(/datum/preference/text/living/ooc_notes))
data["vore_egg_type"] = pref.vore_egg_type
data["autohiss"] = pref.autohiss
data["emote_sound_mode"] = pref.read_preference(/datum/preference/choiced/living/emote_sound_mode)
else if(href_list["always_random_name"])
pref.update_preference_by_type(/datum/preference/toggle/human/name_is_always_random, !pref.read_preference(/datum/preference/toggle/human/name_is_always_random))
return TOPIC_REFRESH
return data
else if(href_list["nickname"])
var/raw_nickname = tgui_input_text(user, "Choose your character's nickname:", "Character Nickname", pref.nickname)
if (!isnull(raw_nickname) && CanUseTopic(user))
var/new_nickname = sanitize_name(raw_nickname, pref.species, is_FBP())
if(new_nickname)
pref.nickname = new_nickname
return TOPIC_REFRESH
else
to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ."))
return TOPIC_NOACTION
/datum/category_item/player_setup_item/general/basic/tgui_static_data(mob/user)
var/list/data = ..()
else if(href_list["reset_nickname"])
var/nick_choice = tgui_alert(user, "Wipe your Nickname? This will completely remove any chosen nickname(s).","Wipe Nickname",list("Yes","No"))
if(nick_choice == "Yes")
pref.nickname = null
return TOPIC_REFRESH
data["allow_metadata"] = CONFIG_GET(flag/allow_metadata)
else if(href_list["bio_gender"])
var/new_gender = tgui_input_list(user, "Choose your character's biological sex:", "Character Preference", get_genders(), pref.biological_gender)
if(new_gender && CanUseTopic(user))
pref.set_biological_gender(new_gender)
return TOPIC_REFRESH_UPDATE_PREVIEW
return data
else if(href_list["id_gender"])
var/new_gender = tgui_input_list(user, "Choose your character's pronouns:", "Character Preference", pronoun_set_to_genders, genders_to_pronoun_set[pref.identifying_gender])
if(new_gender && CanUseTopic(user))
pref.identifying_gender = pronoun_set_to_genders[new_gender]
return TOPIC_REFRESH
/datum/category_item/player_setup_item/general/basic/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
else if(href_list["age"])
var/min_age = get_min_age()
var/max_age = get_max_age()
var/new_age = tgui_input_number(user, "Choose your character's age:\n([min_age]-[max_age])", "Character Preference", pref.read_preference(/datum/preference/numeric/human/age), max_age, min_age)
if(new_age && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(round(text2num(new_age)), max_age), min_age))
var/mob/user = ui.user
switch(action)
if("rename")
var/raw_name = tgui_input_text(user, "Choose your character's name:", "Character Name", pref.real_name)
if(!isnull(raw_name))
var/new_name = sanitize_name(raw_name, pref.species, is_FBP())
if(new_name)
pref.real_name = new_name
return TOPIC_REFRESH
else
to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ."))
return TOPIC_NOACTION
if("random_name")
pref.real_name = random_name(pref.identifying_gender, pref.species)
return TOPIC_REFRESH
else if(href_list["bday_month"])
var/new_month = tgui_input_number(user, "Choose your character's birth month (number)", "Birthday Month", pref.read_preference(/datum/preference/numeric/human/bday_month), 12, 0)
if(new_month && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, new_month)
else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes") && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0)
return TOPIC_REFRESH
if("always_random_name")
pref.update_preference_by_type(/datum/preference/toggle/human/name_is_always_random, !pref.read_preference(/datum/preference/toggle/human/name_is_always_random))
return TOPIC_REFRESH
else if(href_list["bday_day"])
if(!pref.read_preference(/datum/preference/numeric/human/bday_month))
tgui_alert(user,"You must set a birth month before you can set a day.", "Error", list("Okay"))
return
var/max_days
switch(pref.read_preference(/datum/preference/numeric/human/bday_month))
if(1)
max_days = 31
if(2)
max_days = 29
if(3)
max_days = 31
if(4)
max_days = 30
if(5)
max_days = 31
if(6)
max_days = 30
if(7)
max_days = 31
if(8)
max_days = 31
if(9)
max_days = 30
if(10)
max_days = 31
if(11)
max_days = 30
if(12)
max_days = 31
if("nickname")
var/raw_nickname = tgui_input_text(user, "Choose your character's nickname:", "Character Nickname", pref.nickname)
if(!isnull(raw_nickname))
var/new_nickname = sanitize_name(raw_nickname, pref.species, is_FBP())
if(new_nickname)
pref.nickname = new_nickname
return TOPIC_REFRESH
else
to_chat(user, span_warning("Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and ."))
return TOPIC_NOACTION
var/new_day = tgui_input_number(user, "Choose your character's birth day (number, 1-[max_days])", "Birthday Day", pref.read_preference(/datum/preference/numeric/human/bday_day), max_days, 0)
if(new_day && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, new_day)
else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes") && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0)
return TOPIC_REFRESH
if("reset_nickname")
var/nick_choice = tgui_alert(user, "Wipe your Nickname? This will completely remove any chosen nickname(s).","Wipe Nickname",list("Yes","No"))
if(nick_choice == "Yes")
pref.nickname = null
return TOPIC_REFRESH
else if(href_list["bday_announce"])
pref.update_preference_by_type(/datum/preference/toggle/human/bday_announce, !pref.read_preference(/datum/preference/toggle/human/bday_announce))
return TOPIC_REFRESH
if("bio_gender")
var/new_gender = lowertext(params["gender"])
if(new_gender in get_genders())
pref.set_biological_gender(new_gender)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["spawnpoint"])
var/list/spawnkeys = list()
for(var/spawntype in spawntypes)
spawnkeys += spawntype
var/choice = tgui_input_list(user, "Where would you like to spawn when late-joining?", "Late-Join Choice", spawnkeys)
if(!choice || !spawntypes[choice] || !CanUseTopic(user)) return TOPIC_NOACTION
pref.update_preference_by_type(/datum/preference/choiced/living/spawnpoint, choice)
return TOPIC_REFRESH
if("id_gender")
var/new_gender = lowertext(params["gender"])
if(new_gender in all_genders_define_list)
pref.identifying_gender = new_gender
return TOPIC_REFRESH
if("age")
var/min_age = get_min_age()
var/max_age = get_max_age()
var/new_age = tgui_input_number(user, "Choose your character's age:\n([min_age]-[max_age])", "Character Preference", pref.read_preference(/datum/preference/numeric/human/age), max_age, min_age)
if(new_age)
pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(round(text2num(new_age)), max_age), min_age))
return TOPIC_REFRESH
if("bday_month")
var/new_month = tgui_input_number(user, "Choose your character's birth month (number)", "Birthday Month", pref.read_preference(/datum/preference/numeric/human/bday_month), 12, 0)
if(new_month)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, new_month)
else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes"))
pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0)
if("bday_day")
if(!pref.read_preference(/datum/preference/numeric/human/bday_month))
tgui_alert(user,"You must set a birth month before you can set a day.", "Error", list("Okay"))
return
var/max_days
switch(pref.read_preference(/datum/preference/numeric/human/bday_month))
if(1)
max_days = 31
if(2)
max_days = 29
if(3)
max_days = 31
if(4)
max_days = 30
if(5)
max_days = 31
if(6)
max_days = 30
if(7)
max_days = 31
if(8)
max_days = 31
if(9)
max_days = 30
if(10)
max_days = 31
if(11)
max_days = 30
if(12)
max_days = 31
var/new_day = tgui_input_number(user, "Choose your character's birth day (number, 1-[max_days])", "Birthday Day", pref.read_preference(/datum/preference/numeric/human/bday_day), max_days, 0)
if(new_day)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, new_day)
else if((tgui_alert(user, "Would you like to clear the birthday entry?","Clear?",list("No","Yes")) == "Yes"))
pref.update_preference_by_type(/datum/preference/numeric/human/bday_month, 0)
pref.update_preference_by_type(/datum/preference/numeric/human/bday_day, 0)
return TOPIC_REFRESH
if("bday_announce")
pref.update_preference_by_type(/datum/preference/toggle/human/bday_announce, !pref.read_preference(/datum/preference/toggle/human/bday_announce))
return TOPIC_REFRESH
if("spawnpoint")
var/list/spawnkeys = list()
for(var/spawntype in spawntypes)
spawnkeys += spawntype
var/choice = tgui_input_list(user, "Where would you like to spawn when late-joining?", "Late-Join Choice", spawnkeys)
if(!choice || !spawntypes[choice])
return TOPIC_NOACTION
pref.update_preference_by_type(/datum/preference/choiced/living/spawnpoint, choice)
return TOPIC_REFRESH
if("edit_ooc_notes")
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see, such as Roleplay-preferences. This will not be saved permanently unless you click save in the Character Setup panel!", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata)
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes, new_metadata)
if("edit_ooc_note_favs")
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your FAVOURITE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_favs)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata)
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_favs, new_metadata)
if("edit_ooc_note_likes")
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_likes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata)
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_likes, new_metadata)
if("edit_ooc_note_maybes")
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your MAYBE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_maybes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata && CanUseTopic(user))
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_maybes, new_metadata)
if("edit_ooc_note_dislikes")
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_dislikes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata)
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_dislikes, new_metadata)
if("edit_ooc_note_style")
pref.update_preference_by_type(/datum/preference/toggle/living/ooc_notes_style, !pref.read_preference(/datum/preference/toggle/living/ooc_notes_style))
return TOPIC_REFRESH
if("vore_egg_type")
var/list/vore_egg_types = GLOB.global_vore_egg_types
var/selection = tgui_input_list(user, "Choose your character's egg type:", "Character Preference", vore_egg_types, pref.vore_egg_type)
if(selection)
pref.vore_egg_type = selection
return TOPIC_REFRESH
if("autohiss")
var/list/autohiss_selection = list("Full", "Basic", "Off")
var/selection = tgui_input_list(user, "Choose your default autohiss setting:", "Character Preference", autohiss_selection, pref.autohiss)
if(selection)
pref.autohiss = selection
else if(!selection)
pref.autohiss = "Full"
return TOPIC_REFRESH
if("emote_sound_mode")
var/datum/preference/choiced/living/emote_sound_mode = GLOB.preference_entries_by_key["emote_sound_mode"]
var/selection = tgui_input_list(user, "Choose your emote sound mode", "Emote Sound Mode", emote_sound_mode.get_choices())
if(selection)
pref.update_preference(emote_sound_mode, selection)
return TOPIC_REFRESH
else if(href_list["edit_ooc_notes"])
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see, such as Roleplay-preferences. This will not be saved permanently unless you click save in the Character Setup panel!", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes, new_metadata)
else if(href_list["edit_ooc_note_likes"])
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your LIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_likes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata && CanUseTopic(user))
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_likes, new_metadata)
else if(href_list["edit_ooc_note_dislikes"])
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your DISLIKED roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_dislikes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata && CanUseTopic(user))
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_dislikes, new_metadata)
else if(href_list["edit_ooc_note_favs"])
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your FAVOURITE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_favs)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata && CanUseTopic(user))
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_favs, new_metadata)
else if(href_list["edit_ooc_note_maybes"])
var/new_metadata = strip_html_simple(tgui_input_text(user, "Enter any information you'd like others to see relating to your MAYBE roleplay preferences. This will not be saved permanently unless you click save in the Character Setup panel! Type \"!clear\" to empty.", "Game Preference" , html_decode(pref.read_preference(/datum/preference/text/living/ooc_notes_maybes)), multiline = TRUE, prevent_enter = TRUE))
if(new_metadata && CanUseTopic(user))
if(new_metadata == "!clear")
new_metadata = ""
pref.update_preference_by_type(/datum/preference/text/living/ooc_notes_maybes, new_metadata)
else if(href_list["edit_ooc_note_style"])
pref.update_preference_by_type(/datum/preference/toggle/living/ooc_notes_style, !pref.read_preference(/datum/preference/toggle/living/ooc_notes_style))
return TOPIC_REFRESH
return ..()
/datum/category_item/player_setup_item/general/basic/proc/get_genders()
@@ -232,3 +303,7 @@
possible_genders = possible_genders.Copy()
possible_genders |= NEUTER
return possible_genders
#undef AUTOHISS_OFF
#undef AUTOHISS_BASIC
#undef AUTOHISS_FULL
@@ -68,42 +68,59 @@
pref.runechat_color = sanitize_hexcolor(pref.runechat_color, COLOR_BLACK)
/datum/category_item/player_setup_item/general/language/content()
. += span_bold("Languages") + "<br>"
/datum/category_item/player_setup_item/general/language/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/list/languages_list = list()
var/datum/species/S = GLOB.all_species[pref.species]
if(pref.alternate_languages.len > (S.num_alternate_languages + pref.extra_languages))
testing("LANGSANI: Truncated [pref.client]'s character [pref.real_name || "-name not yet loaded-"] language list because it was too long (len: [pref.alternate_languages.len], allowed: [S.num_alternate_languages])")
pref.alternate_languages.len = (S.num_alternate_languages + pref.extra_languages) // Truncate to allowed length
if(S.language)
. += "- [S.language] - <a href='byond://?src=\ref[src];set_custom_key=[S.language]'>Set Custom Key</a><br>"
UNTYPED_LIST_ADD(languages_list, list(
"name" = S.language,
"removable" = FALSE
))
if(S.default_language && S.default_language != S.language)
. += "- [S.default_language] - <a href='byond://?src=\ref[src];set_custom_key=[S.default_language]'>Set Custom Key</a><br>"
if(S.num_alternate_languages + pref.extra_languages)
if(pref.alternate_languages.len)
for(var/i = 1 to pref.alternate_languages.len)
var/lang = pref.alternate_languages[i]
. += "- [lang] - <a href='byond://?src=\ref[src];remove_language=[i]'>remove</a> - <a href='byond://?src=\ref[src];set_custom_key=[lang]'>Set Custom Key</a><br>"
UNTYPED_LIST_ADD(languages_list, list(
"name" = S.default_language,
"removable" = FALSE
))
for(var/lang in pref.alternate_languages)
UNTYPED_LIST_ADD(languages_list, list(
"name" = lang,
"removable" = TRUE
))
if(pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages))
. += "- <a href='byond://?src=\ref[src];add_language=1'>add</a> ([(S.num_alternate_languages + pref.extra_languages) - pref.alternate_languages.len] remaining)<br>"
else
. += "- [pref.species] cannot choose secondary languages.<br>"
data["languages"] = languages_list
. += span_bold("Language Keys") + "<br>"
. += " [jointext(pref.language_prefixes, " ")] <a href='byond://?src=\ref[src];change_prefix=1'>Change</a> <a href='byond://?src=\ref[src];reset_prefix=1'>Reset</a><br>"
. += span_bold("Preferred Language") + " <a href='byond://?src=\ref[src];pref_lang=1'>[pref.preferred_language]</a><br>" // VOREStation Add
. += span_bold("Runechat Color") + " <a href='byond://?src=\ref[src];pref_runechat_color=1'>Change Runechat Color</a> [color_square(hex = pref.runechat_color)]"
data["languages_can_add"] = LAZYLEN(pref.alternate_languages) < (S.num_alternate_languages + pref.extra_languages)
data["language_keys"] = pref.language_prefixes
data["preferred_language"] = pref.preferred_language
data["runechat_color"] = pref.runechat_color
return data
/datum/category_item/player_setup_item/general/language/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
var/datum/species/S = GLOB.all_species[pref.species]
switch(action)
if("remove_language")
var/lang_to_remove = params["lang"]
var/index = pref.alternate_languages.Find(lang_to_remove)
if(!index)
return TOPIC_REFRESH
pref.alternate_languages.Cut(index, index+1)
return TOPIC_REFRESH
if("add_language")
if(pref.alternate_languages.len >= (S.num_alternate_languages + pref.extra_languages))
tgui_alert_async(user, "You have already selected the maximum number of alternate languages for this species!")
return TOPIC_REFRESH
/datum/category_item/player_setup_item/general/language/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["remove_language"])
var/index = text2num(href_list["remove_language"])
pref.alternate_languages.Cut(index, index+1)
return TOPIC_REFRESH
else if(href_list["add_language"])
var/datum/species/S = GLOB.all_species[pref.species]
if(pref.alternate_languages.len >= (S.num_alternate_languages + pref.extra_languages))
tgui_alert_async(user, "You have already selected the maximum number of alternate languages for this species!")
else
var/list/available_languages = S.secondary_langs.Copy()
for(var/L in GLOB.all_languages)
var/datum/language/lang = GLOB.all_languages[L]
@@ -117,74 +134,73 @@
if(!available_languages.len)
tgui_alert_async(user, "There are no additional languages available to select.")
else
var/new_lang = tgui_input_list(user, "Select an additional language", "Character Generation", available_languages)
if(new_lang && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages))
var/datum/language/chosen_lang = GLOB.all_languages[new_lang]
if(istype(chosen_lang))
var/choice = tgui_alert(user, "[chosen_lang.desc]",chosen_lang.name, list("Take","Cancel"))
if(choice != "Cancel" && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages))
pref.alternate_languages |= new_lang
return TOPIC_REFRESH
return TOPIC_REFRESH
else if(href_list["change_prefix"])
var/char
var/keys[0]
do
char = tgui_input_text(user, "Enter a single special character.\nYou may re-select the same characters.\nThe following characters are already in use by radio: ; : .\nThe following characters are already in use by special say commands: ! * ^", "Enter Character - [3 - keys.len] remaining")
if(char)
if(length(char) > 1)
tgui_alert_async(user, "Only single characters allowed.", "Error")
else if(char in list(";", ":", "."))
tgui_alert_async(user, "Radio character. Rejected.", "Error")
else if(char in list("!","*","^","-"))
tgui_alert_async(user, "Say character. Rejected.", "Error")
else if(contains_az09(char))
tgui_alert_async(user, "Non-special character. Rejected.", "Error")
else
keys.Add(char)
while(char && keys.len < 3)
var/new_lang = tgui_input_list(user, "Select an additional language", "Character Generation", available_languages)
if(new_lang && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages))
var/datum/language/chosen_lang = GLOB.all_languages[new_lang]
if(istype(chosen_lang))
var/choice = tgui_alert(user, "[chosen_lang.desc]",chosen_lang.name, list("Take","Cancel"))
if(choice != "Cancel" && pref.alternate_languages.len < (S.num_alternate_languages + pref.extra_languages))
pref.alternate_languages |= new_lang
return TOPIC_REFRESH
if(keys.len == 3)
pref.language_prefixes = keys
return TOPIC_REFRESH
else if(href_list["reset_prefix"])
var/list/prefixes = CONFIG_GET(str_list/language_prefixes)
pref.language_prefixes = prefixes.Copy()
return TOPIC_REFRESH
if("change_prefix")
var/char
var/keys[0]
do
char = tgui_input_text(user, "Enter a single special character.\nYou may re-select the same characters.\nThe following characters are already in use by radio: ; : .\nThe following characters are already in use by special say commands: ! * ^", "Enter Character - [3 - keys.len] remaining")
if(char)
if(length(char) > 1)
tgui_alert_async(user, "Only single characters allowed.", "Error")
else if(char in list(";", ":", "."))
tgui_alert_async(user, "Radio character. Rejected.", "Error")
else if(char in list("!","*","^","-"))
tgui_alert_async(user, "Say character. Rejected.", "Error")
else if(contains_az09(char))
tgui_alert_async(user, "Non-special character. Rejected.", "Error")
else
keys.Add(char)
while(char && keys.len < 3)
else if(href_list["set_custom_key"])
var/lang = href_list["set_custom_key"]
if(!(lang in GLOB.all_languages))
if(keys.len == 3)
pref.language_prefixes = keys
return TOPIC_REFRESH
if("reset_prefix")
var/list/prefixes = CONFIG_GET(str_list/language_prefixes)
pref.language_prefixes = prefixes.Copy()
return TOPIC_REFRESH
var/oldkey = ""
for(var/key in pref.language_custom_keys)
if(pref.language_custom_keys[key] == lang)
oldkey = key
break
if("set_custom_key")
var/lang = params["lang"]
if(!(lang in GLOB.all_languages))
return TOPIC_REFRESH
var/char = tgui_input_text(user, "Input a language key for [lang]. Input a single space to reset.", "Language Custom Key", oldkey)
if(length(char) != 1)
return TOPIC_REFRESH
else if(char == " ")
var/oldkey = ""
for(var/key in pref.language_custom_keys)
if(pref.language_custom_keys[key] == lang)
pref.language_custom_keys -= key
oldkey = key
break
else if(contains_az09(char))
if(!(char in pref.language_custom_keys))
pref.language_custom_keys += char
pref.language_custom_keys[char] = lang
else
tgui_alert_async(user, "Improper language key. Rejected.", "Error")
return TOPIC_REFRESH
var/char = tgui_input_text(user, "Input a language key for [lang]. Input a single space to reset.", "Language Custom Key", oldkey)
if(length(char) != 1)
return TOPIC_REFRESH
else if(char == " ")
for(var/key in pref.language_custom_keys)
if(pref.language_custom_keys[key] == lang)
pref.language_custom_keys -= key
break
else if(contains_az09(char))
if(!(char in pref.language_custom_keys))
pref.language_custom_keys += char
pref.language_custom_keys[char] = lang
else
tgui_alert_async(user, "Improper language key. Rejected.", "Error")
// VOREStation Add: Preferred Language
else if(href_list["pref_lang"])
if(pref.species) // Safety to prevent a null runtime here
var/datum/species/S = GLOB.all_species[pref.species]
return TOPIC_REFRESH
if("pref_lang")
var/list/lang_opts = list(S.language) + pref.alternate_languages + LANGUAGE_GALCOM
var/selection = tgui_input_list(user, "Choose your preferred spoken language:", "Preferred Spoken Language", lang_opts, pref.preferred_language)
if(!selection) // Set our preferred to default, just in case.
@@ -196,19 +212,15 @@
else // Did they set anything else?
tgui_alert_async(user, "You will now speak [pref.preferred_language] if you do not specify a language when speaking.", "Preferred Language Set")
return TOPIC_REFRESH
// VOREStation Add End
else if(href_list["pref_runechat_color"])
var/new_runechat_color = tgui_color_picker(user, "Choose your character's runechat colour (#000000 for random):", "Character Preference", pref.runechat_color)
if(new_runechat_color && CanUseTopic(user))
pref.runechat_color = new_runechat_color
// whenever we change this, we update our mob
var/mob/pref_mob = preference_mob()
if(pref_mob)
pref_mob.chat_color = new_runechat_color
pref_mob.chat_color_darkened = new_runechat_color
pref_mob.chat_color_name = pref_mob.name
return TOPIC_REFRESH
return ..()
if("pref_runechat_color")
var/new_runechat_color = tgui_color_picker(user, "Choose your character's runechat colour (#000000 for random):", "Character Preference", pref.runechat_color)
if(new_runechat_color)
pref.runechat_color = new_runechat_color
// whenever we change this, we update our mob
var/mob/pref_mob = preference_mob()
if(pref_mob)
pref_mob.chat_color = new_runechat_color
pref_mob.chat_color_darkened = new_runechat_color
pref_mob.chat_color_name = pref_mob.name
return TOPIC_REFRESH
File diff suppressed because it is too large Load Diff
@@ -43,130 +43,133 @@
character.personal_faction = pref.faction
character.religion = pref.religion
/datum/category_item/player_setup_item/general/background/content(var/mob/user)
. += span_bold("Background Information") + "<br>"
. += "Economic Status: <a href='byond://?src=\ref[src];econ_status=1'>[pref.economic_status]</a><br/>"
. += "Home: <a href='byond://?src=\ref[src];home_system=1'>[pref.home_system]</a><br/>"
. += "Birthplace: <a href='byond://?src=\ref[src];birthplace=1'>[pref.birthplace]</a><br/>"
. += "Citizenship: <a href='byond://?src=\ref[src];citizenship=1'>[pref.citizenship]</a><br/>"
. += "Faction: <a href='byond://?src=\ref[src];faction=1'>[pref.faction]</a><br/>"
. += "Religion: <a href='byond://?src=\ref[src];religion=1'>[pref.religion]</a><br/>"
/datum/category_item/player_setup_item/general/background/tgui_data(mob/user)
var/list/data = ..()
data["economic_status"] = pref.economic_status
data["home_system"] = pref.home_system
data["birthplace"] = pref.birthplace
data["citizenship"] = pref.citizenship
data["faction"] = pref.faction
data["religion"] = pref.religion
data["ooc_note_style"] = pref.read_preference(/datum/preference/toggle/living/ooc_notes_style)
. += "<br/><b>Records</b>:<br/>"
if(jobban_isbanned(user, "Records"))
. += span_danger("You are banned from using character records.") + "<br>"
data["records_banned"] = TRUE
else
. += "Medical Records:<br>"
. += "<a href='byond://?src=\ref[src];set_medical_records=1'>[TextPreview(pref.med_record,40)]</a><br>"
. += " (<a href='byond://?src=\ref[src];reset_medrecord=1'>Reset</A>)<br><br>"
. += "Employment Records:<br>"
. += "<a href='byond://?src=\ref[src];set_general_records=1'>[TextPreview(pref.gen_record,40)]</a><br>"
. += "(<a href='byond://?src=\ref[src];reset_emprecord=1'>Reset</A>)<br><br>"
. += "Security Records:<br>"
. += "<a href='byond://?src=\ref[src];set_security_records=1'>[TextPreview(pref.sec_record,40)]</a><br>"
. += "(<a href='byond://?src=\ref[src];reset_secrecord=1'>Reset</A>)"
data["records_banned"] = FALSE
/datum/category_item/player_setup_item/general/background/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["econ_status"])
var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status)
if(new_class && CanUseTopic(user))
pref.economic_status = new_class
data["med_record"] = TextPreview(pref.med_record,40)
data["gen_record"] = TextPreview(pref.gen_record,40)
data["sec_record"] = TextPreview(pref.sec_record,40)
return data
/datum/category_item/player_setup_item/general/background/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("econ_status")
var/new_class = tgui_input_list(user, "Choose your economic status. This will affect the amount of money you will start with.", "Character Preference", ECONOMIC_CLASS, pref.economic_status)
if(new_class)
pref.economic_status = new_class
return TOPIC_REFRESH
if("home_system")
var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.home_system = raw_choice
else
pref.home_system = choice
return TOPIC_REFRESH
else if(href_list["home_system"])
var/choice = tgui_input_list(user, "Please choose your home planet and/or system. This should be your current primary residence. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.home_system)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a home system.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice && CanUseTopic(user))
pref.home_system = raw_choice
else
pref.home_system = choice
return TOPIC_REFRESH
if("birthplace")
var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.birthplace = raw_choice
else
pref.birthplace = choice
return TOPIC_REFRESH
else if(href_list["birthplace"])
var/choice = tgui_input_list(user, "Please choose the planet and/or system or other appropriate location that you were born/created. Select \"Other\" to specify manually.", "Character Preference", home_system_choices + list("Unset","Other"), pref.birthplace)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a birthplace.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice && CanUseTopic(user))
pref.birthplace = raw_choice
else
pref.birthplace = choice
return TOPIC_REFRESH
if("citizenship")
var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.citizenship = raw_choice
else
pref.citizenship = choice
return TOPIC_REFRESH
else if(href_list["citizenship"])
var/choice = tgui_input_list(user, "Please select the faction or political entity with which you currently hold citizenship. Select \"Other\" to specify manually.", "Character Preference", citizenship_choices + list("None","Other"), pref.citizenship)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter your current citizenship.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice && CanUseTopic(user))
pref.citizenship = raw_choice
else
pref.citizenship = choice
return TOPIC_REFRESH
if("faction")
var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.faction = raw_choice
else
pref.faction = choice
return TOPIC_REFRESH
else if(href_list["faction"])
var/choice = tgui_input_list(user, "Please choose the faction you primarily work for, if you are not under the direct employ of NanoTrasen. Select \"Other\" to specify manually.", "Character Preference", faction_choices + list("None","Other"), pref.faction)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.faction = raw_choice
else
pref.faction = choice
return TOPIC_REFRESH
if("religion")
var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.religion = sanitize(raw_choice)
else
pref.religion = choice
return TOPIC_REFRESH
else if(href_list["religion"])
var/choice = tgui_input_list(user, "Please choose a religion. Select \"Other\" to specify manually.", "Character Preference", religion_choices + list("None","Other"), pref.religion)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = strip_html_simple(tgui_input_text(user, "Please enter a religon.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.religion = sanitize(raw_choice)
else
pref.religion = choice
return TOPIC_REFRESH
if("set_medical_records")
var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(new_medical && !jobban_isbanned(user, JOB_RECORDS))
pref.med_record = new_medical
return TOPIC_REFRESH
else if(href_list["set_medical_records"])
var/new_medical = strip_html_simple(tgui_input_text(user,"Enter medical information here.","Character Preference", html_decode(pref.med_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(new_medical && !jobban_isbanned(user, JOB_RECORDS) && CanUseTopic(user))
pref.med_record = new_medical
return TOPIC_REFRESH
if("set_general_records")
var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(new_general && !jobban_isbanned(user, JOB_RECORDS))
pref.gen_record = new_general
return TOPIC_REFRESH
else if(href_list["set_general_records"])
var/new_general = strip_html_simple(tgui_input_text(user,"Enter employment information here.","Character Preference", html_decode(pref.gen_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(new_general && !jobban_isbanned(user, JOB_RECORDS) && CanUseTopic(user))
pref.gen_record = new_general
return TOPIC_REFRESH
if("set_security_records")
var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(sec_medical && !jobban_isbanned(user, JOB_RECORDS))
pref.sec_record = sec_medical
return TOPIC_REFRESH
else if(href_list["set_security_records"])
var/sec_medical = strip_html_simple(tgui_input_text(user,"Enter security information here.","Character Preference", html_decode(pref.sec_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(sec_medical && !jobban_isbanned(user, JOB_RECORDS) && CanUseTopic(user))
pref.sec_record = sec_medical
return TOPIC_REFRESH
if("reset_medrecord")
var/resetmed_choice = tgui_alert(user, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No"))
if(resetmed_choice == "Yes")
pref.med_record = null
return TOPIC_REFRESH
else if(href_list["reset_medrecord"])
var/resetmed_choice = tgui_alert(user, "Wipe your Medical Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No"))
if(resetmed_choice == "Yes")
pref.med_record = null
return TOPIC_REFRESH
if("reset_emprecord")
var/resetemp_choice = tgui_alert(user, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No"))
if(resetemp_choice == "Yes")
pref.gen_record = null
return TOPIC_REFRESH
else if(href_list["reset_emprecord"])
var/resetemp_choice = tgui_alert(user, "Wipe your Employment Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No"))
if(resetemp_choice == "Yes")
pref.gen_record = null
return TOPIC_REFRESH
else if(href_list["reset_secrecord"])
var/resetsec_choice = tgui_alert(user, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No"))
if(resetsec_choice == "Yes")
pref.sec_record = null
return TOPIC_REFRESH
return ..()
if("reset_secrecord")
var/resetsec_choice = tgui_alert(user, "Wipe your Security Records? This cannot be reverted if you have not saved your character recently! You may wish to make a backup first.","Reset Records",list("Yes","No"))
if(resetsec_choice == "Yes")
pref.sec_record = null
return TOPIC_REFRESH
@@ -50,12 +50,37 @@
character.flavor_texts["feet"] = pref.flavor_texts["feet"]
character.custom_link = pref.custom_link
/datum/category_item/player_setup_item/general/flavor/content(var/mob/user)
. += span_bold("Flavor:") + "<br>"
. += "<a href='byond://?src=\ref[src];flavor_text=open'>Set Flavor Text</a><br/>"
. += "<a href='byond://?src=\ref[src];flavour_text_robot=open'>Set Robot Flavor Text</a><br/>"
. += "<a href='byond://?src=\ref[src];custom_link=1'>Set Custom Link</a><br/>"
/datum/category_item/player_setup_item/general/flavor/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["flavor_text_length"] = LAZYLEN(pref.flavor_texts["general"])
return data
/datum/category_item/player_setup_item/general/flavor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("flavor_text")
SetFlavorText(user)
return TOPIC_HANDLED
if("flavour_text_robot")
SetFlavourTextRobot(user)
return TOPIC_HANDLED
if("custom_link")
var/new_link = strip_html_simple(tgui_input_text(user, "Enter a link to add on to your examine text! This should be a related image link/gallery, or things like your F-list. This is not the place for memes.", "Custom Link" , html_decode(pref.custom_link), max_length = 100, encode = TRUE, prevent_enter = TRUE))
if(new_link)
if(length(new_link) > 100)
to_chat(user, span_warning("Your entry is too long, it must be 100 characters or less."))
return
pref.custom_link = new_link
log_admin("[user]/[user.ckey] set their custom link to [pref.custom_link]")
// README: This must stay for SetFlavorText to work!
/datum/category_item/player_setup_item/general/flavor/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["flavor_text"])
switch(href_list["flavor_text"])
@@ -75,7 +100,6 @@
pref.flavor_texts[href_list["flavor_text"]] = msg
SetFlavorText(user)
return TOPIC_HANDLED
else if(href_list["flavour_text_robot"])
switch(href_list["flavour_text_robot"])
if("open")
@@ -0,0 +1,128 @@
/datum/category_item/player_setup_item/general/basic_antagonism
name = "Basic"
sort_order = 7
var/datum/paiCandidate/candidate
/datum/category_item/player_setup_item/general/basic_antagonism/load_character(list/save_data)
pref.exploit_record = save_data["exploit_record"]
pref.antag_faction = save_data["antag_faction"]
pref.antag_vis = save_data["antag_vis"]
/datum/category_item/player_setup_item/general/basic_antagonism/save_character(list/save_data)
save_data["exploit_record"] = pref.exploit_record
save_data["antag_faction"] = pref.antag_faction
save_data["antag_vis"] = pref.antag_vis
/datum/category_item/player_setup_item/general/basic_antagonism/load_preferences(datum/json_savefile/savefile)
if(!candidate)
candidate = new()
var/preference_mob = preference_mob()
if(!preference_mob)// No preference mob - this happens when we're called from client/New() before it calls ..() (via datum/preferences/New())
spawn()
preference_mob = preference_mob()
if(!preference_mob)
return
candidate.savefile_load(preference_mob)
return
candidate.savefile_load(preference_mob)
/datum/category_item/player_setup_item/general/basic_antagonism/save_preferences(datum/json_savefile/savefile)
if(!candidate)
return
if(!preference_mob())
return
candidate.savefile_save(preference_mob())
/datum/category_item/player_setup_item/general/basic_antagonism/sanitize_character()
if(!pref.antag_faction) pref.antag_faction = "None"
if(!pref.antag_vis) pref.antag_vis = "Hidden"
// Moved from /datum/preferences/proc/copy_to()
/datum/category_item/player_setup_item/general/basic_antagonism/copy_to_mob(var/mob/living/carbon/human/character)
character.exploit_record = pref.exploit_record
character.antag_faction = pref.antag_faction
character.antag_vis = pref.antag_vis
/datum/category_item/player_setup_item/general/basic_antagonism/tgui_data(mob/user)
var/list/data = ..()
data["antag_faction"] = pref.antag_faction
data["antag_vis"] = pref.antag_vis
data["uplink_type"] = pref.read_preference(/datum/preference/choiced/uplinklocation)
data["record_banned"] = jobban_isbanned(user, "Records")
if(!jobban_isbanned(user, "Records"))
data["exploitable_record"] = TextPreview(pref.exploit_record, 40)
if(!candidate)
CRASH("[user] pAI prefs have a null candidate var.")
data["pai_name"] = candidate.name ? candidate.name : "None Set"
data["pai_desc"] = candidate.description ? TextPreview(candidate.description, 40) : "None Set"
data["pai_role"] = candidate.role ? TextPreview(candidate.role, 40) : "None Set"
data["pai_comments"] = candidate.comments ? TextPreview(candidate.comments, 40) : "None Set"
return data
/datum/category_item/player_setup_item/general/basic_antagonism/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("uplinklocation")
var/new_uplinklocation = tgui_input_list(user, "Choose your uplink location:", "Character Preference", GLOB.uplink_locations, pref.read_preference(/datum/preference/choiced/uplinklocation))
if(new_uplinklocation && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/choiced/uplinklocation, new_uplinklocation)
return TOPIC_REFRESH
if("exploitable_record")
var/exploitmsg = sanitize(tgui_input_text(user,"Set exploitable information about you here.","Exploitable Information", html_decode(pref.exploit_record), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH, extra = 0)
if(!isnull(exploitmsg) && !jobban_isbanned(user, "Records") && CanUseTopic(user))
pref.exploit_record = exploitmsg
return TOPIC_REFRESH
if("antagfaction")
var/choice = tgui_input_list(user, "Please choose an antagonistic faction to work for.", "Character Preference", antag_faction_choices + list("None","Other"), pref.antag_faction)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = sanitize(tgui_input_text(user, "Please enter a faction.", "Character Preference", null, MAX_NAME_LEN), MAX_NAME_LEN)
if(raw_choice)
pref.antag_faction = raw_choice
else
pref.antag_faction = choice
return TOPIC_REFRESH
if("antagvis")
var/choice = tgui_input_list(user, "Please choose an antagonistic visibility level.", "Character Preference", antag_visiblity_choices, pref.antag_vis)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
else
pref.antag_vis = choice
return TOPIC_REFRESH
if("option")
var/t
switch(params["option"])
if("name")
t = sanitizeName(tgui_input_text(user, "Enter a name for your pAI", "Global Preference", candidate.name, MAX_NAME_LEN), MAX_NAME_LEN, 1)
if(t && CanUseTopic(user))
candidate.name = t
if("desc")
t = tgui_input_text(user, "Enter a description for your pAI", "Global Preference", html_decode(candidate.description), multiline = TRUE, prevent_enter = TRUE)
if(!isnull(t) && CanUseTopic(user))
candidate.description = sanitize(t)
if("role")
t = tgui_input_text(user, "Enter a role for your pAI", "Global Preference", html_decode(candidate.role))
if(!isnull(t) && CanUseTopic(user))
candidate.role = sanitize(t)
if("ooc")
t = tgui_input_text(user, "Enter any OOC comments", "Global Preference", html_decode(candidate.comments), multiline = TRUE, prevent_enter = TRUE)
if(!isnull(t) && CanUseTopic(user))
candidate.comments = sanitize(t)
return TOPIC_REFRESH
@@ -0,0 +1,81 @@
var/global/list/special_roles = list( //keep synced with the defines BE_* in setup.dm --rastaf
//some autodetection here.
// Change these to 0 if the equivalent mode is disabled for whatever reason!
"traitor" = 0, // 0
"operative" = 0, // 1
"changeling" = 0, // 2
"wizard" = 0, // 3
"malf AI" = 0, // 4
"revolutionary" = 0, // 5
"alien candidate" = 0, // 6
"positronic brain" = 1, // 7
"cultist" = 0, // 8
"renegade" = 0, // 9
"ninja" = 0, // 10
"raider" = 0, // 11
"diona" = 0, // 12
"mutineer" = 0, // 13
"loyalist" = 0, // 14
"pAI candidate" = 1, // 15
//VOREStation Add
"lost drone" = 1, // 16
"maint pred" = 1, // 17
"maint lurker" = 1, // 18
"morph" = 1, // 19
"corgi" = 1, // 20
"cursed sword" = 1, // 21
"Ship Survivor" = 1, // 22
//VOREStation Add End
)
/datum/category_item/player_setup_item/general/candidacy
name = "Candidacy"
sort_order = 8
/datum/category_item/player_setup_item/general/candidacy/load_character(list/save_data)
pref.be_special = save_data["be_special"]
/datum/category_item/player_setup_item/general/candidacy/save_character(list/save_data)
save_data["be_special"] = pref.be_special
/datum/category_item/player_setup_item/general/candidacy/sanitize_character()
pref.be_special = sanitize_integer(pref.be_special, 0, 16777215, initial(pref.be_special)) //VOREStation Edit - 24 bits of support
/datum/category_item/player_setup_item/general/candidacy/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
switch(action)
if("be_special")
var/num = text2num(params["be_special"])
pref.be_special ^= (1<<num)
return TOPIC_REFRESH
/datum/category_item/player_setup_item/general/candidacy/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["syndicate_ban"] = FALSE
if(jobban_isbanned(user, JOB_SYNDICATE))
data["syndicate_ban"] = TRUE
return
var/list/special_data = list()
var/n = 0
for(var/i in special_roles)
if(special_roles[i])
var/banned = FALSE
if(jobban_isbanned(user, i) || (i == "positronic brain" && jobban_isbanned(user, JOB_AI) && jobban_isbanned(user, JOB_CYBORG)) || (i == "pAI candidate" && jobban_isbanned(user, JOB_PAI)))
banned = TRUE
UNTYPED_LIST_ADD(special_data, list(
"idx" = n,
"name" = "Be [i]",
"selected" = pref.be_special & (1<<n),
"banned" = banned
))
n++
data["special_roles"] = special_data
return data
@@ -0,0 +1,305 @@
// Body weight limits on a character.
#define WEIGHT_CHANGE_MIN 0
#define WEIGHT_CHANGE_MAX 100
// Define a place to save in character setup
/datum/preferences
var/size_multiplier = RESIZE_NORMAL
// Body weight stuff.
var/weight_vr = 137 // bodyweight of character (pounds, because I'm not doing the math again -Spades)
var/weight_gain = 100 // Weight gain rate.
var/weight_loss = 50 // Weight loss rate.
var/fuzzy = 0 // Preference toggle for sharp/fuzzy icon. Default sharp.
var/offset_override = FALSE
var/voice_freq = 42500
var/voice_sound = "beep-boop"
var/custom_speech_bubble = "default"
var/custom_footstep = "Default"
var/species_sound = "Unset"
// Definition of the stuff for Sizing
/datum/category_item/player_setup_item/general/size
name = "Size"
sort_order = 9
/datum/category_item/player_setup_item/general/size/load_character(list/save_data)
pref.size_multiplier = save_data["size_multiplier"]
pref.weight_vr = save_data["weight_vr"]
pref.weight_gain = save_data["weight_gain"]
pref.weight_loss = save_data["weight_loss"]
pref.fuzzy = save_data["fuzzy"]
pref.offset_override = save_data["offset_override"]
pref.voice_freq = save_data["voice_freq"]
pref.voice_sound = save_data["voice_sound"]
pref.custom_speech_bubble = save_data["custom_speech_bubble"]
pref.custom_footstep = save_data["custom_footstep"]
pref.species_sound = save_data["species_sound"]
/datum/category_item/player_setup_item/general/size/save_character(list/save_data)
save_data["size_multiplier"] = pref.size_multiplier
save_data["weight_vr"] = pref.weight_vr
save_data["weight_gain"] = pref.weight_gain
save_data["weight_loss"] = pref.weight_loss
save_data["fuzzy"] = pref.fuzzy
save_data["offset_override"] = pref.offset_override
save_data["voice_freq"] = pref.voice_freq
save_data["voice_sound"] = pref.voice_sound
save_data["custom_speech_bubble"] = pref.custom_speech_bubble
save_data["custom_footstep"] = pref.custom_footstep
save_data["species_sound"] = pref.species_sound
/datum/category_item/player_setup_item/general/size/sanitize_character()
pref.weight_vr = sanitize_integer(pref.weight_vr, WEIGHT_MIN, WEIGHT_MAX, initial(pref.weight_vr))
pref.weight_gain = sanitize_integer(pref.weight_gain, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_gain))
pref.weight_loss = sanitize_integer(pref.weight_loss, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_loss))
pref.fuzzy = sanitize_integer(pref.fuzzy, 0, 1, initial(pref.fuzzy))
pref.offset_override = sanitize_integer(pref.offset_override, 0, 1, initial(pref.offset_override))
if(pref.voice_freq != 0)
pref.voice_freq = sanitize_integer(pref.voice_freq, MIN_VOICE_FREQ, MAX_VOICE_FREQ, initial(pref.fuzzy))
if(pref.size_multiplier == null || pref.size_multiplier < RESIZE_TINY || pref.size_multiplier > RESIZE_HUGE)
pref.size_multiplier = initial(pref.size_multiplier)
if(!(pref.custom_speech_bubble in GLOB.selectable_speech_bubbles))
pref.custom_speech_bubble = "default"
if(!(pref.custom_footstep))
pref.custom_footstep = "Default"
if(!(pref.species_sound))
pref.species_sound = "Unset"
/datum/category_item/player_setup_item/general/size/copy_to_mob(var/mob/living/carbon/human/character)
character.weight = pref.weight_vr
character.weight_gain = pref.weight_gain
character.weight_loss = pref.weight_loss
character.fuzzy = pref.fuzzy
character.offset_override = pref.offset_override
character.voice_freq = pref.voice_freq
character.resize(pref.size_multiplier, animate = FALSE, ignore_prefs = TRUE)
if(!pref.voice_sound)
character.voice_sounds_list = DEFAULT_TALK_SOUNDS
else
character.voice_sounds_list = get_talk_sound(pref.voice_sound)
character.custom_speech_bubble = pref.custom_speech_bubble
character.custom_footstep = pref.custom_footstep
/datum/category_item/player_setup_item/general/size/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["size_multiplier"] = round(pref.size_multiplier * 100)
data["fuzzy"] = pref.fuzzy
data["offset_override"] = pref.offset_override
data["voice_freq"] = pref.voice_freq
data["voice_sound"] = pref.voice_sound
data["custom_speech_bubble"] = pref.custom_speech_bubble
data["custom_footstep"] = pref.custom_footstep
data["custom_species_sound"] = pref.species_sound
data["weight_vr"] = pref.weight_vr
data["weight_gain"] = pref.weight_gain
data["weight_loss"] = pref.weight_loss
return data
/datum/category_item/player_setup_item/general/size/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("size_multiplier")
var/new_size = tgui_input_number(user, "Choose your character's size, ranging from [RESIZE_MINIMUM * 100]% to [RESIZE_MAXIMUM * 100]%", "Set Size", pref.size_multiplier * 100, RESIZE_MAXIMUM * 100, RESIZE_MINIMUM * 100)
if (!ISINRANGE(new_size, RESIZE_MINIMUM * 100, RESIZE_MAXIMUM * 100))
pref.size_multiplier = 1
to_chat(user, span_notice("Invalid size."))
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(new_size)
pref.size_multiplier = (new_size / 100)
return TOPIC_REFRESH_UPDATE_PREVIEW
if("toggle_fuzzy")
pref.fuzzy = pref.fuzzy ? 0 : 1;
return TOPIC_REFRESH_UPDATE_PREVIEW
if("toggle_offset_override")
pref.offset_override = pref.offset_override ? 0 : 1;
return TOPIC_REFRESH_UPDATE_PREVIEW
if("weight")
var/new_weight = tgui_input_number(user, "Choose your character's relative body weight.\n\
Note: Scifi characters come in all shapes and sizes in this game, and not all follow the traditional shape of a human. Like a naga or a taur or a giant will weigh a\n\
lot more than what this allows, or a micro will weigh a lot less. Just ignore all of that for a second and PRETEND the weight you're setting is visually for an \n\
average human. This is the best solution we have at the moment.!\n\
([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE)
if(new_weight)
var/unit_of_measurement = tgui_alert(user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms"))
if(!unit_of_measurement)
return TOPIC_NOACTION
if(unit_of_measurement == "Pounds")
new_weight = round(text2num(new_weight),4)
if(unit_of_measurement == "Kilograms")
new_weight = round(2.20462*text2num(new_weight),4)
pref.weight_vr = sanitize_integer(new_weight, WEIGHT_MIN, WEIGHT_MAX, pref.weight_vr)
return TOPIC_REFRESH
if("weight_gain")
var/weight_gain_rate = tgui_input_number(user, "Choose your character's rate of weight gain between 100% \
(full realism body fat gain) and 0% (no body fat gain).\n\
(If you want to disable weight gain, set this to 0.01 to round it to 0%.)\
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE)
if(weight_gain_rate)
pref.weight_gain = round(text2num(weight_gain_rate),1)
return TOPIC_REFRESH
if("weight_loss")
var/weight_loss_rate = tgui_input_number(user, "Choose your character's rate of weight loss between 100% \
(full realism body fat loss) and 0% (no body fat loss).\n\
(If you want to disable weight loss, set this to 0.01 round it to 0%.)\
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE)
if(weight_loss_rate)
pref.weight_loss = round(text2num(weight_loss_rate),1)
return TOPIC_REFRESH
if("voice_freq")
var/list/preset_voice_freqs = list("high" = MAX_VOICE_FREQ, "middle-high" = 56250, "middle" = 42500, "middle-low"= 28750, "low" = MIN_VOICE_FREQ, "custom" = 1, "random" = 0)
var/choice = tgui_input_list(user, "What would you like to set your voice frequency to? ([MIN_VOICE_FREQ] - [MAX_VOICE_FREQ])", "Voice Frequency", preset_voice_freqs)
if(!choice)
return
choice = preset_voice_freqs[choice]
if(choice == 0)
pref.voice_freq = choice
return TOPIC_REFRESH
else if(choice == 1)
choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ)
if(choice > MAX_VOICE_FREQ)
choice = MAX_VOICE_FREQ
else if(choice < MIN_VOICE_FREQ)
choice = MIN_VOICE_FREQ
pref.voice_freq = choice
return TOPIC_REFRESH
if("voice_sounds_list")
var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's speech sounds?", "Voice Sounds", SSsounds.talk_sound_map)
if(!choice)
pref.voice_sound = "beep-boop"
else
pref.voice_sound = choice
return TOPIC_REFRESH
if("customize_speech_bubble")
var/choice = tgui_input_list(user, "What speech bubble style do you want to use?", "Custom Speech Bubble", GLOB.selectable_speech_bubbles)
if(choice)
pref.custom_speech_bubble = choice
return TOPIC_REFRESH
if("customize_footsteps")
var/list/footstep_choice = selectable_footstep
var/choice = tgui_input_list(user, "What footstep sounds would your character make?", "Custom Foostep Sounds", footstep_choice)
if(choice)
pref.custom_footstep = footstep_choice[choice]
return TOPIC_REFRESH
if("voice_test")
var/sound/S = sound(pick(SSsounds.talk_sound_map[pref.voice_sound])) // talk_sound_map returns a list of sounds
if(S)
S.frequency = pick(pref.voice_freq)
S.volume = 50
SEND_SOUND(user, S)
if("customize_species_sounds") // You shouldn't be able to see this option if you don't have the option to select a custom icon base, so we don't need to re-check for safety here.
var/list/possible_species_sound_types = species_sound_map
var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's species sounds? (Cough, Sneeze, Scream, Pain, Gasp, Death)", "Species Sounds", possible_species_sound_types)
if(choice)
pref.species_sound = choice
return TOPIC_REFRESH
if("cough_test")
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["cough"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["cough"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have cough sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
if("sneeze_test")
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["sneeze"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["sneeze"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have sneeze sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
if("scream_test")
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["scream"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["scream"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have scream sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
if("pain_test")
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["pain"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["pain"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have pain sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
if("gasp_test")
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["gasp"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["gasp"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have gasp sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
if("death_test")
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["death"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["death"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have death sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
#undef WEIGHT_CHANGE_MIN
#undef WEIGHT_CHANGE_MAX
@@ -0,0 +1,39 @@
// Define a place to save in character setup
/datum/preferences
var/persistence_settings = PERSIST_DEFAULT // Control what if anything is persisted for this character between rounds.
// Definition of the stuff for Sizing
/datum/category_item/player_setup_item/general/persistence
name = "Persistence"
sort_order = 10
/datum/category_item/player_setup_item/general/persistence/load_character(list/save_data)
pref.persistence_settings = save_data["persistence_settings"]
sanitize_character() // Don't let new characters start off with nulls
/datum/category_item/player_setup_item/general/persistence/save_character(list/save_data)
save_data["persistence_settings"] = pref.persistence_settings
/datum/category_item/player_setup_item/general/persistence/sanitize_character()
pref.persistence_settings = sanitize_integer(pref.persistence_settings, 0, (1<<(PERSIST_COUNT+1)-1), initial(pref.persistence_settings))
/datum/category_item/player_setup_item/general/persistence/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["persistence_settings"] = pref.persistence_settings
return data
/datum/category_item/player_setup_item/general/persistence/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
switch(action)
if("toggle_persist")
var/bit = text2num(params["bit"])
if(pref.persistence_settings & bit)
pref.persistence_settings &= ~bit
else
pref.persistence_settings |= bit
return TOPIC_REFRESH
@@ -0,0 +1,333 @@
// Define a place to save in character setup
/datum/preferences
var/vantag_volunteer = 0 // What state I want to be in, in terms of being affected by antags.
var/vantag_preference = VANTAG_NONE // Whether I'd like to volunteer to be an antag at some point.
var/resleeve_lock = 0 // Whether movs should have OOC reslieving protection. Default false.
var/resleeve_scan = 1 // Whether mob should start with a pre-spawn body scan. Default true.
var/mind_scan = 1 // Whether mob should start with a pre-spawn mind scan. Default true.
var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already.
var/custom_say = null
var/custom_whisper = null
var/custom_ask = null
var/custom_exclaim = null
var/list/custom_heat = list()
var/list/custom_cold = list()
/datum/category_item/player_setup_item/general/vore_misc
name = "Misc Settings"
sort_order = 1
/datum/category_item/player_setup_item/general/vore_misc/load_character(list/save_data)
pref.show_in_directory = save_data["show_in_directory"]
pref.directory_tag = save_data["directory_tag"]
pref.directory_gendertag = save_data["directory_gendertag"]
pref.directory_sexualitytag = save_data["directory_sexualitytag"]
pref.directory_erptag = save_data["directory_erptag"]
pref.directory_ad = save_data["directory_ad"]
pref.sensorpref = save_data["sensorpref"]
pref.capture_crystal = save_data["capture_crystal"]
pref.auto_backup_implant = save_data["auto_backup_implant"]
pref.borg_petting = save_data["borg_petting"]
pref.resleeve_lock = save_data["resleeve_lock"]
pref.resleeve_scan = save_data["resleeve_scan"]
pref.mind_scan = save_data["mind_scan"]
pref.vantag_volunteer = save_data["vantag_volunteer"]
pref.vantag_preference = save_data["vantag_preference"]
pref.custom_species = save_data["custom_species"]
pref.custom_say = save_data["custom_say"]
pref.custom_whisper = save_data["custom_whisper"]
pref.custom_ask = save_data["custom_ask"]
pref.custom_exclaim = save_data["custom_exclaim"]
pref.custom_heat = check_list_copy(save_data["custom_heat"])
pref.custom_cold = check_list_copy(save_data["custom_cold"])
/datum/category_item/player_setup_item/general/vore_misc/save_character(list/save_data)
save_data["show_in_directory"] = pref.show_in_directory
save_data["directory_tag"] = pref.directory_tag
save_data["directory_gendertag"] = pref.directory_gendertag
save_data["directory_sexualitytag"] = pref.directory_sexualitytag
save_data["directory_erptag"] = pref.directory_erptag
save_data["directory_ad"] = pref.directory_ad
save_data["sensorpref"] = pref.sensorpref
save_data["capture_crystal"] = pref.capture_crystal
save_data["auto_backup_implant"] = pref.auto_backup_implant
save_data["borg_petting"] = pref.borg_petting
save_data["resleeve_lock"] = pref.resleeve_lock
save_data["resleeve_scan"] = pref.resleeve_scan
save_data["mind_scan"] = pref.mind_scan
save_data["vantag_volunteer"] = pref.vantag_volunteer
save_data["vantag_preference"] = pref.vantag_preference
save_data["custom_species"] = pref.custom_species
save_data["custom_say"] = pref.custom_say
save_data["custom_whisper"] = pref.custom_whisper
save_data["custom_ask"] = pref.custom_ask
save_data["custom_exclaim"] = pref.custom_exclaim
save_data["custom_heat"] = check_list_copy(pref.custom_heat)
save_data["custom_cold"] = check_list_copy(pref.custom_cold)
/datum/category_item/player_setup_item/general/vore_misc/copy_to_mob(var/mob/living/carbon/human/character)
character.custom_species = pref.custom_species
character.custom_say = lowertext(trim(pref.custom_say))
character.custom_ask = lowertext(trim(pref.custom_ask))
character.custom_whisper = lowertext(trim(pref.custom_whisper))
character.custom_exclaim = lowertext(trim(pref.custom_exclaim))
character.custom_heat = pref.custom_heat
character.custom_cold = pref.custom_cold
if(pref.sensorpref > 5 || pref.sensorpref < 1)
pref.sensorpref = 5
character.sensorpref = pref.sensorpref
character.capture_crystal = pref.capture_crystal
//Vore Stomach Sprite Preference
character.recalculate_vis()
if(character && !istype(character,/mob/living/carbon/human/dummy))
character.vantag_pref = pref.vantag_preference
BITSET(character.hud_updateflag, VANTAG_HUD)
spawn(50)
if(QDELETED(character) || QDELETED(pref))
return // They might have been deleted during the wait
if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore
if(pref.resleeve_scan)
var/datum/transhuman/body_record/BR = new()
BR.init_from_mob(character, pref.resleeve_scan, pref.resleeve_lock)
if(pref.mind_scan)
var/datum/transcore_db/our_db = SStranscore.db_by_key(null)
if(our_db)
our_db.m_backup(character.mind,character.nif,one_time = TRUE)
if(pref.resleeve_lock)
character.resleeve_lock = character.ckey
character.original_player = character.ckey
/datum/category_item/player_setup_item/general/vore_misc/sanitize_character()
pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory))
pref.directory_tag = sanitize_inlist(pref.directory_tag, GLOB.char_directory_tags, initial(pref.directory_tag))
pref.directory_gendertag = sanitize_inlist(pref.directory_gendertag, GLOB.char_directory_gendertags, initial(pref.directory_gendertag))
pref.directory_sexualitytag = sanitize_inlist(pref.directory_sexualitytag, GLOB.char_directory_sexualitytags, initial(pref.directory_sexualitytag))
pref.directory_erptag = sanitize_inlist(pref.directory_erptag, GLOB.char_directory_erptags, initial(pref.directory_erptag))
pref.sensorpref = sanitize_integer(pref.sensorpref, 1, GLOB.sensorpreflist.len, initial(pref.sensorpref))
pref.capture_crystal = sanitize_integer(pref.capture_crystal, 0, 1, initial(pref.capture_crystal))
pref.auto_backup_implant = sanitize_integer(pref.auto_backup_implant, 0, 1, initial(pref.auto_backup_implant))
pref.borg_petting = sanitize_integer(pref.borg_petting, 0, 1, initial(pref.borg_petting))
pref.resleeve_lock = sanitize_integer(pref.resleeve_lock, 0, 1, initial(pref.resleeve_lock))
pref.resleeve_scan = sanitize_integer(pref.resleeve_scan, 0, 1, initial(pref.resleeve_scan))
pref.mind_scan = sanitize_integer(pref.mind_scan, 0, 1, initial(pref.mind_scan))
pref.vantag_volunteer = sanitize_integer(pref.vantag_volunteer, 0, 1, initial(pref.vantag_volunteer))
pref.vantag_preference = sanitize_inlist(pref.vantag_preference, GLOB.vantag_choices_list, initial(pref.vantag_preference))
pref.custom_say = lowertext(trim(pref.custom_say))
pref.custom_whisper = lowertext(trim(pref.custom_whisper))
pref.custom_ask = lowertext(trim(pref.custom_ask))
pref.custom_exclaim = lowertext(trim(pref.custom_exclaim))
if (islist(pref.custom_heat)) //don't bother checking these for actual singular message length, they should already have been checked and it'd take too long every time it's sanitized
if (length(pref.custom_heat) > 10)
pref.custom_heat.Cut(11)
else
pref.custom_heat = list()
if (islist(pref.custom_cold))
if (length(pref.custom_cold) > 10)
pref.custom_cold.Cut(11)
else
pref.custom_cold = list()
/datum/category_item/player_setup_item/general/vore_misc/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["show_in_directory"] = pref.show_in_directory
data["directory_tag"] = pref.directory_tag
data["directory_gendertag"] = pref.directory_gendertag
data["directory_sexualitytag"] = pref.directory_sexualitytag
data["directory_erptag"] = pref.directory_erptag
data["sensorpref"] = GLOB.sensorpreflist[pref.sensorpref]
data["capture_crystal"] = pref.capture_crystal
data["auto_backup_implant"] = pref.auto_backup_implant
data["borg_petting"] = pref.borg_petting
data["resleeve_lock"] = pref.resleeve_lock
data["resleeve_scan"] = pref.resleeve_scan
data["mind_scan"] = pref.mind_scan
data["vantag_volunteer"] = pref.vantag_volunteer
data["vantag_preference"] = GLOB.vantag_choices_list[pref.vantag_preference]
data["custom_species"] = pref.custom_species
return data
/datum/category_item/player_setup_item/general/vore_misc/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("toggle_show_in_directory")
pref.show_in_directory = pref.show_in_directory ? 0 : 1;
return TOPIC_REFRESH
if("directory_tag")
var/new_tag = tgui_input_list(user, "Pick a new Vore tag for the character directory", "Character Vore Tag", GLOB.char_directory_tags, pref.directory_tag)
if(!new_tag)
return
pref.directory_tag = new_tag
return TOPIC_REFRESH
if("directory_gendertag")
var/new_gendertag = tgui_input_list(user, "Pick a new Gender tag for the character directory. This is YOUR gender, not what you prefer.", "Character Gender Tag", GLOB.char_directory_gendertags, pref.directory_gendertag)
if(!new_gendertag)
return
pref.directory_gendertag = new_gendertag
return TOPIC_REFRESH
if("directory_sexualitytag")
var/new_sexualitytag = tgui_input_list(user, "Pick a new Sexuality/Orientation tag for the character directory", "Character Sexuality/Orientation Tag", GLOB.char_directory_sexualitytags, pref.directory_sexualitytag)
if(!new_sexualitytag)
return
pref.directory_sexualitytag = new_sexualitytag
return TOPIC_REFRESH
if("directory_erptag")
var/new_erptag = tgui_input_list(user, "Pick a new ERP tag for the character directory", "Character ERP Tag", GLOB.char_directory_erptags, pref.directory_erptag)
if(!new_erptag)
return
pref.directory_erptag = new_erptag
return TOPIC_REFRESH
if("directory_ad")
var/msg = sanitize(tgui_input_text(user,"Write your advertisement here!", "Flavor Text", html_decode(pref.directory_ad), multiline = TRUE, prevent_enter = TRUE), extra = 0) //VOREStation Edit: separating out OOC notes
if(!msg)
return
pref.directory_ad = msg
return TOPIC_REFRESH
if("toggle_sensor_setting")
var/new_sensorpref = tgui_input_list(user, "Choose your character's sensor preferences:", "Character Preferences", GLOB.sensorpreflist, GLOB.sensorpreflist[pref.sensorpref])
if (!isnull(new_sensorpref))
pref.sensorpref = GLOB.sensorpreflist.Find(new_sensorpref)
return TOPIC_REFRESH
if("toggle_capture_crystal")
pref.capture_crystal = pref.capture_crystal ? 0 : 1;
return TOPIC_REFRESH
if("toggle_implant")
pref.auto_backup_implant = pref.auto_backup_implant ? 0 : 1;
return TOPIC_REFRESH
if("toggle_borg_petting")
pref.borg_petting = pref.borg_petting ? 0 : 1;
return TOPIC_REFRESH
if("edit_private_notes")
var/new_metadata = sanitize(tgui_input_text(user,"Write some notes for yourself. These can be anything that is useful, whether it's character events that you want to remember or a bit of lore. Things that you would normally stick in a txt file for yourself!", "Private Notes", html_decode(pref.read_preference(/datum/preference/text/living/private_notes)), multiline = TRUE, prevent_enter = TRUE), extra = 0)
if(new_metadata)
pref.update_preference_by_type(/datum/preference/text/living/private_notes, new_metadata)
return TOPIC_REFRESH
if("toggle_resleeve_lock")
pref.resleeve_lock = pref.resleeve_lock ? 0 : 1;
return TOPIC_REFRESH
if("toggle_resleeve_scan")
pref.resleeve_scan = pref.resleeve_scan ? 0 : 1;
return TOPIC_REFRESH
if("toggle_mind_scan")
pref.mind_scan = pref.mind_scan ? 0 : 1;
return TOPIC_REFRESH
if("toggle_vantag_volunteer")
pref.vantag_volunteer = pref.vantag_volunteer ? 0 : 1
return TOPIC_REFRESH
if("change_vantag")
var/list/names_list = list()
for(var/C in GLOB.vantag_choices_list)
names_list[GLOB.vantag_choices_list[C]] = C
var/selection = tgui_input_list(user, "How do you want to be involved with VS Event Characters, ERP-wise? They will see this choice on you in a HUD. Event characters are admin-selected and spawned players, possibly with assigned objectives, who are obligated to respect ERP prefs and RP their actions like any other player, though it may be a slightly shorter RP if they are pressed for time or being caught.", "Event Preference", names_list)
if(selection && selection != "Normal")
pref.vantag_preference = names_list[selection]
return TOPIC_REFRESH
if("custom_say")
var/say_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'says': [pref.real_name] says, \"Hi.\"", "Custom Say", pref.custom_say, 12), 12)
if(say_choice)
pref.custom_say = say_choice
return TOPIC_REFRESH
if("custom_whisper")
var/whisper_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'whispers': [pref.real_name] whispers, \"Hi...\"", "Custom Whisper", pref.custom_whisper, 12), 12)
if(whisper_choice)
pref.custom_whisper = whisper_choice
return TOPIC_REFRESH
if("custom_ask")
var/ask_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'asks': [pref.real_name] asks, \"Hi?\"", "Custom Ask", pref.custom_ask, 12), 12)
if(ask_choice)
pref.custom_ask = ask_choice
return TOPIC_REFRESH
if("custom_exclaim")
var/exclaim_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [pref.real_name] exclaims, \"Hi!\"", "Custom Exclaim", pref.custom_exclaim, 12), 12)
if(exclaim_choice)
pref.custom_exclaim = exclaim_choice
return TOPIC_REFRESH
if("custom_heat")
tgui_alert(user, "You are setting custom heat messages. These will overwrite your species' defaults. To return to defaults, click reset.")
var/old_message = pref.custom_heat.Join("\n\n")
var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Heat Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0)
if(length(new_message) > 0)
var/list/raw_list = splittext(new_message,"\n\n")
if(raw_list.len > 10)
raw_list.Cut(11)
for(var/i = 1, i <= raw_list.len, i++)
if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160)
raw_list.Cut(i,i)
else
raw_list[i] = readd_quotes(raw_list[i])
ASSERT(raw_list.len <= 10)
pref.custom_heat = raw_list
return TOPIC_REFRESH
if("custom_cold")
tgui_alert(user, "You are setting custom cold messages. These will overwrite your species' defaults. To return to defaults, click reset.")
var/old_message = pref.custom_heat.Join("\n\n")
var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Cold Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0)
if(length(new_message) > 0)
var/list/raw_list = splittext(new_message,"\n\n")
if(raw_list.len > 10)
raw_list.Cut(11)
for(var/i = 1, i <= raw_list.len, i++)
if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160)
raw_list.Cut(i,i)
else
raw_list[i] = readd_quotes(raw_list[i])
ASSERT(raw_list.len <= 10)
pref.custom_cold = raw_list
return TOPIC_REFRESH
if("reset_say")
var/say_choice = tgui_alert(user, "Reset your Custom Say Verb?","Reset Verb",list("Yes","No"))
if(say_choice == "Yes")
pref.custom_say = null
return TOPIC_REFRESH
if("reset_whisper")
var/whisper_choice = tgui_alert(user, "Reset your Custom Whisper Verb?","Reset Verb",list("Yes","No"))
if(whisper_choice == "Yes")
pref.custom_whisper = null
return TOPIC_REFRESH
if("reset_ask")
var/ask_choice = tgui_alert(user, "Reset your Custom Ask Verb?","Reset Verb",list("Yes","No"))
if(ask_choice == "Yes")
pref.custom_ask = null
return TOPIC_REFRESH
if("reset_exclaim")
var/exclaim_choice = tgui_alert(user, "Reset your Custom Exclaim Verb?","Reset Verb",list("Yes","No"))
if(exclaim_choice == "Yes")
pref.custom_exclaim = null
return TOPIC_REFRESH
if("reset_cold")
var/cold_choice = tgui_alert(user, "Reset your Custom Cold Discomfort messages?", "Reset Discomfort",list("Yes","No"))
if(cold_choice == "Yes")
pref.custom_cold = list()
return TOPIC_REFRESH
if("reset_heat")
var/heat_choice = tgui_alert(user, "Reset your Custom Heat Discomfort messages?", "Reset Discomfort",list("Yes","No"))
if(heat_choice == "Yes")
pref.custom_heat = list()
return TOPIC_REFRESH
if("custom_species")
var/raw_choice = sanitize(tgui_input_text(user, "Input your custom species name:",
"Character Preference", pref.custom_species, MAX_NAME_LEN), MAX_NAME_LEN)
pref.custom_species = raw_choice
return TOPIC_REFRESH
@@ -0,0 +1,412 @@
#define POSITIVE_MODE 1
#define NEUTRAL_MODE 2
#define NEGATIVE_MODE 3
var/global/list/valid_bloodreagents = list("default",REAGENT_ID_IRON,REAGENT_ID_COPPER,REAGENT_ID_PHORON,REAGENT_ID_SILVER,REAGENT_ID_GOLD,REAGENT_ID_SLIMEJELLY) //allowlist-based so people don't make their blood restored by alcohol or something really silly. use reagent IDs!
/datum/preferences
var/custom_base // What to base the custom species on
var/blood_color = "#A10808"
var/list/pos_traits = list() // What traits they've selected for their custom species
var/list/neu_traits = list()
var/list/neg_traits = list()
var/traits_cheating = 0 //Varedit by admins allows saving new maximums on people who apply/etc
var/starting_trait_points = 0
var/max_traits = MAX_SPECIES_TRAITS
var/dirty_synth = 0 //Are you a synth
var/gross_meatbag = 0 //Where'd I leave my Voight-Kampff test kit?
/datum/preferences/proc/get_custom_bases_for_species(var/new_species)
if (!new_species)
new_species = species
var/list/choices
var/datum/species/spec = GLOB.all_species[new_species]
if (spec.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER)
choices = spec.get_valid_shapeshifter_forms()
choices = choices.Copy()
else if (spec.selects_bodytype == SELECTS_BODYTYPE_CUSTOM)
choices = GLOB.custom_species_bases.Copy()
if(new_species != SPECIES_CUSTOM)
choices = (choices | new_species)
return choices
/datum/category_item/player_setup_item/general/traits/proc/get_pref_choice_from_trait(var/mob/user, var/datum/trait/trait, var/preference)
if (!trait || !preference)
return
var/list/trait_prefs
var/datum/trait/instance = GLOB.all_traits[trait]
var/list/traitlist
switch(instance.category)
if (1)
traitlist = pref.pos_traits
if (0)
traitlist = pref.neu_traits
if (-1)
traitlist = pref.neg_traits
if (!LAZYLEN(instance.has_preferences) || !(preference in instance.has_preferences) || !traitlist)
return
if (!LAZYLEN(traitlist[trait]))
traitlist[trait] = instance.get_default_prefs()
trait_prefs = traitlist[trait]
if (!(preference in trait_prefs))
trait_prefs[preference] = instance.default_value_for_pref(preference) //won't be called at all often
switch(instance.has_preferences[preference][1])
if (1) //TRAIT_PREF_TYPE_BOOLEAN
trait_prefs[preference] = !trait_prefs[preference]
if (2) //TRAIT_PREF_TYPE_COLOR
var/new_color = tgui_color_picker(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference])
if (new_color)
trait_prefs[preference] = new_color
if (3) //TRAIT_PREF_TYPE_STRING
var/new_string = instance.apply_sanitization_to_string(preference, tgui_input_text(user, "What should the new value be?", instance.has_preferences[preference][2], trait_prefs[preference], MAX_NAME_LEN))
trait_prefs[preference] = new_string
// Definition of the stuff for Ears
/datum/category_item/player_setup_item/general/traits
name = "Traits"
sort_order = 7
/datum/category_item/player_setup_item/general/traits/load_character(list/save_data)
pref.custom_base = save_data["custom_base"]
pref.pos_traits = text2path_list(save_data["pos_traits"])
pref.neu_traits = text2path_list(save_data["neu_traits"])
pref.neg_traits = text2path_list(save_data["neg_traits"])
pref.blood_color = save_data["blood_color"]
pref.blood_reagents = save_data["blood_reagents"]
pref.traits_cheating = save_data["traits_cheating"]
pref.max_traits = save_data["max_traits"]
pref.starting_trait_points = save_data["trait_points"]
/datum/category_item/player_setup_item/general/traits/save_character(list/save_data)
save_data["custom_base"] = pref.custom_base
save_data["pos_traits"] = check_list_copy(pref.pos_traits)
save_data["neu_traits"] = check_list_copy(pref.neu_traits)
save_data["neg_traits"] = check_list_copy(pref.neg_traits)
save_data["blood_color"] = pref.blood_color
save_data["blood_reagents"] = pref.blood_reagents
save_data["traits_cheating"] = pref.traits_cheating
save_data["max_traits"] = pref.max_traits
save_data["trait_points"] = pref.starting_trait_points
/datum/category_item/player_setup_item/general/traits/sanitize_character()
if(!pref.pos_traits) pref.pos_traits = list()
if(!pref.neu_traits) pref.neu_traits = list()
if(!pref.neg_traits) pref.neg_traits = list()
pref.blood_color = sanitize_hexcolor(pref.blood_color, default="#A10808")
pref.blood_reagents = sanitize_text(pref.blood_reagents, initial(pref.blood_reagents))
if(!pref.traits_cheating)
var/datum/species/S = GLOB.all_species[pref.species]
if(S)
pref.starting_trait_points = S.trait_points
else
pref.starting_trait_points = 0
pref.max_traits = MAX_SPECIES_TRAITS
if(pref.organ_data[O_BRAIN]) //Checking if we have a synth on our hands, boys.
pref.dirty_synth = 1
pref.gross_meatbag = 0
else
pref.gross_meatbag = 1
pref.dirty_synth = 0
// Clean up positive traits
for(var/datum/trait/path as anything in pref.pos_traits)
if(!(path in GLOB.positive_traits))
pref.pos_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_positive))
pref.pos_traits -= path
continue
var/take_flags = initial(path.can_take)
if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS)))
pref.pos_traits -= path
//Neutral traits
for(var/datum/trait/path as anything in pref.neu_traits)
if(!(path in GLOB.neutral_traits))
to_world_log("removing [path] for not being in neutral_traits")
pref.neu_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_neutral))
to_world_log("removing [path] for not being a custom species")
pref.neu_traits -= path
continue
var/take_flags = initial(path.can_take)
if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS)))
to_world_log("removing [path] for being a dirty synth")
pref.neu_traits -= path
//Negative traits
for(var/datum/trait/path as anything in pref.neg_traits)
if(!(path in GLOB.negative_traits))
pref.neg_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_negative))
pref.neg_traits -= path
continue
var/take_flags = initial(path.can_take)
if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS)))
pref.neg_traits -= path
var/datum/species/selected_species = GLOB.all_species[pref.species]
if(selected_species.selects_bodytype)
if (!(pref.custom_base in pref.get_custom_bases_for_species()))
pref.custom_base = SPECIES_HUMAN
//otherwise, allowed!
else if(!pref.custom_base || !(pref.custom_base in GLOB.custom_species_bases))
pref.custom_base = SPECIES_HUMAN
/datum/category_item/player_setup_item/general/traits/copy_to_mob(var/mob/living/carbon/human/character)
if(character.isSynthetic()) //Checking if we have a synth on our hands, boys.
pref.dirty_synth = 1
pref.gross_meatbag = 0
else
pref.gross_meatbag = 1
pref.dirty_synth = 0
var/datum/species/S = character.species
var/datum/species/new_S = S.produceCopy(pref.pos_traits + pref.neu_traits + pref.neg_traits, character, pref.custom_base, TRUE)
for(var/datum/trait/T in new_S.traits)
T.apply_pref(src)
//Any additional non-trait settings can be applied here
new_S.blood_color = pref.blood_color
if(!(pref.blood_reagents == "default"))
new_S.blood_reagents = pref.blood_reagents
var/species_sounds_to_copy = pref.species_sound // What sounds are we using?
if(species_sounds_to_copy == "Unset") // Are we unset?
species_sounds_to_copy = select_default_species_sound(pref) // This will also grab gendered versions of the sounds, if they exist.
new_S.species_sounds = species_sounds_to_copy // Now we send our sounds over to the mob
if(pref.species == SPECIES_CUSTOM)
//Statistics for this would be nice
var/english_traits = english_list(new_S.traits, and_text = ";", comma_text = ";")
log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet
/datum/category_item/player_setup_item/general/traits/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/datum/species/selected_species = GLOB.all_species[pref.species]
data["selects_bodytype"] = selected_species.selects_bodytype
data["custom_base"] = pref.custom_base
data["blood_color"] = pref.blood_color
data["blood_reagents"] = pref.blood_reagents
data["pos_traits"] = pref.pos_traits
data["neu_traits"] = pref.neu_traits
data["neg_traits"] = pref.neg_traits
data["traits_cheating"] = pref.traits_cheating
data["max_traits"] = pref.max_traits
data["trait_points"] = pref.starting_trait_points
return data
/datum/category_item/player_setup_item/general/traits/tgui_constant_data()
var/list/data = ..()
var/list/all_traits = list()
for(var/path in GLOB.all_traits)
var/datum/trait/T = GLOB.all_traits[path]
all_traits[path] = list(
"cost" = T.cost,
"name" = T.name,
"category" = T.category,
"has_preferences" = T.has_preferences,
)
data["all_traits"] = all_traits
return data
/datum/category_item/player_setup_item/general/traits/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("custom_base")
var/list/choices = pref.get_custom_bases_for_species()
var/text_choice = tgui_input_list(user, "Pick an icon set for your species:","Icon Base", choices)
if(text_choice in choices)
pref.custom_base = text_choice
return TOPIC_REFRESH_UPDATE_PREVIEW
if("blood_color")
var/color_choice = tgui_color_picker(user, "Pick a blood color (does not apply to synths)","Blood Color",pref.blood_color)
if(color_choice)
pref.blood_color = sanitize_hexcolor(color_choice, default="#A10808")
return TOPIC_REFRESH
if("blood_reset")
var/datum/species/spec = GLOB.all_species[pref.species]
var/new_blood = spec.blood_color ? spec.blood_color : "#A10808"
var/choice = tgui_alert(user, "Reset blood color to species default ([new_blood])?","Reset Blood Color",list("Reset","Cancel"))
if(choice == "Reset")
pref.blood_color = new_blood
return TOPIC_REFRESH
if("blood_reagents")
var/new_blood_reagents = tgui_input_list(user, "Choose your character's blood restoration reagent:", "Character Preference", valid_bloodreagents)
if(new_blood_reagents && CanUseTopic(user))
pref.blood_reagents = new_blood_reagents
return TOPIC_REFRESH
if("clicked_pos_trait")
var/datum/trait/trait = text2path(params["trait"])
var/choice = tgui_alert(user, "Remove [initial(trait.name)] and regain [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.pos_traits -= trait
var/datum/trait/instance = GLOB.all_traits[trait]
instance.remove_pref(pref)
return TOPIC_REFRESH
if("clicked_neu_trait")
var/datum/trait/trait = text2path(params["trait"])
var/choice = tgui_alert(user, "Remove [initial(trait.name)]?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.neu_traits -= trait
var/datum/trait/instance = GLOB.all_traits[trait]
instance.remove_pref(pref)
return TOPIC_REFRESH
if("clicked_neg_trait")
var/datum/trait/trait = text2path(params["trait"])
var/choice = tgui_alert(user, "Remove [initial(trait.name)] and lose [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.neg_traits -= trait
var/datum/trait/instance = GLOB.all_traits[trait]
instance.remove_pref(pref)
return TOPIC_REFRESH
if("clicked_trait_pref")
var/datum/trait/trait = text2path(params["clicked_trait_pref"])
get_pref_choice_from_trait(user, trait, params["pref"])
return TOPIC_REFRESH
if("add_trait")
var/mode = text2num(params["add_trait"])
var/list/picklist
var/list/mylist
switch(mode)
if(POSITIVE_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = GLOB.positive_traits.Copy() - pref.pos_traits
mylist = pref.pos_traits
else
picklist = GLOB.everyone_traits_positive.Copy() - pref.pos_traits
mylist = pref.pos_traits
if(NEUTRAL_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = GLOB.neutral_traits.Copy() - pref.neu_traits
mylist = pref.neu_traits
else
picklist = GLOB.everyone_traits_neutral.Copy() - pref.neu_traits
mylist = pref.neu_traits
if(NEGATIVE_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = GLOB.negative_traits.Copy() - pref.neg_traits
mylist = pref.neg_traits
else
picklist = GLOB.everyone_traits_negative.Copy() - pref.neg_traits
mylist = pref.neg_traits
if(isnull(picklist))
return TOPIC_REFRESH
if(isnull(mylist))
return TOPIC_REFRESH
var/list/nicelist = list()
for(var/P in picklist)
var/datum/trait/T = picklist[P]
nicelist[T.name] = P
var/points_left = pref.starting_trait_points
for(var/T in pref.pos_traits + pref.neu_traits + pref.neg_traits)
points_left -= GLOB.traits_costs[T]
var/traits_left = pref.max_traits - (pref.pos_traits.len + pref.neg_traits.len)
var/message = "Select a trait to learn more."
if(mode != NEUTRAL_MODE)
message = "\[Remaining: [points_left] points, [traits_left] traits\]\n" + message
var/title = "Traits"
switch(mode)
if(POSITIVE_MODE)
title = "Positive Traits"
if(NEUTRAL_MODE)
title = "Neutral Traits"
if(NEGATIVE_MODE)
title = "Negative Traits"
var/trait_choice
var/done = FALSE
while(!done)
trait_choice = tgui_input_list(user, message, title, nicelist)
if(!trait_choice)
done = TRUE
if(trait_choice in nicelist)
var/datum/trait/path = nicelist[trait_choice]
var/choice = tgui_alert(user, "\[Cost:[initial(path.cost)]\] [initial(path.desc)]",initial(path.name), list("Take Trait","Go Back"))
if(choice == "Take Trait")
done = TRUE
if(!trait_choice)
return TOPIC_REFRESH
else if(trait_choice in nicelist)
var/datum/trait/path = nicelist[trait_choice]
var/datum/trait/instance = GLOB.all_traits[path]
var/conflict = FALSE
if(pref.dirty_synth && !(instance.can_take & SYNTHETICS))
tgui_alert_async(user, "The trait you've selected can only be taken by organic characters!", "Error")
return TOPIC_REFRESH
if(pref.gross_meatbag && !(instance.can_take & ORGANICS))
tgui_alert_async(user, "The trait you've selected can only be taken by synthetic characters!", "Error")
return TOPIC_REFRESH
if(pref.species in instance.banned_species)
tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error")
return TOPIC_REFRESH
if( LAZYLEN(instance.allowed_species) && !(pref.species in instance.allowed_species))
tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error")
return TOPIC_REFRESH
if(trait_choice in (pref.pos_traits + pref.neu_traits + pref.neg_traits))
conflict = instance.name
varconflict:
for(var/P in (pref.pos_traits + pref.neu_traits + pref.neg_traits))
var/datum/trait/instance_test = GLOB.all_traits[P]
if(path in instance_test.excludes)
conflict = instance_test.name
break varconflict
for(var/V in instance.var_changes)
if(V == "flags")
continue
if(V in instance_test.var_changes)
conflict = instance_test.name
break varconflict
for(var/V in instance.var_changes_pref)
if(V in instance_test.var_changes_pref)
conflict = instance_test.name
break varconflict
if(conflict)
tgui_alert_async(user, "You cannot take this trait and [conflict] at the same time. Please remove that trait, or pick another trait to add.", "Error")
return TOPIC_REFRESH
instance.apply_pref(pref)
mylist[path] = instance.get_default_prefs()
return TOPIC_REFRESH
#undef POSITIVE_MODE
#undef NEUTRAL_MODE
#undef NEGATIVE_MODE
@@ -6,7 +6,7 @@
// Definition of the stuff for NIFs
// Magic bullshit, they're stored separately from everything else
/datum/category_item/player_setup_item/vore/nif
/datum/category_item/player_setup_item/general/nif
name = "NIF Data"
sort_order = 8
@@ -15,7 +15,7 @@
return
return "data/player_saves/[copytext(ckey,1,2)]/[ckey]/nif.json"
/datum/category_item/player_setup_item/vore/nif/load_character()
/datum/category_item/player_setup_item/general/nif/load_character()
var/datum/json_savefile/savefile = new /datum/json_savefile(nif_savefile_path(pref.client_ckey))
var/list/save_data_file = savefile.get_entry("character[pref.default_slot]", list())
@@ -23,7 +23,7 @@
pref.nif_durability = save_data_file["nif_durability"]
pref.nif_savedata = check_list_copy(save_data_file["nif_savedata"])
/datum/category_item/player_setup_item/vore/nif/save_character()
/datum/category_item/player_setup_item/general/nif/save_character()
var/datum/json_savefile/savefile = new /datum/json_savefile(nif_savefile_path(pref.client_ckey))
var/list/save_data_file = savefile.get_entry("character[pref.default_slot]", list())
@@ -34,7 +34,7 @@
savefile.set_entry("character[pref.default_slot]", save_data_file)
savefile.save()
/datum/category_item/player_setup_item/vore/nif/sanitize_character()
/datum/category_item/player_setup_item/general/nif/sanitize_character()
if(pref.nif_path && !ispath(pref.nif_path)) //We have at least a text string that should be a path.
pref.nif_path = text2path(pref.nif_path) //Try to convert it to a hard path.
if(!pref.nif_path) //If we couldn't, kill it.
@@ -51,10 +51,14 @@
if(!islist(pref.nif_savedata))
pref.nif_savedata = list()
/datum/category_item/player_setup_item/vore/nif/copy_to_mob(var/mob/living/carbon/human/character)
/datum/category_item/player_setup_item/general/nif/copy_to_mob(var/mob/living/carbon/human/character)
//If you had a NIF...
if(istype(character) && ispath(pref.nif_path) && pref.nif_durability)
new pref.nif_path(character, pref.nif_durability, pref.nif_savedata)
/datum/category_item/player_setup_item/vore/nif/content(var/mob/user)
. += span_bold("NIF:") + " [ispath(pref.nif_path) ? "Present" : "None"]"
/datum/category_item/player_setup_item/general/nif/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["nif"] = ispath(pref.nif_path)
return data
@@ -2,11 +2,11 @@
var/list/all_underwear
var/list/all_underwear_metadata
/datum/category_item/player_setup_item/general/equipment
name = "Clothing"
sort_order = 4
/datum/category_item/player_setup_item/loadout/equipment
name = "Equipment"
sort_order = 1
/datum/category_item/player_setup_item/general/equipment/load_character(list/save_data)
/datum/category_item/player_setup_item/loadout/equipment/load_character(list/save_data)
pref.all_underwear = check_list_copy(save_data["all_underwear"])
pref.all_underwear_metadata = check_list_copy(save_data["all_underwear_metadata"])
for(var/i in pref.all_underwear_metadata)
@@ -19,7 +19,7 @@
pref.shoe_hater = save_data["shoe_hater"]
pref.no_jacket = save_data["no_jacket"]
/datum/category_item/player_setup_item/general/equipment/save_character(list/save_data)
/datum/category_item/player_setup_item/loadout/equipment/save_character(list/save_data)
save_data["all_underwear"] = pref.all_underwear
var/list/underwear = list()
for(var/i in pref.all_underwear_metadata)
@@ -61,7 +61,7 @@ var/global/list/valid_ringtones = list(
)
// Moved from /datum/preferences/proc/copy_to()
/datum/category_item/player_setup_item/general/equipment/copy_to_mob(var/mob/living/carbon/human/character)
/datum/category_item/player_setup_item/loadout/equipment/copy_to_mob(var/mob/living/carbon/human/character)
character.all_underwear.Cut()
character.all_underwear_metadata.Cut()
@@ -88,9 +88,7 @@ var/global/list/valid_ringtones = list(
pref.pdachoice = 1
character.pdachoice = pref.pdachoice
/datum/category_item/player_setup_item/general/equipment/sanitize_character()
if(!islist(pref.gear)) pref.gear = list()
/datum/category_item/player_setup_item/loadout/equipment/sanitize_character()
if(!istype(pref.all_underwear))
pref.all_underwear = list()
@@ -120,29 +118,53 @@ var/global/list/valid_ringtones = list(
pref.pdachoice = sanitize_integer(pref.pdachoice, 1, pdachoicelist.len, initial(pref.pdachoice))
pref.ringtone = sanitize(pref.ringtone, 20)
/datum/category_item/player_setup_item/general/equipment/content()
. = list()
. += span_bold("Equipment:") + "<br>"
/datum/category_item/player_setup_item/loadout/equipment/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/list/underwear_data = list()
for(var/datum/category_group/underwear/UWC in global_underwear.categories)
var/item_name = pref.all_underwear[UWC.name] ? pref.all_underwear[UWC.name] : "None"
. += "[UWC.name]: <a href='byond://?src=\ref[src];change_underwear=[UWC.name]'><b>[item_name]</b></a>"
var/item_name = LAZYACCESS(pref.all_underwear, UWC.name) || "None"
var/list/tweaks = list()
var/datum/category_item/underwear/UWI = UWC.items_by_name[item_name]
if(UWI)
for(var/datum/gear_tweak/gt in UWI.tweaks)
. += " <a href='byond://?src=\ref[src];underwear=[UWC.name];tweak=\ref[gt]'>[gt.get_contents(get_metadata(UWC.name, gt))]</a>"
UNTYPED_LIST_ADD(tweaks, list(
"ref" = REF(gt),
"contents" = gt.get_contents(get_metadata(UWC.name, gt))
))
. += "<br>"
. += "Headset Type: <a href='byond://?src=\ref[src];change_headset=1'><b>[GLOB.headsetlist[pref.headset]]</b></a><br>"
. += "Backpack Type: <a href='byond://?src=\ref[src];change_backpack=1'><b>[backbaglist[pref.backbag]]</b></a><br>"
. += "PDA Type: <a href='byond://?src=\ref[src];change_pda=1'><b>[pdachoicelist[pref.pdachoice]]</b></a><br>"
. += "Communicator Visibility: <a href='byond://?src=\ref[src];toggle_comm_visibility=1'><b>[(pref.communicator_visibility) ? "Yes" : "No"]</b></a><br>"
. += "Ringtone (leave blank for job default): <a href='byond://?src=\ref[src];set_ringtone=1'><b>[pref.ringtone]</b></a><br>"
. += "Spawn With Shoes:<a href='byond://?src=\ref[src];toggle_shoes=1'><b>[(pref.shoe_hater) ? "No" : "Yes"]</b></a><br>"
. += "Spawn With Jacket:<a href='byond://?src=\ref[src];toggle_jacket=1'><b>[(pref.no_jacket) ? "No" : "Yes"]</b></a><br>"
UNTYPED_LIST_ADD(underwear_data, list(
"category" = UWC.name,
"name" = item_name,
"tweaks" = tweaks
))
data["underwear"] = underwear_data
return jointext(.,null)
data["headset_type"] = GLOB.headsetlist[pref.headset]
data["backpack_type"] = backbaglist[pref.backbag]
data["pda_type"] = pdachoicelist[pref.pdachoice]
data["communicator_visibility"] = pref.communicator_visibility // boolean
data["ringtone"] = pref.ringtone
data["shoes"] = pref.shoe_hater
data["jacket"] = pref.no_jacket
/datum/category_item/player_setup_item/general/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt)
return data
/datum/category_item/player_setup_item/loadout/equipment/tgui_static_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
return data
/datum/category_item/player_setup_item/loadout/equipment/tgui_constant_data()
var/list/data = ..()
data["headsetlist"] = GLOB.headsetlist
data["backbaglist"] = backbaglist
data["pdachoicelist"] = pdachoicelist
return data
/datum/category_item/player_setup_item/loadout/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt)
var/metadata = pref.all_underwear_metadata[underwear_category]
if(!metadata)
metadata = list()
@@ -154,72 +176,80 @@ var/global/list/valid_ringtones = list(
metadata["[gt]"] = tweak_data
return tweak_data
/datum/category_item/player_setup_item/general/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata)
/datum/category_item/player_setup_item/loadout/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata)
var/list/metadata = pref.all_underwear_metadata[underwear_category]
metadata["[gt]"] = new_metadata
/datum/category_item/player_setup_item/loadout/equipment/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
/datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["change_headset"])
var/new_headset = tgui_input_list(user, "Choose your character's style of headset:", "Character Preference", GLOB.headsetlist, GLOB.headsetlist[pref.headset])
if(!isnull(new_headset) && CanUseTopic(user))
pref.headset = GLOB.headsetlist.Find(new_headset)
var/mob/user = ui.user
switch(action)
if("change_headset")
// Takes the JS index
var/new_headset = text2num(params["headset"]) + 1
if(LAZYACCESS(GLOB.headsetlist, new_headset))
pref.headset = new_headset
return TOPIC_REFRESH_UPDATE_PREVIEW
if("change_backpack")
// Takes the JS index
var/new_backbag = text2num(params["backbag"]) + 1
if(LAZYACCESS(backbaglist, new_backbag))
pref.backbag = new_backbag
return TOPIC_REFRESH_UPDATE_PREVIEW
if("change_pda")
// Takes the JS index
var/new_pdachoice = text2num(params["pda"]) + 1
if(LAZYACCESS(backbaglist, new_pdachoice))
pref.pdachoice = new_pdachoice
return TOPIC_REFRESH_UPDATE_PREVIEW
if("change_underwear")
var/datum/category_group/underwear/UWC = LAZYACCESS(global_underwear.categories_by_name, params["underwear"])
if(!UWC)
return
var/datum/category_item/underwear/selected_underwear = tgui_input_list(user, "Choose underwear:", "Character Preference", UWC.items, pref.all_underwear[UWC.name])
if(selected_underwear)
pref.all_underwear[UWC.name] = selected_underwear.name
return TOPIC_REFRESH_UPDATE_PREVIEW
if(href_list["change_backpack"])
var/new_backbag = tgui_input_list(user, "Choose your character's style of bag:", "Character Preference", backbaglist, backbaglist[pref.backbag])
if(!isnull(new_backbag) && CanUseTopic(user))
pref.backbag = backbaglist.Find(new_backbag)
return TOPIC_REFRESH_UPDATE_PREVIEW
if("underwear_tweak")
var/underwear = params["underwear"]
if(!(underwear in pref.all_underwear))
return TOPIC_NOACTION
var/datum/gear_tweak/gt = locate(params["tweak"])
if(!gt)
return TOPIC_NOACTION
var/new_metadata = gt.get_metadata(user, get_metadata(underwear, gt))
if(new_metadata)
set_metadata(underwear, gt, new_metadata)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["change_pda"])
var/new_pdachoice = tgui_input_list(user, "Choose your character's style of PDA:", "Character Preference", pdachoicelist, pdachoicelist[pref.pdachoice])
if(!isnull(new_pdachoice) && CanUseTopic(user))
pref.pdachoice = pdachoicelist.Find(new_pdachoice)
return TOPIC_REFRESH
else if(href_list["change_underwear"])
var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]]
if(!UWC)
return
var/datum/category_item/underwear/selected_underwear = tgui_input_list(user, "Choose underwear:", "Character Preference", UWC.items, pref.all_underwear[UWC.name])
if(selected_underwear && CanUseTopic(user))
pref.all_underwear[UWC.name] = selected_underwear.name
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["underwear"] && href_list["tweak"])
var/underwear = href_list["underwear"]
if(!(underwear in pref.all_underwear))
return TOPIC_NOACTION
var/datum/gear_tweak/gt = locate(href_list["tweak"])
if(!gt)
return TOPIC_NOACTION
var/new_metadata = gt.get_metadata(user, get_metadata(underwear, gt))
if(new_metadata)
set_metadata(underwear, gt, new_metadata)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["toggle_comm_visibility"])
if(CanUseTopic(user))
if("toggle_comm_visibility")
pref.communicator_visibility = !pref.communicator_visibility
return TOPIC_REFRESH
else if(href_list["set_ringtone"])
var/choice = tgui_input_list(user, "Please select a ringtone. All of these choices come with an associated preset sound. Alternately, select \"Other\" to specify manually.", "Character Preference", valid_ringtones + "Other", pref.ringtone)
if(!choice || !CanUseTopic(user))
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = sanitize(tgui_input_text(user, "Please enter a custom ringtone. If this doesn't match any of the other listed choices, your PDA will use the default (\"beep\") sound.", "Character Preference", null, 20), 20)
if(raw_choice && CanUseTopic(user))
pref.ringtone = raw_choice
else
pref.ringtone = choice
return TOPIC_REFRESH
else if(href_list["toggle_shoes"])
if(CanUseTopic(user))
pref.shoe_hater = !pref.shoe_hater
return TOPIC_REFRESH
else if(href_list["toggle_jacket"])
if(CanUseTopic(user))
pref.no_jacket = !pref.no_jacket
if("set_ringtone")
var/choice = tgui_input_list(user, "Please select a ringtone. All of these choices come with an associated preset sound. Alternately, select \"Other\" to specify manually.", "Character Preference", valid_ringtones + "Other", pref.ringtone)
if(!choice)
return TOPIC_NOACTION
if(choice == "Other")
var/raw_choice = sanitize(tgui_input_text(user, "Please enter a custom ringtone. If this doesn't match any of the other listed choices, your PDA will use the default (\"beep\") sound.", "Character Preference", null, 20), 20)
if(raw_choice)
pref.ringtone = raw_choice
else
pref.ringtone = choice
return TOPIC_REFRESH
return ..()
if("toggle_shoes")
pref.shoe_hater = !pref.shoe_hater
return TOPIC_REFRESH
if("toggle_jacket")
pref.no_jacket = !pref.no_jacket
return TOPIC_REFRESH
@@ -0,0 +1,295 @@
var/list/loadout_categories = list()
var/list/gear_datums = list()
/datum/loadout_category
var/category = ""
var/list/gear = list()
/datum/loadout_category/New(var/cat)
category = cat
..()
/hook/startup/proc/populate_gear_list()
//create a list of gear datums to sort
for(var/datum/gear/G as anything in subtypesof(/datum/gear))
if(initial(G.type_category) == G)
continue
var/use_name = initial(G.display_name)
var/use_category = initial(G.sort_category)
if(!use_name)
error("Loadout - Missing display name: [G]")
continue
if(isnull(initial(G.cost)))
error("Loadout - Missing cost: [G]")
continue
if(!initial(G.path))
error("Loadout - Missing path definition: [G]")
continue
if(!loadout_categories[use_category])
loadout_categories[use_category] = new /datum/loadout_category(use_category)
var/datum/loadout_category/LC = loadout_categories[use_category]
gear_datums[use_name] = new G
LC.gear[use_name] = gear_datums[use_name]
loadout_categories = sortAssoc(loadout_categories)
for(var/loadout_category in loadout_categories)
var/datum/loadout_category/LC = loadout_categories[loadout_category]
LC.gear = sortAssoc(LC.gear)
return 1
/datum/category_item/player_setup_item/loadout/loadout
name = "Loadout"
sort_order = 2
var/current_tab = "General"
/datum/category_item/player_setup_item/loadout/loadout/load_character(list/save_data)
pref.gear_list = list()
var/all_gear = check_list_copy(save_data["gear_list"])
for(var/i in all_gear)
var/list/entries = check_list_copy(all_gear["[i]"])
for(var/j in entries)
entries["[j]"] = path2text_list(entries["[j]"])
pref.gear_list["[i]"] = entries
pref.gear_slot = save_data["gear_slot"]
/datum/category_item/player_setup_item/loadout/loadout/save_character(list/save_data)
var/list/all_gear = list()
var/worn_gear = check_list_copy(pref.gear_list)
for(var/i in worn_gear)
var/list/entries = check_list_copy(worn_gear["[i]"])
if(!length(entries))
entries = check_list_copy(worn_gear[i])
for(var/j in entries)
entries["[j]"] = check_list_copy(entries["[j]"])
all_gear["[i]"] = entries
save_data["gear_list"] = all_gear
save_data["gear_slot"] = pref.gear_slot
/datum/category_item/player_setup_item/loadout/loadout/proc/is_valid_gear(datum/gear/G, max_cost)
if(G.whitelisted && CONFIG_GET(flag/loadout_whitelist) != LOADOUT_WHITELIST_OFF && pref.client) //VOREStation Edit.
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_STRICT && G.whitelisted != pref.species)
return FALSE
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_LAX && !is_alien_whitelisted(pref.client, GLOB.all_species[G.whitelisted]))
return FALSE
if(max_cost && G.cost > max_cost)
return FALSE
if(pref.client)
if(G.ckeywhitelist && !(pref.client_ckey in G.ckeywhitelist))
return FALSE
if(G.character_name && !(pref.real_name in G.character_name))
return FALSE
return TRUE
/datum/category_item/player_setup_item/loadout/loadout/sanitize_character()
var/mob/preference_mob = preference_mob()
if(LAZYLEN(pref.gear_list) < 0)
return
if(pref.gear_slot > LAZYLEN(pref.gear_list))
pref.gear_slot = 1
var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]")
var/total_cost = 0
for(var/gear_name in active_gear_list)
if(!gear_datums[gear_name])
to_chat(preference_mob, span_warning("You cannot have the \the [gear_name]."))
active_gear_list -= gear_name
continue
var/datum/gear/G = gear_datums[gear_name]
if(!is_valid_gear(G))
to_chat(preference_mob, span_warning("You cannot take \the [gear_name] as you are not whitelisted for the species or item."))
active_gear_list -= gear_name
continue
if(total_cost + G.cost > MAX_GEAR_COST)
active_gear_list -= gear_name
to_chat(preference_mob, span_warning("You cannot afford to take \the [gear_name]"))
continue
total_cost += G.cost
/datum/category_item/player_setup_item/loadout/loadout/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["total_cost"] = get_total()
data["gear_slot"] = pref.gear_slot
var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]")
data["active_gear_list"] = active_gear_list
var/list/gear_tweaks = list()
for(var/item in active_gear_list)
var/datum/gear/G = gear_datums[item]
var/list/tweaks = list()
for(var/datum/gear_tweak/tweak in G.gear_tweaks)
UNTYPED_LIST_ADD(tweaks, list(
"ref" = REF(tweak),
"contents" = tweak.get_contents(get_tweak_metadata(G, tweak))
))
gear_tweaks[G.display_name] = tweaks
data["gear_tweaks"] = gear_tweaks
return data
/datum/category_item/player_setup_item/loadout/loadout/tgui_static_data(mob/user)
var/list/data = ..()
var/list/categories = list()
for(var/category in loadout_categories)
var/datum/loadout_category/LC = loadout_categories[category]
var/list/items = list()
for(var/gear in LC.gear)
var/datum/gear/G = LC.gear[gear]
if(!is_valid_gear(G))
continue
UNTYPED_LIST_ADD(items, list(
"name" = G.display_name,
"desc" = G.description,
"cost" = G.cost,
"show_roles" = G.show_roles,
"allowed_roles" = G.allowed_roles
))
categories[category] = items
data["categories"] = categories
data["max_gear_cost"] = MAX_GEAR_COST
return data
/datum/category_item/player_setup_item/loadout/loadout/proc/get_gear_metadata(var/datum/gear/G)
var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]")
// {"/datum/gear_tweak/custom_name": "" }
. = LAZYACCESS(active_gear_list, G.display_name)
if(!.)
. = list()
/datum/category_item/player_setup_item/loadout/loadout/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak)
var/list/metadata = get_gear_metadata(G)
. = metadata["[tweak]"]
if(!.)
. = tweak.get_default()
metadata["[tweak]"] = .
/datum/category_item/player_setup_item/loadout/loadout/proc/set_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak, var/new_metadata)
var/list/metadata = get_gear_metadata(G)
metadata["[tweak]"] = new_metadata
/datum/category_item/player_setup_item/loadout/loadout/proc/get_total()
var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]")
. = 0
for(var/gear_name in active_gear_list)
var/datum/gear/G = gear_datums[gear_name]
if(G)
. += G.cost
/datum/category_item/player_setup_item/loadout/loadout/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/list/active_gear_list = LAZYACCESS(pref.gear_list, "[pref.gear_slot]")
var/mob/user = ui.user
switch(action)
if("next_slot")
pref.gear_slot = pref.gear_slot + 1
if(pref.gear_slot > CONFIG_GET(number/loadout_slots))
pref.gear_slot = 1
if(!("[pref.gear_slot]" in pref.gear_list))
pref.gear_list["[pref.gear_slot]"] = list()
return TOPIC_REFRESH_UPDATE_PREVIEW
if("prev_slot")
pref.gear_slot = pref.gear_slot - 1
if(pref.gear_slot <= 0)
pref.gear_slot = CONFIG_GET(number/loadout_slots)
if(!("[pref.gear_slot]" in pref.gear_list))
pref.gear_list["[pref.gear_slot]"] = list()
return TOPIC_REFRESH_UPDATE_PREVIEW
if("clear_loadout")
active_gear_list.Cut()
return TOPIC_REFRESH_UPDATE_PREVIEW
if("copy_loadout")
var/copy_to = tgui_input_number(user, "What slot would you like to copy slot [pref.gear_slot] to?", "Copy Slot", 1, CONFIG_GET(number/loadout_slots), 1)
if(!copy_to)
return TOPIC_HANDLED
if("[copy_to]" in pref.gear_list)
var/confirm = tgui_alert(user, "This will overwrite slot [copy_to], are you sure?", "Are you sure?", list("No", "Yes"))
if(confirm != "Yes")
return TOPIC_HANDLED
pref.gear_list["[copy_to]"] = check_list_copy(active_gear_list)
return TOPIC_REFRESH
if("toggle_gear")
var/datum/gear/TG = gear_datums[params["gear"]]
if(TG)
if(TG.display_name in active_gear_list)
active_gear_list -= TG.display_name
else if(get_total() + TG.cost <= MAX_GEAR_COST)
active_gear_list[TG.display_name] = list()
return TOPIC_REFRESH_UPDATE_PREVIEW
if("gear_tweak")
var/datum/gear/gear = gear_datums[params["gear"]]
var/datum/gear_tweak/tweak = locate(params["tweak"])
if(!tweak || !gear || !(tweak in gear.gear_tweaks))
return TOPIC_HANDLED
var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak))
if(!metadata)
return TOPIC_HANDLED
set_tweak_metadata(gear, tweak, metadata)
return TOPIC_REFRESH_UPDATE_PREVIEW
/datum/gear
var/display_name //Name/index. Must be unique.
var/description //Description of this gear. If left blank will default to the description of the pathed item.
var/path //Path to item.
var/cost = 1 //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points.
var/slot //Slot to equip to.
var/list/allowed_roles //Roles that can spawn with this item.
var/show_roles = TRUE //Show the role restrictions on this item?
var/whitelisted //Term to check the whitelist for..
var/sort_category = "General"
var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned.
var/exploitable = 0 //Does it go on the exploitable information list?
var/type_category = null
var/list/ckeywhitelist //restricted based on these ckeys?
var/list/character_name //restricted to these character names?
/datum/gear/New()
..()
if(!description)
var/obj/O = path
description = initial(O.desc)
gear_tweaks = list(gear_tweak_free_name, gear_tweak_free_desc, GLOB.gear_tweak_item_tf_spawn, GLOB.gear_tweak_free_matrix_recolor)
/datum/gear_data
var/path
var/location
/datum/gear_data/New(var/path, var/location)
src.path = path
src.location = location
/datum/gear/proc/spawn_item(var/location, var/metadata)
var/datum/gear_data/gd = new(path, location)
if(length(gear_tweaks) && metadata)
for(var/datum/gear_tweak/gt in gear_tweaks)
gt.tweak_gear_data(metadata["[gt]"], gd)
var/item = new gd.path(gd.location)
if(length(gear_tweaks) && metadata)
for(var/datum/gear_tweak/gt in gear_tweaks)
gt.tweak_item(item, metadata["[gt]"])
var/mob/M = location
if(istype(M) && exploitable) //Update exploitable info records for the mob without creating a duplicate object at their feet.
M.amend_exploitable(item)
return item
@@ -1,304 +0,0 @@
var/list/loadout_categories = list()
var/list/gear_datums = list()
/datum/loadout_category
var/category = ""
var/list/gear = list()
/datum/loadout_category/New(var/cat)
category = cat
..()
/hook/startup/proc/populate_gear_list()
//create a list of gear datums to sort
for(var/datum/gear/G as anything in subtypesof(/datum/gear))
if(initial(G.type_category) == G)
continue
var/use_name = initial(G.display_name)
var/use_category = initial(G.sort_category)
if(!use_name)
error("Loadout - Missing display name: [G]")
continue
if(isnull(initial(G.cost)))
error("Loadout - Missing cost: [G]")
continue
if(!initial(G.path))
error("Loadout - Missing path definition: [G]")
continue
if(!loadout_categories[use_category])
loadout_categories[use_category] = new /datum/loadout_category(use_category)
var/datum/loadout_category/LC = loadout_categories[use_category]
gear_datums[use_name] = new G
LC.gear[use_name] = gear_datums[use_name]
loadout_categories = sortAssoc(loadout_categories)
for(var/loadout_category in loadout_categories)
var/datum/loadout_category/LC = loadout_categories[loadout_category]
LC.gear = sortAssoc(LC.gear)
return 1
/datum/category_item/player_setup_item/loadout
name = "Loadout"
sort_order = 1
var/current_tab = "General"
/datum/category_item/player_setup_item/loadout/load_character(list/save_data)
pref.gear_list = list()
var/all_gear = check_list_copy(save_data["gear_list"])
for(var/i in all_gear)
var/list/entries = check_list_copy(all_gear["[i]"])
for(var/j in entries)
entries["[j]"] = path2text_list(entries["[j]"])
pref.gear_list["[i]"] = entries
pref.gear_slot = save_data["gear_slot"]
if(pref.gear_list!=null && pref.gear_slot!=null)
pref.gear = pref.gear_list["[pref.gear_slot]"]
else
pref.gear = check_list_copy(save_data["gear"])
pref.gear_slot = 1
/datum/category_item/player_setup_item/loadout/save_character(list/save_data)
pref.gear_list["[pref.gear_slot]"] = pref.gear
var/list/all_gear = list()
var/worn_gear = check_list_copy(pref.gear_list)
for(var/i in worn_gear)
var/list/entries = check_list_copy(worn_gear["[i]"])
if(!length(entries))
entries = check_list_copy(worn_gear[i])
for(var/j in entries)
entries["[j]"] = check_list_copy(entries["[j]"])
all_gear["[i]"] = entries
save_data["gear_list"] = all_gear
save_data["gear_slot"] = pref.gear_slot
/datum/category_item/player_setup_item/loadout/proc/valid_gear_choices(var/max_cost)
. = list()
for(var/gear_name in gear_datums)
var/datum/gear/G = gear_datums[gear_name]
if(G.whitelisted && CONFIG_GET(flag/loadout_whitelist) != LOADOUT_WHITELIST_OFF && pref.client) //VOREStation Edit.
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_STRICT && G.whitelisted != pref.species)
continue
if(CONFIG_GET(flag/loadout_whitelist) == LOADOUT_WHITELIST_LAX && !is_alien_whitelisted(pref.client, GLOB.all_species[G.whitelisted]))
continue
if(max_cost && G.cost > max_cost)
continue
if(pref.client)
if(G.ckeywhitelist && !(pref.client_ckey in G.ckeywhitelist))
continue
if(G.character_name && pref.client.prefs && !(pref.client.prefs.real_name in G.character_name))
continue
. += gear_name
/datum/category_item/player_setup_item/loadout/sanitize_character()
var/mob/preference_mob = preference_mob()
if(!islist(pref.gear))
pref.gear = list()
if(!islist(pref.gear_list))
pref.gear_list = list()
for(var/gear_name in pref.gear)
if(!(gear_name in gear_datums))
pref.gear -= gear_name
var/total_cost = 0
for(var/gear_name in pref.gear)
if(!gear_datums[gear_name])
to_chat(preference_mob, span_warning("You cannot have more than one of the \the [gear_name]"))
pref.gear -= gear_name
else if(!(gear_name in valid_gear_choices()))
to_chat(preference_mob, span_warning("You cannot take \the [gear_name] as you are not whitelisted for the species or item.")) //Vorestation Edit
pref.gear -= gear_name
else
var/datum/gear/G = gear_datums[gear_name]
if(total_cost + G.cost > MAX_GEAR_COST)
pref.gear -= gear_name
to_chat(preference_mob, span_warning("You cannot afford to take \the [gear_name]"))
else
total_cost += G.cost
/datum/category_item/player_setup_item/loadout/content()
. = list()
var/mob/preference_mob = preference_mob() //Vorestation Edit
var/total_cost = 0
if(pref.gear && pref.gear.len)
for(var/i = 1; i <= pref.gear.len; i++)
var/datum/gear/G = gear_datums[pref.gear[i]]
if(G)
total_cost += G.cost
var/fcolor = "#3366CC"
if(total_cost < MAX_GEAR_COST)
fcolor = "#E67300"
. += "<table align = 'center' width = 100%>"
. += "<tr><td colspan=3><center><a href='byond://?src=\ref[src];prev_slot=1'>\<\<</a>" + span_bold("<font color = '[fcolor]'>\[[pref.gear_slot]\]</font> ") + "<a href='byond://?src=\ref[src];next_slot=1'>\>\></a>" + span_bold("<font color = '[fcolor]'>[total_cost]/[MAX_GEAR_COST]</font> loadout points spent.") + " \[<a href='byond://?src=\ref[src];clear_loadout=1'>Clear Loadout</a>\]</center></td></tr>"
. += "<tr><td colspan=3><center><b>"
var/firstcat = 1
for(var/category in loadout_categories)
if(firstcat)
firstcat = 0
else
. += " |"
var/datum/loadout_category/LC = loadout_categories[category]
var/category_cost = 0
for(var/gear in LC.gear)
if(gear in pref.gear)
var/datum/gear/G = LC.gear[gear]
category_cost += G.cost
if(category == current_tab)
. += " " + span_linkOn("[category] - [category_cost]") + " "
else
if(category_cost)
. += " <a href='byond://?src=\ref[src];select_category=[category]'><font color = '#E67300'>[category] - [category_cost]</font></a> "
else
. += " <a href='byond://?src=\ref[src];select_category=[category]'>[category] - 0</a> "
. += "</b></center></td></tr>"
var/datum/loadout_category/LC = loadout_categories[current_tab]
. += "<tr><td colspan=3><hr></td></tr>"
. += "<tr><td colspan=3>" + span_bold("<center>[LC.category]</center>") + "</td></tr>"
. += "<tr><td colspan=3><hr></td></tr>"
for(var/gear_name in LC.gear)
var/datum/gear/G = LC.gear[gear_name]
if(preference_mob && preference_mob.client)
if(G.ckeywhitelist && !(preference_mob.ckey in G.ckeywhitelist))
continue
if(G.character_name && preference_mob.client.prefs && !(preference_mob.client.prefs.real_name in G.character_name))
continue
var/ticked = (G.display_name in pref.gear)
. += "<tr style='vertical-align:top;'><td width=25%><a style='white-space:normal;' [ticked ? "class='linkOn' " : ""]href='byond://?src=\ref[src];toggle_gear=[html_encode(G.display_name)]'>[G.display_name]</a></td>"
. += "<td width = 10% style='vertical-align:top'>[G.cost]</td>"
. += "<td>" + span_normal(span_italics("[G.description]")) + "</td></tr>"
if(G.show_roles && G.allowed_roles)
. += "<td colspan=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Restricted to: [list2text(G.allowed_roles,", ")]</td>"
if(ticked)
. += "<tr><td colspan=3>"
for(var/datum/gear_tweak/tweak in G.gear_tweaks)
. += " <a href='byond://?src=\ref[src];gear=[url_encode(G.display_name)];tweak=\ref[tweak]'>[tweak.get_contents(get_tweak_metadata(G, tweak))]</a>"
. += "</td></tr>"
. += "</table>"
. = jointext(., null)
/datum/category_item/player_setup_item/loadout/proc/get_gear_metadata(var/datum/gear/G)
. = pref.gear[G.display_name]
if(!.)
. = list()
pref.gear[G.display_name] = .
/datum/category_item/player_setup_item/loadout/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak)
var/list/metadata = get_gear_metadata(G)
. = metadata["[tweak]"]
if(!.)
. = tweak.get_default()
metadata["[tweak]"] = .
/datum/category_item/player_setup_item/loadout/proc/set_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak, var/new_metadata)
var/list/metadata = get_gear_metadata(G)
metadata["[tweak]"] = new_metadata
/datum/category_item/player_setup_item/loadout/OnTopic(href, href_list, user)
if(href_list["toggle_gear"])
var/datum/gear/TG = gear_datums[href_list["toggle_gear"]]
if(TG.display_name in pref.gear)
pref.gear -= TG.display_name
else
var/total_cost = 0
for(var/gear_name in pref.gear)
var/datum/gear/G = gear_datums[gear_name]
if(istype(G)) total_cost += G.cost
if((total_cost+TG.cost) <= MAX_GEAR_COST)
pref.gear += TG.display_name
return TOPIC_REFRESH_UPDATE_PREVIEW
if(href_list["gear"] && href_list["tweak"])
var/datum/gear/gear = gear_datums[url_decode(href_list["gear"])]
var/datum/gear_tweak/tweak = locate(href_list["tweak"])
if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks))
return TOPIC_NOACTION
var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak))
if(!metadata || !CanUseTopic(user))
return TOPIC_NOACTION
set_tweak_metadata(gear, tweak, metadata)
return TOPIC_REFRESH_UPDATE_PREVIEW
if(href_list["next_slot"] || href_list["prev_slot"])
//Set the current slot in the gear list to the currently selected gear
pref.gear_list["[pref.gear_slot]"] = pref.gear
//If we're moving up a slot..
if(href_list["next_slot"])
//change the current slot number
pref.gear_slot = pref.gear_slot+1
if(pref.gear_slot > CONFIG_GET(number/loadout_slots))
pref.gear_slot = 1
//If we're moving down a slot..
else if(href_list["prev_slot"])
//change current slot one down
pref.gear_slot = pref.gear_slot-1
if(pref.gear_slot<1)
pref.gear_slot = CONFIG_GET(number/loadout_slots)
// Set the currently selected gear to whatever's in the new slot
if(pref.gear_list["[pref.gear_slot]"])
pref.gear = pref.gear_list["[pref.gear_slot]"]
else
pref.gear = list()
pref.gear_list["[pref.gear_slot]"] = list()
// Refresh?
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["select_category"])
current_tab = href_list["select_category"]
return TOPIC_REFRESH
else if(href_list["clear_loadout"])
pref.gear.Cut()
return TOPIC_REFRESH_UPDATE_PREVIEW
return ..()
/datum/gear
var/display_name //Name/index. Must be unique.
var/description //Description of this gear. If left blank will default to the description of the pathed item.
var/path //Path to item.
var/cost = 1 //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points.
var/slot //Slot to equip to.
var/list/allowed_roles //Roles that can spawn with this item.
var/show_roles = TRUE //Show the role restrictions on this item?
var/whitelisted //Term to check the whitelist for..
var/sort_category = "General"
var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned.
var/exploitable = 0 //Does it go on the exploitable information list?
var/type_category = null
var/list/ckeywhitelist //restricted based on these ckeys?
var/list/character_name //restricted to these character names?
/datum/gear/New()
..()
if(!description)
var/obj/O = path
description = initial(O.desc)
gear_tweaks = list(gear_tweak_free_name, gear_tweak_free_desc, GLOB.gear_tweak_item_tf_spawn, GLOB.gear_tweak_free_matrix_recolor)
/datum/gear_data
var/path
var/location
/datum/gear_data/New(var/path, var/location)
src.path = path
src.location = location
/datum/gear/proc/spawn_item(var/location, var/metadata)
var/datum/gear_data/gd = new(path, location)
if(length(gear_tweaks) && metadata)
for(var/datum/gear_tweak/gt in gear_tweaks)
gt.tweak_gear_data(metadata["[gt]"], gd)
var/item = new gd.path(gd.location)
if(length(gear_tweaks) && metadata)
for(var/datum/gear_tweak/gt in gear_tweaks)
gt.tweak_item(item, metadata["[gt]"])
var/mob/M = location
if(istype(M) && exploitable) //Update exploitable info records for the mob without creating a duplicate object at their feet.
M.amend_exploitable(item)
return item
@@ -64,26 +64,11 @@
if(alt_title && !(alt_title in job.alt_titles))
pref.player_alt_titles -= job.title
/datum/category_item/player_setup_item/occupation/content(mob/user, limit = 25, list/splitJobs = list())
if(!job_master)
return
. = list()
. += "<tt><center>"
. += span_bold("Choose occupation chances") + "<br>Unavailable occupations are crossed out.<br>"
. += "<script type='text/javascript'>function setJobPrefRedirect(level, rank) { window.location.href='byond://?src=\ref[src];level=' + level + ';set_job=' + encodeURIComponent(rank); return false; }</script>"
. += "<table width='100%' cellpadding='1' cellspacing='0'><tr><td width='20%' valign='top'>" // Table within a table for alignment, also allows you to easily add more columns.
. += "<table width='100%' cellpadding='1' cellspacing='0'>"
var/index = -1
//The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows.
var/datum/job/lastJob
var/datum/department/last_department = null // Used to avoid repeating the look-ahead check for if a whole department can fit.
/datum/category_item/player_setup_item/occupation/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
var/list/jobs_data = list()
var/list/all_valid_jobs = list()
// If the occupation window gets opened before SSJob initializes, then it'll just be blank, with no runtimes.
// It will work once init is finished.
for(var/D in SSjob.department_datums)
var/datum/department/department = SSjob.department_datums[D]
if(department.centcom_only) // No joining as a centcom role, if any are ever added.
@@ -92,194 +77,141 @@
for(var/J in department.primary_jobs)
all_valid_jobs += department.jobs[J]
jobs_data[department.name] = list()
for(var/datum/job/job in all_valid_jobs)
if(job.latejoin_only) continue //VOREStation Code
if(job.latejoin_only)
continue
var/datum/department/current_department = SSjob.get_primary_department_of_job(job)
// for the is_job_whitelisted check..
usr = user
var/list/job_data = list(
"title" = job.title,
"ref" = REF(job),
"selection_color" = job.selection_color,
// reasons you can't select it
"banned" = !!jobban_isbanned(user, job.title),
"denylist_days" = !job.player_old_enough(user.client),
"available_in_days" = !job.available_in_days(user.client),
"denylist_playtime" = !job.player_has_enough_playtime(user.client),
"available_in_hours" = job.available_in_playhours(user.client),
"denylist_whitelist" = !is_job_whitelisted(user, job.title),
// tigercat2000 - these shouldn't exist >:(
"denylist_character_age" = FALSE,
"min_age" = job.get_min_age(pref.species, pref.organ_data[O_BRAIN]),
"special_color" = "",
"selected" = 0,
"selected_title" = "",
"alt_titles" = list(),
)
// Should we add a new column?
var/make_new_column = FALSE
if(++index > limit)
// Ran out of rows, make a new column.
make_new_column = TRUE
else if(job.title in splitJobs)
// Is hardcoded to split at this job title.
make_new_column = TRUE
else if(current_department != last_department)
// If the department is bigger than the limit then we have to split.
if(limit >= current_department.primary_jobs.len)
// Look ahead to see if we would need to split, and if so, avoid it.
if(index + current_department.primary_jobs.len > limit)
// Looked ahead, and determined that a new column is needed to avoid splitting the department into two.
make_new_column = TRUE
if(make_new_column)
/*******
if((index < limit) && (lastJob != null))
//If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with
//the last job's selection color and blank buttons that do nothing. Creating a rather nice effect.
for(var/i = 0, i < (limit - index), i++)
. += "<tr bgcolor='[lastJob.selection_color]'><td width='60%' align='right'>//>&nbsp</a></td><td><a>&nbsp</a></td></tr>"
*******/
. += "</table></td><td width='20%' valign='top'><table width='100%' cellpadding='1' cellspacing='0'>"
index = 0
last_department = current_department
. += "<tr bgcolor='[job.selection_color]'><td width='60%' align='right'>"
var/rank = job.title
lastJob = job
. += "<a href='byond://?src=\ref[src];job_info=[rank]'>"
if(jobban_isbanned(user, rank))
. += "<del>[rank]</del></td></a><td>" + span_bold(" \[BANNED]") + "</td></tr>"
continue
if(!job.player_old_enough(user.client))
var/available_in_days = job.available_in_days(user.client)
. += "<del>[rank]</del></td></a><td> \[IN [(available_in_days)] DAYS]</td></tr>"
continue
//VOREStation Add
if(!job.player_has_enough_playtime(user.client))
var/available_in_hours = job.available_in_playhours(user.client)
. += "<del>[rank]</del></td></a><td> \[IN [round(available_in_hours, 0.1)] DEPTHOURS]</td></tr>"
continue
if(!is_job_whitelisted(user,rank))
. += "<del>[rank]</del></td></a><td>" + span_bold(" \[WHITELIST ONLY]") + "</td></tr>"
continue
//VOREStation Add End
if(job.is_species_banned(user.client.prefs.species, user.client.prefs.organ_data[O_BRAIN]) == TRUE)
. += "<del>[rank]</del></td></a><td> \[THIS RACE/BRAIN TYPE CANNOT TAKE THIS ROLE.\]</td></tr>"
continue
if((job.minimum_character_age || job.min_age_by_species) && user.client && (user.read_preference(/datum/preference/numeric/human/age) < job.get_min_age(user.client.prefs.species, user.client.prefs.organ_data[O_BRAIN])))
. += "<del>[rank]</del></td></a><td> \[MINIMUM CHARACTER AGE FOR SELECTED RACE/BRAIN TYPE: [job.get_min_age(user.client.prefs.species, user.client.prefs.organ_data[O_BRAIN])]\]</td></tr>"
continue
job_data["denylist_character_age"] = TRUE
if((pref.job_civilian_low & ASSISTANT) && job.type != /datum/job/assistant)
. += span_gray("[rank]") + "</a></td><td></td></tr>"
continue
if((rank in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND) ) || (rank == JOB_AI))//Bold head jobs
. += span_bold("[rank]") + "</a>"
else
. += "[rank]</a>"
. += "</td><td width='40%'>"
var/prefLevelLabel = "ERROR"
var/prefLevelColor = "pink"
var/prefUpperLevel = -1 // level to assign on left click
var/prefLowerLevel = -1 // level to assign on right click
if(pref.GetJobDepartment(job, 1) & job.flag)
prefLevelLabel = "High"
prefLevelColor = "55cc55"
prefUpperLevel = 4
prefLowerLevel = 2
else if(pref.GetJobDepartment(job, 2) & job.flag)
prefLevelLabel = "Medium"
prefLevelColor = "eecc22"
prefUpperLevel = 1
prefLowerLevel = 3
else if(pref.GetJobDepartment(job, 3) & job.flag)
prefLevelLabel = "Low"
prefLevelColor = "cc5555"
prefUpperLevel = 2
prefLowerLevel = 4
else
prefLevelLabel = "NEVER"
prefLevelColor = "black"
prefUpperLevel = 3
prefLowerLevel = 1
. += "<a href='byond://?src=\ref[src];set_job=[rank];level=[prefUpperLevel]' oncontextmenu='javascript:return setJobPrefRedirect([prefLowerLevel], \"[rank]\");'>"
job_data["special_color"] = "gray"
if((job.title in SSjob.get_job_titles_in_department(DEPARTMENT_COMMAND) ) || (job.title == JOB_AI))//Bold head jobs
job_data["special_color"] = "bold"
if(job.type == /datum/job/assistant)//Assistant is special
if(pref.job_civilian_low & ASSISTANT)
. += " <font color=55cc55>\[Yes]</font>"
job_data["selected"] = 4
else
. += " " + span_black("\[No]")
if(LAZYLEN(job.alt_titles)) //Blatantly cloned from a few lines down.
. += "</a></td></tr><tr bgcolor='[lastJob.selection_color]'><td width='60%' align='center'>&nbsp</td><td><a href='byond://?src=\ref[src];select_alt_title=\ref[job]'>\[[pref.GetPlayerAltTitle(job)]\]</a></td></tr>"
. += "</a></td></tr>"
continue
job_data["selected"] = 0
else if(pref.GetJobDepartment(job, 1) & job.flag)
job_data["selected"] = 3
else if(pref.GetJobDepartment(job, 2) & job.flag)
job_data["selected"] = 2
else if(pref.GetJobDepartment(job, 3) & job.flag)
job_data["selected"] = 1
else
job_data["selected"] = 0
. += " <font color=[prefLevelColor]>\[[prefLevelLabel]]</font>"
if(LAZYLEN(job.alt_titles))
. += "</a></td></tr><tr bgcolor='[lastJob.selection_color]'><td width='60%' align='center'>&nbsp</td><td><a href='byond://?src=\ref[src];select_alt_title=\ref[job]'>\[[pref.GetPlayerAltTitle(job)]\]</a></td></tr>"
. += "</a></td></tr>"
. += "</td'></tr></table>"
. += "</center></table><center>"
job_data["selected_title"] = pref.GetPlayerAltTitle(job)
for(var/title in job.alt_titles)
job_data["alt_titles"] += title
switch(pref.alternate_option)
if(GET_RANDOM_JOB)
. += span_underline("<a href='byond://?src=\ref[src];job_alternative=1'>Get random job if preferences unavailable</a>")
if(BE_ASSISTANT)
. += span_underline("<a href='byond://?src=\ref[src];job_alternative=1'>Be assistant if preference unavailable</a>")
if(RETURN_TO_LOBBY)
. += span_underline("<a href='byond://?src=\ref[src];job_alternative=1'>Return to lobby if preference unavailable</a>")
UNTYPED_LIST_ADD(jobs_data[current_department.name], job_data)
. += "<a href='byond://?src=\ref[src];reset_jobs=1'>\[Reset\]</a></center>"
. += "</tt>"
. = jointext(.,null)
data["jobs"] = jobs_data
data["alternate_option"] = pref.alternate_option
return data
/datum/category_item/player_setup_item/occupation/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
switch(action)
if("reset_jobs")
ResetJobs()
return TOPIC_REFRESH_UPDATE_PREVIEW
if("job_alternative")
if(pref.alternate_option == GET_RANDOM_JOB || pref.alternate_option == BE_ASSISTANT)
pref.alternate_option += 1
else if(pref.alternate_option == RETURN_TO_LOBBY)
pref.alternate_option = 0
return TOPIC_REFRESH
if("select_alt_title")
var/datum/job/J = locate(params["job"])
if(istype(J))
var/choices = list(J.title) + J.alt_titles
var/choice = tgui_input_list(user, "Choose a title for [J.title].", "Choose Title", choices, pref.GetPlayerAltTitle(J))
if(choice)
SetPlayerAltTitle(J, choice)
return TOPIC_REFRESH_UPDATE_PREVIEW
if("set_job")
if(SetJob(user, params["set_job"], text2num(params["level"])))
return TOPIC_REFRESH_UPDATE_PREVIEW
return TOPIC_HANDLED
if("job_info")
var/rank = params["rank"]
var/datum/job/job = job_master.GetJob(rank)
if(!istype(job))
return TOPIC_NOACTION
var/dat = list()
dat += "<p style='background-color: [job.selection_color]'><br><br><p>"
if(job.alt_titles)
dat += span_italics(span_bold("Alternate titles:") + " [english_list(job.alt_titles)].")
send_rsc(user, job.get_job_icon(), "job[ckey(rank)].png")
dat += "<img src=job[ckey(rank)].png width=96 height=96 style='float:left;'>"
if(job.departments)
dat += span_bold("Departments:") + " [english_list(job.departments)]."
if(LAZYLEN(job.departments_managed))
dat += "You manage these departments: [english_list(job.departments_managed)]"
dat += "You answer to " + span_bold("[job.supervisors]") + " normally."
dat += "<hr style='clear:left;'>"
if(CONFIG_GET(string/wikiurl))
dat += "<a href='byond://?src=\ref[src];job_wiki=[rank]'>Open wiki page in browser</a>"
var/alt_title = pref.GetPlayerAltTitle(job)
var/list/description = job.get_description_blurb(alt_title)
if(LAZYLEN(description))
dat += html_encode(description[1])
if(description.len > 1)
if(!isnull(description[2]))
dat += "<br>"
dat += html_encode(description[2])
var/datum/browser/popup = new(user, "Job Info", "[capitalize(rank)]", 430, 520, src)
popup.set_content(jointext(dat,"<br>"))
popup.open()
return TOPIC_HANDLED
// must stay for job popup to work
/datum/category_item/player_setup_item/occupation/OnTopic(href, href_list, user)
if(href_list["reset_jobs"])
ResetJobs()
return TOPIC_REFRESH
else if(href_list["job_alternative"])
if(pref.alternate_option == GET_RANDOM_JOB || pref.alternate_option == BE_ASSISTANT)
pref.alternate_option += 1
else if(pref.alternate_option == RETURN_TO_LOBBY)
pref.alternate_option = 0
return TOPIC_REFRESH
else if(href_list["select_alt_title"])
var/datum/job/job = locate(href_list["select_alt_title"])
if (job)
var/choices = list(job.title) + job.alt_titles
var/choice = tgui_input_list(user, "Choose a title for [job.title].", "Choose Title", choices, pref.GetPlayerAltTitle(job))
if(choice && CanUseTopic(user))
SetPlayerAltTitle(job, choice)
return (pref.equip_preview_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH)
else if(href_list["set_job"])
if(SetJob(user, href_list["set_job"], text2num(href_list["level"])))
return (pref.equip_preview_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH)
else if(href_list["job_info"])
var/rank = href_list["job_info"]
var/datum/job/job = job_master.GetJob(rank)
var/dat = list()
dat += "<p style='background-color: [job.selection_color]'><br><br><p>"
if(job.alt_titles)
dat += span_italics(span_bold("Alternate titles:") + " [english_list(job.alt_titles)].")
send_rsc(user, job.get_job_icon(), "job[ckey(rank)].png")
dat += "<img src=job[ckey(rank)].png width=96 height=96 style='float:left;'>"
if(job.departments)
dat += span_bold("Departments:") + " [english_list(job.departments)]."
if(LAZYLEN(job.departments_managed))
dat += "You manage these departments: [english_list(job.departments_managed)]"
dat += "You answer to " + span_bold("[job.supervisors]") + " normally."
dat += "<hr style='clear:left;'>"
if(CONFIG_GET(string/wikiurl))
dat += "<a href='byond://?src=\ref[src];job_wiki=[rank]'>Open wiki page in browser</a>"
var/alt_title = pref.GetPlayerAltTitle(job)
var/list/description = job.get_description_blurb(alt_title)
if(LAZYLEN(description))
dat += html_encode(description[1])
if(description.len > 1)
if(!isnull(description[2]))
dat += "<br>"
dat += html_encode(description[2])
var/datum/browser/popup = new(user, "Job Info", "[capitalize(rank)]", 430, 520, src)
popup.set_content(jointext(dat,"<br>"))
popup.open()
else if(href_list["job_wiki"])
if(href_list["job_wiki"])
var/rank = href_list["job_wiki"]
open_link(user,"[CONFIG_GET(string/wikiurl)][rank]")
@@ -12,11 +12,6 @@
sort_order = 3
category_item_type = /datum/category_item/player_setup_item/occupation
/datum/category_group/player_setup_category/appearance_preferences
name = "Special Roles"
sort_order = 4
category_item_type = /datum/category_item/player_setup_item/antagonism
/datum/category_group/player_setup_category/loadout_preferences
name = "Loadout"
sort_order = 5
@@ -86,10 +81,6 @@
dat += "<a href='byond://?src=\ref[src];game_prefs=1'>Game Options</a>"
return dat
/datum/category_collection/player_setup_collection/proc/content(var/mob/user)
if(selected_category)
return selected_category.content(user)
/datum/category_collection/player_setup_collection/Topic(var/href,var/list/href_list)
if(..())
return 1
@@ -104,7 +95,7 @@
. = 1
else if(href_list["game_prefs"])
user.client.prefs.tgui_interact(user)
user.client.game_options()
if(.)
user.client.prefs.ShowChoices(user)
@@ -152,21 +143,6 @@
for(var/datum/category_item/player_setup_item/PI in items)
PI.copy_to_mob(C)
/datum/category_group/player_setup_category/proc/content(var/mob/user)
. = "<table style='width:100%'><tr style='vertical-align:top'><td style='width:50%'>"
var/current = 0
var/halfway = items.len / 2
for(var/datum/category_item/player_setup_item/PI in items)
if(halfway && current++ >= halfway)
halfway = 0
. += "</td><td></td><td style='width:50%'>"
. += "[PI.content(user)]<br>"
. += "</td></tr></table>"
/datum/category_group/player_setup_category/occupation_preferences/content(var/mob/user)
for(var/datum/category_item/player_setup_item/PI in items)
. += "[PI.content(user)]<br>"
/**********************
* Category Item Setup *
**********************/
@@ -216,9 +192,6 @@
/datum/category_item/player_setup_item/proc/copy_to_mob(var/mob/living/carbon/human/C)
return
/datum/category_item/player_setup_item/proc/content()
return
/datum/category_item/player_setup_item/proc/sanitize_character()
return
@@ -1,305 +0,0 @@
// This contains character setup datums for traits.
// The actual modifiers (if used) for these are stored inside code/modules/mob/_modifiers/traits.dm
/datum/trait/modifier
abstract_type = /datum/trait/modifier
var/modifier_type = null // Type to add to the mob post spawn.
/datum/trait/modifier/apply_trait_post_spawn(mob/living/L)
L.add_modifier(modifier_type)
/datum/trait/modifier/is_available()
return !!modifier_type
/datum/trait/modifier/generate_desc()
var/new_desc = desc
if(!is_available())
new_desc = "[new_desc] This trait is not implemented yet."
return new_desc
var/datum/modifier/M = new modifier_type()
if(!desc)
new_desc = M.desc // Use the modifier's description, if the trait doesn't have one defined.
var/modifier_effects = M.describe_modifier_effects()
new_desc = "[new_desc][modifier_effects ? "<br>[modifier_effects]":""]" // Now describe what the trait actually does.
qdel(M)
return new_desc
// Physical traits are what they sound like, and involve the character's physical body, as opposed to their mental state.
/datum/trait/modifier/physical
abstract_type = /datum/trait/modifier/physical
name = "Physical"
category = "Physical"
/datum/trait/modifier/physical/flimsy
name = "Flimsy"
desc = "You're more fragile than most, and have less of an ability to endure harm."
modifier_type = /datum/modifier/trait/flimsy
mutually_exclusive = list(/datum/trait/modifier/physical/frail)
/datum/trait/modifier/physical/frail
name = "Frail"
desc = "Your body is very fragile, and has even less of an ability to endure harm."
modifier_type = /datum/modifier/trait/frail
mutually_exclusive = list(/datum/trait/modifier/physical/flimsy)
/datum/trait/modifier/physical/haemophilia
name = "Haemophilia"
desc = "Some say that when it rains, it pours. Unfortunately, this is also true for yourself if you get cut."
modifier_type = /datum/modifier/trait/haemophilia
/datum/trait/modifier/physical/haemophilia/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
return "Full Body Prosthetics cannot bleed."
// If a species lacking blood is added, it is suggested to add a check for them here.
return ..()
/datum/trait/modifier/physical/weak
name = "Weak"
desc = "A lack of physical strength causes a diminshed capability in close quarters combat."
modifier_type = /datum/modifier/trait/weak
mutually_exclusive = list(/datum/trait/modifier/physical/wimpy)
/datum/trait/modifier/physical/wimpy
name = "Wimpy"
desc = "An extreme lack of physical strength causes a greatly diminished capability in close quarters combat."
modifier_type = /datum/modifier/trait/wimpy
mutually_exclusive = list(/datum/trait/modifier/physical/weak)
/datum/trait/modifier/physical/inaccurate
name = "Inaccurate"
desc = "You're rather inexperienced with guns, you've never used one in your life, or you're just really rusty. \
Regardless, you find it quite difficult to land shots where you wanted them to go."
modifier_type = /datum/modifier/trait/inaccurate
/datum/trait/modifier/physical/shorter
name = "Shorter"
modifier_type = /datum/modifier/trait/shorter
mutually_exclusive = list(/datum/trait/modifier/physical/short, /datum/trait/modifier/physical/tall, /datum/trait/modifier/physical/taller)
/datum/trait/modifier/physical/short
name = "Short"
modifier_type = /datum/modifier/trait/short
mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/tall, /datum/trait/modifier/physical/taller)
/datum/trait/modifier/physical/tall
name = "Tall"
modifier_type = /datum/modifier/trait/tall
mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/short, /datum/trait/modifier/physical/taller)
/datum/trait/modifier/physical/taller
name = "Taller"
modifier_type = /datum/modifier/trait/taller
mutually_exclusive = list(/datum/trait/modifier/physical/shorter, /datum/trait/modifier/physical/short, /datum/trait/modifier/physical/tall)
/datum/trait/modifier/physical/thin
name = "Thin"
modifier_type = /datum/modifier/trait/thin
mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thinner)
/datum/trait/modifier/physical/thinner
name = "Rail Thin"
modifier_type = /datum/modifier/trait/thinner
mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thin)
/datum/trait/modifier/physical/fat
name = "Broad-Shouldered"
modifier_type = /datum/modifier/trait/fat
mutually_exclusive = list(/datum/trait/modifier/physical/thin, /datum/trait/modifier/physical/obese, /datum/trait/modifier/physical/thinner)
/datum/trait/modifier/physical/obese
name = "Heavily Built"
modifier_type = /datum/modifier/trait/obese
mutually_exclusive = list(/datum/trait/modifier/physical/fat, /datum/trait/modifier/physical/thinner, /datum/trait/modifier/physical/thin)
/datum/trait/modifier/physical/colorblind_protanopia
name = "Protanopia"
desc = "You have a form of red-green colorblindness. You cannot see reds, and have trouble distinguishing them from yellows and greens."
modifier_type = /datum/modifier/trait/colorblind_protanopia
mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_deuteranopia, /datum/trait/modifier/physical/colorblind_tritanopia, /datum/trait/modifier/physical/colorblind_monochrome)
/datum/trait/modifier/physical/colorblind_deuteranopia
name = "Deuteranopia"
desc = "You have a form of red-green colorblindness. You cannot see greens, and have trouble distinguishing them from yellows and reds."
modifier_type = /datum/modifier/trait/colorblind_deuteranopia
mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_protanopia, /datum/trait/modifier/physical/colorblind_tritanopia, /datum/trait/modifier/physical/colorblind_monochrome)
/datum/trait/modifier/physical/colorblind_tritanopia
name = "Tritanopia"
desc = "You have a form of blue-yellow colorblindness. You have trouble distinguishing between blues, greens, and yellows, and see blues and violets as dim."
modifier_type = /datum/modifier/trait/colorblind_tritanopia
mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_protanopia, /datum/trait/modifier/physical/colorblind_deuteranopia, /datum/trait/modifier/physical/colorblind_monochrome)
/datum/trait/modifier/physical/colorblind_monochrome
name = "Monochromacy"
desc = "You are fully colorblind. Your condition is rare, but you can see no colors at all."
modifier_type = /datum/modifier/trait/colorblind_monochrome
mutually_exclusive = list(/datum/trait/modifier/physical/colorblind_protanopia, /datum/trait/modifier/physical/colorblind_deuteranopia, /datum/trait/modifier/physical/colorblind_tritanopia)
// These two traits might be borderline, feel free to remove if they get abused.
/datum/trait/modifier/physical/high_metabolism
name = "High Metabolism"
modifier_type = /datum/modifier/trait/high_metabolism
mutually_exclusive = list(/datum/trait/modifier/physical/low_metabolism)
/datum/trait/modifier/physical/high_metabolism/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
return "Full Body Prosthetics do not have a metabolism."
return ..()
/datum/trait/modifier/physical/low_metabolism
name = "Low Metabolism"
modifier_type = /datum/modifier/trait/low_metabolism
mutually_exclusive = list(/datum/trait/modifier/physical/high_metabolism)
/datum/trait/modifier/physical/low_metabolism/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
return "Full Body Prosthetics do not have a metabolism."
return ..()
/datum/trait/modifier/physical/cloned
name = "Cloned"
desc = "At some point in your life, you died and were cloned."
modifier_type = /datum/modifier/cloned
/datum/trait/modifier/physical/cloned/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
return "Full Body Prosthetics cannot be cloned."
return ..()
/datum/trait/modifier/physical/no_clone
name = "Cloning Incompatibility"
modifier_type = /datum/modifier/no_clone
/datum/trait/modifier/physical/no_clone/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
return "Full Body Prosthetics cannot be cloned anyways."
return ..()
/datum/trait/modifier/physical/no_borg
name = "Cybernetic Incompatibility"
modifier_type = /datum/modifier/no_borg
/datum/trait/modifier/physical/no_borg/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
return "Full Body Prosthetics are already partly or fully mechanical."
return ..()
// 'Mental' traits are just those that only sapients can have, for now, and generally involves fears.
// So far, all of them are just for fluff/don't have mechanical effects.
/datum/trait/modifier/mental
abstract_type = /datum/trait/modifier/mental
name = "Mental"
category = "Mental"
/datum/trait/modifier/mental/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
if(setup.is_FBP())
if(setup.get_FBP_type() == PREF_FBP_SOFTWARE)
return "Drone Intelligences cannot feel emotions."
return ..()
/datum/trait/modifier/mental/arachnophobe
name = "Arachnophobic"
desc = "Spiders are quite creepy to most people, however for you, those chitters of pure evil inspire pure dread and fear."
modifier_type = /datum/modifier/trait/phobia/arachnophobe
/datum/trait/modifier/mental/nyctophobe
name = "Nyctophobic"
desc = "More commonly known as the fear of darkness. The shadows can hide many dangers, which makes the prospect of going into the depths of Maintenance rather worrisome."
modifier_type = /datum/modifier/trait/phobia/nyctophobe
/datum/trait/modifier/mental/haemophobe
name = "Haemophobia"
desc = "Not to be confused with Haemophilia (which makes you bleed faster), Haemophobia is the fear of blood. Seeing a bunch of blood isn't really \
pleasant for most people, but for you, it is very distressing."
modifier_type = /datum/modifier/trait/phobia/haemophobia
/datum/trait/modifier/mental/claustrophobe
name = "Claustrophobic"
desc = "Small spaces and tight quarters makes you feel distressed. Unfortunately both are rather common when living in space."
modifier_type = /datum/modifier/trait/phobia/claustrophobe
/datum/trait/modifier/mental/blennophobe
name = "Blennophobia"
desc = "Slimes are quite dangerous, but just the aspect of something being slimey is uncomfortable."
modifier_type = /datum/modifier/trait/phobia/blennophobe
/datum/trait/modifier/mental/trypanophobe
name = "Trypanophobia"
desc = "Syringes and needles make you very distressed. You really don't want to get sick..."
modifier_type = /datum/modifier/trait/phobia/trypanophobe
/*
// Uncomment this when/if these get finished.
/datum/trait/modifier/mental/synthphobe
name = "Synthphobic"
desc = "You know, deep down, that synthetics cannot be trusted, and so you are always on guard whenever you see one wandering around. No one knows how a Positronic's mind works, \
Drones are just waiting for the right time for Emergence, and the poor brains trapped in the cage of Man Machine Interfaces are now soulless, despite being unaware of it. None \
can be trusted."
/datum/trait/modifier/mental/xenophobe
name = "Xenophobic"
desc = "The mind of the Alien is unknowable, and as such, their intentions cannot be known. You always watch the xenos closely, as they most certainly are watching you \
closely, waiting to strike."
mutually_exclusive = list(
/datum/trait/modifier/mental/humanphobe,
/datum/trait/modifier/mental/skrellphobe,
/datum/trait/modifier/mental/tajaraphobe,
/datum/trait/modifier/mental/unathiphobe,
/datum/trait/modifier/mental/teshariphobe,
/datum/trait/modifier/mental/prometheanphobe
)
/datum/trait/modifier/mental/humanphobe
name = "Human-phobic"
desc = "Boilerplate racism for monkeys goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
/datum/trait/modifier/mental/skrellphobe
name = "Skrell-phobic"
desc = "Boilerplate racism for squid goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
/datum/trait/modifier/mental/tajaraphobe
name = "Tajaran-phobic"
desc = "Boilerplate racism for cats goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
/datum/trait/modifier/mental/unathiphobe
name = "Unathi-phobic"
desc = "Boilerplate racism for lizards goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
// Not sure why anyone would hate/fear these guys but for the sake of completeness here we are.
/datum/trait/modifier/mental/dionaphobe
name = "Diona-phobic"
desc = "Boilerplate racism for trees goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
/datum/trait/modifier/mental/teshariphobe
name = "Teshari-phobic"
desc = "Boilerplate racism for birds goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
/datum/trait/modifier/mental/prometheanphobe
name = "Promethean-phobic"
desc = "Boilerplate racism for jellos goes here."
mutually_exclusive = list(/datum/trait/modifier/mental/xenophobe)
*/
@@ -1,205 +0,0 @@
var/list/trait_datums = list() // Assoc list using name = instance. Traits are saved as a list of strings.
var/list/trait_type_to_ref = list() // Similar to above but uses paths, which is more reliable but more risky to save.
var/list/trait_categories = list() // The categories available for the trait menu.
/hook/startup/proc/populate_trait_list()
//create a list of trait datums
for(var/trait_type as anything in subtypesof(/datum/trait))
if (is_abstract(trait_type))
continue
var/datum/trait/T = new trait_type
if(!T.is_available())
qdel(T)
continue
if(!T.name)
error("Trait Menu - Missing name: [T.type]")
continue
if(!T.category)
error("Trait Menu - Missing category: [T.type]")
continue
T.desc = T.generate_desc()
trait_datums[T.name] = T
trait_type_to_ref[T.type] = T
if(!(T.category in trait_categories))
trait_categories += T.category
return 1
/datum/category_item/player_setup_item/traits
name = "Quirks" //VOREStation Edit
sort_order = 1
var/current_tab = "Physical"
/datum/category_item/player_setup_item/traits/load_character(list/save_data)
pref.traits = save_data["traits"]
/datum/category_item/player_setup_item/traits/save_character(list/save_data)
save_data["traits"] = pref.traits
/datum/category_item/player_setup_item/traits/content()
. = list()
. += "<table align = 'center' width = 100%>"
. += "<tr><td colspan=3><hr></td></tr>"
. += "<tr><td colspan=3>" + span_bold("<center>Traits</center>") + "</td></tr>"
. += "<tr><td colspan=3><hr></td></tr>"
. += "<tr><td colspan=3><center>"
var/firstcat = 1
for(var/category in trait_categories)
if(firstcat)
firstcat = 0
else
. += " |"
if(category == current_tab)
. += " " + span_linkOn("[category]") + " "
else
. += " <a href='byond://?src=\ref[src];select_category=[category]'>[category]</a> "
. += "</center></td></tr>"
for(var/trait_name in trait_datums)
var/datum/trait/T = trait_datums[trait_name]
if(T.category != current_tab)
continue
var/ticked = (T.name in pref.traits)
var/style_class
if(!T.validate(pref.traits, src))
style_class = "linkOff"
else if(ticked)
style_class = "linkOn"
. += "<tr style='vertical-align:top;'><td width=25%><div align='center'><a style='white-space:normal;' [style_class ? "class='[style_class]' " : ""]href='byond://?src=\ref[src];toggle_trait=[html_encode(T.name)]'>[T.name]</a></div></td>"
// . += "<td width = 10% style='vertical-align:top'>[G.cost]</td>"
var/invalidity = T.test_for_invalidity(src)
var/conflicts = T.test_for_trait_conflict(pref.traits)
var/invalid = ""
if(invalidity)
invalid += "[invalidity] "
if(conflicts)
invalid += "This trait is mutually exclusive with [conflicts]."
. += "<td width = 75%><font size=2>" + span_italics("[T.desc]") + "\
[invalid ? span_red("<br>Cannot take trait. Reason: [invalid]"):""]</font></td></tr>"
// if(ticked)
// . += "<tr><td colspan=3>"
// for(var/datum/gear_tweak/tweak in G.gear_tweaks)
// . += " <a href='byond://?src=\ref[src];gear=[G.display_name];tweak=\ref[tweak]'>[tweak.get_contents(get_tweak_metadata(G, tweak))]</a>"
// . += "</td></tr>"
. += "</table>"
. = jointext(., null)
/datum/category_item/player_setup_item/traits/sanitize_character()
var/mob/preference_mob = preference_mob()
if(!islist(pref.traits))
pref.traits = list()
for(var/trait_name in pref.traits)
if(!(trait_name in trait_datums))
pref.traits -= trait_name
for(var/trait_name in pref.traits)
if(!trait_datums[trait_name])
to_chat(preference_mob, span_warning("You cannot have more than one of trait: [trait_name]"))
pref.traits -= trait_name
else
var/datum/trait/T = trait_datums[trait_name]
var/invalidity = T.test_for_invalidity(src)
if(invalidity)
pref.traits -= trait_name
to_chat(preference_mob, span_warning("You cannot take the [trait_name] trait. Reason: [invalidity]"))
var/conflicts = T.test_for_trait_conflict(pref.traits)
if(conflicts)
pref.traits -= trait_name
to_chat(preference_mob, span_warning("The [trait_name] trait is mutually exclusive with [conflicts]."))
/datum/category_item/player_setup_item/traits/OnTopic(href, href_list, user)
if(href_list["toggle_trait"])
var/datum/trait/T = trait_datums[href_list["toggle_trait"]]
if(T.name in pref.traits)
pref.traits -= T.name
else
var/invalidity = T.test_for_invalidity(src)
if(invalidity)
to_chat(user, span_warning("You cannot take the [T.name] trait. Reason: [invalidity]"))
return TOPIC_NOACTION
var/conflicts = T.test_for_trait_conflict(pref.traits)
if(conflicts)
to_chat(user, span_warning("The [T.name] trait is mutually exclusive with [conflicts]."))
return TOPIC_NOACTION
pref.traits += T.name
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["select_category"])
current_tab = href_list["select_category"]
return TOPIC_REFRESH
return ..()
/datum/trait
abstract_type = /datum/trait
var/name = null // Name to show on UI
var/desc = null // Description of what it does, also shown on UI.
var/list/mutually_exclusive = list() // List of trait types which cannot be taken alongside this trait.
var/category = null // What section to place this trait inside.
// Applies effects to the newly spawned mob.
/datum/trait/proc/apply_trait_post_spawn(var/mob/living/L)
return
// Used to forbid a trait based on certain criteria (e.g. if they are an FBP).
// It receives the player_setup_item datum since some reasons for being invalid depend on the currently loaded preferences.
// Returns a string explaining why the trait is invalid. Returns null if valid.
/datum/trait/proc/test_for_invalidity(var/datum/category_item/player_setup_item/traits/setup)
return null
// Checks mutually_exclusive. current_traits needs to be a list of strings.
// Returns null if everything is well, similar to the above proc. Otherwise returns an english_list() of conflicting traits.
/datum/trait/proc/test_for_trait_conflict(var/list/current_traits)
var/list/conflicts = list()
var/result
if(mutually_exclusive.len)
for(var/trait_name in current_traits)
var/datum/trait/T = trait_datums[trait_name]
if(T.type in mutually_exclusive)
conflicts.Add(T.name)
if(conflicts.len)
result = english_list(conflicts)
return result
/datum/trait/proc/is_available()
return TRUE
// Similar to above, but uses the above two procs, in one place.
// Returns TRUE is everything is well.
/datum/trait/proc/validate(var/list/current_traits, var/datum/category_item/player_setup_item/traits/setup)
if(test_for_invalidity(setup))
return FALSE
if(test_for_trait_conflict(current_traits))
return FALSE
return TRUE
// Creates a description, if one doesn't exist.
// This one is for inheritence, and so doesn't do anything.
/datum/trait/proc/generate_desc()
return desc
/mob/living/proc/apply_traits()
if(!mind || !mind.traits || !mind.traits.len)
return
for(var/trait in mind.traits)
var/datum/trait/T = trait_datums[trait]
if(istype(T))
T.apply_trait_post_spawn(src)
@@ -1,106 +0,0 @@
/datum/category_group/player_setup_category/volume_sliders
name = "Sound"
sort_order = 7
category_item_type = /datum/category_item/player_setup_item/volume_sliders
/datum/category_item/player_setup_item/volume_sliders/volume
name = "General Volume"
sort_order = 1
/datum/category_item/player_setup_item/volume_sliders/volume/load_preferences(datum/json_savefile/savefile)
pref.volume_channels = savefile.get_entry("volume_channels")
/datum/category_item/player_setup_item/volume_sliders/volume/save_preferences(datum/json_savefile/savefile)
savefile.set_entry("volume_channels", pref.volume_channels)
/datum/category_item/player_setup_item/volume_sliders/volume/sanitize_preferences()
if(isnull(pref.volume_channels))
pref.volume_channels = list()
for(var/channel in pref.volume_channels)
if(!(channel in GLOB.all_volume_channels))
// Channel no longer exists, yeet
pref.volume_channels.Remove(channel)
for(var/channel in GLOB.all_volume_channels)
if(!(channel in pref.volume_channels))
pref.volume_channels["[channel]"] = 1
else
pref.volume_channels["[channel]"] = clamp(pref.volume_channels["[channel]"], 0, 2)
/datum/category_item/player_setup_item/volume_sliders/volume/content(var/mob/user)
. += span_bold("Volume Settings") + "<br>"
for(var/channel in pref.volume_channels)
. += "[channel]: <a href='byond://?src=\ref[src];change_volume=[channel];'><b>[pref.volume_channels[channel] * 100]%</b></a><br>"
. += "<br>"
/datum/category_item/player_setup_item/volume_sliders/volume/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["change_volume"])
if(CanUseTopic(user))
var/channel = href_list["change_volume"]
if(!(channel in pref.volume_channels))
pref.volume_channels["[channel]"] = 1
var/value = tgui_input_number(user, "Choose your volume for [channel] (0-200%)", "[channel] volume", (pref.volume_channels[channel] * 100), 200, 0)
if(isnum(value))
value = CLAMP(value, 0, 200)
pref.volume_channels["[channel]"] = (value / 100)
return TOPIC_REFRESH
return ..()
/mob/proc/get_preference_volume_channel(volume_channel)
if(!client)
return 0
return client.get_preference_volume_channel(volume_channel)
/client/proc/get_preference_volume_channel(volume_channel)
if(!volume_channel || !prefs)
return 1
if(!(volume_channel in prefs.volume_channels))
prefs.volume_channels["[volume_channel]"] = 1
return prefs.volume_channels["[volume_channel]"]
// Neat little volume adjuster thing in case you don't wanna touch preferences by hand you lazy fuck
/datum/volume_panel
/datum/volume_panel/tgui_state(mob/user)
return GLOB.tgui_always_state
/datum/volume_panel/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "VolumePanel", "Volume Panel")
ui.open()
/datum/volume_panel/tgui_data(mob/user)
if(!user.client || !user.client.prefs)
return list("error" = TRUE)
var/list/data = ..()
data["volume_channels"] = user.client.prefs.volume_channels
return data
/datum/volume_panel/tgui_act(action, params, datum/tgui/ui)
if(..())
return TRUE
if(!ui.user?.client?.prefs)
return TRUE
var/datum/preferences/P = ui.user.client.prefs
switch(action)
if("adjust_volume")
var/channel = params["channel"]
if(channel in P.volume_channels)
P.volume_channels["[channel]"] = clamp(params["vol"], 0, 2)
SScharacter_setup.queue_preferences_save(P)
return TRUE
/client/verb/volume_panel()
set name = "Volume Panel"
set category = "Preferences.Sounds"
set desc = "Allows you to adjust volume levels on the fly."
if(!volume_panel)
volume_panel = new(src)
volume_panel.tgui_interact(mob)
@@ -1,53 +0,0 @@
/datum/preferences
var/media_volume = 1
var/media_player = 2 // 0 = VLC, 1 = WMP, 2 = HTML5, 3+ = unassigned
/datum/category_item/player_setup_item/volume_sliders/media
name = "Media"
sort_order = 2
/datum/category_item/player_setup_item/volume_sliders/media/load_preferences(datum/json_savefile/savefile)
pref.media_volume = savefile.get_entry("media_volume")
pref.media_player = savefile.get_entry("media_player")
/datum/category_item/player_setup_item/volume_sliders/media/save_preferences(datum/json_savefile/savefile)
savefile.set_entry("media_volume", pref.media_volume)
savefile.set_entry("media_player", pref.media_player)
/datum/category_item/player_setup_item/volume_sliders/media/sanitize_preferences()
pref.media_volume = isnum(pref.media_volume) ? CLAMP(pref.media_volume, 0, 1) : initial(pref.media_volume)
pref.media_player = sanitize_inlist(pref.media_player, list(0, 1, 2), initial(pref.media_player))
/datum/category_item/player_setup_item/volume_sliders/media/content(var/mob/user)
. += span_bold("Jukebox Volume:")
. += "<a href='byond://?src=\ref[src];change_media_volume=1'><b>[round(pref.media_volume * 100)]%</b></a><br>"
. += span_bold("Media Player Type:") + " Depending on you operating system, one of these might work better. "
. += "Use HTML5 if it works for you. If neither HTML5 nor WMP work, you'll have to fall back to using VLC, "
. += "but this requires you have the VLC client installed on your comptuer."
. += "Try the others if you want but you'll probably just get no music.<br>"
. += (pref.media_player == 2) ? (span_linkOn(span_bold("HTML5")) + " ") : "<a href='byond://?src=\ref[src];set_media_player=2'>HTML5</a> "
. += (pref.media_player == 1) ? (span_linkOn(span_bold("WMP")) + " ") : "<a href='byond://?src=\ref[src];set_media_player=1'>WMP</a> "
. += (pref.media_player == 0) ? (span_linkOn(span_bold("VLC")) + " ") : "<a href='byond://?src=\ref[src];set_media_player=0'>VLC</a> "
. += "<br>"
/datum/category_item/player_setup_item/volume_sliders/media/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["change_media_volume"])
if(CanUseTopic(user))
var/value = tgui_input_number(user, "Choose your Jukebox volume (0-100%)", "Jukebox volume", round(pref.media_volume * 100), 100, 0)
if(isnum(value))
value = CLAMP(value, 0, 100)
pref.media_volume = value/100.0
if(user.client && user.client.media)
user.client.media.update_volume(pref.media_volume)
return TOPIC_REFRESH
else if(href_list["set_media_player"])
if(CanUseTopic(user))
var/newval = sanitize_inlist(text2num(href_list["set_media_player"]), list(0, 1, 2), pref.media_player)
if(newval != pref.media_player)
pref.media_player = newval
if(user.client && user.client.media)
user.client.media.open()
spawn(10)
user.update_music()
return TOPIC_REFRESH
return ..()
@@ -1,5 +0,0 @@
// Global stuff that will put us on the map
/datum/category_group/player_setup_category/vore
name = "VORE"
sort_order = 8
category_item_type = /datum/category_item/player_setup_item/vore
@@ -1,314 +0,0 @@
// Body weight limits on a character.
#define WEIGHT_CHANGE_MIN 0
#define WEIGHT_CHANGE_MAX 100
// Define a place to save in character setup
/datum/preferences
var/size_multiplier = RESIZE_NORMAL
// Body weight stuff.
var/weight_vr = 137 // bodyweight of character (pounds, because I'm not doing the math again -Spades)
var/weight_gain = 100 // Weight gain rate.
var/weight_loss = 50 // Weight loss rate.
var/fuzzy = 0 // Preference toggle for sharp/fuzzy icon. Default sharp.
var/offset_override = FALSE
var/voice_freq = 42500
var/voice_sound = "goon speak 1"
var/custom_speech_bubble = "default"
var/custom_footstep = "Default"
var/species_sound = "Unset"
// Definition of the stuff for Sizing
/datum/category_item/player_setup_item/vore/size
name = "Size"
sort_order = 2
/datum/category_item/player_setup_item/vore/size/load_character(list/save_data)
pref.size_multiplier = save_data["size_multiplier"]
pref.weight_vr = save_data["weight_vr"]
pref.weight_gain = save_data["weight_gain"]
pref.weight_loss = save_data["weight_loss"]
pref.fuzzy = save_data["fuzzy"]
pref.offset_override = save_data["offset_override"]
pref.voice_freq = save_data["voice_freq"]
pref.voice_sound = save_data["voice_sound"]
pref.custom_speech_bubble = save_data["custom_speech_bubble"]
pref.custom_footstep = save_data["custom_footstep"]
pref.species_sound = save_data["species_sound"]
/datum/category_item/player_setup_item/vore/size/save_character(list/save_data)
save_data["size_multiplier"] = pref.size_multiplier
save_data["weight_vr"] = pref.weight_vr
save_data["weight_gain"] = pref.weight_gain
save_data["weight_loss"] = pref.weight_loss
save_data["fuzzy"] = pref.fuzzy
save_data["offset_override"] = pref.offset_override
save_data["voice_freq"] = pref.voice_freq
save_data["voice_sound"] = pref.voice_sound
save_data["custom_speech_bubble"] = pref.custom_speech_bubble
save_data["custom_footstep"] = pref.custom_footstep
save_data["species_sound"] = pref.species_sound
/datum/category_item/player_setup_item/vore/size/sanitize_character()
pref.weight_vr = sanitize_integer(pref.weight_vr, WEIGHT_MIN, WEIGHT_MAX, initial(pref.weight_vr))
pref.weight_gain = sanitize_integer(pref.weight_gain, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_gain))
pref.weight_loss = sanitize_integer(pref.weight_loss, WEIGHT_CHANGE_MIN, WEIGHT_CHANGE_MAX, initial(pref.weight_loss))
pref.fuzzy = sanitize_integer(pref.fuzzy, 0, 1, initial(pref.fuzzy))
pref.offset_override = sanitize_integer(pref.offset_override, 0, 1, initial(pref.offset_override))
if(pref.voice_freq != 0)
pref.voice_freq = sanitize_integer(pref.voice_freq, MIN_VOICE_FREQ, MAX_VOICE_FREQ, initial(pref.voice_freq))
if(pref.size_multiplier == null || pref.size_multiplier < RESIZE_TINY || pref.size_multiplier > RESIZE_HUGE)
pref.size_multiplier = initial(pref.size_multiplier)
if(!(pref.custom_speech_bubble in GLOB.selectable_speech_bubbles))
pref.custom_speech_bubble = "default"
if(!(pref.custom_footstep))
pref.custom_footstep = "Default"
// var/datum/species/selected_species = GLOB.all_species[pref.species]
if(!(pref.species_sound))
pref.species_sound = "Unset"
/datum/category_item/player_setup_item/vore/size/copy_to_mob(var/mob/living/carbon/human/character)
character.weight = pref.weight_vr
character.weight_gain = pref.weight_gain
character.weight_loss = pref.weight_loss
character.fuzzy = pref.fuzzy
character.offset_override = pref.offset_override
character.voice_freq = pref.voice_freq
character.resize(pref.size_multiplier, animate = FALSE, ignore_prefs = TRUE)
if(!pref.voice_sound)
character.voice_sounds_list = DEFAULT_TALK_SOUNDS
else
character.voice_sounds_list = get_talk_sound(pref.voice_sound)
character.custom_speech_bubble = pref.custom_speech_bubble
character.custom_footstep = pref.custom_footstep
/datum/category_item/player_setup_item/vore/size/content(var/mob/user)
. += "<br>"
. += span_bold("Scale:") + " <a href='byond://?src=\ref[src];size_multiplier=1'>[round(pref.size_multiplier*100)]%</a><br>"
. += span_bold("Scaled Appearance:") + " <a [pref.fuzzy ? "" : ""] href='byond://?src=\ref[src];toggle_fuzzy=1'><b>[pref.fuzzy ? "Fuzzy" : "Sharp"]</b></a><br>"
. += span_bold("Scaling Center:") + " <a [pref.offset_override ? "" : ""] href='byond://?src=\ref[src];toggle_offset_override=1'><b>[pref.offset_override ? "Odd" : "Even"]</b></a><br>"
. += "<br>"
. += span_bold("Mob Speech/Noise Customization") + ""
. += "<br>"
. += span_bold("Voice Frequency:") + " <a href='byond://?src=\ref[src];voice_freq=1'>[pref.voice_freq]</a><br>"
. += span_bold("Voice Sounds:") + " <a href='byond://?src=\ref[src];voice_sounds_list=1'>[pref.voice_sound]</a><br>"
. += "<a href='byond://?src=\ref[src];voice_test=1'><b>Test Selected Voice</b></a><br>"
. += span_bold("Custom Speech Bubble:") + " <a href='byond://?src=\ref[src];customize_speech_bubble=1'>[pref.custom_speech_bubble]</a><br>"
. += span_bold("Custom Footstep Sounds:") + "<a href='byond://?src=\ref[src];customize_footsteps=1'>[pref.custom_footstep]</a><br>"
. += "<br>"
. += span_bold("Species Sounds:") + " <a href='byond://?src=\ref[src];species_sound_options=1'>[pref.species_sound]</a><br>"
. += "<a href='byond://?src=\ref[src];cough_test=1'><b>Test Cough Sounds</b></a><br>"
. += "<a href='byond://?src=\ref[src];sneeze_test=1'><b>Test Sneeze Sounds</b></a><br>"
. += "<a href='byond://?src=\ref[src];scream_test=1'><b>Test Scream Sounds</b></a><br>"
. += "<a href='byond://?src=\ref[src];pain_test=1'><b>Test Pain Sounds</b></a><br>"
. += "<a href='byond://?src=\ref[src];gasp_test=1'><b>Test Gasp Sounds</b></a><br>"
. += "<a href='byond://?src=\ref[src];death_test=1'><b>Test Death Sounds</b></a><br>"
. += "<br>"
. += span_bold("Relative Weight:") + " <a href='byond://?src=\ref[src];weight=1'>[pref.weight_vr]</a><br>"
. += span_bold("Weight Gain Rate:") + " <a href='byond://?src=\ref[src];weight_gain=1'>[pref.weight_gain]</a><br>"
. += span_bold("Weight Loss Rate:") + " <a href='byond://?src=\ref[src];weight_loss=1'>[pref.weight_loss]</a><br>"
/datum/category_item/player_setup_item/vore/size/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["size_multiplier"])
var/new_size = tgui_input_number(user, "Choose your character's size, ranging from [RESIZE_MINIMUM * 100]% to [RESIZE_MAXIMUM * 100]%", "Set Size", pref.size_multiplier * 100, RESIZE_MAXIMUM * 100, RESIZE_MINIMUM * 100)
if (!ISINRANGE(new_size, RESIZE_MINIMUM * 100, RESIZE_MAXIMUM * 100))
pref.size_multiplier = 1
to_chat(user, span_notice("Invalid size."))
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(new_size)
pref.size_multiplier = (new_size / 100)
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["toggle_fuzzy"])
pref.fuzzy = pref.fuzzy ? 0 : 1;
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["toggle_offset_override"])
pref.offset_override = pref.offset_override ? 0 : 1;
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["weight"])
var/new_weight = tgui_input_number(user, "Choose your character's relative body weight.\n\
Note: Scifi characters come in all shapes and sizes in this game, and not all follow the traditional shape of a human. Like a naga or a taur or a giant will weigh a\n\
lot more than what this allows, or a micro will weigh a lot less. Just ignore all of that for a second and PRETEND the weight you're setting is visually for an \n\
average human. This is the best solution we have at the moment.!\n\
([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE)
if(new_weight)
var/unit_of_measurement = tgui_alert(user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms"))
if(!unit_of_measurement)
return TOPIC_NOACTION
if(unit_of_measurement == "Pounds")
new_weight = round(text2num(new_weight),4)
if(unit_of_measurement == "Kilograms")
new_weight = round(2.20462*text2num(new_weight),4)
pref.weight_vr = sanitize_integer(new_weight, WEIGHT_MIN, WEIGHT_MAX, pref.weight_vr)
return TOPIC_REFRESH
else if(href_list["weight_gain"])
var/weight_gain_rate = tgui_input_number(user, "Choose your character's rate of weight gain between 100% \
(full realism body fat gain) and 0% (no body fat gain).\n\
(If you want to disable weight gain, set this to 0.01 to round it to 0%.)\
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_gain, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE)
if(weight_gain_rate)
pref.weight_gain = round(text2num(weight_gain_rate),1)
return TOPIC_REFRESH
else if(href_list["weight_loss"])
var/weight_loss_rate = tgui_input_number(user, "Choose your character's rate of weight loss between 100% \
(full realism body fat loss) and 0% (no body fat loss).\n\
(If you want to disable weight loss, set this to 0.01 round it to 0%.)\
([WEIGHT_CHANGE_MIN]-[WEIGHT_CHANGE_MAX])", "Character Preference", pref.weight_loss, WEIGHT_CHANGE_MAX, WEIGHT_CHANGE_MIN, round_value=FALSE)
if(weight_loss_rate)
pref.weight_loss = round(text2num(weight_loss_rate),1)
return TOPIC_REFRESH
else if(href_list["voice_freq"])
var/list/preset_voice_freqs = list("high" = MAX_VOICE_FREQ, "middle-high" = 56250, "middle" = 42500, "middle-low"= 28750, "low" = MIN_VOICE_FREQ, "custom" = 1, "random" = 0)
var/choice = tgui_input_list(user, "What would you like to set your voice frequency to? ([MIN_VOICE_FREQ] - [MAX_VOICE_FREQ])", "Voice Frequency", preset_voice_freqs)
if(!choice)
return
choice = preset_voice_freqs[choice]
if(choice == 0)
pref.voice_freq = 42500
return TOPIC_REFRESH
else if(choice == 1)
choice = tgui_input_number(user, "Choose your character's voice frequency, ranging from [MIN_VOICE_FREQ] to [MAX_VOICE_FREQ]", "Custom Voice Frequency", null, MAX_VOICE_FREQ, MIN_VOICE_FREQ)
if(choice > MAX_VOICE_FREQ)
choice = MAX_VOICE_FREQ
else if(choice < MIN_VOICE_FREQ)
choice = MIN_VOICE_FREQ
pref.voice_freq = choice
return TOPIC_REFRESH
else if(href_list["voice_sounds_list"])
var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's speech sounds?", "Voice Sounds", SSsounds.talk_sound_map)
if(!pref.voice_sound)
pref.voice_sound = "goon speak 1"
else if(!choice)
return TOPIC_REFRESH
else
pref.voice_sound = choice
return TOPIC_REFRESH
else if(href_list["customize_speech_bubble"])
var/choice = tgui_input_list(user, "What speech bubble style do you want to use? (default for automatic selection)", "Custom Speech Bubble", GLOB.selectable_speech_bubbles)
if(!choice)
pref.custom_speech_bubble = "default"
else
pref.custom_speech_bubble = choice
return TOPIC_REFRESH
else if(href_list["customize_footsteps"])
var/list/footstep_choice = selectable_footstep
var/choice = tgui_input_list(user, "What footstep sounds would your character make?", "Custom Foostep Sounds", footstep_choice)
if(choice)
pref.custom_footstep = footstep_choice[choice]
return TOPIC_REFRESH
else if(href_list["voice_test"])
var/sound/S = sound(pick(SSsounds.talk_sound_map[pref.voice_sound])) // talk_sound_map returns a list of sounds
if(S)
S.frequency = pick(pref.voice_freq)
S.volume = 50
SEND_SOUND(user, S)
else if(href_list["species_sound_options"]) // You shouldn't be able to see this option if you don't have the option to select a custom icon base, so we don't need to re-check for safety here.
var/list/possible_species_sound_types = species_sound_map
var/choice = tgui_input_list(user, "Which set of sounds would you like to use for your character's species sounds? (Cough, Sneeze, Scream, Pain, Gasp, Death)", "Species Sounds", possible_species_sound_types)
if(!choice)
return TOPIC_REFRESH // No choice? Don't reset our selection
else
pref.species_sound = choice
return TOPIC_REFRESH
else if(href_list["cough_test"])
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["cough"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["cough"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have cough sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
return TOPIC_REFRESH
else if(href_list["sneeze_test"])
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["sneeze"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["sneeze"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have sneeze sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
return TOPIC_REFRESH
else if(href_list["scream_test"])
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["scream"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["scream"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have scream sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
return TOPIC_REFRESH
else if(href_list["pain_test"])
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["pain"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["pain"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have pain sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
return TOPIC_REFRESH
else if(href_list["gasp_test"])
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["gasp"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["gasp"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have gasp sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
return TOPIC_REFRESH
else if(href_list["death_test"])
var/sound/S
var/ourpref = pref.species_sound
var/oursound = get_species_sound(ourpref)["death"]
S = sound(pick(oursound))
if(pref.species_sound == "Unset")
oursound = get_species_sound(select_default_species_sound(pref))["death"]
S = sound(pick(oursound))
if(pref.species_sound == "None" || oursound == null)
to_chat(user, span_warning("This set does not have death sounds!"))
return TOPIC_REFRESH
S.frequency = pick(pref.voice_freq)
S.volume = 20
SEND_SOUND(user, S)
return TOPIC_REFRESH
return ..();
#undef WEIGHT_CHANGE_MIN
#undef WEIGHT_CHANGE_MAX
@@ -1,76 +0,0 @@
// Define a place to save appearance in character setup
// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than #
#define AUTOHISS_OFF 0
#define AUTOHISS_BASIC 1
#define AUTOHISS_FULL 2
// VOREStation Add End
/datum/preferences
var/vore_egg_type = "Egg" //The egg type they have.
var/autohiss = "Full" // VOREStation Add: Whether we have Autohiss on. I'm hijacking the egg panel bc this one has a shitton of unused space.
// Definition of the stuff for the egg type.
/datum/category_item/player_setup_item/vore/egg
name = "Egg appearance."
sort_order = 3
/datum/category_item/player_setup_item/vore/egg/load_character(list/save_data)
pref.vore_egg_type = save_data["vore_egg_type"]
pref.autohiss = save_data["autohiss"]
/datum/category_item/player_setup_item/vore/egg/save_character(list/save_data)
save_data["vore_egg_type"] = pref.vore_egg_type
save_data["autohiss"] = pref.autohiss
/datum/category_item/player_setup_item/vore/egg/sanitize_character()
pref.vore_egg_type = sanitize_inlist(pref.vore_egg_type, GLOB.global_vore_egg_types, initial(pref.vore_egg_type))
/datum/category_item/player_setup_item/vore/egg/copy_to_mob(var/mob/living/carbon/human/character)
character.vore_egg_type = pref.vore_egg_type
// VOREStation Add
if(pref.client) // Safety, just in case so we don't runtime.
if(!pref.autohiss)
pref.client.autohiss_mode = AUTOHISS_FULL
else
switch(pref.autohiss)
if("Full")
pref.client.autohiss_mode = AUTOHISS_FULL
if("Basic")
pref.client.autohiss_mode = AUTOHISS_BASIC
if("Off")
pref.client.autohiss_mode = AUTOHISS_OFF
// VOREStation Add
/datum/category_item/player_setup_item/vore/egg/content(var/mob/user)
. += "<br>"
. += " Egg Type: <a href='byond://?src=\ref[src];vore_egg_type=1'>[pref.vore_egg_type]</a><br>"
. += span_bold("Autohiss Default Setting:") + " <a href='byond://?src=\ref[src];autohiss=1'>[pref.autohiss]</a><br>" // VOREStation Add
/datum/category_item/player_setup_item/vore/egg/OnTopic(var/href, var/list/href_list, var/mob/user)
if(!CanUseTopic(user))
return TOPIC_NOACTION
else if(href_list["vore_egg_type"])
var/list/vore_egg_types = GLOB.global_vore_egg_types
var/selection = tgui_input_list(user, "Choose your character's egg type:", "Character Preference", vore_egg_types, pref.vore_egg_type)
if(selection)
pref.vore_egg_type = selection
return TOPIC_REFRESH
// VOREStation Add Start
else if(href_list["autohiss"])
var/list/autohiss_selection = list("Full", "Basic", "Off")
var/selection = tgui_input_list(user, "Choose your default autohiss setting:", "Character Preference", autohiss_selection, pref.autohiss)
if(selection)
pref.autohiss = selection
else if(!selection)
pref.autohiss = "Full"
return TOPIC_REFRESH
// VOREStation Add End
else
return
// VOREStation Add Start: Doing this here bc AUTOHISS_FULL is more readable than #
#undef AUTOHISS_OFF
#undef AUTOHISS_BASIC
#undef AUTOHISS_FULL
// VOREStation Add End
@@ -1,60 +0,0 @@
// Define a place to save in character setup
/datum/preferences
var/resleeve_lock = 0 // Whether movs should have OOC reslieving protection. Default false.
var/resleeve_scan = 1 // Whether mob should start with a pre-spawn body scan. Default true.
var/mind_scan = 1 // Whether mob should start with a pre-spawn mind scan. Default true.
// Definition of the stuff for Sizing
/datum/category_item/player_setup_item/vore/resleeve
name = "Resleeving"
sort_order = 4
/datum/category_item/player_setup_item/vore/resleeve/load_character(list/save_data)
pref.resleeve_lock = save_data["resleeve_lock"]
pref.resleeve_scan = save_data["resleeve_scan"]
pref.mind_scan = save_data["mind_scan"]
/datum/category_item/player_setup_item/vore/resleeve/save_character(list/save_data)
save_data["resleeve_lock"] = pref.resleeve_lock
save_data["resleeve_scan"] = pref.resleeve_scan
save_data["mind_scan"] = pref.mind_scan
/datum/category_item/player_setup_item/vore/resleeve/sanitize_character()
pref.resleeve_lock = sanitize_integer(pref.resleeve_lock, 0, 1, initial(pref.resleeve_lock))
pref.resleeve_scan = sanitize_integer(pref.resleeve_scan, 0, 1, initial(pref.resleeve_scan))
pref.mind_scan = sanitize_integer(pref.mind_scan, 0, 1, initial(pref.mind_scan))
/datum/category_item/player_setup_item/vore/resleeve/copy_to_mob(var/mob/living/carbon/human/character)
if(character && !istype(character,/mob/living/carbon/human/dummy))
spawn(50)
if(QDELETED(character) || QDELETED(pref))
return // They might have been deleted during the wait
if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore
if(pref.resleeve_scan)
var/datum/transhuman/body_record/BR = new()
BR.init_from_mob(character, pref.resleeve_scan, pref.resleeve_lock)
if(pref.mind_scan)
var/datum/transcore_db/our_db = SStranscore.db_by_key(null)
if(our_db)
our_db.m_backup(character.mind,character.nif,one_time = TRUE)
if(pref.resleeve_lock)
character.resleeve_lock = character.ckey
character.original_player = character.ckey
/datum/category_item/player_setup_item/vore/resleeve/content(var/mob/user)
. += "<br>"
. += span_bold("Start With Body Scan:") + " <a [pref.resleeve_scan ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_resleeve_scan=1'><b>[pref.resleeve_scan ? "Yes" : "No"]</b></a><br>"
. += span_bold("Start With Mind Scan:") + " <a [pref.mind_scan ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_mind_scan=1'><b>[pref.mind_scan ? "Yes" : "No"]</b></a><br>"
. += span_bold("Prevent Body Impersonation:") + " <a [pref.resleeve_lock ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_resleeve_lock=1'><b>[pref.resleeve_lock ? "Yes" : "No"]</b></a><br>"
/datum/category_item/player_setup_item/vore/resleeve/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["toggle_resleeve_lock"])
pref.resleeve_lock = pref.resleeve_lock ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["toggle_resleeve_scan"])
pref.resleeve_scan = pref.resleeve_scan ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["toggle_mind_scan"])
pref.mind_scan = pref.mind_scan ? 0 : 1;
return TOPIC_REFRESH
return ..();
@@ -1,63 +0,0 @@
// Define a place to save in character setup
/datum/preferences
var/persistence_settings = PERSIST_DEFAULT // Control what if anything is persisted for this character between rounds.
// Definition of the stuff for Sizing
/datum/category_item/player_setup_item/vore/persistence
name = "Persistence"
sort_order = 5
/datum/category_item/player_setup_item/vore/persistence/load_character(list/save_data)
pref.persistence_settings = save_data["persistence_settings"]
sanitize_character() // Don't let new characters start off with nulls
/datum/category_item/player_setup_item/vore/persistence/save_character(list/save_data)
save_data["persistence_settings"] = pref.persistence_settings
/datum/category_item/player_setup_item/vore/persistence/sanitize_character()
pref.persistence_settings = sanitize_integer(pref.persistence_settings, 0, (1<<(PERSIST_COUNT+1)-1), initial(pref.persistence_settings))
/datum/category_item/player_setup_item/vore/persistence/content(var/mob/user)
. = list()
. += span_bold("Round-to-Round Persistence") + "<br>"
. += "<table>"
. += "<tr><td title=\"Set spawn location based on where you cryo'd out.\">Save Spawn Location: </td>"
. += make_yesno(PERSIST_SPAWN)
. += "</tr>"
. += "<tr><td title=\"Save your character's weight until next round.\">Save Weight: </td>"
. += make_yesno(PERSIST_WEIGHT)
. += "</tr>"
. += "<tr><td title=\"Update organ preferences (normal/amputated/robotic/etc) and model (for robotic) based on what you have at round end.\">Save Organs: </td>"
. += make_yesno(PERSIST_ORGANS)
. += "</tr>"
. += "<tr><td title=\"Update marking preferences (type and color) based on what you have at round end.\">Save Markings: </td>"
. += make_yesno(PERSIST_MARKINGS)
. += "</tr>"
. += "<tr><td title=\"Update character scale based on what you were at round end.\">Save Scale: </td>"
. += make_yesno(PERSIST_SIZE)
. += "</tr>"
. += "</table>"
return jointext(., "")
/datum/category_item/player_setup_item/vore/persistence/proc/make_yesno(var/bit)
if(pref.persistence_settings & bit)
return "<td>" + span_linkOn(span_bold("Yes")) + "</td> <td><a href='byond://?src=\ref[src];toggle_off=[bit]'>No</a></td>"
else
return "<td><a href='byond://?src=\ref[src];toggle_on=[bit]'>Yes</a></td> <td>" + span_linkOn(span_bold("No")) + "</td>"
/datum/category_item/player_setup_item/vore/persistence/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["toggle_on"])
var/bit = text2num(href_list["toggle_on"])
pref.persistence_settings |= bit
return TOPIC_REFRESH
else if(href_list["toggle_off"])
var/bit = text2num(href_list["toggle_off"])
pref.persistence_settings &= ~bit
return TOPIC_REFRESH
return ..()
@@ -1,48 +0,0 @@
// Define a place to save in character setup
/datum/preferences
var/vantag_volunteer = 0 // What state I want to be in, in terms of being affected by antags.
var/vantag_preference = VANTAG_NONE // Whether I'd like to volunteer to be an antag at some point.
// Definition of the stuff for Sizing
/datum/category_item/player_setup_item/vore/vantag
name = "VS Events"
sort_order = 6
/datum/category_item/player_setup_item/vore/vantag/load_character(list/save_data)
pref.vantag_volunteer = save_data["vantag_volunteer"]
pref.vantag_preference = save_data["vantag_preference"]
/datum/category_item/player_setup_item/vore/vantag/save_character(list/save_data)
save_data["vantag_volunteer"] = pref.vantag_volunteer
save_data["vantag_preference"] = pref.vantag_preference
/datum/category_item/player_setup_item/vore/vantag/sanitize_character()
pref.vantag_volunteer = sanitize_integer(pref.vantag_volunteer, 0, 1, initial(pref.vantag_volunteer))
pref.vantag_preference = sanitize_inlist(pref.vantag_preference, GLOB.vantag_choices_list, initial(pref.vantag_preference))
/datum/category_item/player_setup_item/vore/vantag/copy_to_mob(var/mob/living/carbon/human/character)
if(character && !istype(character,/mob/living/carbon/human/dummy))
character.vantag_pref = pref.vantag_preference
BITSET(character.hud_updateflag, VANTAG_HUD)
/datum/category_item/player_setup_item/vore/vantag/content(var/mob/user)
. += "<br>"
. += span_bold("Event Volunteer:") + " <a [pref.vantag_volunteer ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_vantag_volunteer=1'>" + span_bold("[pref.vantag_volunteer ? "Yes" : "No"]") + "</a><br>"
. += span_bold("Event Pref:") + " <a href='byond://?src=\ref[src];change_vantag=1'>" + span_bold("[GLOB.vantag_choices_list[pref.vantag_preference]]") + "</a><br>"
/datum/category_item/player_setup_item/vore/vantag/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["toggle_vantag_volunteer"])
pref.vantag_volunteer = pref.vantag_volunteer ? 0 : 1
return TOPIC_REFRESH
else if(href_list["change_vantag"])
var/list/names_list = list()
for(var/C in GLOB.vantag_choices_list)
names_list[GLOB.vantag_choices_list[C]] = C
var/selection = tgui_input_list(user, "How do you want to be involved with VS Event Characters, ERP-wise? They will see this choice on you in a HUD. Event characters are admin-selected and spawned players, possibly with assigned objectives, who are obligated to respect ERP prefs and RP their actions like any other player, though it may be a slightly shorter RP if they are pressed for time or being caught.", "Event Preference", names_list)
if(selection && selection != "Normal")
pref.vantag_preference = names_list[selection]
return TOPIC_REFRESH
return ..()
@@ -1,651 +0,0 @@
#define POSITIVE_MODE 1
#define NEUTRAL_MODE 2
#define NEGATIVE_MODE 3
var/global/list/valid_bloodreagents = list("default",REAGENT_ID_IRON,REAGENT_ID_COPPER,REAGENT_ID_PHORON,REAGENT_ID_SILVER,REAGENT_ID_GOLD,REAGENT_ID_SLIMEJELLY) //allowlist-based so people don't make their blood restored by alcohol or something really silly. use reagent IDs!
/datum/preferences
var/custom_species // Custom species name, can't be changed due to it having been used in savefiles already.
var/custom_base // What to base the custom species on
var/blood_color = "#A10808"
var/custom_say = null
var/custom_whisper = null
var/custom_ask = null
var/custom_exclaim = null
var/list/custom_heat = list()
var/list/custom_cold = list()
var/list/pos_traits = list() // What traits they've selected for their custom species
var/list/neu_traits = list()
var/list/neg_traits = list()
var/traits_cheating = 0 //Varedit by admins allows saving new maximums on people who apply/etc
var/starting_trait_points = 0
var/max_traits = MAX_SPECIES_TRAITS
var/dirty_synth = 0 //Are you a synth
var/gross_meatbag = 0 //Where'd I leave my Voight-Kampff test kit?
/datum/preferences/proc/get_custom_bases_for_species(var/new_species)
if (!new_species)
new_species = species
var/list/choices
var/datum/species/spec = GLOB.all_species[new_species]
if (spec.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER)
choices = spec.get_valid_shapeshifter_forms()
choices = choices.Copy()
else if (spec.selects_bodytype == SELECTS_BODYTYPE_CUSTOM)
choices = GLOB.custom_species_bases.Copy()
if(new_species != SPECIES_CUSTOM)
choices = (choices | new_species)
return choices
/datum/category_item/player_setup_item/vore/traits/proc/get_html_for_trait(var/datum/trait/trait, var/list/trait_prefs = null)
. = ""
if (!LAZYLEN(trait.has_preferences))
return
. = "<br><ul>"
var/altered = FALSE
if (!LAZYLEN(trait_prefs))
trait_prefs = trait.get_default_prefs()
altered = TRUE
for (var/identifier in trait.has_preferences)
var/pref_list = trait.has_preferences[identifier] //faster
if (LAZYLEN(pref_list) >= 2)
if (!(identifier in trait_prefs))
trait_prefs[identifier] = trait.default_value_for_pref(identifier) //won't be called at all often
altered = TRUE
. += "<li>- [pref_list[2]]:"
var/link = " <a href='byond://?src=\ref[src];clicked_trait_pref=[trait.type];pref=[identifier]'>"
switch (pref_list[1])
if (1) //TRAIT_PREF_TYPE_BOOLEAN
. += link + (trait_prefs[identifier] ? "Enabled" : "Disabled")
if (2) //TRAIT_PREF_TYPE_COLOR
. += " " + color_square(hex = trait_prefs[identifier]) + link + "Change"
if (3) //TRAIT_PREF_TYPE_STRING
var/string = trait_prefs[identifier]
. += link + (length(string) > 0 ? string : "\[Empty\]")
. += "</a></li>"
. += "</ul>"
if (altered)
switch(trait.category)
if (1) //TRAIT_TYPE_POSITIVE
pref.pos_traits[trait.type] = trait_prefs
if (0) //TRAIT_TYPE_NEUTRAL
pref.neu_traits[trait.type] = trait_prefs
if (-1)//TRAIT_TYPE_NEGATIVE
pref.neg_traits[trait.type] = trait_prefs
/datum/category_item/player_setup_item/vore/traits/proc/get_pref_choice_from_trait(var/mob/user, var/datum/trait/trait, var/preference)
if (!trait || !preference)
return
var/list/trait_prefs
var/datum/trait/instance = GLOB.all_traits[trait]
var/list/traitlist
switch(instance.category)
if (1)
traitlist = pref.pos_traits
if (0)
traitlist = pref.neu_traits
if (-1)
traitlist = pref.neg_traits
if (!LAZYLEN(instance.has_preferences) || !(preference in instance.has_preferences) || !traitlist)
return
if (!LAZYLEN(traitlist[trait]))
traitlist[trait] = instance.get_default_prefs()
trait_prefs = traitlist[trait]
if (!(preference in trait_prefs))
trait_prefs[preference] = instance.default_value_for_pref(preference) //won't be called at all often
switch(instance.has_preferences[preference][1])
if (1) //TRAIT_PREF_TYPE_BOOLEAN
trait_prefs[preference] = !trait_prefs[preference]
if (2) //TRAIT_PREF_TYPE_COLOR
var/new_color = tgui_color_picker(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference])
if (new_color)
trait_prefs[preference] = new_color
if (3) //TRAIT_PREF_TYPE_STRING
var/new_string = instance.apply_sanitization_to_string(preference, tgui_input_text(user, "What should the new value be?", instance.has_preferences[preference][2], trait_prefs[preference], MAX_NAME_LEN))
trait_prefs[preference] = new_string
// Definition of the stuff for Ears
/datum/category_item/player_setup_item/vore/traits
name = "Traits"
sort_order = 7
/datum/category_item/player_setup_item/vore/traits/load_character(list/save_data)
pref.custom_species = save_data["custom_species"]
pref.custom_base = save_data["custom_base"]
pref.pos_traits = text2path_list(save_data["pos_traits"])
pref.neu_traits = text2path_list(save_data["neu_traits"])
pref.neg_traits = text2path_list(save_data["neg_traits"])
pref.blood_color = save_data["blood_color"]
pref.blood_reagents = save_data["blood_reagents"]
pref.traits_cheating = save_data["traits_cheating"]
pref.max_traits = save_data["max_traits"]
pref.starting_trait_points = save_data["trait_points"]
pref.custom_say = save_data["custom_say"]
pref.custom_whisper = save_data["custom_whisper"]
pref.custom_ask = save_data["custom_ask"]
pref.custom_exclaim = save_data["custom_exclaim"]
pref.custom_heat = check_list_copy(save_data["custom_heat"])
pref.custom_cold = check_list_copy(save_data["custom_cold"])
/datum/category_item/player_setup_item/vore/traits/save_character(list/save_data)
save_data["custom_species"] = pref.custom_species
save_data["custom_base"] = pref.custom_base
save_data["pos_traits"] = check_list_copy(pref.pos_traits)
save_data["neu_traits"] = check_list_copy(pref.neu_traits)
save_data["neg_traits"] = check_list_copy(pref.neg_traits)
save_data["blood_color"] = pref.blood_color
save_data["blood_reagents"] = pref.blood_reagents
save_data["traits_cheating"] = pref.traits_cheating
save_data["max_traits"] = pref.max_traits
save_data["trait_points"] = pref.starting_trait_points
save_data["custom_say"] = pref.custom_say
save_data["custom_whisper"] = pref.custom_whisper
save_data["custom_ask"] = pref.custom_ask
save_data["custom_exclaim"] = pref.custom_exclaim
save_data["custom_heat"] = check_list_copy(pref.custom_heat)
save_data["custom_cold"] = check_list_copy(pref.custom_cold)
/datum/category_item/player_setup_item/vore/traits/sanitize_character()
if(!pref.pos_traits) pref.pos_traits = list()
if(!pref.neu_traits) pref.neu_traits = list()
if(!pref.neg_traits) pref.neg_traits = list()
pref.blood_color = sanitize_hexcolor(pref.blood_color, default="#A10808")
pref.blood_reagents = sanitize_text(pref.blood_reagents, initial(pref.blood_reagents))
if(!pref.traits_cheating)
var/datum/species/S = GLOB.all_species[pref.species]
if(S)
pref.starting_trait_points = S.trait_points
else
pref.starting_trait_points = 0
pref.max_traits = MAX_SPECIES_TRAITS
if(pref.organ_data[O_BRAIN]) //Checking if we have a synth on our hands, boys.
pref.dirty_synth = 1
pref.gross_meatbag = 0
else
pref.gross_meatbag = 1
pref.dirty_synth = 0
// Clean up positive traits
for(var/datum/trait/path as anything in pref.pos_traits)
if(!(path in GLOB.positive_traits))
pref.pos_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_positive))
pref.pos_traits -= path
continue
var/take_flags = initial(path.can_take)
if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS)))
pref.pos_traits -= path
//Neutral traits
for(var/datum/trait/path as anything in pref.neu_traits)
if(!(path in GLOB.neutral_traits))
to_world_log("removing [path] for not being in neutral_traits")
pref.neu_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_neutral))
to_world_log("removing [path] for not being a custom species")
pref.neu_traits -= path
continue
var/take_flags = initial(path.can_take)
if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS)))
to_world_log("removing [path] for being a dirty synth")
pref.neu_traits -= path
//Negative traits
for(var/datum/trait/path as anything in pref.neg_traits)
if(!(path in GLOB.negative_traits))
pref.neg_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in GLOB.everyone_traits_negative))
pref.neg_traits -= path
continue
var/take_flags = initial(path.can_take)
if((pref.dirty_synth && !(take_flags & SYNTHETICS)) || (pref.gross_meatbag && !(take_flags & ORGANICS)))
pref.neg_traits -= path
var/datum/species/selected_species = GLOB.all_species[pref.species]
if(selected_species.selects_bodytype)
if (!(pref.custom_base in pref.get_custom_bases_for_species()))
pref.custom_base = SPECIES_HUMAN
//otherwise, allowed!
else if(!pref.custom_base || !(pref.custom_base in GLOB.custom_species_bases))
pref.custom_base = SPECIES_HUMAN
pref.custom_say = lowertext(trim(pref.custom_say))
pref.custom_whisper = lowertext(trim(pref.custom_whisper))
pref.custom_ask = lowertext(trim(pref.custom_ask))
pref.custom_exclaim = lowertext(trim(pref.custom_exclaim))
if (islist(pref.custom_heat)) //don't bother checking these for actual singular message length, they should already have been checked and it'd take too long every time it's sanitized
if (length(pref.custom_heat) > 10)
pref.custom_heat.Cut(11)
else
pref.custom_heat = list()
if (islist(pref.custom_cold))
if (length(pref.custom_cold) > 10)
pref.custom_cold.Cut(11)
else
pref.custom_cold = list()
/datum/category_item/player_setup_item/vore/traits/copy_to_mob(var/mob/living/carbon/human/character)
character.custom_species = pref.custom_species
character.custom_say = lowertext(trim(pref.custom_say))
character.custom_ask = lowertext(trim(pref.custom_ask))
character.custom_whisper = lowertext(trim(pref.custom_whisper))
character.custom_exclaim = lowertext(trim(pref.custom_exclaim))
character.custom_heat = pref.custom_heat
character.custom_cold = pref.custom_cold
if(character.isSynthetic()) //Checking if we have a synth on our hands, boys.
pref.dirty_synth = 1
pref.gross_meatbag = 0
else
pref.gross_meatbag = 1
pref.dirty_synth = 0
var/datum/species/S = character.species
var/datum/species/new_S = S.produceCopy(pref.pos_traits + pref.neu_traits + pref.neg_traits, character, pref.custom_base, TRUE)
for(var/datum/trait/T in new_S.traits)
T.apply_pref(src)
//Any additional non-trait settings can be applied here
new_S.blood_color = pref.blood_color
if(!(pref.blood_reagents == "default"))
new_S.blood_reagents = pref.blood_reagents
//Any additional non-trait settings can be applied here
new_S.blood_color = pref.blood_color
var/species_sounds_to_copy = pref.species_sound // What sounds are we using?
if(species_sounds_to_copy == "Unset") // Are we unset?
species_sounds_to_copy = select_default_species_sound(pref) // This will also grab gendered versions of the sounds, if they exist.
new_S.species_sounds = species_sounds_to_copy // Now we send our sounds over to the mob
if(pref.species == SPECIES_CUSTOM)
//Statistics for this would be nice
var/english_traits = english_list(new_S.traits, and_text = ";", comma_text = ";")
log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet
/datum/category_item/player_setup_item/vore/traits/content(var/mob/user)
. += span_bold("Custom Species Name:") + " "
. += "<a href='byond://?src=\ref[src];custom_species=1'>[pref.custom_species ? pref.custom_species : "-Input Name-"]</a><br>"
var/datum/species/selected_species = GLOB.all_species[pref.species]
if(selected_species.selects_bodytype)
. += span_bold("Icon Base:") + " "
. += "<a href='byond://?src=\ref[src];custom_base=1'>[pref.custom_base ? pref.custom_base : "Human"]</a><br>"
var/traits_left = pref.max_traits
var/points_left = pref.starting_trait_points
for(var/T in pref.pos_traits + pref.neg_traits)
points_left -= GLOB.traits_costs[T]
if(T in pref.pos_traits)
traits_left--
. += span_bold("Traits Left:") + " [traits_left]<br>"
. += span_bold("Points Left:") + " [points_left]<br>"
if(points_left < 0 || traits_left < 0 || (!pref.custom_species && pref.species == SPECIES_CUSTOM))
. += span_red(span_bold("^ Fix things! ^")) + "<br>"
. += "<a href='byond://?src=\ref[src];add_trait=[POSITIVE_MODE]'>Positive Trait(s) (Limited) +</a><br>"
. += "<ul>"
for(var/T in pref.pos_traits)
var/datum/trait/trait = GLOB.positive_traits[T]
. += "<li>- <a href='byond://?src=\ref[src];clicked_pos_trait=[T]'>[trait.name] ([trait.cost])</a> [get_html_for_trait(trait, pref.pos_traits[T])]</li>"
. += "</ul>"
. += "<a href='byond://?src=\ref[src];add_trait=[NEUTRAL_MODE]'>Neutral Trait(s) (No Limit) +</a><br>"
. += "<ul>"
for(var/T in pref.neu_traits)
var/datum/trait/trait = GLOB.neutral_traits[T]
. += "<li>- <a href='byond://?src=\ref[src];clicked_neu_trait=[T]'>[trait.name] ([trait.cost])</a> [get_html_for_trait(trait, pref.neu_traits[T])]</li>"
. += "</ul>"
. += "<a href='byond://?src=\ref[src];add_trait=[NEGATIVE_MODE]'>Negative Trait(s) (No Limit) +</a><br>"
. += "<ul>"
for(var/T in pref.neg_traits)
var/datum/trait/trait = GLOB.negative_traits[T]
. += "<li>- <a href='byond://?src=\ref[src];clicked_neg_trait=[T]'>[trait.name] ([trait.cost])</a> [get_html_for_trait(trait, pref.neg_traits[T])]</li>"
. += "</ul>"
. += span_bold("Blood Color: ") //People that want to use a certain species to have that species traits (xenochimera/promethean/spider) should be able to set their own blood color.
. += "<a href='byond://?src=\ref[src];blood_color=1'>Set Color <font color='[pref.blood_color]'>&#9899;</font></a>"
. += "<a href='byond://?src=\ref[src];blood_reset=1'>R</a><br>"
. += span_bold("Blood Reagent: ") //Wanna be copper-based? Go ahead.
. += "<a href='byond://?src=\ref[src];blood_reagents=1'>[pref.blood_reagents]</a><br>"
. += "<br>"
. += span_bold("Custom Say: ")
. += "<a href='byond://?src=\ref[src];custom_say=1'>Set Say Verb</a>"
. += "(<a href='byond://?src=\ref[src];reset_say=1'>Reset</A>)"
. += "<br>"
. += span_bold("Custom Whisper: ")
. += "<a href='byond://?src=\ref[src];custom_whisper=1'>Set Whisper Verb</a>"
. += "(<a href='byond://?src=\ref[src];reset_whisper=1'>Reset</A>)"
. += "<br>"
. += span_bold("Custom Ask: ")
. += "<a href='byond://?src=\ref[src];custom_ask=1'>Set Ask Verb</a>"
. += "(<a href='byond://?src=\ref[src];reset_ask=1'>Reset</A>)"
. += "<br>"
. += span_bold("Custom Exclaim: ")
. += "<a href='byond://?src=\ref[src];custom_exclaim=1'>Set Exclaim Verb</a>"
. += "(<a href='byond://?src=\ref[src];reset_exclaim=1'>Reset</A>)"
. += "<br>"
. += span_bold("Custom Heat Discomfort: ")
. += "<a href='byond://?src=\ref[src];custom_heat=1'>Set Heat Messages</a>"
. += "(<a href='byond://?src=\ref[src];reset_heat=1'>Reset</A>)"
. += "<br>"
. += span_bold("Custom Cold Discomfort: ")
. += "<a href='byond://?src=\ref[src];custom_cold=1'>Set Cold Messages</a>"
. += "(<a href='byond://?src=\ref[src];reset_cold=1'>Reset</A>)"
. += "<br>"
/datum/category_item/player_setup_item/vore/traits/OnTopic(var/href,var/list/href_list, var/mob/user)
if(!CanUseTopic(user))
return TOPIC_NOACTION
else if(href_list["custom_species"])
var/raw_choice = sanitize(tgui_input_text(user, "Input your custom species name:",
"Character Preference", pref.custom_species, MAX_NAME_LEN), MAX_NAME_LEN)
if (CanUseTopic(user))
pref.custom_species = raw_choice
return TOPIC_REFRESH
else if(href_list["custom_base"])
var/list/choices = pref.get_custom_bases_for_species()
var/text_choice = tgui_input_list(user, "Pick an icon set for your species:","Icon Base", choices)
if(text_choice in choices)
pref.custom_base = text_choice
return TOPIC_REFRESH_UPDATE_PREVIEW
else if(href_list["blood_color"])
var/color_choice = tgui_color_picker(user, "Pick a blood color (does not apply to synths)","Blood Color",pref.blood_color)
if(color_choice)
pref.blood_color = sanitize_hexcolor(color_choice, default="#A10808")
return TOPIC_REFRESH
else if(href_list["blood_reset"])
var/datum/species/spec = GLOB.all_species[pref.species]
var/new_blood = spec.blood_color ? spec.blood_color : "#A10808"
var/choice = tgui_alert(user, "Reset blood color to species default ([new_blood])?","Reset Blood Color",list("Reset","Cancel"))
if(choice == "Reset")
pref.blood_color = new_blood
return TOPIC_REFRESH
else if(href_list["blood_reagents"])
var/new_blood_reagents = tgui_input_list(user, "Choose your character's blood restoration reagent:", "Character Preference", valid_bloodreagents)
if(new_blood_reagents && CanUseTopic(user))
pref.blood_reagents = new_blood_reagents
return TOPIC_REFRESH
else if(href_list["clicked_pos_trait"])
var/datum/trait/trait = text2path(href_list["clicked_pos_trait"])
var/choice = tgui_alert(user, "Remove [initial(trait.name)] and regain [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.pos_traits -= trait
var/datum/trait/instance = GLOB.all_traits[trait]
instance.remove_pref(pref)
return TOPIC_REFRESH
else if(href_list["clicked_neu_trait"])
var/datum/trait/trait = text2path(href_list["clicked_neu_trait"])
var/choice = tgui_alert(user, "Remove [initial(trait.name)]?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.neu_traits -= trait
var/datum/trait/instance = GLOB.all_traits[trait]
instance.remove_pref(pref)
return TOPIC_REFRESH
else if(href_list["clicked_neg_trait"])
var/datum/trait/trait = text2path(href_list["clicked_neg_trait"])
var/choice = tgui_alert(user, "Remove [initial(trait.name)] and lose [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.neg_traits -= trait
var/datum/trait/instance = GLOB.all_traits[trait]
instance.remove_pref(pref)
return TOPIC_REFRESH
else if(href_list["clicked_trait_pref"])
var/datum/trait/trait = text2path(href_list["clicked_trait_pref"])
get_pref_choice_from_trait(user, trait, href_list["pref"])
return TOPIC_REFRESH
else if(href_list["custom_say"])
var/say_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'says': [pref.real_name] says, \"Hi.\"", "Custom Say", pref.custom_say, 12), 12)
if(say_choice)
pref.custom_say = say_choice
return TOPIC_REFRESH
else if(href_list["custom_whisper"])
var/whisper_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'whispers': [pref.real_name] whispers, \"Hi...\"", "Custom Whisper", pref.custom_whisper, 12), 12)
if(whisper_choice)
pref.custom_whisper = whisper_choice
return TOPIC_REFRESH
else if(href_list["custom_ask"])
var/ask_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'asks': [pref.real_name] asks, \"Hi?\"", "Custom Ask", pref.custom_ask, 12), 12)
if(ask_choice)
pref.custom_ask = ask_choice
return TOPIC_REFRESH
else if(href_list["custom_exclaim"])
var/exclaim_choice = sanitize(tgui_input_text(user, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [pref.real_name] exclaims, \"Hi!\"", "Custom Exclaim", pref.custom_exclaim, 12), 12)
if(exclaim_choice)
pref.custom_exclaim = exclaim_choice
return TOPIC_REFRESH
else if(href_list["custom_heat"])
tgui_alert(user, "You are setting custom heat messages. These will overwrite your species' defaults. To return to defaults, click reset.")
var/old_message = pref.custom_heat.Join("\n\n")
var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Heat Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0)
if(length(new_message) > 0)
var/list/raw_list = splittext(new_message,"\n\n")
if(raw_list.len > 10)
raw_list.Cut(11)
for(var/i = 1, i <= raw_list.len, i++)
if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160)
raw_list.Cut(i,i)
else
raw_list[i] = readd_quotes(raw_list[i])
ASSERT(raw_list.len <= 10)
pref.custom_heat = raw_list
return TOPIC_REFRESH
else if(href_list["custom_cold"])
tgui_alert(user, "You are setting custom cold messages. These will overwrite your species' defaults. To return to defaults, click reset.")
var/old_message = pref.custom_heat.Join("\n\n")
var/new_message = sanitize(tgui_input_text(user,"Use double enter between messages to enter a new one. Must be at least 3 characters long, 160 characters max and up to 10 messages are allowed.","Cold Discomfort messages",old_message, multiline= TRUE, prevent_enter = TRUE), MAX_MESSAGE_LEN,0,0,0)
if(length(new_message) > 0)
var/list/raw_list = splittext(new_message,"\n\n")
if(raw_list.len > 10)
raw_list.Cut(11)
for(var/i = 1, i <= raw_list.len, i++)
if(length(raw_list[i]) < 3 || length(raw_list[i]) > 160)
raw_list.Cut(i,i)
else
raw_list[i] = readd_quotes(raw_list[i])
ASSERT(raw_list.len <= 10)
pref.custom_cold = raw_list
return TOPIC_REFRESH
else if(href_list["reset_say"])
var/say_choice = tgui_alert(user, "Reset your Custom Say Verb?","Reset Verb",list("Yes","No"))
if(say_choice == "Yes")
pref.custom_say = null
return TOPIC_REFRESH
else if(href_list["reset_whisper"])
var/whisper_choice = tgui_alert(user, "Reset your Custom Whisper Verb?","Reset Verb",list("Yes","No"))
if(whisper_choice == "Yes")
pref.custom_whisper = null
return TOPIC_REFRESH
else if(href_list["reset_ask"])
var/ask_choice = tgui_alert(user, "Reset your Custom Ask Verb?","Reset Verb",list("Yes","No"))
if(ask_choice == "Yes")
pref.custom_ask = null
return TOPIC_REFRESH
else if(href_list["reset_exclaim"])
var/exclaim_choice = tgui_alert(user, "Reset your Custom Exclaim Verb?","Reset Verb",list("Yes","No"))
if(exclaim_choice == "Yes")
pref.custom_exclaim = null
return TOPIC_REFRESH
else if(href_list["reset_cold"])
var/cold_choice = tgui_alert(user, "Reset your Custom Cold Discomfort messages?", "Reset Discomfort",list("Yes","No"))
if(cold_choice == "Yes")
pref.custom_cold = list()
return TOPIC_REFRESH
else if(href_list["reset_heat"])
var/heat_choice = tgui_alert(user, "Reset your Custom Heat Discomfort messages?", "Reset Discomfort",list("Yes","No"))
if(heat_choice == "Yes")
pref.custom_heat = list()
return TOPIC_REFRESH
else if(href_list["add_trait"])
var/mode = text2num(href_list["add_trait"])
var/list/picklist
var/list/mylist
switch(mode)
if(POSITIVE_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = GLOB.positive_traits.Copy() - pref.pos_traits
mylist = pref.pos_traits
else
picklist = GLOB.everyone_traits_positive.Copy() - pref.pos_traits
mylist = pref.pos_traits
if(NEUTRAL_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = GLOB.neutral_traits.Copy() - pref.neu_traits
mylist = pref.neu_traits
else
picklist = GLOB.everyone_traits_neutral.Copy() - pref.neu_traits
mylist = pref.neu_traits
if(NEGATIVE_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = GLOB.negative_traits.Copy() - pref.neg_traits
mylist = pref.neg_traits
else
picklist = GLOB.everyone_traits_negative.Copy() - pref.neg_traits
mylist = pref.neg_traits
if(isnull(picklist))
return TOPIC_REFRESH
if(isnull(mylist))
return TOPIC_REFRESH
var/list/nicelist = list()
for(var/P in picklist)
var/datum/trait/T = picklist[P]
nicelist[T.name] = P
var/points_left = pref.starting_trait_points
for(var/T in pref.pos_traits + pref.neu_traits + pref.neg_traits)
points_left -= GLOB.traits_costs[T]
var/traits_left = pref.max_traits - pref.pos_traits.len
var/message = "Select a trait to learn more."
if(mode != NEUTRAL_MODE)
message = "\[Remaining: [points_left] points, [traits_left] traits\]\n" + message
var/title = "Traits"
switch(mode)
if(POSITIVE_MODE)
title = "Positive Traits"
if(NEUTRAL_MODE)
title = "Neutral Traits"
if(NEGATIVE_MODE)
title = "Negative Traits"
var/trait_choice
var/done = FALSE
while(!done)
trait_choice = tgui_input_list(user, message, title, nicelist)
if(!trait_choice)
done = TRUE
if(trait_choice in nicelist)
var/datum/trait/path = nicelist[trait_choice]
var/choice = tgui_alert(user, "\[Cost:[initial(path.cost)]\] [initial(path.desc)]",initial(path.name), list("Take Trait","Go Back"))
if(choice == "Take Trait")
done = TRUE
if(!trait_choice)
return TOPIC_REFRESH
else if(trait_choice in nicelist)
var/datum/trait/path = nicelist[trait_choice]
var/datum/trait/instance = GLOB.all_traits[path]
var/conflict = FALSE
if(pref.dirty_synth && !(instance.can_take & SYNTHETICS))
tgui_alert_async(user, "The trait you've selected can only be taken by organic characters!", "Error")
return TOPIC_REFRESH
if(pref.gross_meatbag && !(instance.can_take & ORGANICS))
tgui_alert_async(user, "The trait you've selected can only be taken by synthetic characters!", "Error")
return TOPIC_REFRESH
if(pref.species in instance.banned_species)
tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error")
return TOPIC_REFRESH
if( LAZYLEN(instance.allowed_species) && !(pref.species in instance.allowed_species))
tgui_alert_async(user, "The trait you've selected cannot be taken by the species you've chosen!", "Error")
return TOPIC_REFRESH
if(trait_choice in (pref.pos_traits + pref.neu_traits + pref.neg_traits))
conflict = instance.name
varconflict:
for(var/P in (pref.pos_traits + pref.neu_traits + pref.neg_traits))
var/datum/trait/instance_test = GLOB.all_traits[P]
if(path in instance_test.excludes)
conflict = instance_test.name
break varconflict
for(var/V in instance.var_changes)
if(V == "flags")
continue
if(V in instance_test.var_changes)
conflict = instance_test.name
break varconflict
for(var/V in instance.var_changes_pref)
if(V in instance_test.var_changes_pref)
conflict = instance_test.name
break varconflict
if(conflict)
tgui_alert_async(user, "You cannot take this trait and [conflict] at the same time. Please remove that trait, or pick another trait to add.", "Error")
return TOPIC_REFRESH
instance.apply_pref(pref)
mylist[path] = instance.get_default_prefs()
return TOPIC_REFRESH
return ..()
#undef POSITIVE_MODE
#undef NEUTRAL_MODE
#undef NEGATIVE_MODE
@@ -1,115 +0,0 @@
/datum/category_item/player_setup_item/vore/misc
name = "Misc Settings"
sort_order = 9
/datum/category_item/player_setup_item/vore/misc/load_character(list/save_data)
pref.show_in_directory = save_data["show_in_directory"]
pref.directory_tag = save_data["directory_tag"]
pref.directory_gendertag = save_data["directory_gendertag"]
pref.directory_sexualitytag = save_data["directory_sexualitytag"]
pref.directory_erptag = save_data["directory_erptag"]
pref.directory_ad = save_data["directory_ad"]
pref.sensorpref = save_data["sensorpref"]
pref.capture_crystal = save_data["capture_crystal"]
pref.auto_backup_implant = save_data["auto_backup_implant"]
pref.borg_petting = save_data["borg_petting"]
/datum/category_item/player_setup_item/vore/misc/save_character(list/save_data)
save_data["show_in_directory"] = pref.show_in_directory
save_data["directory_tag"] = pref.directory_tag
save_data["directory_gendertag"] = pref.directory_gendertag
save_data["directory_sexualitytag"] = pref.directory_sexualitytag
save_data["directory_erptag"] = pref.directory_erptag
save_data["directory_ad"] = pref.directory_ad
save_data["sensorpref"] = pref.sensorpref
save_data["capture_crystal"] = pref.capture_crystal
save_data["auto_backup_implant"] = pref.auto_backup_implant
save_data["borg_petting"] = pref.borg_petting
/datum/category_item/player_setup_item/vore/misc/copy_to_mob(var/mob/living/carbon/human/character)
if(pref.sensorpref > 5 || pref.sensorpref < 1)
pref.sensorpref = 5
character.sensorpref = pref.sensorpref
character.capture_crystal = pref.capture_crystal
//Vore Stomach Sprite Preference
character.recalculate_vis()
/datum/category_item/player_setup_item/vore/misc/sanitize_character()
pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory))
pref.directory_tag = sanitize_inlist(pref.directory_tag, GLOB.char_directory_tags, initial(pref.directory_tag))
pref.directory_gendertag = sanitize_inlist(pref.directory_gendertag, GLOB.char_directory_gendertags, initial(pref.directory_gendertag))
pref.directory_sexualitytag = sanitize_inlist(pref.directory_sexualitytag, GLOB.char_directory_sexualitytags, initial(pref.directory_sexualitytag))
pref.directory_erptag = sanitize_inlist(pref.directory_erptag, GLOB.char_directory_erptags, initial(pref.directory_erptag))
pref.sensorpref = sanitize_integer(pref.sensorpref, 1, GLOB.sensorpreflist.len, initial(pref.sensorpref))
pref.capture_crystal = sanitize_integer(pref.capture_crystal, 0, 1, initial(pref.capture_crystal))
pref.auto_backup_implant = sanitize_integer(pref.auto_backup_implant, 0, 1, initial(pref.auto_backup_implant))
pref.borg_petting = sanitize_integer(pref.borg_petting, 0, 1, initial(pref.borg_petting))
/datum/category_item/player_setup_item/vore/misc/content(var/mob/user)
. += "<br>"
. += span_bold("Appear in Character Directory:") + " <a [pref.show_in_directory ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_show_in_directory=1'><b>[pref.show_in_directory ? "Yes" : "No"]</b></a><br>"
. += span_bold("Character Directory Vore Tag:") + " <a href='byond://?src=\ref[src];directory_tag=1'><b>[pref.directory_tag]</b></a><br>"
. += span_bold("Character Directory Gender:") + " <a href='byond://?src=\ref[src];directory_gendertag=1'><b>[pref.directory_gendertag]</b></a><br>"
. += span_bold("Character Directory Sexuality:") + " <a href='byond://?src=\ref[src];directory_sexualitytag=1'><b>[pref.directory_sexualitytag]</b></a><br>"
. += span_bold("Character Directory ERP Tag:") + " <a href='byond://?src=\ref[src];directory_erptag=1'><b>[pref.directory_erptag]</b></a><br>"
. += span_bold("Character Directory Advertisement:") + " <a href='byond://?src=\ref[src];directory_ad=1'><b>Set Directory Ad</b></a><br>"
. += span_bold("Suit Sensors Preference:") + " <a [pref.sensorpref ? "" : ""] href='byond://?src=\ref[src];toggle_sensor_setting=1'><b>[GLOB.sensorpreflist[pref.sensorpref]]</b></a><br>"
. += span_bold("Capture Crystal Preference:") + " <a [pref.capture_crystal ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_capture_crystal=1'><b>[pref.capture_crystal ? "Yes" : "No"]</b></a><br>"
. += span_bold("Spawn With Backup Implant:") + " <a [pref.auto_backup_implant ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_implant=1'><b>[pref.auto_backup_implant ? "Yes" : "No"]</b></a><br>"
. += span_bold("Allow petting as robot:") + " <a [pref.borg_petting ? "class='linkOn'" : ""] href='byond://?src=\ref[src];toggle_borg_petting=1'><b>[pref.borg_petting ? "Yes" : "No"]</b></a><br>"
if(CONFIG_GET(flag/allow_metadata))
. += span_bold("Private Notes: <a href='byond://?src=\ref[src];edit_private_notes=1'>Edit</a>") + "<br>"
/datum/category_item/player_setup_item/vore/misc/OnTopic(var/href, var/list/href_list, var/mob/user)
if(href_list["toggle_show_in_directory"])
pref.show_in_directory = pref.show_in_directory ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["directory_tag"])
var/new_tag = tgui_input_list(user, "Pick a new Vore tag for the character directory", "Character Vore Tag", GLOB.char_directory_tags, pref.directory_tag)
if(!new_tag)
return
pref.directory_tag = new_tag
return TOPIC_REFRESH
else if(href_list["directory_gendertag"])
var/new_gendertag = tgui_input_list(user, "Pick a new Gender tag for the character directory. This is YOUR gender, not what you prefer.", "Character Gender Tag", GLOB.char_directory_gendertags, pref.directory_gendertag)
if(!new_gendertag)
return
pref.directory_gendertag = new_gendertag
return TOPIC_REFRESH
else if(href_list["directory_sexualitytag"])
var/new_sexualitytag = tgui_input_list(user, "Pick a new Sexuality/Orientation tag for the character directory", "Character Sexuality/Orientation Tag", GLOB.char_directory_sexualitytags, pref.directory_sexualitytag)
if(!new_sexualitytag)
return
pref.directory_sexualitytag = new_sexualitytag
return TOPIC_REFRESH
else if(href_list["directory_erptag"])
var/new_erptag = tgui_input_list(user, "Pick a new ERP tag for the character directory", "Character ERP Tag", GLOB.char_directory_erptags, pref.directory_erptag)
if(!new_erptag)
return
pref.directory_erptag = new_erptag
return TOPIC_REFRESH
else if(href_list["directory_ad"])
var/msg = sanitize(tgui_input_text(user,"Write your advertisement here!", "Flavor Text", html_decode(pref.directory_ad), multiline = TRUE, prevent_enter = TRUE), extra = 0) //VOREStation Edit: separating out OOC notes
if(!msg)
return
pref.directory_ad = msg
return TOPIC_REFRESH
else if(href_list["toggle_sensor_setting"])
var/new_sensorpref = tgui_input_list(user, "Choose your character's sensor preferences:", "Character Preferences", GLOB.sensorpreflist, GLOB.sensorpreflist[pref.sensorpref])
if (!isnull(new_sensorpref) && CanUseTopic(user))
pref.sensorpref = GLOB.sensorpreflist.Find(new_sensorpref)
return TOPIC_REFRESH
else if(href_list["toggle_capture_crystal"])
pref.capture_crystal = pref.capture_crystal ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["toggle_implant"])
pref.auto_backup_implant = pref.auto_backup_implant ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["toggle_borg_petting"])
pref.borg_petting = pref.borg_petting ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["edit_private_notes"])
var/new_metadata = sanitize(tgui_input_text(user,"Write some notes for yourself. These can be anything that is useful, whether it's character events that you want to remember or a bit of lore. Things that you would normally stick in a txt file for yourself!", "Private Notes", html_decode(pref.read_preference(/datum/preference/text/living/private_notes)), multiline = TRUE, prevent_enter = TRUE), extra = 0)
if(new_metadata && CanUseTopic(user))
pref.update_preference_by_type(/datum/preference/text/living/private_notes, new_metadata)
return ..();