Merge pull request #5753 from Citadel-Station-13/upstream-merge-35440

[MIRROR] Adds minor roundstart traits! (ala CDDA, etc.)
This commit is contained in:
LetterJay
2018-03-02 23:37:41 -06:00
committed by GitHub
36 changed files with 810 additions and 52 deletions

View File

@@ -73,6 +73,7 @@
#define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow.
#define LIGHTING_PLANE_ALPHA_VISIBLE 255
#define LIGHTING_PLANE_ALPHA_NV_TRAIT 250
#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192
#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 //For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell.
#define LIGHTING_PLANE_ALPHA_INVISIBLE 0

View File

@@ -216,6 +216,10 @@
#define REAGENTS_METABOLISM 0.4 //How many units of reagent are consumed per tick, by default.
#define REAGENTS_EFFECT_MULTIPLIER (REAGENTS_METABOLISM / 0.4) // By defining the effect multiplier this way, it'll exactly adjust all effects according to how they originally were with the 0.4 metabolism
// Roundstart trait system
#define MAX_TRAITS 6 //The maximum amount of traits one character can have at roundstart
// AI Toggles
#define AI_CAMERA_LUMINOSITY 5
#define AI_VOX // Comment out if you don't want VOX to be enabled and have players download the voice sounds.

View File

@@ -56,13 +56,14 @@
#define INIT_ORDER_RESEARCH 14
#define INIT_ORDER_EVENTS 13
#define INIT_ORDER_JOBS 12
#define INIT_ORDER_TICKER 11
#define INIT_ORDER_MAPPING 10
#define INIT_ORDER_ATOMS 9
#define INIT_ORDER_NETWORKS 8
#define INIT_ORDER_LANGUAGE 7
#define INIT_ORDER_MACHINES 6
#define INIT_ORDER_CIRCUIT 5
#define INIT_ORDER_TRAITS 11
#define INIT_ORDER_TICKER 10
#define INIT_ORDER_MAPPING 9
#define INIT_ORDER_ATOMS 8
#define INIT_ORDER_NETWORKS 7
#define INIT_ORDER_LANGUAGE 6
#define INIT_ORDER_MACHINES 5
#define INIT_ORDER_CIRCUIT 4
#define INIT_ORDER_TIMER 1
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_AIR -1

View File

@@ -24,6 +24,18 @@
#define TRAIT_ANTIMAGIC "anti_magic"
#define TRAIT_HOLY "holy"
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
#define TRAIT_AGEUSIA "ageusia"
#define TRAIT_HEAVY_SLEEPER "heavy_sleeper"
#define TRAIT_NIGHT_VISION "night_vision"
#define TRAIT_LIGHT_STEP "light_step"
#define TRAIT_SPIRITUAL "spiritual"
#define TRAIT_VORACIOUS "voracious"
#define TRAIT_SELF_AWARE "self_aware"
#define TRAIT_FREERUNNING "freerunning"
#define TRAIT_SKITTISH "skittish"
#define TRAIT_POOR_AIM "poor_aim"
#define TRAIT_PROSOPAGNOSIA "prosopagnosia"
// common trait sources
#define TRAIT_GENERIC "generic"
@@ -33,6 +45,7 @@
#define MAGIC_TRAIT "magic"
#define TRAUMA_TRAIT "trauma"
#define SPECIES_TRAIT "species"
#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
// unique trait sources, still defines
#define STATUE_MUTE "statue"

View File

@@ -314,7 +314,7 @@
inv.hud = src
inv_slots[inv.slot_id] = inv
inv.update_icon()
update_locked_slots()
/datum/hud/human/update_locked_slots()

View File

@@ -276,6 +276,8 @@
/datum/config_entry/flag/ic_printing
/datum/config_entry/flag/roundstart_traits
/datum/config_entry/flag/enable_night_shifts
/datum/config_entry/flag/randomize_shift_time

View File

@@ -0,0 +1,36 @@
//Used to process and handle roundstart trait datums
//Trait datums are separate from trait strings:
// - Trait strings are used for faster checking in code
// - Trait datums are stored and hold different effects, as well as being a vector for applying trait string
PROCESSING_SUBSYSTEM_DEF(traits)
name = "Traits"
init_order = INIT_ORDER_TRAITS
flags = SS_BACKGROUND
wait = 10
runlevels = RUNLEVEL_GAME
var/list/traits = list() //Assoc. list of all roundstart trait datums; "name" = /path/
var/list/trait_points = list() //Assoc. list of trait names and their "point cost"; positive numbers are good traits, and negative ones are bad
var/list/trait_objects = list() //A list of all trait objects in the game, since some may process
/datum/controller/subsystem/processing/traits/Initialize(timeofday)
if(!traits.len)
SetupTraits()
..()
/datum/controller/subsystem/processing/traits/proc/SetupTraits()
for(var/V in subtypesof(/datum/trait))
var/datum/trait/T = V
traits[initial(T.name)] = T
trait_points[initial(T.name)] = initial(T.value)
/datum/controller/subsystem/processing/traits/proc/AssignTraits(mob/living/user, client/cli)
if(!isnewplayer(user))
GenerateTraits(cli)
for(var/V in cli.prefs.character_traits)
user.add_trait_datum(V)
/datum/controller/subsystem/processing/traits/proc/GenerateTraits(client/user)
if(user.prefs.character_traits.len)
return
user.prefs.character_traits = user.prefs.all_traits

View File

@@ -46,6 +46,8 @@
return
var/damage = rand(min_damage, max_damage)
if(H.has_trait(TRAIT_LIGHT_STEP))
damage *= 0.75
H.apply_damage(damage, BRUTE, picked_def_zone)
if(cooldown < world.time - 10) //cooldown to avoid message spam.

View File

