Refactors quirk code. (#59618)

Adds new signal - COMSIG_MOB_EQUIPPED_ITEM. Signal sent at the same time COMSIG_ITEM_EQUIPPED is and allows something to Register for every time a mob equips an item. Replaced a process() in /datum/quirk/badback with signals relating to this. Holds a weakref to any backpack it has registered signals with for use in its own remove() proc.

Removes snowflake code in /datum/quirk/light_step - This quirk no longer uses GetComponent to directly modify a component. Instead, the same code has been shifted into the component itself, which now checks for TRAIT_LIGHT_STEP and mods the volume and range accordingly.

Refactors quirk code in general - Quirks no longer do major logic in New() and no longer qdel themselves in New(). You now fully instantiate a quirk assigned to a var, then call /datum/quirk/proc/add_to_holder(). Various scenarios that shouldn't happen now get handled in this proc and calling code can cleanup properly. Quirks "support" having no quirk_holder - Since their default state is no quirk_holder until the quirk is added to a holder, and similarly quirks can be removed from a holder as well. Destroying a quirk with a quirk_holder will remove it from the quirk_holder properly. qdeling a quirk_holder will clean up any quirks attached to them.

Rethinks processing quirks - Quirks no longer all process automatically. The new logic changes mean their previous need to process() just to check if their quirk_holder had been QDELETED so they didn't become runtime factories has been removed by the refactored code. The few quirks that still process require processing_quirk = TRUE which will start them processing when added to a quirk_holder and stop processing on removal. This means there should be some many hundred fewer quirks process()ing 24/7 every shift.

Subtypes item quirks - A number of quirks are designed to give items to the player. There's code duplication and varying implementation issues, so item quirks have their own subtype with a proc and some vars to handle this. Quirks will no longer fail to give items at all (some quirks actually explictly qdel'd the items they give if the user had no free hands or slots) and will drop items on the floor in the worst-case scenario. Players will always get the opportunity to see messages related to item quirks as these are in a code path that, when the quirk is first added to a new mob, will either output immediately if the mob has a client, or wait for the mob to have a client otherwise.

"Roundstart quirks" paradigm removed - Quirks now have a path to add unique effects that aren't replicated if the quirk is transferred from one mob to another - add_unique(). Item spawning and other similar one-shot logic is done here. This means that adding a quirk to a mob will trigger the one-time effects. Transferring it between mobs (for example, slimepeople changing bodies or swapping a golem shell) will not. roundstart_quirks var renamed to just quirks since it genuinely is just a list of quirks the mob has from any source - Whether roundstart, admin or transferred.

Family Heirloom quirk - Heirloom is now a weakref.

Nyctophobia quirk - No longer process()es, now Registers COMSIG_MOVABLE_MOVED. Every footstep in the dark will slow you back to walking.

Reality Dissociation Syndrome quirk - No longer snowflakes behaviour for mindbreaker toxin on process. Now mindbreaker toxin has the anti-hallucination functionality built into its already existing HAS_TRAIT(M, TRAIT_INSANITY) check in on_mob_life.

Tongue Tied quirk - Now uses .getorganslot(ORGAN_SLOT_TONGUE) to find the tongue instead of locate() in internal_organs

Obsessed antag - Thanks to the Family Heirloom quirk now holding a weakref to the heirloom item itself, this antag type will no longer get the steal heirloom objective if the heirloom doesn't exist (ie. the weakref is null or fails to resolve, meaning the item has been destroyed)

Various quirks that were impossible to remove before are now removable - Examples being light step (which no longer directly modifies the footstep component) and bad back.

In addition, adds some extra documentation overall and improves compliance with code requirements in a number of procs (but certainly not all).

