Ports a character customization option: Quirks (#28388)

* Makes the datum

* Initial Commit

* Gets TGUI menu not crashing

* Autodoc

* quirk menu bugged

* oh.

* Working menu + buttons

* trait defintes pt 1/?

* Moves traits around

* ACTUALLY START ON THE QUIRKS THEMSELVES

* TGUI Bundle Rebuild

* TGUI properly saves now

* Removes allergies for scope reasons, make foreigner work properly

* TGUI tweaks

* Reworks TGUI Menu

* quirks store the mob, not mind

* Adds asthma and mute

* minor tweaks

* Cloning re-adds quirks

* Adds functionality to frail

* Adds crafty

* TGUI fix

* Fixes a frail bug

* Makes sure the list is in the correct format

* TGUI size change

* Makes skittish work

* Adds glutton, removes unused traits

* Changes description and adds function to glutton

* Freerunner functioning

* Crafty buff

* Clear quirk list when changing species

* Forgot to remove light step

* Delete

* Refactor item-giving

* TGUI Bundle Rebuild

* whoops

* Apply quirks when changing species

* Removes some unneeded defines/paths

* Replaces improved charging cord with a bad charger

* coment

* Guard clause

* Changes how quirks add and remove organs

* unused var

* adding organs works, removing them doesn't yet

* oh that fixed it

* Hopefully finally once and for all fix the json issues

* Buffs asthma

* switches the processor to ssobj since it's slower

* actually spawns the items whoops

* maybe actually fixes json issues I really hope this doesn't come up again?

* moves sql to the right directory

* CULPRIT FOUND, JSON ISSUES ACTUALLY SOLVED

* adds admin tooling for quirks

* Ups the cost of lungs

* Adds removal for the drinking ones

* Fixes some duplicate code

* Removing foreigner works

* wops

* bumps sql version to 65

* prettier

* semicolon

* Foreigners can understand galcomm but not speak it, as per headmin request

* Different solution was agreed upon

* CC announcements now get auto-translated

* Apply suggestions from code review

Lewc quick suggestions

Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: BiancaWilkson <42818125+BiancaWilkson@users.noreply.github.com>

* Move alcohol tolerance basetype

* accomodates for reset_styles

* Swap to species flags, and being dead is a universal process stopper

* adds a foreigner trait

* Xenos and the like can't understand CC's translated announcements

* TGUI Bundle Rebuild

* Lewc quick fixes

Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: BiancaWilkson <42818125+BiancaWilkson@users.noreply.github.com>

* i forgor

* i forgor pt 2

* process properly returns true

* TGUI Bundle Rebuild

* sql update for fixing merge conflicts

* bundle changes

* actual sql fix

* Not test tiny in prod

* sql fix?

* tgui update, first pass

* Adds schema changes to main quirk branch

* persistent data works now

* TGUI Bundle Rebuild

* Fixes conflict with job assignment datumization

* TGUI Bundle Rebuild

* Adds pacifism by request, -3 points

* gives skittish a small cd

* Makes the lunch randomizer  I was thinking about doing

* GC returns a qdel hint now

* Removes hard refs

* SQL save fix

* Fixes admin tools

* Fixes wizards getting quirked up

* ragin too

* Lightweight is more lightweight

* Adds admin logging for when someone makes a languagesless character

* TGUI Bundle Rebuild

* Adds colorblind, hungry, and buffs asthma

* fixed vv post-merge conflict

* behold, my monster

* rebuild and merge master

* Adds more positive quirks, and makes monochrome give more points

* adds nearsighted, and no whispering

* sorry linter

* Adds the cool quirk, thanks to pumpkin312

* Oh dear that signal gets sent to everyone every time someone spawns in

* i forgot that slimes dont have eyes

* tgui fix

* bundle rebuild probably

* prettier

* prettier

* Skittish package fix

* force add bundle

* Makes plasmemes be unable to take foreigner

* Makes advanced lungs respect the owner's species

* makes breaking open wrapped crates/lockers a do_after_once

* Makes plasmeme/vox advanced lungs actually help

* You get glasses anyway whoops

* bundle rebuild

---------

Signed-off-by: BiancaWilkson <42818125+BiancaWilkson@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: warriorstar-orion <orion@snowfrost.garden>
This commit is contained in:
BiancaWilkson
2025-11-03 19:18:52 -05:00
committed by GitHub
parent 99d57fd35d
commit d558a69583
49 changed files with 971 additions and 78 deletions
+5 -2
View File
@@ -653,12 +653,15 @@ Traitors and the like can also be revived with the previous role mostly intact.
switch(alert("Should this be announced to the general population?", null,"Yes","No", "Cancel"))
if("Yes")
var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound
var/should_translate = alert(usr, "Should it be auto-translated for all human mobs?", "Translation", "Yes", "No")
if(should_translate != "Yes")
should_translate = null // We can just do it like this since force_translation just needs any value to not be false/null
GLOB.major_announcement.Announce(
message,
new_title = type,
new_subtitle = subtitle,
new_sound = MsgSound[beepsound]
new_sound = MsgSound[beepsound],
force_translation = should_translate
)
print_command_report(message, subtitle)
if("No")
@@ -64,7 +64,8 @@
height,
cyborg_brain_type,
body_type,
pda_ringtone
pda_ringtone,
quirks
FROM characters WHERE ckey=:ckey"}, list(
"ckey" = C.ckey
))
+41 -5
View File
@@ -110,6 +110,8 @@
var/runechat_color = "#FFFFFF"
/// The ringtone their PDA should start with
var/pda_ringtone
/// A list/JSON of the quirk datums to attach to the character
var/list/quirks = list()
// Fuckery to prevent null characters
/datum/character_save/New()
@@ -131,6 +133,8 @@
playertitlelist = list2params(player_alt_titles)
if(length(loadout_gear))
gearlist = json_encode(loadout_gear)
if(islist(quirks))
quirks = json_encode(quirks)
var/datum/db_query/firstquery = SSdbcore.NewQuery("SELECT slot FROM characters WHERE ckey=:ckey ORDER BY slot", list(
"ckey" = C.ckey
@@ -221,7 +225,8 @@
runechat_color=:runechat_color,
cyborg_brain_type=:cyborg_brain_type,
body_type=:body_type,
pda_ringtone=:pda_ringtone
pda_ringtone=:pda_ringtone,
quirks=:quirks
WHERE ckey=:ckey
AND slot=:slot"}, list(
// OH GOD SO MANY PARAMETERS
@@ -286,7 +291,8 @@
"cyborg_brain_type" = cyborg_brain_type,
"pda_ringtone" = pda_ringtone,
"ckey" = C.ckey,
"slot" = slot_number
"slot" = slot_number,
"quirks" = quirks
))
if(!query.warn_execute())
@@ -325,7 +331,7 @@
player_alt_titles,
disabilities, organ_data, rlimb_data, nanotrasen_relation, physique, height, speciesprefs,
socks, body_accessory, gear, autohiss,
hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha, custom_emotes, runechat_color, cyborg_brain_type, body_type, pda_ringtone)
hair_gradient, hair_gradient_offset, hair_gradient_colour, hair_gradient_alpha, custom_emotes, runechat_color, cyborg_brain_type, body_type, pda_ringtone, quirks)
VALUES
(:ckey, :slot, :metadata, :name, :be_random_name, :gender,
:age, :species, :language,
@@ -352,7 +358,7 @@
:playertitlelist,
:disabilities, :organ_list, :rlimb_list, :nanotrasen_relation, :physique, :height, :speciesprefs,
:socks, :body_accessory, :gearlist, :autohiss_mode,
:h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha, :custom_emotes, :runechat_color, :cyborg_brain_type, :body_type, :pda_ringtone)
:h_grad_style, :h_grad_offset, :h_grad_colour, :h_grad_alpha, :custom_emotes, :runechat_color, :cyborg_brain_type, :body_type, :pda_ringtone, :quirks)
"}, list(
// This has too many params for anyone to look at this without going insae
"ckey" = C.ckey,
@@ -416,7 +422,8 @@
"custom_emotes" = json_encode(custom_emotes),
"runechat_color" = runechat_color,
"cyborg_brain_type" = cyborg_brain_type,
"pda_ringtone" = pda_ringtone
"pda_ringtone" = pda_ringtone,
"quirks" = quirks
))
if(!query.warn_execute())
@@ -512,6 +519,8 @@
body_type = query.item[60]
pda_ringtone = query.item[61]
quirks = query.item[62]
//Sanitize
var/datum/species/SP = GLOB.all_species[species]
if(!SP)
@@ -599,6 +608,7 @@
runechat_color = sanitize_hexcolor(runechat_color)
cyborg_brain_type = sanitize_inlist(cyborg_brain_type, GLOB.borg_brain_choices, initial(cyborg_brain_type))
pda_ringtone = sanitize_inlist(pda_ringtone, GLOB.pda_ringtone_choices, initial(pda_ringtone))
quirks = sanitize_json(quirks)
if(!player_alt_titles)
player_alt_titles = new()
if(!organ_data)
@@ -1885,6 +1895,11 @@
character.set_species(S.type, delay_icon_update = TRUE) // Yell at me if this causes everything to melt
if(be_random_name)
real_name = random_name(gender, species)
var/balance_check = rebuild_quirks()
for(var/datum/quirk/to_add in quirks)
to_add.apply_quirk_effects(character)
if(balance_check > 0)
log_debug("[src] spawned in with more quirks than they should have been able to. Quirk balance of [balance_check].")
character.add_language(language)
character.real_name = real_name
@@ -2135,6 +2150,9 @@
if(job.barred_by_disability(user.client))
html += "<del class='dark'>[rank]</del></td><td class='bad'><b> \[DISABILITY\]</b></td></tr>"
continue
if(job.barred_by_quirk(user.client))
html += "<del class='dark'>[rank]</del></td><td class='bad'><b> \[QUIRK\]</b></td></tr>"
continue
if(job.barred_by_missing_limbs(user.client))
html += "<del class='dark'>[rank]</del></td><td class='bad'><b> \[MISSING LIMBS\]</b></td></tr>"
continue
@@ -2274,3 +2292,21 @@
custom_emotes[custom_emote.name] = emote_text
return custom_emotes
/*
* This serves two purposes. It tallies up and returns the total quirk balance of the current loadout
* It also rebuilds then entire list of quirks, converting the JSON format it could be into a usable datum.
*/
/datum/character_save/proc/rebuild_quirks()
var/point_total = 0
if(!islist(quirks)) // If it's not a normal list then it has to be JSON. Or something went horribly wrong.
quirks = json_decode(quirks)
var/list/quirk_cache = quirks.Copy()
quirks.Cut()
for(var/quirk_name in quirk_cache)
var/datum/quirk/chosen_quirk = GLOB.quirk_paths["[quirk_name]"]
var/datum/quirk/quirk = new chosen_quirk.type // Don't want hard refs to the global list
if(!quirk)
continue
point_total += quirk.cost
quirks += quirk
return point_total
@@ -199,6 +199,7 @@
to_chat(user, "<span class='warning'>Invalid species, please pick something else.</span>")
return
if(prev_species != active_character.species)
active_character.quirks = list() //Reset their quirks
active_character.age = clamp(active_character.age, NS.min_age, NS.max_age)
var/datum/robolimb/robohead
if(NS.bodyflags & ALL_RPARTS)
@@ -682,6 +683,11 @@
loadout.ui_interact(user)
return FALSE
if("quirks")
var/datum/ui_module/quirk/quirk = new()
quirk.ui_interact(user)
return FALSE
if("nt_relation")
var/new_relation = tgui_input_list(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference", list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed"))
if(new_relation)
@@ -187,7 +187,7 @@ GLOBAL_LIST_INIT(special_role_times, list(
active_character.species = initial(active_character.species)
S = GLOB.all_species[active_character.species]
active_character.randomise()
active_character.rebuild_quirks()
dat += "<div class='statusDisplay' style='max-width: 128px; position: absolute; left: 150px; top: 150px'><img src=previewicon.png class='charPreview'><img src=previewicon2.png class='charPreview'></div>"
dat += "<table width='100%'><tr><td width='405px' height='25px' valign='top'>"
dat += "<b>Name: </b>"
@@ -397,6 +397,7 @@ GLOBAL_LIST_INIT(special_role_times, list(
dat += "<b>Socks:</b> <a href='byond://?_src_=prefs;preference=socks;task=input'>[active_character.socks]</a><BR>"
dat += "<b>Backpack Type:</b> <a href='byond://?_src_=prefs;preference=bag;task=input'>[active_character.backbag]</a><br><br>"
dat += "<a style='font-size: 1.5em;' href='byond://?_src_=prefs;preference=loadout;task=input'>Open Loadout</a><br>"
dat += "<a href='byond://?_src_=prefs;preference=quirks;task=input'>Open Quirk Menu</a><br>"
var/datum/species/myspecies = GLOB.all_species[active_character.species]
if(!isnull(myspecies))
@@ -0,0 +1,153 @@
/datum/quirk/alcohol_tolerance
var/alcohol_modifier = 1
/datum/quirk/alcohol_tolerance/apply_quirk_effects(mob/living/quirky)
..()
owner.physiology.alcohol_mod *= alcohol_modifier
/datum/quirk/alcohol_tolerance/remove_quirk_effects()
..()
owner.physiology.alcohol_mod /= alcohol_modifier
/datum/quirk/alcohol_tolerance/lightweight
name = "Lightweight"
desc = "You can't handle liquor very well, and get drunker quicker."
cost = -1
alcohol_modifier = 1.5
/datum/quirk/foreigner
name = "Foreigner"
desc = "You just recently joined the greater galactic community, and don't understand the common tongue yet. You cannot sign up for a command or security position."
cost = -2
item_to_give = /obj/item/taperecorder
blacklisted = TRUE
trait_to_apply = TRAIT_FOREIGNER
species_flags = QUIRK_PLASMAMAN_INCOMPATIBLE
/datum/quirk/foreigner/apply_quirk_effects(mob/living/quirky)
..()
owner.remove_language("Galactic Common")
if(!length(owner.languages))
log_admin("[owner] set up a character with no known languages.") // It's possible to do this but I have no idea how to prevent it without just giving them back galcom for free, so admins can ask them to not do that
return
owner.set_default_language(quirky.languages[1]) // set_default_language needs to be passed a direct reference to the user's language list
/datum/quirk/foreigner/remove_quirk_effects()
owner.add_language("Galactic Common")
..()
/datum/quirk/deaf
name = "Deafness"
desc = "You are incurably deaf, and cannot take a command or security position."
cost = -4
trait_to_apply = TRAIT_DEAF
blacklisted = TRUE
/datum/quirk/blind
name = "Blind"
desc = "You are incurably blind, and cannot take a command or security position."
cost = -4
trait_to_apply = TRAIT_BLIND
blacklisted = TRUE
item_to_give = /obj/item/clothing/glasses/sunglasses/blindfold
item_slot = ITEM_SLOT_EYES
/datum/quirk/mute
name = "Mute"
desc = "You are incurably mute, and cannot take a command or security position."
cost = -3
blacklisted = TRUE
trait_to_apply = TRAIT_MUTE
/datum/quirk/frail
name = "Frail"
desc = "You get major injuries much easier than most people."
cost = -3
trait_to_apply = TRAIT_FRAIL
#define ASTHMA_ATTACK_THRESHOLD 50
/datum/quirk/asthma
name = "Asthma"
desc = "You have trouble catching your breath, and can have violent coughing fits when exerting yourself. IPCs cannot take this."
cost = -3
species_flags = QUIRK_MACHINE_INCOMPATIBLE
trait_to_apply = TRAIT_ASTHMATIC
processes = TRUE
item_to_give = /obj/item/reagent_containers/pill/salbutamol // If an inhaler ever gets made put it here
/datum/quirk/asthma/process()
if(!..())
return
var/ease_of_breathing = owner.getOxyLoss() + owner.getStaminaLoss() / 2
if(ease_of_breathing < ASTHMA_ATTACK_THRESHOLD)
return
owner.emote("cough")
if(prob(ease_of_breathing / 4))
trigger_asthma_symptom(ease_of_breathing)
/* Causes an asthmatic flareup, which gets worse depending on how much oxygen and stamina damage the owner already has.
* If a bad attack isn't treated, it can easily feed into itself and kill the user.
*/
/datum/quirk/asthma/proc/trigger_asthma_symptom(current_severity)
owner.visible_message("<span class='notice'>[owner] violently coughs!</span>", "<span class='warning'>Your asthma flares up!</span>")
switch(current_severity)
if(50 to 75)
owner.adjustOxyLoss(5)
if(76 to 100)
owner.adjustOxyLoss(7)
if(101 to 150) // By now you're doubled over coughing
owner.adjustOxyLoss(5)
owner.AdjustLoseBreath(4 SECONDS)
owner.KnockDown(4 SECONDS)
if(151 to INFINITY)
owner.adjustOxyLoss(15)
#undef ASTHMA_ATTACK_THRESHOLD
/datum/quirk/no_apc_charging
name = "High Internal Resistance"
desc = "The station's outlets operate at a higher voltage than your chassis can handle, so you can only safely charge from recharging stations. Only IPCs can take this."
cost = -2
species_flags = QUIRK_ORGANIC_INCOMPATIBLE
trait_to_apply = TRAIT_NO_APC_CHARGING
organ_slot_to_remove = "r_arm_device" // This feels like such a dumb way to do this but I can't think of a smarter solution
/datum/quirk/pacifism
name = "Pacifist"
desc = "You can't bring yourself to hurt others, and cannot take a command or security position."
cost = -3
trait_to_apply = TRAIT_PACIFISM
/datum/quirk/hungry
name = "Hungry"
desc = "You get hungry faster."
cost = -1
/datum/quirk/hungry/apply_quirk_effects()
..()
owner.dna.species.hunger_drain += 0.03
/datum/quirk/hungry/remove_quirk_effects()
..()
owner.dna.species.hunger_drain += 0.03
/datum/quirk/colorblind
name = "Monochromacy"
desc = "You can't see any color."
cost = -2
trait_to_apply = TRAIT_COLORBLIND
species_flags = QUIRK_SLIME_INCOMPATIBLE
/datum/quirk/loudmouthed
name = "Loudmouthed"
desc = "You can't seem to lower your volume to a whisper."
cost = -1
trait_to_apply = TRAIT_NO_WHISPERING
/datum/quirk/nearsighted
name = "Nearsighted"
desc = "You can't see well without prescription glasses."
cost = -1
trait_to_apply = TRAIT_NEARSIGHT
species_flags = QUIRK_SLIME_INCOMPATIBLE
@@ -0,0 +1,136 @@
/datum/quirk/skittish
name = "Skittish"
desc = "You can hide yourself in crates by lying down, and WILL jump into a locker or crate if you bump into one while running, \
as long as you have access."
cost = 4
trait_to_apply = TRAIT_SKITTISH
/datum/quirk/freerunner
name = "Freerunner"
desc = "You're practiced in vaulting over things."
cost = 4
trait_to_apply = TRAIT_FREERUNNER
/datum/quirk/crafty
name = "Crafty"
desc = "You can craft things twice as fast."
cost = 2
trait_to_apply = TRAIT_CRAFTY
/datum/quirk/alcohol_tolerance/heavy_drinker
name = "Heavy Drinker"
desc = "You're used to the effects of alcohol, and get drunk slower than others."
cost = 1
alcohol_modifier = 0.7
/datum/quirk/meal_prepper
name = "Meal Prepper"
desc = "You thought ahead and pre-packed a meal for the day."
cost = 1
item_to_give = /obj/item/storage/box/papersack/prepped_meal
/datum/quirk/glutton
name = "Glutton"
desc = "You can eat faster, and don't suffer any ill-effects from being overweight. IPCs cannot take this."
cost = 2
trait_to_apply = TRAIT_GLUTTON
species_flags = QUIRK_MACHINE_INCOMPATIBLE
/obj/item/storage/box/papersack/prepped_meal
name = "packed meal"
var/list/entree_options = list(
/obj/item/food/sandwich,
/obj/item/food/toastedsandwich,
/obj/item/food/jellysandwich,
/obj/item/food/grilledcheese,
/obj/item/food/burger/cheese,
/obj/item/food/blt,
/obj/item/food/philly_cheesesteak,
/obj/item/food/sliced/hawaiian_pizza,
/obj/item/food/sliced/pepperoni_pizza,
/obj/item/food/meatkebab,
/obj/item/food/salmonsteak, // If anyone microwaves their leftover fish in the workplace it should be on sight
/obj/item/food/shrimp_skewer,
/obj/item/food/omelette
)
var/list/snack_options = list(
/obj/item/food/chips,
/obj/item/food/sosjerky,
/obj/item/food/pistachios,
/obj/item/food/no_raisin,
/obj/item/food/stroopwafel,
/obj/item/food/candy/toffee,
/obj/item/food/candy/chocolate_orange,
/obj/item/food/sliced/mothmallow,
/obj/item/food/sliced/apple_cake,
/obj/item/food/sliced/banarnarbread
)
var/list/drink_options = list(
/obj/item/reagent_containers/drinks/h_chocolate,
/obj/item/reagent_containers/drinks/tea,
/obj/item/reagent_containers/drinks/cans/cola,
/obj/item/reagent_containers/drinks/cans/space_mountain_wind,
/obj/item/reagent_containers/drinks/cans/dr_gibb,
/obj/item/reagent_containers/drinks/cans/space_up,
/obj/item/reagent_containers/drinks/cans/iced_tea,
/obj/item/reagent_containers/drinks/cans/starkist,
/obj/item/reagent_containers/drinks/bottle/beer // Don't tell your boss
)
/obj/item/storage/box/papersack/prepped_meal/populate_contents()
var/entree = pick(entree_options)
var/snack = pick(snack_options)
var/drink = pick(drink_options)
new entree (src)
new snack (src)
new drink (src)
/datum/quirk/upgraded_lungs
name = "Upgraded Cybernetic Lungs"
desc = "Your lungs have been replaced with upgraded cybernetics."
cost = 3
species_flags = QUIRK_MACHINE_INCOMPATIBLE
organ_to_give = /obj/item/organ/internal/lungs/cybernetic/upgraded
/datum/quirk/upgraded_lungs/give_organ(datum/source, datum/job/job, mob/living/spawned, client/player_client)
if(spawned != owner)
return
var/obj/item/organ/internal/lungs/cybernetic/new_lungs = new organ_to_give
if(isvox(owner))
new_lungs.configure_species("vox")
if(isplasmaman(owner))
new_lungs.configure_species("plasmamen")
INVOKE_ASYNC(new_lungs, TYPE_PROC_REF(/obj/item/organ/internal, insert), owner, TRUE)
/datum/quirk/culinary_implant
name = "IPC Culinary Implant"
desc = "Either you or your creator wanted you to seem more organic, and gave you an artificial mouth and stomach."
cost = 2
species_flags = QUIRK_ORGANIC_INCOMPATIBLE
organ_to_give = /obj/item/organ/internal/cyberimp/chest/ipc_food
/datum/quirk/home_cook
name = "Home Cook"
desc = "You have experience in the kitchen, and can examine kitchen machinery to see if the ingredients inside will cook into a proper meal. \
Chefs can already do this."
cost = 1
trait_to_apply = TRAIT_KNOWS_COOKING_RECIPES
/datum/quirk/pet_owner
name = "Animal Lover"
desc = "You brought one of your pets to work today! Make sure to name them with your collar."
cost = 1
item_to_give = /obj/item/petcollar
var/list/possible_pets = list(/mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/pug,
/mob/living/simple_animal/pet/dog/fox, /mob/living/basic/chick, /mob/living/basic/bunny, /mob/living/basic/turkey)
/datum/quirk/pet_owner/apply_quirk_effects()
mob_to_spawn = pick(possible_pets)
..()
/datum/quirk/cool
name = "Cool"
desc = "You can land every flip. You're so cool."
cost = 1
trait_to_apply = TRAIT_COOL
@@ -0,0 +1,134 @@
GLOBAL_LIST_EMPTY(quirk_paths)
/datum/quirk
/// Name of the quirk. It's important that the basetypes don't have a name, and that any quirks you want people to see to have one.
var/name
/// The (somewhat) IC explanation of what this quirk does, to be shown in the TGUI menu.
var/desc = "Uh oh sisters! No description!"
/// A positive or negative number, good quirks should be 1 to 4, bad quirks should be -1 to -4
var/cost = 0
/// The mob that this quirk gets applied to.
var/mob/living/carbon/human/owner
/// If IPCs and/or organic people can use it
var/species_flags
/// If having this bars you from rolling sec/command
var/blacklisted = FALSE
/// If this quirk needs to do something every life cycle
var/processes = FALSE
/// If this quirk applies a trait, what trait should be applied.
var/trait_to_apply
/// If this quirk lets the mob spawn with an item
var/item_to_give
/// If there's an item to give, what slot should it be equipped to roundstart?
var/item_slot = ITEM_SLOT_IN_BACKPACK
/// The path of the organ the quirk should give.
var/organ_to_give
/// What organ should be removed (if any). Must be the string name of the organ as found in the has_organ var from the species datum.
var/organ_slot_to_remove
/// If the quirk should spawn a mob with the player.
var/mob_to_spawn
/datum/quirk/Destroy(force, ...)
remove_quirk_effects()
owner.quirks.Remove(src)
owner = null
return ..()
/* For any quirk that processes, you'll want to have
*
* if(!..())
* return
* At the beginning to prevent it from firing on dead people
*/
/datum/quirk/process()
if(owner.stat == DEAD)
return FALSE
return TRUE
/*
* The proc for actually applying a quirk to a mob, most often during spawning.
*/
/datum/quirk/proc/apply_quirk_effects(mob/living/carbon/human/quirky)
SHOULD_CALL_PARENT(TRUE)
if(!istype(quirky))
log_debug("[src] did not find a mob to apply its effects to.")
return FALSE
owner = quirky
owner.quirks += src
if(processes)
START_PROCESSING(SSobj, src)
if(trait_to_apply)
ADD_TRAIT(owner, trait_to_apply, "quirk")
if(organ_slot_to_remove)
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, PROC_REF(remove_organ))
if(organ_to_give)
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, PROC_REF(give_organ))
if(mob_to_spawn)
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_SPAWN, PROC_REF(spawn_mob))
owner.update_sight()
/datum/quirk/proc/remove_organ(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER //COMSIG_GLOB_JOB_AFTER_SPAWN
if(spawned != owner)
return
var/obj/item/organ/to_remove = owner.get_organ_slot(organ_slot_to_remove)
INVOKE_ASYNC(to_remove, TYPE_PROC_REF(/obj/item/organ/internal, remove), owner, TRUE)
/datum/quirk/proc/give_organ(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER //COMSIG_GLOB_JOB_AFTER_SPAWN
if(spawned != owner)
return
var/obj/item/organ/internal/cybernetic = new organ_to_give
INVOKE_ASYNC(cybernetic, TYPE_PROC_REF(/obj/item/organ/internal, insert), owner, TRUE)
/datum/quirk/proc/spawn_mob(datum/source, datum/job/job, mob/living/spawned, client/player_client)
SIGNAL_HANDLER //COMSIG_GLOB_JOB_AFTER_SPAWN
if(spawned != owner)
return
new mob_to_spawn(owner.loc)
/// For any behavior that needs to happen before a quirk is destroyed
/datum/quirk/proc/remove_quirk_effects()
SHOULD_CALL_PARENT(TRUE)
if(trait_to_apply)
REMOVE_TRAIT(owner, trait_to_apply, "quirk")
if(processes)
STOP_PROCESSING(SSprocessing, src)
owner.update_sight()
/********************************************************************
* Mob Procs, mostly for many mob/new_player in the lobby screen *
********************************************************************/
/mob/proc/add_quirk_to_save(datum/quirk/to_add)
var/datum/character_save/active_character = src.client?.prefs?.active_character
if(!active_character)
return FALSE
if((to_add.species_flags & QUIRK_MACHINE_INCOMPATIBLE) && (active_character.species == "Machine"))
to_chat(src.client, "<span class='warning'>You can't put that quirk on a robotic character.</span>")
return FALSE
if((to_add.species_flags & QUIRK_ORGANIC_INCOMPATIBLE) && (active_character.species != "Machine"))
to_chat(src.client, "<span class='warning'>You can't put that quirk on an organic character.</span>")
return FALSE
if((to_add.species_flags & QUIRK_SLIME_INCOMPATIBLE) && (active_character.species == "Slime People")) //Since they don't have eyes
to_chat(src.client, "<span class='warning'>You can't put that quirk on a slime character, you have no eyes!</span>")
return FALSE
if((to_add.species_flags & QUIRK_PLASMAMAN_INCOMPATIBLE) && (active_character.species == "Plasmaman")) //If someone can figure out how to only let plasmaman with a secondary language take this feel free to do that
to_chat(src.client, "<span class='warning'>You can't put that quirk on a plasmaman, you have no species language!</span>")
return FALSE
active_character.quirks += to_add
return TRUE
/// Returns true if a quirk was removed, false otherwise
/mob/proc/remove_quirk_from_save(datum/quirk/to_remove)
var/datum/character_save/active_character = src.client?.prefs?.active_character
if(!active_character)
return FALSE
for(var/datum/quirk/quirk as anything in active_character.quirks)
if(quirk.name == to_remove.name)
active_character.quirks.Remove(quirk)
return TRUE
return FALSE
/mob/living/carbon/human/proc/clear_quirks()
for(var/datum/quirk/quirk in quirks)
qdel(quirk)
@@ -0,0 +1,47 @@
GLOBAL_LIST_EMPTY(quirk_tgui_info)
/datum/ui_module/quirk
name = "Quirks"
/datum/ui_module/quirk/ui_state(mob/user)
return GLOB.always_state
/datum/ui_module/quirk/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "QuirkMenu", name)
ui.set_autoupdate(FALSE)
ui.open()
/datum/ui_module/quirk/ui_data(mob/user)
var/datum/character_save/character = user?.client?.prefs.active_character
var/list/data = list("quirk_balance" = character.rebuild_quirks())
var/list/selected_quirks = list()
for(var/quirk in character?.quirks)
selected_quirks += quirk
data["selected_quirks"] = selected_quirks
return data
/datum/ui_module/quirk/ui_static_data(mob/user)
var/list/data = list(
"all_quirks" = GLOB.quirk_tgui_info
)
return data
/datum/ui_module/quirk/ui_act(action, list/params)
if(..())
return
. = TRUE
var/mob/user = usr
var/quirk_path = text2path(params["path"])
var/datum/quirk/quirk = new quirk_path
user.client.prefs.active_character.rebuild_quirks()
switch(action)
if("add_quirk")
user.add_quirk_to_save(quirk)
if("remove_quirk")
user.remove_quirk_from_save(quirk)
@@ -185,7 +185,7 @@
var/shot_leg = pick("l_foot", "r_foot")
gun.process_fire(user, user, 0, params, zone_override = shot_leg)
user.drop_item()
else if(prob(50) && !HAS_TRAIT(user, TRAIT_BADASS))
else if(prob(50) && !(HAS_TRAIT(user, TRAIT_BADASS) || HAS_TRAIT(user, TRAIT_COOL)))
message = "attempts to twirl [thing] around in their hand, but fumbles!"
user.drop_item()
else
@@ -1226,7 +1226,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
return TRUE
/mob/living/carbon/proc/selfFeed(obj/item/food/to_eat, fullness)
if(to_eat.junkiness && satiety < -150 && nutrition > NUTRITION_LEVEL_STARVING + 50)
var/is_glutton = HAS_TRAIT(src, TRAIT_GLUTTON)
if(!is_glutton && to_eat.junkiness && satiety < -150 && nutrition > NUTRITION_LEVEL_STARVING + 50)
to_chat(src, "<span class='notice'>You don't feel like eating any more junk food at the moment.</span>")
return FALSE
@@ -1243,7 +1244,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
else if(fullness > (600 * (1 + overeatduration / 2000))) // The more you eat - the more you can eat
to_chat(src, "<span class='warning'>You cannot force any more of [to_eat] to go down your throat.</span>")
return FALSE
if(is_glutton)
src.changeNext_move(CLICK_CD_RAPID) // Hungry hungry spessman
to_chat(src, "<span class='notice'>[jointext(reaction_msg, " ")]</span>")
return TRUE
@@ -108,6 +108,10 @@
/// Lazylist of sources to track what our alpha should be, alpha is set to the minimum. Use the `set_alpha_tracking` and `get_alpha` helpers.
var/list/alpha_sources
/// List of quirk datums attached to the mob
var/list/quirks = list()
/// The cooldown for jumping into a closet or crate
COOLDOWN_DECLARE(skittish_cooldown)
/mob/living/carbon/human/fake
flags = ABSTRACT
@@ -487,7 +487,7 @@
to_chat(user, "You need at least one hand in good working order to snap your fingers.")
return TRUE
if(prob(5))
if(prob(5) && !HAS_TRAIT(user, TRAIT_COOL))
user.visible_message("<span class='danger'><b>[user]</b> snaps [user.p_their()] fingers right off!</span>")
playsound(user.loc, 'sound/effects/snap.ogg', 50, 1)
return TRUE
@@ -67,6 +67,7 @@
. = ..()
SSmobs.cubemonkeys -= src
QDEL_LIST_CONTENTS(bodyparts)
QDEL_LIST_CONTENTS(quirks)
splinted_limbs.Cut()
QDEL_NULL(physiology)
GLOB.human_list -= src
@@ -316,6 +317,9 @@
valid_limbs = list("l_hand", "l_foot", "r_hand", "r_foot")
limb_loss_chance = 25
if(HAS_TRAIT(src, TRAIT_FRAIL))
limb_loss_chance *= 2
//attempt to dismember bodyparts
for(var/X in valid_limbs)
var/obj/item/organ/external/BP = get_organ(X)
@@ -1235,10 +1239,8 @@
kept_items[I] = thing
item_flags[I] = I.flags
I.flags = 0 // Temporary set the flags to 0
if(!transformation) //Distinguish between creating a mob and switching species
dna.species.on_species_gain(src)
var/list/missing_bodyparts = list() // should line up here to pop out only what's missing
if(keep_missing_bodyparts)
for(var/organ_name as anything in bodyparts_by_name)
@@ -226,6 +226,8 @@ GLOBAL_LIST_INIT(soapy_words, list(
R.talk_into(src, message_pieces, null, verb)
if("whisper")
if(HAS_TRAIT(src, TRAIT_NO_WHISPERING))
return FALSE
whisper_say(message_pieces)
return 1
else
@@ -3,6 +3,8 @@
VV_DROPDOWN_OPTION(VV_HK_MAKE_SKELETON, "Make 2spooky")
VV_DROPDOWN_OPTION(VV_HK_HALLUCINATE, "Hallucinate")
VV_DROPDOWN_OPTION(VV_HK_ADDQUIRK, "Add Quirk")
VV_DROPDOWN_OPTION(VV_HK_REMQUIRK, "Remove Quirk")
/mob/living/carbon/human/vv_do_topic(list/href_list)
. = ..()
@@ -33,3 +35,29 @@
message_admins("[key_name(usr)] has given [key_name(src)] the [haltype] hallucination")
log_admin("[key_name_admin(usr)] has given [key_name_admin(src)] the [haltype] hallucination")
href_list["datumrefresh"] = UID()
else if(href_list[VV_HK_ADDQUIRK])
if(!check_rights(R_SPAWN))
return
if(QDELETED(src))
to_chat(usr, "<span class='notice'>Mob doesn't exist anymore.</span>")
return
var/quirk_name = tgui_input_list(usr, "What quirk do you want to add to [src]?", "Quirk to add", GLOB.quirk_paths)
if(!quirk_name)
return
var/datum/quirk/chosen_quirk = GLOB.quirk_paths[quirk_name]
var/datum/quirk/to_add = new chosen_quirk.type // Don't want hard refs to the global list
log_admin("[key_name_admin(usr)] has given [key_name_admin(src)] the [to_add] quirk.")
to_add.apply_quirk_effects(src)
else if(href_list[VV_HK_REMQUIRK])
if(!check_rights(R_SPAWN))
return
if(QDELETED(src))
to_chat(usr, "<span class='notice'>Mob doesn't exist anymore.</span>")
return
var/datum/quirk/to_remove = tgui_input_list(usr, "What quirk do you want to remove from [src]?", "Quirk to remove", src.quirks)
if(!to_remove)
return
log_admin("[key_name_admin(usr)] has removed the [to_remove] quirk from [key_name_admin(src)].")
qdel(to_remove)
@@ -43,6 +43,8 @@
var/hunger_mod = 1
/// Bonus damage added to melee attacks. NOTE: this is additive rather than multiplicative, trying to multiply this will break things.
var/melee_bonus = 0
/// % drunkenness modifier
var/alcohol_mod = 1
/datum/physiology/New()
@@ -258,7 +258,6 @@
LAZYREINITLIST(H.bodyparts)
LAZYREINITLIST(H.bodyparts_by_name)
LAZYREINITLIST(H.internal_organs)
for(var/limb_name in has_limbs)
if(bodyparts_to_omit && (limb_name in bodyparts_to_omit))
H.bodyparts_by_name[limb_name] = null // Null it out, but leave the name here so it's still "there"
@@ -384,7 +383,7 @@
var/hungry = (500 - H.nutrition) / 5 // So overeat would be 100 and default level would be 80
if((hungry >= 70) && !flight)
. += hungry/50
if(HAS_TRAIT(H, TRAIT_FAT))
if(HAS_TRAIT(H, TRAIT_FAT) && !HAS_TRAIT(H, TRAIT_GLUTTON))
. += (1.5 - flight)
if(H.bodytemperature < H.dna.species.cold_level_1 && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
+3 -1
View File
@@ -320,7 +320,9 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
/mob/living/whisper(message as text)
message = trim_strip_html_tags(message)
if(HAS_TRAIT(src, TRAIT_NO_WHISPERING) && !cannot_speak_loudly()) //If you're whispering cause you're out of breath, no blabbermouth will help with that
say(message)
return TRUE
//parse the language code and consume it
var/list/message_pieces = parse_languages(message)
if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages
+2 -2
View File
@@ -166,7 +166,7 @@
borg.visible_message("<span class='warning'><span class='name'>[user]</span> drops their hat!</span>",
"<span class='warning'>As you flip your hat falls off!</span>")
if(prob(5) && ishuman(user))
if(prob(5) && ishuman(user) && !HAS_TRAIT(user, TRAIT_COOL))
var/turf = get_turf(L)
message = "attempts a flip and [isspaceturf(turf) ? "loses balance" : "crashes to the floor"]!"
if(istype(L))
@@ -190,7 +190,7 @@
if(!.)
return FALSE
if(prob(95) || isobserver(user) || !ishuman(user))
if(prob(95) || isobserver(user) || !ishuman(user) || HAS_TRAIT(user, TRAIT_COOL))
user.spin(20, 1)
return TRUE
+4 -1
View File
@@ -323,6 +323,9 @@
if(thisjob.barred_by_disability(client))
to_chat(src, alert("[rank] is not available due to your character's disability. Please try another."))
return 0
if(thisjob.barred_by_quirk(client))
to_chat(src, alert("[rank] is not available due to your character's quirk. Please try another."))
return 0
if(thisjob.barred_by_missing_limbs(client))
to_chat(src, alert("[rank] is not available due to your character having amputated limbs without a prosthetic replacement. Please try another."))
return 0
@@ -485,7 +488,7 @@
"Supply" = list(jobs = list(), titles = GLOB.supply_positions, color = "#ead4ae"),
)
for(var/datum/job/job in SSjobs.occupations)
if(job && IsJobAvailable(job.title) && !job.barred_by_disability(client) && !job.barred_by_missing_limbs(client))
if(job && IsJobAvailable(job.title) && !job.barred_by_disability(client) && !job.barred_by_missing_limbs(client) && !job.barred_by_quirk(client))
num_jobs_available++
activePlayers[job] = 0
var/categorized = 0
+1 -1
View File
@@ -1001,7 +1001,7 @@
to_chat(H, "<span class='userdanger'>You feel surrounded by sadness. Sadness... and HONKS!</span>")
H.makeCluwne()
else if(myeffect == "Demote")
GLOB.major_announcement.Announce("[target.real_name] is hereby demoted to the rank of Assistant. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order")
GLOB.major_announcement.Announce("[target.real_name] is hereby demoted to the rank of Assistant. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order", force_translation = TRUE)
for(var/datum/data/record/R in sortRecord(GLOB.data_core.security))
if(R.fields["name"] == target.real_name)
R.fields["criminal"] = SEC_RECORD_STATUS_DEMOTE
+18 -1
View File
@@ -23,16 +23,28 @@
..()
/obj/structure/big_delivery/attack_hand(mob/user as mob)
if(user in src)
return
open()
/obj/structure/big_delivery/container_resist(mob/living/L)
var/breakout_time = 1 MINUTES
to_chat(L, "<span class='notice'>You attempt to break the wrapping on [src].</span>")
if(do_after_once(L, breakout_time, needhand = FALSE, target = src, allow_moving = TRUE, allow_moving_target = TRUE))
open()
/obj/structure/big_delivery/proc/open()
playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1)
if(wrapped)
wrapped.forceMove(get_turf(src))
if(istype(wrapped, /obj/structure/closet))
var/obj/structure/closet/O = wrapped
O.welded = init_welded
for(var/mob/living/stowaway in src)
stowaway.forceMove(O)
var/turf/T = get_turf(src)
for(var/atom/movable/AM in src)
AM.loc = T
qdel(src)
/obj/structure/big_delivery/item_interaction(mob/living/user, obj/item/W, list/modifiers)
@@ -233,6 +245,9 @@
if(amount < 3)
to_chat(user, "<span class='warning'>You need more paper.</span>")
return
if(user in C)
to_chat(user, "<span class='warning'>How do you plan to wrap it from the inside?</span>")
return
// Checking these again since it's after a delay
var/wrap_do_after = wrap_time
if(user.mind && HAS_TRAIT(user.mind, TRAIT_PACK_RAT))
@@ -241,6 +256,8 @@
return
var/obj/structure/big_delivery/P = new(get_turf(C))
for(var/mob/living/stowaway in C)
stowaway.forceMove(P)
P.wrapped = C
C.loc = P
return P
@@ -515,6 +515,7 @@
/obj/item/apc_powercord/proc/powerdraw_loop(obj/machinery/power/apc/A, mob/living/carbon/human/H)
H.visible_message("<span class='notice'>[H] inserts a power connector into \the [A].</span>", "<span class='notice'>You begin to draw power from \the [A].</span>")
drawing_power = TRUE
var/can_safely_charge = HAS_TRAIT(H, TRAIT_NO_APC_CHARGING) ? FALSE : TRUE
while(do_after(H, 10, target = A))
if(loc != H)
to_chat(H, "<span class='warning'>You must keep your connector out while charging!</span>")
@@ -524,6 +525,8 @@
break
A.charging = APC_IS_CHARGING
if(A.cell.charge >= 500)
if(!can_safely_charge)
H.adjust_bodytemperature(60) // Don't overcharge your batteries
H.adjust_nutrition(50)
A.cell.charge -= 500
to_chat(H, "<span class='notice'>You siphon off some of the stored charge for your own use.</span>")
@@ -537,6 +540,14 @@
break
H.visible_message("<span class='notice'>[H] unplugs from \the [A].</span>", "<span class='notice'>You unplug from \the [A].</span>")
drawing_power = FALSE
if(can_safely_charge)
return
if(H.bodytemperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
H.fire_stacks += 10
H.IgniteMob()
var/datum/organ/battery/microbattery = H.get_int_organ_datum(ORGAN_DATUM_BATTERY)
microbattery.linked_organ.receive_damage(H.bodytemperature / 50)
H.visible_message("<span class='warning'>...Then immediately bursts into flame!</span>", "<span class='userdanger'>Something inside you combusts!</span>")
/datum/action/item_action/organ_action/toggle/telebaton
button_icon = 'icons/obj/items.dmi'
+6 -6
View File
@@ -46,6 +46,7 @@
origin_tech = "biotech=4"
status = ORGAN_ROBOT
var/species_state = "default"
var/list/possible = list("default" = /datum/organ/lungs, "vox" = /datum/organ/lungs/vox, "plasmamen" = /datum/organ/lungs/plasmamen)
/obj/item/organ/internal/lungs/cybernetic/examine(mob/user)
. = ..()
@@ -55,15 +56,15 @@
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
var/possible = list("default" = /datum/organ/lungs, "vox" = /datum/organ/lungs/vox, "plasmamen" = /datum/organ/lungs/plasmamen)
var/chosen = input(user, "Select lung type", "What kind of lung settings?") as null|anything in possible
if(isnull(chosen) || chosen == species_state || !Adjacent(user) || !I.use_tool(src, user, 0, volume = I.tool_volume))
return
species_state = chosen
to_chat(user, "<span class='notice'>You configure [src] to [chosen] settings.</span>")
configure_species(chosen)
var/typepath = possible[chosen]
/obj/item/organ/internal/lungs/cybernetic/proc/configure_species(new_species_state)
species_state = new_species_state
var/typepath = possible[new_species_state]
var/datum/organ/lungs/lungs = new typepath(src)
qdel(organ_datums[lungs.organ_tag])
organ_datums[lungs.organ_tag] = lungs
@@ -75,10 +76,9 @@
desc = "A more advanced version of the stock cybernetic lungs. They are capable of filtering out lower levels of toxins and carbon dioxide."
icon_state = "lungs-c-u"
origin_tech = "biotech=5"
organ_datums = list(/datum/organ/lungs/advanced_cyber)
/obj/item/organ/internal/lungs/cybernetic/upgraded/multitool_act(mob/user, obj/item/I)
/obj/item/organ/internal/lungs/cybernetic/upgraded/configure_species(new_species_state)
. = ..()
var/datum/organ/lungs/lungs = organ_datums[ORGAN_DATUM_LUNGS]
lungs.make_advanced()
@@ -78,6 +78,7 @@
if(H.health < HEALTH_THRESHOLD_CRIT)
return FALSE
var/asthma_multiplier = HAS_TRAIT(H, TRAIT_ASTHMATIC) ? 0.5 : 1
//Partial pressures in our breath
var/O2_pp = breath.get_breath_partial_pressure(breath.oxygen())
var/N2_pp = breath.get_breath_partial_pressure(breath.nitrogen())
@@ -103,7 +104,7 @@
handle_too_little_breath(H, O2_pp, safe_oxygen_min, breath.oxygen())
H.throw_alert("not_enough_oxy", /atom/movable/screen/alert/not_enough_oxy)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS * asthma_multiplier)
H.clear_alert("not_enough_oxy")
// Exhale
@@ -127,7 +128,7 @@
handle_too_little_breath(H, N2_pp, safe_nitro_min, breath.nitrogen())
H.throw_alert("not_enough_nitro", /atom/movable/screen/alert/not_enough_nitro)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS * asthma_multiplier)
H.clear_alert("not_enough_nitro")
// Exhale
@@ -160,7 +161,7 @@
handle_too_little_breath(H, CO2_pp, safe_co2_min, breath.carbon_dioxide())
H.throw_alert("not_enough_co2", /atom/movable/screen/alert/not_enough_co2)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS * asthma_multiplier)
H.clear_alert("not_enough_co2")
// Exhale
@@ -186,7 +187,7 @@
handle_too_little_breath(H, Toxins_pp, safe_toxins_min, breath.toxins())
H.throw_alert("not_enough_tox", /atom/movable/screen/alert/not_enough_tox)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS * asthma_multiplier)
H.clear_alert("not_enough_tox")
// Exhale
@@ -366,3 +367,13 @@
cold_level_1_threshold = 200
cold_level_2_threshold = 140
cold_level_3_threshold = 100
/datum/organ/lungs/vox/make_advanced()
. = ..()
safe_oxygen_max = 20
/datum/organ/lungs/plasmamen/make_advanced()
. = ..()
safe_oxygen_max = 20
safe_toxins_max = 0
+20 -6
View File
@@ -224,6 +224,8 @@
var/max_limb_damage = max_damage
if(owner && fragile)
max_limb_damage -= (HAS_TRAIT(owner, TRAIT_IPC_JOINTS_MAG) ? max_damage * 0.25 : 0)
if(owner && HAS_TRAIT(owner, TRAIT_FRAIL))
max_limb_damage /= 2
if(tough && !ignore_resists)
brute = max(0, brute - 5)
burn = max(0, burn - 4)
@@ -319,9 +321,9 @@
//If limb took enough damage, try to cut or tear it off
if(owner)
if(sharp && !(limb_flags & CANNOT_DISMEMBER))
if(limb_brute + limb_burn >= max_damage && prob(original_brute / 2))
if(limb_brute + limb_burn >= max_limb_damage && prob(original_brute / 2))
droplimb(0, DROPLIMB_SHARP)
if(limb_brute + limb_burn >= max_damage && prob(original_burn / 2))
if(limb_brute + limb_burn >= max_limb_damage && prob(original_burn / 2))
droplimb(0, DROPLIMB_BURN)
if(owner_old)
@@ -529,21 +531,33 @@ Note that amputating the affected organ does in fact remove the infection from t
//Updates brute_damn and burn_damn from wound damages. Updates BLEEDING status.
/obj/item/organ/external/proc/check_fracture(damage_inflicted)
if(GLOB.configuration.general.breakable_bones && brute_dam > min_broken_damage && !is_robotic())
if(prob(damage_inflicted))
var/frail_multiplier = 1
if(owner)
frail_multiplier = HAS_TRAIT(owner, TRAIT_FRAIL) ? 2 : 1
var/adjusted_broken_damage = min_broken_damage / frail_multiplier
if(GLOB.configuration.general.breakable_bones && brute_dam > adjusted_broken_damage && !is_robotic())
if(prob(damage_inflicted * frail_multiplier))
fracture()
/obj/item/organ/external/proc/check_for_internal_bleeding(damage)
var/frail_multiplier = 1
if(owner && (NO_BLOOD in owner.dna.species.species_traits))
return
if(owner)
frail_multiplier = HAS_TRAIT(owner, TRAIT_FRAIL) ? 2 : 1
var/adjusted_broken_damage = min_broken_damage / frail_multiplier
var/local_damage = brute_dam + damage
if(damage > 15 && local_damage > min_broken_damage && prob(damage))
if(damage > 15 && local_damage > adjusted_broken_damage && prob(damage * frail_multiplier))
cause_internal_bleeding()
/obj/item/organ/external/proc/check_for_burn_wound(damage, update_health = TRUE)
var/frail_multiplier = 1
if(is_robotic())
return
if(burn_dam >= min_broken_damage && prob(damage * max(owner.bodytemperature / BODYTEMP_HEAT_DAMAGE_LIMIT, 1)))
if(owner)
frail_multiplier = HAS_TRAIT(owner, TRAIT_FRAIL) ? 2 : 1
var/adjusted_broken_damage = min_broken_damage / frail_multiplier
if(burn_dam >= adjusted_broken_damage && prob(damage * frail_multiplier * max(owner.bodytemperature / BODYTEMP_HEAT_DAMAGE_LIMIT, 1)))
cause_burn_wound(update_health)
/obj/item/organ/external/proc/check_skin_covers(brute, burn)