@@ -0,0 +1,108 @@
//every trait in this folder should be coded around being applied on spawn
//these are NOT "mob traits" like GOTTAGOFAST, but exist as a medium to apply them and other different effects
/datum/trait
var/name = "Test Trait"
var/desc = "This is a test trait."
var/value = 0
var/human_only = TRUE
var/gain_text
var/lose_text
var/medical_record_text //This text will appear on medical records for the trait. Not yet implemented
var/mob_trait //if applicable, apply and remove this mob trait
var/mob/living/trait_holder
/datum/trait/New(mob/living/trait_mob)
..()
if(!trait_mob || (human_only && !ishuman(trait_mob)) || trait_mob.has_trait_datum(type))
qdel(src)
trait_holder = trait_mob
SStraits.trait_objects += src
to_chat(trait_holder, gain_text)
trait_holder.roundstart_traits += src
if(mob_trait)
trait_holder.add_trait(mob_trait, ROUNDSTART_TRAIT)
START_PROCESSING(SStraits, src)
add()
if(!SSticker.HasRoundStarted()) //on roundstart or on latejoin; latejoin code is in new_player.dm
on_spawn()
addtimer(CALLBACK(src, .proc/post_add), 30)
/datum/trait/Destroy()
STOP_PROCESSING(SStraits, src)
remove()
if(trait_holder)
to_chat(trait_holder, lose_text)
trait_holder.roundstart_traits -= src
if(mob_trait)
trait_holder.remove_trait(mob_trait, ROUNDSTART_TRAIT, TRUE)
SStraits.trait_objects -= src
return ..()
/datum/trait/proc/add() //special "on add" effects
/datum/trait/proc/on_spawn() //these should only trigger when the character is being created for the first time, i.e. roundstart/latejoin
/datum/trait/proc/remove() //special "on remove" effects
/datum/trait/proc/on_process() //process() has some special checks, so this is the actual process
/datum/trait/proc/post_add() //for text, disclaimers etc. given after you spawn in with the trait
/datum/trait/process()
if(QDELETED(trait_holder))
qdel(src)
return
on_process()
/mob/living/proc/get_trait_string(medical) //helper string. gets a string of all the traits the mob has
var/list/dat = list()
if(!medical)
for(var/V in roundstart_traits)
var/datum/trait/T = V
dat += T.name
if(!dat.len)
return "None"
return dat.Join(", ")
else
for(var/V in roundstart_traits)
var/datum/trait/T = V
dat += T.medical_record_text
if(!dat.len)
return "None"
return dat.Join("<br>")
/*
Commented version of Nearsighted to help you add your own traits
Use this as a guideline
/datum/trait/nearsighted
name = "Nearsighted"
///The trait's name
desc = "You are nearsighted without prescription glasses, but spawn with a pair."
///Short description, shows next to name in the trait panel
value = -1
///If this is above 0, it's a positive trait; if it's not, it's a negative one; if it's 0, it's a neutral
mob_trait = TRAIT_NEARSIGHT
///This define is in __DEFINES/traits.dm and is the actual "trait" that the game tracks
///You'll need to use "has_trait(X, sources)" checks around the code to check this; for instance, the Ageusia trait is checked in taste code
///If you need help finding where to put it, the declaration finder on GitHub is the best way to locate it
gain_text = "<span class='danger'>Things far away from you start looking blurry.</span>"
lose_text = "<span class='notice'>You start seeing faraway things normally again.</span>"
medical_record_text = "Subject has permanent nearsightedness."
///These three are self-explanatory
/datum/trait/nearsighted/on_spawn()
var/mob/living/carbon/human/H = trait_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
H.put_in_hands(glasses)
H.equip_to_slot(glasses, slot_glasses)
H.regenerate_icons()
//This whole proc is called automatically
//It spawns a set of prescription glasses on the user, then attempts to put it into their hands, then attempts to make them equip it.
//This means that if they fail to equip it, they glasses spawn in their hands, and if they fail to be put into the hands, they spawn on the ground
//Hooray for fallbacks!
//If you don't need any special effects like spawning glasses, then you don't need an add()
*/

View File

@@ -0,0 +1,83 @@
//predominantly positive traits
//this file is named weirdly so that positive traits are listed above negative ones
/datum/trait/alcohol_tolerance
name = "Alcohol Tolerance"
desc = "You become drunk more slowly and suffer fewer drawbacks from alcohol."
value = 1
mob_trait = TRAIT_ALCOHOL_TOLERANCE
gain_text = "<span class='notice'>You feel like you could drink a whole keg!</span>"
lose_text = "<span class='danger'>You don't feel as resistant to alcohol anymore. Somehow.</span>"
/datum/trait/freerunning
name = "Freerunning"
desc = "You're great at quick moves! You can climb tables more quickly."
value = 2
mob_trait = TRAIT_FREERUNNING
gain_text = "<span class='notice'>You feel lithe on your feet!</span>"
lose_text = "<span class='danger'>You feel clumsy again.</span>"
/datum/trait/light_step
name = "Light Step"
desc = "You walk with a gentle step, making stepping on sharp objects quieter and less painful."
value = 1
mob_trait = TRAIT_LIGHT_STEP
gain_text = "<span class='notice'>You walk with a little more lithenessk.</span>"
lose_text = "<span class='danger'>You start tromping around like a barbarian.</span>"
/datum/trait/night_vision
name = "Night Vision"
desc = "You can see slightly more clearly in full darkness than most people."
value = 1
mob_trait = TRAIT_NIGHT_VISION
gain_text = "<span class='notice'>The shadows seem a little less dark.</span>"
lose_text = "<span class='danger'>Everything seems a little darker.</span>"
/datum/trait/night_vision/on_spawn()
var/mob/living/carbon/human/H = trait_holder
var/obj/item/organ/eyes/eyes = H.getorgan(/obj/item/organ/eyes)
if(!eyes || eyes.lighting_alpha)
return
eyes.Insert(H) //refresh their eyesight and vision
/datum/trait/selfaware
name = "Self-Aware"
desc = "You know your body well, and can accurately assess the extent of your wounds."
value = 2
mob_trait = TRAIT_SELF_AWARE
/datum/trait/skittish
name = "Skittish"
desc = "You can conceal yourself in danger. Ctrl-shift-click a closed locker to jump into it, as long as you have access."
value = 2
mob_trait = TRAIT_SKITTISH
/datum/trait/spiritual
name = "Spiritual"
desc = "You're in tune with the gods, and your prayers may be more likely to be heard. Or not."
value = 1
mob_trait = TRAIT_SPIRITUAL
gain_text = "<span class='notice'>You feel a little more faithful to the gods today.</span>"
lose_text = "<span class='danger'>You feel less faithful in the gods.</span>"
/datum/trait/voracious
name = "Voracious"
desc = "Nothing gets between you and your food. You eat twice as fast as everyone else!"
value = 1
mob_trait = TRAIT_VORACIOUS
gain_text = "<span class='notice'>You feel HONGRY.</span>"
lose_text = "<span class='danger'>You no longer feel HONGRY.</span>"

View File

