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:
Neerti
2017-10-13 02:17:39 -04:00
committed by Anewbe
parent ef13c24d40
commit a8513844ec
23 changed files with 1102 additions and 15 deletions

View File

@@ -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

View File

@@ -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))

View File

@@ -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)

View File

@@ -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>"

View File

@@ -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
/****************************

View 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)
*/

View 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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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)]%"

View File

@@ -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

View 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)

View File

@@ -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

View File

@@ -1180,6 +1180,8 @@
hud_used = new /datum/hud(src)
if(species)
if(mind)
apply_traits()
return 1
else
return 0

View File

@@ -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

View File

@@ -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

View File

@@ -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))

View File

@@ -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)

View File

@@ -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

View File

@@ -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))

View File

@@ -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">

View File

@@ -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:

View File

@@ -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"