Files
CHOMPStation2/code/modules/client/preference_setup/vore/07_traits.dm
Rykka 25eed899d7 Trait Tweak - Positives capped at 6, Negatives Uncapped
As above.
Traits now have an unlimited amount for negatives, given they are intended to harm you.
Positives have a cap of 6 total.
Prior to this, one could do 6 positives and two extremely expensive negatives, but no other 'fun' negatives.

Now, one can still do 6 strong positives, but as many negatives as they like.
IDEALLY this will allow for custom setups with a lot of small-cost negatives for higher cost positives.

Assuming Tankiness PR comes in before this, it will need adjusting to account for this, but, assuming one powergames to get the ABSOLUTE best traits possible (using the tankiness PR):

Positive:
High Endurance - 3
Burn Resist, Major - 3
Brute Resist, Major - 3
Increased Pain Tolerance - 2
Extreme Hardy - 3
Sharp Melee - 1

Traits Left: 0
Points Left: -15

Negative:
Reduced Biocompatability, Major - -8
Conductive Major - -3
Haemophilia - -2
Extreme Photosensitivity - -2

Points Left: 0

Extremely low healing (30% effective heals, meaning a heal for 10 will only heal 3)
Double incoming taser/electrical damage
Bleed 2x as fast
Flashes stun 2x as long. (This trait in particular is badly in need of an overhaul so it's not free points on 99% of rounds).
2022-03-29 15:35:18 -06:00

408 lines
16 KiB
Plaintext

#define POSITIVE_MODE 1
#define NEUTRAL_MODE 2
#define NEGATIVE_MODE 3
#define ORGANICS 1
#define SYNTHETICS 2
/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/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 = STARTING_SPECIES_POINTS
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?
// 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(var/savefile/S)
S["custom_species"] >> pref.custom_species
S["custom_base"] >> pref.custom_base
S["pos_traits"] >> pref.pos_traits
S["neu_traits"] >> pref.neu_traits
S["neg_traits"] >> pref.neg_traits
S["blood_color"] >> pref.blood_color
S["traits_cheating"] >> pref.traits_cheating
S["max_traits"] >> pref.max_traits
S["trait_points"] >> pref.starting_trait_points
S["custom_say"] >> pref.custom_say
S["custom_whisper"] >> pref.custom_whisper
S["custom_ask"] >> pref.custom_ask
S["custom_exclaim"] >> pref.custom_exclaim
/datum/category_item/player_setup_item/vore/traits/save_character(var/savefile/S)
S["custom_species"] << pref.custom_species
S["custom_base"] << pref.custom_base
S["pos_traits"] << pref.pos_traits
S["neu_traits"] << pref.neu_traits
S["neg_traits"] << pref.neg_traits
S["blood_color"] << pref.blood_color
S["traits_cheating"] << pref.traits_cheating
S["max_traits"] << pref.max_traits
S["trait_points"] << pref.starting_trait_points
S["custom_say"] << pref.custom_say
S["custom_whisper"] << pref.custom_whisper
S["custom_ask"] << pref.custom_ask
S["custom_exclaim"] << pref.custom_exclaim
/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")
if(!pref.traits_cheating)
pref.starting_trait_points = STARTING_SPECIES_POINTS
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
if(pref.species != SPECIES_CUSTOM)
pref.pos_traits.Cut()
pref.neg_traits.Cut()
// Clean up positive traits
for(var/datum/trait/path as anything in pref.pos_traits)
if(!(path in positive_traits))
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 neutral_traits))
pref.neu_traits -= path
continue
if(!(pref.species == SPECIES_CUSTOM) && !(path in everyone_traits))
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)))
pref.neu_traits -= path
//Negative traits
for(var/datum/trait/path as anything in pref.neg_traits)
if(!(path in negative_traits))
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)
// 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))
/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))
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)
//Any additional non-trait settings can be applied here
new_S.blood_color = pref.blood_color
//Any additional non-trait settings can be applied here
new_S.blood_color = pref.blood_color
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
else
/datum/category_item/player_setup_item/vore/traits/content(var/mob/user)
. += "<b>Custom Species Name:</b> "
. += "<a href='?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)
. += "<b>Icon Base: </b> "
. += "<a href='?src=\ref[src];custom_base=1'>[pref.custom_base ? pref.custom_base : "Human"]</a><br>"
var/traits_left = pref.max_traits
if(pref.species == SPECIES_CUSTOM)
var/points_left = pref.starting_trait_points
for(var/T in pref.pos_traits + pref.neg_traits) // CHOMPEdit: Only Positive traits cost slots now.
points_left -= traits_costs[T]
for(var/T in pref.pos_traits)
traits_left--
. += "<b>Traits Left:</b> [traits_left]<br>"
. += "<b>Points Left:</b> [points_left]<br>"
if(points_left < 0 || traits_left < 0 || !pref.custom_species)
. += "<span style='color:red;'><b>^ Fix things! ^</b></span><br>"
. += "<a href='?src=\ref[src];add_trait=[POSITIVE_MODE]'>Positive Trait(s) (Limited) +</a><br>" // CHOMPEdit: More obvious/clear to players.
. += "<ul>"
for(var/T in pref.pos_traits)
var/datum/trait/trait = positive_traits[T]
. += "<li>- <a href='?src=\ref[src];clicked_pos_trait=[T]'>[trait.name] ([trait.cost])</a></li>"
. += "</ul>"
. += "<a href='?src=\ref[src];add_trait=[NEGATIVE_MODE]'>Negative Trait(s) (No Limit) +</a><br>" // CHOMPEdit: More obvious/clear to players.
. += "<ul>"
for(var/T in pref.neg_traits)
var/datum/trait/trait = negative_traits[T]
. += "<li>- <a href='?src=\ref[src];clicked_neg_trait=[T]'>[trait.name] ([trait.cost])</a></li>"
. += "</ul>"
. += "<a href='?src=\ref[src];add_trait=[NEUTRAL_MODE]'>Neutral Trait(s) (No Limit) +</a><br>" // CHOMPEdit: More obvious/clear to players.
. += "<ul>"
for(var/T in pref.neu_traits)
var/datum/trait/trait = neutral_traits[T]
. += "<li>- <a href='?src=\ref[src];clicked_neu_trait=[T]'>[trait.name] ([trait.cost])</a></li>"
. += "</ul>"
. += "<b>Blood Color: </b>" //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='?src=\ref[src];blood_color=1'>Set Color</a>"
. += "<a href='?src=\ref[src];blood_reset=1'>R</a><br>"
. += "<br>"
. += "<b>Custom Say: </b>"
. += "<a href='?src=\ref[src];custom_say=1'>Set Say Verb</a><br>"
. += "<b>Custom Whisper: </b>"
. += "<a href='?src=\ref[src];custom_whisper=1'>Set Whisper Verb</a><br>"
. += "<b>Custom Ask: </b>"
. += "<a href='?src=\ref[src];custom_ask=1'>Set Ask Verb</a><br>"
. += "<b>Custom Exclaim: </b>"
. += "<a href='?src=\ref[src];custom_exclaim=1'>Set Exclaim Verb</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(input(user, "Input your custom species name:",
"Character Preference", pref.custom_species) as null|text, MAX_NAME_LEN)
if (CanUseTopic(user))
pref.custom_species = raw_choice
return TOPIC_REFRESH
else if(href_list["custom_base"])
var/list/choices = GLOB.custom_species_bases
if(pref.species != SPECIES_CUSTOM)
choices = (choices | pref.species)
var/text_choice = tgui_input_list(usr, "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 = input(usr, "Pick a blood color (does not apply to synths)","Blood Color",pref.blood_color) as color
if(color_choice)
pref.blood_color = sanitize_hexcolor(color_choice, default="#A10808")
return TOPIC_REFRESH
else if(href_list["blood_reset"])
var/choice = tgui_alert(usr, "Reset blood color to human default (#A10808)?","Reset Blood Color",list("Reset","Cancel"))
if(choice == "Reset")
pref.blood_color = "#A10808"
return TOPIC_REFRESH
else if(href_list["clicked_pos_trait"])
var/traitpath = text2path(href_list["clicked_pos_trait"]) //CHOMPEdit
var/datum/trait/trait = text2path(href_list["clicked_pos_trait"])
var/choice = tgui_alert(usr, "Remove [initial(trait.name)] and regain [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
if(traitpath == /datum/trait/positive/linguist) //CHOMPEdit
pref.num_languages = null //CHOMPEdit
pref.pos_traits -= trait
return TOPIC_REFRESH
else if(href_list["clicked_neu_trait"])
var/datum/trait/trait = text2path(href_list["clicked_neu_trait"])
var/choice = tgui_alert(usr, "Remove [initial(trait.name)]?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.neu_traits -= trait
return TOPIC_REFRESH
else if(href_list["clicked_neg_trait"])
var/datum/trait/trait = text2path(href_list["clicked_neg_trait"])
var/choice = tgui_alert(usr, "Remove [initial(trait.name)] and lose [initial(trait.cost)] points?","Remove Trait",list("Remove","Cancel"))
if(choice == "Remove")
pref.neg_traits -= trait
return TOPIC_REFRESH
else if(href_list["custom_say"])
var/say_choice = sanitize(input(usr, "This word or phrase will appear instead of 'says': [pref.real_name] says, \"Hi.\"", "Custom Say", pref.custom_say) as null|text, 12)
if(say_choice)
pref.custom_say = say_choice
return TOPIC_REFRESH
else if(href_list["custom_whisper"])
var/whisper_choice = sanitize(input(usr, "This word or phrase will appear instead of 'whispers': [pref.real_name] whispers, \"Hi...\"", "Custom Whisper", pref.custom_whisper) as null|text, 12)
if(whisper_choice)
pref.custom_whisper = whisper_choice
return TOPIC_REFRESH
else if(href_list["custom_ask"])
var/ask_choice = sanitize(input(usr, "This word or phrase will appear instead of 'asks': [pref.real_name] asks, \"Hi?\"", "Custom Ask", pref.custom_ask) as null|text, 12)
if(ask_choice)
pref.custom_ask = ask_choice
return TOPIC_REFRESH
else if(href_list["custom_exclaim"])
var/exclaim_choice = sanitize(input(usr, "This word or phrase will appear instead of 'exclaims', 'shouts' or 'yells': [pref.real_name] exclaims, \"Hi!\"", "Custom Exclaim", pref.custom_exclaim) as null|text, 12)
if(exclaim_choice)
pref.custom_exclaim = exclaim_choice
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)
picklist = positive_traits.Copy() - pref.pos_traits
mylist = pref.pos_traits
if(NEUTRAL_MODE)
if(pref.species == SPECIES_CUSTOM)
picklist = neutral_traits.Copy() - pref.neu_traits
mylist = pref.neu_traits
else
picklist = everyone_traits.Copy() - pref.neu_traits
mylist = pref.neu_traits
if(NEGATIVE_MODE)
picklist = negative_traits.Copy() - pref.neg_traits
mylist = pref.neg_traits
else
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 -= traits_costs[T]
var/traits_left = pref.max_traits - pref.pos_traits.len // CHOMPEdit: Only positive traits have a slot limit, to prevent broken builds
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(usr, message, title, nicelist)
if(!trait_choice)
done = TRUE
if(trait_choice in nicelist)
var/datum/trait/path = nicelist[trait_choice]
var/choice = tgui_alert(usr, "\[Cost:[initial(path.cost)]\] [initial(path.desc)]",initial(path.name), list("Take Trait","Go Back"))
if(choice != "Go Back")
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 = all_traits[path]
var/conflict = FALSE
if(pref.dirty_synth && !(instance.can_take & SYNTHETICS))
tgui_alert_async(usr, "The trait you've selected can only be taken by organic characters!", "Error")
//pref.dirty_synth = 0 //Just to be sure //CHOMPEdit this shit broke, stop.
return TOPIC_REFRESH
if(pref.gross_meatbag && !(instance.can_take & ORGANICS))
tgui_alert_async(usr, "The trait you've selected can only be taken by synthetic characters!", "Error")
pref.gross_meatbag = 0 //Just to be sure
return TOPIC_REFRESH
if(pref.species in instance.banned_species)
tgui_alert_async(usr, "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(usr, "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 = all_traits[P]
if(path in instance_test.excludes)
conflict = instance_test.name
break varconflict
for(var/V in instance.var_changes)
if(V in instance_test.var_changes)
conflict = instance_test.name
break varconflict
if(conflict)
tgui_alert_async(usr, "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
if(path==/datum/trait/positive/linguist) //CHOMPEdit
pref.num_languages = 12 //CHOMPEdit
mylist += path
return TOPIC_REFRESH
return ..()
#undef POSITIVE_MODE
#undef NEUTRAL_MODE
#undef NEGATIVE_MODE