@@ -0,0 +1,156 @@
//predominantly negative traits
/datum/trait/heavy_sleeper
name = "Heavy Sleeper"
desc = "You sleep like a rock! Whenever you're put to sleep, you sleep for a little bit longer."
value = -1
mob_trait = TRAIT_HEAVY_SLEEPER
gain_text = "<span class='danger'>You feel sleepy.</span>"
lose_text = "<span class='notice'>You feel awake again.</span>"
medical_record_text = "Patient has abnormal sleep study results and is difficult to wake up."
/datum/trait/nearsighted //t. errorage
name = "Nearsighted"
desc = "You are nearsighted without prescription glasses, but spawn with a pair."
value = -1
gain_text = "<span class='danger'>Things far away from you start looking blurry.</span>"
lose_text = "<span class='notice'>You start seeing faraway things normally again.</span>"
medical_record_text = "Patient requires prescription glasses in order to counteract nearsightedness."
/datum/trait/nearsighted/add()
trait_holder.become_nearsighted(ROUNDSTART_TRAIT)
/datum/trait/nearsighted/on_spawn()
var/mob/living/carbon/human/H = trait_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
H.put_in_hands(glasses)
H.equip_to_slot(glasses, slot_glasses)
H.regenerate_icons() //this is to remove the inhand icon, which persists even if it's not in their hands
/datum/trait/nonviolent
name = "Pacifist"
desc = "The thought of violence makes you sick. So much so, in fact, that you can't hurt anyone."
value = -2
mob_trait = TRAIT_PACIFISM
gain_text = "<span class='danger'>You feel repulsed by the thought of violence!</span>"
lose_text = "<span class='notice'>You think you can defend yourself again.</span>"
medical_record_text = "Patient is unusually pacifistic and cannot bring themselves to cause physical harm."
/datum/trait/nonviolent/on_process()
if(trait_holder.mind && trait_holder.mind.antag_datums.len)
to_chat(trait_holder, "<span class='boldannounce'>Your antagonistic nature has caused you to renounce your pacifism.</span>")
qdel(src)
/datum/trait/poor_aim
name = "Poor Aim"
desc = "You're terrible with guns and can't line up a straight shot to save your life. Dual-wielding is right out."
value = -1
mob_trait = TRAIT_POOR_AIM
medical_record_text = "Patient possesses a strong tremor in both hands."
/datum/trait/prosopagnosia
name = "Prosopagnosia"
desc = "You have a mental disorder that prevents you from being able to recognize faces at all."
value = -1
mob_trait = TRAIT_PROSOPAGNOSIA
medical_record_text = "Patient suffers from prosopagnosia and cannot recognize faces."
/datum/trait/prosthetic_limb
name = "Prosthetic Limb"
desc = "An accident caused you to lose one of your limbs. Because of this, you now have a random prosthetic!"
value = -1
var/slot_string = "limb"
/datum/trait/prosthetic_limb/on_spawn()
var/limb_slot = pick("l_arm", "r_arm", "l_leg", "r_leg")
var/mob/living/carbon/human/H = trait_holder
var/obj/item/bodypart/old_part = H.get_bodypart(limb_slot)
var/obj/item/bodypart/prosthetic
switch(limb_slot)
if("l_arm")
prosthetic = new/obj/item/bodypart/l_arm/robot/surplus(trait_holder)
slot_string = "left arm"
if("r_arm")
prosthetic = new/obj/item/bodypart/r_arm/robot/surplus(trait_holder)
slot_string = "right arm"
if("l_leg")
prosthetic = new/obj/item/bodypart/l_leg/robot/surplus(trait_holder)
slot_string = "left leg"
if("r_leg")
prosthetic = new/obj/item/bodypart/r_leg/robot/surplus(trait_holder)
slot_string = "right leg"
prosthetic.replace_limb(H)
qdel(old_part)
H.regenerate_icons()
/datum/trait/prosthetic_limb/post_add()
to_chat(trait_holder, "<span class='boldannounce'>Your [slot_string] has been replaced with a surplus prosthetic. It is fragile and will easily come apart under duress. Additionally, \
you need to use a welding tool and cables to repair it, instead of bruise packs and ointment.</span>")
/datum/trait/insanity
name = "Reality Dissociation Syndrome"
desc = "You suffer from a severe disorder that causes very vivid hallucinations. Mindbreaker toxin can suppress its effects, and you are immune to mindbreaker's hallucinogenic properties. <b>This is not a license to grief.</b>"
value = -2
//no mob trait because it's handled uniquely
gain_text = "<span class='userdanger'>...</span>"
lose_text = "<span class='notice'>You feel in tune with the world again.</span>"
medical_record_text = "Patient suffers from acute Reality Dissociation Syndrome and experiences vivid hallucinations."
/datum/trait/insanity/on_process()
if(trait_holder.reagents.has_reagent("mindbreaker"))
trait_holder.hallucination = 0
return
if(prob(1)) //we'll all be mad soon enough
madness()
/datum/trait/insanity/proc/madness(mad_fools)
set waitfor = FALSE
if(!mad_fools)
mad_fools = prob(20)
if(mad_fools)
var/hallucination_type = pick(subtypesof(/datum/hallucination/rds))
new hallucination_type (trait_holder, FALSE)
else
trait_holder.hallucination += rand(10, 50)
/datum/trait/insanity/post_add() //I don't /think/ we'll need this but for newbies who think "roleplay as insane" = "license to kill" it's probably a good thing to have
if(!trait_holder.mind || trait_holder.mind.special_role)
return
to_chat(trait_holder, "<span class='big bold info'>Please note that your dissociation syndrome does NOT give you the right to attack people or otherwise cause any interference to \
the round. You are not an antagonist, and the rules will treat you the same as other crewmembers.</span>")
/datum/trait/social_anxiety
name = "Social Anxiety"
desc = "Talking to people is very difficult for you, and you often stutter or even lock up."
value = -1
gain_text = "<span class='danger'>You start worrying about what you're saying.</span>"
lose_text = "<span class='notice'>You feel easier about talking again.</span>" //if only it were that easy!
medical_record_text = "Patient is usually anxious in social encounters and prefers to avoid them."
var/dumb_thing = TRUE
/datum/trait/social_anxiety/on_process()
var/mob/living/carbon/human/H = trait_holder
if(prob(5))
H.stuttering = max(3, H.stuttering)
else if(prob(1) && !H.silent)
to_chat(H, "<span class='danger'>You retreat into yourself. You <i>really</i> don't feel up to talking.</span>")
H.silent = max(10, H.silent)
else if(prob(0.5) && dumb_thing)
to_chat(H, "<span class='danger'>You think of a dumb thing you said a long time ago and scream internally.</span>")
dumb_thing = FALSE //only once per life