Probably various other little changes here and there to make the above all mesh together.
This commit is contained in:
Timberpoes
2021-06-13 19:47:15 +01:00
committed by GitHub
parent d7408997d1
commit ec2938c72c
21 changed files with 645 additions and 547 deletions
+2
View File
@@ -772,6 +772,8 @@
///from base of obj/item/equipped(): (/mob/equipper, slot)
#define COMSIG_ITEM_EQUIPPED "item_equip"
/// A mob has just equipped an item. Called on [/mob] from base of [/obj/item/equipped()]: (/obj/item/equipped_item, slot)
#define COMSIG_MOB_EQUIPPED_ITEM "mob_equipped_item"
///called on [/obj/item] before unequip from base of [mob/proc/doUnEquip]: (force, atom/newloc, no_move, invdrop, silent)
#define COMSIG_ITEM_PRE_UNEQUIP "item_pre_unequip"
///only the pre unequip can be cancelled
+17
View File
@@ -219,3 +219,20 @@ GLOBAL_LIST_INIT(security_wintercoat_allowed, typecacheof(list(
/obj/item/reagent_containers/spray/pepper,
/obj/item/restraints/handcuffs,
)))
/// String for items placed into the left pocket.
#define LOCATION_LPOCKET "in your left pocket"
/// String for items placed into the right pocket
#define LOCATION_RPOCKET "in your right pocket"
/// String for items placed into the backpack.
#define LOCATION_BACKPACK "in your backpack"
/// String for items placed into the hands.
#define LOCATION_HANDS "in your hands"
/// String for items placed in the glove slot.
#define LOCATION_GLOVES "on your hands"
/// String for items placed in the eye/glasses slot.
#define LOCATION_EYES "covering your eyes"
/// String for items placed on the head/hat slot.
#define LOCATION_HEAD "on your head"
/// String for items placed in the neck slot.
#define LOCATION_NECK "around your neck"
+2
View File
@@ -486,6 +486,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define ROUNDSTART_TRAIT "roundstart"
#define JOB_TRAIT "job"
#define CYBORG_ITEM_TRAIT "cyborg-item"
/// Any traits granted by quirks.
#define QUIRK_TRAIT "quirk_trait"
/// (B)admins only.
#define ADMIN_TRAIT "admin"
#define CHANGELING_TRAIT "changeling"
+17 -14
View File
@@ -13,7 +13,6 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
var/list/quirks = list() //Assoc. list of all roundstart quirk datum types; "name" = /path/
var/list/quirk_points = list() //Assoc. list of quirk names and their "point cost"; positive numbers are good traits, and negative ones are bad
var/list/quirk_objects = list() //A list of all quirk objects in the game, since some may process
var/list/quirk_blacklist = list() //A list of quirks that can not be used with each other. Format: list(quirk1,quirk2),list(quirk3,quirk4)
///An assoc list of quirks that can be obtained as a hardcore character, and their hardcore value.
var/list/hardcore_quirks = list()
@@ -36,23 +35,27 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
// Sort by Positive, Negative, Neutral; and then by name
var/list/quirk_list = sortList(subtypesof(/datum/quirk), /proc/cmp_quirk_asc)
for(var/V in quirk_list)
var/datum/quirk/T = V
quirks[initial(T.name)] = T
quirk_points[initial(T.name)] = initial(T.value)
for(var/type in quirk_list)
var/datum/quirk/quirk_type = type
var/hardcore_value = initial(T.hardcore_value)
if(initial(quirk_type.abstract_parent_type) == type)
continue
quirks[initial(quirk_type.name)] = quirk_type
quirk_points[initial(quirk_type.name)] = initial(quirk_type.value)
var/hardcore_value = initial(quirk_type.hardcore_value)
if(!hardcore_value)
continue
hardcore_quirks[T] += hardcore_value
hardcore_quirks[quirk_type] += hardcore_value
/datum/controller/subsystem/processing/quirks/proc/AssignQuirks(mob/living/user, client/cli, spawn_effects)
/datum/controller/subsystem/processing/quirks/proc/AssignQuirks(mob/living/user, client/cli)
var/badquirk = FALSE
for(var/V in cli.prefs.all_quirks)
var/datum/quirk/Q = quirks[V]
if(Q)
user.add_quirk(Q, spawn_effects)
user.add_quirk(Q)
else
stack_trace("Invalid quirk \"[V]\" in client [cli.ckey] preferences")
cli.prefs.all_quirks -= V
@@ -65,14 +68,14 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
human.hardcore_survival_score = cli.prefs.hardcore_survival_score //Only do this if we actually asign quirks, to prevent sillicons etc from getting the points.
// Assign wayfinding pinpointer granting quirk if they're new
if(cli.get_exp_living(TRUE) < EXP_ASSIGN_WAYFINDER && !user.has_quirk(/datum/quirk/needswayfinder))
user.add_quirk(/datum/quirk/needswayfinder, TRUE)
if(cli.get_exp_living(TRUE) < EXP_ASSIGN_WAYFINDER && !user.has_quirk(/datum/quirk/item_quirk/needswayfinder))
user.add_quirk(/datum/quirk/item_quirk/needswayfinder)
/*
*Randomises the quirks for a specified mob
*/
/datum/controller/subsystem/processing/quirks/proc/randomise_quirks(mob/living/user)
var/bonus_quirks = max((length(user.roundstart_quirks) + rand(-RANDOM_QUIRK_BONUS, RANDOM_QUIRK_BONUS)), MINIMUM_RANDOM_QUIRKS)
var/bonus_quirks = max((length(user.quirks) + rand(-RANDOM_QUIRK_BONUS, RANDOM_QUIRK_BONUS)), MINIMUM_RANDOM_QUIRKS)
var/added_quirk_count = 0 //How many we've added
var/list/quirks_to_add = list() //Quirks we're adding
var/good_count = 0 //Maximum of 6 good perks
@@ -122,14 +125,14 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
score += quirk_points[quirk]
quirks_to_add += quirk
for(var/datum/quirk/quirk as anything in user.roundstart_quirks)
for(var/datum/quirk/quirk as anything in user.quirks)
if(quirk.name in quirks_to_add) //Don't delete ones we keep
quirks_to_add -= quirk.name //Already there, no need to add.
continue
user.remove_quirk(quirk.type) //these quirks are objects
for(var/datum/quirk/quirk as anything in quirks_to_add)
user.add_quirk(quirks[quirk], spawn_effects = TRUE)//these are typepaths converted from string
user.add_quirk(quirks[quirk]) //these are typepaths converted from string
#undef RANDOM_QUIRK_BONUS
#undef MINIMUM_RANDOM_QUIRKS
+1 -1
View File
@@ -388,7 +388,7 @@ SUBSYSTEM_DEF(ticker)
if(player_assigned_role != new_player_human.mind.special_role)
SSjob.EquipRank(new_player_mob, player_assigned_role, FALSE, player_is_captain)
if(CONFIG_GET(flag/roundstart_traits) && ishuman(new_player_human))
SSquirks.AssignQuirks(new_player_human, new_player_mob.client, TRUE)
SSquirks.AssignQuirks(new_player_human, new_player_mob.client)
CHECK_TICK
if(captainless)
+12 -4
View File
@@ -107,6 +107,14 @@
if(HAS_TRAIT(parent, TRAIT_SILENT_FOOTSTEPS))
return
var/volume_multiplier = 1
var/range_adjustment = 0
if(HAS_TRAIT(parent, TRAIT_LIGHT_STEP))
volume_multiplier = 0.6
range_adjustment = -2
var/turf/open/T = prepare_step()
if(!T)
return
@@ -115,17 +123,17 @@
if ((H.wear_suit?.body_parts_covered | H.w_uniform?.body_parts_covered | H.shoes?.body_parts_covered) & FEET)
// we are wearing shoes
playsound(T, pick(GLOB.footstep[T.footstep][1]),
GLOB.footstep[T.footstep][2] * volume,
GLOB.footstep[T.footstep][2] * volume * volume_multiplier,
TRUE,
GLOB.footstep[T.footstep][3] + e_range, falloff_distance = 1, vary = sound_vary)
GLOB.footstep[T.footstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary)
else
if(H.dna.species.special_step_sounds)
playsound(T, pick(H.dna.species.special_step_sounds), 50, TRUE, falloff_distance = 1, vary = sound_vary)
else
playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
GLOB.barefootstep[T.barefootstep][2] * volume,
GLOB.barefootstep[T.barefootstep][2] * volume * volume_multiplier,
TRUE,
GLOB.barefootstep[T.barefootstep][3] + e_range, falloff_distance = 1, vary = sound_vary)
GLOB.barefootstep[T.barefootstep][3] + e_range + range_adjustment, falloff_distance = 1, vary = sound_vary)
///Prepares a footstep for machine walking
+139 -99
View File
@@ -10,30 +10,92 @@
var/medical_record_text //This text will appear on medical records for the trait. Not yet implemented
var/mood_quirk = FALSE //if true, this quirk affects mood and is unavailable if moodlets are disabled
var/mob_trait //if applicable, apply and remove this mob trait
///Amount of points this trait is worth towards the hardcore character mode; minus points implies a positive quirk, positive means its hard. This is used to pick the quirks assigned to a hardcore character. 0 means its not available to hardcore draws.
/// Amount of points this trait is worth towards the hardcore character mode; minus points implies a positive quirk, positive means its hard. This is used to pick the quirks assigned to a hardcore character. 0 means its not available to hardcore draws.
var/hardcore_value = 0
var/mob/living/quirk_holder
/// This quirk should START_PROCESSING when added and STOP_PROCESSING when removed.
var/processing_quirk = FALSE
/// When making an abstract quirk (in OOP terms), don't forget to set this var to the type path for that abstract quirk.
var/abstract_parent_type = /datum/quirk
/datum/quirk/Destroy()
if(quirk_holder)
remove_from_current_holder()
return ..()
/// Called when quirk_holder is qdeleting. Simply qdels this datum and lets Destroy() handle the rest.
/datum/quirk/proc/on_holder_qdeleting(mob/living/source, force)
SIGNAL_HANDLER
qdel(src)
/**
* Adds the quirk to a new quirk_holder.
*
* Performs logic to make sure new_holder is a valid holder of this quirk.
* Returns FALSEy if there was some kind of error. Returns TRUE otherwise.
* Arguments:
* * new_holder - The mob to add this quirk to.
* * quirk_transfer - If this is being added to the holder as part of a quirk transfer. Quirks can use this to decide not to spawn new items or apply any other one-time effects.
*/
/datum/quirk/proc/add_to_holder(mob/living/new_holder, quirk_transfer = FALSE)
if(!new_holder)
CRASH("Quirk attempted to be added to null mob.")
if(human_only && !ishuman(new_holder))
CRASH("Human only quirk attempted to be added to non-human mob.")
if(new_holder.has_quirk(type))
CRASH("Quirk attempted to be added to mob which already had this quirk.")
if(quirk_holder)
CRASH("Attempted to add quirk to a holder when it already has a holder.")
quirk_holder = new_holder
quirk_holder.quirks += src
/datum/quirk/New(mob/living/quirk_mob, spawn_effects)
..()
if(!quirk_mob || (human_only && !ishuman(quirk_mob)) || quirk_mob.has_quirk(type))
qdel(src)
return
quirk_holder = quirk_mob
SSquirks.quirk_objects += src
to_chat(quirk_holder, gain_text)
quirk_holder.roundstart_quirks += src
if(mob_trait)
ADD_TRAIT(quirk_holder, mob_trait, ROUNDSTART_TRAIT)
START_PROCESSING(SSquirks, src)
add()
if(spawn_effects)
on_spawn()
if(quirk_holder.client)
post_add()
else
RegisterSignal(quirk_holder, COMSIG_MOB_LOGIN, .proc/on_quirk_holder_first_login)
ADD_TRAIT(quirk_holder, mob_trait, QUIRK_TRAIT)
add()
if(processing_quirk)
START_PROCESSING(SSquirks, src)
if(!quirk_transfer)
to_chat(quirk_holder, gain_text)
add_unique()
if(quirk_holder.client)
post_add()
else
RegisterSignal(quirk_holder, COMSIG_MOB_LOGIN, .proc/on_quirk_holder_first_login)
RegisterSignal(quirk_holder, COMSIG_PARENT_QDELETING, .proc/on_holder_qdeleting)
return TRUE
/// Removes the quirk from the current quirk_holder.
/datum/quirk/proc/remove_from_current_holder(quirk_transfer = FALSE)
if(!quirk_holder)
CRASH("Attempted to remove quirk from the current holder when it has no current holder.")
UnregisterSignal(quirk_holder, list(COMSIG_MOB_LOGIN, COMSIG_PARENT_QDELETING))
quirk_holder.quirks -= src
if(!quirk_transfer)
to_chat(quirk_holder, lose_text)
if(mob_trait)
REMOVE_TRAIT(quirk_holder, mob_trait, ROUNDSTART_TRAIT)
if(processing_quirk)
STOP_PROCESSING(SSquirks, src)
remove()
quirk_holder = null
/**
* On client connection set quirk preferences.
@@ -48,41 +110,59 @@
UnregisterSignal(source, COMSIG_MOB_LOGIN)
post_add()
/datum/quirk/Destroy()
STOP_PROCESSING(SSquirks, src)
remove()
if(quirk_holder)
to_chat(quirk_holder, lose_text)
quirk_holder.roundstart_quirks -= src
if(mob_trait)
REMOVE_TRAIT(quirk_holder, mob_trait, ROUNDSTART_TRAIT)
SSquirks.quirk_objects -= src
return ..()
/// Any effect that should be applied every single time the quirk is added to any mob, even when transferred.
/datum/quirk/proc/add()
/// Any effects from the proc that should not be done multiple times if the quirk is transferred between mobs. Put stuff like spawning items in here.
/datum/quirk/proc/add_unique()
/// Removal of any reversible effects added by the quirk.
/datum/quirk/proc/remove()
/// Any special effects or chat messages which should be applied. This proc is guaranteed to run if the mob has a client when the quirk is added. Otherwise, it runs once on the next COMSIG_MOB_LOGIN.
/datum/quirk/proc/post_add()
/datum/quirk/proc/transfer_mob(mob/living/to_mob)
quirk_holder.roundstart_quirks -= src
to_mob.roundstart_quirks += src
if(mob_trait)
REMOVE_TRAIT(quirk_holder, mob_trait, ROUNDSTART_TRAIT)
ADD_TRAIT(to_mob, mob_trait, ROUNDSTART_TRAIT)
quirk_holder = to_mob
on_transfer()
/// Subtype quirk that has some bonus logic to spawn items for the player.
/datum/quirk/item_quirk
/// Lazylist of strings describing where all the quirk items have been spawned.
var/list/where_items_spawned
/// If true, the backpack automatically opens on post_add(). Usually set to TRUE when an item is equipped inside the player's backpack.
var/open_backpack = FALSE
abstract_parent_type = /datum/quirk/item_quirk
/datum/quirk/proc/add() //special "on add" effects
/datum/quirk/proc/on_spawn() //these should only trigger when the character is being created for the first time, i.e. roundstart/latejoin
/datum/quirk/proc/remove() //special "on remove" effects
/datum/quirk/proc/on_process() //process() has some special checks, so this is the actual process
/datum/quirk/proc/post_add() //for text, disclaimers etc. given after you spawn in with the trait
/datum/quirk/proc/on_transfer() //code called when the trait is transferred to a new mob
/**
* Handles inserting an item in any of the valid slots provided, then allows for post_add notification.
*
* If no valid slot is available for an item, the item is left at the mob's feet.
* Arguments:
* * quirk_item - The item to give to the quirk holder. If the item is a path, the item will be spawned in first on the player's turf.
* * valid_slots - Assoc list of descriptive location strings to item slots that is fed into [/mob/living/carbon/proc/equip_in_one_of_slots]. list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK)
* * flavour_text - Optional flavour text to append to the where_items_spawned string after the item's location.
* * default_location - If the item isn't possible to equip in a valid slot, this is a description of where the item was spawned.
* * notify_player - If TRUE, adds strings to where_items_spawned list to be output to the player in [/datum/quirk/item_quirk/post_add()]
*/
/datum/quirk/item_quirk/proc/give_item_to_holder(quirk_item, list/valid_slots, flavour_text = null, default_location = "at your feet", notify_player = TRUE)
if(ispath(quirk_item))
quirk_item = new quirk_item(get_turf(quirk_holder))
/datum/quirk/process(delta_time)
if(QDELETED(quirk_holder))
quirk_holder = null
qdel(src)
return
if(quirk_holder.stat == DEAD)
return
on_process(delta_time)
var/mob/living/carbon/human/human_holder = quirk_holder
var/where = human_holder.equip_in_one_of_slots(quirk_item, valid_slots, qdel_on_fail = FALSE) || default_location
if(where == LOCATION_BACKPACK)
open_backpack = TRUE
if(notify_player)
LAZYADD(where_items_spawned, "<span class='boldnotice'>You have \a [quirk_item] [where]. [flavour_text]</span>")
/datum/quirk/item_quirk/post_add()
if(open_backpack)
var/mob/living/carbon/human/human_holder = quirk_holder
// post_add() can be called via delayed callback. Check they still have a backpack equipped before trying to open it.
if(human_holder.back)
SEND_SIGNAL(human_holder.back, COMSIG_TRY_STORAGE_SHOW, human_holder)
for(var/chat_string in where_items_spawned)
to_chat(quirk_holder, chat_string)
where_items_spawned = null
/**
* get_quirk_string() is used to get a printable string of all the quirk traits someone has for certain criteria
@@ -95,24 +175,24 @@
var/list/dat = list()
switch(category)
if(CAT_QUIRK_ALL)
for(var/V in roundstart_quirks)
for(var/V in quirks)
var/datum/quirk/T = V
dat += medical ? T.medical_record_text : T.name
//Major Disabilities
if(CAT_QUIRK_MAJOR_DISABILITY)
for(var/V in roundstart_quirks)
for(var/V in quirks)
var/datum/quirk/T = V
if(T.value < -4)
dat += medical ? T.medical_record_text : T.name
//Minor Disabilities
if(CAT_QUIRK_MINOR_DISABILITY)
for(var/V in roundstart_quirks)
for(var/V in quirks)
var/datum/quirk/T = V
if(T.value >= -4 && T.value < 0)
dat += medical ? T.medical_record_text : T.name
//Neutral and Positive quirks
if(CAT_QUIRK_NOTES)
for(var/V in roundstart_quirks)
for(var/V in quirks)
var/datum/quirk/T = V
if(T.value > -1)
dat += medical ? T.medical_record_text : T.name
@@ -121,51 +201,11 @@
return medical ? dat.Join("<br>") : dat.Join(", ")
/mob/living/proc/cleanse_trait_datums() //removes all trait datums
for(var/V in roundstart_quirks)
for(var/V in quirks)
var/datum/quirk/T = V
qdel(T)
/mob/living/proc/transfer_trait_datums(mob/living/to_mob)
for(var/V in roundstart_quirks)
var/datum/quirk/T = V
T.transfer_mob(to_mob)
/*
Commented version of Nearsighted to help you add your own traits
Use this as a guideline
/datum/quirk/nearsighted
name = "Nearsighted"
///The trait's name
desc = "You are nearsighted without prescription glasses, but spawn with a pair."
///Short description, shows next to name in the trait panel
value = -1
///If this is above 0, it's a positive trait; if it's not, it's a negative one; if it's 0, it's a neutral
mob_trait = TRAIT_NEARSIGHT
///This define is in __DEFINES/traits.dm and is the actual "trait" that the game tracks
///You'll need to use "HAS_TRAIT_FROM(src, X, sources)" checks around the code to check this; for instance, the Ageusia trait is checked in taste code
///If you need help finding where to put it, the declaration finder on GitHub is the best way to locate it
gain_text = "<span class='danger'>Things far away from you start looking blurry.</span>"
lose_text = "<span class='notice'>You start seeing faraway things normally again.</span>"
medical_record_text = "Subject has permanent nearsightedness."
///These three are self-explanatory
/datum/quirk/nearsighted/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
H.put_in_hands(glasses)
H.equip_to_slot(glasses, ITEM_SLOT_EYES)
H.regenerate_icons()
//This whole proc is called automatically
//It spawns a set of prescription glasses on the user, then attempts to put it into their hands, then attempts to make them equip it.
//This means that if they fail to equip it, they glasses spawn in their hands, and if they fail to be put into the hands, they spawn on the ground
//Hooray for fallbacks!
//If you don't need any special effects like spawning glasses, then you don't need an add()
*/
for(var/datum/quirk/quirk as anything in quirks)
quirk.remove_from_current_holder(quirk_transfer = TRUE)
quirk.add_to_holder(to_mob, quirk_transfer = TRUE)
+47 -65
View File
@@ -23,10 +23,9 @@
mood.mood_modifier -= 0.2
/datum/quirk/apathetic/remove()
if(quirk_holder)
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
if(mood)
mood.mood_modifier += 0.2
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
if(mood)
mood.mood_modifier += 0.2
/datum/quirk/drunkhealing
name = "Drunken Resilience"
@@ -36,19 +35,20 @@
gain_text = "<span class='notice'>You feel like a drink would do you good.</span>"
lose_text = "<span class='danger'>You no longer feel like drinking would ease your pain.</span>"
medical_record_text = "Patient has unusually efficient liver metabolism and can slowly regenerate wounds by drinking alcoholic beverages."
processing_quirk = TRUE
/datum/quirk/drunkhealing/on_process(delta_time)
var/mob/living/carbon/C = quirk_holder
switch(C.drunkenness)
/datum/quirk/drunkhealing/process(delta_time)
var/mob/living/carbon/carbon_holder = quirk_holder
switch(carbon_holder.drunkenness)
if (6 to 40)
C.adjustBruteLoss(-0.1*delta_time, FALSE)
C.adjustFireLoss(-0.05*delta_time, FALSE)
carbon_holder.adjustBruteLoss(-0.1*delta_time, FALSE)
carbon_holder.adjustFireLoss(-0.05*delta_time, FALSE)
if (41 to 60)
C.adjustBruteLoss(-0.4*delta_time, FALSE)
C.adjustFireLoss(-0.2*delta_time, FALSE)
carbon_holder.adjustBruteLoss(-0.4*delta_time, FALSE)
carbon_holder.adjustFireLoss(-0.2*delta_time, FALSE)
if (61 to INFINITY)
C.adjustBruteLoss(-0.8*delta_time, FALSE)
C.adjustFireLoss(-0.4*delta_time, FALSE)
carbon_holder.adjustBruteLoss(-0.8*delta_time, FALSE)
carbon_holder.adjustFireLoss(-0.4*delta_time, FALSE)
/datum/quirk/empath
name = "Empath"
@@ -59,7 +59,7 @@
lose_text = "<span class='danger'>You feel isolated from others.</span>"
medical_record_text = "Patient is highly perceptive of and sensitive to social cues, or may possibly have ESP. Further testing needed."
/datum/quirk/fan_clown
/datum/quirk/item_quirk/fan_clown
name = "Clown Fan"
desc = "You enjoy clown antics and get a mood boost from wearing your clown pin."
value = 2
@@ -68,18 +68,14 @@
lose_text = "<span class='danger'>The clown doesn't seem so great.</span>"
medical_record_text = "Patient reports being a big fan of clowns."
/datum/quirk/fan_clown/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/accessory/fan_clown_pin/B = new(get_turf(H))
var/list/slots = list (
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS,
)
H.equip_in_one_of_slots(B, slots , qdel_on_fail = TRUE)
var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN]
fan.add_hud_to(H)
/datum/quirk/item_quirk/fan_clown/add_unique()
give_item_to_holder(/obj/item/clothing/accessory/fan_clown_pin, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/fan_mime
/datum/quirk/item_quirk/fan_clown/add()
var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN]
fan.add_hud_to(quirk_holder)
/datum/quirk/item_quirk/fan_mime
name = "Mime Fan"
desc = "You enjoy mime antics and get a mood boost from wearing your mime pin."
value = 2
@@ -88,16 +84,12 @@
lose_text = "<span class='danger'>The mime doesn't seem so great.</span>"
medical_record_text = "Patient reports being a big fan of mimes."
/datum/quirk/fan_mime/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/accessory/fan_mime_pin/B = new(get_turf(H))
var/list/slots = list (
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS,
)
H.equip_in_one_of_slots(B, slots , qdel_on_fail = TRUE)
/datum/quirk/item_quirk/fan_mime/add_unique()
give_item_to_holder(/obj/item/clothing/accessory/fan_mime_pin, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/item_quirk/fan_mime/add()
var/datum/atom_hud/fan = GLOB.huds[DATA_HUD_FAN]
fan.add_hud_to(H)
fan.add_hud_to(quirk_holder)
/datum/quirk/freerunning
name = "Freerunning"
@@ -135,13 +127,7 @@
lose_text = "<span class='danger'>You start tromping around like a barbarian.</span>"
medical_record_text = "Patient's dexterity belies a strong capacity for stealth."
/datum/quirk/light_step/on_spawn()
var/datum/component/footstep/C = quirk_holder.GetComponent(/datum/component/footstep)
if(C)
C.volume *= 0.6
C.e_range -= 2
/datum/quirk/musician
/datum/quirk/item_quirk/musician
name = "Musician"
desc = "You can tune handheld musical instruments to play melodies that clear certain negative effects and soothe the soul."
value = 2
@@ -150,14 +136,8 @@
lose_text = "<span class='danger'>You forget how musical instruments work.</span>"
medical_record_text = "Patient brain scans show a highly-developed auditory pathway."
/datum/quirk/musician/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/choice_beacon/music/B = new(get_turf(H))
var/list/slots = list (
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS,
)
H.equip_in_one_of_slots(B, slots , qdel_on_fail = TRUE)
/datum/quirk/item_quirk/musician/add_unique()
give_item_to_holder(/obj/item/choice_beacon/music, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/night_vision
name = "Night Vision"
@@ -168,12 +148,19 @@
lose_text = "<span class='danger'>Everything seems a little darker.</span>"
medical_record_text = "Patient's eyes show above-average acclimation to darkness."
/datum/quirk/night_vision/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/organ/eyes/eyes = H.getorgan(/obj/item/organ/eyes)
/datum/quirk/night_vision/add()
refresh_quirk_holder_eyes()
/datum/quirk/night_vision/remove()
refresh_quirk_holder_eyes()
/datum/quirk/night_vision/proc/refresh_quirk_holder_eyes()
var/mob/living/carbon/human/human_quirk_holder = quirk_holder
var/obj/item/organ/eyes/eyes = human_quirk_holder.getorgan(/obj/item/organ/eyes)
if(!eyes || eyes.lighting_alpha)
return
eyes.refresh() //refresh their eyesight and vision
// We've either added or removed TRAIT_NIGHT_VISION before calling this proc. Just refresh the eyes.
eyes.refresh()
/datum/quirk/selfaware
name = "Self-Aware"
@@ -189,7 +176,7 @@
mob_trait = TRAIT_SKITTISH
medical_record_text = "Patient demonstrates a high aversion to danger and has described hiding in containers out of fear."
/datum/quirk/spiritual
/datum/quirk/item_quirk/spiritual
name = "Spiritual"
desc = "You hold a spiritual belief, whether in God, nature or the arcane rules of the universe. You gain comfort from the presence of holy people, and believe that your prayers are more special than others. Being in the chapel makes you happy."
value = 4
@@ -198,12 +185,11 @@
lose_text = "<span class='danger'>You lose faith!</span>"
medical_record_text = "Patient reports a belief in a higher power."
/datum/quirk/spiritual/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
H.equip_to_slot_or_del(new /obj/item/storage/fancy/candle_box(H), ITEM_SLOT_BACKPACK)
H.equip_to_slot_or_del(new /obj/item/storage/box/matches(H), ITEM_SLOT_BACKPACK)
/datum/quirk/item_quirk/spiritual/add_unique()
give_item_to_holder(/obj/item/storage/fancy/candle_box, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
give_item_to_holder(/obj/item/storage/box/matches, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/tagger
/datum/quirk/item_quirk/tagger
name = "Tagger"
desc = "You're an experienced artist. People will actually be impressed by your graffiti, and you can get twice as many uses out of drawing supplies."
value = 4
@@ -212,12 +198,8 @@
lose_text = "<span class='danger'>You forget how to tag walls properly.</span>"
medical_record_text = "Patient was recently seen for possible paint huffing incident."
/datum/quirk/tagger/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/toy/crayon/spraycan/spraycan = new(get_turf(H))
H.put_in_hands(spraycan)
H.equip_to_slot(spraycan, ITEM_SLOT_BACKPACK)
H.regenerate_icons()
/datum/quirk/item_quirk/tagger/add_unique()
give_item_to_holder(/obj/item/toy/crayon/spraycan, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/voracious
name = "Voracious"
+249 -196
View File
@@ -1,11 +1,5 @@
//predominantly negative traits
/// Defines for locations of items being added to your inventory on spawn
#define LOCATION_LPOCKET "in your left pocket"
#define LOCATION_RPOCKET "in your right pocket"
#define LOCATION_BACKPACK "in your backpack"
#define LOCATION_HANDS "in your hands"
/datum/quirk/badback
name = "Bad Back"
desc = "Thanks to your poor posture, backpacks and other bags never sit right on your back. More evently weighted objects are fine, though."
@@ -15,14 +9,46 @@
lose_text = "<span class='notice'>Your back feels better.</span>"
medical_record_text = "Patient scans indicate severe and chronic back pain."
hardcore_value = 4
var/datum/weakref/backpack
/datum/quirk/badback/on_process()
var/mob/living/carbon/human/H = quirk_holder
if(H.back && istype(H.back, /obj/item/storage/backpack))
/datum/quirk/badback/add()
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/storage/backpack/equipped_backpack = human_holder.back
if(istype(equipped_backpack))
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "back_pain", /datum/mood_event/back_pain)
RegisterSignal(human_holder.back, COMSIG_ITEM_POST_UNEQUIP, .proc/on_unequipped_backpack)
else
RegisterSignal(quirk_holder, COMSIG_MOB_EQUIPPED_ITEM, .proc/on_equipped_item)
/datum/quirk/badback/remove()
UnregisterSignal(quirk_holder, COMSIG_MOB_EQUIPPED_ITEM)
var/obj/item/storage/equipped_backpack = backpack?.resolve()
if(equipped_backpack)
UnregisterSignal(equipped_backpack, COMSIG_ITEM_POST_UNEQUIP)
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "back_pain")
/// Signal handler for when the quirk_holder equips an item. If it's a backpack, adds the back_pain mood event.
/datum/quirk/badback/proc/on_equipped_item(mob/living/source, obj/item/equipped_item, slot)
SIGNAL_HANDLER
if((slot != ITEM_SLOT_BACK) || !istype(equipped_item, /obj/item/storage/backpack))
return
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "back_pain", /datum/mood_event/back_pain)
RegisterSignal(equipped_item, COMSIG_ITEM_POST_UNEQUIP, .proc/on_unequipped_backpack)
UnregisterSignal(quirk_holder, COMSIG_MOB_EQUIPPED_ITEM)
backpack = WEAKREF(equipped_item)
/// Signal handler for when the quirk_holder unequips an equipped backpack. Removes the back_pain mood event.
/datum/quirk/badback/proc/on_unequipped_backpack(obj/item/source, force, atom/newloc, no_move, invdrop, silent)
SIGNAL_HANDLER
UnregisterSignal(source, COMSIG_ITEM_POST_UNEQUIP)
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "back_pain")
backpack = null
RegisterSignal(quirk_holder, COMSIG_MOB_EQUIPPED_ITEM, .proc/on_equipped_item)
/datum/quirk/blooddeficiency
name = "Blood Deficiency"
desc = "Your body can't produce enough blood to sustain itself."
@@ -31,8 +57,12 @@
lose_text = "<span class='notice'>You feel vigorous again.</span>"
medical_record_text = "Patient requires regular treatment for blood loss due to low production of blood."
hardcore_value = 8
processing_quirk = TRUE
/datum/quirk/blooddeficiency/process(delta_time)
if(quirk_holder.stat == DEAD)
return
/datum/quirk/blooddeficiency/on_process(delta_time)
var/mob/living/carbon/human/H = quirk_holder
if(NOBLOOD in H.dna.species.species_traits) //can't lose blood if your species doesn't have any
return
@@ -40,7 +70,7 @@
if (H.blood_volume > (BLOOD_VOLUME_SAFE - 25)) // just barely survivable without treatment
H.blood_volume -= 0.275 * delta_time
/datum/quirk/blindness
/datum/quirk/item_quirk/blindness
name = "Blind"
desc = "You are completely blind, nothing can counteract this."
value = -16
@@ -49,21 +79,21 @@
medical_record_text = "Patient has permanent blindness."
hardcore_value = 15
/datum/quirk/blindness/add()
quirk_holder.become_blind(ROUNDSTART_TRAIT)
/datum/quirk/item_quirk/blindness/add_unique()
give_item_to_holder(/obj/item/clothing/glasses/blindfold/white, list(LOCATION_EYES = ITEM_SLOT_EYES, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/blindness/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/blindfold/white/B = new(get_turf(H))
if(!H.equip_to_slot_if_possible(B, ITEM_SLOT_EYES, bypass_equip_delay_self = TRUE)) //if you can't put it on the user's eyes, put it in their hands, otherwise put it on their eyes
H.put_in_hands(B)
/datum/quirk/item_quirk/blindness/add()
quirk_holder.become_blind(QUIRK_TRAIT)
/datum/quirk/item_quirk/blindness/remove()
quirk_holder.cure_blind(QUIRK_TRAIT)
/* A couple of brain tumor stats for anyone curious / looking at this quirk for balancing:
* - It takes less 16 minute 40 seconds to die from brain death due to a brain tumor.
* - It takes 1 minutes 40 seconds to take 10% (20 organ damage) brain damage.
* - 5u mannitol will heal 12.5% (25 organ damage) brain damage
*/
/datum/quirk/brainproblems
/datum/quirk/item_quirk/brainproblems
name = "Brain Tumor"
desc = "You have a little friend in your brain that is slowly destroying it. Better bring some mannitol!"
value = -12
@@ -71,30 +101,27 @@
lose_text = "<span class='notice'>You feel wrinkled again.</span>"
medical_record_text = "Patient has a tumor in their brain that is slowly driving them to brain death."
hardcore_value = 12
/// Location of the bottle of pills on spawn
var/where
processing_quirk = TRUE
/datum/quirk/brainproblems/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/pills = new /obj/item/storage/pill_bottle/mannitol/braintumor()
var/list/slots = list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS
/datum/quirk/item_quirk/brainproblems/add_unique()
give_item_to_holder(
/obj/item/storage/pill_bottle/mannitol/braintumor,
list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS,
),
flavour_text = "These will keep you alive until you can secure a supply of medication. Don't rely on them too much!",
)
where = H.equip_in_one_of_slots(pills, slots, FALSE) || "at your feet"
/datum/quirk/brainproblems/post_add()
if(where == LOCATION_BACKPACK)
var/mob/living/carbon/human/H = quirk_holder
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
/datum/quirk/item_quirk/brainproblems/process(delta_time)
if(quirk_holder.stat == DEAD)
return
to_chat(quirk_holder, "<span class='boldnotice'>There is a bottle of mannitol pills [where] to keep you alive until you can secure a supply of medication. Don't rely on it too much!</span>")
/datum/quirk/brainproblems/on_process(delta_time)
if(HAS_TRAIT(quirk_holder, TRAIT_TUMOR_SUPPRESSED))
return
quirk_holder.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.2 * delta_time)
/datum/quirk/deafness
@@ -118,30 +145,27 @@
mood_quirk = TRUE
hardcore_value = 2
/datum/quirk/family_heirloom
/datum/quirk/item_quirk/family_heirloom
name = "Family Heirloom"
desc = "You are the current owner of an heirloom, passed down for generations. You have to keep it safe!"
value = -2
mood_quirk = TRUE
medical_record_text = "Patient demonstrates an unnatural attachment to a family heirloom."
hardcore_value = 1
/// A reference to our heirloom.
var/obj/item/heirloom
/// Where our heirloom is spawning.
var/heirloom_spawn_loc
processing_quirk = TRUE
/// A weak reference to our heirloom.
var/datum/weakref/heirloom
/datum/quirk/family_heirloom/on_spawn()
/// The quirk holder, casted to human
/datum/quirk/item_quirk/family_heirloom/add_unique()
var/mob/living/carbon/human/human_holder = quirk_holder
/// The heirloom we will spawn
var/obj/item/heirloom_type
/// The quirk holder's species - we have a 50% chance, if we have a species with a set heirloom, to choose a species heirloom.
// The quirk holder's species - we have a 50% chance, if we have a species with a set heirloom, to choose a species heirloom.
var/datum/species/holder_species = human_holder.dna?.species
if(holder_species && LAZYLEN(holder_species.family_heirlooms) && prob(50))
heirloom_type = pick(holder_species.family_heirlooms)
else
/// Our quirk holder's job
// Our quirk holder's job
var/datum/job/holder_job = SSjob.GetJob(human_holder.mind?.assigned_role)
if(holder_job && LAZYLEN(holder_job.family_heirlooms))
heirloom_type = pick(holder_job.family_heirlooms)
@@ -150,39 +174,49 @@
if(!heirloom_type)
heirloom_type = pick(/obj/item/toy/cards/deck, /obj/item/lighter, /obj/item/dice/d20)
heirloom = new heirloom_type(get_turf(quirk_holder))
var/list/slots = list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS
var/obj/new_heirloom = new heirloom_type(get_turf(human_holder))
heirloom = WEAKREF(new_heirloom)
give_item_to_holder(
new_heirloom,
list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS,
),
flavour_text = "This is a precious family heirloom, passed down from generation to generation. Keep it safe!",
)
heirloom_spawn_loc = human_holder.equip_in_one_of_slots(heirloom, slots, FALSE) || "at your feet"
/datum/quirk/family_heirloom/post_add()
if(heirloom_spawn_loc == LOCATION_BACKPACK)
var/mob/living/carbon/human/H = quirk_holder
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
to_chat(quirk_holder, "<span class='boldnotice'>There is a precious family [heirloom.name] [heirloom_spawn_loc], passed down from generation to generation. Keep it safe!</span>")
/datum/quirk/item_quirk/family_heirloom/post_add()
var/list/names = splittext(quirk_holder.real_name, " ")
var/family_name = names[names.len]
heirloom.AddComponent(/datum/component/heirloom, quirk_holder.mind, family_name)
var/obj/family_heirloom = heirloom?.resolve()
if(!family_heirloom)
to_chat(quirk_holder, "<span class='boldnotice'>A wave of existential dread runs over you as you realise your precious family heirloom is missing. Perhaps the Gods will show mercy on your cursed soul?</span>")
return
/datum/quirk/family_heirloom/on_process()
if(heirloom in quirk_holder.GetAllContents())
family_heirloom.AddComponent(/datum/component/heirloom, quirk_holder.mind, family_name)
return ..()
/datum/quirk/item_quirk/family_heirloom/process()
if(quirk_holder.stat == DEAD)
return
var/obj/family_heirloom = heirloom?.resolve()
if(family_heirloom && (family_heirloom in quirk_holder.GetAllContents()))
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "family_heirloom_missing")
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "family_heirloom", /datum/mood_event/family_heirloom)
else
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "family_heirloom")
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "family_heirloom_missing", /datum/mood_event/family_heirloom_missing)
/datum/quirk/family_heirloom/remove()
if(quirk_holder) // if the holder is still exists lets remove moods
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "family_heirloom_missing")
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "family_heirloom")
/datum/quirk/item_quirk/family_heirloom/remove()
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "family_heirloom_missing")
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "family_heirloom")
/datum/quirk/frail
name = "Frail"
@@ -219,10 +253,9 @@
mood.mood_modifier += 0.5
/datum/quirk/hypersensitive/remove()
if(quirk_holder)
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
if(mood)
mood.mood_modifier -= 0.5
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
if(mood)
mood.mood_modifier -= 0.5
/datum/quirk/light_drinker
name = "Light Drinker"
@@ -234,7 +267,7 @@
medical_record_text = "Patient demonstrates a low tolerance for alcohol. (Wimp)"
hardcore_value = 3
/datum/quirk/nearsighted //t. errorage
/datum/quirk/item_quirk/nearsighted
name = "Nearsighted"
desc = "You are nearsighted without prescription glasses, but spawn with a pair."
value = -4
@@ -243,14 +276,14 @@
medical_record_text = "Patient requires prescription glasses in order to counteract nearsightedness."
hardcore_value = 5
/datum/quirk/nearsighted/add()
quirk_holder.become_nearsighted(ROUNDSTART_TRAIT)
/datum/quirk/item_quirk/nearsighted/add_unique()
give_item_to_holder(/obj/item/clothing/glasses/regular, list(LOCATION_EYES = ITEM_SLOT_EYES, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/nearsighted/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
if(!H.equip_to_slot_if_possible(glasses, ITEM_SLOT_EYES, bypass_equip_delay_self = TRUE))
H.put_in_hands(glasses)
/datum/quirk/item_quirk/nearsighted/add()
quirk_holder.become_nearsighted(QUIRK_TRAIT)
/datum/quirk/item_quirk/nearsighted/remove()
quirk_holder.cure_nearsighted(QUIRK_TRAIT)
/datum/quirk/nyctophobia
name = "Nyctophobia"
@@ -259,19 +292,37 @@
medical_record_text = "Patient demonstrates a fear of the dark. (Seriously?)"
hardcore_value = 5
/datum/quirk/nyctophobia/on_process()
var/mob/living/carbon/human/H = quirk_holder
if(H.dna.species.id in list("shadow", "nightmare"))
return //we're tied with the dark, so we don't get scared of it; don't cleanse outright to avoid cheese
var/turf/T = get_turf(quirk_holder)
var/lums = T.get_lumcount()
if(lums <= 0.2)
if(quirk_holder.m_intent == MOVE_INTENT_RUN)
to_chat(quirk_holder, "<span class='warning'>Easy, easy, take it slow... you're in the dark...</span>")
quirk_holder.toggle_move_intent()
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "nyctophobia", /datum/mood_event/nyctophobia)
else
/datum/quirk/nyctophobia/add()
RegisterSignal(quirk_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved)
/datum/quirk/nyctophobia/remove()
UnregisterSignal(quirk_holder, COMSIG_MOVABLE_MOVED)
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "nyctophobia")
/// Called when the quirk holder moves. Updates the quirk holder's mood.
/datum/quirk/nyctophobia/proc/on_holder_moved(/mob/living/source, atom/old_loc, dir, forced)
SIGNAL_HANDLER
if(quirk_holder.stat == DEAD)
return
var/mob/living/carbon/human/human_holder = quirk_holder
if(human_holder.dna?.species.id in list("shadow", "nightmare"))
return
var/turf/holder_turf = get_turf(quirk_holder)
var/lums = holder_turf.get_lumcount()
if(lums > 0.2)
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "nyctophobia")
return
if(quirk_holder.m_intent == MOVE_INTENT_RUN)
to_chat(quirk_holder, "<span class='warning'>Easy, easy, take it slow... You're in the dark...</span>")
quirk_holder.toggle_move_intent()
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "nyctophobia", /datum/mood_event/nyctophobia)
/datum/quirk/nonviolent
name = "Pacifist"
@@ -293,23 +344,18 @@
medical_record_text = "Patient has an untreatable impairment in motor function in the lower extremities."
hardcore_value = 15
/datum/quirk/paraplegic/add()
var/datum/brain_trauma/severe/paralysis/paraplegic/T = new()
var/mob/living/carbon/human/H = quirk_holder
H.gain_trauma(T, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/paraplegic/on_spawn()
/datum/quirk/paraplegic/add_unique()
if(quirk_holder.buckled) // Handle late joins being buckled to arrival shuttle chairs.
quirk_holder.buckled.unbuckle_mob(quirk_holder)
var/turf/T = get_turf(quirk_holder)
var/obj/structure/chair/spawn_chair = locate() in T
var/turf/holder_turf = get_turf(quirk_holder)
var/obj/structure/chair/spawn_chair = locate() in holder_turf
var/obj/vehicle/ridden/wheelchair/wheels
if(quirk_holder.client?.get_award_status(HARDCORE_RANDOM_SCORE) >= 5000) //More than 5k score? you unlock the gamer wheelchair.
wheels = new /obj/vehicle/ridden/wheelchair/gold(T)
wheels = new /obj/vehicle/ridden/wheelchair/gold(holder_turf)
else
wheels = new(T)
wheels = new(holder_turf)
if(spawn_chair) // Makes spawning on the arrivals shuttle more consistent looking
wheels.setDir(spawn_chair.dir)
@@ -317,10 +363,17 @@
// During the spawning process, they may have dropped what they were holding, due to the paralysis
// So put the things back in their hands.
for(var/obj/item/dropped_item in holder_turf)
if(dropped_item.fingerprintslast == quirk_holder.ckey)
quirk_holder.put_in_hands(dropped_item)
for(var/obj/item/I in T)
if(I.fingerprintslast == quirk_holder.ckey)
quirk_holder.put_in_hands(I)
/datum/quirk/paraplegic/add()
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/paraplegic/remove()
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.cure_trauma_type(/datum/brain_trauma/severe/paralysis/paraplegic, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/poor_aim
name = "Stormtrooper Aim"
@@ -346,10 +399,10 @@
medical_record_text = "During physical examination, patient was found to have a prosthetic limb."
hardcore_value = 3
/datum/quirk/prosthetic_limb/on_spawn()
/datum/quirk/prosthetic_limb/add_unique()
var/limb_slot = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/bodypart/old_part = H.get_bodypart(limb_slot)
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/bodypart/old_part = human_holder.get_bodypart(limb_slot)
var/obj/item/bodypart/prosthetic
switch(limb_slot)
if(BODY_ZONE_L_ARM)
@@ -364,9 +417,9 @@
if(BODY_ZONE_R_LEG)
prosthetic = new/obj/item/bodypart/r_leg/robot/surplus(quirk_holder)
slot_string = "right leg"
prosthetic.replace_limb(H)
prosthetic.replace_limb(human_holder)
qdel(old_part)
H.regenerate_icons()
human_holder.regenerate_icons()
/datum/quirk/prosthetic_limb/post_add()
to_chat(quirk_holder, "<span class='boldannounce'>Your [slot_string] has been replaced with a surplus prosthetic. It is fragile and will easily come apart under duress. Additionally, \
@@ -391,16 +444,14 @@
lose_text = "<span class='notice'>You feel in tune with the world again.</span>"
medical_record_text = "Patient suffers from acute Reality Dissociation Syndrome and experiences vivid hallucinations."
hardcore_value = 6
processing_quirk = TRUE
/datum/quirk/insanity/on_process(delta_time)
if(quirk_holder.reagents.has_reagent(/datum/reagent/toxin/mindbreaker, needs_metabolizing = TRUE))
quirk_holder.hallucination = 0
/datum/quirk/insanity/process(delta_time)
if(quirk_holder.stat == DEAD)
return
if(DT_PROB(2, delta_time)) //we'll all be mad soon enough
madness()
/datum/quirk/insanity/proc/madness()
quirk_holder.hallucination += rand(10, 25)
if(DT_PROB(2, delta_time))
quirk_holder.hallucination += rand(10, 25)
/datum/quirk/insanity/post_add() //I don't /think/ we'll need this but for newbies who think "roleplay as insane" = "license to kill" it's probably a good thing to have
if(!quirk_holder.mind || quirk_holder.mind.special_role)
@@ -534,13 +585,14 @@
mood_change = -5
timeout = 3 MINUTES
/datum/quirk/junkie
/datum/quirk/item_quirk/junkie
name = "Junkie"
desc = "You can't get enough of hard drugs."
value = -6
gain_text = "<span class='danger'>You suddenly feel the craving for drugs.</span>"
medical_record_text = "Patient has a history of hard drugs."
hardcore_value = 4
processing_quirk = TRUE
var/drug_list = list(/datum/reagent/drug/crank, /datum/reagent/drug/krokodil, /datum/reagent/medicine/morphine, /datum/reagent/drug/happiness, /datum/reagent/drug/methamphetamine) //List of possible IDs
var/datum/reagent/reagent_type //!If this is defined, reagent_id will be unused and the defined reagent type will be instead.
var/datum/reagent/reagent_instance //! actual instanced version of the reagent
@@ -550,71 +602,78 @@
var/obj/item/accessory_type //! If this is null, an accessory won't be spawned.
var/process_interval = 30 SECONDS //! how frequently the quirk processes
var/next_process = 0 //! ticker for processing
var/drug_flavour_text = "Better hope you don't run out..."
/datum/quirk/junkie/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
if (!reagent_type)
/datum/quirk/item_quirk/junkie/add_unique()
var/mob/living/carbon/human/human_holder = quirk_holder
if(!reagent_type)
reagent_type = pick(drug_list)
reagent_instance = new reagent_type()
for(var/addiction in reagent_instance.addiction_types)
H.mind.add_addiction_points(addiction, 1000) ///Max that shit out
human_holder.mind.add_addiction_points(addiction, 1000)
var/current_turf = get_turf(quirk_holder)
if (!drug_container_type)
if(!drug_container_type)
drug_container_type = /obj/item/storage/pill_bottle
var/obj/item/drug_instance = new drug_container_type(current_turf)
if (istype(drug_instance, /obj/item/storage/pill_bottle))
if(istype(drug_instance, /obj/item/storage/pill_bottle))
var/pill_state = "pill[rand(1,20)]"
for(var/i in 1 to 7)
var/obj/item/reagent_containers/pill/P = new(drug_instance)
P.icon_state = pill_state
P.reagents.add_reagent(reagent_type, 3)
var/obj/item/reagent_containers/pill/pill = new(drug_instance)
pill.icon_state = pill_state
pill.reagents.add_reagent(reagent_type, 3)
var/obj/item/accessory_instance
if (accessory_type)
accessory_instance = new accessory_type(current_turf)
var/list/slots = list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK
give_item_to_holder(
drug_instance,
list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS,
),
flavour_text = drug_flavour_text,
)
where_drug = H.equip_in_one_of_slots(drug_instance, slots, FALSE) || "at your feet"
if (accessory_instance)
where_accessory = H.equip_in_one_of_slots(accessory_instance, slots, FALSE) || "at your feet"
announce_drugs()
/datum/quirk/junkie/post_add()
if(where_drug == LOCATION_BACKPACK || where_accessory == LOCATION_BACKPACK)
var/mob/living/carbon/human/H = quirk_holder
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
if(accessory_type)
give_item_to_holder(
accessory_type,
list(
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS,
)
)
/datum/quirk/junkie/remove()
/datum/quirk/item_quirk/junkie/remove()
if(quirk_holder && reagent_instance)
for(var/addiction_type in subtypesof(/datum/addiction))
quirk_holder.mind.remove_addiction_points(addiction_type, MAX_ADDICTION_POINTS) //chat feedback here. No need of lose_text.
quirk_holder.mind.remove_addiction_points(addiction_type, MAX_ADDICTION_POINTS)
/datum/quirk/junkie/proc/announce_drugs()
to_chat(quirk_holder, "<span class='boldnotice'>There is a [initial(drug_container_type.name)] of [initial(reagent_type.name)] [where_drug]. Better hope you don't run out...</span>")
/datum/quirk/junkie/on_process()
/datum/quirk/item_quirk/junkie/process(delta_time)
if(HAS_TRAIT(quirk_holder, TRAIT_NOMETABOLISM))
return
var/mob/living/carbon/human/H = quirk_holder
var/mob/living/carbon/human/human_holder = quirk_holder
if(world.time > next_process)
next_process = world.time + process_interval
var/deleted = QDELETED(reagent_instance)
var/missing_addiction = FALSE
for(var/addiction_type in reagent_instance.addiction_types)
if(!LAZYACCESS(H.mind.active_addictions, addiction_type))
if(!LAZYACCESS(human_holder.mind.active_addictions, addiction_type))
missing_addiction = TRUE
if(deleted || missing_addiction)
if(deleted)
reagent_instance = new reagent_type()
to_chat(quirk_holder, "<span class='danger'>You thought you kicked it, but you feel like you're falling back onto bad habits..</span>")
for(var/addiction in reagent_instance.addiction_types)
H.mind.add_addiction_points(addiction, 1000) ///Max that shit out
human_holder.mind.add_addiction_points(addiction, 1000) ///Max that shit out
/datum/quirk/junkie/smoker
/datum/quirk/item_quirk/junkie/smoker
name = "Smoker"
desc = "Sometimes you just really want a smoke. Probably not great for your lungs."
value = -4
@@ -623,28 +682,29 @@
reagent_type = /datum/reagent/drug/nicotine
accessory_type = /obj/item/lighter/greyscale
hardcore_value = 1
drug_flavour_text = "Make sure you get your favorite brand when you run out."
/datum/quirk/junkie/smoker/on_spawn()
/datum/quirk/item_quirk/junkie/smoker/New()
drug_container_type = pick(/obj/item/storage/fancy/cigarettes,
/obj/item/storage/fancy/cigarettes/cigpack_midori,
/obj/item/storage/fancy/cigarettes/cigpack_uplift,
/obj/item/storage/fancy/cigarettes/cigpack_robust,
/obj/item/storage/fancy/cigarettes/cigpack_robustgold,
/obj/item/storage/fancy/cigarettes/cigpack_carp)
quirk_holder?.mind?.store_memory("Your favorite cigarette packets are [initial(drug_container_type.name)]s.")
return ..()
/datum/quirk/item_quirk/junkie/smoker/post_add()
. = ..()
quirk_holder.mind.store_memory("Your favorite cigarette packets are [initial(drug_container_type.name)]s.")
/datum/quirk/junkie/smoker/announce_drugs()
to_chat(quirk_holder, "<span class='boldnotice'>There is a [initial(drug_container_type.name)] [where_drug], and a lighter [where_accessory]. Make sure you get your favorite brand when you run out.</span>")
/datum/quirk/junkie/smoker/on_process()
/datum/quirk/item_quirk/junkie/smoker/process(delta_time)
. = ..()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/I = H.get_item_by_slot(ITEM_SLOT_MASK)
if (istype(I, /obj/item/clothing/mask/cigarette))
var/obj/item/storage/fancy/cigarettes/C = drug_container_type
if(istype(I, initial(C.spawn_type)))
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/mask_item = human_holder.get_item_by_slot(ITEM_SLOT_MASK)
if (istype(mask_item, /obj/item/clothing/mask/cigarette))
var/obj/item/storage/fancy/cigarettes/cigarettes = drug_container_type
if(istype(mask_item, initial(cigarettes.spawn_type)))
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "wrong_cigs")
return
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "wrong_cigs", /datum/mood_event/wrong_brand)
@@ -659,7 +719,7 @@
medical_record_text = "Patient's mind is in a vulnerable state, and cannot recover from traumatic events."
hardcore_value = 9
/datum/quirk/allergic
/datum/quirk/item_quirk/allergic
name = "Extreme Medicine Allergy"
desc = "Ever since you were a kid, you've been allergic to certain chemicals..."
value = -6
@@ -667,45 +727,43 @@
lose_text = "<span class='notice'>You feel your immune system phase back into perfect shape.</span>"
medical_record_text = "Patient's immune system responds violently to certain chemicals."
hardcore_value = 3
processing_quirk = TRUE
var/list/allergies = list()
var/list/blacklist = list(/datum/reagent/medicine/c2,/datum/reagent/medicine/epinephrine,/datum/reagent/medicine/adminordrazine,/datum/reagent/medicine/omnizine/godblood,/datum/reagent/medicine/cordiolis_hepatico,/datum/reagent/medicine/synaphydramine,/datum/reagent/medicine/diphenhydramine)
var/allergy_string
/datum/quirk/allergic/on_spawn()
/datum/quirk/item_quirk/allergic/add_unique()
var/list/chem_list = subtypesof(/datum/reagent/medicine) - blacklist
var/list/allergy_chem_names = list()
for(var/i in 0 to 5)
var/chem = pick(chem_list)
chem_list -= chem
allergies += chem
var/datum/reagent/medicine/chem_type = pick_n_take(chem_list)
allergies += chem_type
allergy_chem_names += initial(chem_type.name)
allergy_string = allergy_chem_names.Join(", ")
name = "Extreme [allergy_string] Allergies"
medical_record_text = "Patient's immune system responds violently to [allergy_string]"
/datum/quirk/allergic/post_add()
var/display = ""
for(var/C in allergies)
var/datum/reagent/chemical = C
display += initial(chemical.name) + ", "
name = "Extreme " + display +"Allergies"
medical_record_text = "Patient's immune system responds violently to [display]"
quirk_holder?.mind.store_memory("You are allergic to [display]")
to_chat(quirk_holder, "<span class='boldnotice'>You are allergic to [display]make sure not to consume any of it!</span>")
if(!ishuman(quirk_holder))
return
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/clothing/accessory/allergy_dogtag/dogtag = new(get_turf(human_holder))
var/list/slots = list (
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS
)
dogtag.display = display
human_holder.equip_in_one_of_slots(dogtag, slots , qdel_on_fail = TRUE)
dogtag.display = allergy_string
/datum/quirk/allergic/on_process(delta_time)
. = ..()
give_item_to_holder(dogtag, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS), flavour_text = "Make sure medical staff can see this...")
/datum/quirk/item_quirk/allergic/post_add()
quirk_holder.mind.store_memory("You are allergic to [allergy_string]")
to_chat(quirk_holder, "<span class='boldnotice'>You are allergic to [allergy_string], make sure not to consume any of these!</span>")
/datum/quirk/item_quirk/allergic/process(delta_time)
if(!iscarbon(quirk_holder))
return
if(IS_IN_STASIS(quirk_holder))
return
var/mob/living/carbon/carbon_quirk_holder = quirk_holder
for(var/M in allergies)
var/datum/reagent/instantiated_med = carbon_quirk_holder.reagents.has_reagent(M)
for(var/allergy in allergies)
var/datum/reagent/instantiated_med = carbon_quirk_holder.reagents.has_reagent(allergy)
if(!instantiated_med)
continue
//Just halts the progression, I'd suggest you run to medbay asap to get it fixed
@@ -745,8 +803,3 @@
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "bad_touch", /datum/mood_event/very_bad_touch)
else
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "bad_touch", /datum/mood_event/bad_touch)
#undef LOCATION_LPOCKET
#undef LOCATION_RPOCKET
#undef LOCATION_BACKPACK
#undef LOCATION_HANDS
+115 -136
View File
@@ -37,16 +37,16 @@
medical_record_text = "Patient does not speak Galactic Common and may require an interpreter."
/datum/quirk/foreigner/add()
var/mob/living/carbon/human/H = quirk_holder
H.add_blocked_language(/datum/language/common)
if(ishumanbasic(H))
H.grant_language(/datum/language/uncommon)
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.add_blocked_language(/datum/language/common)
if(ishumanbasic(human_holder))
human_holder.grant_language(/datum/language/uncommon)
/datum/quirk/foreigner/remove()
var/mob/living/carbon/human/H = quirk_holder
H.remove_blocked_language(/datum/language/common)
if(ishumanbasic(H))
H.remove_language(/datum/language/uncommon)
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.remove_blocked_language(/datum/language/common)
if(ishumanbasic(human_holder))
human_holder.remove_language(/datum/language/uncommon)
/datum/quirk/vegetarian
name = "Vegetarian"
@@ -57,19 +57,19 @@
medical_record_text = "Patient reports a vegetarian diet."
/datum/quirk/vegetarian/add()
var/mob/living/carbon/human/H = quirk_holder
var/datum/species/species = H.dna.species
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
species.liked_food &= ~MEAT
species.disliked_food |= MEAT
/datum/quirk/vegetarian/remove()
var/mob/living/carbon/human/H = quirk_holder
if(H)
var/datum/species/species = H.dna.species
if(initial(species.liked_food) & MEAT)
species.liked_food |= MEAT
if(!(initial(species.disliked_food) & MEAT))
species.disliked_food &= ~MEAT
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
if(initial(species.liked_food) & MEAT)
species.liked_food |= MEAT
if(!(initial(species.disliked_food) & MEAT))
species.disliked_food &= ~MEAT
/datum/quirk/snob
name = "Snob"
@@ -89,15 +89,14 @@
medical_record_text = "Patient demonstrates a pathological love of pineapple."
/datum/quirk/pineapple_liker/add()
var/mob/living/carbon/human/H = quirk_holder
var/datum/species/species = H.dna.species
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
species.liked_food |= PINEAPPLE
/datum/quirk/pineapple_liker/remove()
var/mob/living/carbon/human/H = quirk_holder
if(H)
var/datum/species/species = H.dna.species
species.liked_food &= ~PINEAPPLE
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
species.liked_food &= ~PINEAPPLE
/datum/quirk/pineapple_hater
name = "Ananas Aversion"
@@ -108,15 +107,14 @@
medical_record_text = "Patient is correct to think that pineapple is disgusting."
/datum/quirk/pineapple_hater/add()
var/mob/living/carbon/human/H = quirk_holder
var/datum/species/species = H.dna.species
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
species.disliked_food |= PINEAPPLE
/datum/quirk/pineapple_hater/remove()
var/mob/living/carbon/human/H = quirk_holder
if(H)
var/datum/species/species = H.dna.species
species.disliked_food &= ~PINEAPPLE
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
species.disliked_food &= ~PINEAPPLE
/datum/quirk/deviant_tastes
name = "Deviant Tastes"
@@ -127,18 +125,17 @@
medical_record_text = "Patient demonstrates irregular nutrition preferences."
/datum/quirk/deviant_tastes/add()
var/mob/living/carbon/human/H = quirk_holder
var/datum/species/species = H.dna.species
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
var/liked = species.liked_food
species.liked_food = species.disliked_food
species.disliked_food = liked
/datum/quirk/deviant_tastes/remove()
var/mob/living/carbon/human/H = quirk_holder
if(H)
var/datum/species/species = H.dna.species
species.liked_food = initial(species.liked_food)
species.disliked_food = initial(species.disliked_food)
var/mob/living/carbon/human/human_holder = quirk_holder
var/datum/species/species = human_holder.dna.species
species.liked_food = initial(species.liked_food)
species.disliked_food = initial(species.disliked_food)
/datum/quirk/monochromatic
name = "Monochromacy"
@@ -155,59 +152,52 @@
quirk_holder.playsound_local(quirk_holder, 'sound/ambience/ambidet1.ogg', 50, FALSE)
/datum/quirk/monochromatic/remove()
if(quirk_holder)
quirk_holder.remove_client_colour(/datum/client_colour/monochrome)
quirk_holder.remove_client_colour(/datum/client_colour/monochrome)
/datum/quirk/phobia
name = "Phobia"
desc = "You are irrationally afraid of something."
value = 0
medical_record_text = "Patient has an irrational fear of something."
var/phobia
/datum/quirk/phobia/add()
if(!phobia && quirk_holder.client?.prefs.phobia)
phobia = quirk_holder.client?.prefs.phobia
if(phobia)
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.gain_trauma(new /datum/brain_trauma/mild/phobia(phobia), TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/phobia/post_add()
var/mob/living/carbon/human/H = quirk_holder
H.gain_trauma(new /datum/brain_trauma/mild/phobia(H.client?.prefs.phobia), TRAUMA_RESILIENCE_ABSOLUTE)
if(!phobia)
var/mob/living/carbon/human/human_holder = quirk_holder
phobia = human_holder.client.prefs.phobia
human_holder.gain_trauma(new /datum/brain_trauma/mild/phobia(phobia), TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/phobia/remove()
var/mob/living/carbon/human/H = quirk_holder
if(H)
H.cure_trauma_type(/datum/brain_trauma/mild/phobia, TRAUMA_RESILIENCE_ABSOLUTE)
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.cure_trauma_type(/datum/brain_trauma/mild/phobia, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/needswayfinder
/datum/quirk/item_quirk/needswayfinder
name = "Navigationally Challenged"
desc = "Lacking familiarity with certain stations, you start with a wayfinding pinpointer where available."
value = 0
medical_record_text = "Patient demonstrates a keen ability to get lost."
var/obj/item/pinpointer/wayfinding/wayfinder
var/where
/datum/quirk/needswayfinder/on_spawn()
/datum/quirk/item_quirk/needswayfinder/add_unique()
if(!GLOB.wayfindingbeacons.len)
return
var/mob/living/carbon/human/H = quirk_holder
wayfinder = new /obj/item/pinpointer/wayfinding
wayfinder.owner = H.real_name
wayfinder.roundstart = TRUE
var/mob/living/carbon/human/human_holder = quirk_holder
var/list/slots = list(
"in your left pocket" = ITEM_SLOT_LPOCKET,
"in your right pocket" = ITEM_SLOT_RPOCKET,
"in your backpack" = ITEM_SLOT_BACKPACK
)
where = H.equip_in_one_of_slots(wayfinder, slots, FALSE) || "at your feet"
var/obj/item/pinpointer/wayfinding/wayfinder = new(get_turf(quirk_holder))
wayfinder.owner = human_holder.real_name
wayfinder.from_quirk = TRUE
/datum/quirk/needswayfinder/post_add()
if(!GLOB.wayfindingbeacons.len)
return
if(where == "in your backpack")
var/mob/living/carbon/human/H = quirk_holder
SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H)
give_item_to_holder(wayfinder, list(LOCATION_LPOCKET = ITEM_SLOT_LPOCKET, LOCATION_RPOCKET = ITEM_SLOT_RPOCKET, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
to_chat(quirk_holder, "<span class='notice'>There is a pinpointer [where], which can help you find your way around. Click in-hand to activate.</span>")
/datum/quirk/bald
/datum/quirk/item_quirk/bald
name = "Smooth-Headed"
desc = "You have no hair and are quite insecure about it! Keep your wig on, or at least your head covered up."
value = 0
@@ -215,41 +205,39 @@
gain_text = "<span class='notice'>Your head is as smooth as can be, it's terrible.</span>"
lose_text = "<span class='notice'>Your head itches, could it be... growing hair?!</span>"
medical_record_text = "Patient starkly refused to take off headwear during examination."
///The user's starting hairstyle
/// The user's starting hairstyle
var/old_hair
/datum/quirk/bald/add()
var/mob/living/carbon/human/H = quirk_holder
old_hair = H.hairstyle
H.hairstyle = "Bald"
H.update_hair()
RegisterSignal(H, COMSIG_CARBON_EQUIP_HAT, .proc/equip_hat)
RegisterSignal(H, COMSIG_CARBON_UNEQUIP_HAT, .proc/unequip_hat)
/datum/quirk/item_quirk/bald/add()
var/mob/living/carbon/human/human_holder = quirk_holder
old_hair = human_holder.hairstyle
human_holder.hairstyle = "Bald"
human_holder.update_hair()
RegisterSignal(human_holder, COMSIG_CARBON_EQUIP_HAT, .proc/equip_hat)
RegisterSignal(human_holder, COMSIG_CARBON_UNEQUIP_HAT, .proc/unequip_hat)
/datum/quirk/bald/remove()
var/mob/living/carbon/human/H = quirk_holder
H.hairstyle = old_hair
H.update_hair()
UnregisterSignal(H, list(COMSIG_CARBON_EQUIP_HAT, COMSIG_CARBON_UNEQUIP_HAT))
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "bad_hair_day")
/datum/quirk/item_quirk/bald/add_unique()
var/obj/item/clothing/head/wig/natural/baldie_wig = new(get_turf(quirk_holder))
/datum/quirk/bald/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/head/wig/natural/W = new(get_turf(H))
if (old_hair == "Bald")
W.hairstyle = pick(GLOB.hairstyles_list - "Bald")
baldie_wig.hairstyle = pick(GLOB.hairstyles_list - "Bald")
else
W.hairstyle = old_hair
W.update_appearance()
var/list/slots = list (
"head" = ITEM_SLOT_HEAD,
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS,
)
H.equip_in_one_of_slots(W, slots , qdel_on_fail = TRUE)
baldie_wig.hairstyle = old_hair
baldie_wig.update_appearance()
give_item_to_holder(baldie_wig, list(LOCATION_HEAD = ITEM_SLOT_HEAD, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/item_quirk/bald/remove()
. = ..()
var/mob/living/carbon/human/human_holder = quirk_holder
human_holder.hairstyle = old_hair
human_holder.update_hair()
UnregisterSignal(human_holder, list(COMSIG_CARBON_EQUIP_HAT, COMSIG_CARBON_UNEQUIP_HAT))
SEND_SIGNAL(human_holder, COMSIG_CLEAR_MOOD_EVENT, "bad_hair_day")
///Checks if the headgear equipped is a wig and sets the mood event accordingly
/datum/quirk/bald/proc/equip_hat(mob/user, obj/item/hat)
/datum/quirk/item_quirk/bald/proc/equip_hat(mob/user, obj/item/hat)
SIGNAL_HANDLER
if(istype(hat, /obj/item/clothing/head/wig))
@@ -258,34 +246,32 @@
SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "bad_hair_day") //Our head is covered
///Applies a bad moodlet for having an uncovered head
/datum/quirk/bald/proc/unequip_hat(mob/user, obj/item/clothing, force, newloc, no_move, invdrop, silent)
/datum/quirk/item_quirk/bald/proc/unequip_hat(mob/user, obj/item/clothing, force, newloc, no_move, invdrop, silent)
SIGNAL_HANDLER
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "bad_hair_day", /datum/mood_event/bald)
/datum/quirk/tongue_tied
/datum/quirk/item_quirk/tongue_tied
name = "Tongue Tied"
desc = "Due to a past incident, your ability to communicate has been relegated to your hands."
value = 0
medical_record_text = "During physical examination, patient's tongue was found to be uniquely damaged."
//Adds tongue & gloves
/datum/quirk/tongue_tied/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/organ/tongue/old_tongue = locate() in H.internal_organs
var/obj/item/organ/tongue/tied/new_tongue = new(get_turf(H))
var/obj/item/clothing/gloves/radio/gloves = new(get_turf(H))
old_tongue.Remove(H)
new_tongue.Insert(H)
/datum/quirk/item_quirk/tongue_tied/add_unique()
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/organ/tongue/old_tongue = human_holder.getorganslot(ORGAN_SLOT_TONGUE)
old_tongue.Remove(human_holder)
qdel(old_tongue)
if(!H.equip_to_slot_if_possible(gloves, ITEM_SLOT_GLOVES, bypass_equip_delay_self = TRUE))
H.put_in_hands(gloves)
/datum/quirk/tongue_tied/post_add()
var/obj/item/organ/tongue/tied/new_tongue = new(get_turf(human_holder))
new_tongue.Insert(human_holder)
give_item_to_holder(/obj/item/clothing/gloves/radio, list(LOCATION_GLOVES = ITEM_SLOT_GLOVES, LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
/datum/quirk/item_quirk/tongue_tied/post_add()
to_chat(quirk_holder, "<span class='boldannounce'>Because you speak with your hands, having them full hinders your ability to communicate!</span>")
/datum/quirk/photographer
/datum/quirk/item_quirk/photographer
name = "Photographer"
desc = "You carry your camera and personal photo album everywhere you go, and your scrapbooks are legendary among your coworkers."
value = 0
@@ -294,37 +280,30 @@
lose_text = "<span class='danger'>You forget how photo cameras work.</span>"
medical_record_text = "Patient mentions photography as a stress-relieving hobby."
/datum/quirk/photographer/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/storage/photo_album/personal/photo_album = new(get_turf(H))
var/list/album_slots = list (
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS
)
H.equip_in_one_of_slots(photo_album, album_slots , qdel_on_fail = TRUE)
photo_album.persistence_id = "personal_[H.mind.key]" // this is a persistent album, the ID is tied to the account's key to avoid tampering
/datum/quirk/item_quirk/photographer/add_unique()
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/storage/photo_album/personal/photo_album = new(get_turf(human_holder))
photo_album.persistence_id = "personal_[human_holder.mind.key]" // this is a persistent album, the ID is tied to the account's key to avoid tampering
photo_album.persistence_load()
photo_album.name = "[H.real_name]'s photo album"
var/obj/item/camera/camera = new(get_turf(H))
var/list/camera_slots = list (
"neck" = ITEM_SLOT_NECK,
"left pocket" = ITEM_SLOT_LPOCKET,
"right pocket" = ITEM_SLOT_RPOCKET,
"backpack" = ITEM_SLOT_BACKPACK,
"hands" = ITEM_SLOT_HANDS
)
H.equip_in_one_of_slots(camera, camera_slots , qdel_on_fail = TRUE)
H.regenerate_icons()
photo_album.name = "[human_holder.real_name]'s photo album"
/datum/quirk/colorist
give_item_to_holder(photo_album, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
give_item_to_holder(
/obj/item/camera,
list(
LOCATION_NECK = ITEM_SLOT_NECK,
LOCATION_LPOCKET = ITEM_SLOT_LPOCKET,
LOCATION_RPOCKET = ITEM_SLOT_RPOCKET,
LOCATION_BACKPACK = ITEM_SLOT_BACKPACK,
LOCATION_HANDS = ITEM_SLOT_HANDS
)
)
/datum/quirk/item_quirk/colorist
name = "Colorist"
desc = "You like carrying around a hair dye spray to quickly apply color patterns to your hair."
value = 0
medical_record_text = "Patient enjoys dyeing their hair with pretty colors."
/datum/quirk/colorist/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/dyespray/spraycan = new(get_turf(H))
H.put_in_hands(spraycan)
H.equip_to_slot(spraycan, ITEM_SLOT_BACKPACK)
H.regenerate_icons()
/datum/quirk/item_quirk/colorist/add_unique()
give_item_to_holder(/obj/item/dyespray, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
+1
View File
@@ -599,6 +599,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
SHOULD_CALL_PARENT(TRUE)
visual_equipped(user, slot, initial)
SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
SEND_SIGNAL(user, COMSIG_MOB_EQUIPPED_ITEM, src, slot)
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot.
+1 -1
View File
@@ -232,7 +232,7 @@ GENE SCANNER
trauma_desc += B.scan_desc
trauma_text += trauma_desc
render_list += "<span class='alert ml-1'>Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].</span>\n"
if(C.roundstart_quirks.len)
if(C.quirks.len)
render_list += "<span class='info ml-1'>Subject Major Disabilities: [C.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY)].</span>\n"
if(advanced)
render_list += "<span class='info ml-1'>Subject Minor Disabilities: [C.get_quirk_string(FALSE, CAT_QUIRK_MINOR_DISABILITY)].</span>\n"
+2 -2
View File
@@ -169,7 +169,7 @@
itsmypinpointer = FALSE
var/is_a_thing = "are [refund_amt] credit\s."
if(refund_amt > 0 && synth_acc.has_money(refund_amt) && !attacking_pinpointer.roundstart)
if(refund_amt > 0 && synth_acc.has_money(refund_amt) && !attacking_pinpointer.from_quirk)
synth_acc.adjust_money(-refund_amt)
var/obj/item/holochip/holochip = new (user.loc)
holochip.credits = refund_amt
@@ -241,7 +241,7 @@
worn_icon_state = "pinpointer_way"
var/owner = null
var/list/beacons = list()
var/roundstart = FALSE
var/from_quirk = FALSE
/obj/item/pinpointer/wayfinding/attack_self(mob/living/user)
if(active)
+9 -7
View File
@@ -48,17 +48,19 @@
var/datum/objective/assassinate/obsessed/kill = new
kill.owner = owner
kill.target = obsessionmind
var/datum/quirk/family_heirloom/family_heirloom
var/obj/family_heirloom
for(var/datum/quirk/quirky in obsessionmind.current.roundstart_quirks)
if(istype(quirky, /datum/quirk/family_heirloom))
family_heirloom = quirky
for(var/datum/quirk/quirky in obsessionmind.current.quirks)
if(istype(quirky, /datum/quirk/item_quirk/family_heirloom))
var/datum/quirk/item_quirk/family_heirloom/heirloom_quirk = quirky
family_heirloom = heirloom_quirk.heirloom?.resolve()
break
if(family_heirloom)//oh, they have an heirloom? Well you know we have to steal that.
if(family_heirloom)
objectives_left += "heirloom"
// If they have no coworkers, jealousy will pick someone else on the station. This will never be a free objective.
if(obsessionmind.assigned_role && obsessionmind.assigned_role != "Captain")
objectives_left += "jealous"//if they have no coworkers, jealousy will pick someone else on the station. this will never be a free objective, nice.
objectives_left += "jealous"
for(var/i in 1 to 3)
var/chosen_objective = pick(objectives_left)
@@ -83,7 +85,7 @@
var/datum/objective/steal/heirloom_thief/heirloom_thief = new
heirloom_thief.owner = owner
heirloom_thief.target = obsessionmind//while you usually wouldn't need this for stealing, we need the name of the obsession
heirloom_thief.steal_target = family_heirloom.heirloom
heirloom_thief.steal_target = family_heirloom
objectives += heirloom_thief
if("jealous")
var/datum/objective/assassinate/jealous/jealous = new
@@ -375,7 +375,7 @@
SSticker.mode.make_antag_chance(humanc)
if(humanc && CONFIG_GET(flag/roundstart_traits))
SSquirks.AssignQuirks(humanc, humanc.client, TRUE)
SSquirks.AssignQuirks(humanc, humanc.client)
log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)
+1 -1
View File
@@ -253,7 +253,7 @@
blood_data["features"] = dna.features
blood_data["factions"] = faction
blood_data["quirks"] = list()
for(var/V in roundstart_quirks)
for(var/V in quirks)
var/datum/quirk/T = V
blood_data["quirks"] += T.type
return blood_data
+10 -6
View File
@@ -816,22 +816,26 @@
return
var/list/options = list("Clear"="Clear")
for(var/x in subtypesof(/datum/quirk))
var/datum/quirk/T = x
var/qname = initial(T.name)
options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T
for(var/type in subtypesof(/datum/quirk))
var/datum/quirk/quirk_type = type
if(initial(quirk_type.abstract_parent_type) == type)
continue
var/qname = initial(quirk_type.name)
options[has_quirk(quirk_type) ? "[qname] (Remove)" : "[qname] (Add)"] = quirk_type
var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in sortList(options)
if(result)
if(result == "Clear")
for(var/datum/quirk/q in roundstart_quirks)
for(var/datum/quirk/q in quirks)
remove_quirk(q.type)
else
var/T = options[result]
if(has_quirk(T))
remove_quirk(T)
else
add_quirk(T,TRUE)
add_quirk(T)
if(href_list[VV_HK_MAKE_MONKEY])
if(!check_rights(R_SPAWN))
return
@@ -865,7 +865,7 @@
damaged_message += D
combined_msg += "<span class='info'>Your [damaged_message] [damaged_plural ? "are" : "is"] hurt.</span>"
if(roundstart_quirks.len)
if(quirks.len)
combined_msg += "<span class='notice'>You have these quirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)].</span>"
to_chat(src, combined_msg.Join("\n"))
+1 -1
View File
@@ -62,7 +62,7 @@
*/
var/incorporeal_move = FALSE
var/list/roundstart_quirks = list()
var/list/quirks = list()
var/list/surgeries = list() ///a list of surgery datums. generally empty, they're added when the player wants them.
///Mob specific surgery speed modifier
+13 -11
View File
@@ -421,27 +421,29 @@
priority_absorb_key["stuns_absorbed"] += amount
return TRUE
/////////////////////////////////// DISABILITIES ////////////////////////////////////
/mob/living/proc/add_quirk(quirktype, spawn_effects) //separate proc due to the way these ones are handled
/mob/living/proc/add_quirk(quirktype) //separate proc due to the way these ones are handled
if(HAS_TRAIT(src, quirktype))
return
var/datum/quirk/T = quirktype
var/qname = initial(T.name)
var/datum/quirk/quirk = quirktype
var/qname = initial(quirk.name)
if(!SSquirks || !SSquirks.quirks[qname])
return
new quirktype (src, spawn_effects)
return TRUE
quirk = new quirktype()
if(quirk.add_to_holder(src))
return TRUE
qdel(quirk)
return FALSE
/mob/living/proc/remove_quirk(quirktype)
for(var/datum/quirk/Q in roundstart_quirks)
if(Q.type == quirktype)
qdel(Q)
for(var/datum/quirk/quirk in quirks)
if(quirk.type == quirktype)
qdel(quirk)
return TRUE
return FALSE
/mob/living/proc/has_quirk(quirktype)
for(var/datum/quirk/Q in roundstart_quirks)
if(Q.type == quirktype)
for(var/datum/quirk/quirk in quirks)
if(quirk.type == quirktype)
return TRUE
return FALSE
@@ -301,8 +301,11 @@
addiction_types = list(/datum/addiction/hallucinogens = 18) //7.2 per 2 seconds
/datum/reagent/toxin/mindbreaker/on_mob_life(mob/living/carbon/M, delta_time, times_fired)
if(!HAS_TRAIT(M, TRAIT_INSANITY))
if(HAS_TRAIT(M, TRAIT_INSANITY))
M.hallucination = 0
else
M.hallucination += 5 * REM * delta_time
return ..()
/datum/reagent/toxin/plantbgone