diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index d9286a76577..d59ce9f34c9 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -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 diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index 9ed95e77ef8..7bfa62733df 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -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" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 9f47245254e..e3539d2c3a5 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -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" diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index 4a508559291..dbd7b7e1834 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -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 diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index ccda4edc811..0e69292de5b 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -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) diff --git a/code/datums/components/footstep.dm b/code/datums/components/footstep.dm index ff9a56a5e6a..01be6039a9e 100644 --- a/code/datums/components/footstep.dm +++ b/code/datums/components/footstep.dm @@ -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 diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm index 2aeb2867f91..0431ff999ba 100644 --- a/code/datums/quirks/_quirk.dm +++ b/code/datums/quirks/_quirk.dm @@ -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, "You have \a [quirk_item] [where]. [flavour_text]") + +/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("
") : 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 = "Things far away from you start looking blurry." - lose_text = "You start seeing faraway things normally again." - 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) diff --git a/code/datums/quirks/good.dm b/code/datums/quirks/good.dm index 388de274960..5c6d65954ef 100644 --- a/code/datums/quirks/good.dm +++ b/code/datums/quirks/good.dm @@ -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 = "You feel like a drink would do you good." lose_text = "You no longer feel like drinking would ease your pain." 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 = "You feel isolated from others." 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 = "The clown doesn't seem so great." 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 = "The mime doesn't seem so great." 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 = "You start tromping around like a barbarian." 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 = "You forget how musical instruments work." 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 = "Everything seems a little darker." 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 = "You lose faith!" 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 = "You forget how to tag walls properly." 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" diff --git a/code/datums/quirks/negative.dm b/code/datums/quirks/negative.dm index bbd68a2b26a..2fc3cfdaea3 100644 --- a/code/datums/quirks/negative.dm +++ b/code/datums/quirks/negative.dm @@ -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 = "Your back feels better." 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 = "You feel vigorous again." 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 = "You feel wrinkled again." 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, "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!") - -/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, "There is a precious family [heirloom.name] [heirloom_spawn_loc], passed down from generation to generation. Keep it safe!") +/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, "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?") + 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, "Easy, easy, take it slow... you're in the dark...") - 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, "Easy, easy, take it slow... You're in the dark...") + 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, "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 = "You feel in tune with the world again." 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 = "You suddenly feel the craving for drugs." 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, "There is a [initial(drug_container_type.name)] of [initial(reagent_type.name)] [where_drug]. Better hope you don't run out...") - -/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, "You thought you kicked it, but you feel like you're falling back onto bad habits..") 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, "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.") - - -/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 = "You feel your immune system phase back into perfect shape." 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, "You are allergic to [display]make sure not to consume any of it!") - 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, "You are allergic to [allergy_string], make sure not to consume any of these!") + +/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 diff --git a/code/datums/quirks/neutral.dm b/code/datums/quirks/neutral.dm index 64a6dea04e6..23b52fd37a4 100644 --- a/code/datums/quirks/neutral.dm +++ b/code/datums/quirks/neutral.dm @@ -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, "There is a pinpointer [where], which can help you find your way around. Click in-hand to activate.") - -/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 = "Your head is as smooth as can be, it's terrible." lose_text = "Your head itches, could it be... growing hair?!" 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, "Because you speak with your hands, having them full hinders your ability to communicate!") -/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 = "You forget how photo cameras work." 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)) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 216bf867a28..72811e66e5f 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -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. diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 348db7e77b4..2d1921ddfb6 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -232,7 +232,7 @@ GENE SCANNER trauma_desc += B.scan_desc trauma_text += trauma_desc render_list += "Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n" - if(C.roundstart_quirks.len) + if(C.quirks.len) render_list += "Subject Major Disabilities: [C.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY)].\n" if(advanced) render_list += "Subject Minor Disabilities: [C.get_quirk_string(FALSE, CAT_QUIRK_MINOR_DISABILITY)].\n" diff --git a/code/game/objects/items/wayfinding.dm b/code/game/objects/items/wayfinding.dm index 9ed5bd11b3d..c799266fd27 100644 --- a/code/game/objects/items/wayfinding.dm +++ b/code/game/objects/items/wayfinding.dm @@ -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) diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm index 8e325e2ffeb..5442e2af377 100644 --- a/code/modules/antagonists/creep/creep.dm +++ b/code/modules/antagonists/creep/creep.dm @@ -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 diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 99c4d8a7331..77c7a1801cc 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -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) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index abe82f32b95..16308e68a1f 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -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 diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 391c1debb07..2c6f09aeb48 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -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 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index f714b7f0663..f92aefafdde 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -865,7 +865,7 @@ damaged_message += D combined_msg += "Your [damaged_message] [damaged_plural ? "are" : "is"] hurt." - if(roundstart_quirks.len) + if(quirks.len) combined_msg += "You have these quirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]." to_chat(src, combined_msg.Join("\n")) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 05ec2d751fd..baee489db08 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -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 diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 5820b1f9bd9..34404c8d254 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -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 diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 6e6109a8ecb..b9d0bc22b42 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -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