View File

@@ -0,0 +1,33 @@
//traits with no real impact that can be taken freely
//MAKE SURE THESE DO NOT MAJORLY IMPACT GAMEPLAY. those should be positive or negative traits.
/datum/trait/no_taste
name = "Ageusia"
desc = "You can't taste anything! Toxic food will still poison you."
value = 0
mob_trait = TRAIT_AGEUSIA
gain_text = "<span class='notice'>You can't taste anything!</span>"
lose_text = "<span class='notice'>You can taste again!</span>"
medical_record_text = "Patient suffers from ageusia and is incapable of tasting food or reagents."
/datum/trait/deviant_tastes
name = "Deviant Tastes"
desc = "You dislike food that most people enjoy, and find delicious what they don't."
value = 0
gain_text = "<span class='notice'>You start craving something that tastes strange.</span>"
lose_text = "<span class='notice'>You feel like eating normal food again.</span>"
/datum/trait/deviant_tastes/add()
var/mob/living/carbon/human/H = trait_holder
var/datum/species/species = H.dna.species
var/liked = species.liked_food
species.liked_food = species.disliked_food
species.disliked_food = liked
/datum/trait/deviant_tastes/remove()
var/mob/living/carbon/human/H = trait_holder
var/datum/species/species = H.dna.species
species.liked_food = initial(species.liked_food)
species.disliked_food = initial(species.disliked_food)

View File

@@ -172,6 +172,8 @@ GAS ANALYZER
trauma_desc += B.scan_desc
trauma_text += trauma_desc
to_chat(user, "\t<span class='alert'>Cerebral traumas detected: subjects appears to be suffering from [english_list(trauma_text)].</span>")
if(C.roundstart_traits.len)
to_chat(user, "\t<span class='info'>Subject has the following physiological traits: [C.get_trait_string()].</span>")
if(advanced)
to_chat(user, "\t<span class='info'>Brain Activity Level: [(200 - M.getBrainLoss())/2]%.</span>")
if (M.radiation)

View File

@@ -304,5 +304,8 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
/obj/item/shard/Crossed(mob/living/L)
if(istype(L) && has_gravity(loc))
playsound(loc, 'sound/effects/glass_step.ogg', 50, 1)
if(L.has_trait(TRAIT_LIGHT_STEP))
playsound(loc, 'sound/effects/glass_step.ogg', 30, 1)
else
playsound(loc, 'sound/effects/glass_step.ogg', 50, 1)
. = ..()

View File

@@ -74,6 +74,8 @@
adjusted_climb_time *= 2
if(isalien(user))
adjusted_climb_time *= 0.25 //aliens are terrifyingly fast
if(user.has_trait(TRAIT_FREERUNNING)) //do you have any idea how fast I am???
adjusted_climb_time *= 0.8
structureclimber = user
if(do_mob(user, user, adjusted_climb_time))
if(src.loc) //Checking if structure has been destroyed

View File

@@ -397,7 +397,14 @@
else
togglelock(user)
/obj/structure/closet/proc/togglelock(mob/living/user)
/obj/structure/closet/CtrlShiftClick(mob/living/user)
if(!user.has_trait(TRAIT_SKITTISH))
return ..()
if(!user.canUseTopic(src) || !isturf(user.loc))
return
dive_into(user)
/obj/structure/closet/proc/togglelock(mob/living/user, silent)
if(secure && !broken)
if(allowed(user))
if(iscarbon(user))
@@ -406,7 +413,7 @@
user.visible_message("<span class='notice'>[user] [locked ? null : "un"]locks [src].</span>",
"<span class='notice'>You [locked ? null : "un"]lock [src].</span>")
update_icon()
else
else if(!silent)
to_chat(user, "<span class='notice'>Access Denied</span>")
else if(secure && broken)
to_chat(user, "<span class='warning'>\The [src] is broken!</span>")
@@ -456,3 +463,23 @@
/obj/structure/closet/return_temperature()
return
/obj/structure/closet/proc/dive_into(mob/living/user)
var/turf/T1 = get_turf(user)
var/turf/T2 = get_turf(src)
if(!open() && !opened)
togglelock(user, TRUE)
if(!open())
to_chat(user, "<span class='warning'>It won't budge!</span>")
return
step_towards(user, T2)
T1 = get_turf(user)
if(T1 == T2)
user.resting = TRUE //so people can jump into crates without slamming the lid on their head
if(!close())
to_chat(user, "<span class='warning'>You can't get [src] to close!</span>")
user.resting = FALSE
return
user.resting = FALSE
togglelock(user)
T1.visible_message("<span class='warning'>[user] dives into [src]!</span>")

View File

@@ -32,6 +32,12 @@
font_color = "red"
prayer_type = "CULTIST PRAYER"
deity = "Nar-Sie"
else if(isliving(usr))
var/mob/living/L = usr
if(L.has_trait(TRAIT_SPIRITUAL))
cross.icon_state = "holylight"
font_color = "blue"
prayer_type = "SPIRITUAL PRAYER"
msg = "<span class='adminnotice'>[icon2html(cross, GLOB.admins)]<b><font color=[font_color]>[prayer_type][deity ? " (to [deity])" : ""]: </font>[ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]:</b> [msg]</span>"

View File

