mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
Adds a Trait system (#4048)
* Works on trait system, wip. * Finalizes traits. Fixes a few bugs with medical machines assuming a max health of 100.
This commit is contained in:
@@ -68,6 +68,9 @@
|
||||
//used for optional self-objectives that antagonists can give themselves, which are displayed at the end of the round.
|
||||
var/ambitions
|
||||
|
||||
//used to store what traits the player had picked out in their preferences before joining, in text form.
|
||||
var/list/traits = list()
|
||||
|
||||
/datum/mind/New(var/key)
|
||||
src.key = key
|
||||
|
||||
|
||||
@@ -300,6 +300,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
job_master.EquipRank(player, player.mind.assigned_role, 0)
|
||||
UpdateFactionList(player)
|
||||
equip_custom_items(player)
|
||||
player.apply_traits()
|
||||
if(captainless)
|
||||
for(var/mob/M in player_list)
|
||||
if(!istype(M,/mob/new_player))
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
if(DEAD)
|
||||
data["stat"] = "<font color='red'>Dead</font>"
|
||||
data["health"] = occupant.health
|
||||
data["maxHealth"] = occupant.getMaxHealth()
|
||||
if(iscarbon(occupant))
|
||||
var/mob/living/carbon/C = occupant
|
||||
data["pulse"] = C.get_pulse(GETPULSE_TOOL)
|
||||
|
||||
@@ -266,6 +266,7 @@
|
||||
occupantData["name"] = H.name
|
||||
occupantData["stat"] = H.stat
|
||||
occupantData["health"] = H.health
|
||||
occupantData["maxHealth"] = H.getMaxHealth()
|
||||
|
||||
occupantData["hasVirus"] = H.virus2.len
|
||||
|
||||
@@ -423,7 +424,7 @@
|
||||
t1 = "Unconscious"
|
||||
else
|
||||
t1 = "*dead*"
|
||||
dat += "<font color=[occupant.health > 50 ? "blue" : "red"]>\tHealth %: [occupant.health], ([t1])</font><br>"
|
||||
dat += "<font color=[occupant.health > (occupant.getMaxHealth() / 2) ? "blue" : "red"]>\tHealth %: [(occupant.health / occupant.getMaxHealth())*100], ([t1])</font><br>"
|
||||
|
||||
if(occupant.virus2.len)
|
||||
dat += "<font color='red'>Viral pathogen detected in blood stream.</font><BR>"
|
||||
|
||||
@@ -33,9 +33,14 @@
|
||||
sort_order = 5
|
||||
category_item_type = /datum/category_item/player_setup_item/loadout
|
||||
|
||||
/datum/category_group/player_setup_category/trait_preferences
|
||||
name = "Traits"
|
||||
sort_order = 6
|
||||
category_item_type = /datum/category_item/player_setup_item/traits
|
||||
|
||||
/datum/category_group/player_setup_category/global_preferences
|
||||
name = "Global"
|
||||
sort_order = 6
|
||||
sort_order = 7
|
||||
category_item_type = /datum/category_item/player_setup_item/player_global
|
||||
|
||||
/****************************
|
||||
|
||||
212
code/modules/client/preference_setup/traits/trait_defines.dm
Normal file
212
code/modules/client/preference_setup/traits/trait_defines.dm
Normal file
@@ -0,0 +1,212 @@
|
||||
// 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
|
||||
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/generate_desc()
|
||||
var/new_desc = desc
|
||||
if(!modifier_type)
|
||||
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
|
||||
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
|
||||
muturally_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
|
||||
muturally_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
|
||||
|
||||
|
||||
/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
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
/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
|
||||
|
||||
/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 Incompatability"
|
||||
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 Incompatability"
|
||||
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
|
||||
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
|
||||
|
||||
/*
|
||||
// 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."
|
||||
muturally_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."
|
||||
muturally_exclusive = list(/datum/trait/modifier/mental/xenophobe)
|
||||
|
||||
/datum/trait/modifier/mental/skrellphobe
|
||||
name = "Skrell-phobic"
|
||||
desc = "Boilerplate racism for squid goes here."
|
||||
muturally_exclusive = list(/datum/trait/modifier/mental/xenophobe)
|
||||
|
||||
/datum/trait/modifier/mental/tajaraphobe
|
||||
name = "Tajara-phobic"
|
||||
desc = "Boilerplate racism for cats goes here."
|
||||
muturally_exclusive = list(/datum/trait/modifier/mental/xenophobe)
|
||||
|
||||
/datum/trait/modifier/mental/unathiphobe
|
||||
name = "Unathi-phobic"
|
||||
desc = "Boilerplate racism for lizards goes here."
|
||||
muturally_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."
|
||||
muturally_exclusive = list(/datum/trait/modifier/mental/xenophobe)
|
||||
|
||||
/datum/trait/modifier/mental/teshariphobe
|
||||
name = "Teshari-phobic"
|
||||
desc = "Boilerplate racism for birds goes here."
|
||||
muturally_exclusive = list(/datum/trait/modifier/mental/xenophobe)
|
||||
|
||||
/datum/trait/modifier/mental/prometheanphobe
|
||||
name = "Promethean-phobic"
|
||||
desc = "Boilerplate racism for jellos goes here."
|
||||
muturally_exclusive = list(/datum/trait/modifier/mental/xenophobe)
|
||||
*/
|
||||
196
code/modules/client/preference_setup/traits/traits.dm
Normal file
196
code/modules/client/preference_setup/traits/traits.dm
Normal file
@@ -0,0 +1,196 @@
|
||||
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 in typesof(/datum/trait) - list(/datum/trait, /datum/trait/modifier))
|
||||
var/datum/trait/T = new trait_type
|
||||
|
||||
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 = "Traits"
|
||||
sort_order = 1
|
||||
var/current_tab = "Physical"
|
||||
|
||||
/datum/category_item/player_setup_item/loadout/load_character(var/savefile/S)
|
||||
S["traits"] >> pref.traits
|
||||
|
||||
/datum/category_item/player_setup_item/loadout/save_character(var/savefile/S)
|
||||
S["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><b><center>Traits</center></b></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 class='linkOn'>[category]</span> "
|
||||
else
|
||||
. += " <a href='?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='?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 muturally exclusive with [conflicts]."
|
||||
|
||||
. += "<td width = 75%><font size=2><i>[T.desc]</i>\
|
||||
[invalid ? "<font color='#FF0000'><br>Cannot take trait. Reason: [invalid]</font>":""]</font></td></tr>"
|
||||
// if(ticked)
|
||||
// . += "<tr><td colspan=3>"
|
||||
// for(var/datum/gear_tweak/tweak in G.gear_tweaks)
|
||||
// . += " <a href='?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])
|
||||
preference_mob << "<span class='warning'>You cannot have more than one of trait: [trait_name]</span>"
|
||||
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
|
||||
preference_mob << "<span class='warning'>You cannot take the [trait_name] trait. Reason: [invalidity]</span>"
|
||||
|
||||
var/conflicts = T.test_for_trait_conflict(pref.traits)
|
||||
if(conflicts)
|
||||
pref.traits -= trait_name
|
||||
to_chat(preference_mob, "<span class='warning'>The [trait_name] trait is muturally exclusive with [conflicts].</span>")
|
||||
|
||||
/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 class='warning'>You cannot take the [T.name] trait. Reason: [invalidity]</span>")
|
||||
return TOPIC_NOACTION
|
||||
|
||||
var/conflicts = T.test_for_trait_conflict(pref.traits)
|
||||
if(conflicts)
|
||||
to_chat(user, "<span class='warning'>The [T.name] trait is muturally exclusive with [conflicts].</span>")
|
||||
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
|
||||
var/name = null // Name to show on UI
|
||||
var/desc = null // Description of what it does, also shown on UI.
|
||||
var/list/muturally_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 muturally_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(muturally_exclusive.len)
|
||||
for(var/trait_name in current_traits)
|
||||
var/datum/trait/T = trait_datums[trait_name]
|
||||
if(T.type in muturally_exclusive)
|
||||
conflicts.Add(T.name)
|
||||
|
||||
if(conflicts.len)
|
||||
result = english_list(conflicts)
|
||||
|
||||
return result
|
||||
|
||||
// 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)
|
||||
@@ -50,6 +50,7 @@ datum/preferences
|
||||
var/list/alternate_languages = list() //Secondary language(s)
|
||||
var/list/language_prefixes = list() //Kanguage prefix keys
|
||||
var/list/gear //Custom/fluff item loadout.
|
||||
var/list/traits //Traits which modifier characters for better or worse (mostly worse).
|
||||
|
||||
//Some faction information.
|
||||
var/home_system = "Unset" //System of birth.
|
||||
|
||||
@@ -34,4 +34,12 @@
|
||||
on_created_text = "<span class='warning'>Life suddenly feels more precious.</span>"
|
||||
on_expired_text = "<span class='notice'>Death is cheap again.</span>"
|
||||
|
||||
flags = MODIFIER_GENETIC
|
||||
|
||||
|
||||
// Prevents borging (specifically the MMI part), actual effect is on the MMI.
|
||||
/datum/modifier/no_borg
|
||||
name = "Cyboernetic Incompatability"
|
||||
desc = "For whatever reason, your brain is incompatable with direct cybernetic interfaces, such as the MMI."
|
||||
|
||||
flags = MODIFIER_GENETIC
|
||||
@@ -37,6 +37,10 @@
|
||||
var/slowdown // Negative numbers speed up, positive numbers slow down movement.
|
||||
var/haste // If set to 1, the mob will be 'hasted', which makes it ignore slowdown and go really fast.
|
||||
var/evasion // Positive numbers reduce the odds of being hit by 15% each. Negative numbers increase the odds.
|
||||
var/bleeding_rate_percent // Adjusts amount of blood lost when bleeding.
|
||||
var/accuracy // Positive numbers makes hitting things with guns easier, negatives make it harder. Each point makes it 15% easier or harder, just like evasion.
|
||||
var/accuracy_dispersion // Positive numbers make gun firing cover a wider tile range, and therefore more inaccurate. Negatives help negate dispersion penalties.
|
||||
var/metabolism_percent // Adjusts the mob's metabolic rate, which affects reagent processing. Won't affect mobs without reagent processing.
|
||||
|
||||
/datum/modifier/New(var/new_holder, var/new_origin)
|
||||
holder = new_holder
|
||||
@@ -136,4 +140,71 @@
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(istype(M, modifier_type))
|
||||
return TRUE
|
||||
return FALSE
|
||||
return FALSE
|
||||
|
||||
// This displays the actual 'numbers' that a modifier is doing. Should only be shown in OOC contexts.
|
||||
// When adding new effects, be sure to update this as well.
|
||||
/datum/modifier/proc/describe_modifier_effects()
|
||||
var/list/effects = list()
|
||||
if(!isnull(max_health_flat))
|
||||
effects += "You [max_health_flat > 0 ? "gain" : "lose"] [abs(max_health_flat)] maximum health."
|
||||
if(!isnull(max_health_percent))
|
||||
effects += "You [max_health_percent > 1.0 ? "gain" : "lose"] [multipler_to_percentage(max_health_percent, TRUE)] maximum health."
|
||||
|
||||
if(!isnull(disable_duration_percent))
|
||||
effects += "Disabling effects on you last [multipler_to_percentage(disable_duration_percent, TRUE)] [disable_duration_percent > 1.0 ? "longer" : "shorter"]"
|
||||
|
||||
if(!isnull(incoming_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_damage_percent, TRUE)] [incoming_damage_percent > 1.0 ? "more" : "less"] damage."
|
||||
if(!isnull(incoming_brute_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_brute_damage_percent, TRUE)] [incoming_brute_damage_percent > 1.0 ? "more" : "less"] brute damage."
|
||||
if(!isnull(incoming_fire_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_fire_damage_percent, TRUE)] [incoming_fire_damage_percent > 1.0 ? "more" : "less"] fire damage."
|
||||
if(!isnull(incoming_tox_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_tox_damage_percent, TRUE)] [incoming_tox_damage_percent > 1.0 ? "more" : "less"] toxin damage."
|
||||
if(!isnull(incoming_oxy_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_oxy_damage_percent, TRUE)] [incoming_oxy_damage_percent > 1.0 ? "more" : "less"] oxy damage."
|
||||
if(!isnull(incoming_clone_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_clone_damage_percent, TRUE)] [incoming_clone_damage_percent > 1.0 ? "more" : "less"] clone damage."
|
||||
if(!isnull(incoming_hal_damage_percent))
|
||||
effects += "You take [multipler_to_percentage(incoming_hal_damage_percent, TRUE)] [incoming_hal_damage_percent > 1.0 ? "more" : "less"] agony damage."
|
||||
|
||||
if(!isnull(incoming_healing_percent))
|
||||
effects += "Healing applied to you is [multipler_to_percentage(incoming_healing_percent, TRUE)] [incoming_healing_percent > 1.0 ? "stronger" : "weaker"]."
|
||||
|
||||
if(!isnull(outgoing_melee_damage_percent))
|
||||
effects += "Damage you do with melee weapons and unarmed combat is [multipler_to_percentage(outgoing_melee_damage_percent, TRUE)] \
|
||||
[outgoing_melee_damage_percent > 1.0 ? "higher" : "lower"]."
|
||||
|
||||
if(!isnull(slowdown))
|
||||
effects += "[slowdown > 0 ? "lose" : "gain"] [slowdown] slowdown."
|
||||
|
||||
if(!isnull(haste))
|
||||
effects += "You move at maximum speed, and cannot be slowed by any means."
|
||||
|
||||
if(!isnull(evasion))
|
||||
effects += "You are [abs(evasion * 15)]% [evasion > 0 ? "harder" : "easier"] to hit with weapons."
|
||||
|
||||
if(!isnull(bleeding_rate_percent))
|
||||
effects += "You bleed [multipler_to_percentage(bleeding_rate_percent, TRUE)] [bleeding_rate_percent > 1.0 ? "faster" : "slower"]."
|
||||
|
||||
if(!isnull(accuracy))
|
||||
effects += "It is [abs(accuracy * 15)]% [accuracy > 0 ? "easier" : "harder"] for you to hit someone with a ranged weapon."
|
||||
|
||||
if(!isnull(accuracy_dispersion))
|
||||
effects += "Projectiles you fire are [accuracy_dispersion > 0 ? "more" : "less"] likely to stray from your intended target."
|
||||
|
||||
if(!isnull(metabolism_percent))
|
||||
effects += "Your metabolism is [metabolism_percent > 1.0 ? "faster" : "slower"], \
|
||||
causing reagents in your body to process, and hunger to occur [multipler_to_percentage(metabolism_percent, TRUE)] [metabolism_percent > 1.0 ? "faster" : "slower"]."
|
||||
|
||||
|
||||
return jointext(effects, "<br>")
|
||||
|
||||
|
||||
|
||||
// Helper to format multiplers (e.g. 1.4) to percentages (like '40%')
|
||||
/proc/multipler_to_percentage(var/multi, var/abs = FALSE)
|
||||
if(abs)
|
||||
return "[abs( ((multi - 1) * 100) )]%"
|
||||
return "[((multi - 1) * 100)]%"
|
||||
|
||||
@@ -1,10 +1,55 @@
|
||||
/datum/modifier/frail
|
||||
name = "frail"
|
||||
desc = "You are more delicate than the average person."
|
||||
/datum/modifier/trait
|
||||
flags = MODIFIER_GENETIC // We want traits to persist if the person gets cloned.
|
||||
|
||||
flags = MODIFIER_GENETIC
|
||||
|
||||
/datum/modifier/trait/flimsy
|
||||
name = "flimsy"
|
||||
desc = "You're more fragile than most, and have less of an ability to endure harm."
|
||||
|
||||
on_created_text = "<span class='warning'>You feel rather weak.</span>"
|
||||
on_expired_text = "<span class='notice'>You feel your strength returning to you.</span>"
|
||||
|
||||
max_health_percent = 0.8
|
||||
|
||||
/datum/modifier/trait/frail
|
||||
name = "frail"
|
||||
desc = "Your body is very fragile, and has even less of an ability to endure harm."
|
||||
|
||||
on_created_text = "<span class='warning'>You feel really weak.</span>"
|
||||
on_expired_text = "<span class='notice'>You feel your strength returning to you.</span>"
|
||||
|
||||
max_health_percent = 0.9
|
||||
max_health_percent = 0.6
|
||||
|
||||
/datum/modifier/trait/weak
|
||||
name = "weak"
|
||||
desc = "A lack of physical strength causes a diminshed capability in close quarters combat"
|
||||
|
||||
outgoing_melee_damage_percent = 0.6
|
||||
|
||||
/datum/modifier/trait/haemophilia
|
||||
name = "haemophilia"
|
||||
desc = "You bleed much faster than average."
|
||||
|
||||
bleeding_rate_percent = 3.0
|
||||
|
||||
/datum/modifier/trait/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."
|
||||
|
||||
accuracy = -1
|
||||
accuracy_dispersion = 1
|
||||
|
||||
/datum/modifier/trait/high_metabolism
|
||||
name = "High Metabolsim"
|
||||
desc = "Your body's metabolism is faster than average."
|
||||
|
||||
metabolism_percent = 2.0
|
||||
incoming_healing_percent = 1.4
|
||||
|
||||
/datum/modifier/trait/low_metabolism
|
||||
name = "Low Metabolism"
|
||||
desc = "Your body's metabolism is slower than average."
|
||||
|
||||
metabolism_percent = 0.5
|
||||
incoming_healing_percent = 0.6
|
||||
491
code/modules/mob/_modifiers/traits_phobias.dm
Normal file
491
code/modules/mob/_modifiers/traits_phobias.dm
Normal file
@@ -0,0 +1,491 @@
|
||||
// Gives various spooky messages to people afraid of a specific thing.
|
||||
// Doesn't have any real mechanical effect, and is more of an aid to remind someone "You're supposed to be afraid of the dark", and such.
|
||||
|
||||
/datum/modifier/trait/phobia
|
||||
var/current_fear = 0 // Counter for how 'afraid' the holder is.
|
||||
var/max_fear = 100 // Cap for current_fear.
|
||||
var/fear_decay_rate = 1 // How much is subtracted every Life() tick when not being spooked by something.
|
||||
|
||||
var/list/zero_fear_up = list() // Message displayed to holder when current_fear raises above 0.
|
||||
var/list/zero_fear_down = list() // Message displayed when reaching 0.
|
||||
|
||||
var/list/half_fear_up = list() // Message displayed when current_fear passes half of max_fear.
|
||||
var/list/half_fear_down = list() // Message displayed when current_fear goes below half of max_fear.
|
||||
|
||||
var/list/full_fear_up = list() // Similar to above, but for the cap.
|
||||
var/list/full_fear_down = list() // Ditto.
|
||||
|
||||
/datum/modifier/trait/phobia/tick()
|
||||
if(holder.stat)
|
||||
return // You got bigger problems.
|
||||
var/new_fear = should_fear()
|
||||
if(new_fear)
|
||||
adjust_fear(new_fear)
|
||||
else
|
||||
adjust_fear(-fear_decay_rate)
|
||||
|
||||
/datum/modifier/trait/phobia/proc/adjust_fear(var/amount)
|
||||
var/last_fear = current_fear
|
||||
current_fear = between(0, current_fear + amount, max_fear)
|
||||
|
||||
// Handle messages. safepick() is used so that if no messages are defined, it just does nothing, verses runtiming.
|
||||
var/message = null
|
||||
if(amount > 0) // Increase in spooks.
|
||||
if(current_fear == max_fear && last_fear < max_fear)
|
||||
message = safepick(full_fear_up)
|
||||
else if(current_fear >= (max_fear / 2) && last_fear < (max_fear / 2))
|
||||
message = safepick(half_fear_up)
|
||||
else if(current_fear > 0 && last_fear == 0)
|
||||
message = safepick(zero_fear_up)
|
||||
else if(amount < 0) // Decrease in spooks.
|
||||
if(last_fear == max_fear && current_fear < max_fear)
|
||||
message = safepick(full_fear_down)
|
||||
else if(last_fear >= (max_fear / 2) && current_fear < (max_fear / 2))
|
||||
message = safepick(half_fear_down)
|
||||
else if(last_fear > 0 && current_fear == 0)
|
||||
message = safepick(zero_fear_down)
|
||||
|
||||
if(message)
|
||||
to_chat(holder, message)
|
||||
|
||||
// Override for specific fears, e.g. seeing blood or spiders.
|
||||
/datum/modifier/trait/phobia/proc/should_fear()
|
||||
return FALSE
|
||||
|
||||
|
||||
// Actual phobia trait implementations below.
|
||||
|
||||
/datum/modifier/trait/phobia/haemophobia
|
||||
name = "haemophobia"
|
||||
desc = "Seeing a bunch of blood isn't really pleasant for most people, but for you, it is very distressing."
|
||||
fear_decay_rate = 4
|
||||
|
||||
on_created_text = "<span class='warning'>You are terrified of seeing blood.</span>"
|
||||
on_expired_text = "<span class='notice'>You feel that blood doesn't bother you, at least, as much as it used to.</span>"
|
||||
|
||||
zero_fear_up = list(
|
||||
"<span class='warning'><font size='3'>You see some blood nearby...</font></span>",
|
||||
"<span class='warning'><font size='3'>You try to avoid looking at the blood nearby.</font></span>"
|
||||
)
|
||||
zero_fear_down = list(
|
||||
"<span class='notice'>You feel better now, with no blood in sight.</span>",
|
||||
"<span class='notice'>At last, the blood is gone.</span>",
|
||||
"<span class='notice'>Hopefully you won't see anymore blood today.</span>"
|
||||
)
|
||||
|
||||
half_fear_up = list(
|
||||
"<span class='danger'><font size='3'>You're still near the blood!</font></span>",
|
||||
"<span class='danger'><font size='3'>So much blood... You can't stand it.</font></span>"
|
||||
)
|
||||
half_fear_down = list(
|
||||
"<span class='warning'>The blood is gone now, but you're still worked up.</span>",
|
||||
"<span class='warning'>You can't see the blood now, but you're still anxious.</span>"
|
||||
)
|
||||
|
||||
full_fear_up = list(
|
||||
"<span class='danger'><font size='4'>The blood is too much!</font></span>",
|
||||
"<span class='danger'><font size='4'>There is so much blood here, you need to leave!</font></span>",
|
||||
"<span class='danger'><font size='4'>You gotta get away from the blood!</font></span>"
|
||||
)
|
||||
full_fear_down = list(
|
||||
"<span class='danger'>The blood is gone, but you're still very anxious.</span>",
|
||||
"<span class='danger'>No more blood... Please.</span>"
|
||||
)
|
||||
|
||||
/datum/modifier/trait/phobia/haemophobia/check_if_valid()
|
||||
if(iscultist(holder)) // Nar-nar can't be having cultists afraid of blood.
|
||||
expire()
|
||||
else
|
||||
..()
|
||||
|
||||
/datum/modifier/trait/phobia/haemophobia/should_fear()
|
||||
if(holder.blinded)
|
||||
return 0 // Can't fear what cannot be seen.
|
||||
|
||||
var/fear_amount = 0
|
||||
for(var/atom/thing in view(5, holder)) // It's 5 and not 7 so players have a chance to go away before getting the prompts, and for performance.
|
||||
// Blood stains are bad.
|
||||
if(istype(thing, /obj/effect/decal/cleanable/blood))
|
||||
var/obj/effect/decal/cleanable/blood/B = thing
|
||||
// Tracks are special, apparently.
|
||||
if(istype(thing, /obj/effect/decal/cleanable/blood/tracks))
|
||||
var/obj/effect/decal/cleanable/blood/tracks/T = B
|
||||
for(var/datum/fluidtrack/F in T.stack)
|
||||
if(F.basecolor != SYNTH_BLOOD_COLOUR)
|
||||
fear_amount++
|
||||
break
|
||||
else
|
||||
if(B.basecolor != SYNTH_BLOOD_COLOUR)
|
||||
fear_amount++
|
||||
|
||||
// People covered in blood is also bad.
|
||||
// Feel free to trim down if its too expensive CPU wise.
|
||||
if(istype(thing, /mob/living/carbon/human))
|
||||
var/mob/living/carbon/human/H = thing
|
||||
var/self_multiplier = H == holder ? 2 : 1
|
||||
var/human_blood_fear_amount = 0
|
||||
if(!H.gloves && H.bloody_hands && H.hand_blood_color != SYNTH_BLOOD_COLOUR)
|
||||
human_blood_fear_amount += 1
|
||||
if(!H.shoes && H.feet_blood_color && H.feet_blood_color != SYNTH_BLOOD_COLOUR)
|
||||
human_blood_fear_amount += 1
|
||||
|
||||
// List of slots. Some slots like pockets are omitted due to not being visible, if H isn't the holder.
|
||||
var/list/clothing_slots = list(H.back, H.wear_mask, H.l_hand, H.r_hand, H.wear_id, H.glasses, H.gloves, H.head, H.shoes, H.belt, H.wear_suit, H.w_uniform, H.s_store, H.l_ear, H.r_ear)
|
||||
if(H == holder)
|
||||
clothing_slots += list(H.l_store, H.r_store)
|
||||
|
||||
for(var/obj/item/clothing/C in clothing_slots)
|
||||
if(C.blood_DNA && C.blood_color && C.blood_color != SYNTH_BLOOD_COLOUR)
|
||||
human_blood_fear_amount += 1
|
||||
|
||||
// This is divided, since humans can wear so many items at once.
|
||||
human_blood_fear_amount = round( (human_blood_fear_amount * self_multiplier) / 3, 1)
|
||||
fear_amount += human_blood_fear_amount
|
||||
|
||||
// Bloody objects are also bad.
|
||||
if(istype(thing, /obj))
|
||||
var/obj/O = thing
|
||||
if(O.blood_DNA && O.blood_color && O.blood_color != SYNTH_BLOOD_COLOUR)
|
||||
fear_amount++
|
||||
|
||||
return fear_amount
|
||||
|
||||
|
||||
/datum/modifier/trait/phobia/arachnophobe
|
||||
name = "arachnophobia"
|
||||
desc = "Spiders are quite creepy to most people, however for you, those chitters of pure evil inspire pure dread and fear."
|
||||
fear_decay_rate = 1
|
||||
|
||||
on_created_text = "<span class='warning'>You are terrified of seeing spiders.</span>"
|
||||
on_expired_text = "<span class='notice'>You feel that your fear of spiders has gone from 'crippling' to 'nope nope nope', which is still an improvement.</span>"
|
||||
|
||||
zero_fear_up = list(
|
||||
"<span class='warning'><font size='3'>You see a spider!</font></span>",
|
||||
"<span class='warning'><font size='3'>Spider!</font></span>"
|
||||
)
|
||||
zero_fear_down = list(
|
||||
"<span class='notice'>Those evil spiders are finally gone.</span>",
|
||||
"<span class='notice'>At last, the chitters are gone.</span>",
|
||||
"<span class='notice'>The spiders are gone, at last.</span>"
|
||||
)
|
||||
|
||||
half_fear_up = list(
|
||||
"<span class='danger'><font size='3'>The spiders are gonna eat you!</font></span>",
|
||||
"<span class='danger'><font size='3'>The spiders are still here!</font></span>",
|
||||
"<span class='danger'><font size='3'>The spiders will lay eggs inside you if you don't run!</font></span>",
|
||||
)
|
||||
half_fear_down = list(
|
||||
"<span class='warning'>The spiders are gone... right?</span>",
|
||||
"<span class='warning'>You can't see any spiders now, but you're still anxious.</span>"
|
||||
)
|
||||
|
||||
full_fear_up = list(
|
||||
"<span class='danger'><font size='4'>The chittering is driving you mad!</font></span>",
|
||||
"<span class='danger'><font size='4'>You're gonna be spider-food if you don't run!</font></span>",
|
||||
"<span class='danger'><font size='4'>The spiders are gonna feast on your eyes!</font></span>"
|
||||
)
|
||||
full_fear_down = list(
|
||||
"<span class='danger'>The spiders must surely be hiding somewhere...</span>",
|
||||
"<span class='danger'>No more spiders... Please.</span>"
|
||||
)
|
||||
|
||||
/datum/modifier/trait/phobia/arachnophobe/should_fear()
|
||||
if(holder.blinded)
|
||||
return 0 // Can't fear what cannot be seen.
|
||||
|
||||
var/fear_amount = 0
|
||||
for(var/atom/thing in view(5, holder)) // See haemophobia for why this is 5.
|
||||
if(istype(thing, /obj/effect/decal/cleanable/spiderling_remains)) // Dead spiderlings are a bit spooky.
|
||||
fear_amount += 1
|
||||
|
||||
if(istype(thing, /obj/effect/spider/spiderling)) // Live spiderlings are also spooky.
|
||||
fear_amount += 2
|
||||
|
||||
if(istype(thing, /obj/item/toy/plushie/spider)) // Plushies are spooky so people can be assholes with them.
|
||||
fear_amount += 1
|
||||
|
||||
if(istype(thing, /mob/living/simple_animal/hostile/giant_spider)) // Actual giant spiders are the scariest of them all.
|
||||
var/mob/living/simple_animal/hostile/giant_spider/S = thing
|
||||
if(S.stat == DEAD) // Dead giant spiders are less scary than alive ones.
|
||||
fear_amount += 4
|
||||
else
|
||||
fear_amount += 8
|
||||
return fear_amount
|
||||
|
||||
|
||||
/datum/modifier/trait/phobia/nyctophobe
|
||||
name = "nyctophobia"
|
||||
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."
|
||||
fear_decay_rate = 5
|
||||
|
||||
on_created_text = "<span class='warning'>You are terrified of the dark.</span>"
|
||||
on_expired_text = "<span class='notice'>You feel that darkness isn't quite as scary anymore.</span>"
|
||||
|
||||
var/fear_threshold = 0.5 // Average lighting needs to be below this to start increasing fear.
|
||||
|
||||
zero_fear_up = list(
|
||||
"<span class='warning'><font size='3'>It's so dark here!</font></span>",
|
||||
"<span class='warning'><font size='3'>It's too dark!</font></span>"
|
||||
)
|
||||
zero_fear_down = list(
|
||||
"<span class='notice'>You feel calmer, now that you're in the light.</span>",
|
||||
"<span class='notice'>At last, no more darkness.</span>",
|
||||
"<span class='notice'>The light makes you feel calmer.</span>"
|
||||
)
|
||||
|
||||
half_fear_up = list(
|
||||
"<span class='danger'><font size='3'>You need to escape this darkness!</font></span>",
|
||||
"<span class='danger'><font size='3'>Something might be lurking near you, but you can't see in this darkness.</font></span>",
|
||||
"<span class='danger'><font size='3'>You need to find a light!</font></span>",
|
||||
)
|
||||
half_fear_down = list(
|
||||
"<span class='warning'>The darkness is gone, for now...</span>",
|
||||
"<span class='warning'>You're not in the dark anymore, but you're still anxious.</span>"
|
||||
)
|
||||
|
||||
full_fear_up = list(
|
||||
"<span class='danger'><font size='4'>What was that?</font></span>",
|
||||
"<span class='danger'><font size='4'>Something is nearby...</font></span>"
|
||||
)
|
||||
full_fear_down = list(
|
||||
"<span class='danger'>Light, at last!</span>",
|
||||
"<span class='danger'>The darkness is finally gone!</span>"
|
||||
)
|
||||
|
||||
/datum/modifier/trait/phobia/nyctophobe/should_fear()
|
||||
if(holder.blinded)
|
||||
return 5 // Unlike most other fears coded here, being blind when afraid of darkness is pretty bad, I imagine.
|
||||
|
||||
if(holder.see_in_dark >= 5)
|
||||
return 0 // What darkness?
|
||||
|
||||
var/fear_amount = 0
|
||||
var/total_lum = 0
|
||||
var/total_tiles = 0
|
||||
var/average_lum = null
|
||||
|
||||
for(var/turf/simulated/T in view(5, get_turf(holder))) // See haemophobia for why this is 5. This uses get_turf() since darkness makes tiles not visible to holder.
|
||||
total_lum += T.get_lumcount()
|
||||
total_tiles++
|
||||
|
||||
average_lum = total_lum / total_tiles
|
||||
|
||||
if(average_lum > fear_threshold)
|
||||
switch(average_lum)
|
||||
if(0.0 to 0.1)
|
||||
fear_amount += 5
|
||||
if(0.1 to 0.2)
|
||||
fear_amount += 4
|
||||
if(0.2 to 0.3)
|
||||
fear_amount += 3
|
||||
if(0.3 to 0.4)
|
||||
fear_amount += 2
|
||||
if(0.4 to 0.5)
|
||||
fear_amount += 1
|
||||
|
||||
var/turf/T = get_turf(holder)
|
||||
if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) // Standing in complete darkness.
|
||||
fear_amount += 5
|
||||
|
||||
return fear_amount
|
||||
|
||||
/datum/modifier/trait/phobia/claustrophobe
|
||||
name = "claustrophobia"
|
||||
desc = "Small spaces and tight quarters makes you feel distressed. Unfortunately both are rather common when living in space."
|
||||
fear_decay_rate = 2
|
||||
|
||||
var/open_tiles_needed = 15 // Tends to be just right, as maint triggers this but hallways don't.
|
||||
|
||||
on_created_text = "<span class='warning'>You are terrified of tight spaces. Why did you come to space??</span>"
|
||||
on_expired_text = "<span class='notice'>Small rooms aren't so bad now.</span>"
|
||||
|
||||
zero_fear_up = list(
|
||||
"<span class='warning'><font size='3'>This room is too small...</font></span>",
|
||||
"<span class='warning'><font size='3'>The walls are too close together...</font></span>"
|
||||
)
|
||||
zero_fear_down = list(
|
||||
"<span class='notice'>You feel calmer, now that you're in a larger room.</span>",
|
||||
"<span class='notice'>At last, the walls are far apart.</span>",
|
||||
"<span class='notice'>The relatively open area makes you feel calmer.</span>"
|
||||
)
|
||||
|
||||
half_fear_up = list(
|
||||
"<span class='danger'><font size='3'>Your surroundings look like they are closing in.</font></span>",
|
||||
"<span class='danger'><font size='3'>Is the room getting smaller?</font></span>",
|
||||
"<span class='danger'><font size='3'>You need to get out of here!</font></span>",
|
||||
)
|
||||
half_fear_down = list(
|
||||
"<span class='warning'>Your surroundings seem to have stopped closing in.</span>",
|
||||
"<span class='warning'>You're not in a tight space anymore, but you're still anxious.</span>"
|
||||
)
|
||||
|
||||
full_fear_up = list(
|
||||
"<span class='danger'><font size='4'>You need to escape!</font></span>",
|
||||
"<span class='danger'><font size='4'>There's barely any room to move around!</font></span>"
|
||||
)
|
||||
full_fear_down = list(
|
||||
"<span class='danger'>The surroundings stop shrinking.</span>",
|
||||
"<span class='danger'>The walls seem to have stopped.</span>"
|
||||
)
|
||||
|
||||
/datum/modifier/trait/phobia/claustrophobe/should_fear()
|
||||
if(holder.blinded)
|
||||
return 0 // No idea if this is accurate.
|
||||
|
||||
if(holder.loc && !isturf(holder.loc)) // Hiding in a locker or inside an exosuit is spooky.
|
||||
return 5
|
||||
|
||||
var/fear_amount = 0
|
||||
var/open_tiles = 0
|
||||
var/radius = 5 // See haemophobia for why this is 5.
|
||||
var/max_open_tiles = radius * radius // Potential maximum tiles. In practice it will be rare for someone to be inside a 5x5 plane.
|
||||
for(var/turf/T in view(radius, holder))
|
||||
var/open = TRUE
|
||||
if(T.density)
|
||||
continue
|
||||
for(var/atom/movable/AM in T)
|
||||
if(AM.density)
|
||||
open = FALSE
|
||||
break
|
||||
if(open)
|
||||
open_tiles++
|
||||
|
||||
if(open_tiles < open_tiles_needed)
|
||||
var/fear_reduction = abs( (open_tiles / max_open_tiles) - 1) // The smaller the space, the smaller this number is, and fear will build up faster.
|
||||
fear_amount = 5 * fear_reduction
|
||||
|
||||
return fear_amount
|
||||
|
||||
// Note for the below 'phobias' are of the xeno-phobic variety, and are less centered on pure fear as above, and more on a mix of distrust, fear, and disdainfulness.
|
||||
// As such, they are mechanically different than the fear-based phobias, in that instead of a buildup of fearful messages, it does intermittent messages specific to what holder sees.
|
||||
|
||||
// This is the catch-all 'everyone but [my species] is up to no good' trait, as opposed to the other specialized variants.
|
||||
/datum/modifier/trait/phobia/xenophobia
|
||||
name = "xenophobia"
|
||||
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."
|
||||
|
||||
on_created_text = "<span class='warning'>You remain vigilant against the Alien.</span>"
|
||||
on_expired_text = "<span class='notice'>Aliens aren't so bad afterall.</span>"
|
||||
|
||||
var/last_message = null // world.time we last did a message.
|
||||
var/message_cooldown = 1 MINUTE
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/tick()
|
||||
if(holder.stat)
|
||||
return // You got bigger problems.
|
||||
if(last_message + message_cooldown <= world.time)
|
||||
if(intermittent_message())
|
||||
last_message = world.time
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/proc/intermittent_message()
|
||||
var/list/xenos = get_xenos()
|
||||
|
||||
if(xenos.len)
|
||||
var/chosen_xeno = pick(xenos)
|
||||
to_chat(holder, "<span class='warning'><font size='3'>[make_message(chosen_xeno)]</font></span>")
|
||||
return TRUE
|
||||
else
|
||||
return FALSE // No xenos in sight, so don't apply the cooldown.
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/proc/get_xenos()
|
||||
return list()
|
||||
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/proc/make_message(var/mob/living/L)
|
||||
return "Someone forgot to override this output message."
|
||||
|
||||
|
||||
// This is the catch-all 'everyone but [my species] is up to no good' trait, as opposed to the other specialized variants.
|
||||
/datum/modifier/trait/phobia/xenophobia/generic
|
||||
name = "xenophobia"
|
||||
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."
|
||||
|
||||
on_created_text = "<span class='warning'>You remain vigilant against the Alien.</span>"
|
||||
on_expired_text = "<span class='notice'>Aliens aren't so bad afterall.</span>"
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/generic/get_xenos()
|
||||
var/list/xenos = list()
|
||||
if(!ishuman(holder))
|
||||
return
|
||||
var/mob/living/carbon/human/us = holder
|
||||
for(var/mob/living/carbon/human/H in view(5, holder)) // See haemophobia for why this is 5.
|
||||
if(!(istype(us.species, H.species) )) // Are they a different species?
|
||||
xenos += H
|
||||
return xenos
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/generic/make_message(var/mob/living/carbon/human/H)
|
||||
// Do special responses first if possible.
|
||||
// if(H.stat == DEAD)
|
||||
// return pick( list("Unsurprising to see a weak and inferior [H.species.name] fail to survive.", "If that [H.species.name] were a [holder.species.name], this wouldn't've have happened.") )
|
||||
|
||||
// Generic responses if none of the above apply.
|
||||
var/list/generic_responses = list(
|
||||
"That [H.species.name] is likely trying to spy on you.",
|
||||
"[H.species.name_plural] tend to be ugly, but this one near you is even worse!",
|
||||
"[H.species.name] scum.",
|
||||
"The [H.species.name] nearby is certainly a spy for Them.",
|
||||
"That [H.species.name] smells awful.",
|
||||
"Can't trust [H.species.name_plural]."
|
||||
)
|
||||
return pick(generic_responses)
|
||||
|
||||
|
||||
// *********
|
||||
// * Human *
|
||||
// *********
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/human
|
||||
name = "anti-human sentiment"
|
||||
desc = "Humans are bound to get us all killed with their reckless use of technology..."
|
||||
|
||||
on_created_text = "<span class='warning'>You unfortunately are likely to have to deal with humans today.</span>"
|
||||
on_expired_text = "<span class='notice'>Humans aren't so bad afterall.</span>"
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/human/get_xenos()
|
||||
var/list/humans = list()
|
||||
for(var/mob/living/carbon/human/H in view(5, holder)) // See haemophobia for why this is 5.
|
||||
if(H == holder)
|
||||
continue // No self loathing here.
|
||||
if(istype(H.species, /datum/species/human) ) // Are they a human.
|
||||
humans += H
|
||||
return humans
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/human/make_message(var/mob/living/carbon/human/H)
|
||||
// Do special responses first if possible.
|
||||
|
||||
// Generic responses if none of the above apply.
|
||||
var/list/generic_responses = list(
|
||||
"Why did you travel to human space? It's full of them."
|
||||
)
|
||||
return pick(generic_responses)
|
||||
|
||||
// **********
|
||||
// * Skrell *
|
||||
// **********
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/skrell
|
||||
name = "anti-skrell sentiment"
|
||||
desc = "The Skrell pretend that they are Humanity's enlightened allies, but you can see past that."
|
||||
|
||||
on_created_text = "<span class='warning'>Hopefully no Skrell show up today.</span>"
|
||||
on_expired_text = "<span class='notice'>Skrell aren't so bad afterall.</span>"
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/skrell/get_xenos()
|
||||
var/list/skrell = list()
|
||||
for(var/mob/living/carbon/human/H in view(5, holder)) // See haemophobia for why this is 5.
|
||||
if(H == holder)
|
||||
continue // No self loathing here.
|
||||
if(istype(H.species, /datum/species/skrell) ) // Are they a squid now?
|
||||
skrell += H
|
||||
return skrell
|
||||
|
||||
/datum/modifier/trait/phobia/xenophobia/skrell/make_message(var/mob/living/carbon/human/H)
|
||||
// Do special responses first if possible.
|
||||
|
||||
// Generic responses if none of the above apply.
|
||||
var/list/generic_responses = list(
|
||||
"WetSkrell was a mistake."
|
||||
)
|
||||
return pick(generic_responses)
|
||||
@@ -52,6 +52,11 @@
|
||||
user << "<span class='warning'>You aren't sure where this brain came from, but you're pretty sure it's useless.</span>"
|
||||
return
|
||||
|
||||
for(var/modifier_type in B.brainmob.modifiers) //Can't be shoved in an MMI.
|
||||
if(istype(modifier_type, /datum/modifier/no_borg))
|
||||
to_chat(user, "<span class='warning'>\The [src] appears to reject this brain. It is incompatable.</span>")
|
||||
return
|
||||
|
||||
user.visible_message("<span class='notice'>\The [user] sticks \a [O] into \the [src].</span>")
|
||||
|
||||
brainmob = B.brainmob
|
||||
@@ -118,6 +123,11 @@
|
||||
brainmob.dna = H.dna
|
||||
brainmob.container = src
|
||||
|
||||
// Copy modifiers.
|
||||
for(var/datum/modifier/M in H.modifiers)
|
||||
if(M.flags & MODIFIER_GENETIC)
|
||||
brainmob.add_modifier(M.type)
|
||||
|
||||
name = "Man-Machine Interface: [brainmob.real_name]"
|
||||
icon_state = "mmi_full"
|
||||
locked = 1
|
||||
|
||||
@@ -1180,6 +1180,8 @@
|
||||
hud_used = new /datum/hud(src)
|
||||
|
||||
if(species)
|
||||
if(mind)
|
||||
apply_traits()
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
@@ -652,7 +652,7 @@
|
||||
if(status_flags & GODMODE)
|
||||
return 1 //godmode
|
||||
|
||||
|
||||
|
||||
if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell))
|
||||
var/cold_dam = 0
|
||||
if(bodytemperature <= species.cold_level_1)
|
||||
@@ -862,8 +862,14 @@
|
||||
heal_overall_damage(1,1)
|
||||
|
||||
// nutrition decrease
|
||||
if (nutrition > 0 && stat != 2)
|
||||
nutrition = max (0, nutrition - species.hunger_factor)
|
||||
if (nutrition > 0 && stat != DEAD)
|
||||
var/nutrition_reduction = species.hunger_factor
|
||||
|
||||
for(var/datum/modifier/mod in modifiers)
|
||||
if(!isnull(mod.metabolism_percent))
|
||||
nutrition_reduction *= mod.metabolism_percent
|
||||
|
||||
nutrition = max (0, nutrition - nutrition_reduction)
|
||||
|
||||
if (nutrition > 450)
|
||||
if(overeatduration < 600) //capped so people don't take forever to unfat
|
||||
|
||||
@@ -359,6 +359,8 @@
|
||||
// Equip our custom items only AFTER deploying to spawn points eh?
|
||||
equip_custom_items(character)
|
||||
|
||||
character.apply_traits()
|
||||
|
||||
character.lastarea = get_area(loc)
|
||||
// Moving wheelchair if they have one
|
||||
if(character.buckled && istype(character.buckled, /obj/structure/bed/chair/wheelchair))
|
||||
@@ -454,6 +456,7 @@
|
||||
new_character.real_name = pick(clown_names) //I hate this being here of all places but unfortunately dna is based on real_name!
|
||||
new_character.rename_self("clown")
|
||||
mind.original = new_character
|
||||
mind.traits = client.prefs.traits.Copy()
|
||||
mind.transfer_to(new_character) //won't transfer key since the mind is not active
|
||||
|
||||
new_character.name = real_name
|
||||
|
||||
@@ -142,6 +142,16 @@ var/const/CE_STABLE_THRESHOLD = 0.5
|
||||
//Bleeding out
|
||||
var/blood_max = 0
|
||||
var/blood_loss_divisor = 30 //lower factor = more blood loss
|
||||
|
||||
// Some modifiers can make bleeding better or worse. Higher multiplers = more bleeding.
|
||||
var/blood_loss_modifier_multiplier = 1.0
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.bleeding_rate_percent))
|
||||
blood_loss_modifier_multiplier += (M.bleeding_rate_percent - 1.0)
|
||||
|
||||
blood_loss_divisor /= blood_loss_modifier_multiplier
|
||||
|
||||
|
||||
//This 30 is the "baseline" of a cut in the "vital" regions (head and torso).
|
||||
for(var/obj/item/organ/external/temp in bad_external_organs)
|
||||
if(!(temp.status & ORGAN_BLEEDING) || (temp.robotic >= ORGAN_ROBOT))
|
||||
|
||||
@@ -79,6 +79,11 @@
|
||||
brainmob.dna = H.dna.Clone()
|
||||
brainmob.timeofhostdeath = H.timeofdeath
|
||||
|
||||
// Copy modifiers.
|
||||
for(var/datum/modifier/M in H.modifiers)
|
||||
if(M.flags & MODIFIER_GENETIC)
|
||||
brainmob.add_modifier(M.type)
|
||||
|
||||
if(H.mind)
|
||||
H.mind.transfer_to(brainmob)
|
||||
|
||||
|
||||
@@ -480,7 +480,7 @@
|
||||
damage_mult = 1.5
|
||||
P.damage *= damage_mult
|
||||
|
||||
/obj/item/weapon/gun/proc/process_accuracy(obj/projectile, mob/user, atom/target, acc_mod, dispersion)
|
||||
/obj/item/weapon/gun/proc/process_accuracy(obj/projectile, mob/living/user, atom/target, acc_mod, dispersion)
|
||||
var/obj/item/projectile/P = projectile
|
||||
if(!istype(P))
|
||||
return //default behaviour only applies to true projectiles
|
||||
@@ -504,6 +504,13 @@
|
||||
//As opposed to no-delay pew pew
|
||||
P.accuracy += 2
|
||||
|
||||
// Some modifiers make it harder or easier to hit things.
|
||||
for(var/datum/modifier/M in user.modifiers)
|
||||
if(!isnull(M.accuracy))
|
||||
P.accuracy += M.accuracy
|
||||
if(!isnull(M.accuracy_dispersion))
|
||||
P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0)
|
||||
|
||||
//does the actual launching of the projectile
|
||||
/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null)
|
||||
var/obj/item/projectile/P = projectile
|
||||
|
||||
@@ -63,7 +63,13 @@
|
||||
return
|
||||
var/removed = metabolism
|
||||
if(!mrate_static == TRUE)
|
||||
// Modifiers
|
||||
for(var/datum/modifier/mod in M.modifiers)
|
||||
if(!isnull(mod.metabolism_percent))
|
||||
removed *= mod.metabolism_percent
|
||||
// Species
|
||||
removed *= M.species.metabolic_rate
|
||||
|
||||
if(ingest_met && (location == CHEM_INGEST))
|
||||
removed = ingest_met
|
||||
if(touch_met && (location == CHEM_TOUCH))
|
||||
|
||||
@@ -18,9 +18,9 @@ Used In File(s): \code\game\machinery\adv_med.dm
|
||||
<div class="itemLabelNarrow">
|
||||
Health:
|
||||
</div>
|
||||
{{:helper.displayBar(data.occupant.health, 0, 100, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 25) ? 'average' : 'bad')}}
|
||||
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 25) ? 'average' : 'bad')}}
|
||||
<div class="itemContent" style="width: 60px">
|
||||
{{:helper.round(data.occupant.health*10)/10}}%
|
||||
{{:helper.round(data.occupant.health / data.occupant.maxHealth)*100}}%
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="itemLabelNarrow">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
Occupant status:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
Health: {{:data.health}}% ({{:data.stat}}).
|
||||
Health: {{:helper.round(data.health / data.maxHealth)*100}}% ({{:data.stat}}).
|
||||
</div>
|
||||
<div class="itemLabel">
|
||||
Pulse:
|
||||
|
||||
@@ -1262,6 +1262,8 @@
|
||||
#include "code\modules\client\preference_setup\loadout\loadout_xeno.dm"
|
||||
#include "code\modules\client\preference_setup\occupation\occupation.dm"
|
||||
#include "code\modules\client\preference_setup\skills\skills.dm"
|
||||
#include "code\modules\client\preference_setup\traits\trait_defines.dm"
|
||||
#include "code\modules\client\preference_setup\traits\traits.dm"
|
||||
#include "code\modules\clothing\chameleon.dm"
|
||||
#include "code\modules\clothing\clothing.dm"
|
||||
#include "code\modules\clothing\clothing_accessories.dm"
|
||||
@@ -1599,6 +1601,7 @@
|
||||
#include "code\modules\mob\_modifiers\cloning.dm"
|
||||
#include "code\modules\mob\_modifiers\modifiers.dm"
|
||||
#include "code\modules\mob\_modifiers\traits.dm"
|
||||
#include "code\modules\mob\_modifiers\traits_phobias.dm"
|
||||
#include "code\modules\mob\dead\death.dm"
|
||||
#include "code\modules\mob\dead\observer\login.dm"
|
||||
#include "code\modules\mob\dead\observer\logout.dm"
|
||||
|
||||
Reference in New Issue
Block a user