From 4b8b03fea035a2d3fa6b132246272ca915c8e1a5 Mon Sep 17 00:00:00 2001 From: BlackMajor Date: Mon, 25 Nov 2019 12:31:15 +1300 Subject: [PATCH] Ports new Brain Traumas from /tg/. Does not include the shitty creeper antag or Wizard Traumas, i'm porting wizard traumas later. Also adds the Desynchronizer and changes the way some heaRing args are handled. --- code/__DEFINES/components.dm | 208 +- code/__DEFINES/status_effects.dm | 3 + code/__DEFINES/traits.dm | 3 + code/__HELPERS/mobs.dm | 1198 ++++++------ code/_globalvars/lists/poll_ignore.dm | 4 + code/_onclick/hud/alert.dm | 6 + code/datums/brain_damage/brain_trauma.dm | 25 +- code/datums/brain_damage/hypnosis.dm | 17 +- code/datums/brain_damage/imaginary_friend.dm | 70 +- code/datums/brain_damage/mild.dm | 164 +- code/datums/brain_damage/phobia.dm | 119 +- code/datums/brain_damage/severe.dm | 6 +- code/datums/brain_damage/special.dm | 109 +- code/datums/brain_damage/split_personality.dm | 16 +- code/datums/components/nanites.dm | 6 - code/datums/components/stationloving.dm | 3 +- code/datums/status_effects/debuffs.dm | 97 +- code/datums/status_effects/neutral.dm | 14 + code/datums/status_effects/status_effect.dm | 9 + code/game/atoms.dm | 1740 ++++++++--------- .../temporary_visuals/miscellaneous.dm | 5 + code/game/say.dm | 2 +- .../antagonists/traitor/datum_traitor.dm | 6 + code/modules/client/client_colour.dm | 3 + code/modules/mob/living/carbon/say.dm | 16 +- code/modules/mob/living/emote.dm | 1038 +++++----- .../chemistry/reagents/alcohol_reagents.dm | 1 + .../chemistry/reagents/drink_reagents.dm | 4 + .../research/designs/bluespace_designs.dm | 162 +- .../research/nanites/nanite_programs.dm | 3 - .../nanites/nanite_programs/sensor.dm | 17 +- code/modules/research/techweb/all_nodes.dm | 9 + icons/mob/screen_alert.dmi | Bin 105669 -> 107438 bytes icons/obj/device.dmi | Bin 53647 -> 54198 bytes tgstation.dme | 1 + 35 files changed, 2740 insertions(+), 2344 deletions(-) diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 1e2ba19d..e2ef92ed 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -1,104 +1,104 @@ -#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) ) - -#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) ) - -#define COMPONENT_INCOMPATIBLE 1 -#define COMPONENT_NOTRANSFER 2 - -// How multiple components of the exact same type are handled in the same datum - -#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default) -#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed -#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted -#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new - -// All signals. Format: -// When the signal is called: (signal arguments) -// All signals send the source datum of the signal as the first argument - -// global signals -// These are signals which can be listened to by any component on any parent -// start global signals with "!", this used to be necessary but now it's just a formatting choice -////////////////////////////////////////////////////////////////// - -// /datum signals -// /atom signals - //Positions for overrides list - //End positions -#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" //from base of atom/ratvar_act(): () -///////////////// - -// /area signals - -// /turf signals - -// /atom/movable signals - -// /mob/living signals - -// /mob/living/carbon signals - -// /obj signals -#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag) - -// /obj/item signals - -// /obj/item/clothing signals - -// /obj/item/implant signals - //This uses all return values of COMSIG_IMPLANT_OTHER - -// /obj/item/pda signals - -// /obj/item/radio signals - -// /obj/item/pen signals - -// /mob/living/carbon/human signals - -// /datum/species signals - -/*******Component Specific Signals*******/ -//Janitor - -//Food - -//Mood -#define COMSIG_INCREASE_SANITY "decrease_sanity" //Called when you want to increase sanity from anywhere in the code. -#define COMSIG_DECREASE_SANITY "increase_sanity" //Same as above but to decrease sanity instead. - -//NTnet - -//Nanites - -// /datum/component/storage signals - -// /datum/action signals - -/*******Non-Signal Component Related Defines*******/ - -//Redirection component init flags -#define REDIRECT_TRANSFER_WITH_TURF 1 - -//Arch -#define ARCH_PROB "probability" //Probability for each item -#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount - -//Ouch my toes! -#define CALTROP_BYPASS_SHOES 1 -#define CALTROP_IGNORE_WALKERS 2 - -#define ELEMENT_INCOMPATIBLE 1 // Return value to cancel attaching - -// /datum/element flags -/// Causes the detach proc to be called when the host object is being deleted -#define ELEMENT_DETACH (1 << 0) -/** - * Only elements created with the same arguments given after `id_arg_index` share an element instance - * The arguments are the same when the text and number values are the same and all other values have the same ref - */ -#define ELEMENT_BESPOKE (1 << 1) -#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" //Called when the movable's glide size is updated: (new_glide_size) - -#define COMSIG_LIVING_FULLY_HEAL "living_fully_healed" //from base of /mob/living/fully_heal(): (admin_revive) - +#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) ) + +#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) ) + +#define COMPONENT_INCOMPATIBLE 1 +#define COMPONENT_NOTRANSFER 2 + +// How multiple components of the exact same type are handled in the same datum + +#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default) +#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed +#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted +#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new + +// All signals. Format: +// When the signal is called: (signal arguments) +// All signals send the source datum of the signal as the first argument + +// global signals +// These are signals which can be listened to by any component on any parent +// start global signals with "!", this used to be necessary but now it's just a formatting choice +////////////////////////////////////////////////////////////////// + +// /datum signals +// /atom signals + //Positions for overrides list + //End positions +#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" //from base of atom/ratvar_act(): () +///////////////// + +// /area signals + +// /turf signals + +// /atom/movable signals + +// /mob/living signals + +// /mob/living/carbon signals + +// /obj signals +#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag) + +// /obj/item signals + +// /obj/item/clothing signals + +// /obj/item/implant signals + //This uses all return values of COMSIG_IMPLANT_OTHER + +// /obj/item/pda signals + +// /obj/item/radio signals + +// /obj/item/pen signals + +// /mob/living/carbon/human signals + +// /datum/species signals + +/*******Component Specific Signals*******/ +//Janitor + +//Food + +//Mood +#define COMSIG_INCREASE_SANITY "decrease_sanity" //Called when you want to increase sanity from anywhere in the code. +#define COMSIG_DECREASE_SANITY "increase_sanity" //Same as above but to decrease sanity instead. + +//NTnet + +//Nanites + +// /datum/component/storage signals + +// /datum/action signals + +/*******Non-Signal Component Related Defines*******/ + +//Redirection component init flags +#define REDIRECT_TRANSFER_WITH_TURF 1 + +//Arch +#define ARCH_PROB "probability" //Probability for each item +#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount + +//Ouch my toes! +#define CALTROP_BYPASS_SHOES 1 +#define CALTROP_IGNORE_WALKERS 2 + +#define ELEMENT_INCOMPATIBLE 1 // Return value to cancel attaching + +// /datum/element flags +/// Causes the detach proc to be called when the host object is being deleted +#define ELEMENT_DETACH (1 << 0) +/** + * Only elements created with the same arguments given after `id_arg_index` share an element instance + * The arguments are the same when the text and number values are the same and all other values have the same ref + */ +#define ELEMENT_BESPOKE (1 << 1) +#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" //Called when the movable's glide size is updated: (new_glide_size) + +#define COMSIG_LIVING_FULLY_HEAL "living_fully_healed" //from base of /mob/living/fully_heal(): (admin_revive) + diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 664ebf8b..1ab4fd3f 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -7,6 +7,8 @@ #define STATUS_EFFECT_REPLACE 2 //if it allows only one, but new instances replace +#define STATUS_EFFECT_REFRESH 3 // if it only allows one, and new instances just instead refresh the timer + /////////// // BUFFS // /////////// @@ -75,6 +77,7 @@ #define STATUS_EFFECT_ICHORIAL_STAIN /datum/status_effect/ichorial_stain //Prevents a servant from being revived by vitality matrices for one minute. #define STATUS_EFFECT_STASIS /datum/status_effect/incapacitating/stasis //Halts biological functions like bleeding, chemical processing, blood regeneration, walking, etc +#define STATUS_EFFECT_SPASMS /datum/status_effect/spasms //causes random muscle spasms #define STATUS_EFFECT_NO_COMBAT_MODE /datum/status_effect/no_combat_mode //Wont allow combat mode and will disable it #define STATUS_EFFECT_MESMERIZE /datum/status_effect/no_combat_mode/mesmerize //Just reskinned no_combat_mode diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 91d7f5a8..3bfb2da0 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -112,6 +112,7 @@ #define TRAIT_NOHARDCRIT "nohardcrit" #define TRAIT_NOSOFTCRIT "nosoftcrit" #define TRAIT_MINDSHIELD "mindshield" +#define TRAIT_SIXTHSENSE "sixthsense" #define TRAIT_DISSECTED "dissected" #define TRAIT_FEARLESS "fearless" #define TRAIT_UNSTABLE "unstable" @@ -121,6 +122,7 @@ #define TRAIT_PARALYSIS_R_LEG "para-r-leg" #define TRAIT_DISK_VERIFIER "disk-verifier" #define TRAIT_UNINTELLIGIBLE_SPEECH "unintelligible-speech" +#define TRAIT_SOOTHED_THROAT "soothed-throat" #define TRAIT_LAW_ENFORCEMENT_METABOLISM "law-enforcement-metabolism" #define TRAIT_STRONG_GRABBER "strong_grabber" #define TRAIT_CALCIUM_HEALER "calcium_healer" @@ -206,6 +208,7 @@ #define CULT_TRAIT "cult" #define CURSED_ITEM_TRAIT "cursed-item" // The item is magically cursed #define ABSTRACT_ITEM_TRAIT "abstract-item" +#define STATUS_EFFECT_TRAIT "status-effect" #define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention // unique trait sources, still defines diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 49ffe578..854fa01e 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -1,598 +1,600 @@ -/proc/random_blood_type() - return pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") - -/proc/random_eye_color() - switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino")) - if("brown") - return "630" - if("hazel") - return "542" - if("grey") - return pick("666","777","888","999","aaa","bbb","ccc") - if("blue") - return "36c" - if("green") - return "060" - if("amber") - return "fc0" - if("albino") - return pick("c","d","e","f") + pick("0","1","2","3","4","5","6","7","8","9") + pick("0","1","2","3","4","5","6","7","8","9") - else - return "000" - -#define UNDIE_COLORABLE(U) (U?.has_color) - -/proc/random_underwear(gender) - if(!GLOB.underwear_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/bottom, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) - switch(gender) - if(MALE) - return pick(GLOB.underwear_m) - if(FEMALE) - return pick(GLOB.underwear_f) - else - return pick(GLOB.underwear_list) - -/proc/random_undershirt(gender) - if(!GLOB.undershirt_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/top, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) - switch(gender) - if(MALE) - return pick(GLOB.undershirt_m) - if(FEMALE) - return pick(GLOB.undershirt_f) - else - return pick(GLOB.undershirt_list) - -/proc/random_socks() - if(!GLOB.socks_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/socks, GLOB.socks_list) - return pick(GLOB.socks_list) - -/proc/random_features(intendedspecies) - if(!GLOB.tails_list_human.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, GLOB.tails_list_human) - if(!GLOB.tails_list_lizard.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard) - if(!GLOB.snouts_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, GLOB.snouts_list) - if(!GLOB.horns_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, GLOB.horns_list) - if(!GLOB.ears_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, GLOB.horns_list) - if(!GLOB.frills_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, GLOB.frills_list) - if(!GLOB.spines_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, GLOB.spines_list) - if(!GLOB.legs_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/legs, GLOB.legs_list) - if(!GLOB.body_markings_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list) - if(!GLOB.wings_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.wings_list) - if(!GLOB.deco_wings_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/deco_wings, GLOB.deco_wings_list) - if(!GLOB.moth_wings_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list) - if(!GLOB.moth_fluffs_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_fluff, GLOB.moth_fluffs_list) - if(!GLOB.moth_markings_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_markings, GLOB.moth_markings_list) - - //CIT CHANGES - genitals and such - if(!GLOB.cock_shapes_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list) - if(!GLOB.balls_shapes_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list) - if(!GLOB.vagina_shapes_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list) - if(!GLOB.breasts_shapes_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list) - if(!GLOB.ipc_screens_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/screen, GLOB.ipc_screens_list) - if(!GLOB.ipc_antennas_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/antenna, GLOB.ipc_antennas_list) - if(!GLOB.mam_body_markings_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_body_markings, GLOB.mam_body_markings_list) - if(!GLOB.mam_tails_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_tails, GLOB.mam_tails_list) - if(!GLOB.mam_ears_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_ears, GLOB.mam_ears_list) - if(!GLOB.mam_snouts_list.len) - init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_snouts, GLOB.mam_snouts_list) - - //snowflake check so people's ckey features don't get randomly put on unmonkeys/spawns - var/list/snowflake_mam_tails_list = list() - for(var/mtpath in GLOB.mam_tails_list) - var/datum/sprite_accessory/mam_tails/instance = GLOB.mam_tails_list[mtpath] - if(istype(instance, /datum/sprite_accessory)) - var/datum/sprite_accessory/S = instance - if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) - continue - if(!S.ckeys_allowed) - snowflake_mam_tails_list[S.name] = mtpath - var/list/snowflake_markings_list = list() - for(var/mmpath in GLOB.mam_body_markings_list) - var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[mmpath] - if(istype(instance, /datum/sprite_accessory)) - var/datum/sprite_accessory/S = instance - if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) - continue - if(!S.ckeys_allowed) - snowflake_markings_list[S.name] = mmpath - var/list/snowflake_ears_list = list() - for(var/mepath in GLOB.mam_ears_list) - var/datum/sprite_accessory/mam_ears/instance = GLOB.mam_ears_list[mepath] - if(istype(instance, /datum/sprite_accessory)) - var/datum/sprite_accessory/S = instance - if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) - continue - if(!S.ckeys_allowed) - snowflake_ears_list[S.name] = mepath - var/list/snowflake_mam_snouts_list = list() - for(var/mspath in GLOB.mam_snouts_list) - var/datum/sprite_accessory/mam_snouts/instance = GLOB.mam_snouts_list[mspath] - if(istype(instance, /datum/sprite_accessory)) - var/datum/sprite_accessory/S = instance - if(!S.ckeys_allowed) - snowflake_mam_snouts_list[S.name] = mspath - var/list/snowflake_ipc_antenna_list = list() - for(var/mspath in GLOB.ipc_antennas_list) - var/datum/sprite_accessory/mam_snouts/instance = GLOB.ipc_antennas_list[mspath] - if(istype(instance, /datum/sprite_accessory)) - var/datum/sprite_accessory/S = instance - if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) - continue - if(!S.ckeys_allowed) - snowflake_ipc_antenna_list[S.name] = mspath - var/color1 = random_short_color() - var/color2 = random_short_color() - var/color3 = random_short_color() - - //CIT CHANGE - changes this entire return to support cit's snowflake parts - return(list( - "mcolor" = color1, - "mcolor2" = color2, - "mcolor3" = color3, - "tail_lizard" = pick(GLOB.tails_list_lizard), - "tail_human" = "None", - "wings" = "None", - "snout" = pick(GLOB.snouts_list), - "horns" = pick(GLOB.horns_list), - "ears" = "None", - "frills" = pick(GLOB.frills_list), - "spines" = pick(GLOB.spines_list), - "body_markings" = pick(GLOB.body_markings_list), - "legs" = pick("Normal Legs","Digitigrade Legs"), - "caps" = pick(GLOB.caps_list), - "moth_wings" = pick(GLOB.moth_wings_list), - "moth_markings" = pick(GLOB.moth_markings_list), - "insect_fluff" = "None", - "taur" = "None", - "mam_body_markings" = snowflake_markings_list.len ? pick(snowflake_markings_list) : "None", - "mam_ears" = snowflake_ears_list ? pick(snowflake_ears_list) : "None", - "mam_snouts" = snowflake_mam_snouts_list ? pick(snowflake_mam_snouts_list) : "None", - "mam_tail" = snowflake_mam_tails_list ? pick(snowflake_mam_tails_list) : "None", - "mam_tail_animated" = "None", - "xenodorsal" = "Standard", - "xenohead" = "Standard", - "xenotail" = "Xenomorph Tail", - "exhibitionist" = FALSE, - "genitals_use_skintone" = FALSE, - "has_cock" = FALSE, - "cock_shape" = pick(GLOB.cock_shapes_list), - "cock_length" = 6, - "cock_girth_ratio" = COCK_GIRTH_RATIO_DEF, - "cock_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "has_sheath" = FALSE, - "sheath_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "has_balls" = FALSE, - "balls_internal" = FALSE, - "balls_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "balls_amount" = 2, - "balls_sack_size" = BALLS_SACK_SIZE_DEF, - "balls_size" = BALLS_SIZE_DEF, - "balls_shape" = "Single", - "balls_cum_rate" = CUM_RATE, - "balls_cum_mult" = CUM_RATE_MULT, - "balls_efficiency" = CUM_EFFICIENCY, - "balls_fluid" = /datum/reagent/consumable/semen, - "has_ovi" = FALSE, - "ovi_shape" = "knotted", - "ovi_length" = 6, - "ovi_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "has_eggsack" = FALSE, - "eggsack_internal" = TRUE, - "eggsack_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "eggsack_size" = BALLS_SACK_SIZE_DEF, - "eggsack_egg_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "eggsack_egg_size" = EGG_GIRTH_DEF, - "has_breasts" = FALSE, - "breasts_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "breasts_size" = pick(GLOB.breasts_size_list), - "breasts_shape" = "Pair", - "breasts_fluid" = /datum/reagent/consumable/milk, - "breasts_producing" = FALSE, - "has_vag" = FALSE, - "vag_shape" = pick(GLOB.vagina_shapes_list), - "vag_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), - "vag_clits" = 1, - "vag_clit_diam" = 0.25, - "vag_clit_len" = 0.25, - "has_womb" = FALSE, - "can_get_preg" = FALSE, - "womb_cum_rate" = CUM_RATE, - "womb_cum_mult" = CUM_RATE_MULT, - "womb_efficiency" = CUM_EFFICIENCY, - "womb_fluid" = "femcum", - "ipc_screen" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None", - "ipc_antenna" = "None", - "flavor_text" = "")) - -/proc/random_hair_style(gender) - switch(gender) - if(MALE) - return pick(GLOB.hair_styles_male_list) - if(FEMALE) - return pick(GLOB.hair_styles_female_list) - else - return pick(GLOB.hair_styles_list) - -/proc/random_facial_hair_style(gender) - switch(gender) - if(MALE) - return pick(GLOB.facial_hair_styles_male_list) - if(FEMALE) - return pick(GLOB.facial_hair_styles_female_list) - else - return pick(GLOB.facial_hair_styles_list) - -/proc/random_unique_name(gender, attempts_to_find_unique_name=10) - for(var/i in 1 to attempts_to_find_unique_name) - if(gender==FEMALE) - . = capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) - else - . = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) - - if(!findname(.)) - break - -/proc/random_unique_lizard_name(gender, attempts_to_find_unique_name=10) - for(var/i in 1 to attempts_to_find_unique_name) - . = capitalize(lizard_name(gender)) - - if(!findname(.)) - break - -/proc/random_unique_plasmaman_name(attempts_to_find_unique_name=10) - for(var/i in 1 to attempts_to_find_unique_name) - . = capitalize(plasmaman_name()) - - if(!findname(.)) - break - -/proc/random_unique_moth_name(attempts_to_find_unique_name=10) - for(var/i in 1 to attempts_to_find_unique_name) - . = capitalize(pick(GLOB.moth_first)) + " " + capitalize(pick(GLOB.moth_last)) - - if(!findname(.)) - break - -/proc/random_skin_tone() - return pick(GLOB.skin_tones) - -GLOBAL_LIST_INIT(skin_tones, list( - "albino", - "caucasian1", - "caucasian2", - "caucasian3", - "latino", - "mediterranean", - "asian1", - "asian2", - "arab", - "indian", - "african1", - "african2" - )) - -GLOBAL_LIST_EMPTY(species_list) - -/proc/age2agedescription(age) - switch(age) - if(0 to 1) - return "infant" - if(1 to 3) - return "toddler" - if(3 to 13) - return "child" - if(13 to 19) - return "teenager" - if(19 to 30) - return "young adult" - if(30 to 45) - return "adult" - if(45 to 60) - return "middle-aged" - if(60 to 70) - return "aging" - if(70 to INFINITY) - return "elderly" - else - return "unknown" - -/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0) - if(!user || !target) - return 0 - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/target_loc = target.loc - - var/holding = user.get_active_held_item() - var/datum/progressbar/progbar - if (progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - . = 1 - while (world.time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime) - if(QDELETED(user) || QDELETED(target)) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - if (progress) - qdel(progbar) - - -//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action -/mob/proc/break_do_after_checks(list/checked_health, check_clicks) - if(check_clicks && next_move > world.time) - return FALSE - return TRUE - -//pass a list in the format list("health" = mob's health var) to check health during this -/mob/living/break_do_after_checks(list/checked_health, check_clicks) - if(islist(checked_health)) - if(health < checked_health["health"]) - return FALSE - checked_health["health"] = health - return ..() - -/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) - if(!user) - return 0 - var/atom/Tloc = null - if(target && !isturf(target)) - Tloc = target.loc - - var/atom/Uloc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/holding = user.get_active_held_item() - - var/holdingnull = 1 //User's hand started out empty, check for an empty hand - if(holding) - holdingnull = 0 //Users hand started holding something, check to see if it's still holding that - - delay *= user.do_after_coefficent() - - var/datum/progressbar/progbar - if (progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - . = 1 - while (world.time < endtime) - stoplag(1) - if (progress) - progbar.update(world.time - starttime) - - if(drifting && !user.inertia_dir) - drifting = 0 - Uloc = user.loc - - if(QDELETED(user) || user.stat || user.IsKnockdown() || user.IsStun() || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - - if(isliving(user)) - var/mob/living/L = user - if(L.recoveringstam) - . = 0 - break - - if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) - if((Uloc != Tloc || Tloc != user) && !drifting) - . = 0 - break - - if(needhand) - //This might seem like an odd check, but you can still need a hand even when it's empty - //i.e the hand is used to pull some item/tool out of the construction - if(!holdingnull) - if(!holding) - . = 0 - break - if(user.get_active_held_item() != holding) - . = 0 - break - if (progress) - qdel(progbar) - -/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 - . = 1 - return - -/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks) - if(!user || !targets) - return 0 - if(!islist(targets)) - targets = list(targets) - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/list/originalloc = list() - for(var/atom/target in targets) - originalloc[target] = target.loc - - var/holding = user.get_active_held_item() - var/datum/progressbar/progbar - if(progress) - progbar = new(user, time, targets[1]) - - var/endtime = world.time + time - var/starttime = world.time - . = 1 - mainloop: - while(world.time < endtime) - stoplag(1) - if(progress) - progbar.update(world.time - starttime) - if(QDELETED(user) || !targets) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - for(var/atom/target in targets) - if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break mainloop - if(progbar) - qdel(progbar) - -/proc/is_species(A, species_datum) - . = FALSE - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.dna && istype(H.dna.species, species_datum)) - . = TRUE - -/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args) - var/turf/T = get_turf(target) - if(!T) - CRASH("attempt to spawn atom type: [spawn_type] in nullspace") - - var/list/new_args = list(T) - if(extra_args) - new_args += extra_args - - for(var/j in 1 to amount) - var/atom/X = new spawn_type(arglist(new_args)) - if (admin_spawn) - X.flags_1 |= ADMIN_SPAWNED_1 - -/proc/spawn_and_random_walk(spawn_type, target, amount, walk_chance=100, max_walk=3, always_max_walk=FALSE, admin_spawn=FALSE) - var/turf/T = get_turf(target) - var/step_count = 0 - if(!T) - CRASH("attempt to spawn atom type: [spawn_type] in nullspace") - - for(var/j in 1 to amount) - var/atom/movable/X = new spawn_type(T) - if (admin_spawn) - X.flags_1 |= ADMIN_SPAWNED_1 - - if(always_max_walk || prob(walk_chance)) - if(always_max_walk) - step_count = max_walk - else - step_count = rand(1, max_walk) - - for(var/i in 1 to step_count) - step(X, pick(NORTH, SOUTH, EAST, WEST)) - -/proc/deadchat_broadcast(message, mob/follow_target=null, turf/turf_target=null, speaker_key=null, message_type=DEADCHAT_REGULAR) - message = "[message]" - for(var/mob/M in GLOB.player_list) - var/datum/preferences/prefs - if(M.client && M.client.prefs) - prefs = M.client.prefs - else - prefs = new - - var/adminoverride = 0 - if(M.client && M.client.holder && (prefs.chat_toggles & CHAT_DEAD)) - adminoverride = 1 - if(isnewplayer(M) && !adminoverride) - continue - if(M.stat != DEAD && !adminoverride) - continue - if(speaker_key && speaker_key in prefs.ignoring) - continue - - switch(message_type) - if(DEADCHAT_DEATHRATTLE) - if(prefs.toggles & DISABLE_DEATHRATTLE) - continue - if(DEADCHAT_ARRIVALRATTLE) - if(prefs.toggles & DISABLE_ARRIVALRATTLE) - continue - - if(isobserver(M)) - var/rendered_message = message - - if(follow_target) - var/F - if(turf_target) - F = FOLLOW_OR_TURF_LINK(M, follow_target, turf_target) - else - F = FOLLOW_LINK(M, follow_target) - rendered_message = "[F] [message]" - else if(turf_target) - var/turf_link = TURF_LINK(M, turf_target) - rendered_message = "[turf_link] [message]" - - to_chat(M, rendered_message) - else - to_chat(M, message) - -//Used in chemical_mob_spawn. Generates a random mob based on a given gold_core_spawnable value. -/proc/create_random_mob(spawn_location, mob_class = HOSTILE_SPAWN) - var/static/list/mob_spawn_meancritters = list() // list of possible hostile mobs - var/static/list/mob_spawn_nicecritters = list() // and possible friendly mobs - - if(mob_spawn_meancritters.len <= 0 || mob_spawn_nicecritters.len <= 0) - for(var/T in typesof(/mob/living/simple_animal)) - var/mob/living/simple_animal/SA = T - switch(initial(SA.gold_core_spawnable)) - if(HOSTILE_SPAWN) - mob_spawn_meancritters += T - if(FRIENDLY_SPAWN) - mob_spawn_nicecritters += T - - var/chosen - if(mob_class == FRIENDLY_SPAWN) - chosen = pick(mob_spawn_nicecritters) - else - chosen = pick(mob_spawn_meancritters) - var/mob/living/simple_animal/C = new chosen(spawn_location) - return C +/proc/random_blood_type() + return pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") + +/proc/random_eye_color() + switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino")) + if("brown") + return "630" + if("hazel") + return "542" + if("grey") + return pick("666","777","888","999","aaa","bbb","ccc") + if("blue") + return "36c" + if("green") + return "060" + if("amber") + return "fc0" + if("albino") + return pick("c","d","e","f") + pick("0","1","2","3","4","5","6","7","8","9") + pick("0","1","2","3","4","5","6","7","8","9") + else + return "000" + +#define UNDIE_COLORABLE(U) (U?.has_color) + +/proc/random_underwear(gender) + if(!GLOB.underwear_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/bottom, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) + switch(gender) + if(MALE) + return pick(GLOB.underwear_m) + if(FEMALE) + return pick(GLOB.underwear_f) + else + return pick(GLOB.underwear_list) + +/proc/random_undershirt(gender) + if(!GLOB.undershirt_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/top, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) + switch(gender) + if(MALE) + return pick(GLOB.undershirt_m) + if(FEMALE) + return pick(GLOB.undershirt_f) + else + return pick(GLOB.undershirt_list) + +/proc/random_socks() + if(!GLOB.socks_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear/socks, GLOB.socks_list) + return pick(GLOB.socks_list) + +/proc/random_features(intendedspecies) + if(!GLOB.tails_list_human.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, GLOB.tails_list_human) + if(!GLOB.tails_list_lizard.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard) + if(!GLOB.snouts_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, GLOB.snouts_list) + if(!GLOB.horns_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, GLOB.horns_list) + if(!GLOB.ears_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, GLOB.horns_list) + if(!GLOB.frills_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, GLOB.frills_list) + if(!GLOB.spines_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, GLOB.spines_list) + if(!GLOB.legs_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/legs, GLOB.legs_list) + if(!GLOB.body_markings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list) + if(!GLOB.wings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.wings_list) + if(!GLOB.deco_wings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/deco_wings, GLOB.deco_wings_list) + if(!GLOB.moth_wings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list) + if(!GLOB.moth_fluffs_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_fluff, GLOB.moth_fluffs_list) + if(!GLOB.moth_markings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_markings, GLOB.moth_markings_list) + + //CIT CHANGES - genitals and such + if(!GLOB.cock_shapes_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list) + if(!GLOB.balls_shapes_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list) + if(!GLOB.vagina_shapes_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list) + if(!GLOB.breasts_shapes_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list) + if(!GLOB.ipc_screens_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/screen, GLOB.ipc_screens_list) + if(!GLOB.ipc_antennas_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/antenna, GLOB.ipc_antennas_list) + if(!GLOB.mam_body_markings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_body_markings, GLOB.mam_body_markings_list) + if(!GLOB.mam_tails_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_tails, GLOB.mam_tails_list) + if(!GLOB.mam_ears_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_ears, GLOB.mam_ears_list) + if(!GLOB.mam_snouts_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/mam_snouts, GLOB.mam_snouts_list) + + //snowflake check so people's ckey features don't get randomly put on unmonkeys/spawns + var/list/snowflake_mam_tails_list = list() + for(var/mtpath in GLOB.mam_tails_list) + var/datum/sprite_accessory/mam_tails/instance = GLOB.mam_tails_list[mtpath] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) + continue + if(!S.ckeys_allowed) + snowflake_mam_tails_list[S.name] = mtpath + var/list/snowflake_markings_list = list() + for(var/mmpath in GLOB.mam_body_markings_list) + var/datum/sprite_accessory/mam_body_markings/instance = GLOB.mam_body_markings_list[mmpath] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) + continue + if(!S.ckeys_allowed) + snowflake_markings_list[S.name] = mmpath + var/list/snowflake_ears_list = list() + for(var/mepath in GLOB.mam_ears_list) + var/datum/sprite_accessory/mam_ears/instance = GLOB.mam_ears_list[mepath] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) + continue + if(!S.ckeys_allowed) + snowflake_ears_list[S.name] = mepath + var/list/snowflake_mam_snouts_list = list() + for(var/mspath in GLOB.mam_snouts_list) + var/datum/sprite_accessory/mam_snouts/instance = GLOB.mam_snouts_list[mspath] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if(!S.ckeys_allowed) + snowflake_mam_snouts_list[S.name] = mspath + var/list/snowflake_ipc_antenna_list = list() + for(var/mspath in GLOB.ipc_antennas_list) + var/datum/sprite_accessory/mam_snouts/instance = GLOB.ipc_antennas_list[mspath] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) + continue + if(!S.ckeys_allowed) + snowflake_ipc_antenna_list[S.name] = mspath + var/color1 = random_short_color() + var/color2 = random_short_color() + var/color3 = random_short_color() + + //CIT CHANGE - changes this entire return to support cit's snowflake parts + return(list( + "mcolor" = color1, + "mcolor2" = color2, + "mcolor3" = color3, + "tail_lizard" = pick(GLOB.tails_list_lizard), + "tail_human" = "None", + "wings" = "None", + "snout" = pick(GLOB.snouts_list), + "horns" = pick(GLOB.horns_list), + "ears" = "None", + "frills" = pick(GLOB.frills_list), + "spines" = pick(GLOB.spines_list), + "body_markings" = pick(GLOB.body_markings_list), + "legs" = pick("Normal Legs","Digitigrade Legs"), + "caps" = pick(GLOB.caps_list), + "moth_wings" = pick(GLOB.moth_wings_list), + "moth_markings" = pick(GLOB.moth_markings_list), + "insect_fluff" = "None", + "taur" = "None", + "mam_body_markings" = snowflake_markings_list.len ? pick(snowflake_markings_list) : "None", + "mam_ears" = snowflake_ears_list ? pick(snowflake_ears_list) : "None", + "mam_snouts" = snowflake_mam_snouts_list ? pick(snowflake_mam_snouts_list) : "None", + "mam_tail" = snowflake_mam_tails_list ? pick(snowflake_mam_tails_list) : "None", + "mam_tail_animated" = "None", + "xenodorsal" = "Standard", + "xenohead" = "Standard", + "xenotail" = "Xenomorph Tail", + "exhibitionist" = FALSE, + "genitals_use_skintone" = FALSE, + "has_cock" = FALSE, + "cock_shape" = pick(GLOB.cock_shapes_list), + "cock_length" = 6, + "cock_girth_ratio" = COCK_GIRTH_RATIO_DEF, + "cock_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "has_sheath" = FALSE, + "sheath_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "has_balls" = FALSE, + "balls_internal" = FALSE, + "balls_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "balls_amount" = 2, + "balls_sack_size" = BALLS_SACK_SIZE_DEF, + "balls_size" = BALLS_SIZE_DEF, + "balls_shape" = "Single", + "balls_cum_rate" = CUM_RATE, + "balls_cum_mult" = CUM_RATE_MULT, + "balls_efficiency" = CUM_EFFICIENCY, + "balls_fluid" = /datum/reagent/consumable/semen, + "has_ovi" = FALSE, + "ovi_shape" = "knotted", + "ovi_length" = 6, + "ovi_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "has_eggsack" = FALSE, + "eggsack_internal" = TRUE, + "eggsack_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "eggsack_size" = BALLS_SACK_SIZE_DEF, + "eggsack_egg_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "eggsack_egg_size" = EGG_GIRTH_DEF, + "has_breasts" = FALSE, + "breasts_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "breasts_size" = pick(GLOB.breasts_size_list), + "breasts_shape" = "Pair", + "breasts_fluid" = /datum/reagent/consumable/milk, + "breasts_producing" = FALSE, + "has_vag" = FALSE, + "vag_shape" = pick(GLOB.vagina_shapes_list), + "vag_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), + "vag_clits" = 1, + "vag_clit_diam" = 0.25, + "vag_clit_len" = 0.25, + "has_womb" = FALSE, + "can_get_preg" = FALSE, + "womb_cum_rate" = CUM_RATE, + "womb_cum_mult" = CUM_RATE_MULT, + "womb_efficiency" = CUM_EFFICIENCY, + "womb_fluid" = "femcum", + "ipc_screen" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None", + "ipc_antenna" = "None", + "flavor_text" = "")) + +/proc/random_hair_style(gender) + switch(gender) + if(MALE) + return pick(GLOB.hair_styles_male_list) + if(FEMALE) + return pick(GLOB.hair_styles_female_list) + else + return pick(GLOB.hair_styles_list) + +/proc/random_facial_hair_style(gender) + switch(gender) + if(MALE) + return pick(GLOB.facial_hair_styles_male_list) + if(FEMALE) + return pick(GLOB.facial_hair_styles_female_list) + else + return pick(GLOB.facial_hair_styles_list) + +/proc/random_unique_name(gender, attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + if(gender==FEMALE) + . = capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) + else + . = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) + + if(!findname(.)) + break + +/proc/random_unique_lizard_name(gender, attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + . = capitalize(lizard_name(gender)) + + if(!findname(.)) + break + +/proc/random_unique_plasmaman_name(attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + . = capitalize(plasmaman_name()) + + if(!findname(.)) + break + +/proc/random_unique_moth_name(attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + . = capitalize(pick(GLOB.moth_first)) + " " + capitalize(pick(GLOB.moth_last)) + + if(!findname(.)) + break + +/proc/random_skin_tone() + return pick(GLOB.skin_tones) + +GLOBAL_LIST_INIT(skin_tones, list( + "albino", + "caucasian1", + "caucasian2", + "caucasian3", + "latino", + "mediterranean", + "asian1", + "asian2", + "arab", + "indian", + "african1", + "african2" + )) + +GLOBAL_LIST_EMPTY(species_list) + +/proc/age2agedescription(age) + switch(age) + if(0 to 1) + return "infant" + if(1 to 3) + return "toddler" + if(3 to 13) + return "child" + if(13 to 19) + return "teenager" + if(19 to 30) + return "young adult" + if(30 to 45) + return "adult" + if(45 to 60) + return "middle-aged" + if(60 to 70) + return "aging" + if(70 to INFINITY) + return "elderly" + else + return "unknown" + +/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0) + if(!user || !target) + return 0 + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/target_loc = target.loc + + var/holding = user.get_active_held_item() + var/datum/progressbar/progbar + if (progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + . = 1 + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + if(QDELETED(user) || QDELETED(target)) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + if((!drifting && user.loc != user_loc) || target.loc != target_loc || (!ignorehelditem && user.get_active_held_item() != holding) || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + if (progress) + qdel(progbar) + + +//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action +/mob/proc/break_do_after_checks(list/checked_health, check_clicks) + if(check_clicks && next_move > world.time) + return FALSE + return TRUE + +//pass a list in the format list("health" = mob's health var) to check health during this +/mob/living/break_do_after_checks(list/checked_health, check_clicks) + if(islist(checked_health)) + if(health < checked_health["health"]) + return FALSE + checked_health["health"] = health + return ..() + +/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) + if(!user) + return 0 + var/atom/Tloc = null + if(target && !isturf(target)) + Tloc = target.loc + + var/atom/Uloc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/holding = user.get_active_held_item() + + var/holdingnull = 1 //User's hand started out empty, check for an empty hand + if(holding) + holdingnull = 0 //Users hand started holding something, check to see if it's still holding that + + delay *= user.do_after_coefficent() + + var/datum/progressbar/progbar + if (progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + . = 1 + while (world.time < endtime) + stoplag(1) + if (progress) + progbar.update(world.time - starttime) + + if(drifting && !user.inertia_dir) + drifting = 0 + Uloc = user.loc + + if(QDELETED(user) || user.stat || user.IsKnockdown() || user.IsStun() || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + + if(isliving(user)) + var/mob/living/L = user + if(L.recoveringstam) + . = 0 + break + + if(!QDELETED(Tloc) && (QDELETED(target) || Tloc != target.loc)) + if((Uloc != Tloc || Tloc != user) && !drifting) + . = 0 + break + + if(needhand) + //This might seem like an odd check, but you can still need a hand even when it's empty + //i.e the hand is used to pull some item/tool out of the construction + if(!holdingnull) + if(!holding) + . = 0 + break + if(user.get_active_held_item() != holding) + . = 0 + break + if (progress) + qdel(progbar) + +/mob/proc/do_after_coefficent() // This gets added to the delay on a do_after, default 1 + . = 1 + return + +/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks) + if(!user || !targets) + return 0 + if(!islist(targets)) + targets = list(targets) + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/list/originalloc = list() + for(var/atom/target in targets) + originalloc[target] = target.loc + + var/holding = user.get_active_held_item() + var/datum/progressbar/progbar + if(progress) + progbar = new(user, time, targets[1]) + + var/endtime = world.time + time + var/starttime = world.time + . = 1 + mainloop: + while(world.time < endtime) + stoplag(1) + if(progress) + progbar.update(world.time - starttime) + if(QDELETED(user) || !targets) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + for(var/atom/target in targets) + if((!drifting && user_loc != user.loc) || QDELETED(target) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) + . = 0 + break mainloop + if(progbar) + qdel(progbar) + +/proc/is_species(A, species_datum) + . = FALSE + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.dna && istype(H.dna.species, species_datum)) + . = TRUE + +/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args) + var/turf/T = get_turf(target) + if(!T) + CRASH("attempt to spawn atom type: [spawn_type] in nullspace") + + var/list/new_args = list(T) + if(extra_args) + new_args += extra_args + + for(var/j in 1 to amount) + var/atom/X = new spawn_type(arglist(new_args)) + if (admin_spawn) + X.flags_1 |= ADMIN_SPAWNED_1 + +/proc/spawn_and_random_walk(spawn_type, target, amount, walk_chance=100, max_walk=3, always_max_walk=FALSE, admin_spawn=FALSE) + var/turf/T = get_turf(target) + var/step_count = 0 + if(!T) + CRASH("attempt to spawn atom type: [spawn_type] in nullspace") + + for(var/j in 1 to amount) + var/atom/movable/X = new spawn_type(T) + if (admin_spawn) + X.flags_1 |= ADMIN_SPAWNED_1 + + if(always_max_walk || prob(walk_chance)) + if(always_max_walk) + step_count = max_walk + else + step_count = rand(1, max_walk) + + for(var/i in 1 to step_count) + step(X, pick(NORTH, SOUTH, EAST, WEST)) + +/proc/deadchat_broadcast(message, mob/follow_target=null, turf/turf_target=null, speaker_key=null, message_type=DEADCHAT_REGULAR) + message = "[message]" + for(var/mob/M in GLOB.player_list) + var/datum/preferences/prefs + if(M.client && M.client.prefs) + prefs = M.client.prefs + else + prefs = new + + var/override = FALSE + if(M.client && M.client.holder && (prefs.chat_toggles & CHAT_DEAD)) + override = TRUE + if(HAS_TRAIT(M, TRAIT_SIXTHSENSE)) + override = TRUE + if(isnewplayer(M) && !override) + continue + if(M.stat != DEAD && !override) + continue + if(speaker_key && speaker_key in prefs.ignoring) + continue + + switch(message_type) + if(DEADCHAT_DEATHRATTLE) + if(prefs.toggles & DISABLE_DEATHRATTLE) + continue + if(DEADCHAT_ARRIVALRATTLE) + if(prefs.toggles & DISABLE_ARRIVALRATTLE) + continue + + if(isobserver(M)) + var/rendered_message = message + + if(follow_target) + var/F + if(turf_target) + F = FOLLOW_OR_TURF_LINK(M, follow_target, turf_target) + else + F = FOLLOW_LINK(M, follow_target) + rendered_message = "[F] [message]" + else if(turf_target) + var/turf_link = TURF_LINK(M, turf_target) + rendered_message = "[turf_link] [message]" + + to_chat(M, rendered_message) + else + to_chat(M, message) + +//Used in chemical_mob_spawn. Generates a random mob based on a given gold_core_spawnable value. +/proc/create_random_mob(spawn_location, mob_class = HOSTILE_SPAWN) + var/static/list/mob_spawn_meancritters = list() // list of possible hostile mobs + var/static/list/mob_spawn_nicecritters = list() // and possible friendly mobs + + if(mob_spawn_meancritters.len <= 0 || mob_spawn_nicecritters.len <= 0) + for(var/T in typesof(/mob/living/simple_animal)) + var/mob/living/simple_animal/SA = T + switch(initial(SA.gold_core_spawnable)) + if(HOSTILE_SPAWN) + mob_spawn_meancritters += T + if(FRIENDLY_SPAWN) + mob_spawn_nicecritters += T + + var/chosen + if(mob_class == FRIENDLY_SPAWN) + chosen = pick(mob_spawn_nicecritters) + else + chosen = pick(mob_spawn_meancritters) + var/mob/living/simple_animal/C = new chosen(spawn_location) + return C diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index b28ee975..fea595ab 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -13,6 +13,8 @@ #define POLL_IGNORE_GOLEM "golem" #define POLL_IGNORE_SWARMER "swarmer" #define POLL_IGNORE_DRONE "drone" +#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend" +#define POLL_IGNORE_SPLITPERSONALITY "split_personality" #define POLL_IGNORE_DEMON "demon" #define POLL_IGNORE_WIZARD "wizard" @@ -30,6 +32,8 @@ GLOBAL_LIST_INIT(poll_ignore_desc, list( POLL_IGNORE_GOLEM = "Golems", POLL_IGNORE_SWARMER = "Swarmer shells", POLL_IGNORE_DRONE = "Drone shells", + POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend", + POLL_IGNORE_SPLITPERSONALITY = "Split Personality", POLL_IGNORE_DEMON = "Demons", POLL_IGNORE_WIZARD = "Wizards", )) diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index afbe3218..7c135d7b 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -213,6 +213,12 @@ or something covering your eyes." desc = "Whoa man, you're tripping balls! Careful you don't get addicted... if you aren't already." icon_state = "high" +/obj/screen/alert/hypnosis + name = "Hypnosis" + desc = "Something's hypnotizing you, but you're not really sure about what." + icon_state = "hypnosis" + var/phrase + /obj/screen/alert/drunk //Not implemented name = "Drunk" desc = "All that alcohol you've been drinking is impairing your speech, motor skills, and mental cognition. Make sure to act like it." diff --git a/code/datums/brain_damage/brain_trauma.dm b/code/datums/brain_damage/brain_trauma.dm index 56a3f396..1aa1341c 100644 --- a/code/datums/brain_damage/brain_trauma.dm +++ b/code/datums/brain_damage/brain_trauma.dm @@ -1,10 +1,12 @@ //Brain Traumas are the new actual brain damage. Brain damage itself acts as a way to acquire traumas: every time brain damage is dealt, there's a chance of receiving a trauma. //This chance gets higher the higher the mob's brainloss is. Removing traumas is a separate thing from removing brain damage: you can get restored to full brain operativity, -//but keep the quirks, until repaired by mannitol (for mild/special ones) or brain surgery (for severe ones). +// but keep the quirks, until repaired by neurine, surgery, lobotomy or magic; depending on the resilience +// of the trauma. + /datum/brain_trauma var/name = "Brain Trauma" var/desc = "A trauma caused by brain damage, which causes issues to the patient." - var/scan_desc = "a generic brain trauma" //description when detected by a health scanner + var/scan_desc = "generic brain trauma" //description when detected by a health scanner var/mob/living/carbon/owner //the poor bastard var/obj/item/organ/brain/brain //the poor bastard's brain var/gain_text = "You feel traumatized." @@ -12,15 +14,21 @@ var/can_gain = TRUE var/random_gain = TRUE //can this be gained through random traumas? var/resilience = TRAUMA_RESILIENCE_BASIC //how hard is this to cure? + var/clonable = TRUE // will this transfer if the brain is cloned? /datum/brain_trauma/Destroy() - brain.traumas -= src + if(brain && brain.traumas) + brain.traumas -= src if(owner) on_lose() brain = null owner = null return ..() +/datum/brain_trauma/proc/on_clone() + if(clonable) + return new type + //Called on life ticks /datum/brain_trauma/proc/on_life() return @@ -33,17 +41,24 @@ /datum/brain_trauma/proc/on_gain() to_chat(owner, gain_text) RegisterSignal(owner, COMSIG_MOB_SAY, .proc/handle_speech) + RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/handle_hearing) //Called when removed from a mob /datum/brain_trauma/proc/on_lose(silent) if(!silent) to_chat(owner, lose_text) UnregisterSignal(owner, COMSIG_MOB_SAY) + UnregisterSignal(owner, COMSIG_MOVABLE_HEAR) //Called when hearing a spoken message -/datum/brain_trauma/proc/on_hear(message, speaker, message_language, raw_message, radio_freq) - return message +/datum/brain_trauma/proc/handle_hearing(datum/source, list/hearing_args) + UnregisterSignal(owner, COMSIG_MOVABLE_HEAR) //Called when speaking /datum/brain_trauma/proc/handle_speech(datum/source, list/speech_args) UnregisterSignal(owner, COMSIG_MOB_SAY) + + +//Called when hugging. expand into generally interacting, where future coders could switch the intent? +/datum/brain_trauma/proc/on_hug(mob/living/hugger, mob/living/hugged) + return diff --git a/code/datums/brain_damage/hypnosis.dm b/code/datums/brain_damage/hypnosis.dm index 8909d1b8..f937c196 100644 --- a/code/datums/brain_damage/hypnosis.dm +++ b/code/datums/brain_damage/hypnosis.dm @@ -5,6 +5,7 @@ gain_text = "" lose_text = "" resilience = TRAUMA_RESILIENCE_SURGERY + var/hypnotic_phrase = "" var/regex/target_phrase @@ -44,18 +45,17 @@ "You feel a part of your mind repeating this over and over. You need to follow these words.",\ "Something about this sounds... right, for some reason. You feel like you should follow these words.",\ "These words keep echoing in your mind. You find yourself completely fascinated by them.")]") - if(!HAS_TRAIT(owner, "hypnotherapy")) - to_chat(owner, "You've been hypnotized by this sentence. You must follow these words. If it isn't a clear order, you can freely interpret how to do so,\ + to_chat(owner, "You've been hypnotized by this sentence. You must follow these words. If it isn't a clear order, you can freely interpret how to do so,\ as long as you act like the words are your highest priority.") - else - to_chat(owner, "You've been hypnotized by this sentence. You feel an incredible desire to follow these words, but are able to resist it somewhat. If it isn't a clear order, you can freely interpret how to do so,\ - however this does not take precedence over your other objectives.") + var/obj/screen/alert/hypnosis/hypno_alert = owner.throw_alert("hypnosis", /obj/screen/alert/hypnosis) + hypno_alert.desc = "\"[hypnotic_phrase]\"... your mind seems to be fixated on this concept." ..() /datum/brain_trauma/hypnosis/on_lose() message_admins("[ADMIN_LOOKUPFLW(owner)] is no longer hypnotized with the phrase '[hypnotic_phrase]'.") log_game("[key_name(owner)] is no longer hypnotized with the phrase '[hypnotic_phrase]'.") - to_chat(owner, "You suddenly snap out of your fixation. The phrase '[hypnotic_phrase]' no longer feels important to you.") + to_chat(owner, "You suddenly snap out of your hypnosis. The phrase '[hypnotic_phrase]' no longer feels important to you.") + owner.clear_alert("hypnosis") ..() /datum/brain_trauma/hypnosis/on_life() @@ -67,6 +67,5 @@ if(2) new /datum/hallucination/chat(owner, TRUE, FALSE, "[hypnotic_phrase]") -/datum/brain_trauma/hypnosis/on_hear(message, speaker, message_language, raw_message, radio_freq) - message = target_phrase.Replace(message, "$1") - return message +/datum/brain_trauma/hypnosis/handle_hearing(datum/source, list/hearing_args) + hearing_args[HEARING_MESSAGE] = target_phrase.Replace(hearing_args[HEARING_MESSAGE], "$1") diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index 912dd0dd..47a96e6e 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -8,6 +8,10 @@ var/friend_initialized = FALSE /datum/brain_trauma/special/imaginary_friend/on_gain() + var/mob/living/M = owner + if(M.stat == DEAD || !M.client) + qdel(src) + return ..() make_friend() get_ghost() @@ -43,7 +47,7 @@ /datum/brain_trauma/special/imaginary_friend/proc/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) friend.key = C.key @@ -74,26 +78,34 @@ /mob/camera/imaginary_friend/Login() ..() - to_chat(src, "You are the imaginary friend of [owner]!") - to_chat(src, "You are absolutely loyal to your friend, no matter what.") - to_chat(src, "You cannot directly influence the world around you, but you can see what [owner] cannot.") + greet() Show() +/mob/camera/imaginary_friend/proc/greet() + to_chat(src, "You are the imaginary friend of [owner]!") + to_chat(src, "You are absolutely loyal to your friend, no matter what.") + to_chat(src, "You cannot directly influence the world around you, but you can see what [owner] cannot.") + /mob/camera/imaginary_friend/Initialize(mapload, _trauma) . = ..() - var/gender = pick(MALE, FEMALE) - real_name = random_unique_name(gender) - name = real_name + trauma = _trauma owner = trauma.owner copy_known_languages_from(owner, TRUE) - human_image = get_flat_human_icon(null, pick(SSjob.occupations)) + + setup_friend() join = new join.Grant(src) hide = new hide.Grant(src) +/mob/camera/imaginary_friend/proc/setup_friend() + var/gender = pick(MALE, FEMALE) + real_name = random_unique_name(gender) + name = real_name + human_image = get_flat_human_icon(null, pick(SSjob.occupations)) + /mob/camera/imaginary_friend/proc/Show() if(!client) //nobody home return @@ -132,7 +144,7 @@ if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot send IC messages (muted).") return - if (src.client.handle_spam_prevention(message,MUTE_IC)) + if (!(ignore_spam || forced) && src.client.handle_spam_prevention(message,MUTE_IC)) return friend_talk(message) @@ -220,4 +232,42 @@ var/mob/camera/imaginary_friend/I = owner I.hidden = !I.hidden I.Show() - update_status() \ No newline at end of file + update_status() + +//down here is the trapped mind +//like imaginary friend but a lot less imagination and more like mind prison// + +/datum/brain_trauma/special/imaginary_friend/trapped_owner + name = "Trapped Victim" + desc = "Patient appears to be targeted by an invisible entity." + gain_text = "" + lose_text = "" + random_gain = FALSE + +/datum/brain_trauma/special/imaginary_friend/trapped_owner/make_friend() + friend = new /mob/camera/imaginary_friend/trapped(get_turf(owner), src) + +/datum/brain_trauma/special/imaginary_friend/trapped_owner/reroll_friend() //no rerolling- it's just the last owner's hell + if(friend.client) //reconnected + return + friend_initialized = FALSE + QDEL_NULL(friend) + qdel(src) + +/datum/brain_trauma/special/imaginary_friend/trapped_owner/get_ghost() //no randoms + return + +/mob/camera/imaginary_friend/trapped + name = "figment of imagination?" + real_name = "figment of imagination?" + desc = "The previous host of this body." + +/mob/camera/imaginary_friend/trapped/greet() + to_chat(src, "You have managed to hold on as a figment of the new host's imagination!") + to_chat(src, "All hope is lost for you, but at least you may interact with your host. You do not have to be loyal to them.") + to_chat(src, "You cannot directly influence the world around you, but you can see what the host cannot.") + +/mob/camera/imaginary_friend/trapped/setup_friend() + real_name = "[owner.real_name]?" + name = real_name + human_image = icon('icons/mob/lavaland/lavaland_monsters.dmi', icon_state = "curseblob") diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index c049a7db..012f771a 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -64,21 +64,21 @@ name = "Speech Impediment" desc = "Patient is unable to form coherent sentences." scan_desc = "communication disorder" - gain_text = "" //mutation will handle the text - lose_text = "" + gain_text = "You can't seem to form any coherent thoughts!" + lose_text = "Your mind feels more clear." /datum/brain_trauma/mild/speech_impediment/on_gain() ADD_TRAIT(owner, TRAIT_UNINTELLIGIBLE_SPEECH, TRAUMA_TRAIT) - . = ..() + ..() /datum/brain_trauma/mild/speech_impediment/on_lose() REMOVE_TRAIT(owner, TRAIT_UNINTELLIGIBLE_SPEECH, TRAUMA_TRAIT) - . = ..() + ..() /datum/brain_trauma/mild/concussion name = "Concussion" desc = "Patient's brain is concussed." - scan_desc = "a concussion" + scan_desc = "concussion" gain_text = "Your head hurts!" lose_text = "The pressure inside your head starts fading." @@ -157,54 +157,108 @@ gain_text = "Your muscles feel oddly faint." lose_text = "You feel in control of your muscles again." -/datum/brain_trauma/mild/muscle_spasms/on_life() - if(prob(7)) - switch(rand(1,5)) - if(1) - if(owner.canmove && !isspaceturf(owner.loc)) - to_chat(owner, "Your leg spasms!") - step(owner, pick(GLOB.cardinals)) - if(2) - if(owner.incapacitated()) - return - var/obj/item/I = owner.get_active_held_item() - if(I) - to_chat(owner, "Your fingers spasm!") - owner.log_message("used [I] due to a Muscle Spasm", LOG_ATTACK) - I.attack_self(owner) - if(3) - var/prev_intent = owner.a_intent - owner.a_intent = INTENT_HARM - - var/range = 1 - if(istype(owner.get_active_held_item(), /obj/item/gun)) //get targets to shoot at - range = 7 - - var/list/mob/living/targets = list() - for(var/mob/M in oview(owner, range)) - if(isliving(M)) - targets += M - if(LAZYLEN(targets)) - to_chat(owner, "Your arm spasms!") - owner.log_message(" attacked someone due to a Muscle Spasm") //the following attack will log itself - owner.ClickOn(pick(targets)) - owner.a_intent = prev_intent - if(4) - var/prev_intent = owner.a_intent - owner.a_intent = INTENT_HARM - to_chat(owner, "Your arm spasms!") - owner.log_message("attacked [owner.p_them()]self to a Muscle Spasm", LOG_ATTACK) - owner.ClickOn(owner) - owner.a_intent = prev_intent - if(5) - if(owner.incapacitated()) - return - var/obj/item/I = owner.get_active_held_item() - var/list/turf/targets = list() - for(var/turf/T in oview(owner, 3)) - targets += T - if(LAZYLEN(targets) && I) - to_chat(owner, "Your arm spasms!") - owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK) - owner.throw_item(pick(targets)) +/datum/brain_trauma/mild/muscle_spasms/on_gain() + owner.apply_status_effect(STATUS_EFFECT_SPASMS) ..() + +/datum/brain_trauma/mild/muscle_spasms/on_lose() + owner.remove_status_effect(STATUS_EFFECT_SPASMS) + ..() + +/datum/brain_trauma/mild/nervous_cough + name = "Nervous Cough" + desc = "Patient feels a constant need to cough." + scan_desc = "nervous cough" + gain_text = "Your throat itches incessantly..." + lose_text = "Your throat stops itching." + +/datum/brain_trauma/mild/nervous_cough/on_life() + if(prob(12) && !HAS_TRAIT(owner, TRAIT_SOOTHED_THROAT)) + if(prob(5)) + to_chat(owner, "[pick("You have a coughing fit!", "You can't stop coughing!")]") + owner.Stun(20) + owner.emote("cough") + addtimer(CALLBACK(owner, /mob/.proc/emote, "cough"), 6) + addtimer(CALLBACK(owner, /mob/.proc/emote, "cough"), 12) + owner.emote("cough") + ..() + +/datum/brain_trauma/mild/expressive_aphasia + name = "Expressive Aphasia" + desc = "Patient is affected by partial loss of speech leading to a reduced vocabulary." + scan_desc = "inability to form complex sentences" + gain_text = "You lose your grasp on complex words." + lose_text = "You feel your vocabulary returning to normal again." + + var/static/list/common_words = world.file2list("strings/1000_most_common.txt") + +/datum/brain_trauma/mild/expressive_aphasia/handle_speech(datum/source, list/speech_args) + var/message = speech_args[SPEECH_MESSAGE] + if(message) + var/list/message_split = splittext(message, " ") + var/list/new_message = list() + + for(var/word in message_split) + var/suffix = copytext(word,-1) + + // Check if we have a suffix and break it out of the word + if(suffix in list("." , "," , ";" , "!" , ":" , "?")) + word = copytext(word,1,-1) + else + suffix = "" + + word = html_decode(word) + + if(lowertext(word) in common_words) + new_message += word + suffix + else + if(prob(30) && message_split.len > 2) + new_message += pick("uh","erm") + break + else + var/list/charlist = string2charlist(word) // Stupid shit code + shuffle_inplace(charlist) + charlist.len = round(charlist.len * 0.5,1) + new_message += html_encode(jointext(charlist,"")) + suffix + + message = jointext(new_message, " ") + + speech_args[SPEECH_MESSAGE] = trim(message) + +/datum/brain_trauma/mild/mind_echo + name = "Mind Echo" + desc = "Patient's language neurons do not terminate properly, causing previous speech patterns to occasionally resurface spontaneously." + scan_desc = "looping neural pattern" + gain_text = "You feel a faint echo of your thoughts..." + lose_text = "The faint echo fades away." + var/list/hear_dejavu = list() + var/list/speak_dejavu = list() + +/datum/brain_trauma/mild/mind_echo/handle_hearing(datum/source, list/hearing_args) + if(owner == hearing_args[HEARING_SPEAKER]) + return + if(hear_dejavu.len >= 5) + if(prob(25)) + var/deja_vu = pick_n_take(hear_dejavu) + var/static/regex/quoted_spoken_message = regex("\".+\"", "gi") + hearing_args[HEARING_MESSAGE] = quoted_spoken_message.Replace(hearing_args[HEARING_MESSAGE], "\"[deja_vu]\"") //Quotes included to avoid cases where someone says part of their name + return + if(hear_dejavu.len >= 15) + if(prob(50)) + popleft(hear_dejavu) //Remove the oldest + hear_dejavu += hearing_args[HEARING_RAW_MESSAGE] + else + hear_dejavu += hearing_args[HEARING_RAW_MESSAGE] + +/datum/brain_trauma/mild/mind_echo/handle_speech(datum/source, list/speech_args) + if(speak_dejavu.len >= 5) + if(prob(25)) + var/deja_vu = pick_n_take(speak_dejavu) + speech_args[SPEECH_MESSAGE] = deja_vu + return + if(speak_dejavu.len >= 15) + if(prob(50)) + popleft(speak_dejavu) //Remove the oldest + speak_dejavu += speech_args[SPEECH_MESSAGE] + else + speak_dejavu += speech_args[SPEECH_MESSAGE] \ No newline at end of file diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm index f802555c..034f2baa 100644 --- a/code/datums/brain_damage/phobia.dm +++ b/code/datums/brain_damage/phobia.dm @@ -2,8 +2,8 @@ name = "Phobia" desc = "Patient is unreasonably afraid of something." scan_desc = "phobia" - gain_text = "" - lose_text = "" + gain_text = "You start finding default values very unnerving..." + lose_text = "You no longer feel afraid of default values." var/phobia_type var/next_check = 0 var/next_scare = 0 @@ -14,8 +14,10 @@ var/list/trigger_turfs var/list/trigger_species -/datum/brain_trauma/mild/phobia/New(specific_type) - phobia_type = specific_type +/datum/brain_trauma/mild/phobia/New(new_phobia_type) + if(new_phobia_type) + phobia_type = new_phobia_type + if(!phobia_type) phobia_type = pick(SStraumas.phobia_types) @@ -29,6 +31,11 @@ trigger_species = SStraumas.phobia_species[phobia_type] ..() + +/datum/brain_trauma/mild/phobia/on_clone() + if(clonable) + return new type(phobia_type) + /datum/brain_trauma/mild/phobia/on_life() ..() if(HAS_TRAIT(owner, TRAIT_FEARLESS)) @@ -44,6 +51,12 @@ if(is_type_in_typecache(O, trigger_objs)) freak_out(O) return + for(var/mob/living/carbon/human/HU in seen_atoms) //check equipment for trigger items + for(var/X in HU.get_all_slots() | HU.held_items) + var/obj/I = X + if(!QDELETED(I) && is_type_in_typecache(I, trigger_objs)) + freak_out(I) + return if(LAZYLEN(trigger_turfs)) for(var/turf/T in seen_atoms) @@ -51,45 +64,41 @@ freak_out(T) return - if(LAZYLEN(trigger_mobs) || LAZYLEN(trigger_objs)) + seen_atoms -= owner //make sure they aren't afraid of themselves. + if(LAZYLEN(trigger_mobs) || LAZYLEN(trigger_species)) for(var/mob/M in seen_atoms) if(is_type_in_typecache(M, trigger_mobs)) freak_out(M) return - else if(ishuman(M)) //check their equipment for trigger items + else if(ishuman(M)) //check their species var/mob/living/carbon/human/H = M if(LAZYLEN(trigger_species) && H.dna && H.dna.species && is_type_in_typecache(H.dna.species, trigger_species)) freak_out(H) + return - for(var/X in H.get_all_slots() | H.held_items) - var/obj/I = X - if(!QDELETED(I) && is_type_in_typecache(I, trigger_objs)) - freak_out(I) - return - -/datum/brain_trauma/mild/phobia/on_hear(message, speaker, message_language, raw_message, radio_freq) +/datum/brain_trauma/mild/phobia/handle_hearing(datum/source, list/hearing_args) if(!owner.can_hear() || world.time < next_scare) //words can't trigger you if you can't hear them *taps head* - return message + return if(HAS_TRAIT(owner, TRAIT_FEARLESS)) - return message + return for(var/word in trigger_words) - var/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i") + var/regex/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i") - if(findtext(raw_message, reg)) + if(findtext(hearing_args[HEARING_RAW_MESSAGE], reg)) addtimer(CALLBACK(src, .proc/freak_out, null, word), 10) //to react AFTER the chat message + hearing_args[HEARING_MESSAGE] = reg.Replace(hearing_args[HEARING_MESSAGE], "$1") break - return message /datum/brain_trauma/mild/phobia/handle_speech(datum/source, list/speech_args) if(HAS_TRAIT(owner, TRAIT_FEARLESS)) return for(var/word in trigger_words) - var/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i") + var/regex/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i") if(findtext(speech_args[SPEECH_MESSAGE], reg)) - to_chat(owner, "You can't bring yourself to say the word \"[word]\"!") + to_chat(owner, "You can't bring yourself to say the word \"[word]\"!") speech_args[SPEECH_MESSAGE] = "" /datum/brain_trauma/mild/phobia/proc/freak_out(atom/reason, trigger_word) @@ -125,6 +134,76 @@ owner.Jitter(10) owner.stuttering += 10 +// Defined phobia types for badminry, not included in the RNG trauma pool to avoid diluting. + +/datum/brain_trauma/mild/phobia/spiders + phobia_type = "spiders" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/space + phobia_type = "space" + random_gain = FALSE + /datum/brain_trauma/mild/phobia/security phobia_type = "security" random_gain = FALSE + +/datum/brain_trauma/mild/phobia/clowns + phobia_type = "clowns" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/greytide + phobia_type = "greytide" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/lizards + phobia_type = "lizards" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/skeletons + phobia_type = "skeletons" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/snakes + phobia_type = "snakes" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/robots + phobia_type = "robots" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/doctors + phobia_type = "doctors" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/authority + phobia_type = "authority" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/supernatural + phobia_type = "the supernatural" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/aliens + phobia_type = "aliens" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/strangers + phobia_type = "strangers" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/birds + phobia_type = "birds" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/falling + phobia_type = "falling" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/anime + phobia_type = "anime" + random_gain = FALSE + +/datum/brain_trauma/mild/phobia/conspiracies + phobia_type = "conspiracies" + random_gain = FALSE diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index 890e9cf9..0e08c4cd 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -119,7 +119,7 @@ owner.update_disabled_bodyparts() /datum/brain_trauma/severe/paralysis/paraplegic - //can_gain = FALSE maybe breaks. + random_gain = FALSE paralysis_type = "legs" resilience = TRAUMA_RESILIENCE_ABSOLUTE @@ -149,7 +149,7 @@ /datum/brain_trauma/severe/monophobia name = "Monophobia" desc = "Patient feels sick and distressed when not around other people, leading to potentially lethal levels of stress." - scan_desc = "severe monophobia" + scan_desc = "monophobia" gain_text = "" lose_text = "You feel like you could be safe on your own." var/stress = 0 @@ -168,7 +168,7 @@ if(stress > 10 && (prob(5))) stress_reaction() else - stress -= 4 + stress = max(stress - 4, 0) /datum/brain_trauma/severe/monophobia/proc/check_alone() if(HAS_TRAIT(owner, TRAIT_BLIND)) diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm index d9e6f006..b52c7d39 100644 --- a/code/datums/brain_damage/special.dm +++ b/code/datums/brain_damage/special.dm @@ -22,6 +22,14 @@ else speak("neutral", prob(25)) +/datum/brain_trauma/special/godwoken/on_gain() + ADD_TRAIT(owner, TRAIT_HOLY, TRAUMA_TRAIT) + ..() + +/datum/brain_trauma/special/godwoken/on_lose() + REMOVE_TRAIT(owner, TRAIT_HOLY, TRAUMA_TRAIT) + ..() + /datum/brain_trauma/special/godwoken/proc/speak(type, include_owner = FALSE) var/message switch(type) @@ -36,7 +44,7 @@ else message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral") - playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, 1, 5) + playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 5) voice_of_god(message, owner, list("colossus","yell"), 2.5, include_owner, FALSE) /datum/brain_trauma/special/bluespace_prophet @@ -134,7 +142,101 @@ /datum/brain_trauma/special/psychotic_brawling/bath_salts name = "Chemical Violent Psychosis" - random_gain = FALSE + clonable = FALSE + +/datum/brain_trauma/special/tenacity + name = "Tenacity" + desc = "Patient is psychologically unaffected by pain and injuries, and can remain standing far longer than a normal person." + scan_desc = "traumatic neuropathy" + gain_text = "You suddenly stop feeling pain." + lose_text = "You realize you can feel pain again." + +/datum/brain_trauma/special/tenacity/on_gain() + ADD_TRAIT(owner, TRAIT_NOSOFTCRIT, TRAUMA_TRAIT) + ADD_TRAIT(owner, TRAIT_NOHARDCRIT, TRAUMA_TRAIT) + ..() + +/datum/brain_trauma/special/tenacity/on_lose() + REMOVE_TRAIT(owner, TRAIT_NOSOFTCRIT, TRAUMA_TRAIT) + REMOVE_TRAIT(owner, TRAIT_NOHARDCRIT, TRAUMA_TRAIT) + ..() + +/datum/brain_trauma/special/death_whispers + name = "Functional Cerebral Necrosis" + desc = "Patient's brain is stuck in a functional near-death state, causing occasional moments of lucid hallucinations, which are often interpreted as the voices of the dead." + scan_desc = "chronic functional necrosis" + gain_text = "You feel dead inside." + lose_text = "You feel alive again." + var/active = FALSE + +/datum/brain_trauma/special/death_whispers/on_life() + ..() + if(!active && prob(2)) + whispering() + +/datum/brain_trauma/special/death_whispers/on_lose() + if(active) + cease_whispering() + ..() + +/datum/brain_trauma/special/death_whispers/proc/whispering() + ADD_TRAIT(owner, TRAIT_SIXTHSENSE, TRAUMA_TRAIT) + active = TRUE + addtimer(CALLBACK(src, .proc/cease_whispering), rand(50, 300)) + +/datum/brain_trauma/special/death_whispers/proc/cease_whispering() + REMOVE_TRAIT(owner, TRAIT_SIXTHSENSE, TRAUMA_TRAIT) + active = FALSE + +/datum/brain_trauma/special/existential_crisis + name = "Existential Crisis" + desc = "Patient's hold on reality becomes faint, causing occasional bouts of non-existence." + scan_desc = "existential crisis" + gain_text = "You feel less real." + lose_text = "You feel more substantial again." + var/obj/effect/abstract/sync_holder/veil/veil + var/next_crisis = 0 + +/datum/brain_trauma/special/existential_crisis/on_life() + ..() + if(!veil && world.time > next_crisis && prob(3)) + if(isturf(owner.loc)) + fade_out() + +/datum/brain_trauma/special/existential_crisis/on_lose() + if(veil) + fade_in() + ..() + +/datum/brain_trauma/special/existential_crisis/proc/fade_out() + if(veil) + return + var/duration = rand(50, 450) + veil = new(owner.drop_location()) + to_chat(owner, "[pick("You stop thinking for a moment. Therefore you are not.",\ + "To be or not to be...",\ + "Why exist?",\ + "You stop keeping it real.",\ + "Your grip on existence slips.",\ + "Do you even exist?",\ + "You simply fade away.")]") + owner.forceMove(veil) + SEND_SIGNAL(owner, COMSIG_MOVABLE_SECLUDED_LOCATION) + for(var/thing in owner) + var/atom/movable/AM = thing + SEND_SIGNAL(AM, COMSIG_MOVABLE_SECLUDED_LOCATION) + next_crisis = world.time + 600 + addtimer(CALLBACK(src, .proc/fade_in), duration) + +/datum/brain_trauma/special/existential_crisis/proc/fade_in() + QDEL_NULL(veil) + to_chat(owner, "You fade back into reality.") + next_crisis = world.time + 600 + +//base sync holder is in desynchronizer.dm +/obj/effect/abstract/sync_holder/veil + name = "non-existence" + desc = "Existence is just a state of mind." /datum/brain_trauma/special/beepsky name = "Criminal" @@ -142,6 +244,7 @@ scan_desc = "criminal mind" gain_text = "Justice is coming for you." lose_text = "You were absolved for your crimes." + clonable = FALSE random_gain = FALSE var/obj/effect/hallucination/simple/securitron/beepsky @@ -201,4 +304,4 @@ /obj/effect/hallucination/simple/securitron/Destroy() STOP_PROCESSING(SSfastprocess,src) - return ..() \ No newline at end of file + return ..() diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index 653b8a98..87665961 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -13,6 +13,10 @@ var/mob/living/split_personality/owner_backseat /datum/brain_trauma/severe/split_personality/on_gain() + var/mob/living/M = owner + if(M.stat == DEAD) //No use assigning people to a corpse + qdel(src) + return ..() make_backseats() get_ghost() @@ -23,7 +27,7 @@ /datum/brain_trauma/severe/split_personality/proc/get_ghost() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) stranger_backseat.key = C.key @@ -191,13 +195,13 @@ /datum/brain_trauma/severe/split_personality/brainwashing/on_life() return //no random switching -/datum/brain_trauma/severe/split_personality/brainwashing/on_hear(message, speaker, message_language, raw_message, radio_freq) - if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == speaker) - return message +/datum/brain_trauma/severe/split_personality/brainwashing/handle_hearing(datum/source, list/hearing_args) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) + return + var/message = hearing_args[HEARING_MESSAGE] if(findtext(message, codeword)) - message = replacetext(message, codeword, "[codeword]") + hearing_args[HEARING_MESSAGE] = replacetext(message, codeword, "[codeword]") addtimer(CALLBACK(src, /datum/brain_trauma/severe/split_personality.proc/switch_personalities), 10) - return message /datum/brain_trauma/severe/split_personality/brainwashing/handle_speech(datum/source, list/speech_args) if(findtext(speech_args[SPEECH_MESSAGE], codeword)) diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm index ef52a8fc..11a260d2 100644 --- a/code/datums/components/nanites.dm +++ b/code/datums/components/nanites.dm @@ -53,7 +53,6 @@ RegisterSignal(parent, COMSIG_MOB_ALLOWED, .proc/check_access) RegisterSignal(parent, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_shock) RegisterSignal(parent, COMSIG_LIVING_MINOR_SHOCK, .proc/on_minor_shock) - RegisterSignal(parent, COMSIG_MOVABLE_HEAR, .proc/on_hear) RegisterSignal(parent, COMSIG_SPECIES_GAIN, .proc/check_viable_biotype) RegisterSignal(parent, COMSIG_NANITE_SIGNAL, .proc/receive_signal) @@ -191,11 +190,6 @@ var/datum/nanite_program/NP = X NP.on_death(gibbed) -/datum/component/nanites/proc/on_hear(datum/source, message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) - for(var/X in programs) - var/datum/nanite_program/NP = X - NP.on_hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mode) - /datum/component/nanites/proc/receive_signal(datum/source, code, source = "an unidentified source") for(var/X in programs) var/datum/nanite_program/NP = X diff --git a/code/datums/components/stationloving.dm b/code/datums/components/stationloving.dm index bbcb0d88..91928656 100644 --- a/code/datums/components/stationloving.dm +++ b/code/datums/components/stationloving.dm @@ -8,6 +8,7 @@ if(!ismovableatom(parent)) return COMPONENT_INCOMPATIBLE RegisterSignal(parent, list(COMSIG_MOVABLE_Z_CHANGED), .proc/check_in_bounds) + RegisterSignal(parent, list(COMSIG_MOVABLE_SECLUDED_LOCATION), .proc/relocate) RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED), .proc/check_deletion) RegisterSignal(parent, list(COMSIG_ITEM_IMBUE_SOUL), .proc/check_soul_imbue) src.inform_admins = inform_admins @@ -32,6 +33,7 @@ var/atom/movable/AM = parent AM.forceMove(targetturf) + to_chat(get(parent, /mob), "You can't help but feel that you just lost something back there...") // move the disc, so ghosts remain orbiting it even if it's "destroyed" return targetturf @@ -40,7 +42,6 @@ return else var/turf/currentturf = get_turf(src) - to_chat(get(parent, /mob), "You can't help but feel that you just lost something back there...") var/turf/targetturf = relocate() log_game("[parent] has been moved out of bounds in [loc_name(currentturf)]. Moving it to [loc_name(targetturf)].") if(inform_admins) diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 20bba021..a9ba87b1 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -635,8 +635,7 @@ datum/status_effect/pacify tick_interval = 10 examine_text = "SUBJECTPRONOUN seems slow and unfocused." var/stun = TRUE - var/triggered = FALSE - alert_type = null + alert_type = /obj/screen/alert/status_effect/trance /obj/screen/alert/status_effect/trance name = "Trance" @@ -644,17 +643,6 @@ datum/status_effect/pacify icon_state = "high" /datum/status_effect/trance/tick() - if(HAS_TRAIT(owner, "hypnotherapy")) - if(triggered == TRUE) - UnregisterSignal(owner, COMSIG_MOVABLE_HEAR) - RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/hypnotize) - ADD_TRAIT(owner, TRAIT_MUTE, "trance") - if(!owner.has_quirk(/datum/quirk/monochromatic)) - owner.add_client_colour(/datum/client_colour/monochrome) - to_chat(owner, "[pick("You feel your thoughts slow down...", "You suddenly feel extremely dizzy...", "You feel like you're in the middle of a dream...","You feel incredibly relaxed...")]") - triggered = FALSE - else - return if(stun) owner.Stun(60, TRUE, TRUE) owner.dizziness = 20 @@ -662,48 +650,33 @@ datum/status_effect/pacify /datum/status_effect/trance/on_apply() if(!iscarbon(owner)) return FALSE - if(HAS_TRAIT(owner, "hypnotherapy")) - RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/listen) - return TRUE - alert_type = /obj/screen/alert/status_effect/trance RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/hypnotize) ADD_TRAIT(owner, TRAIT_MUTE, "trance") - if(!owner.has_quirk(/datum/quirk/monochromatic)) - owner.add_client_colour(/datum/client_colour/monochrome) + owner.add_client_colour(/datum/client_colour/monochrome/trance) owner.visible_message("[stun ? "[owner] stands still as [owner.p_their()] eyes seem to focus on a distant point." : ""]", \ "[pick("You feel your thoughts slow down...", "You suddenly feel extremely dizzy...", "You feel like you're in the middle of a dream...","You feel incredibly relaxed...")]") return TRUE -/datum/status_effect/trance/on_creation(mob/living/new_owner, _duration, _stun = TRUE, source_quirk = FALSE)//hypnoquirk makes no visible message, prevents self antag messages, and places phrase below objectives. +/datum/status_effect/trance/on_creation(mob/living/new_owner, _duration, _stun = TRUE) duration = _duration stun = _stun - if(source_quirk == FALSE && HAS_TRAIT(owner, "hypnotherapy")) - REMOVE_TRAIT(owner, "hypnotherapy", ROUNDSTART_TRAIT) return ..() /datum/status_effect/trance/on_remove() UnregisterSignal(owner, COMSIG_MOVABLE_HEAR) REMOVE_TRAIT(owner, TRAIT_MUTE, "trance") owner.dizziness = 0 - if(!owner.has_quirk(/datum/quirk/monochromatic)) - owner.remove_client_colour(/datum/client_colour/monochrome) + owner.remove_client_colour(/datum/client_colour/monochrome/trance) to_chat(owner, "You snap out of your trance!") -/datum/status_effect/trance/proc/listen(datum/source, message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) - to_chat(owner, "[speaker] accidentally sets off your implanted trigger, sending you into a hypnotic daze!") - triggered = TRUE - -/datum/status_effect/trance/proc/hypnotize(datum/source, message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) +/datum/status_effect/trance/proc/hypnotize(datum/source, list/hearing_args) if(!owner.can_hear()) return - if(speaker == owner) + if(hearing_args[HEARING_SPEAKER] == owner) return var/mob/living/carbon/C = owner C.cure_trauma_type(/datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY) //clear previous hypnosis - if(HAS_TRAIT(C, "hypnotherapy")) - addtimer(CALLBACK(C, /mob/living/carbon.proc/gain_trauma, /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, raw_message, TRUE), 10) - else - addtimer(CALLBACK(C, /mob/living/carbon.proc/gain_trauma, /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, raw_message), 10) + addtimer(CALLBACK(C, /mob/living/carbon.proc/gain_trauma, /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, hearing_args[HEARING_RAW_MESSAGE]), 10) addtimer(CALLBACK(C, /mob/living.proc/Stun, 60, TRUE, TRUE), 15) //Take some time to think about it qdel(src) @@ -729,3 +702,59 @@ datum/status_effect/pacify name = "Genetic Breakdown" desc = "I don't feel so good. Your body can't handle the mutations! You have one minute to remove your mutations, or you will be met with a horrible fate." icon_state = "dna_melt" + +/datum/status_effect/spasms + id = "spasms" + status_type = STATUS_EFFECT_MULTIPLE + alert_type = null + +/datum/status_effect/spasms/tick() + if(prob(15)) + switch(rand(1,5)) + if(1) + if((!owner.lying && !owner.buckled) && isturf(owner.loc)) + to_chat(owner, "Your leg spasms!") + step(owner, pick(GLOB.cardinals)) + if(2) + if(owner.incapacitated()) + return + var/obj/item/I = owner.get_active_held_item() + if(I) + to_chat(owner, "Your fingers spasm!") + owner.log_message("used [I] due to a Muscle Spasm", LOG_ATTACK) + I.attack_self(owner) + if(3) + var/prev_intent = owner.a_intent + owner.a_intent = INTENT_HARM + + var/range = 1 + if(istype(owner.get_active_held_item(), /obj/item/gun)) //get targets to shoot at + range = 7 + + var/list/mob/living/targets = list() + for(var/mob/M in oview(owner, range)) + if(isliving(M)) + targets += M + if(LAZYLEN(targets)) + to_chat(owner, "Your arm spasms!") + owner.log_message(" attacked someone due to a Muscle Spasm", LOG_ATTACK) //the following attack will log itself + owner.ClickOn(pick(targets)) + owner.a_intent = prev_intent + if(4) + var/prev_intent = owner.a_intent + owner.a_intent = INTENT_HARM + to_chat(owner, "Your arm spasms!") + owner.log_message("attacked [owner.p_them()]self to a Muscle Spasm", LOG_ATTACK) + owner.ClickOn(owner) + owner.a_intent = prev_intent + if(5) + if(owner.incapacitated()) + return + var/obj/item/I = owner.get_active_held_item() + var/list/turf/targets = list() + for(var/turf/T in oview(owner, 3)) + targets += T + if(LAZYLEN(targets) && I) + to_chat(owner, "Your arm spasms!") + owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK) + owner.throw_item(pick(targets)) \ No newline at end of file diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index 655863e0..cb2b4174 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -69,3 +69,17 @@ /datum/status_effect/in_love/tick() if(date) new /obj/effect/temp_visual/love_heart/invisible(get_turf(date.loc), owner) + +/datum/status_effect/throat_soothed + id = "throat_soothed" + duration = 60 SECONDS + status_type = STATUS_EFFECT_REFRESH + alert_type = null + +/datum/status_effect/throat_soothed/on_apply() + . = ..() + ADD_TRAIT(owner, TRAIT_SOOTHED_THROAT, "[STATUS_EFFECT_TRAIT]_[id]") + +/datum/status_effect/throat_soothed/on_remove() + . = ..() + REMOVE_TRAIT(owner, TRAIT_SOOTHED_THROAT, "[STATUS_EFFECT_TRAIT]_[id]") \ No newline at end of file diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index bc1cfba1..0d84aab7 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -64,6 +64,12 @@ owner = null qdel(src) +/datum/status_effect/proc/refresh() + var/original_duration = initial(duration) + if(original_duration == -1) + return + duration = world.time + original_duration + //clickdelay/nextmove modifiers! /datum/status_effect/proc/nextmove_modifier() return 1 @@ -92,6 +98,9 @@ if(S.id == initial(S1.id) && S.status_type) if(S.status_type == STATUS_EFFECT_REPLACE) S.be_replaced() + else if(S.status_type == STATUS_EFFECT_REFRESH) + S.refresh() + return else return var/list/arguments = args.Copy() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 6dbf96e3..00614f91 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,870 +1,870 @@ -/atom - layer = TURF_LAYER - plane = GAME_PLANE - var/level = 2 - var/article // If non-null, overrides a/an/some in all cases - - var/flags_1 = NONE - var/interaction_flags_atom = NONE - var/datum/reagents/reagents = null - - //This atom's HUD (med/sec, etc) images. Associative list. - var/list/image/hud_list = null - //HUD images that this atom can provide. - var/list/hud_possible - - /// Last name used to calculate a color for the chatmessage overlays - var/chat_color_name - /// Last color calculated for the the chatmessage overlays - var/chat_color - /// A luminescence-shifted value of the last color calculated for chatmessage overlays - var/chat_color_darkened - - //Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 - - var/list/atom_colours //used to store the different colors on an atom - //its inherent color, the colored paint applied on it, special color effect etc... - - var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/remove_overlays // a very temporary list of overlays to remove - var/list/add_overlays // a very temporary list of overlays to add - - var/list/managed_vis_overlays //vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays - var/list/managed_overlays - var/datum/proximity_monitor/proximity_monitor - var/buckle_message_cooldown = 0 - var/fingerprintslast - - var/list/filter_data //For handling persistent filters - - var/datum/component/orbiter/orbiters - - var/rad_flags = NONE // Will move to flags_1 when i can be arsed to - var/rad_insulation = RAD_NO_INSULATION - - var/icon/blood_splatter_icon - var/icon/cum_splatter_icon - var/list/fingerprints - var/list/fingerprintshidden - var/list/blood_DNA - var/list/suit_fibers - -/atom/New(loc, ...) - //atom creation method that preloads variables at creation - if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() - GLOB._preloader.load(src) - - if(datum_flags & DF_USE_TAG) - GenerateTag() - - var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD - if(SSatoms.InitAtom(src, args)) - //we were deleted - return - -//Called after New if the map is being loaded. mapload = TRUE -//Called from base of New if the map is not being loaded. mapload = FALSE -//This base must be called or derivatives must set initialized to TRUE -//must not sleep -//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm - -//Note: the following functions don't call the base for optimization and must copypasta: -// /turf/Initialize -// /turf/open/space/Initialize - -/atom/proc/Initialize(mapload, ...) - if(flags_1 & INITIALIZED_1) - stack_trace("Warning: [src]([type]) initialized multiple times!") - flags_1 |= INITIALIZED_1 - - //atom color stuff - if(color) - add_atom_colour(color, FIXED_COLOUR_PRIORITY) - - if (light_power && light_range) - update_light() - - if (opacity && isturf(loc)) - var/turf/T = loc - T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways. - - if (canSmoothWith) - canSmoothWith = typelist("canSmoothWith", canSmoothWith) - - ComponentInitialize() - - return INITIALIZE_HINT_NORMAL - -//called if Initialize returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -// Put your AddComponent() calls here -/atom/proc/ComponentInitialize() - return - -/atom/Destroy() - if(alternate_appearances) - for(var/K in alternate_appearances) - var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K] - AA.remove_from_hud(src) - - if(reagents) - qdel(reagents) - - LAZYCLEARLIST(overlays) - LAZYCLEARLIST(priority_overlays) - - QDEL_NULL(light) - - return ..() - -/atom/proc/handle_ricochet(obj/item/projectile/P) - return - -/atom/proc/CanPass(atom/movable/mover, turf/target) - return !density - -/atom/proc/onCentCom() - var/turf/T = get_turf(src) - if(!T) - return FALSE - - if(is_reserved_level(T.z)) - for(var/A in SSshuttle.mobile) - var/obj/docking_port/mobile/M = A - if(M.launch_status == ENDGAME_TRANSIT) - for(var/place in M.shuttle_areas) - var/area/shuttle/shuttle_area = place - if(T in shuttle_area) - return TRUE - - if(!is_centcom_level(T.z))//if not, don't bother - return FALSE - - //Check for centcom itself - if(istype(T.loc, /area/centcom)) - return TRUE - - //Check for centcom shuttles - for(var/A in SSshuttle.mobile) - var/obj/docking_port/mobile/M = A - if(M.launch_status == ENDGAME_LAUNCHED) - for(var/place in M.shuttle_areas) - var/area/shuttle/shuttle_area = place - if(T in shuttle_area) - return TRUE - -/atom/proc/onSyndieBase() - var/turf/T = get_turf(src) - if(!T) - return FALSE - - if(!is_centcom_level(T.z))//if not, don't bother - return FALSE - - if(istype(T.loc, /area/shuttle/syndicate) || istype(T.loc, /area/syndicate_mothership) || istype(T.loc, /area/shuttle/assault_pod)) - return TRUE - - return FALSE - -/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) - if(does_attack_animation) - user.changeNext_move(CLICK_CD_MELEE) - log_combat(user, src, "punched", "hulk powers") - user.do_attack_animation(src, ATTACK_EFFECT_SMASH) - -/atom/proc/CheckParts(list/parts_list) - for(var/A in parts_list) - if(istype(A, /datum/reagent)) - if(!reagents) - reagents = new() - reagents.reagent_list.Add(A) - reagents.conditional_update() - else if(ismovableatom(A)) - var/atom/movable/M = A - if(isliving(M.loc)) - var/mob/living/L = M.loc - L.transferItemToLoc(M, src) - else - M.forceMove(src) - -//common name -/atom/proc/update_multiz(prune_on_fail = FALSE) - return FALSE - -/atom/proc/assume_air(datum/gas_mixture/giver) - qdel(giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -/atom/proc/check_eye(mob/user) - return - -/atom/proc/Bumped(atom/movable/AM) - set waitfor = FALSE - -// Convenience procs to see if a container is open for chemistry handling -/atom/proc/is_open_container() - return is_refillable() && is_drainable() - -/atom/proc/is_injectable(allowmobs = TRUE) - return reagents && (reagents.reagents_holder_flags & (INJECTABLE | REFILLABLE)) - -/atom/proc/is_drawable(allowmobs = TRUE) - return reagents && (reagents.reagents_holder_flags & (DRAWABLE | DRAINABLE)) - -/atom/proc/is_refillable() - return reagents && (reagents.reagents_holder_flags & REFILLABLE) - -/atom/proc/is_drainable() - return reagents && (reagents.reagents_holder_flags & DRAINABLE) - - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/CheckExit() - return TRUE - -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(severity) - var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) - if(!(protection & EMP_PROTECT_WIRES) && istype(wires)) - wires.emp_pulse() - return protection // Pass the protection value collected here upwards - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) - . = P.on_hit(src, 0, def_zone) - -//used on altdisarm() for special interactions between the shoved victim (target) and the src, with user being the one shoving the target on it. -// IMPORTANT: if you wish to add a new own shove_act() to a certain object, remember to add SHOVABLE_ONTO to its obj_flags bitfied var first. -/atom/proc/shove_act(mob/living/target, mob/living/user) - return FALSE - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return TRUE - else if(src in container) - return TRUE - return FALSE - -/atom/proc/update_icon_state() - -/atom/proc/update_overlaysb() - SHOULD_CALL_PARENT(1) - . = list() - SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .) - -/atom/proc/update_iconb() - var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON) - - if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE)) - update_icon_state() - - if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS)) - var/list/new_overlays = update_overlaysb() - if(managed_overlays) - cut_overlay(managed_overlays) - managed_overlays = null - if(length(new_overlays)) - managed_overlays = new_overlays - add_overlay(new_overlays) - -/atom/proc/get_examine_name(mob/user) - . = "\a [src]" - var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]") - if(article) - . = "[article] [src]" - override[EXAMINE_POSITION_ARTICLE] = article - - var/should_override = FALSE - - if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED) - should_override = TRUE - - - if(blood_DNA && !istype(src, /obj/effect/decal)) - override[EXAMINE_POSITION_BEFORE] = " blood-stained " - should_override = TRUE - - if(should_override) - . = override.Join("") - -///Generate the full examine string of this atom (including icon for goonchat) -/atom/proc/get_examine_string(mob/user, thats = FALSE) - return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" - -/atom/proc/examine(mob/user) - . = list("[get_examine_string(user, TRUE)].") - - if(desc) - . += desc - - if(reagents) - if(reagents.reagents_holder_flags & TRANSPARENT) - . += "It contains:" - if(length(reagents.reagent_list)) - if(user.can_see_reagents()) //Show each individual reagent - for(var/datum/reagent/R in reagents.reagent_list) - . += "[R.volume] units of [R.name]" - else //Otherwise, just show the total volume - var/total_volume = 0 - for(var/datum/reagent/R in reagents.reagent_list) - total_volume += R.volume - . += "[total_volume] units of various reagents" - else - . += "Nothing." - else if(reagents.reagents_holder_flags & AMOUNT_VISIBLE) - if(reagents.total_volume) - . += "It has [reagents.total_volume] unit\s left." - else - . += "It's empty." - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) - -/atom/proc/relaymove(mob/user) - if(buckle_message_cooldown <= world.time) - buckle_message_cooldown = world.time + 50 - to_chat(user, "You can't move while buckled to [src]!") - return - -/atom/proc/prevent_content_explosion() - return FALSE - -/atom/proc/contents_explosion(severity, target) - return //For handling the effects of explosions on contents that would not normally be effected - -/atom/proc/ex_act(severity, target) - set waitfor = FALSE - contents_explosion(severity, target) - SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target) - -/atom/proc/blob_act(obj/structure/blob/B) - SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) - return - -/atom/proc/fire_act(exposed_temperature, exposed_volume) - SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) - return - -/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) - -/atom/proc/hitby_react(atom/movable/AM) - if(AM && isturf(AM.loc)) - step(AM, turn(AM.dir, 180)) - -/atom/proc/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube) - return - -//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list -/mob/living/proc/get_blood_dna_list() - var/blood_id = get_blood_id() - if(!(blood_id in GLOB.blood_reagent_types)) - return - return list("ANIMAL DNA" = "Y-") - -/mob/living/carbon/get_blood_dna_list() - var/blood_id = get_blood_id() - if(!(blood_id in GLOB.blood_reagent_types)) - return - var/list/blood_dna = list() - if(dna) - blood_dna[dna.unique_enzymes] = dna.blood_type - else - blood_dna["UNKNOWN DNA"] = "X*" - return blood_dna - -/mob/living/carbon/alien/get_blood_dna_list() - return list("UNKNOWN DNA" = "X*") - -//to add a mob's dna info into an object's blood_DNA list. -/atom/proc/transfer_mob_blood_dna(mob/living/L) - // Returns 0 if we have that blood already - var/new_blood_dna = L.get_blood_dna_list() - if(!new_blood_dna) - return FALSE - LAZYINITLIST(blood_DNA) //if our list of DNA doesn't exist yet, initialise it. - var/old_length = blood_DNA.len - blood_DNA |= new_blood_dna - if(blood_DNA.len == old_length) - return FALSE - return TRUE - -//to add blood dna info to the object's blood_DNA list -/atom/proc/transfer_blood_dna(list/blood_dna, list/datum/disease/diseases) - LAZYINITLIST(blood_DNA) - var/old_length = blood_DNA.len - blood_DNA |= blood_dna - if(blood_DNA.len > old_length) - return TRUE - //some new blood DNA was added - -//to add blood from a mob onto something, and transfer their dna info -/atom/proc/add_mob_blood(mob/living/M) - var/list/blood_dna = M.get_blood_dna_list() - if(!blood_dna) - return FALSE - return add_blood_DNA(blood_dna, M.diseases) - -//to add blood onto something, with blood dna info to include. -/atom/proc/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - return FALSE - -/obj/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - return transfer_blood_dna(blood_dna, diseases) - -/obj/item/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - . = ..() - if(!.) - return - add_blood_overlay() - -/obj/item/proc/add_blood_overlay() - if(!blood_DNA.len) - return - if(initial(icon) && initial(icon_state)) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon.Blend(blood_DNA_to_color(), ICON_MULTIPLY) - add_overlay(blood_splatter_icon) - -/obj/item/clothing/gloves/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - . = ..() - transfer_blood = rand(2, 4) - -/turf/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src - if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) - B.transfer_blood_dna(blood_dna, diseases) //give blood info to the blood decal. - return TRUE //we bloodied the floor - -/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) - if(head) - head.add_blood_DNA(blood_dna, diseases) - update_inv_head() - else if(wear_mask) - wear_mask.add_blood_DNA(blood_dna, diseases) - update_inv_wear_mask() - if(wear_neck) - wear_neck.add_blood_DNA(blood_dna, diseases) - update_inv_neck() - if(wear_suit) - wear_suit.add_blood_DNA(blood_dna, diseases) - update_inv_wear_suit() - else if(w_uniform) - w_uniform.add_blood_DNA(blood_dna, diseases) - update_inv_w_uniform() - if(gloves) - var/obj/item/clothing/gloves/G = gloves - G.add_blood_DNA(blood_dna, diseases) - else - transfer_blood_dna(blood_dna, diseases) - bloody_hands = rand(2, 4) - update_inv_gloves() //handles bloody hands overlays and updating - return TRUE - -/atom/proc/blood_DNA_to_color() - var/list/colors = list()//first we make a list of all bloodtypes present - for(var/bloop in blood_DNA) - if(colors[blood_DNA[bloop]]) - colors[blood_DNA[bloop]]++ - else - colors[blood_DNA[bloop]] = 1 - - var/final_rgb = BLOOD_COLOR_HUMAN //a default so we don't have white blood graphics if something messed up - - if(colors.len) - var/sum = 0 //this is all shitcode, but it works; trust me - final_rgb = bloodtype_to_color(colors[1]) - sum = colors[colors[1]] - if(colors.len > 1) - var/i = 2 - while(i <= colors.len) - var/tmp = colors[colors[i]] - final_rgb = BlendRGB(final_rgb, bloodtype_to_color(colors[i]), tmp/(tmp+sum)) - sum += tmp - i++ - - return final_rgb - -/atom/proc/clean_blood() - . = blood_DNA? TRUE : FALSE - blood_DNA = null - -/atom/proc/wash_cream() - return TRUE - -/atom/proc/isinspace() - if(isspaceturf(get_turf(src))) - return TRUE - else - return FALSE - -/atom/proc/handle_fall() - return - -/atom/proc/singularity_act() - return - -/atom/proc/singularity_pull(obj/singularity/S, current_size) - SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) - -/atom/proc/acid_act(acidpwr, acid_volume) - SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) - -/atom/proc/emag_act() - SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) - -/atom/proc/rad_act(strength) - SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength) - -/atom/proc/narsie_act() - SEND_SIGNAL(src, COMSIG_ATOM_NARSIE_ACT) - -/atom/proc/ratvar_act() - SEND_SIGNAL(src, COMSIG_ATOM_RATVAR_ACT) - -/atom/proc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) - return FALSE - -/atom/proc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) - SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode) - return FALSE - -/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user) - if(GetComponent(/datum/component/storage)) - return component_storage_contents_dump_act(src_object, user) - return FALSE - -/atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user) - var/list/things = src_object.contents() - var/datum/progressbar/progress = new(user, things.len, src) - var/datum/component/storage/STR = GetComponent(/datum/component/storage) - while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress))) - stoplag(1) - qdel(progress) - to_chat(user, "You dump as much of [src_object.parent]'s contents into [STR.insert_preposition]to [src] as you can.") - STR.orient2hud(user) - src_object.orient2hud(user) - if(user.active_storage) //refresh the HUD to show the transfered contents - user.active_storage.close(user) - user.active_storage.show_to(user) - return TRUE - -/atom/proc/get_dumping_location(obj/item/storage/source,mob/user) - return null - -//This proc is called on the location of an atom when the atom is Destroy()'d -/atom/proc/handle_atom_del(atom/A) - SEND_SIGNAL(src, COMSIG_ATOM_CONTENTS_DEL, A) - -//called when the turf the atom resides on is ChangeTurfed -/atom/proc/HandleTurfChange(turf/T) - for(var/a in src) - var/atom/A = a - A.HandleTurfChange(T) - -//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it) -/atom/proc/get_remote_view_fullscreens(mob/user) - return - -//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) -/atom/proc/update_remote_sight(mob/living/user) - return - - -//Hook for running code when a dir change occurs -/atom/proc/setDir(newdir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) - dir = newdir - -/atom/proc/mech_melee_attack(obj/mecha/M) - return - -//If a mob logouts/logins in side of an object you can use this proc -/atom/proc/on_log(login) - if(loc) - loc.on_log(login) - - -/* - Atom Colour Priority System - A System that gives finer control over which atom colour to colour the atom with. - The "highest priority" one is always displayed as opposed to the default of - "whichever was set last is displayed" -*/ - - -/* - Adds an instance of colour_type to the atom's atom_colours list -*/ -/atom/proc/add_atom_colour(coloration, colour_priority) - if(!atom_colours || !atom_colours.len) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(!coloration) - return - if(colour_priority > atom_colours.len) - return - atom_colours[colour_priority] = coloration - update_atom_colour() - - -/* - Removes an instance of colour_type from the atom's atom_colours list -*/ -/atom/proc/remove_atom_colour(colour_priority, coloration) - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(colour_priority > atom_colours.len) - return - if(coloration && atom_colours[colour_priority] != coloration) - return //if we don't have the expected color (for a specific priority) to remove, do nothing - atom_colours[colour_priority] = null - update_atom_colour() - - -/* - Resets the atom's color to null, and then sets it to the highest priority - colour available -*/ -/atom/proc/update_atom_colour() - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - color = null - for(var/C in atom_colours) - if(islist(C)) - var/list/L = C - if(L.len) - color = L - return - else if(C) - color = C - return - -/atom/vv_edit_var(var_name, var_value) - if(!GLOB.Debug2) - flags_1 |= ADMIN_SPAWNED_1 - . = ..() - switch(var_name) - if("color") - add_atom_colour(color, ADMIN_COLOUR_PRIORITY) - -/atom/vv_get_dropdown() - . = ..() - . += "---" - var/turf/curturf = get_turf(src) - if (curturf) - .["Jump to"] = "?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" - .["Modify Transform"] = "?_src_=vars;[HrefToken()];modtransform=[REF(src)]" - .["Add reagent"] = "?_src_=vars;[HrefToken()];addreagent=[REF(src)]" - .["Trigger EM pulse"] = "?_src_=vars;[HrefToken()];emp=[REF(src)]" - .["Trigger explosion"] = "?_src_=vars;[HrefToken()];explode=[REF(src)]" - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : get_turf(L) - -/atom/Entered(atom/movable/AM, atom/oldLoc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) - -/atom/Exit(atom/movable/AM, atom/newLoc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/newLoc) - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) - -/atom/proc/return_temperature() - return - -// Tool behavior procedure. Redirects to tool-specific procs by default. -// You can override it to catch all tool interactions, for use in complex deconstruction procs. -// Just don't forget to return ..() in the end. -/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) - switch(tool_type) - if(TOOL_CROWBAR) - return crowbar_act(user, I) - if(TOOL_MULTITOOL) - return multitool_act(user, I) - if(TOOL_SCREWDRIVER) - return screwdriver_act(user, I) - if(TOOL_WRENCH) - return wrench_act(user, I) - if(TOOL_WIRECUTTER) - return wirecutter_act(user, I) - if(TOOL_WELDER) - return welder_act(user, I) - if(TOOL_ANALYZER) - return analyzer_act(user, I) - -// Tool-specific behavior procs. To be overridden in subtypes. -/atom/proc/crowbar_act(mob/living/user, obj/item/I) - return - -/atom/proc/multitool_act(mob/living/user, obj/item/I) - return - -/atom/proc/screwdriver_act(mob/living/user, obj/item/I) - SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I) - -/atom/proc/wrench_act(mob/living/user, obj/item/I) - return - -/atom/proc/wirecutter_act(mob/living/user, obj/item/I) - return - -/atom/proc/welder_act(mob/living/user, obj/item/I) - return - -/atom/proc/analyzer_act(mob/living/user, obj/item/I) - return - -/atom/proc/GenerateTag() - return - -// Generic logging helper -/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) - if(!log_globally) - return - - var/log_text = "[key_name(src)] [message] [loc_name(src)]" - switch(message_type) - if(LOG_ATTACK) - log_attack(log_text) - if(LOG_SAY) - log_say(log_text) - if(LOG_WHISPER) - log_whisper(log_text) - if(LOG_EMOTE) - log_emote(log_text) - if(LOG_DSAY) - log_dsay(log_text) - if(LOG_PDA) - log_pda(log_text) - if(LOG_CHAT) - log_chat(log_text) - if(LOG_COMMENT) - log_comment(log_text) - if(LOG_TELECOMMS) - log_telecomms(log_text) - if(LOG_OOC) - log_ooc(log_text) - if(LOG_ADMIN) - log_admin(log_text) - if(LOG_ADMIN_PRIVATE) - log_admin_private(log_text) - if(LOG_ASAY) - log_adminsay(log_text) - if(LOG_OWNERSHIP) - log_game(log_text) - if(LOG_GAME) - log_game(log_text) - else - stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") - log_game(log_text) - -// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) -/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null) - var/prefix = tag ? "([tag]) " : "" - var/suffix = forced_by ? " FORCED by [forced_by]" : "" - log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally) - -// Helper for logging of messages with only one sender and receiver -/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) - if(!tag) - stack_trace("Unspecified tag for private message") - tag = "UNKNOWN" - - source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") - if(source != target) - target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) - -/* -Proc for attack log creation, because really why not -1 argument is the actor performing the action -2 argument is the target of the action -3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) -4 is a tool with which the action was made (usually an item) -5 is any additional text, which will be appended to the rest of the log line -*/ - -/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) - var/ssource = key_name(user) - var/starget = key_name(target) - - var/mob/living/living_target = target - var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" - - var/sobject = "" - if(object) - sobject = " with [key_name(object)]" - var/saddition = "" - if(addition) - saddition = " [addition]" - - var/postfix = "[sobject][saddition][hp]" - - var/message = "has [what_done] [starget][postfix]" - user.log_message(message, LOG_ATTACK, color="red") - - if(user != target) - var/reverse_message = "has been [what_done] by [ssource][postfix]" - target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) - -// Filter stuff -/atom/movable/proc/add_filter(name,priority,list/params) - if(!filter_data) - filter_data = list() - var/list/p = params.Copy() - p["priority"] = priority - filter_data[name] = p - update_filters() - -/atom/movable/proc/update_filters() - filters = null - sortTim(filter_data,associative = TRUE) - for(var/f in filter_data) - var/list/data = filter_data[f] - var/list/arguments = data.Copy() - arguments -= "priority" - filters += filter(arglist(arguments)) - -/atom/movable/proc/get_filter(name) - if(filter_data && filter_data[name]) - return filters[filter_data.Find(name)] - -/atom/movable/proc/remove_filter(name) - if(filter_data[name]) - filter_data -= name - update_filters() - return TRUE - -/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) - . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) +/atom + layer = TURF_LAYER + plane = GAME_PLANE + var/level = 2 + var/article // If non-null, overrides a/an/some in all cases + + var/flags_1 = NONE + var/interaction_flags_atom = NONE + var/datum/reagents/reagents = null + + //This atom's HUD (med/sec, etc) images. Associative list. + var/list/image/hud_list = null + //HUD images that this atom can provide. + var/list/hud_possible + + /// Last name used to calculate a color for the chatmessage overlays + var/chat_color_name + /// Last color calculated for the the chatmessage overlays + var/chat_color + /// A luminescence-shifted value of the last color calculated for chatmessage overlays + var/chat_color_darkened + + //Value used to increment ex_act() if reactionary_explosions is on + var/explosion_block = 0 + + var/list/atom_colours //used to store the different colors on an atom + //its inherent color, the colored paint applied on it, special color effect etc... + + var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add + + var/list/managed_vis_overlays //vis overlays managed by SSvis_overlays to automaticaly turn them like other overlays + var/list/managed_overlays + var/datum/proximity_monitor/proximity_monitor + var/buckle_message_cooldown = 0 + var/fingerprintslast + + var/list/filter_data //For handling persistent filters + + var/datum/component/orbiter/orbiters + + var/rad_flags = NONE // Will move to flags_1 when i can be arsed to + var/rad_insulation = RAD_NO_INSULATION + + var/icon/blood_splatter_icon + var/icon/cum_splatter_icon + var/list/fingerprints + var/list/fingerprintshidden + var/list/blood_DNA + var/list/suit_fibers + +/atom/New(loc, ...) + //atom creation method that preloads variables at creation + if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() + GLOB._preloader.load(src) + + if(datum_flags & DF_USE_TAG) + GenerateTag() + + var/do_initialize = SSatoms.initialized + if(do_initialize != INITIALIZATION_INSSATOMS) + args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(SSatoms.InitAtom(src, args)) + //we were deleted + return + +//Called after New if the map is being loaded. mapload = TRUE +//Called from base of New if the map is not being loaded. mapload = FALSE +//This base must be called or derivatives must set initialized to TRUE +//must not sleep +//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm + +//Note: the following functions don't call the base for optimization and must copypasta: +// /turf/Initialize +// /turf/open/space/Initialize + +/atom/proc/Initialize(mapload, ...) + if(flags_1 & INITIALIZED_1) + stack_trace("Warning: [src]([type]) initialized multiple times!") + flags_1 |= INITIALIZED_1 + + //atom color stuff + if(color) + add_atom_colour(color, FIXED_COLOUR_PRIORITY) + + if (light_power && light_range) + update_light() + + if (opacity && isturf(loc)) + var/turf/T = loc + T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways. + + if (canSmoothWith) + canSmoothWith = typelist("canSmoothWith", canSmoothWith) + + ComponentInitialize() + + return INITIALIZE_HINT_NORMAL + +//called if Initialize returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +// Put your AddComponent() calls here +/atom/proc/ComponentInitialize() + return + +/atom/Destroy() + if(alternate_appearances) + for(var/K in alternate_appearances) + var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K] + AA.remove_from_hud(src) + + if(reagents) + qdel(reagents) + + LAZYCLEARLIST(overlays) + LAZYCLEARLIST(priority_overlays) + + QDEL_NULL(light) + + return ..() + +/atom/proc/handle_ricochet(obj/item/projectile/P) + return + +/atom/proc/CanPass(atom/movable/mover, turf/target) + return !density + +/atom/proc/onCentCom() + var/turf/T = get_turf(src) + if(!T) + return FALSE + + if(is_reserved_level(T.z)) + for(var/A in SSshuttle.mobile) + var/obj/docking_port/mobile/M = A + if(M.launch_status == ENDGAME_TRANSIT) + for(var/place in M.shuttle_areas) + var/area/shuttle/shuttle_area = place + if(T in shuttle_area) + return TRUE + + if(!is_centcom_level(T.z))//if not, don't bother + return FALSE + + //Check for centcom itself + if(istype(T.loc, /area/centcom)) + return TRUE + + //Check for centcom shuttles + for(var/A in SSshuttle.mobile) + var/obj/docking_port/mobile/M = A + if(M.launch_status == ENDGAME_LAUNCHED) + for(var/place in M.shuttle_areas) + var/area/shuttle/shuttle_area = place + if(T in shuttle_area) + return TRUE + +/atom/proc/onSyndieBase() + var/turf/T = get_turf(src) + if(!T) + return FALSE + + if(!is_centcom_level(T.z))//if not, don't bother + return FALSE + + if(istype(T.loc, /area/shuttle/syndicate) || istype(T.loc, /area/syndicate_mothership) || istype(T.loc, /area/shuttle/assault_pod)) + return TRUE + + return FALSE + +/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) + if(does_attack_animation) + user.changeNext_move(CLICK_CD_MELEE) + log_combat(user, src, "punched", "hulk powers") + user.do_attack_animation(src, ATTACK_EFFECT_SMASH) + +/atom/proc/CheckParts(list/parts_list) + for(var/A in parts_list) + if(istype(A, /datum/reagent)) + if(!reagents) + reagents = new() + reagents.reagent_list.Add(A) + reagents.conditional_update() + else if(ismovableatom(A)) + var/atom/movable/M = A + if(isliving(M.loc)) + var/mob/living/L = M.loc + L.transferItemToLoc(M, src) + else + M.forceMove(src) + +//common name +/atom/proc/update_multiz(prune_on_fail = FALSE) + return FALSE + +/atom/proc/assume_air(datum/gas_mixture/giver) + qdel(giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +/atom/proc/check_eye(mob/user) + return + +/atom/proc/Bumped(atom/movable/AM) + set waitfor = FALSE + +// Convenience procs to see if a container is open for chemistry handling +/atom/proc/is_open_container() + return is_refillable() && is_drainable() + +/atom/proc/is_injectable(allowmobs = TRUE) + return reagents && (reagents.reagents_holder_flags & (INJECTABLE | REFILLABLE)) + +/atom/proc/is_drawable(allowmobs = TRUE) + return reagents && (reagents.reagents_holder_flags & (DRAWABLE | DRAINABLE)) + +/atom/proc/is_refillable() + return reagents && (reagents.reagents_holder_flags & REFILLABLE) + +/atom/proc/is_drainable() + return reagents && (reagents.reagents_holder_flags & DRAINABLE) + + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/CheckExit() + return TRUE + +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(severity) + var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) + if(!(protection & EMP_PROTECT_WIRES) && istype(wires)) + wires.emp_pulse() + return protection // Pass the protection value collected here upwards + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) + . = P.on_hit(src, 0, def_zone) + +//used on altdisarm() for special interactions between the shoved victim (target) and the src, with user being the one shoving the target on it. +// IMPORTANT: if you wish to add a new own shove_act() to a certain object, remember to add SHOVABLE_ONTO to its obj_flags bitfied var first. +/atom/proc/shove_act(mob/living/target, mob/living/user) + return FALSE + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return TRUE + else if(src in container) + return TRUE + return FALSE + +/atom/proc/update_icon_state() + +/atom/proc/update_overlaysb() + SHOULD_CALL_PARENT(1) + . = list() + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .) + +/atom/proc/update_iconb() + var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON) + + if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE)) + update_icon_state() + + if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS)) + var/list/new_overlays = update_overlaysb() + if(managed_overlays) + cut_overlay(managed_overlays) + managed_overlays = null + if(length(new_overlays)) + managed_overlays = new_overlays + add_overlay(new_overlays) + +/atom/proc/get_examine_name(mob/user) + . = "\a [src]" + var/list/override = list(gender == PLURAL ? "some" : "a", " ", "[name]") + if(article) + . = "[article] [src]" + override[EXAMINE_POSITION_ARTICLE] = article + + var/should_override = FALSE + + if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED) + should_override = TRUE + + + if(blood_DNA && !istype(src, /obj/effect/decal)) + override[EXAMINE_POSITION_BEFORE] = " blood-stained " + should_override = TRUE + + if(should_override) + . = override.Join("") + +///Generate the full examine string of this atom (including icon for goonchat) +/atom/proc/get_examine_string(mob/user, thats = FALSE) + return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]" + +/atom/proc/examine(mob/user) + . = list("[get_examine_string(user, TRUE)].") + + if(desc) + . += desc + + if(reagents) + if(reagents.reagents_holder_flags & TRANSPARENT) + . += "It contains:" + if(length(reagents.reagent_list)) + if(user.can_see_reagents()) //Show each individual reagent + for(var/datum/reagent/R in reagents.reagent_list) + . += "[R.volume] units of [R.name]" + else //Otherwise, just show the total volume + var/total_volume = 0 + for(var/datum/reagent/R in reagents.reagent_list) + total_volume += R.volume + . += "[total_volume] units of various reagents" + else + . += "Nothing." + else if(reagents.reagents_holder_flags & AMOUNT_VISIBLE) + if(reagents.total_volume) + . += "It has [reagents.total_volume] unit\s left." + else + . += "It's empty." + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) + +/atom/proc/relaymove(mob/user) + if(buckle_message_cooldown <= world.time) + buckle_message_cooldown = world.time + 50 + to_chat(user, "You can't move while buckled to [src]!") + return + +/atom/proc/prevent_content_explosion() + return FALSE + +/atom/proc/contents_explosion(severity, target) + return //For handling the effects of explosions on contents that would not normally be effected + +/atom/proc/ex_act(severity, target) + set waitfor = FALSE + contents_explosion(severity, target) + SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target) + +/atom/proc/blob_act(obj/structure/blob/B) + SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) + return + +/atom/proc/fire_act(exposed_temperature, exposed_volume) + SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) + return + +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) + +/atom/proc/hitby_react(atom/movable/AM) + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) + +/atom/proc/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube) + return + +//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list +/mob/living/proc/get_blood_dna_list() + var/blood_id = get_blood_id() + if(!(blood_id in GLOB.blood_reagent_types)) + return + return list("ANIMAL DNA" = "Y-") + +/mob/living/carbon/get_blood_dna_list() + var/blood_id = get_blood_id() + if(!(blood_id in GLOB.blood_reagent_types)) + return + var/list/blood_dna = list() + if(dna) + blood_dna[dna.unique_enzymes] = dna.blood_type + else + blood_dna["UNKNOWN DNA"] = "X*" + return blood_dna + +/mob/living/carbon/alien/get_blood_dna_list() + return list("UNKNOWN DNA" = "X*") + +//to add a mob's dna info into an object's blood_DNA list. +/atom/proc/transfer_mob_blood_dna(mob/living/L) + // Returns 0 if we have that blood already + var/new_blood_dna = L.get_blood_dna_list() + if(!new_blood_dna) + return FALSE + LAZYINITLIST(blood_DNA) //if our list of DNA doesn't exist yet, initialise it. + var/old_length = blood_DNA.len + blood_DNA |= new_blood_dna + if(blood_DNA.len == old_length) + return FALSE + return TRUE + +//to add blood dna info to the object's blood_DNA list +/atom/proc/transfer_blood_dna(list/blood_dna, list/datum/disease/diseases) + LAZYINITLIST(blood_DNA) + var/old_length = blood_DNA.len + blood_DNA |= blood_dna + if(blood_DNA.len > old_length) + return TRUE + //some new blood DNA was added + +//to add blood from a mob onto something, and transfer their dna info +/atom/proc/add_mob_blood(mob/living/M) + var/list/blood_dna = M.get_blood_dna_list() + if(!blood_dna) + return FALSE + return add_blood_DNA(blood_dna, M.diseases) + +//to add blood onto something, with blood dna info to include. +/atom/proc/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + return FALSE + +/obj/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + return transfer_blood_dna(blood_dna, diseases) + +/obj/item/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + . = ..() + if(!.) + return + add_blood_overlay() + +/obj/item/proc/add_blood_overlay() + if(!blood_DNA.len) + return + if(initial(icon) && initial(icon_state)) + blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + blood_splatter_icon.Blend(blood_DNA_to_color(), ICON_MULTIPLY) + add_overlay(blood_splatter_icon) + +/obj/item/clothing/gloves/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + . = ..() + transfer_blood = rand(2, 4) + +/turf/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src + if(!B) + B = new /obj/effect/decal/cleanable/blood/splatter(src, diseases) + B.transfer_blood_dna(blood_dna, diseases) //give blood info to the blood decal. + return TRUE //we bloodied the floor + +/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases) + if(head) + head.add_blood_DNA(blood_dna, diseases) + update_inv_head() + else if(wear_mask) + wear_mask.add_blood_DNA(blood_dna, diseases) + update_inv_wear_mask() + if(wear_neck) + wear_neck.add_blood_DNA(blood_dna, diseases) + update_inv_neck() + if(wear_suit) + wear_suit.add_blood_DNA(blood_dna, diseases) + update_inv_wear_suit() + else if(w_uniform) + w_uniform.add_blood_DNA(blood_dna, diseases) + update_inv_w_uniform() + if(gloves) + var/obj/item/clothing/gloves/G = gloves + G.add_blood_DNA(blood_dna, diseases) + else + transfer_blood_dna(blood_dna, diseases) + bloody_hands = rand(2, 4) + update_inv_gloves() //handles bloody hands overlays and updating + return TRUE + +/atom/proc/blood_DNA_to_color() + var/list/colors = list()//first we make a list of all bloodtypes present + for(var/bloop in blood_DNA) + if(colors[blood_DNA[bloop]]) + colors[blood_DNA[bloop]]++ + else + colors[blood_DNA[bloop]] = 1 + + var/final_rgb = BLOOD_COLOR_HUMAN //a default so we don't have white blood graphics if something messed up + + if(colors.len) + var/sum = 0 //this is all shitcode, but it works; trust me + final_rgb = bloodtype_to_color(colors[1]) + sum = colors[colors[1]] + if(colors.len > 1) + var/i = 2 + while(i <= colors.len) + var/tmp = colors[colors[i]] + final_rgb = BlendRGB(final_rgb, bloodtype_to_color(colors[i]), tmp/(tmp+sum)) + sum += tmp + i++ + + return final_rgb + +/atom/proc/clean_blood() + . = blood_DNA? TRUE : FALSE + blood_DNA = null + +/atom/proc/wash_cream() + return TRUE + +/atom/proc/isinspace() + if(isspaceturf(get_turf(src))) + return TRUE + else + return FALSE + +/atom/proc/handle_fall() + return + +/atom/proc/singularity_act() + return + +/atom/proc/singularity_pull(obj/singularity/S, current_size) + SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) + +/atom/proc/acid_act(acidpwr, acid_volume) + SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) + +/atom/proc/emag_act() + SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) + +/atom/proc/rad_act(strength) + SEND_SIGNAL(src, COMSIG_ATOM_RAD_ACT, strength) + +/atom/proc/narsie_act() + SEND_SIGNAL(src, COMSIG_ATOM_NARSIE_ACT) + +/atom/proc/ratvar_act() + SEND_SIGNAL(src, COMSIG_ATOM_RATVAR_ACT) + +/atom/proc/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) + return FALSE + +/atom/proc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) + SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode) + return FALSE + +/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user) + if(GetComponent(/datum/component/storage)) + return component_storage_contents_dump_act(src_object, user) + return FALSE + +/atom/proc/component_storage_contents_dump_act(datum/component/storage/src_object, mob/user) + var/list/things = src_object.contents() + var/datum/progressbar/progress = new(user, things.len, src) + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(STR, /datum/component/storage.proc/handle_mass_item_insertion, things, src_object, user, progress))) + stoplag(1) + qdel(progress) + to_chat(user, "You dump as much of [src_object.parent]'s contents into [STR.insert_preposition]to [src] as you can.") + STR.orient2hud(user) + src_object.orient2hud(user) + if(user.active_storage) //refresh the HUD to show the transfered contents + user.active_storage.close(user) + user.active_storage.show_to(user) + return TRUE + +/atom/proc/get_dumping_location(obj/item/storage/source,mob/user) + return null + +//This proc is called on the location of an atom when the atom is Destroy()'d +/atom/proc/handle_atom_del(atom/A) + SEND_SIGNAL(src, COMSIG_ATOM_CONTENTS_DEL, A) + +//called when the turf the atom resides on is ChangeTurfed +/atom/proc/HandleTurfChange(turf/T) + for(var/a in src) + var/atom/A = a + A.HandleTurfChange(T) + +//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it) +/atom/proc/get_remote_view_fullscreens(mob/user) + return + +//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) +/atom/proc/update_remote_sight(mob/living/user) + return + + +//Hook for running code when a dir change occurs +/atom/proc/setDir(newdir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) + dir = newdir + +/atom/proc/mech_melee_attack(obj/mecha/M) + return + +//If a mob logouts/logins in side of an object you can use this proc +/atom/proc/on_log(login) + if(loc) + loc.on_log(login) + + +/* + Atom Colour Priority System + A System that gives finer control over which atom colour to colour the atom with. + The "highest priority" one is always displayed as opposed to the default of + "whichever was set last is displayed" +*/ + + +/* + Adds an instance of colour_type to the atom's atom_colours list +*/ +/atom/proc/add_atom_colour(coloration, colour_priority) + if(!atom_colours || !atom_colours.len) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(!coloration) + return + if(colour_priority > atom_colours.len) + return + atom_colours[colour_priority] = coloration + update_atom_colour() + + +/* + Removes an instance of colour_type from the atom's atom_colours list +*/ +/atom/proc/remove_atom_colour(colour_priority, coloration) + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(colour_priority > atom_colours.len) + return + if(coloration && atom_colours[colour_priority] != coloration) + return //if we don't have the expected color (for a specific priority) to remove, do nothing + atom_colours[colour_priority] = null + update_atom_colour() + + +/* + Resets the atom's color to null, and then sets it to the highest priority + colour available +*/ +/atom/proc/update_atom_colour() + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + color = null + for(var/C in atom_colours) + if(islist(C)) + var/list/L = C + if(L.len) + color = L + return + else if(C) + color = C + return + +/atom/vv_edit_var(var_name, var_value) + if(!GLOB.Debug2) + flags_1 |= ADMIN_SPAWNED_1 + . = ..() + switch(var_name) + if("color") + add_atom_colour(color, ADMIN_COLOUR_PRIORITY) + +/atom/vv_get_dropdown() + . = ..() + . += "---" + var/turf/curturf = get_turf(src) + if (curturf) + .["Jump to"] = "?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" + .["Modify Transform"] = "?_src_=vars;[HrefToken()];modtransform=[REF(src)]" + .["Add reagent"] = "?_src_=vars;[HrefToken()];addreagent=[REF(src)]" + .["Trigger EM pulse"] = "?_src_=vars;[HrefToken()];emp=[REF(src)]" + .["Trigger explosion"] = "?_src_=vars;[HrefToken()];explode=[REF(src)]" + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : L.drop_location() + +/atom/Entered(atom/movable/AM, atom/oldLoc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) + +/atom/Exit(atom/movable/AM, atom/newLoc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/newLoc) + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) + +/atom/proc/return_temperature() + return + +// Tool behavior procedure. Redirects to tool-specific procs by default. +// You can override it to catch all tool interactions, for use in complex deconstruction procs. +// Just don't forget to return ..() in the end. +/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) + switch(tool_type) + if(TOOL_CROWBAR) + return crowbar_act(user, I) + if(TOOL_MULTITOOL) + return multitool_act(user, I) + if(TOOL_SCREWDRIVER) + return screwdriver_act(user, I) + if(TOOL_WRENCH) + return wrench_act(user, I) + if(TOOL_WIRECUTTER) + return wirecutter_act(user, I) + if(TOOL_WELDER) + return welder_act(user, I) + if(TOOL_ANALYZER) + return analyzer_act(user, I) + +// Tool-specific behavior procs. To be overridden in subtypes. +/atom/proc/crowbar_act(mob/living/user, obj/item/I) + return + +/atom/proc/multitool_act(mob/living/user, obj/item/I) + return + +/atom/proc/screwdriver_act(mob/living/user, obj/item/I) + SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I) + +/atom/proc/wrench_act(mob/living/user, obj/item/I) + return + +/atom/proc/wirecutter_act(mob/living/user, obj/item/I) + return + +/atom/proc/welder_act(mob/living/user, obj/item/I) + return + +/atom/proc/analyzer_act(mob/living/user, obj/item/I) + return + +/atom/proc/GenerateTag() + return + +// Generic logging helper +/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE) + if(!log_globally) + return + + var/log_text = "[key_name(src)] [message] [loc_name(src)]" + switch(message_type) + if(LOG_ATTACK) + log_attack(log_text) + if(LOG_SAY) + log_say(log_text) + if(LOG_WHISPER) + log_whisper(log_text) + if(LOG_EMOTE) + log_emote(log_text) + if(LOG_DSAY) + log_dsay(log_text) + if(LOG_PDA) + log_pda(log_text) + if(LOG_CHAT) + log_chat(log_text) + if(LOG_COMMENT) + log_comment(log_text) + if(LOG_TELECOMMS) + log_telecomms(log_text) + if(LOG_OOC) + log_ooc(log_text) + if(LOG_ADMIN) + log_admin(log_text) + if(LOG_ADMIN_PRIVATE) + log_admin_private(log_text) + if(LOG_ASAY) + log_adminsay(log_text) + if(LOG_OWNERSHIP) + log_game(log_text) + if(LOG_GAME) + log_game(log_text) + else + stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).") + log_game(log_text) + +// Helper for logging chat messages or other logs with arbitrary inputs (e.g. announcements) +/atom/proc/log_talk(message, message_type, tag=null, log_globally=TRUE, forced_by=null) + var/prefix = tag ? "([tag]) " : "" + var/suffix = forced_by ? " FORCED by [forced_by]" : "" + log_message("[prefix]\"[message]\"[suffix]", message_type, log_globally=log_globally) + +// Helper for logging of messages with only one sender and receiver +/proc/log_directed_talk(atom/source, atom/target, message, message_type, tag) + if(!tag) + stack_trace("Unspecified tag for private message") + tag = "UNKNOWN" + + source.log_talk(message, message_type, tag="[tag] to [key_name(target)]") + if(source != target) + target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) + +/* +Proc for attack log creation, because really why not +1 argument is the actor performing the action +2 argument is the target of the action +3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) +4 is a tool with which the action was made (usually an item) +5 is any additional text, which will be appended to the rest of the log line +*/ + +/proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) + var/ssource = key_name(user) + var/starget = key_name(target) + + var/mob/living/living_target = target + var/hp = istype(living_target) ? " (NEWHP: [living_target.health]) " : "" + + var/sobject = "" + if(object) + sobject = " with [key_name(object)]" + var/saddition = "" + if(addition) + saddition = " [addition]" + + var/postfix = "[sobject][saddition][hp]" + + var/message = "has [what_done] [starget][postfix]" + user.log_message(message, LOG_ATTACK, color="red") + + if(user != target) + var/reverse_message = "has been [what_done] by [ssource][postfix]" + target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) + +// Filter stuff +/atom/movable/proc/add_filter(name,priority,list/params) + if(!filter_data) + filter_data = list() + var/list/p = params.Copy() + p["priority"] = priority + filter_data[name] = p + update_filters() + +/atom/movable/proc/update_filters() + filters = null + sortTim(filter_data,associative = TRUE) + for(var/f in filter_data) + var/list/data = filter_data[f] + var/list/arguments = data.Copy() + arguments -= "priority" + filters += filter(arglist(arguments)) + +/atom/movable/proc/get_filter(name) + if(filter_data && filter_data[name]) + return filters[filter_data.Find(name)] + +/atom/movable/proc/remove_filter(name) + if(filter_data[name]) + filter_data -= name + update_filters() + return TRUE + +/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) + . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 3be5e846..b1638844 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -315,6 +315,11 @@ randomdir = 0 duration = 6 +/obj/effect/temp_visual/desynchronizer + name = "desynchronizer field" + icon_state = "chronofield" + duration = 3 + /obj/effect/temp_visual/impact_effect icon_state = "impact_bullet" duration = 5 diff --git a/code/game/say.dm b/code/game/say.dm index 9c2ef0a9..8f7c9704 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -29,7 +29,7 @@ GLOBAL_LIST_INIT(freqtospan, list( send_speech(message, 7, src, , spans, message_language=language) /atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) - SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, message, speaker, message_language, raw_message, radio_freq, spans, message_mode) + SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) /atom/movable/proc/can_speak() return 1 diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index c5c59156..ba762bea 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -49,6 +49,12 @@ A.malf_picker.remove_malf_verbs(A) qdel(A.malf_picker) +/datum/antagonist/traitor/proc/handle_hearing(datum/source, list/hearing_args) + var/message = hearing_args[HEARING_MESSAGE] + message = GLOB.syndicate_code_phrase_regex.Replace(message, "$1") + message = GLOB.syndicate_code_response_regex.Replace(message, "$1") + hearing_args[HEARING_MESSAGE] = message + SSticker.mode.traitors -= owner if(!silent && owner.current) to_chat(owner.current," You are no longer the [special_role]! ") diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm index f1477fb4..22b15ea8 100644 --- a/code/modules/client/client_colour.dm +++ b/code/modules/client/client_colour.dm @@ -111,3 +111,6 @@ /datum/client_colour/monochrome colour = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) priority = INFINITY //we can't see colors anyway! + +/datum/client_colour/monochrome/trance + priority = 1 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm index 452c8f8b..30c962f9 100644 --- a/code/modules/mob/living/carbon/say.dm +++ b/code/modules/mob/living/carbon/say.dm @@ -17,18 +17,4 @@ if(T) . = T.could_speak_in_language(dt) else - . = initial(dt.flags) & TONGUELESS_SPEECH - -/mob/living/carbon/hear_intercept(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode) - . = ..() - if(!client) - return - for(var/T in get_traumas()) - var/datum/brain_trauma/trauma = T - message = trauma.on_hear(message, speaker, message_language, raw_message, radio_freq) - - if (src.mind.has_antag_datum(/datum/antagonist/traitor)) - message = GLOB.syndicate_code_phrase_regex.Replace(message, "$1") - message = GLOB.syndicate_code_response_regex.Replace(message, "$1") - - return message + . = initial(dt.flags) & TONGUELESS_SPEECH \ No newline at end of file diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 43293556..2fc3bc0c 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -1,519 +1,519 @@ - -/* EMOTE DATUMS */ -/datum/emote/living - mob_type_allowed_typecache = /mob/living - mob_type_blacklist_typecache = list(/mob/living/simple_animal/slime, /mob/living/brain) - -/datum/emote/living/blush - key = "blush" - key_third_person = "blushes" - message = "blushes." - -/datum/emote/living/bow - key = "bow" - key_third_person = "bows" - message = "bows." - message_param = "bows to %t." - restraint_check = TRUE - -/datum/emote/living/burp - key = "burp" - key_third_person = "burps" - message = "burps." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/choke - key = "choke" - key_third_person = "chokes" - message = "chokes!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/cross - key = "cross" - key_third_person = "crosses" - message = "crosses their arms." - restraint_check = TRUE - -/datum/emote/living/chuckle - key = "chuckle" - key_third_person = "chuckles" - message = "chuckles." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/collapse - key = "collapse" - key_third_person = "collapses" - message = "collapses!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/collapse/run_emote(mob/user, params) - . = ..() - if(. && isliving(user)) - var/mob/living/L = user - L.Unconscious(40) - -/datum/emote/living/cough - key = "cough" - key_third_person = "coughs" - message = "coughs!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/cough/can_run_emote(mob/user, status_check = TRUE) - . = ..() - if(user.reagents && (user.reagents.get_reagent("menthol") || user.reagents.get_reagent("peppermint_patty"))) - return FALSE - -/datum/emote/living/dance - key = "dance" - key_third_person = "dances" - message = "dances around happily." - restraint_check = TRUE - -/datum/emote/living/deathgasp - key = "deathgasp" - key_third_person = "deathgasps" - message = "seizes up and falls limp, their eyes dead and lifeless..." - message_robot = "shudders violently for a moment before falling still, its eyes slowly darkening." - message_AI = "lets out a flurry of sparks, its screen flickering as its systems slowly halt." - message_alien = "lets out a waning guttural screech, green blood bubbling from its maw..." - message_larva = "lets out a sickly hiss of air and falls limply to the floor..." - message_monkey = "lets out a faint chimper as it collapses and stops moving..." - message_simple = "stops moving..." - stat_allowed = UNCONSCIOUS - -/datum/emote/living/deathgasp/run_emote(mob/user, params) - var/mob/living/simple_animal/S = user - if(istype(S) && S.deathmessage) - message_simple = S.deathmessage - . = ..() - message_simple = initial(message_simple) - if(. && isalienadult(user)) - playsound(user.loc, 'sound/voice/hiss6.ogg', 80, 1, 1) - -/datum/emote/living/drool - key = "drool" - key_third_person = "drools" - message = "drools." - -/datum/emote/living/faint - key = "faint" - key_third_person = "faints" - message = "faints." - -/datum/emote/living/faint/run_emote(mob/user, params) - . = ..() - if(. && isliving(user)) - var/mob/living/L = user - L.SetSleeping(200) - -/datum/emote/living/flap - key = "flap" - key_third_person = "flaps" - message = "flaps their wings." - restraint_check = TRUE - var/wing_time = 20 - -/datum/emote/living/flap/run_emote(mob/user, params) - . = ..() - if(. && ishuman(user)) - var/mob/living/carbon/human/H = user - var/open = FALSE - if(H.dna.features["wings"] != "None") - if("wingsopen" in H.dna.species.mutant_bodyparts) - open = TRUE - H.CloseWings() - else - H.OpenWings() - addtimer(CALLBACK(H, open ? /mob/living/carbon/human.proc/OpenWings : /mob/living/carbon/human.proc/CloseWings), wing_time) - -/datum/emote/living/flap/aflap - key = "aflap" - key_third_person = "aflaps" - message = "flaps their wings ANGRILY!" - restraint_check = TRUE - wing_time = 10 - -/datum/emote/living/frown - key = "frown" - key_third_person = "frowns" - message = "frowns." - -/datum/emote/living/gag - key = "gag" - key_third_person = "gags" - message = "gags." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/gasp - key = "gasp" - key_third_person = "gasps" - message = "gasps!" - emote_type = EMOTE_AUDIBLE - stat_allowed = UNCONSCIOUS - -/datum/emote/living/giggle - key = "giggle" - key_third_person = "giggles" - message = "giggles." - message_mime = "giggles silently!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/glare - key = "glare" - key_third_person = "glares" - message = "glares." - message_param = "glares at %t." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/grin - key = "grin" - key_third_person = "grins" - message = "grins." - -/datum/emote/living/groan - key = "groan" - key_third_person = "groans" - message = "groans!" - message_mime = "appears to groan!" - -/datum/emote/living/grimace - key = "grimace" - key_third_person = "grimaces" - message = "grimaces." - -/datum/emote/living/jump - key = "jump" - key_third_person = "jumps" - message = "jumps!" - restraint_check = TRUE - -/datum/emote/living/kiss - key = "kiss" - key_third_person = "kisses" - message = "blows a kiss." - message_param = "blows a kiss to %t." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/laugh - key = "laugh" - key_third_person = "laughs" - message = "laughs." - message_mime = "laughs silently!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/laugh/can_run_emote(mob/living/user, status_check = TRUE) - . = ..() - if(. && iscarbon(user)) - var/mob/living/carbon/C = user - return !C.silent - -/datum/emote/living/laugh/run_emote(mob/user, params) - . = ..() - if(. && iscarbon(user)) //Citadel Edit because this is hilarious - var/mob/living/carbon/C = user - if(!C.mind || C.mind.miming) - return - if(iscatperson(C)) //we ask for is cat first because they're a subtype that tests true for ishumanbasic because HERESY - playsound(C, pick('sound/voice/catpeople/nyahaha1.ogg', - 'sound/voice/catpeople/nyahaha2.ogg', - 'sound/voice/catpeople/nyaha.ogg', - 'sound/voice/catpeople/nyahehe.ogg'), - 50, 1) - return - if(ishumanbasic(C)) - if(user.gender == FEMALE) - playsound(C, 'sound/voice/human/womanlaugh.ogg', 50, 1) - else - playsound(C, pick('sound/voice/human/manlaugh1.ogg', 'sound/voice/human/manlaugh2.ogg'), 50, 1) - -/datum/emote/living/look - key = "look" - key_third_person = "looks" - message = "looks." - message_param = "looks at %t." - -/datum/emote/living/nod - key = "nod" - key_third_person = "nods" - message = "nods." - message_param = "nods at %t." - -/datum/emote/living/point - key = "point" - key_third_person = "points" - message = "points." - message_param = "points at %t." - restraint_check = TRUE - -/datum/emote/living/point/run_emote(mob/user, params) - message_param = initial(message_param) // reset - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.get_num_arms() == 0) - if(H.get_num_legs() != 0) - message_param = "tries to point at %t with a leg, falling down in the process!" - H.Knockdown(20) - else - message_param = "bumps [user.p_their()] head on the ground trying to motion towards %t." - H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5) - ..() - -/datum/emote/living/pout - key = "pout" - key_third_person = "pouts" - message = "pouts." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/scream - key = "scream" - key_third_person = "screams" - message = "screams." - message_mime = "acts out a scream!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/scowl - key = "scowl" - key_third_person = "scowls" - message = "scowls." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/shake - key = "shake" - key_third_person = "shakes" - message = "shakes their head." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/shiver - key = "shiver" - key_third_person = "shiver" - message = "shivers." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/sigh - key = "sigh" - key_third_person = "sighs" - message = "sighs." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/sit - key = "sit" - key_third_person = "sits" - message = "sits down." - -/datum/emote/living/smile - key = "smile" - key_third_person = "smiles" - message = "smiles." - -/datum/emote/living/sneeze - key = "sneeze" - key_third_person = "sneezes" - message = "sneezes." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/smug - key = "smug" - key_third_person = "smugs" - message = "grins smugly." - -/datum/emote/living/sniff - key = "sniff" - key_third_person = "sniffs" - message = "sniffs." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/snore - key = "snore" - key_third_person = "snores" - message = "snores." - message_mime = "sleeps soundly." - emote_type = EMOTE_AUDIBLE - stat_allowed = UNCONSCIOUS - -/datum/emote/living/stare - key = "stare" - key_third_person = "stares" - message = "stares." - message_param = "stares at %t." - -/datum/emote/living/strech - key = "stretch" - key_third_person = "stretches" - message = "stretches their arms." - -/datum/emote/living/sulk - key = "sulk" - key_third_person = "sulks" - message = "sulks down sadly." - -/datum/emote/living/surrender - key = "surrender" - key_third_person = "surrenders" - message = "puts their hands on their head and falls to the ground, they surrender!" - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/surrender/run_emote(mob/user, params) - . = ..() - if(. && isliving(user)) - var/mob/living/L = user - L.Knockdown(200) - -/datum/emote/living/sway - key = "sway" - key_third_person = "sways" - message = "sways around dizzily." - -/datum/emote/living/tremble - key = "tremble" - key_third_person = "trembles" - message = "trembles in fear!" - -/datum/emote/living/twitch - key = "twitch" - key_third_person = "twitches" - message = "twitches violently." - -/datum/emote/living/twitch_s - key = "twitch_s" - message = "twitches." - -/datum/emote/living/wave - key = "wave" - key_third_person = "waves" - message = "waves." - -/datum/emote/living/whimper - key = "whimper" - key_third_person = "whimpers" - message = "whimpers." - message_mime = "appears hurt." - -/datum/emote/living/wsmile - key = "wsmile" - key_third_person = "wsmiles" - message = "smiles weakly." - -/datum/emote/living/yawn - key = "yawn" - key_third_person = "yawns" - message = "yawns." - emote_type = EMOTE_AUDIBLE - -/datum/emote/living/custom - key = "me" - key_third_person = "custom" - message = null - -/datum/emote/living/custom/proc/check_invalid(mob/user, input) - . = TRUE - if(copytext(input,1,5) == "says") - to_chat(user, "Invalid emote.") - else if(copytext(input,1,9) == "exclaims") - to_chat(user, "Invalid emote.") - else if(copytext(input,1,6) == "yells") - to_chat(user, "Invalid emote.") - else if(copytext(input,1,5) == "asks") - to_chat(user, "Invalid emote.") - else - . = FALSE - -/datum/emote/living/custom/run_emote(mob/user, params, type_override = null) - if(jobban_isbanned(user, "emote")) - to_chat(user, "You cannot send custom emotes (banned).") - return FALSE - else if(QDELETED(user)) - return FALSE - else if(user.client && user.client.prefs.muted & MUTE_IC) - to_chat(user, "You cannot send IC messages (muted).") - return FALSE - else if(!params) - var/custom_emote = copytext(sanitize(input("Choose an emote to display.") as message|null), 1, MAX_MESSAGE_LEN) //CIT CHANGE - expands emote textbox - if(custom_emote && !check_invalid(user, custom_emote)) - var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") - switch(type) - if("Visible") - emote_type = EMOTE_VISIBLE - if("Hearable") - emote_type = EMOTE_AUDIBLE - else - alert("Unable to use this emote, must be either hearable or visible.") - return - message = custom_emote - else - message = params - if(type_override) - emote_type = type_override - message = user.say_emphasis(message) - . = ..() - message = null - emote_type = EMOTE_VISIBLE - -/datum/emote/living/custom/replace_pronoun(mob/user, message) - return message - -/datum/emote/living/help - key = "help" - -/datum/emote/living/help/run_emote(mob/user, params) - var/list/keys = list() - var/list/message = list("Available emotes, you can use them with say \"*emote\": ") - - var/datum/emote/E - var/list/emote_list = E.emote_list - for(var/e in emote_list) - if(e in keys) - continue - E = emote_list[e] - if(E.can_run_emote(user, status_check = FALSE)) - keys += E.key - - keys = sortList(keys) - - for(var/emote in keys) - if(LAZYLEN(message) > 1) - message += ", [emote]" - else - message += "[emote]" - - message += "." - - message = jointext(message, "") - - to_chat(user, message) - -/datum/emote/sound/beep - key = "beep" - key_third_person = "beeps" - message = "beeps." - message_param = "beeps at %t." - sound = 'sound/machines/twobeep.ogg' - mob_type_allowed_typecache = list(/mob/living/brain, /mob/living/silicon, /mob/living/carbon/human) - -/datum/emote/living/circle - key = "circle" - key_third_person = "circles" - restraint_check = TRUE - -/datum/emote/living/circle/run_emote(mob/user, params) - . = ..() - var/obj/item/circlegame/N = new(user) - if(user.put_in_hands(N)) - to_chat(user, "You make a circle with your hand.") - else - qdel(N) - to_chat(user, "You don't have any free hands to make a circle with.") - -/datum/emote/living/slap - key = "slap" - key_third_person = "slaps" - restraint_check = TRUE - -/datum/emote/living/slap/run_emote(mob/user, params) - . = ..() - if(!.) - return - var/obj/item/slapper/N = new(user) - if(user.put_in_hands(N)) - to_chat(user, "You ready your slapping hand.") - else - to_chat(user, "You're incapable of slapping in your current state.") + +/* EMOTE DATUMS */ +/datum/emote/living + mob_type_allowed_typecache = /mob/living + mob_type_blacklist_typecache = list(/mob/living/simple_animal/slime, /mob/living/brain) + +/datum/emote/living/blush + key = "blush" + key_third_person = "blushes" + message = "blushes." + +/datum/emote/living/bow + key = "bow" + key_third_person = "bows" + message = "bows." + message_param = "bows to %t." + restraint_check = TRUE + +/datum/emote/living/burp + key = "burp" + key_third_person = "burps" + message = "burps." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/choke + key = "choke" + key_third_person = "chokes" + message = "chokes!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/cross + key = "cross" + key_third_person = "crosses" + message = "crosses their arms." + restraint_check = TRUE + +/datum/emote/living/chuckle + key = "chuckle" + key_third_person = "chuckles" + message = "chuckles." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/collapse + key = "collapse" + key_third_person = "collapses" + message = "collapses!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/collapse/run_emote(mob/user, params) + . = ..() + if(. && isliving(user)) + var/mob/living/L = user + L.Unconscious(40) + +/datum/emote/living/cough + key = "cough" + key_third_person = "coughs" + message = "coughs!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/cough/can_run_emote(mob/user, status_check = TRUE , intentional) + . = ..() + if(HAS_TRAIT(user, TRAIT_SOOTHED_THROAT)) + return FALSE + +/datum/emote/living/dance + key = "dance" + key_third_person = "dances" + message = "dances around happily." + restraint_check = TRUE + +/datum/emote/living/deathgasp + key = "deathgasp" + key_third_person = "deathgasps" + message = "seizes up and falls limp, their eyes dead and lifeless..." + message_robot = "shudders violently for a moment before falling still, its eyes slowly darkening." + message_AI = "lets out a flurry of sparks, its screen flickering as its systems slowly halt." + message_alien = "lets out a waning guttural screech, green blood bubbling from its maw..." + message_larva = "lets out a sickly hiss of air and falls limply to the floor..." + message_monkey = "lets out a faint chimper as it collapses and stops moving..." + message_simple = "stops moving..." + stat_allowed = UNCONSCIOUS + +/datum/emote/living/deathgasp/run_emote(mob/user, params) + var/mob/living/simple_animal/S = user + if(istype(S) && S.deathmessage) + message_simple = S.deathmessage + . = ..() + message_simple = initial(message_simple) + if(. && isalienadult(user)) + playsound(user.loc, 'sound/voice/hiss6.ogg', 80, 1, 1) + +/datum/emote/living/drool + key = "drool" + key_third_person = "drools" + message = "drools." + +/datum/emote/living/faint + key = "faint" + key_third_person = "faints" + message = "faints." + +/datum/emote/living/faint/run_emote(mob/user, params) + . = ..() + if(. && isliving(user)) + var/mob/living/L = user + L.SetSleeping(200) + +/datum/emote/living/flap + key = "flap" + key_third_person = "flaps" + message = "flaps their wings." + restraint_check = TRUE + var/wing_time = 20 + +/datum/emote/living/flap/run_emote(mob/user, params) + . = ..() + if(. && ishuman(user)) + var/mob/living/carbon/human/H = user + var/open = FALSE + if(H.dna.features["wings"] != "None") + if("wingsopen" in H.dna.species.mutant_bodyparts) + open = TRUE + H.CloseWings() + else + H.OpenWings() + addtimer(CALLBACK(H, open ? /mob/living/carbon/human.proc/OpenWings : /mob/living/carbon/human.proc/CloseWings), wing_time) + +/datum/emote/living/flap/aflap + key = "aflap" + key_third_person = "aflaps" + message = "flaps their wings ANGRILY!" + restraint_check = TRUE + wing_time = 10 + +/datum/emote/living/frown + key = "frown" + key_third_person = "frowns" + message = "frowns." + +/datum/emote/living/gag + key = "gag" + key_third_person = "gags" + message = "gags." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/gasp + key = "gasp" + key_third_person = "gasps" + message = "gasps!" + emote_type = EMOTE_AUDIBLE + stat_allowed = UNCONSCIOUS + +/datum/emote/living/giggle + key = "giggle" + key_third_person = "giggles" + message = "giggles." + message_mime = "giggles silently!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/glare + key = "glare" + key_third_person = "glares" + message = "glares." + message_param = "glares at %t." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/grin + key = "grin" + key_third_person = "grins" + message = "grins." + +/datum/emote/living/groan + key = "groan" + key_third_person = "groans" + message = "groans!" + message_mime = "appears to groan!" + +/datum/emote/living/grimace + key = "grimace" + key_third_person = "grimaces" + message = "grimaces." + +/datum/emote/living/jump + key = "jump" + key_third_person = "jumps" + message = "jumps!" + restraint_check = TRUE + +/datum/emote/living/kiss + key = "kiss" + key_third_person = "kisses" + message = "blows a kiss." + message_param = "blows a kiss to %t." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/laugh + key = "laugh" + key_third_person = "laughs" + message = "laughs." + message_mime = "laughs silently!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/laugh/can_run_emote(mob/living/user, status_check = TRUE) + . = ..() + if(. && iscarbon(user)) + var/mob/living/carbon/C = user + return !C.silent + +/datum/emote/living/laugh/run_emote(mob/user, params) + . = ..() + if(. && iscarbon(user)) //Citadel Edit because this is hilarious + var/mob/living/carbon/C = user + if(!C.mind || C.mind.miming) + return + if(iscatperson(C)) //we ask for is cat first because they're a subtype that tests true for ishumanbasic because HERESY + playsound(C, pick('sound/voice/catpeople/nyahaha1.ogg', + 'sound/voice/catpeople/nyahaha2.ogg', + 'sound/voice/catpeople/nyaha.ogg', + 'sound/voice/catpeople/nyahehe.ogg'), + 50, 1) + return + if(ishumanbasic(C)) + if(user.gender == FEMALE) + playsound(C, 'sound/voice/human/womanlaugh.ogg', 50, 1) + else + playsound(C, pick('sound/voice/human/manlaugh1.ogg', 'sound/voice/human/manlaugh2.ogg'), 50, 1) + +/datum/emote/living/look + key = "look" + key_third_person = "looks" + message = "looks." + message_param = "looks at %t." + +/datum/emote/living/nod + key = "nod" + key_third_person = "nods" + message = "nods." + message_param = "nods at %t." + +/datum/emote/living/point + key = "point" + key_third_person = "points" + message = "points." + message_param = "points at %t." + restraint_check = TRUE + +/datum/emote/living/point/run_emote(mob/user, params) + message_param = initial(message_param) // reset + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.get_num_arms() == 0) + if(H.get_num_legs() != 0) + message_param = "tries to point at %t with a leg, falling down in the process!" + H.Knockdown(20) + else + message_param = "bumps [user.p_their()] head on the ground trying to motion towards %t." + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5) + ..() + +/datum/emote/living/pout + key = "pout" + key_third_person = "pouts" + message = "pouts." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/scream + key = "scream" + key_third_person = "screams" + message = "screams." + message_mime = "acts out a scream!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/scowl + key = "scowl" + key_third_person = "scowls" + message = "scowls." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/shake + key = "shake" + key_third_person = "shakes" + message = "shakes their head." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/shiver + key = "shiver" + key_third_person = "shiver" + message = "shivers." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/sigh + key = "sigh" + key_third_person = "sighs" + message = "sighs." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/sit + key = "sit" + key_third_person = "sits" + message = "sits down." + +/datum/emote/living/smile + key = "smile" + key_third_person = "smiles" + message = "smiles." + +/datum/emote/living/sneeze + key = "sneeze" + key_third_person = "sneezes" + message = "sneezes." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/smug + key = "smug" + key_third_person = "smugs" + message = "grins smugly." + +/datum/emote/living/sniff + key = "sniff" + key_third_person = "sniffs" + message = "sniffs." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/snore + key = "snore" + key_third_person = "snores" + message = "snores." + message_mime = "sleeps soundly." + emote_type = EMOTE_AUDIBLE + stat_allowed = UNCONSCIOUS + +/datum/emote/living/stare + key = "stare" + key_third_person = "stares" + message = "stares." + message_param = "stares at %t." + +/datum/emote/living/strech + key = "stretch" + key_third_person = "stretches" + message = "stretches their arms." + +/datum/emote/living/sulk + key = "sulk" + key_third_person = "sulks" + message = "sulks down sadly." + +/datum/emote/living/surrender + key = "surrender" + key_third_person = "surrenders" + message = "puts their hands on their head and falls to the ground, they surrender!" + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/surrender/run_emote(mob/user, params) + . = ..() + if(. && isliving(user)) + var/mob/living/L = user + L.Knockdown(200) + +/datum/emote/living/sway + key = "sway" + key_third_person = "sways" + message = "sways around dizzily." + +/datum/emote/living/tremble + key = "tremble" + key_third_person = "trembles" + message = "trembles in fear!" + +/datum/emote/living/twitch + key = "twitch" + key_third_person = "twitches" + message = "twitches violently." + +/datum/emote/living/twitch_s + key = "twitch_s" + message = "twitches." + +/datum/emote/living/wave + key = "wave" + key_third_person = "waves" + message = "waves." + +/datum/emote/living/whimper + key = "whimper" + key_third_person = "whimpers" + message = "whimpers." + message_mime = "appears hurt." + +/datum/emote/living/wsmile + key = "wsmile" + key_third_person = "wsmiles" + message = "smiles weakly." + +/datum/emote/living/yawn + key = "yawn" + key_third_person = "yawns" + message = "yawns." + emote_type = EMOTE_AUDIBLE + +/datum/emote/living/custom + key = "me" + key_third_person = "custom" + message = null + +/datum/emote/living/custom/proc/check_invalid(mob/user, input) + . = TRUE + if(copytext(input,1,5) == "says") + to_chat(user, "Invalid emote.") + else if(copytext(input,1,9) == "exclaims") + to_chat(user, "Invalid emote.") + else if(copytext(input,1,6) == "yells") + to_chat(user, "Invalid emote.") + else if(copytext(input,1,5) == "asks") + to_chat(user, "Invalid emote.") + else + . = FALSE + +/datum/emote/living/custom/run_emote(mob/user, params, type_override = null) + if(jobban_isbanned(user, "emote")) + to_chat(user, "You cannot send custom emotes (banned).") + return FALSE + else if(QDELETED(user)) + return FALSE + else if(user.client && user.client.prefs.muted & MUTE_IC) + to_chat(user, "You cannot send IC messages (muted).") + return FALSE + else if(!params) + var/custom_emote = copytext(sanitize(input("Choose an emote to display.") as message|null), 1, MAX_MESSAGE_LEN) //CIT CHANGE - expands emote textbox + if(custom_emote && !check_invalid(user, custom_emote)) + var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") + switch(type) + if("Visible") + emote_type = EMOTE_VISIBLE + if("Hearable") + emote_type = EMOTE_AUDIBLE + else + alert("Unable to use this emote, must be either hearable or visible.") + return + message = custom_emote + else + message = params + if(type_override) + emote_type = type_override + message = user.say_emphasis(message) + . = ..() + message = null + emote_type = EMOTE_VISIBLE + +/datum/emote/living/custom/replace_pronoun(mob/user, message) + return message + +/datum/emote/living/help + key = "help" + +/datum/emote/living/help/run_emote(mob/user, params) + var/list/keys = list() + var/list/message = list("Available emotes, you can use them with say \"*emote\": ") + + var/datum/emote/E + var/list/emote_list = E.emote_list + for(var/e in emote_list) + if(e in keys) + continue + E = emote_list[e] + if(E.can_run_emote(user, status_check = FALSE)) + keys += E.key + + keys = sortList(keys) + + for(var/emote in keys) + if(LAZYLEN(message) > 1) + message += ", [emote]" + else + message += "[emote]" + + message += "." + + message = jointext(message, "") + + to_chat(user, message) + +/datum/emote/sound/beep + key = "beep" + key_third_person = "beeps" + message = "beeps." + message_param = "beeps at %t." + sound = 'sound/machines/twobeep.ogg' + mob_type_allowed_typecache = list(/mob/living/brain, /mob/living/silicon, /mob/living/carbon/human) + +/datum/emote/living/circle + key = "circle" + key_third_person = "circles" + restraint_check = TRUE + +/datum/emote/living/circle/run_emote(mob/user, params) + . = ..() + var/obj/item/circlegame/N = new(user) + if(user.put_in_hands(N)) + to_chat(user, "You make a circle with your hand.") + else + qdel(N) + to_chat(user, "You don't have any free hands to make a circle with.") + +/datum/emote/living/slap + key = "slap" + key_third_person = "slaps" + restraint_check = TRUE + +/datum/emote/living/slap/run_emote(mob/user, params) + . = ..() + if(!.) + return + var/obj/item/slapper/N = new(user) + if(user.put_in_hands(N)) + to_chat(user, "You ready your slapping hand.") + else + to_chat(user, "You're incapable of slapping in your current state.") diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 0cc469dd..a9f9bb48 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -1743,6 +1743,7 @@ datum/reagent/consumable/ethanol/creme_de_coconut value = 2 /datum/reagent/consumable/ethanol/peppermint_patty/on_mob_life(mob/living/carbon/M) + M.apply_status_effect(/datum/status_effect/throat_soothed) M.adjust_bodytemperature(5 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL) ..() diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index b27d66e2..55356066 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -760,6 +760,10 @@ glass_name = "glass of menthol" glass_desc = "Tastes naturally minty, and imparts a very mild numbing sensation." +/datum/reagent/consumable/menthol/on_mob_life(mob/living/L) + L.apply_status_effect(/datum/status_effect/throat_soothed) + ..() + /datum/reagent/consumable/grenadine name = "Grenadine" description = "Not cherry flavored!" diff --git a/code/modules/research/designs/bluespace_designs.dm b/code/modules/research/designs/bluespace_designs.dm index b7a201f5..ec4309b6 100644 --- a/code/modules/research/designs/bluespace_designs.dm +++ b/code/modules/research/designs/bluespace_designs.dm @@ -1,76 +1,86 @@ - -///////////////////////////////////////// -//////////////Blue Space///////////////// -///////////////////////////////////////// - -/datum/design/beacon - name = "Tracking Beacon" - desc = "A blue space tracking beacon." - id = "beacon" - build_type = PROTOLATHE - materials = list(MAT_METAL = 150, MAT_GLASS = 100) - build_path = /obj/item/beacon - category = list("Bluespace Designs") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SECURITY - -/datum/design/bag_holding - name = "Bag of Holding" - desc = "A backpack that opens into a localized pocket of bluespace." - id = "bag_holding" - build_type = PROTOLATHE - materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000) - build_path = /obj/item/storage/backpack/holding - category = list("Bluespace Designs") - dangerous_construction = TRUE - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/satchel_holding - name = "Satchel of Holding" - desc = "A satchel that opens into a localized pocket of bluespace." - id = "satchel_holding" - build_type = PROTOLATHE - materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000) - build_path = /obj/item/storage/backpack/holding/satchel - category = list("Bluespace Designs") - dangerous_construction = TRUE - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/biobag_holding - name = "Bio Bag of Holding" - desc = "A chemical holding thingy. Mostly used for xenobiology." - id = "biobag_holding" - build_type = PROTOLATHE - materials = list(MAT_GOLD = 1500, MAT_DIAMOND = 750, MAT_URANIUM = 250, MAT_BLUESPACE = 1000) - build_path = /obj/item/storage/bag/bio/holding - category = list("Bluespace Designs") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/bluespace_crystal - name = "Artificial Bluespace Crystal" - desc = "A small blue crystal with mystical properties." - id = "bluespace_crystal" - build_type = PROTOLATHE - materials = list(MAT_DIAMOND = 1500, MAT_PLASMA = 1500) - build_path = /obj/item/stack/ore/bluespace_crystal/artificial - category = list("Bluespace Designs") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/telesci_gps - name = "GPS Device" - desc = "Little thingie that can track its position at all times." - id = "telesci_gps" - build_type = PROTOLATHE - materials = list(MAT_METAL = 500, MAT_GLASS = 1000) - build_path = /obj/item/gps - category = list("Bluespace Designs") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO - -/datum/design/miningsatchel_holding - name = "Mining Satchel of Holding" - desc = "A mining satchel that can hold an infinite amount of ores." - id = "minerbag_holding" - build_type = PROTOLATHE - materials = list(MAT_GOLD = 250, MAT_URANIUM = 500) //quite cheap, for more convenience - build_path = /obj/item/storage/bag/ore/holding - category = list("Bluespace Designs") - departmental_flags = DEPARTMENTAL_FLAG_CARGO + +///////////////////////////////////////// +//////////////Blue Space///////////////// +///////////////////////////////////////// + +/datum/design/beacon + name = "Tracking Beacon" + desc = "A blue space tracking beacon." + id = "beacon" + build_type = PROTOLATHE + materials = list(MAT_METAL = 150, MAT_GLASS = 100) + build_path = /obj/item/beacon + category = list("Bluespace Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SECURITY + +/datum/design/bag_holding + name = "Bag of Holding" + desc = "A backpack that opens into a localized pocket of bluespace." + id = "bag_holding" + build_type = PROTOLATHE + materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000) + build_path = /obj/item/storage/backpack/holding + category = list("Bluespace Designs") + dangerous_construction = TRUE + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/satchel_holding + name = "Satchel of Holding" + desc = "A satchel that opens into a localized pocket of bluespace." + id = "satchel_holding" + build_type = PROTOLATHE + materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000) + build_path = /obj/item/storage/backpack/holding/satchel + category = list("Bluespace Designs") + dangerous_construction = TRUE + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/biobag_holding + name = "Bio Bag of Holding" + desc = "A chemical holding thingy. Mostly used for xenobiology." + id = "biobag_holding" + build_type = PROTOLATHE + materials = list(MAT_GOLD = 1500, MAT_DIAMOND = 750, MAT_URANIUM = 250, MAT_BLUESPACE = 1000) + build_path = /obj/item/storage/bag/bio/holding + category = list("Bluespace Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/bluespace_crystal + name = "Artificial Bluespace Crystal" + desc = "A small blue crystal with mystical properties." + id = "bluespace_crystal" + build_type = PROTOLATHE + materials = list(MAT_DIAMOND = 1500, MAT_PLASMA = 1500) + build_path = /obj/item/stack/ore/bluespace_crystal/artificial + category = list("Bluespace Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/telesci_gps + name = "GPS Device" + desc = "Little thingie that can track its position at all times." + id = "telesci_gps" + build_type = PROTOLATHE + materials = list(MAT_METAL = 500, MAT_GLASS = 1000) + build_path = /obj/item/gps + category = list("Bluespace Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO + +/datum/design/desynchronizer + name = "Desynchronizer" + desc = "A device that can desynchronize the user from spacetime." + id = "desynchronizer" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_SILVER = 1500, MAT_BLUESPACE = 1000) + build_path = /obj/item/desynchronizer + category = list("Bluespace Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/miningsatchel_holding + name = "Mining Satchel of Holding" + desc = "A mining satchel that can hold an infinite amount of ores." + id = "minerbag_holding" + build_type = PROTOLATHE + materials = list(MAT_GOLD = 250, MAT_URANIUM = 500) //quite cheap, for more convenience + build_path = /obj/item/storage/bag/ore/holding + category = list("Bluespace Designs") + departmental_flags = DEPARTMENTAL_FLAG_CARGO diff --git a/code/modules/research/nanites/nanite_programs.dm b/code/modules/research/nanites/nanite_programs.dm index a06311f2..2a6563fe 100644 --- a/code/modules/research/nanites/nanite_programs.dm +++ b/code/modules/research/nanites/nanite_programs.dm @@ -208,9 +208,6 @@ datum/nanite_program/proc/on_mob_remove() /datum/nanite_program/proc/on_death() return -/datum/nanite_program/proc/on_hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) - return - /datum/nanite_program/proc/software_error(type) if(!type) type = rand(1,5) diff --git a/code/modules/research/nanites/nanite_programs/sensor.dm b/code/modules/research/nanites/nanite_programs/sensor.dm index d494ec8c..535b92c6 100644 --- a/code/modules/research/nanites/nanite_programs/sensor.dm +++ b/code/modules/research/nanites/nanite_programs/sensor.dm @@ -345,6 +345,14 @@ var/sentence = "" var/inclusive = TRUE + +/datum/nanite_program/sensor/voice/on_mob_add() + . = ..() + RegisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear) + +/datum/nanite_program/sensor/voice/on_mob_remove() + UnregisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear) + /datum/nanite_program/sensor/voice/set_extra_setting(user, setting) if(setting == "Sent Code") var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num @@ -378,15 +386,12 @@ target.sentence = sentence target.inclusive = inclusive -/datum/nanite_program/sensor/voice/on_hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) +/datum/nanite_program/sensor/voice/proc/on_hear(datum/source, list/hearing_args) if(!sentence) return - //To make it not case sensitive - var/low_message = lowertext(raw_message) - var/low_sentence = lowertext(sentence) if(inclusive) - if(findtext(low_message, low_sentence)) + if(findtextEx(hearing_args[HEARING_RAW_MESSAGE], sentence)) send_code() else - if(low_message == low_sentence) + if(hearing_args[HEARING_RAW_MESSAGE] == sentence) send_code() \ No newline at end of file diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index a13739e3..0a518774 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -323,6 +323,15 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 +/datum/techweb_node/unregulated_bluespace + id = "unregulated_bluespace" + display_name = "Unregulated Bluespace Research" + description = "Bluespace technology using unstable or unbalanced procedures, prone to damaging the fabric of bluespace. Outlawed by galactic conventions." + prereq_ids = list("bluespace_warping", "syndicate_basic") + design_ids = list("desynchronizer") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + export_price = 2500 + /////////////////////////plasma tech///////////////////////// /datum/techweb_node/basic_plasma id = "basic_plasma" diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index 397d2ce57b44aa0746570cb79456d6d370873e15..fef57630e29c20ef251f141154b59e66290ed380 100644 GIT binary patch delta 21311 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 diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 9a9a4017d16fc08dec2d672899f78d32142eec4c..37a1a0877b3e289289169ac2467e273f039a0272 100644 GIT binary patch delta 14109 zcmbWdby!rv`!{^(4y79fL8U|KHUJ5g?h;Wtlx7c&(uxR33kVVt(zQ#YNJt6@OE)ZC zyUV@@zrXMEyw6|Hd%awHam|UDbI(09pZjxvW)kuUY6}Sxc|oMVzVWMj_Fi_LFWq0g zbaMp&-;6R7ZMSK4N`+~->WgyEchcVJ3a3@mw!hWTOvKgq9(_+MUyfZc0I{6I#&%{O z)~aahk>b};pVWVmVh^EiEsg$LZ6om8;f*61%%i0KaN8g$3XQlT$mPm#W9;bJOh z|I71OYU`3r@BU0$AElTWypnsfwzfR1Bqh?+AoBw9L%Qd8b6U=t5)s>P*<;#DGuKl` z#Vd}MKBb&29}m;kiQ+%vkvp~3Q^8{7*teYa-D;9#K>I+V9}WWOUT4RW-=haCN1=9% zu5t^;j92STZ%9h(%$mRZE`O{Mz~AeWUF{R=v)bpaVM?Js{b;zu1^#L`TzJD#G0|u# zaQzE|ijtdX8L9v8_P9+CuuZxH$tb$8pi-hKAgJ*dpc(UMO)Egi&?PZ5=8 z-g6`_iUwOsUK735`0MfXzVC;*7CAsKYg0b{h^WuDOb$(KYaiGtwd<&DTrjEK!q+&E zDm8@QF0h(!z4ky`TbCDdRQ`hayJ@iIu@QM>m5|ZVE#VGohk0LcForSE{`8 z7~d4`oQ>biN+Q?D@Qa6@&^ljR$Ii;(VxL*VT1vf-+LWKIlqEP6?4HNc`19*&r3Cf* z_*=L-lotJ9=UR@o#}x+BjsH|`4ylylm{osC`g^9=Z_@GFSNs@4N2D0T@!K@s?{^+< zY8;S&nOE|)f_2>aZhVP`<4y3Q`ZW!6`Rj``bzf){WFfK;WTTjHCc_glB<(_=n_mQS zuI@3V4q@(SCwazV)k?x!0yj(X9u#(o^n!7D2Q83eKgZa5qDB) zGDY%gv6#GfXEFcK&(iAcPQmW45`!RC-JEF#x%iiE+rB1f#CNRs+PyhzMxj#?uS8=U zpFaFu10|Mycsr+?{{?FZ?*iDS_cW;{vCRp!Ue2ignWu=2cbSGnZ1n3{$5ODpz8`&G zM(D%JbhZk!HcCa!dlXt70lz-1#?xh4bDMI@zu4J*moW21ZGMTyVtdjrBo4pPN&pqvXZS$8LG)cZrI!T!W)Mm7*kIv4rB^M#8?foR}y0 z)mL=?4cIV{lqKYCvnR2M<**TbPw}QrTyJY~v{guV(0Y!m!Z0x-d{F($7-xw7b?cqC z`?6_8?=U)RmCe`7c+EJPXtlTk%fiQ)!>)qVE@J95c49?uRpOe?SvUGU=@5tg#RP?P zw^cXkZZDSbxAA&BxcdCyhF{h9WmELLp3I)*B-Iidz8qWF_UCVie4SxS+%}?MpqPJ4 zAm@yz5%Hb;{dT|F*};!bvfZ-=LVkrHcNyV>s=<493m&fScSrtwtr>fs@L}OsN@*z^ zd{3B}hH09_-fUty%PxduvT6l|6+v_( zhD&AQz14u`Aprnz0~+^K^nEk7X8Z!b;}h*=fWpPHI9xf;^~D=1RmwgFl2W;Xw{m{f zGJiM6np%B@mAq*ZWVp?Gd)f4p>m+QZYFO1Kc{JJ_D3u@L3 zkGX`+qRHFfFHO-jY`^bpIm%=&^H^Y$#QOVSHh(>KfRl86hRtCxMdgKaf{xVY6DtcU z<*@T~sY*E%AuAE^X`ra=TQ}7!|i6F;0i0 zQVKmrmNb7Fl=aDcd?NV09nNe86q7CU+shszE4U- zBXO!Jbu>M&ulJ5>%xH^2l_)1r%46rbLZg!`LN_mDe&2xJLOSMWj6qSeKY`kL#3Eu( zuO2;-TMy2)Nc*cMfNry~w?-_VG)=~nDBCIJt{NNYE&{Oz1_s7e&)x#S$QNz9Ku+=O zgSBT6H>L_jh}1#TIt9S!(j ze|rp{%5Sxcg9lB{k-JD#GiqlP(7i+WtE|NGr>bO(w9lULT%Tshs3j5?@k)@huHwt`6)r#pM zwwce+_ufnK3`dSpfQ6cxLn8Td!bTQd^Udb$=arOuWZ>hb9{`|II3k11eL3<)jI-6b z|A5gE%>?cdEs`ji9rPm4G4E+_B56i`E(c})cUe*48vguwX100s#j4Q~;m_=9>M9Xj zsIZ(;nq>EShERppaW`dh#N_v$UXepw{p`@2H8csRQt!v`DcJw-cmdmhk^bUTob2C5 z4}v<^u{GD`<18l7i^ZFf^HF|n!4rObQPeaU z>}t{Yxz&O|(X^`hez;Q#KDSD>J-fWY)*Y!WLS>(E7q`sW{YPOp=5}wnhjNqN|AQH% za^8*{1M34@u7q-R963qR4rST=N<=?~ow35ajHFv<8|bhd=bxt@#}(lPRlY=uy&~W3 zDMEKV>BorrjZZVS?F@>*du6>_sP05M)%$I;Rw-JITyN)6#!KWjWp^Zg>~=op%A`UY zE_5O5b63uPftC<{yv8!hqLe(5y|*m zb3VV8V88ZzbK+)sX}3dmnYVQBdM!5nWt7s0Z75e1UrdpaOOFvPyeT4xoN% z8~H}H=>u5kE+suc7Rvp`P({<2`HZYxt)2t0vzyS_?ToB`Fzf5W7qD29cF4s7LiR9y z9dR_tH0;~h*yz=zTHN_!SFk8)_$Y44fcFqBg} z8p$#X3r(bFjXv6(5P(S$Tb5q4_5cNL%~3<}Y0HUD`vGJyzSS9}*mvh%iW%7txr_&|>yw=>c@Wm_Ygbn*9Q zxf{*dbgPn~T-fp&-!HXBjyTaxt49>m<{VM9c$U8;#9AbN3eG3`NK$b-Bq zt1OhnFC0LUSUnVH+i$M2A67-NL z<*1X!wx%)M$Q(c=dJawN+^P&>N;~x$`1N=cF5)90(4bERta$}U$%~)9ybmM(X4TLc zivIhSU|DI-*F^>d=4OR+fnb3sC()((E1XDbSfVcI`hiFF)@`G7Z4VD;rx!OD78k|b#^u8wMiPi}Qf|+`%~^`Mfc(lxPyg(yX!HoB z?~+mn?J!C)jP11baU*uv#k~gmilKteysQNu!F)kX8QnJ0@le&Os3%xOjIqx* z>C0{k4L5uNQ5y)Ht%MAvOGdzGIncW~dAVPjixX@>>ZXt$FVf$OE0--QqwhWkxs$BH)z;RwAn!1-OzwDkHrARH>C=AJoY0A=+wDl`Tm@05 zcs}E+*2B9xmMyZzS1%rWShJmab%nS@hbwEGsx4Q#wPgz=1eK_h3Qip`vZ4L2g8+BK ztJ+fx(u4m=^{P<$1*Ky^hRS5ar0v|JQa^*hi=AA%wcqtbQ=AU+E9m7ED`udwQLj@G zFWd6?C9&)ZHrPJ<`J28%B`M7{aEI~&GsG=M=sT>W?5kk_pV|iVu$Bf`&ARNo@pY?P z#9?@w6XiYUN1J%e9aJcyzbAeM<)xvrxO+(w;5t7WnwPjr1`aMYLrsV+&rlJlA7&&@s?zi z(!mL_?<5>+N}r|dbt(ah5C#!00aRP!l6BNP@vfpVek zANKkh@J^=OEH%i<$wAe}&uH6g)XDz~jVL&lGr((xVP%22pvEF7K*#7XNtUD(^kTk1fzj?^_TyjK&(VBPDVJl7Zmy*7MvoloUpOSg46L=GEq zLvXKW9mw2$0qH{&dqSh{+#d^1OYcv1ikY82-R|x8nQ)`D-Z=9k(h*WB4=!FdI!C+R z+8s$g&aM>WhALE^K9!z})yZ!IB9(qIXSzvnEk@i?+4P(=Qmta$&# zZX}_r=qEBkgzZyd0w~GEaR5W?C9L2B(6jWwkDzY)pid9(0lKC5JSMeq+vwi_KApSP z-HcC+$j9dky16cOQarzE>9$Sql1mb(3i{p7)1fd$GVlp$s-%1a!#=5)NM46zH^NX8 zAyO>;HRO*P9R@kKCcl&LNBM31IUIh(NoC`Ro^bhdvvj)R$)7~C1d5AQHD)E{MW|Ebnu2{Z;AJ zU$J2`D7IVYp!DB2T+~w6g+27EuNvV$#O-j*&nE|Ut}j0Pws88c#8(W(c@Mf-PP}tR z=13a3qtUtLddzhj9;&u%B)Z-cA`Mz{#X*la4_eSWO|}6mf);TJc33fobw>cvQvR#Q zq>cH36xKLa=8+N4UYryQ_{*e`H612+Tm&D?t1Lc`!g(j_qLVK!%*ahlOhP3OrSFergs1l9%k>!HY8 z8*5Ad^&5As5y0r^=pT!_qN23-@83UNO46107B!q6WRdcwWMh*u&1m)Qum6r(%U9`V zSR8|!`E2Ru@2_rlAOh>`cuKxW7*037#NTbQv7`T5>pFQxw0U)e0dT(7CD4ziC7giA@D%3oj?kRHR9Wl$riHaCY-HFhs-!@gVZ5 zTf#aRkiwxOWGH@D|YjL zSm`PAKgI{$4W#_vb^mXa5&TCpe`_+!c?*7re+c@&wbs7a+>$rSgQfe=*QvM1W`WNx zyT|cQkH;%!+o$C;O$M_Rw7(!($ITuGpyk`b$>`Y0YXuBTcQpfk=)&$`Yt^;2YyQ+i z@ZD7#h5?sO?%*#sPcx!ddJ=M%hk{>oGr?4DepQ3QbZ)}IiY zj>vf<_&EbG2QBXfdBAtOWhMvRm4QKW z7y2@6I|ZmD-VP4$F-a1qT>S1`e`r!>*ayX5z7^LL#q1QHpDSqD%x~I+PeUKSudbGi zaqP%Z2?Kz*xVVMpB!1oNva+TbI@;RyS<EwsM(gHqM{08K3-lz zwMfdg2nr@qhXMBI0)(>k0K_h}S>Z6Oe++|W%DVt0M{oHzt7z|vk8!DrRAx*fVB4GZ z5diq!t-x=*d9wQJ)?Atdf0fDiVNv%v1=JP{H605icCxoeM2U%nG}{v%p~^1Hg9 zWc3m>HDwK0c*_p}Z6P6Rd|yS~i%W{#&C4I&yGOWxaPTfGOSsgs+QIA$t6mysSv8?T`(sKM(YFF#yR$4K9t)+#FeFBt$bOYd?foz3Qa z{*3Q1m~~Vf2I&cXT@sn3##1mka}{>}G5EzD0P90+Gm-#!_O$e^_&PW1}pfY+=CxacAaT8oNn}oR>1|-xV(zb0j|S zy#G@dqoDDeC#gp#*A)qU^9zV6bz!cEsdSF%)9_0ou((x<2U0$6LJ!hZ3HeythCYu++EK5ynwH&x5JX>)@S z7!?(@xLFSOevv*d9!aR8yn4c9*Y)e=0)~uBux%`Dr1JzX2gF?%mIU7w2!=w8Dm3y0 zK-`0{-O|V5PEn>7mO};r>zmvzi;TU8osHkzB~;euJal~b0W+tFz|Zt3inN_kgSDVTqgjC zH*xKrHgfFuNS@I^TGMQy>C}AlIzm7JFzkD!e)0*qHCJ9_)m>JE{M8R9xPJnlLc*W1 zjqX31-XT*e$Lx(Xt~6g|y`6Qe4IuXMuKCMeeXi4CDw#=~B}27HO->7bPG!aq3k%a0 zrhK=sd!HMc=@2WnPB1m}@M9h>{J`@n# zmPM{p-SqCZLzC!!MDE%gDj~hj^(`tcOPXEEZRRR6#g-FuT3xt4UY4FSvLdNXbMqx^S#6 zto)pg5YL+OMe#z2Sh)JELlR*rIu(yHn43HIt1oP+MjQbm>HB-jKf>AnB$_2+hHY9kX8;)apJ&r825gF_#6wZ4) z_Jp#i_^XNms2wMb!<-#hdO?_~xVdVeEHH*}`$#~>_2}br$dztQ>%wI~@guf~j2cOn z4*}5m2T!2ekqJ~UJR=|BeI&IfA_^zo_NbhuqzV% zLg%PBnFKA2sOCGM5kJmc47qta430`BFTH4aGtX z7eKt>6#|qKF{&8?5-qlxZ^^BnpTG&2-(<FY+*5L}vG0XD{QYz8Mo7F~<$2&akjlOy+iyoiu+%Nhtx8c za-_>UAeC75yL%m@GP~WIz&Sj6c17pCvtANT=-L*pY$iQHB02zkskU&pF9cK-F2sDWULz1TpW_I$l5$-jr@~2GB{dZYnTz8k%xks4||8j(Oeu7WAvrF;X z&8@%Q>{@Rlnmu_m?bgaQxkMhbOB!H8*{}?5fDbO~zmNWu&4u|4(5vi`Q=a`me#H8>a1JV>1rNQu zugd%F=Sz3Sum=jLs>Y_o=^Gf>Ojg+s&=6*3W(uVuBigM+|FqEEH#GeCu$|;=?1(^{ zDK9nq$vEOw)`Kc%x^ISnh-E1~if{op9smTJxxwSRV&WLu*1-{+^U)n3X%8qE#OQGE z41MhJ=TSP_9_|(pUE+tEUfB12B!k$;^MI3`ng(-schT$DuRk_0h=4XbMDNYlB{=4w ztL3Ui3~dL0DJW-%VfJ#ZN)2!Ti4LBeznPfZ3@jG2agO%}Z*ZrXuR5%TGHm`8k*ElY zK9UN}MbDT8A~<|rk$h>k4pe*kyQE@|Rg_r0!2vL?;GX%qeT}qT-Oi53Qplg-rel^v zIsQ|)`3al{$@-S-CnL}^H!hMb0K+n&bU4!#uTI+MUCB(7x&IQRc!6tqf83PkR<><) zg!UPCE7R;@f%s)faj`hd1S)IJi8PXUiYczA;mss?rHK5iI2p)x*SVt6G71e`RirFG zHu;K4TU?8{&X_&2PFQBRIqv)X`||>u?$`##!KYDjn+b7o_jPorZSCx|SNi%7h#^mX zx}YHmBn5nH)(lG2tPj_I6a1$8?4v30ac7qJmkDlTtRzk9_LotZBji&mw!5VAg!rOl ze#0OPR(p;~Q^QNFNa+6QBr@l!JmOdrOQNy>66yyDf2t~c0 z;C<3HN=Z(>CLu9WhE&>5LZ)D!-gW-N?fkd?9YjDW*4O**8yp}~K7NdZm}o4EME93; zd~O>qr@WuL(K#tXvBr{+)h7S(k@-IFyW-oH+; zzXS+_SuAXYROdcVvEu>9+tWQ;26&L_eYf&1bq@Qm2c#&!Fv~9}kU6YFnEj6>><6?r zqj=_N{v$ur68^ru`wYle{2$G_EV$tJtv8K0Mfs;r*p&oapd%wSr$VGyMbI5lgG+%L zyv$c(y^oo${Yxmgr>_2g%wp!B0;7!m?!NBUmuV0;>1X@-S3qpsnD|WaeSDe_iRbiB znE$0n?VBGyXxR!>O;lpht6$$MOua|01F1=QbhdUij0>RgHYH<8!O#Cl^I3)Y&#tm5 zG_nEK!}+o|IohW_Aqzrs?Cff^j5#BUmgy>A{06T5M^yf;U=7(1Wnj|snEb-k=!4;5 z;>E>;JHDs$ewKq=F27O3WpkdMukh20?0@&gB`cAG|8#uc^y=z1zLAOY&0f@XSI~m! z?@$FL&Aq$z-^rWu3mr=QP17)!0Dl>sN=V9K4D^DC!_kX{yc}!12eHgR=0KGh>)H0= zQzBYXL%gIUik6FxC+6l`E=2#TBtaW;ZZWKFkv8tAsX=$_9%q@`aw-}Z9 zzd9m-O34!<60PxAzio|RKFtc;*8mXE1e*urcI#kH!URCe?5SGn`>PJGCreAUAtP#1 zQ3EtL{#TcbWWLm*KkHa`Ja4qy0mV2dG~nP zER~hZBx?B9TnGUPIpm*M5@p>erJOT3_4;OSK5yaplf+CH%ogeYY6i)Q2^X&)BT1xV zWL()(4DRY88tU${YYX**EC7J;$Ve49jD>a?K~9aY{QnxC`!J{QD7Gk7lXqYXHQoE} zYvP_CZH?w?@59X&0so|m3iUIo&w#h6=|#)i&^tuaE|EplWZ6cy6C~w&dodAKb9?*3G70R-?CQRj?+U3SDH772}BeHj$x`? ziLilef){|mTFQMJB{^o$<=o;_9W69fR)M^Ih%SGV?M8sT7(u~VVVp2N-Z%pHdsiIH zj+*rGpvm$~z~~!&q0u*UvX|WYR6juO+0G3v>j;eDlRs{iT!BT{OpN(TQHETcb+R>QhQO#@kBL)!h^QR^+Kfm41-v%If zY3Z51pnzt`H0hSv1nWd-qOS4 znjL$DCwRH4|4l-$Z@=$%;UL%(x?bI5xSa$mBo5Vi^vDiU5UvOWvPro<1GK)J?99X* z9==N8UC6KYb3GCGrIWq7UwgUd{vUu?#)hAG+y)$LOc_~N-a>C)6B3Hs%jvNyEGVc9 z4aHx3_4C8Me<;OGy6UlU+d5N+pPrunO|tSp;x#qp`GkM?OZxfs+@YwbJfS8Uw>{Iu zM1S!gEH)+4$;9XTpR$tBv7!4v0HY1}`7hZ2|InfGTBg{P|A|g*GX94};0&4;3F5G? z7Rl=C%3pB52z?Ru;bXi#;C|$2G&&)1dPX)x_*WU;WcZs)JCxg0P<}`&Q+DhbOd%F* zoC>?f!_%>(U_}wOlIylH`jrp;kQ`~`iS8``jGv0Xb(t>c!d|(5iOA8Y(}nl+$4HBW z#a4F(Vw#JR!LzYh+!AOs;g+zlK;}G$7~nhD*l?ZSeJAIEY;mip-@ZW_TEU2sKB66Z zA{v=)_CD55V6OW9(s2@~*HyW_oeRhbLMws`fpOhjDdn#^=R=7ErF?W^ zrk+C)ku6}JMzqX%&yflDYxJughb+U?6y&a(i`&cUClNKfB$6nzb(m){elHDdeZ66! z8Hy>Vr*?7IlM{#ElWl%giM3H3r8)xlg$iBWUE-r3t zMiElmo?SM#DzZZqx%ta8McN#1X)GZ$l<3rXS<~Z!;>V9mT-hAI{-Q>dNFW0SGw-zwaZXY3`7NQmfyXS0nT+)L3QC^JXynp{~sag~S(4hCd$K0ap7#`_k zEvZ+{!*4DcS;YwP5w$LvqR&V#DN03xM4_PtndPuM&-Nayxw+7UeV2WM1(Jf3Mgpl z|H#7^lOITa!pclF97s}ged|iR_S2O|g|>|5;rWUOZ zlx=OV8L zYQt&d`#*5Fp#iRg#^;KaNT0rknkfKzY-`tsDw5`;qa5T29vbpqo2nsbg|CuwBE?7n ztzvNE+t_tU{j#zfrm`%!lW^Y;splz~(ME57Tq>Rt^A8)@X;cRybDHHBm5%F-V}C?Y z@fFH|%b_&Ec0KWLH;YP2)YR2U@$vD^r>NtHw^I&C2nnqr*$Wa^gCFFnXlYRbbBYZ# zq@m3CXBUfwLDOMx(JAE}((q)&5YYBBMT8Web-Lb)9NM=N6MOf19bf*Up4h`9^U4$t zvxTfrcYiwIjtA^wD|qUC_hcO%W0mU6YC-y7vAMHV1)pMB^vJv+Zd=RWBYlF#dg-JT z*LT92Q__D0uw$-dQkCD%hPo44`$%wPY=zYE3{QFb#>e^BO4&|IJ!2#B!5Xxlw+XvC zJKtQpKQ%ab1)qSRG6?N0kHZ`+NcJQvhB#IuKV};NaLoQA`Lm@Y>hhlE4Sp&R>^{ky zOLr@-ls=H$?OE}Qkk{Tgn-OG@{T!*te;$V0R?TA{zkW-FLDXxL!hA)Fi{S9hTu&EZr_IYbr7PMOXM&0J-j#g`>MhXOHw}R zr$+YNw<^za)~KNUb8%~V|7ci074Mh{5|_1qfSU9Be!Qv>KpY`jgq|?pTe9tm;WN4*Ht>Y$r+y$Xj_8G zeZqp{H#jsz(LnMb8kNA>zyPWC9Y2zmi^^OO{pnI|shjg|KfM$x4@wuQZ44-EkERM3bhZe@CGy13#}=e@dqYbhXhN% zvZ2F^3dgBKKO4WD;l+lC*`4BJ0Mhu{2C7ps{I%&ik>X;_$4{Qn054uhM?B$)Gs@@Gz|LG28aAhL^ki_YVVPi=?&II_|tQ+-{WaTr}kwx%> zJ5eAad$+yat3TW8-NzLr*gXrSC`glZYU&g>0hi(QdtRe|aq^g@<>lNwL}!#xe&3#6 zL%gjeQR&yOZ4iaOoaSh*S`R?yZo2njv}M7xNS({dv}-6qZWv0bA*jzo7Knt%Zo99H z3oSW0W2(MdGZkI%?k+byyB`-QQm7Cd8tUV&>zjDJ(8?^7>#+|m$tp+*&^I!&D>??< z_iRa{qrc6sXd|p|cDY44w|B)jRDht2_s-~CC)wO`%Io%`C*KVCjRdV+k6HrMb`W+6 zALnNi<+N5r%=_SW$qnX4kG)2xa7#wW?9|lW=Yv^s zk&yuAP|s&;dNryFtnz23O!3(HOXiG(5u4SLLa2%_0^eGBx&8(7?&SNrbHE4%lQ8|# zO4GI#XTdjQwC*k3o-_(wfJISbcH`9)b<3p zy^x`uot@l#e8dxJ+=|ZEEaE{!*3TJaA!1@;r~P`m;5E^kz085XxI9xc?59TRnn~^(jFIpID)u&V1%5>FLb(Fy1@!*V1Bj z5F0QtJ0aNEfhZ0X=Cq!nT245RS6SWe8yOqNqTnYz4PGe~AkKtc#*;cZ`H8f@zPl0z zuO8gQUPv2~E^ReL*hK&z)iNyMi`TtT>k&tC!uX;CDeZ;*t4+&MjTZqVXNG7xzSp8h z4`=h{=bWRYKmF6lW@>ipm;+-L4o2 zRIrjSw`ce3eKmYsf@M$NuK4YJ@Q{o71iZm*_4}19u z?B~speNwi@9JqnLB#lNLB2-|Aiprk`Cno=syVSiGBf55vpYHRQTF%fA5g{61Zz}A& z{ov0Iy#CE$GF{?JS4^0z+N{BrqCgt0^9e2hWMS&6*#>dZfm$oX|rM}-m z-@z4nqVx2RaeY150-B;tRLgxaP+L3hw70KMg5@D~urn~3^?<; z@y~#uGI{U%zqWnbPpYu-6$IncE>2cQAun^1cQy?rsJQ{r?jceoQq{alBnJWaO`j`~ zQ?Pa^F&YrOLrehVR9DkK`Y3pX<|=ezkJCrw+&x`%acrJa^!UyK34^6yVk_uAOtlzA zjV}`bi-ShS_IK0{m6{4{89VH;#u!vTVgt6iXG>H5XjfPkJ}U^?9o>CYw|F*z&55)s z!@>hjT<^Z#&=)%d5J|d5K>@w z+R#o7beNxJeA=#yYHAyIHoxaF_0`52SbWSpye{GJfdfCU{uzGQ97zSSX!m1tPgXSs z=FozI(|^M=F2ZyHlL=rXp=L)E8haOr8Z1^ljA|xRE3~pEwY&=r+WKawGp`+RA>4M9bMSj!Rq3AVKyVcba`m= z=g`x3u6enlAOUe`p&(np0I8TaZ)j0pY_7h_;0V*%i8f#sux)5)V8nX?VfR)TjUq6l zU-tACP&@{^fzMs8IglYkx7d@w6l~~Cp2{6aL=J|t76su<_DdG6zCWj;prmwGG`QJN zVSOWe1usgh1g~eRi&>x`yDp?<1fI3A=%DlHC0y>0+z|&e^I95cV+~1IoR=xz<4+KQ zNQxnPe`D}R@H(xa=i=ZbYh(eLSzVBXUqa@F_}4je}BK!5U)k$(F=It{FoBw1=Lcda&W|PD)^HY*xUc}U&bxHFKRc43Fm|9V99A0nc<$kl- zG+#>MR!I}8bvj>YIEeD{V2Hun_;_OLQbJ*qs_-ZOn){-ab{(P=NJ-xY4KghP1~@^) z9f+c8hl20@g6E}&X&$?>n5jI12G%9LWLuJEXm}c~ksFdjrh5Od?Fwqu7Jr3mNxxr6pPkt+{Eq&pfAGch<9Vrfq&W z6iAYAMB^-5yRwvNmc}|Sb>GTqmIUQ;GY^G}BlWp+YPw|?4T?90E|z}{wxRPb85kbA zbQo(Z<*HCx+Qa8w@xakNxxSXm!DBPwNQWM+=!8xl`RY0;>m-`(oI^!p^?P2ZlE zxpLiXMS_Ml^eI^8Sy9X-79yvWPXWDL?<;3D==tfqOy1VG(EEiOmqELFeTdwE(h?NdsSS0CWvfv@7M;skF0PW<58#Ayr_#KTp>z9+0$;N0LvF#cn&EK{)dO;$ zZSps5;e*{T6|B|Xwx4hr3Z282Qc4ZT<{4s&oOT~PP>I}r(0l%fT9d(uS8p=0tF%Om z|I?PNiSB14xJtuiPwW*Y!7Y}Ep{>5*@3w8n91s@$;~+B8ZvE0GoNb2^pC8fGPKtNGmHUWG-Jl=i$c7 zKWmSl-F7tGChciL9h?PFW;>6Q>l7rYKh0KPCL4#m-Fvp1udEC zSB>GfG`&O2ADK4d3@!u`U!j)Qtu$&N0d$WlAN}x1=(rVU4Wzw~c2(5%Rxl0oi$EDV(w4U=V9ikb{ zo)-nQ6%&7+Ntci>;F_3_PlTm8uNVDc6l{9~y>HCS!yZZ!VK#XvNiTHGP{1pXqCi2f zmClOS__gzk_seLun);cN?oD>lX5Xf%siNFi`_-=NiS|d!oMy!DSGR|AZ}Dz?f?EAJ ztKHY1)8RSpa_f0uk7<7EH2oZ|nEnYAZp8Wtry`L(c*yUbXUtC5Z8XYhNbz4l`KZ0^ za}?3NL|fua%AZO~Vd(;8^vXid1T}UaSzd1pr%#oB+sVzv|8T(F@57O@S~v@3I7OZy z1I3ABSzE4#taFqS7bPXVm2$W?y@m`gD+N7Q7Zz7m!(U!k9MrjjVBK7et(`uWZcmL#hivjN@!t{qCOOwdoU=rw^DvoM1NC5z*XaEN6$?V3tha zkT5-31ubAxP{#c25UCubEQ=RuDU6aAIBdW1s8LouFC0O=~fS*3jt|2SofNKXv zusIm&bzim0RR-K;3#(;x49+FH!@vO0i|}cukxd)roj6Tf4Wk7MQ*-f z^``N4HH*lurz*OalJ${3be!W6h0`?ExhIrom_91Lf3!Mai{);mTQtXU1dpJ7&gbdf z&-ddrB##Q3ip|ygks#=`^_KzI89LpUdAUdtgFSEk=0y$ZNt65zech85x3#~r5X~$B z09$`6TJwBSZ51!}%fOkw!)^oI)xAIyY3Q5n#QhQUpW4Htu~wD%O{4zyGrow7?2W?{ zZOxv%`&KYzm43*crHHDXIwF z*mgk{SY?Kxz?A;1B+JXgcU4|8nQ0=Y0_-t9%8$75YJL=zbPIQ6`)*BvPJ!~7$FUb$ zg6=yTC)dJRUND~vOLOLye3*T1FA%nTd*{fo4pD0|ECo?Ko1`1Aq*2pwxt+;VH~S_z zqV&AZ`2ftj_5cZsX3XHGaI$Yo0;h$9Ol4k~A z(p2uvgX`1w0K!WA6AL!Sxcr$IN8{9=C)5pC&fs0r1v!}SekX`N<@+flSq=bPfTpUl zKDQp6Er%0F?ctFyRb;4*^lCtCh?*g%q=3F#OG$+Z3ooZhl<5O@EcYGCpLFPEy&jn} z0A1M06h}E*zOssjji#GV(p4K7grrmzcpM+OOf{Ika=pa<$iz6juBu?5uvqry3+B%w z)6FZE&R4_@z+Y6JjVifPw;w$+C(lr$$VU%ns!Sf`IT=Jl4GSqF-M5|qypcIENfA*` zJzG_P1BdC<(eH_tEJ8#j(2y%6%8o6ScyOXe$`!{p<~pM6*z3Nr>=IUK#U!`*=z~Sw zn+T4Wc9akLen}ojUa5hVQX0p2z$}wV#Ov9bj?72RpKVRnIlqKXqbbh*N`|C+-!>XF&vOlg4R)Vzm zOsM>Pc7Q@mfOKl$nc(7AnGnX94EqheLW>Q>er?SMRvASmnZ^DO54JhNC)|WG2@4a< zNODQT*(9m`^TYF>-bCiaHV&(Q6LI4&Guo*(~pCpnI9aj<;%m zCqkmNv!Dm?QiO6*gvQ6;b(5k7orn$sOUdCfPh!H+K}_yMy_N#N<%{)|Nti9BDZ>c= zB>Wuj{?{}L@&6gW3Q%4Qa)r`RA^w>{uS`a&M0hlxn*P_(ZjqnD{=y6-%2QXqsj9t8 z&4~x=geOpMFH8L_nm>9lu3mA<+-U)c! zgg+eBXEqsSZEHz{!;wh?6bg9GK-lsic6+V|OMjC!SRC#E}%B3TRL^2Hv|zEWt!0%0x3SUob-Tsu{7q{ycYOXU82-^7g(XE+K(+ z%-+S+nTSi z^G!2tEcwgIviSlA0I_}S-IVYuZEVqZcJ#2I(DskwU5MQ~rb!BMiM(?lw?XO!iCZW3V#Q_duLSZ^}R3HcL}{ehV!w|nfPQY)4$I;g4M;L-9K11*o=`i-u}6b{7Kt* zl-5h^N+O+K3@HsqDq?MSF`W+9!s1!Fc%xkV;ctqVC8Ew-azA;XN~yM7`Uz({N#tDU z-(}!3QKu(O*z=cM4y>%l_b*$3pp;IZKwGu+dv}n_A!U8xgW~DSk4(55^84a5{vL|( zvk`kOzh!$ssjj1Srz@%8stY-)8KPun#s)z+U2(Aa`|DHuHs%V-#Oj#oZ$o)?%wt?< zF4#X(;)_34xC2`~gRa8JMBy6-9_p2TYc@dT&XuLPm~p~>=C(R49;e2{&+nWZ6e6uYwOePS3j@?6cpfbqwVAHaAEEfiGFPJFyeq!ctd z*w+y2GSE^|nR5Oo{MnbDY1`v?8y&&J{F`fWX$#}83H~FwTemKkZjN6%+kSyJm3%hG zEb*+=5;K1d08~2fOpF;8n|=SA6wU#MSE6CvaI*9}N9MsKeOcTX%?aPbOY+_*kEy)c zSO=b7+vbh^WRr+j{@}?FH3#lzPSaoNzI1MkY~!f9J-#;uvU5u^4AwDbQ)VeP7Djoc zbhH1G$6v-K3v$4Vpo;h=wjXUNq0tzOaVk|1ax7(ty|+0 zCT;%WW`5&&Mg-p3LrmDBX&gFaM*XKGCQ1+ycAu#ny;_n`KkbSoe?G1e3uvvOqU4=WzIEgpa(LC65 z`&+Lz?omuDY<@f9@GpA|>t?OvT~~K%qmt?7?1H6>At{%c%=3(doObZQZF}g&1HDJy ztO@ffW{rx1iX;qAs~GkhFvI);eR7dSC{w*uUwX??DJ@RC`=twqUEmZZ20pB?gFgdB z(tN(4<3QzTZ0(t@SnzpCmE}esOmCLEy^NTwshIdiu;ZHV!TQ{aMMY(Z7Yz$AQRX-x zHV_pX;<@B|hfa?grtU-YtsU$8>l)IKXy>D$#U*IN_GA_G{AjE7&68{$XcJ?vh5r(R zno9BvPCKQFB1OvWlGMuZ?BmV^km*b{=HxSBpA0tc)Qj#p1zAo*Q#_ylTKeb=;V-d& z0RMG`hkS1h0-I_J;oQEixLXrq@`dtCu#YP|S3QjZOW4D?y3o77_!Xq}q9sDeacAt- z{k7Zk2fIAd?wC$mt)5^e2#Af%4+&K9p%s>54H<8s#kFRSc~l?V&4Gg`XvYgO8x^0o zHjTchV8hM6mZxb83)S>OW{0xC?7OkV*?KGLEK_8a)m(nxyPvb#?VQwx!LbCBUOn^$ zZ|WH0J1vW$&yk~PV0!$!cib8(y0$m1Oio6;0vE=f(|nt7^e_&&`>BrnS(aJ_Rq}9O zunvR9Tg=PJ^vxbIZ47Ad5KH9#DCD3SJz{TlVWrVrT~2)ig8(xrAt9Pvov$tYX+fFe zy+&>@2TXODvl+(G<7`iRvb_;mLG*FV#R2b%sxVFXZCPqbwmfNWYNS?C^mnwGKFSix zS)f^Qm@;iyT3NP51YASUkN{qEPH*?*45p?m!zo4TGyUYi_l)&j9R4npv0ybtTzw@8 z@auQqnJ-RYXY4d+t#1Q$nuXfpCL}xv5!{$YNuK*2fEXUuPr`4S78%q@2zH@l-X|n{ zYPKqsi*c?nu`f~PH`n`bCDR#=- zKV@f&CTrQC^55RS@%0AshfW7DzpisNNH+VP2mv3Q2Xn7lg_A4Ok-`Wl>~PMvV|K?h zn*h96$VrtrnPIL%tP_W1KtClX0j{z9xT9zRV#v+ncCKu~Go6%(`k{Ud@hzMe;E(?J z^8cW_+H0c!JX4KVCW`+D5MJ;67uXSCsNMe;G7RO4zkpGKN%v3ojwU^o(m!vk=8ue)5Py=kL<|k_TuIFo)u30r_n(o@UMWfskNdg( z*Brid&&TL9^G3G!-$RqpYsbMhZ(6(2qHnBD(}i+<7ADNJ=}tPnG#}$>TS zCp&W|6Mb+pKnBFs%Rm7O5bF)L`L+2R-*}Qh@r@*dTppva(*zkug0SDee}74Q6B*eQ&#mIVHpnMrRviz1zeHSKuHEuY zQ!PL4wA@JO`K?Y}_%z0Xw26T1>R>+$3x#rFj%(sCOU@yuhldZv=%ju;DtE9xSHC6t z{748;T0aqM;q>{~31^mGrr)Xee`54}o$X-bxHR;3$6@ndH)UpCXJlaD!%dY~!bb3A z-G5VCdzK%os;jr`%32Rw?Iqw-_v>g8l$4Y`R-#<;@@8KgDooye`O=eLP+%>})RaaP z+0Dn#U*6NBg_Ncs$lWbQ5cXRvC(pO#7@~IRo|OeGF#Z(J0tQl?;Lhc&r)yyZA2hW< z5(G0ymRx)4QCqnA{=`%D7X(1Su^g2tC1m#Y_7^*sPe(96Eer|{3=C9zbzxC&j81K7 z-T;4tN{L+nEi!l886kw&hmi;+-vyH3@Cevi?Fdzf!3#lPvRC>JAM+y-H7<#wo;81` z`!}9|JR}$PI5Q`^us0(IM=SyP{{#AkJPTI|h!{ZhpDlgsTONNE`Imzd(9qVlf0!hE z=>N)Z0nI9>Wrnlc=Jp{s8O1OOGYJ{~BKD)C z>nq88^o5#!H{t0+7#*l&40yZt<#+4NfyN?#fm?N9k&)!T;*&mBa8Oce&IiSoL?kL? zti+~a77bfC+1cMBv0H8yx9i@C@lp~0t;b&)?M>n9O~W)(O(jb|3}OcY4%f3C8A>gY z@OzIQz4N;q)n*gIGAHsPkdF0Zd`z2@DKoH`{HHTuGKNj!OX{EH$95oh$M?{6T>&oG z>Bv!C5%A2;j-sopiw2+}gJeovzvRNU{L;T!pNxs?H6!R0gLK;z)y59)bPbf_FDY=U z{amZggv;LllWK2xns8m=iqUro#IegC2q1sHCmPi7jQqYX^P7^fO#sM^H!|?iq=6F@ z16V@SB}s0!UBRjO{Db^kkx9BtDzt1KYpo!FL=uQbb;!K-(=u{X8-7P(r*J^l6*@sXuYr zXz+}1m~LY%(nxG7OddTKBn^BXH}pcD=F^i0muiN&INs}nmwn;Af^V_03>rn4L8@<8 z?_k+<8I)=qEFDo9?6}hjjtfnG(oi;kt@CXgyHH2Y7adjd(1QjZWa4z!Yn3ZGX5#P( zR!{#M4&lWQ`ZORpPrFJAVUOs6cEs6D4knebMQ^F6O*wJQO#lV)<&7k5I~t}Y77Ih7 z+cy#5GtcisePUf*Z%x##pXF>dWOeG|nhxq{5V73X|3#**w-5u&@06;=83Zgb_wIjk z#St*6B!RW#VmLVO9@Mrh8{V}I!aa6nz8pI6SJ?V-CQps~2wD{?Txk)h;lgKpY32_$B`+w~k5aj)x}aCsIpT1T!GJMn^dbv0N1B zv?z{4ImXyFu_S8+d#!_}iO}@l`q^=?-cAHjXRm?v`TouH=;yOzFA%@t_`3I+C`|cE z4_N@t?#2+BFQROpc5rpz0$ErdL zii=;%wk4|SE^lAr9CY%`Fs|5X#y+n4Neg)GZ;XI~&(F~e-6cQtp43!U_MrzLNa1zAh}PPNi+8zwX=?Kn%614>YRC0Zn;!Jb+UCX1ws1a+vL-XsX%Y zIv`m;)ci*|D!z9p2L~y{?5Bmmu~xrbPlOC`#(|*E{L2vZ5_q_iqV<$Gj=A;#^#9m6 z36p#D5yww_6w=E*Y(HNF&@7pZp2dSv%o2mbql;NYh)Q`O$z|l zN~x}JxyYuaOv;vd(@it{{8iHQ@#dFj28zI6H}L~BUDaE7BXN}pEYnV(9UvP+nC8S|+`%RETkUumiai09cWzuG! zObz7_=cd30#j7`{h|i9;qGQ?Re$R;gGK>PFqQ+~zH%jfgE(1-38&Ri0UhQJi)x@*) z=)cNM_P|gl`tn<>&Lnr_8TtqDlm@aJNdELmke8o7dMtw)3RTzVwrY5EV;e(V{)U>XGAz=jdwogFVB7eo)8TyJF0c2TY3X z-pwUGX`x2$t_1_X9yafAtuo={rv<->ZL-v|0`7a?YzmyefW8fuoC>EY$tY*3g|=&! zVH3_ibyHml{A~~GJDXLkRl~M^zT4Y-S!mH&q2yo#ZTkT#olg|8YEj4XT= z5EmD}dTPs$Y=o!d?5=k3{3aXklh8;k>KLP%UFtUcB^MG_f5-vMZgb8wsdG-;3@vxS z&n)!;o5kb;!OOnHyAe6=MbdBqe1P7Shl_ zcDi$EM1%iQw!bkfb(>M-nqkD9WLtj7$GJ@W2rn|Jpb`@klX)Tgit-Jf5-_TEBnnVH z{59VeJ#nx$B<8beghb*qGi~CHK_^;8j)I@V^DmvBI(;W4oO;@!Eg?X>OBaVY3X~_= z+;V9fuEjDDU;zZAhopA+A2N}%GLvNuFc5l6LrjVom>vPR9HUT_$+#a|q?&3aJ%9QT zx0QY?K$-@#o!&l&DwAAE&G))MtxHQwK4XW0)eG8QVMOh3Dv{-l`{$YRnNz|TmzjFw zcjz2iR@k*xPOCZ|%9X+;8XFs%QtO2kCOMzN%Xb%tLBo5u6YpIlMU9Wh(a&|qlQ(5| z2}HlrSpx?|gG8+U?@H$0N@Nv(luSdP-Q}43S(Xri$4^$Qwx`r|`W{EXHsYeAReXG; z=I7@(m%INwfe;KWf?xiVLdro*G#)O$CIidvz<6AkiU9lK6BBO$l~G(kt`nb;Jimno zm|AXRX8C;GCT%p~Q{{Lrnw2o08b+$rB0+WyR)9bf{c)5O=itrmdr%R_75eEwI2fBskID9>bhkG#d?Rc+f z!}n0P;LUd8-?zB7tsg&roLtl2mhKK(Tq=Zy6OdF?ER06-hHmQS({O7-gDBtmLJ?h< zRlyH$LRU#w@${RO{Lp}Xpq4^+IE0)|G+QNoz|oH25lrHCk<~M|O)Et?aI0wwp*tM; zz~xjA!^0;v*O@f(YHaynr;urM-wGh`|6Wi@hR)A_ zcXnC6;j=SqQ-Xd^;FK4tuI83jd}1}$=}l2y8w)&7u#_e289_u5LE+Tj_v5w!Ao*?u ziwT;fv!n$1%}<iw?k(F(a0f!5 z4mby8F-UYvOLnqCR?>-pkXh8Dtg|LN@%+C!dmEco-%3~1t5lTd0{vd<@sZIM|4MjO zl99gSf(cku>2zKHM~kPsfrW%b&uBSXz_iE@JMH2W2&!~t$L)=|(n{b^cY}WZfK<9c zIq)dd>bU4a!|W>T%JTs>ZVgS%X5=66nFsE}PsyOq{;*2$yx|!RsewU1UtGCZLL49P z_>0cy+_A13{wFhmMO*(^oqLYTQ)x1u^h};Gi&|aPkWu81VJSQlbH7k$ajctDz)K*1 zaKv(;q{uGctjfpn4Px19vv??|d?MyqVb;3Cll3hqFCEA5@iEw+{u%}1M{apLM-uzd zApWTklH8s@5YM!TQhH|cX=r~iMk}X1N%XW!7rwW#l=O<&Xs20^S~(tnc$tLATYE|* zoY-indCT}hASNLBPo>D*hYx;9-Ouct)x5mE4F!RXA1>w+LIUyw!HZ}Bf@>fqBm&;- z!%R*gjksEn*r6{qbincPZNz&^2e8YoorxZH2B+beqObLQd3PRdY1tf?IK;#_7KROQ z1NRE%eVKt%a0)1uyAPU$xFHp2v%sjiTY8v_m2aVa{V9_1W4E!gH)Lxb-4f~P z`PtyBzrcF`UspM$Z27bd6p-4|{2^(Uq9xKQ-yc3ax}r^+MAUC17$&AjM5v2iw>_o+ zB4c7gWnbJ8ZHm6L0#W+ZN6SO-3SD>urZ_UT(mcwVaFn!kIO39%_5bvO052i2-2es# z<^<&T6i1&uvwr!Km?O>~GKr+6gV9f=flm(V!&nA@vVIDcnJGKwA3?Mryi2-_l59@&Jrn?X8?Yh!F2bw}Q3 zkZHhvvBF|fGA$?Mz>va$W$AZI&@fRhU1{V)$%Bmv@V~$mqiPzAYu}fi7dv*2P6JPM zbU2{1kvDYq$X_Fw3z|PH?iwZ3P(FFzk)VNi(MhvSbvCuD09~CoE#i)G+?_*zM;05l#KTp3V=`L29SqXR){LxAKBe%wKz76J_t1Cfuh(CV9TCvAgUW0@Ak6m*o1b;%>S32k(XgKc(#s0J@xPe!g zjazRV?M3n2tj2)jQr9+$$I8Y zV_3!;YfTW`3JTN0CnTm#9yGxc4!u7o6(65eQdPRB(uaVtHwnk*QxD+-1!w7!+B0FQ zSd1AhJK?pcYAX0+6Us_=&!js-SDcB|UFE_lfF}27SvTTFvw(oRkWdk!E2`<&leKQB z89J6}J#+ahNf4Fnr{e(oojYywFHqZ?p`)+wP8B!CHorpUh!BNx5fi($a5lZ7bC0I{ z@v%6E!xfC>Q24!gGmEx)9DB~Z@e2BNZ&B3u^z{BT2dPcN+@*$FDk&p=PI}rj4X(F3!zRmnemc zVoOg5z@04cjutYzqk_4s;YASFnboj2ZLdJlGQaq0Etf4?;DXUqZ5n)FMRAEGKREU$ zph7J=kv^`kl3uW52vMHmc)7FHc`J<27^jPROH}y1=GC)5_dL!k5j&*?$pO2XY=IdN z6Z4W*)gi8+zgVu%(ryr-+NVg9laHI?o-)goydYVUjp83|kXW|j<-RYnSJu|nqQJM5 z`gQo36LS|%!%1M4|Bno^_~3|}qDA^DB_dacudH7W^0$(-rIls5s@5XnZUk(1x(-xQ zR%Uoj^Qc9OUiC@)WSKv(+1qn?$E=!K@_J}b{->?rj~_n*h;QF`3oVf|==dye{1Qe} zQ_}|7(z1@6opy#8`c=44hW67SLCEa)Cz^%nO^A0qDGMtwsG6Of#7{TWWjy?ZB6Vhj z6WDP&el@=*?PkC&K4^JsKwlG&wtN%#<`cfcbf1dof)aGnI(a9&vSD09xBx_*(CGF~ zu{ndD@ChK}vzc6d!Dy+fs&Yw64wsa_s@O(;fk{eUmGj+JH8y4j2|SODtt}A{_q#!v z{ug~`6(^_21hLjsV-P!u@@jOS0}($TUskeKTkK}Yqe-N>gLSaq4jiv*ovAb^#*@^ir!oga__lmqb#cCX^rstG1vj44m|FOgrW46kMko_t1;n#@jPN6~CVP>J>|L6)a%y)lgHqqdpy`*fuO~H%eHySI_xAf{ z`66&DV51*v8TbSx5>Z#?SDd)-Q^*XJe(4uD{=>A&HCe_Z)1`%*x7V%*E#~(82z5ye zqj`3T42ZdNFaSyYyY8BDg(QPk@hOn-{=E@;W?IaDk5B%@>ele6k6LerL)%zdppRT{ zs>168{)dmAK8+@r6Cs9bMIDHlm#!C3+W?^`4LEu9v`5fbyPtP%RCp z7002VXJHZZ;|B|%?kDcD#TE_Wp`mis9<5b7Ro1O!WK9O<03vAe ziL>rxtUbDs(589X8p}^`w2akXwiJ}u+ZGl+8FRskmmP5n>Ia9Ooh8MnajDp@vl5K? z6_|S8mWTrHs1TiY&J6Vb^st!68qT>*1+ny%- zB12SvmaplzEsJW$6q%Q|FTIFUor=rQ{o2ZN>3H&wWu@~cJw3hI=w50NZ0yC6ea=sd z-~@~kaM4YB>adJAX`G>>S@?R|fHT>~Fs7R0z(5y84lSSUskO5(()KT$kIkO8&tkjS z*ow~klDyy8qU4>a8@FD1dbV+C&&6>ON8BfGA_N~P5EnE&`g*>DQ*N#)WLd_yn2gZm zG^=(emh(+*lSWYxzF!1sN*dQ!x;PF`d5&YS&bmY0iMOvTE}w5%N+?+(snZN_3%l#* z*$Zg8;Nw_B#cZrRK6{7ExUZ`+16NsL((xC&=^EyL8yd23jVY9a8S1TTioupKpRoi z9Z`tQU|)|yiYAuGA>%q>TY`IZ{`F=?xHMi>+>$|wzzz;!gRtteG?|L*2H3K-Hby>d z4P080In7Gz?{Ulj&G-fLb!N0jq^N#(*Ev;3DBd)-q+@cO6{j zIoG$9p?__41dN@8SFhDAWXU;CUfw}wYSM@O@M8zE=R}0$+3Jorm8)E*0RV=RVQgzd zwymTct6FoHIz36e?tly4>*AjeFhEX#q>#OPWoO!xz(LsDF^~Kv8!$=dbeLi;dhE-? z3>L!6sec?#yF7-sf*VtKqPypKjz!%2j>ALW^ZOE@y?ypc=bt|nb+K%~1kjSSu+aMr z+7x`KZSpnh?OTS{G`0lbztTFP2ug?Jaf!ExG@-`NGz1zyehgmLNmJZXEIjd}eE(I> z_c}j6xwW;mPLkS0r3EDuGdiW@7`I${&^d!F}SVHX`J+HyTvP= z?U<+IH*I7=Sy>$-$JW=ho3EgXfSzU1pdhe50^TO(DtXYGA`QKAL+b(;-CfwD7Y&Eq zgB~>Eg-#E5-Ns8p!o$Pwu(5pjVpJNdyN_s1?nW%E+r@Yf9@jMwWoTx%pU7Soz#fz9 zMl`mzv^Ziol}YG-@P}M-1<1K4zM|9v8DCh zjqgt`3w}!*9dK+J+kT+zIw>=w97gCHlhEl9{nMfji5#-mu*(VbGg4PoC3*1Rft_W7 zoX7Qey*m8!mMTF48A8FfNz4^a;BXF@@%x(ln0uOH*^}qG$r(Uj$S4StYOSDPsXy-`I;5{bz~bGq2sc`@(rA0Y^nlQ%owFnr}K<)8xa{R8@ge zPH5h*IjcsvI&lWR`0sK8ly)-K?IGW&E`8WbNeH68ULI&FG%hCrsX#(vA%ZFnP9_Qd zuqa?496}CAE~t0Uv`-E$~9N9SuRa!(i#Qf}o2H9L+kvMFPt zDFi|^P6sJ5DhqvdvANJ3-85k#;~&2i{Z2K-HX#g1NcgRmmX^)X_qK>RKM6WLS7u-3 zhvi`PVr27OVBf`V|X;6d`p&L)W X7XQnC>JGz8fN*K5>8KXpvkLuRP?TA1 diff --git a/tgstation.dme b/tgstation.dme index bd08e84b..5cebfa8f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -890,6 +890,7 @@ #include "code\game\objects\items\devices\camera_bug.dm" #include "code\game\objects\items\devices\chameleonproj.dm" #include "code\game\objects\items\devices\compressionkit.dm" +#include "code\game\objects\items\devices\desynchronizer.dm" #include "code\game\objects\items\devices\dogborg_sleeper.dm" #include "code\game\objects\items\devices\doorCharge.dm" #include "code\game\objects\items\devices\electroadaptive_pseudocircuit.dm"