@@ -149,6 +149,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//Mob preview
var/icon/preview_icon = null
//Trait list
var/list/positive_traits = list()
var/list/negative_traits = list()
var/list/neutral_traits = list()
var/list/all_traits = list()
var/list/character_traits = list()
//Jobs, uses bitflags
var/job_civilian_high = 0
var/job_civilian_med = 0
@@ -252,6 +259,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<center><h2>Occupation Choices</h2>"
dat += "<a href='?_src_=prefs;preference=job;task=menu'>Set Occupation Preferences</a><br></center>"
if(CONFIG_GET(flag/roundstart_traits))
dat += "<center><h2>Trait Setup</h2>"
dat += "<a href='?_src_=prefs;preference=trait;task=menu'>Configure Traits</a><br></center>"
dat += "<center><b>Current traits:</b> [all_traits.len ? all_traits.Join(", ") : "None"]</center>"
dat += "<h2>Identity</h2>"
dat += "<table width='100%'><tr><td width='75%' valign='top'>"
if(jobban_isbanned(user, "appearance"))
@@ -868,6 +879,65 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return job_engsec_low
return 0
/datum/preferences/proc/SetTraits(mob/user)
if(!SStraits)
to_chat(user, "<span class='danger'>The trait subsystem is still initializing! Try again in a minute.</span>")
return
var/list/dat = list()
if(!SStraits.traits.len)
dat += "The trait subsystem hasn't finished initializing, please hold..."
dat += "<center><a href='?_src_=prefs;preference=trait;task=close'>Done</a></center><br>"
else
dat += "<center><b>Choose trait setup</b></center><br>"
dat += "<div align='center'>Left-click to add or remove traits. You need one negative trait for every positive trait.<br>\
Traits are applied at roundstart and cannot normally be removed.</div>"
dat += "<center><a href='?_src_=prefs;preference=trait;task=close'>Done</a></center>"
dat += "<hr>"
dat += "<center><b>Current traits:</b> [all_traits.len ? all_traits.Join(", ") : "None"]</center>"
/*dat += "<center><font color='#AAFFAA'>[positive_traits.len] / [MAX_POSITIVE_TRAITS]</font> \
| <font color='#AAAAFF'>[neutral_traits.len] / [MAX_NEUTRAL_TRAITS]</font> \
| <font color='#FFAAAA'>[negative_traits.len] / [MAX_NEGATIVE_TRAITS]</font></center><br>"*/
dat += "<center>[all_traits.len] / [MAX_TRAITS] max traits<br>\
<b>Trait balance remaining:</b> [GetTraitBalance()]</center><br>"
for(var/V in SStraits.traits)
var/datum/trait/T = SStraits.traits[V]
var/trait_name = initial(T.name)
var/has_trait
var/trait_cost = initial(T.value) * -1
for(var/_V in all_traits)
if(_V == trait_name)
has_trait = TRUE
if(has_trait)
trait_cost *= -1 //invert it back, since we'd be regaining this amount
if(trait_cost > 0)
trait_cost = "+[trait_cost]"
var/font_color = "#AAAAFF"
if(initial(T.value) != 0)
font_color = initial(T.value) > 0 ? "#AAFFAA" : "#FFAAAA"
if(has_trait)
dat += "<b><font color='[font_color]'>[trait_name]</font></b> - [initial(T.desc)] \
<a href='?_src_=prefs;preference=trait;task=update;trait=[trait_name]'>[has_trait ? "Lose" : "Take"] ([trait_cost] pts.)</a><br>"
else
dat += "<font color='[font_color]'>[trait_name]</font> - [initial(T.desc)] \
<a href='?_src_=prefs;preference=trait;task=update;trait=[trait_name]'>[has_trait ? "Lose" : "Take"] ([trait_cost] pts.)</a><br>"
dat += "<br><center><a href='?_src_=prefs;preference=trait;task=reset'>Reset Traits</a></center>"
user << browse(null, "window=preferences")
var/datum/browser/popup = new(user, "mob_occupation", "<div align='center'>Trait Preferences</div>", 900, 600) //no reason not to reuse the occupation window, as it's cleaner that way
popup.set_window_options("can_close=0")
popup.set_content(dat.Join())
popup.open(0)
return
/datum/preferences/proc/GetTraitBalance()
var/bal = 0
for(var/V in all_traits)
var/datum/trait/T = SStraits.traits[V]
bal -= initial(T.value)
return bal
/datum/preferences/proc/process_link(mob/user, list/href_list)
if(href_list["jobbancheck"])
var/job = sanitizeSQL(href_list["jobbancheck"])
@@ -915,6 +985,64 @@ GLOBAL_LIST_EMPTY(preferences_datums)
SetChoices(user)
return 1
else if(href_list["preference"] == "trait")
if(SSticker.HasRoundStarted() && !isnewplayer(user))
to_chat(user, "<span class='danger'>The round has already started. Please wait until next round to set up your traits!</span>")
return
switch(href_list["task"])
if("close")
user << browse(null, "window=mob_occupation")
ShowChoices(user)
if("update")
var/trait = href_list["trait"]
var/value = SStraits.trait_points[trait]
if(value == 0)
if(trait in neutral_traits)
neutral_traits -= trait
all_traits -= trait
else
if(all_traits.len >= MAX_TRAITS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_TRAITS] traits!</span>")
return
neutral_traits += trait
all_traits += trait
else
var/balance = GetTraitBalance()
if(trait in positive_traits)
positive_traits -= trait
all_traits -= trait
else if(trait in negative_traits)
if(balance + value < 0)
to_chat(user, "<span class='warning'>Refunding this would cause you to go below your balance!</span>")
return
negative_traits -= trait
all_traits -= trait
else if(value > 0)
if(all_traits.len >= MAX_TRAITS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_TRAITS] traits!</span>")
return
if(balance - value < 0)
to_chat(user, "<span class='warning'>You don't have enough balance to gain this trait!</span>")
return
positive_traits += trait
all_traits += trait
else
if(all_traits.len >= MAX_TRAITS)
to_chat(user, "<span class='warning'>You can't have more than [MAX_TRAITS] traits!</span>")
return
negative_traits += trait
all_traits += trait
SetTraits(user)
if("reset")
all_traits = list()
positive_traits = list()
negative_traits = list()
neutral_traits = list()
SetTraits(user)
else
SetTraits(user)
return TRUE
switch(href_list["task"])
if("random")
switch(href_list["preference"])
@@ -1781,3 +1909,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.update_hair()
character.update_body_parts()
character.update_genitals()
if(CONFIG_GET(flag/roundstart_traits))
SStraits.AssignTraits(character, parent)

View File

@@ -369,6 +369,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["job_engsec_med"] >> job_engsec_med
S["job_engsec_low"] >> job_engsec_low
//Traits
S["all_traits"] >> all_traits
S["positive_traits"] >> positive_traits
S["negative_traits"] >> negative_traits
S["neutral_traits"] >> neutral_traits
//Citadel code
S["feature_genitals_use_skintone"] >> features["genitals_use_skintone"]
S["feature_exhibitionist"] >> features["exhibitionist"]
@@ -477,6 +483,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
all_traits = SANITIZE_LIST(all_traits)
positive_traits = SANITIZE_LIST(positive_traits)
negative_traits = SANITIZE_LIST(negative_traits)
neutral_traits = SANITIZE_LIST(neutral_traits)
cit_character_pref_load(S)
return 1
@@ -542,6 +553,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["job_engsec_med"] , job_engsec_med)
WRITE_FILE(S["job_engsec_low"] , job_engsec_low)
//Traits
WRITE_FILE(S["all_traits"] , all_traits)
WRITE_FILE(S["positive_traits"] , positive_traits)
WRITE_FILE(S["negative_traits"] , negative_traits)
WRITE_FILE(S["neutral_traits"] , neutral_traits)
cit_character_pref_save(S)
return 1

