mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-17 10:03:50 +01:00
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:
@@ -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
|
||||
))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user