View File

@@ -1197,3 +1197,28 @@ GLOBAL_LIST_INIT(hallucinations_major, list(
H.preparePixelProjectile(target, start)
H.fire()
qdel(src)
//Reality Dissociation Syndrome hallucinations only trigger in special cases and have no cost
/datum/hallucination/rds
cost = 0
/datum/hallucination/rds/fourth_wall/New(mob/living/carbon/C, forced = TRUE)
..()
to_chat(C, "<span class='userdanger extremelybig'>[pick("Leave the server" , "Close the game window")] [pick("immediately", "right now")].</span>")
/datum/hallucination/rds/supermatter/New(mob/living/carbon/C, forced = TRUE)
..()
SEND_SOUND(C, 'sound/magic/charge.ogg')
to_chat(C, "<span class='boldannounce'>You feel reality distort for a moment...</span>")
/datum/hallucination/rds/narsie/New(mob/living/carbon/C, forced = TRUE)
C.playsound_local(C, 'sound/creatures/narsie_rises.ogg', 50, FALSE, pressure_affected = FALSE)
to_chat(C, "<span class='narsie'>NAR-SIE HAS RISEN</span>")
/datum/hallucination/rds/ark/New(mob/living/carbon/C, forced = TRUE)
set waitfor = FALSE
..()
C.playsound_local(C, 'sound/machines/clockcult/ark_deathrattle.ogg', 50, FALSE, pressure_affected = FALSE)
C.playsound_local(C, 'sound/effects/clockcult_gateway_disrupted.ogg', 50, FALSE, pressure_affected = FALSE)
sleep(27)
C.playsound_local(C, 'sound/effects/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE)

View File

@@ -21,7 +21,7 @@
else
gulp_size = max(round(reagents.total_volume / 5), 5)
/obj/item/reagent_containers/food/drinks/attack(mob/M, mob/user, def_zone)
/obj/item/reagent_containers/food/drinks/attack(mob/living/M, mob/user, def_zone)
if(!reagents || !reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
@@ -36,6 +36,8 @@
if(M == user)
to_chat(M, "<span class='notice'>You swallow a gulp of [src].</span>")
if(M.has_trait(TRAIT_VORACIOUS))
M.changeNext_move(CLICK_CD_MELEE * 0.5) //chug! chug! chug!
else
M.visible_message("<span class='danger'>[user] attempts to feed the contents of [src] to [M].</span>", "<span class='userdanger'>[user] attempts to feed the contents of [src] to [M].</span>")

View File

@@ -19,13 +19,18 @@
if(last_check_time + 50 < world.time)
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(foodtype & H.dna.species.toxic_food)
to_chat(H,"<span class='warning'>What the hell was that thing?!</span>")
H.adjust_disgust(25 + 30 * fraction)
else if(foodtype & H.dna.species.disliked_food)
to_chat(H,"<span class='notice'>That didn't taste very good...</span>")
H.adjust_disgust(11 + 15 * fraction)
else if(foodtype & H.dna.species.liked_food)
to_chat(H,"<span class='notice'>I love this taste!</span>")
H.adjust_disgust(-5 + -2.5 * fraction)
if(!H.has_trait(TRAIT_AGEUSIA))
if(foodtype & H.dna.species.toxic_food)
to_chat(H,"<span class='warning'>What the hell was that thing?!</span>")
H.adjust_disgust(25 + 30 * fraction)
else if(foodtype & H.dna.species.disliked_food)
to_chat(H,"<span class='notice'>That didn't taste very good...</span>")
H.adjust_disgust(11 + 15 * fraction)
else if(foodtype & H.dna.species.liked_food)
to_chat(H,"<span class='notice'>I love this taste!</span>")
H.adjust_disgust(-5 + -2.5 * fraction)
else
if(foodtype & H.dna.species.toxic_food)
to_chat(H, "<span class='warning'>You don't feel so good...</span>")
H.adjust_disgust(25 + 30 * fraction)
last_check_time = world.time

View File

@@ -50,7 +50,7 @@
return
/obj/item/reagent_containers/food/snacks/attack(mob/M, mob/user, def_zone)
/obj/item/reagent_containers/food/snacks/attack(mob/living/M, mob/user, def_zone)
if(user.a_intent == INTENT_HARM)
return ..()
if(!eatverb)
@@ -82,6 +82,8 @@
else if(fullness > (600 * (1 + M.overeatduration / 2000))) // The more you eat - the more you can eat
to_chat(M, "<span class='warning'>You cannot force any more of \the [src] to go down your throat!</span>")
return 0
if(M.has_trait(TRAIT_VORACIOUS))
M.changeNext_move(CLICK_CD_MELEE * 0.5) //nom nom nom
else
if(!isbrain(M)) //If you're feeding it to someone else.
if(fullness <= (600 * (1 + M.overeatduration / 1000)))

View File

@@ -379,6 +379,10 @@
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5)
SSticker.mode.make_antag_chance(humanc)
for(var/V in character.roundstart_traits)
var/datum/trait/T = V
T.on_spawn() //so latejoins still get their correct traits
log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)
/mob/dead/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee)

View File

@@ -6,8 +6,14 @@
var/t_him = p_them()
var/t_has = p_have()
var/t_is = p_are()
var/obscure_name
var/msg = "<span class='info'>*---------*\nThis is <EM>[name]</EM>!\n"
if(isliving(user))
var/mob/living/L = user
if(L.has_trait(TRAIT_PROSOPAGNOSIA))
obscure_name = TRUE
var/msg = "<span class='info'>*---------*\nThis is <EM>[!obscure_name ? name : "Unknown"]</EM>!\n"
var/list/obscured = check_obscured_slots()
var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))
@@ -269,6 +275,7 @@
if(digitalcamo)
msg += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly inhuman manner.\n"
var/traitstring = get_trait_string()
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/cyberimp/eyes/hud/CIH = H.getorgan(/obj/item/organ/cyberimp/eyes/hud)
@@ -296,6 +303,10 @@
R = find_record("name", perpname, GLOB.data_core.medical)
if(R)
msg += "<a href='?src=[REF(src)];hud=m;evaluation=1'>\[Medical evaluation\]</a><br>"
if(traitstring)
msg += "<span class='info'>Detected physiological traits:<br></span>"
msg += "<span class='info'>[traitstring]</span><br>"
if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(CIH, /obj/item/organ/cyberimp/eyes/hud/security))
@@ -312,6 +323,10 @@
msg += "<a href='?src=[REF(src)];hud=s;add_crime=1'>\[Add crime\]</a> "
msg += "<a href='?src=[REF(src)];hud=s;view_comment=1'>\[View comment log\]</a> "
msg += "<a href='?src=[REF(src)];hud=s;add_comment=1'>\[Add comment\]</a>\n"
else if(isobserver(user) && traitstring)
msg += "<span class='info'><b>Traits:</b> [traitstring]</span><br>"
if(print_flavor_text() && get_visible_name() != "Unknown")//Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
msg += "[print_flavor_text()]\n"

View File

@@ -655,24 +655,33 @@
if(prob(30))
burndamage += rand(30,40)
if(brutedamage > 0)
status = "bruised"
if(brutedamage > 20)
status = "battered"
if(brutedamage > 40)
status = "mangled"
if(brutedamage > 0 && burndamage > 0)
status += " and "
if(burndamage > 40)
status += "peeling away"
if(has_trait(TRAIT_SELF_AWARE))
status = "[brutedamage] brute damage and [burndamage] burn damage"
if(!brutedamage && !burndamage)
status = "no damage"
else if(burndamage > 10)
status += "blistered"
else if(burndamage > 0)
status += "numb"
if(status == "")
status = "OK"
to_chat(src, "\t <span class='[status == "OK" ? "notice" : "warning"]'>Your [LB.name] is [status].</span>")
else
if(brutedamage > 0)
status = "bruised"
if(brutedamage > 20)
status = "battered"
if(brutedamage > 40)
status = "mangled"
if(brutedamage > 0 && burndamage > 0)
status += " and "
if(burndamage > 40)
status += "peeling away"
else if(burndamage > 10)
status += "blistered"
else if(burndamage > 0)
status += "numb"
if(status == "")
status = "OK"
var/no_damage
if(status == "OK" || status == "no damage")
no_damage = TRUE
to_chat(src, "\t <span class='[no_damage ? "notice" : "warning"]'>Your [LB.name] [has_trait(TRAIT_SELF_AWARE) ? "has" : "is"] [status].</span>")
for(var/obj/item/I in LB.embedded_objects)
to_chat(src, "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>")
@@ -687,6 +696,23 @@
to_chat(src, "<span class='info'>You're completely exhausted.</span>")
else
to_chat(src, "<span class='info'>You feel fatigued.</span>")
if(has_trait(TRAIT_SELF_AWARE))
if(toxloss)
if(toxloss > 10)
to_chat(src, "<span class='danger'>You feel sick.</span>")
else if(toxloss > 20)
to_chat(src, "<span class='danger'>You feel nauseous.</span>")
else if(toxloss > 40)
to_chat(src, "<span class='danger'>You feel very unwell!</span>")
if(oxyloss)
if(oxyloss > 10)
to_chat(src, "<span class='danger'>You feel lightheaded.</span>")
else if(oxyloss > 20)
to_chat(src, "<span class='danger'>Your thinking is clouded and distant.</span>")
else if(oxyloss > 30)
to_chat(src, "<span class='danger'>You're choking!</span>")
if(roundstart_traits.len)
to_chat(src, "<span class='notice'>You have these traits: [get_trait_string()].</span>")
else
if(wear_suit)
wear_suit.add_fingerprint(M)

View File

@@ -9,6 +9,13 @@
/mob/living/carbon/human/Unconscious(amount, updating = 1, ignore_canunconscious = 0)
amount = dna.species.spec_stun(src,amount)
if(has_trait(TRAIT_HEAVY_SLEEPER))
amount *= rand(1.25, 1.3)
return ..()
/mob/living/carbon/human/Sleeping(amount, updating = 1, ignore_sleepimmune = 0)
if(has_trait(TRAIT_HEAVY_SLEEPER))
amount *= rand(1.25, 1.3)
return ..()
/mob/living/carbon/human/cure_husk(list/sources)

View File

@@ -34,6 +34,8 @@
var/list/status_traits = list()
var/list/roundstart_traits = list()
var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them.
var/now_pushing = null //used by living/Collide() and living/PushAM() to prevent potential infinite loop.

View File

@@ -146,10 +146,23 @@
else
status_traits[trait] |= list(source)
/mob/living/proc/remove_trait(trait, list/sources)
/mob/living/proc/add_trait_datum(trait) //separate proc due to the way these ones are handled
if(has_trait(trait))
return
if(!SStraits || !SStraits.traits[trait])
return
var/datum/trait/T = SStraits.traits[trait]
new T (src)
return TRUE
/mob/living/proc/remove_trait(trait, list/sources, force)
if(!status_traits[trait])
return
if(locate(ROUNDSTART_TRAIT) in status_traits[trait] && !force) //mob traits applied through roundstart cannot normally be removed
return
if(!sources) // No defined source cures the trait entirely.
status_traits -= trait
return
@@ -167,6 +180,12 @@
if(!LAZYLEN(status_traits[trait]))
status_traits -= trait
/mob/living/proc/remove_trait_datum(trait)
var/datum/trait/T = roundstart_traits[trait]
if(T)
qdel(T)
return TRUE
/mob/living/proc/has_trait(trait, list/sources)
if(!status_traits[trait])
return FALSE
@@ -181,6 +200,9 @@
if(LAZYLEN(status_traits[trait]))
return TRUE
/mob/living/proc/has_trait_datum(trait)
return roundstart_traits[trait]
/mob/living/proc/remove_all_traits()
status_traits = list()

View File

@@ -9,7 +9,7 @@
/mob/living/carbon/get_taste_sensitivity()
var/obj/item/organ/tongue/tongue = getorganslot(ORGAN_SLOT_TONGUE)
if(istype(tongue))
if(istype(tongue) && !has_trait(TRAIT_AGEUSIA))
. = tongue.taste_sensitivity
else
. = 101 // can't taste anything without a tongue

View File

@@ -246,6 +246,8 @@
var/rand_spr = rand()
if(spread)
randomized_gun_spread = rand(0,spread)
if(user.has_trait(TRAIT_POOR_AIM)) //nice shootin' tex
bonus_spread += 25
var/randomized_bonus_spread = rand(0, bonus_spread)
if(burst_size > 1)

View File

@@ -37,9 +37,12 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.drunkenness < volume * boozepwr * ALCOHOL_THRESHOLD_MODIFIER)
H.drunkenness = max((H.drunkenness + (sqrt(volume) * boozepwr * ALCOHOL_RATE)), 0) //Volume, power, and server alcohol rate effect how quickly one gets drunk
var/booze_power = boozepwr
if(H.has_trait(TRAIT_ALCOHOL_TOLERANCE)) //we're an accomplished drinker
booze_power *= 0.7
H.drunkenness = max((H.drunkenness + (sqrt(volume) * booze_power * ALCOHOL_RATE)), 0) //Volume, power, and server alcohol rate effect how quickly one gets drunk
var/obj/item/organ/liver/L = H.getorganslot(ORGAN_SLOT_LIVER)
H.applyLiverDamage((max(sqrt(volume) * boozepwr * L.alcohol_tolerance, 0))/4)
H.applyLiverDamage((max(sqrt(volume) * booze_power * L.alcohol_tolerance, 0))/4)
return ..() || .
/datum/reagent/consumable/ethanol/reaction_obj(obj/O, reac_volume)
@@ -117,7 +120,8 @@ All effects don't start immediately, but rather get worse over time; the rate is
M.dizziness = max(0,M.dizziness-5)
M.drowsyness = max(0,M.drowsyness-3)
M.AdjustSleeping(-40, FALSE)
M.Jitter(5)
if(!M.has_trait(TRAIT_ALCOHOL_TOLERANCE))
M.Jitter(5)
..()
. = 1
@@ -150,7 +154,8 @@ All effects don't start immediately, but rather get worse over time; the rate is
M.drowsyness = max(0,M.drowsyness-7)
M.AdjustSleeping(-40)
M.adjust_bodytemperature(-5 * TEMPERATURE_DAMAGE_COEFFICIENT, BODYTEMP_NORMAL)
M.Jitter(5)
if(!M.has_trait(TRAIT_ALCOHOL_TOLERANCE))
M.Jitter(5)
return ..()
/datum/reagent/consumable/ethanol/vodka
@@ -305,7 +310,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
shot_glass_icon_state = "shotglassgreen"
/datum/reagent/consumable/ethanol/absinthe/on_mob_life(mob/living/M)
if(prob(10))
if(prob(10) && !M.has_trait(TRAIT_ALCOHOL_TOLERANCE))
M.hallucination += 4 //Reference to the urban myth
..()
@@ -556,7 +561,10 @@ All effects don't start immediately, but rather get worse over time; the rate is
glass_desc = "Heavy, hot and strong. Just like the Iron fist of the LAW."
/datum/reagent/consumable/ethanol/beepsky_smash/on_mob_life(mob/living/M)
M.Stun(40, 0)
if(M.has_trait(TRAIT_ALCOHOL_TOLERANCE))
M.Stun(30, 0) //this realistically does nothing to prevent chainstunning but will cause them to recover faster once it's out of their system
else
M.Stun(40, 0)
return ..()
/datum/reagent/consumable/ethanol/irish_cream
@@ -585,7 +593,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
/datum/reagent/consumable/ethanol/manly_dorf/on_mob_add(mob/living/M)
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.dna.check_mutation(DWARFISM))
if(H.dna.check_mutation(DWARFISM) || H.has_trait(TRAIT_ALCOHOL_TOLERANCE))
to_chat(H, "<span class='notice'>Now THAT is MANLY!</span>")
boozepwr = 5 //We've had worse in the mines
dorf_mode = TRUE
@@ -1148,8 +1156,9 @@ All effects don't start immediately, but rather get worse over time; the rate is
/datum/reagent/consumable/ethanol/atomicbomb/on_mob_life(mob/living/M)
M.set_drugginess(50)
M.confused = max(M.confused+2,0)
M.Dizzy(10)
if(!M.has_trait(TRAIT_ALCOHOL_TOLERANCE))
M.confused = max(M.confused+2,0)
M.Dizzy(10)
if (!M.slurring)
M.slurring = 1
M.slurring += 3

View File

@@ -184,7 +184,7 @@
/datum/reagent/toxin/mindbreaker
name = "Mindbreaker Toxin"
id = "mindbreaker"
description = "A powerful hallucinogen. Not a thing to be messed with."
description = "A powerful hallucinogen. Not a thing to be messed with. For some mental patients. it counteracts their symptoms and anchors them to reality."
color = "#B31008" // rgb: 139, 166, 233
toxpwr = 0
taste_description = "sourness"

View File

@@ -26,6 +26,8 @@
HMN.regenerate_icons()
else
eye_color = HMN.eye_color
if(HMN.has_trait(TRAIT_NIGHT_VISION) && !lighting_alpha)
lighting_alpha = LIGHTING_PLANE_ALPHA_NV_TRAIT
M.update_tint()
owner.update_sight()

View File

@@ -511,6 +511,9 @@ ALLOW_MISCREANTS
## Determines if players are allowed to print integrated circuits, uncomment to allow.
#IC_PRINTING
## Uncomment to allow roundstart trait selection in the character setup menu.
ROUNDSTART_TRAITS
## Enable night shifts ##
ENABLE_NIGHT_SHIFTS

View File

@@ -292,6 +292,7 @@
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\processing.dm"
#include "code\controllers\subsystem\processing\projectiles.dm"
#include "code\controllers\subsystem\processing\traits.dm"
#include "code\datums\action.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\armor.dm"
@@ -449,6 +450,10 @@
#include "code\datums\status_effects\gas.dm"
#include "code\datums\status_effects\neutral.dm"
#include "code\datums\status_effects\status_effect.dm"
#include "code\datums\traits\_trait.dm"
#include "code\datums\traits\good.dm"
#include "code\datums\traits\negative.dm"
#include "code\datums\traits\neutral.dm"
#include "code\datums\weather\weather.dm"
#include "code\datums\weather\weather_types\acid_rain.dm"
#include "code\datums\weather\weather_types\advanced_darkness.dm"