diff --git a/.github/alternate_byond_versions.txt b/.github/alternate_byond_versions.txt index 7b50af46885e..fa7883a31aee 100644 --- a/.github/alternate_byond_versions.txt +++ b/.github/alternate_byond_versions.txt @@ -5,3 +5,4 @@ # Format is version: map # Example: # 500.1337: runtimestation +515.1626: runtimestation diff --git a/.github/workflows/has_515_compatibility.yml b/.github/workflows/has_515_compatibility.yml deleted file mode 100644 index a026105d7c20..000000000000 --- a/.github/workflows/has_515_compatibility.yml +++ /dev/null @@ -1,17 +0,0 @@ -# This workflow was added in the 515 compatibility PR - https://github.com/tgstation/tgstation/pull/71161 -# By having this in your repository, we know that you have that PR merged as well. -# This will be REMOVED when 515 is fully compatible and ready, but for now we need this -# to prevent merge skew. -# In the future, this will be a more proper system where commits can be added into a text file, -# but that's a lot more difficult and I'm in a huge time crunch right now. -name: "515 Compatibility Pass" -on: - pull_request: - branches: - - master -jobs: - has_515_compatibility: - name: Has 515 Compatibility - runs-on: ubuntu-20.04 - steps: - - run: echo "You're ready to go!" diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm index 012ded7eac16..e1eae54d9437 100644 --- a/code/__DEFINES/_flags.dm +++ b/code/__DEFINES/_flags.dm @@ -289,3 +289,5 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define EMOTE_VISIBLE (1<<1) /// Is it an emote that should be shown regardless of blindness/deafness #define EMOTE_IMPORTANT (1<<2) +/// Emote only prints to runechat, not to the chat window +#define EMOTE_RUNECHAT (1<<3) diff --git a/code/__DEFINES/alerts.dm b/code/__DEFINES/alerts.dm index 1933b592d55c..e6f4feb259a5 100644 --- a/code/__DEFINES/alerts.dm +++ b/code/__DEFINES/alerts.dm @@ -47,11 +47,9 @@ #define ALERT_HACKING_APC "hackingapc" /** MODsuit/Mech related */ -#define ALERT_MODSUIT_CHARGE "mod_charge" #define ALERT_MECH_DAMAGE "mech_damage" /** Food related */ -#define ALERT_NUTRITION "nutrition" #define ALERT_DISGUST "disgust" /** Environment related */ diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm index a027dc61adbf..7d0fbe92ab2b 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -12,10 +12,9 @@ ///from base of atom/get_examine_name(): (/mob, list/overrides) #define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //Positions for overrides list - #define EXAMINE_POSITION_ARTICLE (1<<0) - #define EXAMINE_POSITION_BEFORE (1<<1) - //End positions - #define COMPONENT_EXNAME_CHANGED (1<<0) + #define EXAMINE_POSITION_ARTICLE 1 + #define EXAMINE_POSITION_BEFORE 2 + #define EXAMINE_POSITION_NAME 3 ///from base of atom/examine(): (/mob, list/examine_text, can_see_inside) #define COMSIG_ATOM_REAGENT_EXAMINE "atom_reagent_examine" /// Stop the generic reagent examine text diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index fff19f397741..a044fe75892a 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -180,8 +180,6 @@ #define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" ///from base of obj/item/afterattack_secondary(): (atom/target, obj/item/weapon, proximity_flag, click_parameters) #define COMSIG_MOB_ITEM_AFTERATTACK_SECONDARY "mob_item_afterattack_secondary" -///from base of obj/item/attack_qdeleted(): (atom/target, mob/user, proximity_flag, click_parameters) -#define COMSIG_MOB_ITEM_ATTACK_QDELETED "mob_item_attack_qdeleted" ///from base of mob/RangedAttack(): (atom/A, modifiers) #define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" ///from base of mob/ranged_secondary_attack(): (atom/target, modifiers) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 25efe87272f3..cd510b540a50 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -453,8 +453,6 @@ #define COMPONENT_AFTERATTACK_PROCESSED_ITEM (1<<0) ///from base of obj/item/afterattack_secondary(): (atom/target, mob/user, proximity_flag, click_parameters) #define COMSIG_ITEM_AFTERATTACK_SECONDARY "item_afterattack_secondary" -///from base of obj/item/attack_qdeleted(): (atom/target, mob/user, params) -#define COMSIG_ITEM_ATTACK_QDELETED "item_attack_qdeleted" ///from base of obj/item/embedded(): (atom/target, obj/item/bodypart/part) #define COMSIG_ITEM_EMBEDDED "item_embedded" ///from base of datum/component/embedded/safeRemove(): (mob/living/carbon/victim) diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 5798fd29e82d..c3cd691afc8b 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -78,13 +78,6 @@ #define ui_language_menu "EAST-4:6,SOUTH:21" #define ui_navigate_menu "EAST-4:22,SOUTH:5" -//Upper-middle right (alerts) -#define ui_alert1 "EAST-1:28,CENTER+5:27" -#define ui_alert2 "EAST-1:28,CENTER+4:25" -#define ui_alert3 "EAST-1:28,CENTER+3:23" -#define ui_alert4 "EAST-1:28,CENTER+2:21" -#define ui_alert5 "EAST-1:28,CENTER+1:19" - //Upper left (action buttons) #define ui_action_palette "WEST+0:23,NORTH-1:5" #define ui_action_palette_offset(north_offset) ("WEST+0:23,NORTH-[1+north_offset]:5") @@ -97,6 +90,7 @@ #define ui_health "EAST-1:28,CENTER-1:19" #define ui_internal "EAST-1:28,CENTER+1:21" #define ui_mood "EAST-1:28,CENTER:21" +#define ui_hunger "EAST-1:2,CENTER:21" #define ui_spacesuit "EAST-1:28,CENTER-4:14" #define ui_stamina "EAST-1:28,CENTER-3:14" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 36931380a039..399ee5807185 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -280,6 +280,8 @@ #define SANITY_LEVEL_UNSTABLE 4 #define SANITY_LEVEL_CRAZY 5 #define SANITY_LEVEL_INSANE 6 +/// Equal to the highest sanity level +#define SANITY_LEVEL_MAX SANITY_LEVEL_INSANE //Nutrition levels for humans #define NUTRITION_LEVEL_FAT 600 @@ -490,9 +492,6 @@ #define ROBOTIC_BRUTE_EXAMINE_TEXT "denting" #define ROBOTIC_BURN_EXAMINE_TEXT "charring" -// If a mob has a higher threshold than this, the icon shown will be increased to the big fire icon. -#define MOB_BIG_FIRE_STACK_THRESHOLD 3 - #define GRAB_PIXEL_SHIFT_PASSIVE 6 #define GRAB_PIXEL_SHIFT_AGGRESSIVE 12 #define GRAB_PIXEL_SHIFT_NECK 16 @@ -760,8 +759,8 @@ GLOBAL_LIST_INIT(human_heights_to_offsets, list( #define WOUND_LAYER 3 /// Blood cult ascended halo layer, because there's currently no better solution for adding/removing #define HALO_LAYER 2 -/// Fire layer when you're on fire -#define FIRE_LAYER 1 +/// The highest most layer for mob overlays. Unused +#define HIGHEST_LAYER 1 #define UPPER_BODY "upper body" #define LOWER_BODY "lower body" @@ -802,7 +801,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list( // BODY_BEHIND_LAYER (external organs like wings) // BODY_FRONT_LAYER (external organs like wings) // DAMAGE_LAYER (full body) - // FIRE_LAYER (full body) + // HIGHEST_LAYER (full body) // UNIFORM_LAYER (full body) // WOUND_LAYER (full body) )) diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index ba22075285c6..7955f3b5e1af 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -107,5 +107,10 @@ -//Used in visible_message_flags, audible_message_flags and runechat_flags +// Used in visible_message_flags, audible_message_flags and runechat_flags +/// Automatically applies emote related spans/fonts/formatting to the message #define EMOTE_MESSAGE (1<<0) +/// By default, self_message will respect the visual / audible component of the message. +/// Meaning that if the message is visual, and sourced from a blind mob, they will not see it. +/// This flag skips that behavior, and will always show the self message to the mob. +#define ALWAYS_SHOW_SELF_MESSAGE (1<<1) diff --git a/code/__DEFINES/sprite_accessories.dm b/code/__DEFINES/sprite_accessories.dm index 9c9471130e6f..a4003a31972c 100644 --- a/code/__DEFINES/sprite_accessories.dm +++ b/code/__DEFINES/sprite_accessories.dm @@ -7,3 +7,9 @@ #define FACIAL_HAIR_COLOR "facial_hair_color" /// Color of the sprite accessory will match the owner's (left) eye color #define EYE_COLOR "eye_color" + +// ~color source defines for species hair overrides +/// Uses the species's mutant color for the hair color +#define USE_MUTANT_COLOR "use_mutant_color" +/// Uses the species's fixed mutant color for the hair color +#define USE_FIXED_MUTANT_COLOR "use_fixed_mutant_color" diff --git a/code/__DEFINES/stat_tracking.dm b/code/__DEFINES/stat_tracking.dm index d143e5df4c70..9bd697440406 100644 --- a/code/__DEFINES/stat_tracking.dm +++ b/code/__DEFINES/stat_tracking.dm @@ -54,7 +54,8 @@ #define EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, proc) \ do { \ var/static/last_export = 0; \ - if (world.time - last_export > 1.1 SECONDS) { \ + /* Need to always run if we haven't yet, since this code can be placed ANYWHERE */ \ + if (world.time - last_export > 1.1 SECONDS || (last_export == 0)) { \ last_export = world.time; \ /* spawn() is used here because this is often used to track init times, where timers act oddly. */ \ /* I was making timers and even after init times were complete, the timers didn't run :shrug: */ \ diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 768f1faa514f..4e901c4ba2ce 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -31,6 +31,11 @@ /// If the incapacitated status effect will ignore a mob being agressively grabbed #define IGNORE_GRAB (1<<2) +/// Maxamounts of fire stacks a mob can get +#define MAX_FIRE_STACKS 20 +/// If a mob has a higher threshold than this, the icon shown will be increased to the big fire icon. +#define MOB_BIG_FIRE_STACK_THRESHOLD 3 + // Grouped effect sources, see also code/__DEFINES/traits.dm #define STASIS_MACHINE_EFFECT "stasis_machine" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index b3cd1921a643..1f63fa143486 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -353,8 +353,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_TUMOR_SUPPRESSED "brain_tumor_suppressed" /// Prevents hallucinations from the hallucination brain trauma (RDS) #define TRAIT_RDS_SUPPRESSED "rds_suppressed" -/// mobs that have this trait cannot be extinguished -#define TRAIT_PERMANENTLY_ONFIRE "permanently_onfire" +/// Mobs that have this trait cannot be extinguished +#define TRAIT_NO_EXTINGUISH "no_extinguish" /// Indicates if the mob is currently speaking with sign language #define TRAIT_SIGN_LANG "sign_language" /// This mob is able to use sign language over the radio. @@ -732,6 +732,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_POSTERBOY "poster_boy" #define TRAIT_THROWINGARM "throwing_arm" #define TRAIT_SETTLER "settler" +#define TRAIT_STRONG_STOMACH "strong_stomach" /// This mob always lands on their feet when they fall, for better or for worse. #define TRAIT_CATLIKE_GRACE "catlike_grace" diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm index 117067a05782..7a9cd512d58b 100644 --- a/code/__HELPERS/type_processing.dm +++ b/code/__HELPERS/type_processing.dm @@ -1,14 +1,60 @@ /proc/make_types_fancy(list/types) if (ispath(types)) types = list(types) - . = list() - for(var/type in types) - var/typename = "[type]" - // Longest paths comes first - var/static/list/TYPES_SHORTCUTS = list( - /obj/effect/decal/cleanable = "CLEANABLE", + var/static/list/types_to_replacement + var/static/list/replacement_to_text + if(!types_to_replacement) + // Longer paths come after shorter ones, try and keep the structure + var/list/work_from = list( + /datum = "DATUM", + /area = "AREA", + /atom/movable = "MOVABLE", + /obj = "OBJ", + /turf = "TURF", + /turf/closed = "CLOSED", + /turf/open = "OPEN", + + /mob = "MOB", + /mob/living = "LIVING", + /mob/living/carbon = "CARBON", + /mob/living/carbon/human = "HUMANOID", + /mob/living/simple_animal = "SIMPLE", + /mob/living/basic = "BASIC", + /mob/living/silicon = "SILICON", + /mob/living/silicon/robot = "CYBORG", + + /obj/item = "ITEM", + /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", + /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON", + /obj/item/organ = "ORGAN", + /obj/item/mod/control = "MODSUIT", + /obj/item/mod/module = "MODSUIT_MOD", + /obj/item/gun = "GUN", + /obj/item/gun/magic = "GUN_MAGIC", + /obj/item/gun/energy = "GUN_ENERGY", + /obj/item/gun/energy/laser = "GUN_LASER", + /obj/item/gun/ballistic = "GUN_BALLISTIC", + /obj/item/gun/ballistic/automatic = "GUN_AUTOMATIC", + /obj/item/gun/ballistic/revolver = "GUN_REVOLVER", + /obj/item/gun/ballistic/rifle = "GUN_RIFLE", + /obj/item/gun/ballistic/shotgun = "GUN_SHOTGUN", + /obj/item/stack/sheet = "SHEET", + /obj/item/stack/sheet/mineral = "MINERAL_SHEET", + /obj/item/stack/ore = "ORE", + /obj/item/ai_module = "AI_LAW_MODULE", + /obj/item/circuitboard = "CIRCUITBOARD", + /obj/item/circuitboard/machine = "MACHINE_BOARD", + /obj/item/circuitboard/computer = "COMPUTER_BOARD", + /obj/item/reagent_containers = "REAGENT_CONTAINERS", + /obj/item/reagent_containers/pill = "PILL", + /obj/item/reagent_containers/pill/patch = "MEDPATCH", + /obj/item/reagent_containers/hypospray/medipen = "MEDIPEN", + /obj/item/reagent_containers/cup/glass = "DRINK", + /obj/item/food = "FOOD", /obj/item/bodypart = "BODYPART", + /obj/effect/decal/cleanable = "CLEANABLE", /obj/item/radio/headset = "HEADSET", + /obj/item/clothing = "CLOTHING", /obj/item/clothing/accessory = "ACCESSORY", /obj/item/clothing/mask/gas = "GASMASK", /obj/item/clothing/mask = "MASK", @@ -21,75 +67,46 @@ /obj/item/clothing/head/helmet = "HELMET", /obj/item/clothing/head = "HEAD", /obj/item/clothing/neck = "NECK", - /obj/item/clothing = "CLOTHING", /obj/item/storage/backpack = "BACKPACK", /obj/item/storage/belt = "BELT", - /obj/item/book/manual = "MANUAL", /obj/item/storage/pill_bottle = "PILL_BOTTLE", - /obj/item/reagent_containers/pill/patch = "MEDPATCH", - /obj/item/reagent_containers/pill = "PILL", - /obj/item/reagent_containers/hypospray/medipen = "MEDIPEN", - /obj/item/reagent_containers/cup/glass = "DRINK", - /obj/item/food = "FOOD", - /obj/item/reagent_containers = "REAGENT_CONTAINERS", - /obj/machinery/atmospherics = "ATMOS_MECH", - /obj/machinery/portable_atmospherics = "PORT_ATMOS", - /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON", - /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", - /obj/item/organ = "ORGAN", - /obj/item/mod/control = "MODSUIT", - /obj/item/mod/module = "MODSUIT_MOD", - /obj/item/gun/ballistic/automatic = "GUN_AUTOMATIC", - /obj/item/gun/ballistic/revolver = "GUN_REVOLVER", - /obj/item/gun/ballistic/rifle = "GUN_RIFLE", - /obj/item/gun/ballistic/shotgun = "GUN_SHOTGUN", - /obj/item/gun/ballistic = "GUN_BALLISTIC", - /obj/item/gun/energy/laser = "GUN_LASER", - /obj/item/gun/energy = "GUN_ENERGY", - /obj/item/gun/magic = "GUN_MAGIC", - /obj/item/gun = "GUN", - /obj/item/stack/sheet/mineral = "MINERAL_SHEET", - /obj/item/stack/sheet = "SHEET", - /obj/item/stack/ore = "ORE", - /obj/item/ai_module = "AI_LAW_MODULE", - /obj/item/circuitboard/machine = "MACHINE_BOARD", - /obj/item/circuitboard/computer = "COMPUTER_BOARD", - /obj/item/circuitboard = "CIRCUITBOARD", - /obj/item = "ITEM", - /obj/structure/closet/crate/secure = "LOCKED_CRATE", + /obj/item/book/manual = "MANUAL", + + /obj/structure = "STRUCTURE", + /obj/structure/closet = "CLOSET", /obj/structure/closet/crate = "CRATE", + /obj/structure/closet/crate/secure = "LOCKED_CRATE", /obj/structure/closet/secure_closet = "LOCKED_CLOSET", - /obj/structure/closet = "CLOSET", - /obj/structure = "STRUCTURE", - /obj/machinery/door/airlock = "AIRLOCK", + + /obj/machinery = "MACHINERY", + /obj/machinery/atmospherics = "ATMOS_MECH", + /obj/machinery/portable_atmospherics = "PORT_ATMOS", /obj/machinery/door = "DOOR", + /obj/machinery/door/airlock = "AIRLOCK", /obj/machinery/rnd/production = "RND_FABRICATOR", - /obj/machinery/computer/camera_advanced/shuttle_docker = "DOCKING_COMPUTER", /obj/machinery/computer = "COMPUTER", - /obj/machinery/vending/wardrobe = "JOBDROBE", + /obj/machinery/computer/camera_advanced/shuttle_docker = "DOCKING_COMPUTER", /obj/machinery/vending = "VENDING", - /obj/machinery = "MACHINERY", + /obj/machinery/vending/wardrobe = "JOBDROBE", /obj/effect = "EFFECT", /obj/projectile = "PROJECTILE", - /obj = "O", - /datum = "D", - /turf/open = "OPEN", - /turf/closed = "CLOSED", - /turf = "T", - /mob/living/carbon/human = "HUMANOID", - /mob/living/carbon = "CARBON", - /mob/living/simple_animal = "SIMPLE", - /mob/living/basic = "BASIC", - /mob/living/silicon/robot = "CYBORG", - /mob/living/silicon = "SILICON", - /mob/living = "LIVING", - /mob = "M", ) - for (var/tn in TYPES_SHORTCUTS) - if(copytext(typename, 1, length("[tn]/") + 1) == "[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) - typename = TYPES_SHORTCUTS[tn] + copytext(typename, length("[tn]/")) - break - .[typename] = type + // ignore_root_path so we can draw the root normally + types_to_replacement = zebra_typecacheof(work_from, ignore_root_path = TRUE) + replacement_to_text = list() + for(var/key in work_from) + replacement_to_text[work_from[key]] = "[key]" + + + . = list() + for(var/type in types) + var/replace_with = types_to_replacement[type] + if(!replace_with) + .["[type]"] = type + continue + var/cut_out = replacement_to_text[replace_with] + // + 1 to account for / + .[replace_with + copytext("[type]", length(cut_out) + 1)] = type /proc/get_fancy_list_of_atom_types() var/static/list/pre_generated_list diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm index f3677cf2c644..2d6ad4533334 100644 --- a/code/__byond_version_compat.dm +++ b/code/__byond_version_compat.dm @@ -2,11 +2,11 @@ //Update this whenever you need to take advantage of more recent byond features #define MIN_COMPILER_VERSION 515 -#define MIN_COMPILER_BUILD 1609 +#define MIN_COMPILER_BUILD 1626 #if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) //Don't forget to update this part #error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update. -#error You need version 515.1609 or higher +#error You need version 515.1626 or higher #endif // Keep savefile compatibilty at minimum supported level diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index f33afc11f4b3..653470493e44 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -316,6 +316,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_DEBRAIN_OVERLAY" = TRAIT_NO_DEBRAIN_OVERLAY, "TRAIT_NO_DNA_COPY" = TRAIT_NO_DNA_COPY, "TRAIT_NO_DNA_SCRAMBLE" = TRAIT_NO_DNA_SCRAMBLE, + "TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH, "TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM, "TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE, "TRAIT_NO_GUN_AKIMBO" = TRAIT_NO_GUN_AKIMBO, @@ -351,7 +352,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_PASSTABLE" = TRAIT_PASSTABLE, "TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER, "TRAIT_PERMANENTLY_MORTAL" = TRAIT_PERMANENTLY_MORTAL, - "TRAIT_PERMANENTLY_ONFIRE" = TRAIT_PERMANENTLY_ONFIRE, "TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER, "TRAIT_PIERCEIMMUNE" = TRAIT_PIERCEIMMUNE, "TRAIT_PLANT_SAFE" = TRAIT_PLANT_SAFE, @@ -417,6 +417,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_STABLELIVER" = TRAIT_STABLELIVER, "TRAIT_STASIS" = TRAIT_STASIS, "TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER, + "TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH, "TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE, "TRAIT_SUCCUMB_OVERRIDE" = TRAIT_SUCCUMB_OVERRIDE, "TRAIT_SUICIDED" = TRAIT_SUICIDED, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 8424f1e48303..30b708789dd0 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -128,6 +128,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_NO_AUGMENTS" = TRAIT_NO_AUGMENTS, "TRAIT_NO_BLOOD_OVERLAY" = TRAIT_NO_BLOOD_OVERLAY, "TRAIT_NO_DNA_COPY" = TRAIT_NO_DNA_COPY, + "TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH, "TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE, "TRAIT_NO_PLASMA_TRANSFORM" = TRAIT_NO_PLASMA_TRANSFORM, "TRAIT_NO_SLIP_ALL" = TRAIT_NO_SLIP_ALL, @@ -161,7 +162,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_PARALYSIS_R_LEG" = TRAIT_PARALYSIS_R_LEG, "TRAIT_PASSTABLE" = TRAIT_PASSTABLE, "TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER, - "TRAIT_PERMANENTLY_ONFIRE" = TRAIT_PERMANENTLY_ONFIRE, "TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER, "TRAIT_PIERCEIMMUNE" = TRAIT_PIERCEIMMUNE, "TRAIT_PLANT_SAFE" = TRAIT_PLANT_SAFE, @@ -200,6 +200,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_STABLEHEART" = TRAIT_STABLEHEART, "TRAIT_STABLELIVER" = TRAIT_STABLELIVER, "TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER, + "TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH, "TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE, "TRAIT_SURGEON" = TRAIT_SURGEON, "TRAIT_SURGICALLY_ANALYZED" = TRAIT_SURGICALLY_ANALYZED, diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index e73cebf14c0c..2f2c767c012c 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -68,8 +68,8 @@ if(client && hud_used) hud_used.reorganize_alerts() if(!no_anim) - thealert.transform = matrix(32, 6, MATRIX_TRANSLATE) - animate(thealert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) + thealert.transform = matrix(32, 0, MATRIX_TRANSLATE) + animate(thealert, transform = matrix(), time = 1 SECONDS, easing = ELASTIC_EASING) if(timeout_override) thealert.timeout = timeout_override if(thealert.timeout) @@ -185,22 +185,6 @@ //End gas alerts - -/atom/movable/screen/alert/fat - name = "Fat" - desc = "You ate too much food, lardass. Run around the station and lose some weight." - icon_state = "fat" - -/atom/movable/screen/alert/hungry - name = "Hungry" - desc = "Some food would be good right about now." - icon_state = "hungry" - -/atom/movable/screen/alert/starving - name = "Starving" - desc = "You're severely malnourished. The hunger pains make moving around a chore." - icon_state = "starving" - /atom/movable/screen/alert/gross name = "Grossed out." desc = "That was kind of gross..." @@ -317,12 +301,25 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." /// The offer we're linked to, yes this is suspiciously like a status effect alert var/datum/status_effect/offering/offer /// Additional text displayed in the description of the alert. - var/additional_desc_text = "Click this alert to take it." + var/additional_desc_text = "Click this alert to take it, or shift click it to examiante it." + /// Text to override what appears in screentips for the alert + var/screentip_override_text + /// Whether the offered item can be examined by shift-clicking the alert + var/examinable = TRUE + +/atom/movable/screen/alert/give/Initialize(mapload, datum/hud/hud_owner) + . = ..() + register_context() /atom/movable/screen/alert/give/Destroy() offer = null return ..() +/atom/movable/screen/alert/give/add_context(atom/source, list/context, obj/item/held_item, mob/user) + context[SCREENTIP_CONTEXT_LMB] = screentip_override_text || "Take [offer.offered_item.name]" + context[SCREENTIP_CONTEXT_SHIFT_LMB] = "Examine" + return CONTEXTUAL_SCREENTIP_SET + /** * Handles assigning most of the variables for the alert that pops up when an item is offered * @@ -373,6 +370,16 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." handle_transfer() +/atom/movable/screen/alert/give/examine(mob/user) + if(!examinable) + return ..() + + return list( + span_boldnotice(name), + span_info("[offer.owner] is offering you the following item (click the alert to take it!):"), + "
-----
") - for (var/mob/mob as anything in get_hearers_in_view(DEFAULT_MESSAGE_RANGE, src)) + for (var/mob/mob as anything in get_hearers_in_view(DEFAULT_MESSAGE_RANGE, parent)) to_chat(mob, message) /datum/component/puzzgrid/ui_data(mob/user) diff --git a/code/datums/diseases/tuberculosis.dm b/code/datums/diseases/tuberculosis.dm index de87cab6f3f6..4bb005917b47 100644 --- a/code/datums/diseases/tuberculosis.dm +++ b/code/datums/diseases/tuberculosis.dm @@ -18,6 +18,10 @@ if(!.) return + if(SPT_PROB(stage * 2, seconds_per_tick)) + affected_mob.emote("cough") + to_chat(affected_mob, span_danger("Your chest hurts.")) + switch(stage) if(2) if(SPT_PROB(1, seconds_per_tick)) diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm index 3a3aa4b35300..ac640a01c48e 100644 --- a/code/datums/elements/decals/blood.dm +++ b/code/datums/elements/decals/blood.dm @@ -42,10 +42,13 @@ pic.color = source.get_blood_dna_color() || COLOR_BLOOD // NON-MODULE CHANGE return ..() -/datum/element/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override) +/datum/element/decal/blood/proc/get_examine_name(atom/source, mob/user, list/override) SIGNAL_HANDLER - var/atom/A = source - override[EXAMINE_POSITION_ARTICLE] = A.gender == PLURAL? "some" : "a" - override[EXAMINE_POSITION_BEFORE] = " blood-stained " - return COMPONENT_EXNAME_CHANGED + var/list/all_dna = GET_ATOM_BLOOD_DNA(source) + var/list/all_blood_names = list() + for(var/dna_sample in all_dna) + var/datum/blood_type/blood = GLOB.blood_types[all_dna[dna_sample]] + all_blood_names |= lowertext(initial(blood.reagent_type.name)) + + override[EXAMINE_POSITION_BEFORE] = "[english_list(all_blood_names, nothing_text = "blood")] stained" diff --git a/code/datums/elements/permanent_fire_overlay.dm b/code/datums/elements/permanent_fire_overlay.dm new file mode 100644 index 000000000000..514d0f121a45 --- /dev/null +++ b/code/datums/elements/permanent_fire_overlay.dm @@ -0,0 +1,24 @@ +/// When applied to a mob, they will always have a fire overlay regardless of if they are *actually* on fire. +/datum/element/perma_fire_overlay + +/datum/element/perma_fire_overlay/Attach(atom/target) + . = ..() + if(!isliving(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(add_fire_overlay)) + target.update_appearance(UPDATE_OVERLAYS) + +/datum/element/perma_fire_overlay/Detach(atom/target) + . = ..() + UnregisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS) + target.update_appearance(UPDATE_OVERLAYS) + +/datum/element/perma_fire_overlay/proc/add_fire_overlay(mob/living/source, list/overlays) + SIGNAL_HANDLER + + var/mutable_appearance/created_overlay = source.get_fire_overlay(stacks = MAX_FIRE_STACKS, on_fire = TRUE) + if(isnull(created_overlay)) + return + + overlays |= created_overlay diff --git a/code/datums/elements/skill_reward.dm b/code/datums/elements/skill_reward.dm index 0c0e04754f74..7809eea85f71 100644 --- a/code/datums/elements/skill_reward.dm +++ b/code/datums/elements/skill_reward.dm @@ -24,14 +24,15 @@ if(!LAZYACCESS(modifiers, CTRL_CLICK) && !check_equippable(user)) //Allows other players to drag it around at least. to_chat(user, span_warning("You feel completely and utterly unworthy to even touch \the [source].")) return COMPONENT_CANCEL_ATTACK_CHAIN + return NONE ///We check if the item can be equipped, otherwise we drop it. /datum/element/skill_reward/proc/drop_if_unworthy(datum/source, mob/living/user) SIGNAL_HANDLER - if(check_equippable(user) | !(source in user.get_equipped_items(include_pockets = TRUE, include_accessories = TRUE))) - return + if(check_equippable(user) || !(source in user.get_equipped_items(include_pockets = TRUE, include_accessories = TRUE))) + return NONE to_chat(user, span_warning("You feel completely and utterly unworthy to even touch \the [source].")) - user.dropItemToGround(src, TRUE) + user.dropItemToGround(source, TRUE) return COMPONENT_EQUIPPED_FAILED /datum/element/skill_reward/proc/check_equippable(mob/living/user) diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 3c0e2a903c23..a10b838d02bc 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -88,11 +88,10 @@ * Returns TRUE if it was able to run the emote, FALSE otherwise. */ /datum/emote/proc/run_emote(mob/user, params, type_override, intentional = FALSE) - . = TRUE if(!can_run_emote(user, TRUE, intentional)) return FALSE if(SEND_SIGNAL(user, COMSIG_MOB_PRE_EMOTED, key, params, type_override, intentional) & COMPONENT_CANT_EMOTE) - return // We don't return FALSE because the error output would be incorrect, provide your own if necessary. + return TRUE // We don't return FALSE because the error output would be incorrect, provide your own if necessary. var/msg = select_message_type(user, message, intentional) if(params && message_param) msg = select_param(user, params) @@ -100,31 +99,94 @@ msg = replace_pronoun(user, msg) if(!msg) - return + return TRUE user.log_message(msg, LOG_EMOTE) - var/dchatmsg = "[user] [msg]" var/tmp_sound = get_sound(user) if(tmp_sound && should_play_sound(user, intentional) && TIMER_COOLDOWN_FINISHED(user, type)) TIMER_COOLDOWN_START(user, type, audio_cooldown) playsound(user, tmp_sound, 50, vary) - var/user_turf = get_turf(user) - if (user.client) - for(var/mob/ghost as anything in GLOB.dead_mob_list) - if(!ghost.client || isnewplayer(ghost)) + var/is_important = emote_type & EMOTE_IMPORTANT + var/is_visual = emote_type & EMOTE_VISIBLE + var/is_audible = emote_type & EMOTE_AUDIBLE + + // Emote doesn't get printed to chat, runechat only + if(emote_type & EMOTE_RUNECHAT) + for(var/mob/viewer as anything in viewers(user)) + if(isnull(viewer.client)) + continue + if(!is_important && viewer != user && (!is_visual || !is_audible)) + if(is_audible && !viewer.can_hear()) + continue + if(is_visual && viewer.is_blind()) + continue + if(user.runechat_prefs_check(viewer, EMOTE_MESSAGE)) + viewer.create_chat_message( + speaker = user, + raw_message = msg, + runechat_flags = EMOTE_MESSAGE, + ) + else if(is_important) + to_chat(viewer, "[user] [msg]") + else if(is_audible && is_visual) + viewer.show_message( + "[user] [msg]", MSG_AUDIBLE, + "You see how [user] [msg]", MSG_VISUAL, + ) + else if(is_audible) + viewer.show_message("[user] [msg]", MSG_AUDIBLE) + else if(is_visual) + viewer.show_message("[user] [msg]", MSG_VISUAL) + return TRUE // Early exit so no dchat message + + // The emote has some important information, and should always be shown to the user + else if(is_important) + for(var/mob/viewer as anything in viewers(user)) + to_chat(viewer, "[user] [msg]") + if(user.runechat_prefs_check(viewer, EMOTE_MESSAGE)) + viewer.create_chat_message( + speaker = user, + raw_message = msg, + runechat_flags = EMOTE_MESSAGE, + ) + // Emotes has both an audible and visible component + // Prioritize audible, and provide a visible message if the user is deaf + else if(is_visual && is_audible) + user.audible_message( + message = msg, + deaf_message = "You see how [user] [msg]", + self_message = msg, + audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE, + ) + // Emote is entirely audible, no visible component + else if(is_audible) + user.audible_message( + message = msg, + self_message = msg, + audible_message_flags = EMOTE_MESSAGE, + ) + // Emote is entirely visible, no audible component + else if(is_visual) + user.visible_message( + message = msg, + self_message = msg, + visible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE, + ) + else + CRASH("Emote [type] has no valid emote type set!") + + if(!isnull(user.client)) + var/dchatmsg = "[user] [msg]" + for(var/mob/ghost as anything in GLOB.dead_mob_list - viewers(get_turf(user))) + if(isnull(ghost.client) || isnewplayer(ghost)) + continue + if(!(get_chat_toggles(ghost.client) & CHAT_GHOSTSIGHT)) continue - if(get_chat_toggles(ghost.client) & CHAT_GHOSTSIGHT && !(ghost in viewers(user_turf, null))) - ghost.show_message("[FOLLOW_LINK(ghost, user)] [dchatmsg]") - if(emote_type & (EMOTE_AUDIBLE | EMOTE_VISIBLE)) //emote is audible and visible - user.audible_message(msg, deaf_message = "You see how [user] [msg]", audible_message_flags = EMOTE_MESSAGE) - else if(emote_type & EMOTE_VISIBLE) //emote is only visible - user.visible_message(msg, visible_message_flags = EMOTE_MESSAGE) - if(emote_type & EMOTE_IMPORTANT) - for(var/mob/living/viewer in viewers()) - if(viewer.is_blind() && !viewer.can_hear()) - to_chat(viewer, msg) + to_chat(ghost, "[FOLLOW_LINK(ghost, user)] [dchatmsg]") + + return TRUE /** * For handling emote cooldown, return true to allow the emote to happen. diff --git a/code/datums/mood.dm b/code/datums/mood.dm index 150220cfbcfd..c61ceea0faf6 100644 --- a/code/datums/mood.dm +++ b/code/datums/mood.dm @@ -92,7 +92,6 @@ set_sanity(sanity + 0.4 * seconds_per_tick, SANITY_NEUTRAL, SANITY_MAXIMUM) if(MOOD_LEVEL_HAPPY4) set_sanity(sanity + 0.6 * seconds_per_tick, SANITY_NEUTRAL, SANITY_MAXIMUM) - handle_nutrition() // 0.416% is 15 successes / 3600 seconds. Calculated with 2 minute // mood runtime, so 50% average uptime across the hour. @@ -112,16 +111,18 @@ last_stat = mob_parent.stat /// Handles mood given by nutrition -/datum/mood/proc/handle_nutrition() - if (HAS_TRAIT(mob_parent, TRAIT_NOHUNGER)) - clear_mood_event(MOOD_CATEGORY_NUTRITION) // if you happen to switch species while hungry youre no longer hungy - return FALSE // no moods for nutrition +/datum/mood/proc/update_nutrition_moodlets() + if(HAS_TRAIT(mob_parent, TRAIT_NOHUNGER)) + clear_mood_event(MOOD_CATEGORY_NUTRITION) + return FALSE + + if(HAS_TRAIT(mob_parent, TRAIT_FAT) && !HAS_TRAIT(mob_parent, TRAIT_VORACIOUS)) + add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/fat) + return TRUE + switch(mob_parent.nutrition) if(NUTRITION_LEVEL_FULL to INFINITY) - if (!HAS_TRAIT(mob_parent, TRAIT_VORACIOUS)) - add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/fat) - else - add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/wellfed) // round and full + add_mood_event(MOOD_CATEGORY_NUTRITION, HAS_TRAIT(mob_parent, TRAIT_VORACIOUS) ? /datum/mood_event/wellfed : /datum/mood_event/too_wellfed) if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL) add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/wellfed) if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) @@ -133,6 +134,8 @@ if(0 to NUTRITION_LEVEL_STARVING) add_mood_event(MOOD_CATEGORY_NUTRITION, /datum/mood_event/starving) + return TRUE + /** * Adds a mood event to the mob * diff --git a/code/datums/mood_events/needs_events.dm b/code/datums/mood_events/needs_events.dm index dd710554d8d9..dd5441476dcf 100644 --- a/code/datums/mood_events/needs_events.dm +++ b/code/datums/mood_events/needs_events.dm @@ -3,6 +3,10 @@ description = "I'm so fat..." //muh fatshaming mood_change = -6 +/datum/mood_event/too_wellfed + description = "I think I've eaten too much." + mood_change = 0 + /datum/mood_event/wellfed description = "I'm stuffed!" mood_change = 8 @@ -62,7 +66,7 @@ mood_change = -12 /datum/mood_event/disgust/dirty_food - description = "It was too dirty to eat..." + description = "That was too dirty to eat..." mood_change = -6 timeout = 4 MINUTES diff --git a/code/datums/quirks/negative_quirks/social_anxiety.dm b/code/datums/quirks/negative_quirks/social_anxiety.dm index b6916b2dd038..046ac4715f57 100644 --- a/code/datums/quirks/negative_quirks/social_anxiety.dm +++ b/code/datums/quirks/negative_quirks/social_anxiety.dm @@ -15,9 +15,26 @@ RegisterSignal(quirk_holder, COMSIG_MOB_EYECONTACT, PROC_REF(eye_contact)) RegisterSignal(quirk_holder, COMSIG_MOB_EXAMINATE, PROC_REF(looks_at_floor)) RegisterSignal(quirk_holder, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + quirk_holder.apply_status_effect(/datum/status_effect/speech/stutter/anxiety, INFINITY) /datum/quirk/social_anxiety/remove() UnregisterSignal(quirk_holder, list(COMSIG_MOB_EYECONTACT, COMSIG_MOB_EXAMINATE, COMSIG_MOB_SAY)) + quirk_holder.remove_status_effect(/datum/status_effect/speech/stutter/anxiety) + +/// Calculates how much to modifiy our effects based on our mood level +/datum/quirk/social_anxiety/proc/calculate_mood_mod() + var/nearby_people = 0 + for(var/mob/living/carbon/human/listener in oview(3, quirk_holder)) + if(listener.client || listener.mind) + nearby_people++ + + var/mod = 1 + if(quirk_holder.mob_mood) + mod = 1 + 0.02 * (50 - (max(50, quirk_holder.mob_mood.mood_level * (SANITY_LEVEL_MAX + 1 - quirk_holder.mob_mood.sanity_level)))) //low sanity levels are better, they max at 6 + else + mod = 1 + 0.02 * (50 - (max(50, 0.1 * quirk_holder.nutrition))) + + return mod * nearby_people * 12.5 /datum/quirk/social_anxiety/proc/handle_speech(datum/source, list/speech_args) SIGNAL_HANDLER @@ -27,48 +44,33 @@ if(HAS_TRAIT(source, TRAIT_SIGN_LANG)) // No modifiers for signers, so you're less anxious when you go non-verbal return - var/moodmod - if(quirk_holder.mob_mood) - moodmod = (1+0.02*(50-(max(50, quirk_holder.mob_mood.mood_level*(7-quirk_holder.mob_mood.sanity_level))))) //low sanity levels are better, they max at 6 - else - moodmod = (1+0.02*(50-(max(50, 0.1*quirk_holder.nutrition)))) - var/nearby_people = 0 - for(var/mob/living/carbon/human/H in oview(3, quirk_holder)) - if(H.client) - nearby_people++ + var/moodmod = calculate_mood_mod() var/message = speech_args[SPEECH_MESSAGE] if(message) var/list/message_split = splittext(message, " ") var/list/new_message = list() - var/mob/living/carbon/human/quirker = quirk_holder for(var/word in message_split) - if(prob(max(5,(nearby_people*12.5*moodmod))) && word != message_split[1]) //Minimum 1/20 chance of filler + if(prob(max(5, moodmod)) && word != message_split[1]) //Minimum 1/20 chance of filler new_message += pick("uh,","erm,","um,") - if(prob(min(5,(0.05*(nearby_people*12.5)*moodmod)))) //Max 1 in 20 chance of cutoff after a successful filler roll, for 50% odds in a 15 word sentence - quirker.set_silence_if_lower(6 SECONDS) - to_chat(quirker, span_danger("You feel self-conscious and stop talking. You need a moment to recover!")) + if(prob(min(5, moodmod))) //Max 1 in 20 chance of cutoff after a successful filler roll, for 50% odds in a 15 word sentence + quirk_holder.set_silence_if_lower(6 SECONDS) + to_chat(quirk_holder, span_danger("You feel self-conscious and stop talking. You need a moment to recover!")) break - if(prob(max(5,(nearby_people*12.5*moodmod)))) //Minimum 1/20 chance of stutter - // Add a short stutter, THEN treat our word - quirker.adjust_stutter(0.5 SECONDS) - var/list/message_data = quirker.treat_message(word, capitalize_message = FALSE) - new_message += message_data["message"] - else - new_message += word + new_message += word message = jointext(new_message, " ") - var/mob/living/carbon/human/quirker = quirk_holder - if(prob(min(50,(0.50*(nearby_people*12.5)*moodmod)))) //Max 50% chance of not talking + + if(prob(min(50, (0.50 * moodmod)))) //Max 50% chance of not talking if(dumb_thing) - to_chat(quirker, span_userdanger("You think of a dumb thing you said a long time ago and scream internally.")) + to_chat(quirk_holder, span_userdanger("You think of a dumb thing you said a long time ago and scream internally.")) dumb_thing = FALSE //only once per life if(prob(1)) - new/obj/item/food/spaghetti/pastatomato(get_turf(quirker)) //now that's what I call spaghetti code + new/obj/item/food/spaghetti/pastatomato(get_turf(quirk_holder)) //now that's what I call spaghetti code else to_chat(quirk_holder, span_warning("You think that wouldn't add much to the conversation and decide not to say it.")) - if(prob(min(25,(0.25*(nearby_people*12.75)*moodmod)))) //Max 25% chance of silence stacks after successful not talking roll - to_chat(quirker, span_danger("You retreat into yourself. You really don't feel up to talking.")) - quirker.set_silence_if_lower(10 SECONDS) + if(prob(min(25, (0.25 * moodmod)))) //Max 25% chance of silence stacks after successful not talking roll + to_chat(quirk_holder, span_danger("You retreat into yourself. You really don't feel up to talking.")) + quirk_holder.set_silence_if_lower(10 SECONDS) speech_args[SPEECH_MESSAGE] = pick("Uh.","Erm.","Um.") else diff --git a/code/datums/quirks/positive_quirks/strong_stomach.dm b/code/datums/quirks/positive_quirks/strong_stomach.dm new file mode 100644 index 000000000000..8c0a3f3b1375 --- /dev/null +++ b/code/datums/quirks/positive_quirks/strong_stomach.dm @@ -0,0 +1,12 @@ +/datum/quirk/strong_stomach + name = "Strong Stomach" + desc = "You can eat food discarded on the ground without getting sick, and vomiting affects you less." + icon = FA_ICON_FACE_GRIN_BEAM_SWEAT + value = 4 + mob_trait = TRAIT_STRONG_STOMACH + gain_text = span_notice("You feel like you could eat anything!") + lose_text = span_danger("Looking at food on the ground makes you feel a little queasy.") + medical_record_text = "Patient has a stronger than average immune system...to food poisoning, at least." + mail_goodies = list( + /obj/item/reagent_containers/pill/ondansetron, + ) diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index 4a6e7b6b730f..2f32ff5b3bed 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -7,7 +7,7 @@ /// Current amount of stacks we have var/stacks /// Maximum of stacks that we could possibly get - var/stack_limit = 20 + var/stack_limit = MAX_FIRE_STACKS /// What status effect types do we remove uppon being applied. These are just deleted without any deduction from our or their stacks when forced. var/list/enemy_types /// What status effect types do we merge into if they exist. Ignored when forced. @@ -116,12 +116,8 @@ owner.clear_alert(ALERT_FIRE) else if(!was_on_fire && owner.on_fire) owner.throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire) - -/** - * Used to update owner's effect overlay - */ - -/datum/status_effect/fire_handler/proc/update_overlay() + owner.update_appearance(UPDATE_OVERLAYS) + update_particles() /datum/status_effect/fire_handler/fire_stacks id = "fire_stacks" //fire_stacks and wet_stacks should have different IDs or else has_status_effect won't work @@ -132,8 +128,6 @@ /// If we're on fire var/on_fire = FALSE - /// Stores current fire overlay icon state, for optimisation purposes - var/last_icon_state /// Reference to the mob light emitter itself var/obj/effect/dummy/lighting_obj/moblight /// Type of mob light emitter we use when on fire @@ -160,8 +154,6 @@ return TRUE deal_damage(seconds_between_ticks) - update_overlay() - update_particles() /datum/status_effect/fire_handler/fire_stacks/update_particles() if(on_fire) @@ -239,8 +231,6 @@ moblight = new moblight_type(owner) cache_stacks() - update_overlay() - update_particles() SEND_SIGNAL(owner, COMSIG_LIVING_IGNITED, owner) return TRUE @@ -254,8 +244,6 @@ owner.clear_mood_event("on_fire") SEND_SIGNAL(owner, COMSIG_LIVING_EXTINGUISHED, owner) cache_stacks() - update_overlay() - update_particles() for(var/obj/item/equipped in owner.get_equipped_items()) equipped.extinguish() @@ -263,16 +251,26 @@ if(on_fire) extinguish() set_stacks(0) - update_overlay() - update_particles() + UnregisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS) + owner.update_appearance(UPDATE_OVERLAYS) return ..() -/datum/status_effect/fire_handler/fire_stacks/update_overlay() - last_icon_state = owner.update_fire_overlay(stacks, on_fire, last_icon_state) - /datum/status_effect/fire_handler/fire_stacks/on_apply() . = ..() - update_overlay() + RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(add_fire_overlay)) + owner.update_appearance(UPDATE_OVERLAYS) + +/datum/status_effect/fire_handler/fire_stacks/proc/add_fire_overlay(mob/living/source, list/overlays) + SIGNAL_HANDLER + + if(stacks <= 0 || !on_fire) + return + + var/mutable_appearance/created_overlay = owner.get_fire_overlay(stacks, on_fire) + if(isnull(created_overlay)) + return + + overlays |= created_overlay /obj/effect/dummy/lighting_obj/moblight/fire name = "fire" diff --git a/code/datums/status_effects/debuffs/speech_debuffs.dm b/code/datums/status_effects/debuffs/speech_debuffs.dm index 4968bb42e39b..f8371c82dc21 100644 --- a/code/datums/status_effects/debuffs/speech_debuffs.dm +++ b/code/datums/status_effects/debuffs/speech_debuffs.dm @@ -2,8 +2,10 @@ id = null alert_type = null remove_on_fullheal = TRUE - + tick_interval = -1 + /// If TRUE, TTS will say the original message rather than what we changed it to var/make_tts_message_original = FALSE + /// If set, this will be appended to the TTS filter of the message var/tts_filter = "" /datum/status_effect/speech/on_creation(mob/living/new_owner, duration = 10 SECONDS) @@ -41,7 +43,15 @@ for(var/i = 1, i <= length(phrase), i += length(original_char)) original_char = phrase[i] - final_phrase += apply_speech(original_char, original_char) + final_phrase += apply_speech(original_char) + + if(final_phrase == phrase) + return // No change was done, whatever + + if(length(tts_filter) > 0) + message_args[TREAT_TTS_FILTER_ARG] += tts_filter + if(make_tts_message_original) + message_args[TREAT_TTS_MESSAGE_ARG] = message_args[TREAT_MESSAGE_ARG] message_args[TREAT_MESSAGE_ARG] = sanitize(final_phrase) @@ -51,20 +61,26 @@ * * Return the modified_char to be reapplied to the message. */ -/datum/status_effect/speech/proc/apply_speech(original_char, modified_char) +/datum/status_effect/speech/proc/apply_speech(original_char) stack_trace("[type] didn't implement apply_speech.") return original_char /datum/status_effect/speech/stutter id = "stutter" + make_tts_message_original = TRUE + tts_filter = "tremolo=f=10:d=0.8,rubberband=tempo=0.5" + /// The probability of adding a stutter to any character var/stutter_prob = 80 + /// The chance of a four character stutter + var/four_char_chance = 10 + /// The chance of a three character stutter + var/three_char_chance = 20 + /// The chance of a two character stutter + var/two_char_chance = 95 /// Regex of characters we won't apply a stutter to var/static/regex/no_stutter - make_tts_message_original = TRUE - tts_filter = "tremolo=f=10:d=0.8,rubberband=tempo=0.5" - /datum/status_effect/speech/stutter/on_creation(mob/living/new_owner, ...) . = ..() if(!.) @@ -72,18 +88,32 @@ if(!no_stutter) no_stutter = regex(@@[aeiouAEIOU ""''()[\]{}.!?,:;_`~-]@) -/datum/status_effect/speech/stutter/apply_speech(original_char, modified_char) +/datum/status_effect/speech/stutter/apply_speech(original_char) if(prob(stutter_prob) && !no_stutter.Find(original_char)) - if(prob(10)) - modified_char = "[modified_char]-[modified_char]-[modified_char]-[modified_char]" - else if(prob(20)) - modified_char = "[modified_char]-[modified_char]-[modified_char]" - else if(prob(95)) - modified_char = "[modified_char]-[modified_char]" - else - modified_char = "" + if(prob(four_char_chance)) + return "[original_char]-[original_char]-[original_char]-[original_char]" + if(prob(three_char_chance)) + return "[original_char]-[original_char]-[original_char]" + if(prob(two_char_chance)) + return "[original_char]-[original_char]" - return modified_char + return original_char + +/datum/status_effect/speech/stutter/anxiety + id = "anxiety_stutter" + stutter_prob = 5 + four_char_chance = 4 + three_char_chance = 10 + two_char_chance = 100 + remove_on_fullheal = FALSE + +/datum/status_effect/speech/stutter/anxiety/handle_message(datum/source, list/message_args) + if(HAS_TRAIT(owner, TRAIT_FEARLESS) || HAS_TRAIT(owner, TRAIT_SIGN_LANG)) + stutter_prob = 0 + else + var/datum/quirk/social_anxiety/host_quirk = owner.get_quirk(/datum/quirk/social_anxiety) + stutter_prob = clamp(host_quirk?.calculate_mood_mod() * 0.5, 5, 50) + return ..() /datum/status_effect/speech/stutter/derpspeech id = "derp_stutter" @@ -160,8 +190,9 @@ string_replacements = speech_changes["string_replacements"] string_additions = speech_changes["string_additions"] -/datum/status_effect/speech/slurring/apply_speech(original_char, modified_char) +/datum/status_effect/speech/slurring/apply_speech(original_char) + var/modified_char = original_char var/lower_char = lowertext(modified_char) if(prob(common_prob) && (lower_char in common_replacements)) var/to_replace = common_replacements[lower_char] diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm index de4de4673289..415113c3bd77 100644 --- a/code/datums/wounds/burns.dm +++ b/code/datums/wounds/burns.dm @@ -130,8 +130,7 @@ if(0) to_chat(victim, span_deadsay("The last of the nerve endings in your [limb.plaintext_zone] wither away, as the infection completely paralyzes your joint connector.")) threshold_penalty = 120 // piss easy to destroy - var/datum/brain_trauma/severe/paralysis/sepsis = new (limb.body_zone) - victim.gain_trauma(sepsis) + set_disabling(TRUE) /datum/wound/burn/flesh/get_wound_description(mob/user) if(strikes_to_lose_limb <= 0) diff --git a/code/game/atom/atom_examine.dm b/code/game/atom/atom_examine.dm index de813cd983dc..ad1d83222778 100644 --- a/code/game/atom/atom_examine.dm +++ b/code/game/atom/atom_examine.dm @@ -75,13 +75,16 @@ * [COMSIG_ATOM_GET_EXAMINE_NAME] signal */ /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 - if(SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) & COMPONENT_EXNAME_CHANGED) - . = override.Join("") + var/list/override = list(article, null, "[name]") + SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override) + + if(!isnull(override[EXAMINE_POSITION_ARTICLE])) + override -= null // IF there is no "before", don't try to join it + return jointext(override, " ") + if(!isnull(override[EXAMINE_POSITION_BEFORE])) + override -= null // There is no article, don't try to join it + return "\a [jointext(override, " ")]" + return "\a [src]" ///Generate the full examine string of this atom (including icon for goonchat) /atom/proc/get_examine_string(mob/user, thats = FALSE) diff --git a/code/game/machinery/computer/arcade/arcade.dm b/code/game/machinery/computer/arcade/arcade.dm index 8cac5ae48f17..d0a8b0a63819 100644 --- a/code/game/machinery/computer/arcade/arcade.dm +++ b/code/game/machinery/computer/arcade/arcade.dm @@ -72,8 +72,9 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( /obj/item/toy/plush/peepy = 2)) // NON-MODULE CHANGE: PEEPY /obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" + name = "\proper the arcade cabinet which shouldn't exist" + desc = "This arcade cabinet has no games installed, and in fact, should not exist. \ + Report the location of this machine to your local diety." icon_state = "arcade" icon_keyboard = null icon_screen = "invaders" @@ -137,19 +138,21 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list( new empprize(loc) explosion(src, devastation_range = -1, light_impact_range = 1+num_of_prizes, flame_range = 1+num_of_prizes) -/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/stack/arcadeticket)) - var/obj/item/stack/arcadeticket/T = O - var/amount = T.get_amount() - if(amount <2) - to_chat(user, span_warning("You need 2 tickets to claim a prize!")) - return - prizevend(user) - T.pay_tickets() - T.update_appearance() - O = T - to_chat(user, span_notice("You turn in 2 tickets to the [src] and claim a prize!")) - return +/obj/machinery/computer/arcade/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) + . = ..() + if(. & ITEM_INTERACT_ANY_BLOCKER) + return . + if(!istype(tool, /obj/item/stack/arcadeticket)) + return . + + var/obj/item/stack/arcadeticket/tickets = tool + if(!tickets.use(2)) + balloon_alert(user, "need 2 tickets!") + return ITEM_INTERACT_BLOCKING + + prizevend(user) + balloon_alert(user, "prize claimed") + return ITEM_INTERACT_SUCCESS // ** BATTLE ** // /obj/machinery/computer/arcade/battle diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm index 092a4c7889a2..4e84870ec9c3 100644 --- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm @@ -65,26 +65,22 @@ /obj/item/organ/internal/heart/rat/on_mob_insert(mob/living/carbon/receiver) . = ..() - if(!. || !ishuman(receiver)) + if(!ishuman(receiver)) return var/mob/living/carbon/human/human_receiver = receiver - if(!human_receiver.can_mutate()) - return - human_receiver.dna.add_mutation(/datum/mutation/human/dwarfism) + if(human_receiver.can_mutate()) + human_receiver.dna.add_mutation(/datum/mutation/human/dwarfism) //but 1.5 damage - if(human_receiver.physiology) - human_receiver.physiology.damage_resistance -= 50 + human_receiver.physiology?.damage_resistance -= 50 /obj/item/organ/internal/heart/rat/on_mob_remove(mob/living/carbon/heartless, special) . = ..() if(!ishuman(heartless)) return var/mob/living/carbon/human/human_heartless = heartless - if(!human_heartless.can_mutate()) - return - human_heartless.dna.remove_mutation(/datum/mutation/human/dwarfism) - if(human_heartless.physiology) - human_heartless.physiology.damage_resistance += 50 + if(human_heartless.can_mutate()) + human_heartless.dna.remove_mutation(/datum/mutation/human/dwarfism) + human_heartless.physiology?.damage_resistance += 50 /// you occasionally squeak, and have some rat related verbal tics /obj/item/organ/internal/tongue/rat diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 0bf6bf7872d3..f7e3dbfeefb8 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -557,17 +557,24 @@ dump_inventory_contents() /obj/machinery/suit_storage_unit/process(seconds_per_tick) - var/obj/item/stock_parts/cell/cell - if(suit && istype(suit)) - cell = suit.cell - if(mod) - cell = mod.get_cell() - if(!cell || cell.charge == cell.maxcharge) + var/list/cells_to_charge = list() + for(var/obj/item/charging in list(mod, suit, helmet, mask, storage)) + var/obj/item/stock_parts/cell/cell_charging = charging.get_cell() + if(!istype(cell_charging) || cell_charging.charge == cell_charging.maxcharge) + continue + + cells_to_charge += cell_charging + + var/cell_count = length(cells_to_charge) + if(cell_count <= 0) return - var/cell_charged = cell.give(final_charge_rate * seconds_per_tick) - if(cell_charged) - use_power((active_power_usage + final_charge_rate) * seconds_per_tick) + var/charge_per_item = (final_charge_rate * seconds_per_tick) / cell_count + for(var/obj/item/stock_parts/cell/cell as anything in cells_to_charge) + var/charge_used = use_power_from_net(charge_per_item, take_any = TRUE) + if(charge_used <= 0) + break + cell.give(charge_used) /obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) if(!prob(prb)) diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index f7300f720863..9988f8c91640 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -73,22 +73,24 @@ com.target_ref = null visible_message(span_alert("Cannot authenticate locked on coordinates. Please reinstate coordinate matrix.")) return - if (ismovable(M)) - if(do_teleport(M, target, channel = TELEPORT_CHANNEL_BLUESPACE)) - use_power(active_power_usage) - if(!calibrated && prob(30 - ((accuracy) * 10))) //oh dear a problem - if(ishuman(M))//don't remove people from the round randomly you jerks - var/mob/living/carbon/human/human = M - if(!(human.mob_biotypes & (MOB_ROBOTIC|MOB_MINERAL|MOB_UNDEAD|MOB_SPIRIT))) - var/datum/species/species_to_transform = /datum/species/fly - if(check_holidays(MOTH_WEEK)) - species_to_transform = /datum/species/moth - if(human.dna && human.dna.species.id != initial(species_to_transform.id)) - to_chat(M, span_hear("You hear a buzzing in your ears.")) - human.set_species(species_to_transform) - human.log_message("was turned into a [initial(species_to_transform.name)] through [src].", LOG_GAME) - calibrated = FALSE - return + if(!ismovable(M)) + return + var/turf/start_turf = get_turf(M) + if(!do_teleport(M, target, channel = TELEPORT_CHANNEL_BLUESPACE)) + return + use_power(active_power_usage) + new /obj/effect/temp_visual/portal_animation(start_turf, src, M) + if(!calibrated && ishuman(M) && prob(30 - ((accuracy) * 10))) //oh dear a problem + var/mob/living/carbon/human/human = M + if(!(human.mob_biotypes & (MOB_ROBOTIC|MOB_MINERAL|MOB_UNDEAD|MOB_SPIRIT))) + var/datum/species/species_to_transform = /datum/species/fly + if(check_holidays(MOTH_WEEK)) + species_to_transform = /datum/species/moth + if(human.dna && human.dna.species.id != initial(species_to_transform.id)) + to_chat(M, span_hear("You hear a buzzing in your ears.")) + human.set_species(species_to_transform) + human.log_message("was turned into a [initial(species_to_transform.name)] through [src].", LOG_GAME) + calibrated = FALSE /obj/machinery/teleport/hub/update_icon_state() icon_state = "[base_icon_state][panel_open ? "-o" : (is_ready() ? 1 : 0)]" diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index 6ae54a8c5105..03543a6df169 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -76,7 +76,7 @@ for(var/dna_sample in all_dna) var/datum/blood_type/blood = GLOB.blood_types[all_dna[dna_sample]] all_blood_names |= lowertext(initial(blood.reagent_type.name)) - return english_list(all_blood_names) + return english_list(all_blood_names, nothing_text = "blood") /obj/effect/decal/cleanable/blood/process(seconds_per_tick) if(dried || !can_dry) diff --git a/code/game/objects/effects/particle_holder.dm b/code/game/objects/effects/particle_holder.dm index f2cbea06aa73..b5b4fa47108d 100644 --- a/code/game/objects/effects/particle_holder.dm +++ b/code/game/objects/effects/particle_holder.dm @@ -64,7 +64,6 @@ var/mob/particle_mob = attached.loc particle_mob.vis_contents += src -/// Sets the particles position to the passed coordinate list (X, Y, Z) -/// See [https://www.byond.com/docs/ref/#/{notes}/particles] for position documentation -/obj/effect/abstract/particle_holder/proc/set_particle_position(list/pos) - particles.position = pos +/// Sets the particles position to the passed coordinates +/obj/effect/abstract/particle_holder/proc/set_particle_position(x = 0, y = 0, z = 0) + particles.position = list(x, y, z) diff --git a/code/game/objects/effects/particles/smoke.dm b/code/game/objects/effects/particles/smoke.dm index 4f31ffc08699..27249c65a683 100644 --- a/code/game/objects/effects/particles/smoke.dm +++ b/code/game/objects/effects/particles/smoke.dm @@ -37,6 +37,31 @@ spawning = 2 velocity = list(0, 0.25, 0) +/particles/smoke/cig + icon_state = list("steam_1" = 2, "steam_2" = 1, "steam_3" = 1) + count = 1 + spawning = 0.05 // used to pace it out roughly in time with breath ticks + position = list(-6, -2, 0) + gravity = list(0, 0.75, 0) + lifespan = 0.75 SECONDS + fade = 0.75 SECONDS + velocity = list(0, 0.2, 0) + scale = 0.5 + grow = 0.01 + friction = 0.5 + color = "#d0d0d09d" + +/particles/smoke/cig/big + icon_state = list("steam_1" = 1, "steam_2" = 2, "steam_3" = 2) + gravity = list(0, 0.5, 0) + velocity = list(0, 0.1, 0) + lifespan = 1 SECONDS + fade = 1 SECONDS + grow = 0.1 + scale = 0.75 + spawning = 1 + friction = 0.75 + /particles/smoke/ash icon_state = list("ash_1" = 2, "ash_2" = 2, "ash_3" = 1, "smoke_1" = 3, "smoke_2" = 2) count = 500 @@ -46,3 +71,16 @@ fadein = 0.7 SECONDS position = generator(GEN_VECTOR, list(-3, 5, 0), list(3, 6.5, 0), NORMAL_RAND) velocity = generator(GEN_VECTOR, list(-0.1, 0.4, 0), list(0.1, 0.5, 0), NORMAL_RAND) + +/particles/fog + icon = 'icons/effects/particles/smoke.dmi' + icon_state = list("chill_1" = 2, "chill_2" = 2, "chill_3" = 1) + +/particles/fog/breath + count = 1 + spawning = 1 + lifespan = 1 SECONDS + fade = 0.5 SECONDS + grow = 0.05 + spin = 2 + color = "#fcffff77" diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 091b47006732..8fb5dfea9bfb 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -140,10 +140,12 @@ no_effect = TRUE else last_effect = world.time + var/turf/start_turf = get_turf(M) if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport)) if(isprojectile(M)) var/obj/projectile/P = M P.ignore_source_check = TRUE + new /obj/effect/temp_visual/portal_animation(start_turf, src, M) return TRUE return FALSE @@ -206,3 +208,22 @@ . = ..() if (. && !isdead(M)) qdel(src) + +/** + * Animation used for transitioning atoms which are teleporting somewhere via a portal + * + * To use, pass it the atom doing the teleporting and the atom that is being teleported in init. + */ +/obj/effect/temp_visual/portal_animation + duration = 0.25 SECONDS + +/obj/effect/temp_visual/portal_animation/Initialize(mapload, atom/portal, atom/movable/teleporting) + . = ..() + if(isnull(portal) || isnull(teleporting)) + return + + appearance = teleporting.appearance + dir = teleporting.dir + layer = portal.layer + 0.01 + alpha = teleporting.alpha + animate(src, pixel_x = (portal.x * 32) - (x * 32), pixel_y = (portal.y * 32) - (y * 32), alpha = 0, time = duration) diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 30c853746b48..d6ab992cb1b2 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -137,7 +137,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM heat = 1000 throw_verb = "flick" /// Whether this cigarette has been lit. - var/lit = FALSE + VAR_FINAL/lit = FALSE /// Whether this cigarette should start lit. var/starts_lit = FALSE // Note - these are in masks.dmi not in cigarette.dmi @@ -169,6 +169,12 @@ CIGARETTE PACKETS ARE IN FANCY.DM var/choke_forever = FALSE /// When choking, what is the maximum amount of time we COULD choke for var/choke_time_max = 30 SECONDS // I am mean + /// The particle effect of the smoke rising out of the cigarette when lit + VAR_PRIVATE/obj/effect/abstract/particle_holder/cig_smoke + /// The particle effect of the smoke rising out of the mob when...smoked + VAR_PRIVATE/obj/effect/abstract/particle_holder/mob_smoke + /// How long the current mob has been smoking this cigarette + VAR_FINAL/how_long_have_we_been_smokin = 0 SECONDS /obj/item/clothing/mask/cigarette/Initialize(mapload) . = ..() @@ -184,23 +190,59 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/cigarette/Destroy() STOP_PROCESSING(SSobj, src) + QDEL_NULL(mob_smoke) + QDEL_NULL(cig_smoke) return ..() /obj/item/clothing/mask/cigarette/equipped(mob/equipee, slot) . = ..() if(!(slot & ITEM_SLOT_MASK)) - UnregisterSignal(equipee, COMSIG_HUMAN_FORCESAY) + UnregisterSignal(equipee, list(COMSIG_HUMAN_FORCESAY, COMSIG_ATOM_DIR_CHANGE)) return RegisterSignal(equipee, COMSIG_HUMAN_FORCESAY, PROC_REF(on_forcesay)) + RegisterSignal(equipee, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_mob_dir_change)) + + if(lit && iscarbon(loc)) + make_mob_smoke(loc) -/obj/item/clothing/mask/cigarette/dropped(mob/dropee) +/obj/item/clothing/mask/cigarette/dropped(mob/dropee, silent) . = ..() - UnregisterSignal(dropee, COMSIG_HUMAN_FORCESAY) + // Moving the cigarette from mask to hands (or pocket I guess) will emit a larger puff of smoke + if(!QDELETED(src) && !QDELETED(dropee) && how_long_have_we_been_smokin >= 4 SECONDS && iscarbon(dropee) && iscarbon(loc)) + var/mob/living/carbon/smoker = dropee + // This relies on the fact that dropped is called before slot is nulled + if(src == smoker.wear_mask && !smoker.incapacitated()) + long_exhale(smoker) + + UnregisterSignal(dropee, list(COMSIG_HUMAN_FORCESAY, COMSIG_ATOM_DIR_CHANGE)) + QDEL_NULL(mob_smoke) + how_long_have_we_been_smokin = 0 SECONDS /obj/item/clothing/mask/cigarette/proc/on_forcesay(mob/living/source) SIGNAL_HANDLER source.apply_status_effect(/datum/status_effect/choke, src, lit, choke_forever ? -1 : rand(25 SECONDS, choke_time_max)) +/obj/item/clothing/mask/cigarette/proc/on_mob_dir_change(mob/living/source, old_dir, new_dir) + SIGNAL_HANDLER + if(isnull(mob_smoke)) + return + update_particle_position(mob_smoke, new_dir) + +/obj/item/clothing/mask/cigarette/proc/update_particle_position(obj/effect/abstract/particle_holder/to_edit, new_dir = loc.dir) + var/new_x = 0 + var/new_layer = initial(to_edit.layer) + if(new_dir & NORTH) + new_x = 4 + new_layer = BELOW_MOB_LAYER + else if(new_dir & SOUTH) + new_x = -4 + else if(new_dir & EAST) + new_x = 8 + else if(new_dir & WEST) + new_x = -8 + to_edit.set_particle_position(new_x, 8, 0) + to_edit.layer = new_layer + /obj/item/clothing/mask/cigarette/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is huffing [src] as quickly as [user.p_they()] can! It looks like [user.p_theyre()] trying to give [user.p_them()]self cancer.")) return (TOXLOSS|OXYLOSS) @@ -266,9 +308,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM return lit = TRUE - + make_cig_smoke() if(!(flags_1 & INITIALIZED_1)) - update_icon() + update_appearance(UPDATE_ICON) return attack_verb_continuous = string_list(list("burns", "singes")) @@ -291,17 +333,16 @@ CIGARETTE PACKETS ARE IN FANCY.DM // allowing reagents to react after being lit reagents.flags &= ~(NO_REACT) reagents.handle_reactions() - update_icon() + update_appearance(UPDATE_ICON) if(flavor_text) var/turf/T = get_turf(src) T.visible_message(flavor_text) START_PROCESSING(SSobj, src) - //can't think of any other way to update the overlays :< - if(ismob(loc)) - var/mob/M = loc - M.update_worn_mask() - M.update_held_items() + if(iscarbon(loc)) + var/mob/living/carbon/smoker = loc + if(src == smoker.wear_mask) + make_mob_smoke(smoker) /obj/item/clothing/mask/cigarette/extinguish() . = ..() @@ -315,18 +356,29 @@ CIGARETTE PACKETS ARE IN FANCY.DM STOP_PROCESSING(SSobj, src) reagents.flags |= NO_REACT lit = FALSE - update_icon() - + update_appearance(UPDATE_ICON) if(ismob(loc)) - var/mob/living/M = loc - to_chat(M, span_notice("Your [name] goes out.")) - M.update_worn_mask() - M.update_held_items() + to_chat(loc, span_notice("Your [name] goes out.")) + QDEL_NULL(cig_smoke) + QDEL_NULL(mob_smoke) + +/obj/item/clothing/mask/cigarette/proc/long_exhale(mob/living/carbon/smoker) + smoker.visible_message( + span_notice("[smoker] exhales a large cloud of smoke from [src]."), + span_notice("You exhale a large cloud of smoke from [src]."), + ) + if(!isturf(smoker.loc)) + return + + var/obj/effect/abstract/particle_holder/big_smoke = new(smoker.loc, /particles/smoke/cig/big) + update_particle_position(big_smoke, smoker.dir) + QDEL_IN(big_smoke, big_smoke.particles.lifespan) /// Handles processing the reagents in the cigarette. -/obj/item/clothing/mask/cigarette/proc/handle_reagents() +/obj/item/clothing/mask/cigarette/proc/handle_reagents(seconds_per_tick) if(!reagents.total_volume) return + how_long_have_we_been_smokin += seconds_per_tick * (1 SECONDS) reagents.expose_temperature(heat, 0.05) if(!reagents.total_volume) //may have reacted and gone to 0 after expose_temperature return @@ -375,7 +427,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM open_flame(heat) if((reagents?.total_volume) && COOLDOWN_FINISHED(src, drag_cooldown)) COOLDOWN_START(src, drag_cooldown, dragtime) - handle_reagents() + handle_reagents(seconds_per_tick) /obj/item/clothing/mask/cigarette/attack_self(mob/user) if(lit) @@ -384,11 +436,17 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/cigarette/proc/put_out(mob/user, done_early = FALSE) var/atom/location = drop_location() - if(done_early) - user.visible_message(span_notice("[user] calmly drops and treads on \the [src], putting it out instantly.")) - new /obj/effect/decal/cleanable/ash(location) - else if(user) - to_chat(user, span_notice("Your [name] goes out.")) + if(!isnull(user)) + if(done_early) + if(isfloorturf(location) && location.has_gravity()) + user.visible_message(span_notice("[user] calmly drops and treads on [src], putting it out instantly.")) + new /obj/effect/decal/cleanable/ash(location) + long_exhale(user) + else + user.visible_message(span_notice("[user] pinches out [src].")) + how_long_have_we_been_smokin = 0 SECONDS + else + to_chat(user, span_notice("Your [name] goes out.")) new type_butt(location) qdel(src) @@ -415,6 +473,15 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/cigarette/get_temperature() return lit * heat +/obj/item/clothing/mask/cigarette/proc/make_mob_smoke(mob/living/smoker) + mob_smoke = new(smoker, /particles/smoke/cig) + update_particle_position(mob_smoke, smoker.dir) + return mob_smoke + +/obj/item/clothing/mask/cigarette/proc/make_cig_smoke() + cig_smoke = new(src, /particles/smoke/cig) + cig_smoke.particles.scale *= 1.5 + return cig_smoke // Cigarette brands. /obj/item/clothing/mask/cigarette/space_cigarette @@ -630,7 +697,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM name = "smoking pipe" desc = "A pipe, for smoking. Probably made of meerschaum or something." icon_state = "pipeoff" - icon_on = "pipeon" //Note - these are in masks.dmi + icon_on = "pipeoff" //Note - these are in masks.dmi icon_off = "pipeoff" inhand_icon_state = null inhand_icon_on = null @@ -645,7 +712,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/cigarette/pipe/Initialize(mapload) . = ..() - update_name() + update_appearance(UPDATE_NAME) /obj/item/clothing/mask/cigarette/pipe/update_name() . = ..() @@ -660,11 +727,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM if(user) to_chat(user, span_notice("Your [name] goes out.")) packeditem = null - update_icon() - - inhand_icon_state = icon_off - user?.update_worn_mask() + update_appearance(UPDATE_ICON) STOP_PROCESSING(SSobj, src) + QDEL_NULL(cig_smoke) /obj/item/clothing/mask/cigarette/pipe/attackby(obj/item/thing, mob/user, params) if(!istype(thing, /obj/item/food/grown)) @@ -703,7 +768,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM name = "corn cob pipe" desc = "A nicotine delivery system popularized by folksy backwoodsmen and kept popular in the modern age and beyond by space hipsters. Can be loaded with objects." icon_state = "cobpipeoff" - icon_on = "cobpipeon" //Note - these are in masks.dmi + icon_on = "cobpipeoff" //Note - these are in masks.dmi icon_off = "cobpipeoff" inhand_icon_on = null inhand_icon_off = null diff --git a/code/game/objects/items/stacks/tickets.dm b/code/game/objects/items/stacks/tickets.dm index 20e6843b9770..d1bd7681b8a8 100644 --- a/code/game/objects/items/stacks/tickets.dm +++ b/code/game/objects/items/stacks/tickets.dm @@ -8,14 +8,9 @@ max_amount = 30 merge_type = /obj/item/stack/arcadeticket -/obj/item/stack/arcadeticket/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) - . = ..() - update_appearance() - /obj/item/stack/arcadeticket/update_icon_state() . = ..() - var/amount = get_amount() - switch(amount) + switch(get_amount()) if(12 to INFINITY) icon_state = "arcade-ticket_4" if(6 to 12) @@ -25,10 +20,5 @@ else icon_state = "arcade-ticket" -/obj/item/stack/arcadeticket/proc/pay_tickets() - amount -= 2 - if (amount == 0) - qdel(src) - /obj/item/stack/arcadeticket/thirty amount = 30 diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 439a2ce336e2..f6ace35c2ce7 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -163,10 +163,12 @@ if(!proximity) return - if(isOn()) + if(isOn() && ismovable(attacked_atom)) + use(1) + var/turf/location = get_turf(user) + location.hotspot_expose(700, 50, 1) . |= AFTERATTACK_PROCESSED_ITEM if (!QDELETED(attacked_atom) && isliving(attacked_atom)) // can't ignite something that doesn't exist - handle_fuel_and_temps(1, user) var/mob/living/attacked_mob = attacked_atom if(attacked_mob.ignite_mob()) message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(attacked_mob)] on fire with [src] at [AREACOORD(user)]") @@ -180,21 +182,6 @@ return . -/obj/item/weldingtool/attack_qdeleted(atom/attacked_atom, mob/user, proximity) - . = ..() - if(!proximity) - return - - if(isOn()) - handle_fuel_and_temps(1, user) - - if(!QDELETED(attacked_atom) && isliving(attacked_atom)) // can't ignite something that doesn't exist - var/mob/living/attacked_mob = attacked_atom - if(attacked_mob.ignite_mob()) - message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(attacked_mob)] on fire with [src] at [AREACOORD(user)].") - user.log_message("set [key_name(attacked_mob)] on fire with [src]", LOG_ATTACK) - - /obj/item/weldingtool/attack_self(mob/user) if(src.reagents.has_reagent(/datum/reagent/toxin/plasma)) message_admins("[ADMIN_LOOKUPFLW(user)] activated a rigged welder at [AREACOORD(user)].") @@ -204,11 +191,6 @@ update_appearance() -/obj/item/weldingtool/proc/handle_fuel_and_temps(used = 0, mob/living/user) - use(used) - var/turf/location = get_turf(user) - location.hotspot_expose(700, 50, 1) - /// Returns the amount of fuel in the welder /obj/item/weldingtool/proc/get_fuel() return reagents.get_reagent_amount(/datum/reagent/fuel) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/bar.dm b/code/game/objects/structures/crates_lockers/closets/secure/bar.dm index d10d767f0ccc..ca931d4c6ab1 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/bar.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/bar.dm @@ -31,7 +31,7 @@ for(var/i in 1 to 5) new /obj/item/reagent_containers/cup/glass/colocup(src) -/obj/structure/closet/secure/closet/bar/lavaland_bartender_clothes +/obj/structure/closet/secure_closet/bar/lavaland_bartender_clothes name = "bartender's closet" /obj/structure/closet/secure_closet/bar/lavaland_bartender_clothes/PopulateContents() @@ -39,4 +39,3 @@ new /obj/item/clothing/glasses/sunglasses/reagent(src) new /obj/item/clothing/suit/costume/hawaiian(src) new /obj/item/clothing/shoes/sandal/beach(src) - diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 18a3e15c9871..3e45232fbaea 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -45,6 +45,8 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an var/locked = FALSE ///Cooldown between breakout msesages. COOLDOWN_DECLARE(breakout_message_cooldown) + /// Cooldown between being able to slide the tray in or out. + COOLDOWN_DECLARE(open_close_cd) /obj/structure/bodycontainer/Initialize(mapload) . = ..() @@ -133,30 +135,90 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) /obj/structure/bodycontainer/proc/open() - playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) + if(!COOLDOWN_FINISHED(src, open_close_cd)) + return FALSE + + COOLDOWN_START(src, open_close_cd, 0.25 SECONDS) + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) playsound(src, 'sound/effects/roll.ogg', 5, TRUE) - var/turf/T = get_step(src, dir) - if (connected) - connected.setDir(dir) - for(var/atom/movable/AM in src) - AM.forceMove(T) + var/turf/dump_turf = get_step(src, dir) + connected?.setDir(dir) + for(var/atom/movable/moving in src) + moving.forceMove(dump_turf) + animate_slide_out(moving) update_appearance() + return TRUE /obj/structure/bodycontainer/proc/close() + if(!COOLDOWN_FINISHED(src, open_close_cd)) + return FALSE + + COOLDOWN_START(src, open_close_cd, 0.5 SECONDS) playsound(src, 'sound/effects/roll.ogg', 5, TRUE) playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - for(var/atom/movable/AM in connected.loc) - if(!AM.anchored || AM == connected) - if(isliving(AM)) - var/mob/living/living_mob = AM - if(living_mob.incorporeal_move) - continue - else if(istype(AM, /obj/effect/dummy/phased_mob)) + var/turf/close_loc = connected.loc + for(var/atom/movable/entering in close_loc) + if(entering.anchored && entering != connected) + continue + if(isliving(entering)) + var/mob/living/living_mob = entering + if(living_mob.incorporeal_move) continue - else if(isdead(AM)) - continue - AM.forceMove(src) + else if(istype(entering, /obj/effect/dummy/phased_mob) || isdead(entering)) + continue + animate_slide_in(entering, close_loc) + entering.forceMove(src) update_appearance() + return TRUE + +#define SLIDE_LENGTH (0.3 SECONDS) + +/// Slides the passed object out of the morgue tray. +/obj/structure/bodycontainer/proc/animate_slide_out(atom/movable/animated) + var/old_layer = animated.layer + animated.layer = layer - (animated == connected ? 0.03 : 0.01) + animated.pixel_x = animated.base_pixel_x + (x * 32) - (animated.x * 32) + animated.pixel_y = animated.base_pixel_y + (y * 32) - (animated.y * 32) + animate( + animated, + pixel_x = animated.base_pixel_x, + pixel_y = animated.base_pixel_y, + time = SLIDE_LENGTH, + easing = CUBIC_EASING|EASE_OUT, + flags = ANIMATION_PARALLEL, + ) + addtimer(VARSET_CALLBACK(animated, layer, old_layer), SLIDE_LENGTH) + +/// Slides the passed object into the morgue tray from the passed turf. +/obj/structure/bodycontainer/proc/animate_slide_in(atom/movable/animated, turf/from_loc) + // It's easier to just make a visual for entering than to animate the object itself + var/obj/effect/temp_visual/morgue_content/visual = new(from_loc, animated) + visual.layer = layer - (animated == connected ? 0.03 : 0.01) + animate( + visual, + pixel_x = visual.base_pixel_x + (x * 32) - (visual.x * 32), + pixel_y = visual.base_pixel_y + (y * 32) - (visual.y * 32), + time = SLIDE_LENGTH, + easing = CUBIC_EASING|EASE_IN, + flags = ANIMATION_PARALLEL, + ) + +/// Used to mimic the appearance of an object sliding into a morgue tray. +/obj/effect/temp_visual/morgue_content + duration = SLIDE_LENGTH + +/obj/effect/temp_visual/morgue_content/Initialize(mapload, atom/movable/sliding_in) + . = ..() + if(isnull(sliding_in)) + return + + appearance = sliding_in.appearance + dir = sliding_in.dir + alpha = sliding_in.alpha + base_pixel_x = sliding_in.base_pixel_x + base_pixel_y = sliding_in.base_pixel_y + +#undef SLIDE_LENGTH #define MORGUE_EMPTY 1 #define MORGUE_NO_MOBS 2 @@ -536,6 +598,7 @@ GLOBAL_LIST_EMPTY(crematoriums) name = "crematorium tray" desc = "Apply body before burning." icon_state = "cremat" + layer = /obj/structure/bodycontainer/crematorium::layer - 0.03 /* * Morgue tray @@ -546,6 +609,7 @@ GLOBAL_LIST_EMPTY(crematoriums) icon = 'icons/obj/structures.dmi' icon_state = "morguet" pass_flags_self = PASSTABLE | LETPASSTHROW + layer = /obj/structure/bodycontainer/morgue::layer - 0.03 /obj/structure/tray/m_tray/CanAllowThrough(atom/movable/mover, border_dir) . = ..() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index ad9695434169..52e694d4aa44 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -715,7 +715,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/tinted/frosted/spaw /obj/structure/window/reinforced/fulltile name = "full tile reinforced window" - desc = "A full tile reinforced window" + desc = "A full tile window that is reinforced with metal rods." icon = 'icons/obj/smooth_structures/reinforced_window.dmi' icon_state = "reinforced_window-0" base_icon_state = "reinforced_window" diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index 1a174723d85b..1ea32b0d0a4c 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -52,7 +52,8 @@ /turf/open/lava/Destroy() for(var/mob/living/leaving_mob in contents) - REMOVE_TRAIT(leaving_mob, TRAIT_PERMANENTLY_ONFIRE, TURF_TRAIT) + leaving_mob.RemoveElement(/datum/element/perma_fire_overlay) + REMOVE_TRAIT(leaving_mob, TRAIT_NO_EXTINGUISH, TURF_TRAIT) return ..() /turf/open/lava/update_overlays() @@ -144,7 +145,8 @@ /turf/open/lava/Exited(atom/movable/gone, direction) . = ..() if(isliving(gone) && !islava(gone.loc)) - REMOVE_TRAIT(gone, TRAIT_PERMANENTLY_ONFIRE, TURF_TRAIT) + gone.RemoveElement(/datum/element/perma_fire_overlay) + REMOVE_TRAIT(gone, TRAIT_NO_EXTINGUISH, TURF_TRAIT) /turf/open/lava/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(burn_stuff(AM)) @@ -296,7 +298,7 @@ if(isobj(burn_target)) var/obj/burn_obj = burn_target if(burn_obj.resistance_flags & ON_FIRE) // already on fire; skip it. - return + return TRUE if(!(burn_obj.resistance_flags & FLAMMABLE)) burn_obj.resistance_flags |= FLAMMABLE //Even fireproof things burn up in lava if(burn_obj.resistance_flags & FIRE_PROOF) @@ -305,17 +307,21 @@ burn_obj.set_armor_rating(FIRE, 50) burn_obj.fire_act(temperature_damage, 1000 * seconds_per_tick) if(istype(burn_obj, /obj/structure/closet)) - var/obj/structure/closet/burn_closet = burn_obj - for(var/burn_content in burn_closet.contents) + for(var/burn_content in burn_target) burn_stuff(burn_content) - return + return TRUE - var/mob/living/burn_living = burn_target - ADD_TRAIT(burn_living, TRAIT_PERMANENTLY_ONFIRE, TURF_TRAIT) - burn_living.ignite_mob() - burn_living.adjust_fire_stacks(lava_firestacks * seconds_per_tick) - burn_living.update_fire() - burn_living.adjustFireLoss(lava_damage * seconds_per_tick) + if(isliving(burn_target)) + var/mob/living/burn_living = burn_target + if(!HAS_TRAIT_FROM(burn_living, TRAIT_NO_EXTINGUISH, TURF_TRAIT)) + burn_living.AddElement(/datum/element/perma_fire_overlay) + ADD_TRAIT(burn_living, TRAIT_NO_EXTINGUISH, TURF_TRAIT) + burn_living.adjust_fire_stacks(lava_firestacks * seconds_per_tick) + burn_living.ignite_mob() + burn_living.adjustFireLoss(lava_damage * seconds_per_tick) + return TRUE + + return FALSE /turf/open/lava/can_cross_safely(atom/movable/crossing) return HAS_TRAIT(src, TRAIT_LAVA_STOPPED) || HAS_TRAIT(crossing, immunity_trait ) || HAS_TRAIT(crossing, TRAIT_MOVE_FLYING) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 67271e7f0731..d158a53effa4 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1174,8 +1174,7 @@ GLOBAL_PROTECT(admin_verbs_poll) var/desired_mob = text2path(attempted_target_path) if(!ispath(desired_mob)) - var/static/list/mob_paths = make_types_fancy(subtypesof(/mob/living)) - desired_mob = pick_closest_path(attempted_target_path, mob_paths) + desired_mob = pick_closest_path(attempted_target_path, make_types_fancy(subtypesof(/mob/living))) if(isnull(desired_mob) || !ispath(desired_mob) || QDELETED(head)) return //The user pressed "Cancel" diff --git a/code/modules/antagonists/nightmare/nightmare_species.dm b/code/modules/antagonists/nightmare/nightmare_species.dm index 068bb2b6c5c1..38db2dfae865 100644 --- a/code/modules/antagonists/nightmare/nightmare_species.dm +++ b/code/modules/antagonists/nightmare/nightmare_species.dm @@ -39,7 +39,6 @@ . = ..() C.fully_replace_character_name(null, pick(GLOB.nightmare_names)) - C.set_safe_hunger_level() /datum/species/shadow/nightmare/check_roundstart_eligible() return FALSE diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 237ebfdbabdd..a0c32b76977f 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -16,6 +16,7 @@ attack_verb_simple = list("challenge") strip_delay = 20 equip_delay_other = 40 + article = "a pair of" blood_overlay_type = "glove" // NON-MODULE CHANGE // Path variable. If defined, will produced the type through interaction with wirecutters. var/cut_type = null diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 6dc4f119cbf0..009c29e12eb3 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -12,6 +12,7 @@ armor_type = /datum/armor/clothing_shoes slowdown = SHOES_SLOWDOWN strip_delay = 1 SECONDS + article = "a pair of" blood_overlay_type = "shoe" // NON-MODULE CHANGE reworking clothing blood overlays diff --git a/code/modules/food_and_drinks/machinery/stove_component.dm b/code/modules/food_and_drinks/machinery/stove_component.dm index 697e27f58490..c9c710642236 100644 --- a/code/modules/food_and_drinks/machinery/stove_component.dm +++ b/code/modules/food_and_drinks/machinery/stove_component.dm @@ -249,7 +249,7 @@ return // this gets badly murdered by sidemap soup_smoke = new(parent, particle_type) - soup_smoke.set_particle_position(list(container_x, round(world.icon_size * 0.66), 0)) + soup_smoke.set_particle_position(container_x, round(world.icon_size * 0.66), 0) return QDEL_NULL(soup_smoke) diff --git a/code/modules/forensics/_forensics.dm b/code/modules/forensics/_forensics.dm index 75cbc55df280..77ae7c2b3861 100644 --- a/code/modules/forensics/_forensics.dm +++ b/code/modules/forensics/_forensics.dm @@ -8,8 +8,8 @@ * * List of clothing fibers on the atom */ /datum/forensics - /// Weakref to the parent owning this datum - var/datum/weakref/parent + /// Ref to the parent owning this datum + var/atom/parent /** * List of fingerprints on this atom * @@ -39,15 +39,20 @@ */ var/list/fibers -/datum/forensics/New(atom/parent, fingerprints, hiddenprints, blood_DNA, fibers) +/datum/forensics/New(atom/parent, list/fingerprints, list/hiddenprints, list/blood_DNA, list/fibers) if(!isatom(parent)) stack_trace("We tried adding a forensics datum to something that isnt an atom. What the hell are you doing?") qdel(src) return + if(QDELING(parent)) + stack_trace("Adding a forensics datum to a qdeling atom ([parent])") + qdel(src) + return + RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_act)) - src.parent = WEAKREF(parent) + src.parent = parent src.fingerprints = fingerprints src.hiddenprints = hiddenprints src.blood_DNA = blood_DNA @@ -67,9 +72,7 @@ check_blood() /datum/forensics/Destroy(force) - var/atom/parent_atom = parent.resolve() - if (!isnull(parent_atom)) - UnregisterSignal(parent_atom, list(COMSIG_COMPONENT_CLEAN_ACT)) + UnregisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT) return ..() /// Empties the fingerprints list @@ -147,8 +150,7 @@ /// Adds a single fiber /datum/forensics/proc/add_fibers(mob/living/carbon/human/suspect) var/fibertext - var/atom/actual_parent = parent.resolve() - var/item_multiplier = isitem(actual_parent) ? ITEM_FIBER_MULTIPLIER : NON_ITEM_FIBER_MULTIPLIER + var/item_multiplier = isitem(parent) ? ITEM_FIBER_MULTIPLIER : NON_ITEM_FIBER_MULTIPLIER if(suspect.wear_suit) fibertext = "Material from \a [suspect.wear_suit]." if(prob(10 * item_multiplier) && !LAZYACCESS(fibers, fibertext)) @@ -214,8 +216,7 @@ if(last_stamp_pos) LAZYSET(hiddenprints, suspect.key, copytext(hiddenprints[suspect.key], 1, last_stamp_pos)) hiddenprints[suspect.key] += "\nLast: \[[current_time]\] \"[suspect.real_name]\"[has_gloves]. Ckey: [suspect.ckey]" //made sure to be existing by if(!LAZYACCESS);else - var/atom/parent_atom = parent.resolve() - parent_atom.fingerprintslast = suspect.ckey + parent.fingerprintslast = suspect.ckey return TRUE /// Adds the given list into blood_DNA @@ -230,11 +231,8 @@ /// Updates the blood displayed on parent /datum/forensics/proc/check_blood() - if(!parent || !isitem(parent.resolve())) - return - if(isorgan(parent.resolve())) // organs don't spawn with blood decals by default + if(!isitem(parent) || isorgan(parent)) // organs don't spawn with blood decals by default return if(!length(blood_DNA)) return - var/atom/parent_atom = parent.resolve() - parent_atom.AddElement(/datum/element/decal/blood) + parent.AddElement(/datum/element/decal/blood) diff --git a/code/modules/forensics/forensics_helpers.dm b/code/modules/forensics/forensics_helpers.dm index 72dc89c6a4ac..e7d71a037f1e 100644 --- a/code/modules/forensics/forensics_helpers.dm +++ b/code/modules/forensics/forensics_helpers.dm @@ -1,5 +1,7 @@ /// Adds a list of fingerprints to the atom /atom/proc/add_fingerprint_list(list/fingerprints_to_add) //ASSOC LIST FINGERPRINT = FINGERPRINT + if (QDELING(src)) + return FALSE if (isnull(fingerprints_to_add)) return if (forensics) @@ -19,6 +21,8 @@ /// Add a list of fibers to the atom /atom/proc/add_fiber_list(list/fibers_to_add) //ASSOC LIST FIBERTEXT = FIBERTEXT + if (QDELING(src)) + return FALSE if (isnull(fibers_to_add)) return if (forensics) @@ -29,6 +33,8 @@ /// Adds a single fiber to the atom /atom/proc/add_fibers(mob/living/carbon/human/suspect) + if (QDELING(src)) + return FALSE var/old = 0 if(suspect.gloves && istype(suspect.gloves, /obj/item/clothing)) var/obj/item/clothing/gloves/suspect_gloves = suspect.gloves @@ -47,6 +53,8 @@ /// Adds a list of hiddenprints to the atom /atom/proc/add_hiddenprint_list(list/hiddenprints_to_add) //NOTE: THIS IS FOR ADMINISTRATION FINGERPRINTS, YOU MUST CUSTOM SET THIS TO INCLUDE CKEY/REAL NAMES! CHECK FORENSICS.DM + if (QDELING(src)) + return FALSE if (isnull(hiddenprints_to_add)) return if (forensics) @@ -57,6 +65,8 @@ /// Adds a single hiddenprint to the atom /atom/proc/add_hiddenprint(mob/suspect) + if (QDELING(src)) + return FALSE if (isnull(forensics)) forensics = new(src) forensics.add_hiddenprint(suspect) @@ -92,6 +102,8 @@ return FALSE /obj/add_blood_DNA(list/blood_DNA_to_add) + if (QDELING(src)) + return FALSE if (isnull(blood_DNA_to_add)) return FALSE if (forensics) @@ -139,6 +151,8 @@ return FALSE /mob/living/carbon/human/add_blood_DNA(list/blood_DNA_to_add, list/datum/disease/diseases) + if(QDELING(src)) + return FALSE if(wear_suit) wear_suit.add_blood_DNA(blood_DNA_to_add) update_worn_oversuit() diff --git a/code/modules/hallucination/fake_alert.dm b/code/modules/hallucination/fake_alert.dm index 537f6e294909..6e10daf73aa9 100644 --- a/code/modules/hallucination/fake_alert.dm +++ b/code/modules/hallucination/fake_alert.dm @@ -63,10 +63,6 @@ alert_category = ALERT_TOO_MUCH_CO2 alert_type = /atom/movable/screen/alert/too_much_co2 -/datum/hallucination/fake_alert/nutrition - alert_category = ALERT_NUTRITION - alert_type = list(/atom/movable/screen/alert/fat, /atom/movable/screen/alert/starving) - /datum/hallucination/fake_alert/gravity alert_category = ALERT_GRAVITY alert_type = /atom/movable/screen/alert/weightless diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm index 6a5d1b1d5c4e..d1f58f2e3ce0 100644 --- a/code/modules/library/bibles.dm +++ b/code/modules/library/bibles.dm @@ -88,7 +88,6 @@ GLOBAL_LIST_INIT(bibleitemstates, list( active_slots = ITEM_SLOT_SUITSTORE,\ on_intercepted = CALLBACK(src, PROC_REF(on_intercepted_bullet)),\ ) - carve_out() /obj/item/book/bible/Destroy(force) QDEL_NULL(bullet_catcher) @@ -345,6 +344,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list( /obj/item/book/bible/booze/Initialize(mapload) . = ..() + carve_out() new /obj/item/reagent_containers/cup/glass/bottle/whiskey(src) /obj/item/book/bible/syndicate diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 7676801a0a92..e0b8c322a748 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -856,6 +856,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) var/list/blacklisted_from_rng_placement = list( SPECIES_ETHEREAL, // they revive on death which is bad juju SPECIES_HUMAN, // already have a 50% chance of being selected + SPECIES_LIZARD_SILVER, // NON-MODULE CHANGE - Nope ) /obj/effect/mapping_helpers/dead_body_placer/Initialize(mapload) diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index 8d7db7f8e850..615eb55898c9 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -14,7 +14,6 @@ INITIALIZE_IMMEDIATE(/mob/dead) flags_1 |= INITIALIZED_1 // Initial is non standard here, but ghosts move before they get here so it's needed. this is a cold path too so it's ok SET_PLANE_IMPLICIT(src, initial(plane)) - tag = "mob_[next_mob_id++]" add_to_mob_list() prepare_huds() diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index ba07c944652e..d69665d6bcde 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -270,17 +270,17 @@ /mob/living/basic/on_fire_stack(seconds_per_tick, datum/status_effect/fire_handler/fire_stacks/fire_handler) adjust_bodytemperature((maximum_survivable_temperature + (fire_handler.stacks * 12)) * 0.5 * seconds_per_tick) -/mob/living/basic/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/effects/onfire.dmi', "generic_fire") - if(on_fire && isnull(last_icon_state)) - add_overlay(fire_overlay) - return fire_overlay - else if(!on_fire && !isnull(last_icon_state)) - cut_overlay(fire_overlay) - return null - else if(on_fire && !isnull(last_icon_state)) - return last_icon_state - return null +/mob/living/basic/get_fire_overlay(stacks, on_fire) + var/fire_icon = "generic_fire" + if(!GLOB.fire_appearances[fire_icon]) + GLOB.fire_appearances[fire_icon] = mutable_appearance( + 'icons/mob/effects/onfire.dmi', + fire_icon, + -HIGHEST_LAYER, + appearance_flags = RESET_COLOR, + ) + + return GLOB.fire_appearances[fire_icon] /mob/living/basic/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, ignore_animation = TRUE) . = ..() diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index ca6a61931889..5e53977c4a22 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -2,9 +2,7 @@ blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_SALUTE_MESSAGES = list( - "salutes", - "nods in appreciation towards", - "fist bumps", + "performs an elaborate salute for", ) ) @@ -213,17 +211,13 @@ /datum/ai_behavior/find_and_set/valid_authority behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 30 SECONDS + action_cooldown = BOT_COMMISSIONED_SALUTE_DELAY /datum/ai_behavior/find_and_set/valid_authority/search_tactic(datum/ai_controller/controller, locate_path, search_range) - for(var/mob/living/robot in oview(search_range, controller.pawn)) - if(istype(robot, /mob/living/simple_animal/bot/secbot)) + for(var/mob/living/basic/bot/cleanbot/robot in oview(search_range, controller.pawn)) + // NON-MODULE CHANGE - reverts basic bots to only salute commissioned cleanbots, not beepsky + if(robot.comissioned) return robot - if(!istype(robot, /mob/living/basic/bot/cleanbot)) - continue - var/mob/living/basic/bot/cleanbot/potential_bot = robot - if(potential_bot.comissioned) - return potential_bot return null /datum/ai_behavior/salute_authority @@ -243,7 +237,7 @@ if(our_hat) salute_list += "tips [our_hat] at " - bot_pawn.manual_emote(pick(salute_list) + " [controller.blackboard[target_key]]") + bot_pawn.manual_emote(pick(salute_list) + " [controller.blackboard[target_key]]!") finish_action(controller, TRUE, target_key) return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 2bc1ef8d245c..1a84a3ebcd7c 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -398,6 +398,9 @@ if((HAS_TRAIT(src, TRAIT_NOHUNGER) || HAS_TRAIT(src, TRAIT_TOXINLOVER)) && !force) return TRUE + if(!force && HAS_TRAIT(src, TRAIT_STRONG_STOMACH)) + lost_nutrition *= 0.5 + SEND_SIGNAL(src, COMSIG_CARBON_VOMITED, distance, force) // cache some stuff that we'll need later (at least multiple times) @@ -414,7 +417,10 @@ span_userdanger("You try to throw up, but there's nothing in your stomach!"), ) if(stun) - Stun(20 SECONDS) + var/stun_time = 20 SECONDS + if(HAS_TRAIT(src, TRAIT_STRONG_STOMACH)) + stun_time *= 0.5 + Stun(stun_time) if(knockdown) Knockdown(20 SECONDS) return TRUE @@ -437,11 +443,14 @@ add_mood_event("vomit", /datum/mood_event/vomit) if(stun) - Stun(8 SECONDS) + var/stun_time = 8 SECONDS + if(!blood && HAS_TRAIT(src, TRAIT_STRONG_STOMACH)) + stun_time *= 0.5 + Stun(stun_time) if(knockdown) Knockdown(8 SECONDS) - playsound(get_turf(src), 'sound/effects/splat.ogg', 50, TRUE) + playsound(src, 'sound/effects/splat.ogg', 50, TRUE) var/need_mob_update = FALSE var/turf/location = get_turf(src) @@ -802,8 +811,7 @@ hud_used.stamina.icon_state = "stamina_full" /mob/living/carbon/proc/update_spacesuit_hud_icon(cell_state = "empty") - if(hud_used?.spacesuit) - hud_used.spacesuit.icon_state = "spacesuit_[cell_state]" + hud_used?.spacesuit?.icon_state = "spacesuit_[cell_state]" /mob/living/carbon/set_health(new_value) . = ..() @@ -883,7 +891,11 @@ if(!HAS_TRAIT(src, TRAIT_LIVERLESS_METABOLISM) && !isnull(dna?.species.mutantliver) && !get_organ_slot(ORGAN_SLOT_LIVER)) return FALSE - return ..() + . = ..() + if(.) // if revived successfully + set_heartattack(FALSE) + + return . /mob/living/carbon/fully_heal(heal_flags = HEAL_ALL) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 01d45b0686dd..89183f095f45 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -618,7 +618,7 @@ /obj/item/hand_item/self_grasp/Destroy() if(user) - to_chat(user, span_warning("You stop holding onto your[grasped_part ? " [grasped_part.name]" : "self"].")) + to_chat(user, span_warning("You stop holding onto your[grasped_part ? " [grasped_part.plaintext_zone]" : "self"].")) UnregisterSignal(user, COMSIG_QDELETING) if(grasped_part) UnregisterSignal(grasped_part, list(COMSIG_CARBON_REMOVE_LIMB, COMSIG_QDELETING)) @@ -649,7 +649,7 @@ var/bleed_rate = grasped_part.get_modified_bleed_rate() var/bleeding_text = (bleed_rate ? ", trying to stop the bleeding" : "") - user.visible_message(span_danger("[user] grasps at [user.p_their()] [grasped_part.name][bleeding_text]."), span_notice("You grab hold of your [grasped_part.name] tightly."), vision_distance=COMBAT_MESSAGE_RANGE) + user.visible_message(span_danger("[user] grasps at [user.p_their()] [grasped_part.plaintext_zone][bleeding_text]."), span_notice("You grab hold of your [grasped_part.plaintext_zone] tightly."), vision_distance=COMBAT_MESSAGE_RANGE) playsound(get_turf(src), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) return TRUE diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index 6f82be475e70..1a5b79bd86ab 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -8,27 +8,30 @@ /mob/living/carbon/Move(NewLoc, direct) . = ..() - if(. && !(movement_type & FLOATING)) //floating is easy - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - set_nutrition(NUTRITION_LEVEL_FED - 1) //just less than feeling vigorous - else if(nutrition && stat != DEAD) - adjust_nutrition(-(HUNGER_FACTOR/10)) - if(move_intent == MOVE_INTENT_RUN) - adjust_nutrition(-(HUNGER_FACTOR/10)) + if(!. || (movement_type & FLOATING)) //floating is easy + return + if(stat == DEAD) + return - // NON-MODULE CHANGE START - if(move_intent == MOVE_INTENT_RUN && !(movement_type & FLYING) && (mobility_flags & (MOBILITY_MOVE|MOBILITY_STAND)) && !pulledby) - drain_sprint() - if(momentum_dir & direct) - momentum_distance++ - if(!has_momentum && momentum_distance >= 4 && add_movespeed_modifier(/datum/movespeed_modifier/momentum)) - has_momentum = TRUE - else - momentum_dir = direct - momentum_distance = 0 - if(has_momentum && remove_movespeed_modifier(/datum/movespeed_modifier/momentum)) - has_momentum = FALSE - // NON-MODULE CHANGE END + if(nutrition > 0) + var/hunger_loss = HUNGER_FACTOR / 10 + if(move_intent == MOVE_INTENT_RUN) + hunger_loss *= 2 + adjust_nutrition(-1 * hunger_loss) + + // NON-MODULE CHANGE START + if(move_intent == MOVE_INTENT_RUN && !(movement_type & FLYING) && (mobility_flags & (MOBILITY_MOVE|MOBILITY_STAND)) && !pulledby) + drain_sprint() + if(momentum_dir & direct) + momentum_distance++ + if(!has_momentum && momentum_distance >= 4 && add_movespeed_modifier(/datum/movespeed_modifier/momentum)) + has_momentum = TRUE + else + momentum_dir = direct + momentum_distance = 0 + if(has_momentum && remove_movespeed_modifier(/datum/movespeed_modifier/momentum)) + has_momentum = FALSE + // NON-MODULE CHANGE END /mob/living/carbon/set_usable_legs(new_value) . = ..() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 08e734a01f40..cebeab7cfcd3 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -278,8 +278,8 @@ update_held_items() update_worn_handcuffs() update_worn_legcuffs() - update_fire() update_body() + update_appearance(UPDATE_OVERLAYS) /mob/living/carbon/update_held_items() . = ..() @@ -315,27 +315,18 @@ hands += I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) return hands -/mob/living/carbon/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - var/fire_icon = "[dna?.species.fire_overlay || "human"]_[stacks > MOB_BIG_FIRE_STACK_THRESHOLD ? "big_fire" : "small_fire"][suffix]" +/mob/living/carbon/get_fire_overlay(stacks, on_fire) + var/fire_icon = "[dna?.species.fire_overlay || "human"]_[stacks > MOB_BIG_FIRE_STACK_THRESHOLD ? "big_fire" : "small_fire"]" if(!GLOB.fire_appearances[fire_icon]) - GLOB.fire_appearances[fire_icon] = mutable_appearance('icons/mob/effects/onfire.dmi', fire_icon, -FIRE_LAYER, appearance_flags = RESET_COLOR) - - if((stacks > 0 && on_fire) || HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) - if(fire_icon == last_icon_state) - return last_icon_state - - remove_overlay(FIRE_LAYER) - overlays_standing[FIRE_LAYER] = GLOB.fire_appearances[fire_icon] - apply_overlay(FIRE_LAYER) - return fire_icon - - if(!last_icon_state) - return last_icon_state - - remove_overlay(FIRE_LAYER) - apply_overlay(FIRE_LAYER) - return null + GLOB.fire_appearances[fire_icon] = mutable_appearance( + 'icons/mob/effects/onfire.dmi', + fire_icon, + -HIGHEST_LAYER, + appearance_flags = RESET_COLOR, + ) + + return GLOB.fire_appearances[fire_icon] /mob/living/carbon/update_damage_overlays() remove_overlay(DAMAGE_LAYER) diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index 9ba533a33b5d..e01315094a11 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -160,7 +160,7 @@ key_third_person = "snaps" message = "snaps their fingers." message_param = "snaps their fingers at %t." - emote_type = EMOTE_AUDIBLE + emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE hands_use_check = TRUE muzzle_ignore = TRUE diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 273ba1e185d3..17cb76551066 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -32,8 +32,11 @@ GLOBAL_LIST_EMPTY(features_by_species) ///The maximum number of bodyparts this species can have. var/max_bodypart_count = 6 - ///This allows races to have specific hair colors. If null, it uses the H's hair/facial hair colors. If "mutcolor", it uses the H's mutant_color. If "fixedmutcolor", it uses fixedmutcolor - var/hair_color + /// This allows races to have specific hair colors. + /// If null, it uses the mob's hair/facial hair colors. + /// If USE_MUTANT_COLOR, it uses the mob's mutant_color. + /// If USE_FIXED_MUTANT_COLOR, it uses fixedmutcolor + var/hair_color_mode ///The alpha used by the hair. 255 is completely solid, 0 is invisible. var/hair_alpha = 255 ///The alpha used by the facial hair. 255 is completely solid, 0 is invisible. @@ -113,8 +116,6 @@ GLOBAL_LIST_EMPTY(features_by_species) var/siemens_coeff = 1 ///To use MUTCOLOR with a fixed color that's independent of the mcolor feature in DNA. var/fixed_mut_color = "" - ///A fixed hair color that's independent of the mcolor feature in DNA. - var/fixed_hair_color = "" ///Special mutation that can be found in the genepool exclusively in this species. Dont leave empty or changing species will be a headache var/inert_mutation = /datum/mutation/human/dwarfism ///Used to set the mob's death_sound upon species change @@ -768,19 +769,11 @@ GLOBAL_LIST_EMPTY(features_by_species) if(!forced_colour) switch(accessory.color_src) if(MUTANT_COLOR) - if(fixed_mut_color) - accessory_overlay.color = fixed_mut_color - else - accessory_overlay.color = source.dna.features["mcolor"] + accessory_overlay.color = fixed_mut_color || source.dna.features["mcolor"] if(HAIR_COLOR) - if(hair_color == "mutcolor") - accessory_overlay.color = source.dna.features["mcolor"] - else if(hair_color == "fixedmutcolor") - accessory_overlay.color = fixed_hair_color - else - accessory_overlay.color = source.hair_color + accessory_overlay.color = get_fixed_hair_color(source) || source.hair_color if(FACIAL_HAIR_COLOR) - accessory_overlay.color = source.facial_hair_color + accessory_overlay.color = get_fixed_hair_color(source) || source.facial_hair_color if(EYE_COLOR) accessory_overlay.color = source.eye_color_left else @@ -2181,3 +2174,21 @@ GLOBAL_LIST_EMPTY(features_by_species) return harddel_deets_dumped = TRUE return "Gained / Owned: [properly_gained ? "Yes" : "No"]" + +/** + * Get what hair color is used by this species for a mob. + * + * Arguments + * * for_mob - The mob to get the hair color for. Required. + * + * Returns a color string or null. + */ +/datum/species/proc/get_fixed_hair_color(mob/living/carbon/human/for_mob) + ASSERT(!isnull(for_mob)) + switch(hair_color_mode) + if(USE_MUTANT_COLOR) + return for_mob.dna.features["mcolor"] + if(USE_FIXED_MUTANT_COLOR) + return fixed_mut_color + + return null diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 5e697f74b9c9..1325587a5e4b 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -58,8 +58,14 @@ if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP)) . += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands." else if(GET_ATOM_BLOOD_DNA_LENGTH(src)) + var/list/all_dna = GET_ATOM_BLOOD_DNA(src) + var/list/all_blood_names = list() + for(var/dna_sample in all_dna) + var/datum/blood_type/blood = GLOB.blood_types[all_dna[dna_sample]] + all_blood_names |= lowertext(initial(blood.reagent_type.name)) + if(num_hands) - . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a"] blood-stained hand[num_hands > 1 ? "s" : ""]!") + . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "][english_list(all_blood_names, nothing_text = "blood")] stained hand[num_hands > 1 ? "s" : ""]!") //handcuffed? if(handcuffed) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 167f059ed420..f0e5183c7b62 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -41,10 +41,11 @@ /mob/living/carbon/human/proc/setup_mood() if (CONFIG_GET(flag/disable_human_mood)) return - if (isdummy(src)) - return mob_mood = new /datum/mood(src) +/mob/living/carbon/human/dummy/setup_mood() + return + /// This proc is for holding effects applied when a mob is missing certain organs /// It is called very, very early in human init because all humans innately spawn with no organs and gain them during init /// Gaining said organs removes these effects @@ -975,16 +976,6 @@ remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying) -/mob/living/carbon/human/adjust_nutrition(change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - return FALSE - return ..() - -/mob/living/carbon/human/set_nutrition(change) //Seriously fuck you oldcoders. - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - return FALSE - return ..() - /mob/living/carbon/human/get_exp_list(minutes) . = ..() diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index 9a4a55bb7ac1..89cf7e01ce6f 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -5,6 +5,9 @@ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DWARF), SIGNAL_REMOVETRAIT(TRAIT_DWARF)), PROC_REF(on_dwarf_trait)) RegisterSignal(src, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, PROC_REF(get_name_part)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT)), PROC_REF(on_fat)) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_NOHUNGER), SIGNAL_REMOVETRAIT(TRAIT_NOHUNGER)), PROC_REF(on_nohunger)) + /// Gaining or losing [TRAIT_UNKNOWN] updates our name and our sechud /mob/living/carbon/human/proc/on_unknown_trait(datum/source) SIGNAL_HANDLER @@ -38,3 +41,25 @@ if(name != voice_name) voice_name += " (as [get_id_name("Unknown")])" stored_name[NAME_PART_INDEX] = voice_name + +/mob/living/carbon/human/proc/on_fat(datum/source) + SIGNAL_HANDLER + hud_used?.hunger?.update_appearance() + mob_mood?.update_nutrition_moodlets() + + if(HAS_TRAIT(src, TRAIT_FAT)) + add_movespeed_modifier(/datum/movespeed_modifier/obesity) + else + remove_movespeed_modifier(/datum/movespeed_modifier/obesity) + +/mob/living/carbon/human/proc/on_nohunger(datum/source) + SIGNAL_HANDLER + // When gaining NOHUNGER, we restore nutrition to normal levels, since we no longer interact with the hunger system + if(HAS_TRAIT(src, TRAIT_NOHUNGER)) + set_nutrition(NUTRITION_LEVEL_FED, forced = TRUE) + satiety = 0 + overeatduration = 0 + REMOVE_TRAIT(src, TRAIT_FAT, OBESITY) + else + hud_used?.hunger?.update_appearance() + mob_mood?.update_nutrition_moodlets() diff --git a/code/modules/mob/living/carbon/human/species_types/abductors.dm b/code/modules/mob/living/carbon/human/species_types/abductors.dm index 74d2bedf3a70..67f1b3e66d2c 100644 --- a/code/modules/mob/living/carbon/human/species_types/abductors.dm +++ b/code/modules/mob/living/carbon/human/species_types/abductors.dm @@ -40,8 +40,6 @@ var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] abductor_hud.show_to(C) - C.set_safe_hunger_level() - /datum/species/abductor/on_species_loss(mob/living/carbon/C) . = ..() var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] diff --git a/code/modules/mob/living/carbon/human/species_types/android.dm b/code/modules/mob/living/carbon/human/species_types/android.dm index 2c006d2e936b..b7a6c532106f 100644 --- a/code/modules/mob/living/carbon/human/species_types/android.dm +++ b/code/modules/mob/living/carbon/human/species_types/android.dm @@ -47,11 +47,6 @@ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/robot/android, ) -/datum/species/android/on_species_gain(mob/living/carbon/C) - . = ..() - // Androids don't eat, hunger or metabolise foods. Let's do some cleanup. - C.set_safe_hunger_level() - /datum/species/android/get_physical_attributes() return "Androids are almost, but not quite, identical to fully augmented humans. \ Unlike those, though, they're completely immune to toxin damage, don't have blood or organs (besides their head), don't get hungry, and can reattach their limbs! \ diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index 1407ba80f755..1d7c328f8823 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -58,7 +58,6 @@ eyes.bodypart_insert(my_head) human.update_body() head.update_icon_dropped() - human.set_safe_hunger_level() RegisterSignal(head, COMSIG_QDELETING, PROC_REF(on_head_destroyed)) /// If we gained a new body part, it had better not be a head diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 5d911d293521..9fc68a635095 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -25,7 +25,7 @@ bodytemp_heat_damage_limit = FIRE_MINIMUM_TEMPERATURE_TO_SPREAD // about 150C // Cold temperatures hurt faster as it is harder to move with out the heat energy bodytemp_cold_damage_limit = (T20C - 10) // about 10c - hair_color = "fixedmutcolor" + hair_color_mode = USE_FIXED_MUTANT_COLOR hair_alpha = 140 facial_hair_alpha = 140 @@ -59,7 +59,6 @@ if(!ishuman(new_ethereal)) return default_color = new_ethereal.dna.features["ethcolor"] - fixed_hair_color = default_color r1 = GETREDPART(default_color) g1 = GETGREENPART(default_color) b1 = GETBLUEPART(default_color) @@ -70,7 +69,6 @@ RegisterSignal(new_ethereal, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(refresh_light_color)) ethereal_light = new_ethereal.mob_light(light_type = /obj/effect/dummy/lighting_obj/moblight/species) refresh_light_color(new_ethereal) - new_ethereal.set_safe_hunger_level() // update_mail_goodies(new_ethereal) // NON-MODULE CHANGE var/obj/item/organ/internal/heart/ethereal/ethereal_heart = new_ethereal.get_organ_slot(ORGAN_SLOT_HEART) @@ -128,7 +126,6 @@ ethereal_light.set_light_range_power_color(1 + (2 * healthpercent), 1 + (1 * healthpercent), current_color) ethereal_light.set_light_on(TRUE) fixed_mut_color = current_color - fixed_hair_color = current_color ethereal.update_body() ethereal.set_facial_haircolor(current_color, override = TRUE, update = FALSE) ethereal.set_haircolor(current_color, override = TRUE, update = TRUE) @@ -136,7 +133,6 @@ ethereal_light.set_light_on(FALSE) var/dead_color = rgb(128,128,128) fixed_mut_color = dead_color - fixed_hair_color = dead_color ethereal.update_body() ethereal.set_facial_haircolor(dead_color, override = TRUE, update = FALSE) ethereal.set_haircolor(dead_color, override = TRUE, update = TRUE) diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 7870dcd618cc..2574420b49ce 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -31,7 +31,9 @@ inherent_factions = list(FACTION_SLIME) species_language_holder = /datum/language_holder/jelly ass_image = 'icons/ass/assslime.png' - + hair_color_mode = USE_MUTANT_COLOR + hair_alpha = 150 + facial_hair_alpha = 150 bodypart_overrides = list( BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/jelly, BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/jelly, @@ -213,9 +215,7 @@ name = "\improper Slimeperson" plural_form = "Slimepeople" id = SPECIES_SLIMEPERSON - hair_color = "mutcolor" - hair_alpha = 150 - facial_hair_alpha = 150 + hair_color_mode = USE_MUTANT_COLOR changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT mutanteyes = /obj/item/organ/internal/eyes var/datum/action/innate/split_body/slime_split @@ -223,12 +223,12 @@ var/datum/action/innate/swap_body/swap_body bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/slime, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/slime, - BODY_ZONE_HEAD = /obj/item/bodypart/head/slime, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/slime, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/slime, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/slime, + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/jelly/slime, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/jelly/slime, + BODY_ZONE_HEAD = /obj/item/bodypart/head/jelly/slime, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/jelly/slime, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/jelly/slime, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/jelly/slime, ) /datum/species/jelly/slime/get_physical_attributes() @@ -523,12 +523,12 @@ id = SPECIES_LUMINESCENT examine_limb_id = SPECIES_LUMINESCENT bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/luminescent, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/luminescent, - BODY_ZONE_HEAD = /obj/item/bodypart/head/luminescent, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/luminescent, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/luminescent, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/luminescent, + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/jelly/luminescent, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/jelly/luminescent, + BODY_ZONE_HEAD = /obj/item/bodypart/head/jelly/luminescent, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/jelly/luminescent, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/jelly/luminescent, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/jelly/luminescent, ) mutanteyes = /obj/item/organ/internal/eyes /// How strong is our glow diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index 141ad48de8aa..173bf2474cce 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -5,7 +5,6 @@ changesource_flags = MIRROR_BADMIN | WABBAJACK | ERT_SPAWN fixed_mut_color = "#DBBF92" - hair_color = "#FF4B19" //cap color, spot color uses eye color external_organs = list(/obj/item/organ/external/mushroom_cap = "Round") @@ -57,6 +56,9 @@ if(chem.type == /datum/reagent/toxin/plantbgone/weedkiller) affected.adjustToxLoss(3 * REM * seconds_per_tick) +/datum/species/mush/get_fixed_hair_color(mob/living/carbon/human/for_mob) + return "#FF4B19" //cap color, spot color uses eye color + /// A mushpersons mushroom cap organ /obj/item/organ/external/mushroom_cap name = "mushroom cap" @@ -87,4 +89,3 @@ return FALSE return TRUE - diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 8ce8be6f6d6b..ff1fcc4f6254 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -62,10 +62,6 @@ /// If the bones themselves are burning clothes won't help you much var/internal_fire = FALSE -/datum/species/plasmaman/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) - . = ..() - C.set_safe_hunger_level() - /datum/species/plasmaman/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) . = ..() var/atmos_sealed = TRUE @@ -115,7 +111,7 @@ else internal_fire = FALSE - H.update_fire() + H.update_appearance(UPDATE_OVERLAYS) /datum/species/plasmaman/handle_fire(mob/living/carbon/human/H, seconds_per_tick, no_protection = FALSE) if(internal_fire) diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index a20e240529fb..4718645b5634 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -44,10 +44,6 @@ BODY_ZONE_CHEST = /obj/item/bodypart/chest/skeleton, ) -/datum/species/skeleton/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) - . = ..() - C.set_safe_hunger_level() - /datum/species/skeleton/check_roundstart_eligible() if(check_holidays(HALLOWEEN)) return TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 59c0fbad6239..427c71b9bd00 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -39,7 +39,6 @@ to_chat(new_vampire, "[info_text]") new_vampire.skin_tone = "albino" new_vampire.update_body(0) - new_vampire.set_safe_hunger_level() RegisterSignal(new_vampire, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(damage_weakness)) /datum/species/vampire/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) diff --git a/code/modules/mob/living/carbon/init_signals.dm b/code/modules/mob/living/carbon/init_signals.dm index 190fe9d84534..e64410ab6357 100644 --- a/code/modules/mob/living/carbon/init_signals.dm +++ b/code/modules/mob/living/carbon/init_signals.dm @@ -13,12 +13,6 @@ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_TOXIMMUNE), PROC_REF(on_toximmune_trait_gain)) RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_GENELESS), PROC_REF(on_geneless_trait_gain)) - - RegisterSignals(src, list( - SIGNAL_ADDTRAIT(TRAIT_PERMANENTLY_ONFIRE), - SIGNAL_REMOVETRAIT(TRAIT_PERMANENTLY_ONFIRE), - ), PROC_REF(update_permanently_on_fire)) - /** * On gain of TRAIT_AGENDER * @@ -90,12 +84,6 @@ reagents.end_metabolization(keep_liverless = TRUE) -///On gain of TRAIT_PERMANENTLY_ONFIRE, update the visuals if not on fire -/mob/living/carbon/proc/update_permanently_on_fire(datum/source) - SIGNAL_HANDLER - if(!on_fire) - update_fire() - /** * On gain of TRAIT_VIRUSIMMUNE * diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index db8302b49dfa..63f698a3e3fa 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -72,12 +72,10 @@ key_third_person = "coughs" message = "coughs!" message_mime = "acts out an exaggerated cough!" - emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE + emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_RUNECHAT /datum/emote/living/cough/can_run_emote(mob/user, status_check = TRUE , intentional) - . = ..() - if(HAS_TRAIT(user, TRAIT_SOOTHED_THROAT)) - return FALSE + return !HAS_TRAIT(user, TRAIT_SOOTHED_THROAT) && ..() /datum/emote/living/dance key = "dance" @@ -539,7 +537,7 @@ /// The base chance for your yawn to propagate to someone else if they're on the same tile as you #define YAWN_PROPAGATE_CHANCE_BASE 20 /// The amount the base chance to propagate yawns falls for each tile of distance -#define YAWN_PROPAGATE_CHANCE_DECAY 4 +#define YAWN_PROPAGATE_CHANCE_DECAY 8 /datum/emote/living/yawn key = "yawn" @@ -548,7 +546,7 @@ message_mime = "acts out an exaggerated silent yawn." message_robot = "symphathetically yawns." message_AI = "symphathetically yawns." - emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE + emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_RUNECHAT cooldown = 5 SECONDS /datum/emote/living/yawn/run_emote(mob/user, params, type_override, intentional) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f04b87a205fe..aa409e324ed1 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -929,12 +929,14 @@ if(heal_flags & HEAL_STAM) setStaminaLoss(0, updating_stamina = FALSE, forced = TRUE) - // I don't really care to keep this under a flag - set_nutrition(NUTRITION_LEVEL_FED + 50) + if(heal_flags & HEAL_STATUS) // NON-MODULE CHANGE + set_nutrition(NUTRITION_LEVEL_FED + 50) + overeatduration = 0 + satiety = 0 + set_disgust(0) + if(heal_flags & (HEAL_STATUS|HEAL_OXY)) // NON-MODULE CHANGE + losebreath = 0 - // These should be tracked by status effects - losebreath = 0 - set_disgust(0) cure_husk() if(heal_flags & HEAL_TEMP) @@ -1581,11 +1583,6 @@ GLOBAL_LIST_EMPTY(fire_appearances) return fire_status.ignite(silent) -/mob/living/proc/update_fire() - var/datum/status_effect/fire_handler/fire_stacks/fire_stacks = has_status_effect(/datum/status_effect/fire_handler/fire_stacks) - if(fire_stacks) - fire_stacks.update_overlay() - /** * Extinguish all fire on the mob * @@ -1593,7 +1590,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) * Signals the extinguishing. */ /mob/living/proc/extinguish_mob() - if(HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) //The everlasting flames will not be extinguished + if(HAS_TRAIT(src, TRAIT_NO_EXTINGUISH)) //The everlasting flames will not be extinguished return var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks) if(!fire_status || !fire_status.on_fire) @@ -1612,13 +1609,13 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/proc/adjust_fire_stacks(stacks, fire_type = /datum/status_effect/fire_handler/fire_stacks) if(stacks < 0) - if(HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) //You can't reduce fire stacks of the everlasting flames + if(HAS_TRAIT(src, TRAIT_NO_EXTINGUISH)) //You can't reduce fire stacks of the everlasting flames return stacks = max(-fire_stacks, stacks) apply_status_effect(fire_type, stacks) /mob/living/proc/adjust_wet_stacks(stacks, wet_type = /datum/status_effect/fire_handler/wet_stacks) - if(HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) //The everlasting flames will not be extinguished + if(HAS_TRAIT(src, TRAIT_NO_EXTINGUISH)) //The everlasting flames will not be extinguished return if(stacks < 0) stacks = max(fire_stacks, stacks) @@ -1696,19 +1693,18 @@ GLOBAL_LIST_EMPTY(fire_appearances) ignite_mob() /** - * Sets fire overlay of the mob. + * Gets the fire overlay to use for this mob * - * Vars: + * Args: * * stacks: Current amount of fire_stacks * * on_fire: If we're lit on fire - * * last_icon_state: Holds last fire overlay icon state, used for optimization - * * suffix: Suffix for the fire icon state for special fire types * - * This should return last_icon_state for the fire status efect + * Return a mutable appearance, the overlay that will be applied. */ -/mob/living/proc/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - return last_icon_state +/mob/living/proc/get_fire_overlay(stacks, on_fire) as /mutable_appearance + RETURN_TYPE(/mutable_appearance) + return null /** * Handles effects happening when mob is on normal fire @@ -2335,27 +2331,6 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/carbon/human/will_escape_storage() return TRUE -/// Sets the mob's hunger levels to a safe overall level. Useful for TRAIT_NOHUNGER species changes. -/mob/living/proc/set_safe_hunger_level() - // Nutrition reset and alert clearing. - nutrition = NUTRITION_LEVEL_FED - clear_alert(ALERT_NUTRITION) - satiety = 0 - - // Trait removal if obese - if(HAS_TRAIT_FROM(src, TRAIT_FAT, OBESITY)) - if(overeatduration >= (200 SECONDS)) - to_chat(src, span_notice("Your transformation restores your body's natural fitness!")) - - REMOVE_TRAIT(src, TRAIT_FAT, OBESITY) - remove_movespeed_modifier(/datum/movespeed_modifier/obesity) - update_worn_undersuit() - update_worn_oversuit() - - // Reset overeat duration. - overeatduration = 0 - - /// Changes the value of the [living/body_position] variable. Call this before set_lying_angle() /mob/living/proc/set_body_position(new_value) if(body_position == new_value) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ed9cbe09352c..f73336fe963d 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -352,7 +352,7 @@ var/mutable_appearance/head_overlay = hat.build_worn_icon(default_layer = 20, default_icon_file = 'icons/mob/clothing/head/default.dmi') head_overlay.pixel_z += hat_offset add_overlay(head_overlay) - update_fire() + update_appearance(UPDATE_OVERLAYS) /mob/living/silicon/robot/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) if(same_z_layer) @@ -1017,25 +1017,19 @@ /mob/living/silicon/robot/proc/untip_roleplay() to_chat(src, span_notice("Your frustration has empowered you! You can now right yourself faster!")) -/mob/living/silicon/robot/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - var/fire_icon = "generic_fire[suffix]" +/mob/living/silicon/robot/get_fire_overlay(stacks, on_fire) + var/fire_icon = "generic_fire" if(!GLOB.fire_appearances[fire_icon]) - var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/effects/onfire.dmi', fire_icon, -FIRE_LAYER) - new_fire_overlay.appearance_flags = RESET_COLOR + var/mutable_appearance/new_fire_overlay = mutable_appearance( + 'icons/mob/effects/onfire.dmi', + fire_icon, + -HIGHEST_LAYER, + appearance_flags = RESET_COLOR, + ) GLOB.fire_appearances[fire_icon] = new_fire_overlay - if(stacks && on_fire) - if(last_icon_state == fire_icon) - return last_icon_state - add_overlay(GLOB.fire_appearances[fire_icon]) - return fire_icon - - if(!last_icon_state) - return last_icon_state - - cut_overlay(GLOB.fire_appearances[fire_icon]) - return null + return GLOB.fire_appearances[fire_icon] /// Draw power from the robot /mob/living/silicon/robot/proc/draw_power(power_to_draw) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 22c7439ec803..210554f137fa 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -397,7 +397,7 @@ COOLDOWN_START(src, next_salute_check, BOT_COMMISSIONED_SALUTE_DELAY) for(var/mob/living/simple_animal/bot/B in view(5, src)) if(!B.commissioned && B.bot_mode_flags & BOT_MODE_ON) - visible_message("[B] performs an elaborate salute for [src]!") + manual_emote("performs an elaborate salute for [src]!") break switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command. diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d870a121ee4c..f83334e93c3d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -262,10 +262,12 @@ * message is output to anyone who can see, e.g. `"The [src] does something!"` * * Vars: + * * message is the message output to anyone who can see. * * self_message (optional) is what the src mob sees e.g. "You do something!" * * blind_message (optional) is what blind people will hear e.g. "You hear something!" * * vision_distance (optional) define how many tiles away the message can be seen. - * * ignored_mob (optional) doesn't show any message to a given mob if TRUE. + * * ignored_mobs (optional) doesn't show any message to any mob in this list. + * * visible_message_flags (optional) is the type of message being sent. */ /atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE) var/turf/T = get_turf(src) @@ -314,8 +316,22 @@ ///Adds the functionality to self_message. /mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE) . = ..() - if(self_message) - show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE) + if(!self_message) + return + var/raw_self_message = self_message + var/self_runechat = FALSE + if(visible_message_flags & EMOTE_MESSAGE) + self_message = "[src] [self_message]" // May make more sense as "You do x" + + if(visible_message_flags & ALWAYS_SHOW_SELF_MESSAGE) + to_chat(src, self_message) + self_runechat = TRUE + + else + self_runechat = show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE) + + if(self_runechat && (visible_message_flags & EMOTE_MESSAGE) && runechat_prefs_check(src, visible_message_flags)) + create_chat_message(src, raw_message = raw_self_message, runechat_flags = visible_message_flags) /** * Show a message to all mobs in earshot of this atom @@ -326,6 +342,8 @@ * * message is the message output to anyone who can hear. * * deaf_message (optional) is what deaf people will see. * * hearing_distance (optional) is the range, how many tiles away the message can be heard. + * * self_message (optional) is what the src mob hears. + * * audible_message_flags (optional) is the type of message being sent. */ /atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE) var/list/hearers = get_hearers_in_view(hearing_distance, src) @@ -352,9 +370,20 @@ */ /mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE) . = ..() - if(self_message) - show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) + if(!self_message) + return + var/raw_self_message = self_message + var/self_runechat = FALSE + if(audible_message_flags & EMOTE_MESSAGE) + self_message = "[src] [self_message]" + if(audible_message_flags & ALWAYS_SHOW_SELF_MESSAGE) + to_chat(src, self_message) + self_runechat = TRUE + else + self_runechat = show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) + if(self_runechat && (audible_message_flags & EMOTE_MESSAGE) && runechat_prefs_check(src, audible_message_flags)) + create_chat_message(src, raw_message = raw_self_message, runechat_flags = audible_message_flags) ///Returns the client runechat visible messages preference according to the message type. /atom/proc/runechat_prefs_check(mob/target, visible_message_flags = NONE) @@ -1514,12 +1543,28 @@ get_language_holder().open_language_menu(usr) ///Adjust the nutrition of a mob -/mob/proc/adjust_nutrition(change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks +/mob/proc/adjust_nutrition(change, forced = FALSE) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + return + nutrition = max(0, nutrition + change) + hud_used?.hunger?.update_appearance() + +/mob/living/adjust_nutrition(change, forced) + . = ..() + mob_mood?.update_nutrition_moodlets() ///Force set the mob nutrition -/mob/proc/set_nutrition(change) //Seriously fuck you oldcoders. - nutrition = max(0, change) +/mob/proc/set_nutrition(set_to, forced = FALSE) //Seriously fuck you oldcoders. + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + return + + nutrition = max(0, set_to) + hud_used?.hunger?.update_appearance() + +/mob/living/set_nutrition(set_to, forced) + . = ..() + mob_mood?.update_nutrition_moodlets() /mob/proc/update_equipment_speed_mods() var/speedies = equipped_speed_mods() diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index b8a45656644b..5a65b038401d 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -238,7 +238,8 @@ for(var/obj/item/mod/module/module as anything in modules) module.on_suit_deactivation() update_speed() - update_icon_state() + update_appearance(UPDATE_ICON_STATE) + update_charge_alert() wearer.update_clothing(slot_flags) /// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits. diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index 39111684f62f..f3f2d93017dc 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -243,7 +243,6 @@ if(malfunctioning) malfunctioning_charge_drain = rand(1,20) subtract_charge((charge_drain + malfunctioning_charge_drain) * seconds_per_tick) - update_charge_alert() for(var/obj/item/mod/module/module as anything in modules) if(malfunctioning && module.active && SPT_PROB(5, seconds_per_tick)) module.on_deactivation(display_message = TRUE) @@ -319,7 +318,6 @@ wrench.play_tool_sound(src, 100) balloon_alert(user, "core removed") core.forceMove(drop_location()) - update_charge_alert() return TRUE return ..() @@ -402,7 +400,6 @@ attacking_core.install(src) balloon_alert(user, "core installed") playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) - update_charge_alert() return TRUE else if(is_wire_tool(attacking_item) && open) wires.interact(user) @@ -413,11 +410,9 @@ return ..() /obj/item/mod/control/get_cell() - if(!open) - return var/obj/item/stock_parts/cell/cell = get_charge_source() if(!istype(cell)) - return + return null return cell /obj/item/mod/control/GetAccess() @@ -488,8 +483,8 @@ for(var/obj/item/mod/module/module as anything in modules) module.on_unequip() UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN)) - wearer.clear_alert(ALERT_MODSUIT_CHARGE) SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer) + wearer.update_spacesuit_hud_icon("0") wearer = null /obj/item/mod/control/proc/clean_up() @@ -632,13 +627,21 @@ /obj/item/mod/control/proc/check_charge(amount) return core?.check_charge(amount) || FALSE +/** + * Updates the wearer's hud according to the current state of the MODsuit + */ /obj/item/mod/control/proc/update_charge_alert() - if(!wearer) + if(isnull(wearer)) return - if(!core) - wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/nocore) - return - core.update_charge_alert() + var/state_to_use + if(!active) + state_to_use = "0" + else if(isnull(core)) + state_to_use = "coreless" + else + state_to_use = core.get_charge_icon_state() + + wearer.update_spacesuit_hud_icon(state_to_use || "0") /obj/item/mod/control/proc/update_speed() var/list/all_parts = mod_parts + src @@ -704,7 +707,6 @@ return if(part == core) core.uninstall() - update_charge_alert() return if(part.loc == wearer) return diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm index d5c43497982a..0287c0a23851 100644 --- a/code/modules/mod/mod_core.dm +++ b/code/modules/mod/mod_core.dm @@ -18,9 +18,11 @@ mod = mod_unit mod.core = src forceMove(mod) + mod.update_charge_alert() /obj/item/mod/core/proc/uninstall() mod.core = null + mod.update_charge_alert() mod = null /obj/item/mod/core/proc/charge_source() @@ -41,8 +43,11 @@ /obj/item/mod/core/proc/check_charge(amount) return FALSE -/obj/item/mod/core/proc/update_charge_alert() - mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE) +/** + * Gets what icon state to display on the HUD for the charge level of this core + */ +/obj/item/mod/core/proc/get_charge_icon_state() + return "0" /obj/item/mod/core/infinite name = "MOD infinite core" @@ -68,6 +73,9 @@ /obj/item/mod/core/infinite/check_charge(amount) return TRUE +/obj/item/mod/core/infinite/get_charge_icon_state() + return "high" + /obj/item/mod/core/standard name = "MOD standard core" icon_state = "mod-core-standard" @@ -80,8 +88,7 @@ var/obj/item/stock_parts/cell/cell /obj/item/mod/core/standard/Destroy() - if(cell) - QDEL_NULL(cell) + QDEL_NULL(cell) return ..() /obj/item/mod/core/standard/install(obj/item/mod/control/mod_unit) @@ -116,54 +123,57 @@ /obj/item/mod/core/standard/add_charge(amount) var/obj/item/stock_parts/cell/charge_source = charge_source() - if(!charge_source) + if(isnull(charge_source)) return FALSE - return charge_source.give(amount) + . = charge_source.give(amount) + if(.) + mod.update_charge_alert() + return . /obj/item/mod/core/standard/subtract_charge(amount) var/obj/item/stock_parts/cell/charge_source = charge_source() - if(!charge_source) + if(isnull(charge_source)) return FALSE - return charge_source.use(amount, TRUE) + . = charge_source.use(amount, TRUE) + if(.) + mod.update_charge_alert() + return . /obj/item/mod/core/standard/check_charge(amount) return charge_amount() >= amount -/obj/item/mod/core/standard/update_charge_alert() - var/obj/item/stock_parts/cell/charge_source = charge_source() - if(!charge_source) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/nocell) - return - var/remaining_cell = charge_amount() / max_charge_amount() - switch(remaining_cell) +/obj/item/mod/core/standard/get_charge_icon_state() + if(isnull(charge_source())) + return "missing" + + switch(round(charge_amount() / max_charge_amount(), 0.01)) if(0.75 to INFINITY) - mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE) + return "high" if(0.5 to 0.75) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell, 1) + return "mid" if(0.25 to 0.5) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell, 2) - if(0.01 to 0.25) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell, 3) - else - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/emptycell) + return "low" + if(0.02 to 0.25) + return "very_low" + + return "empty" /obj/item/mod/core/standard/proc/install_cell(new_cell) cell = new_cell cell.forceMove(src) - RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit)) + mod.update_charge_alert() /obj/item/mod/core/standard/proc/uninstall_cell() if(!cell) return + cell.update_appearance() cell = null - UnregisterSignal(src, COMSIG_ATOM_EXITED) - -/obj/item/mod/core/standard/proc/on_exit(datum/source, obj/item/stock_parts/cell, direction) - SIGNAL_HANDLER + mod.update_charge_alert() - if(!istype(cell) || cell.loc == src) - return - uninstall_cell() +/obj/item/mod/core/standard/Exited(atom/movable/gone, direction) + . = ..() + if(gone == cell) + uninstall_cell() /obj/item/mod/core/standard/proc/on_examine(datum/source, mob/examiner, list/examine_text) SIGNAL_HANDLER @@ -195,7 +205,6 @@ var/obj/item/cell_to_move = cell cell_to_move.forceMove(drop_location()) user.put_in_hands(cell_to_move) - mod.update_charge_alert() /obj/item/mod/core/standard/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user) SIGNAL_HANDLER @@ -212,7 +221,6 @@ install_cell(attacking_item) mod.balloon_alert(user, "cell installed") playsound(mod, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE) - mod.update_charge_alert() return COMPONENT_NO_AFTERATTACK return NONE @@ -272,12 +280,8 @@ /obj/item/mod/core/ethereal/check_charge(amount) return charge_amount() >= amount*charge_modifier -/obj/item/mod/core/ethereal/update_charge_alert() - var/obj/item/organ/internal/stomach/ethereal/charge_source = charge_source() - if(charge_source) - mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE) - return - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/nocell) +/obj/item/mod/core/ethereal/get_charge_icon_state() + return charge_source() ? "0" : "missing" #define PLASMA_CORE_ORE_CHARGE 1500 #define PLASMA_CORE_SHEET_CHARGE 2000 @@ -318,28 +322,29 @@ /obj/item/mod/core/plasma/add_charge(amount) charge = min(maxcharge, charge + amount) + mod.update_charge_alert() return TRUE /obj/item/mod/core/plasma/subtract_charge(amount) charge = max(0, charge - amount) + mod.update_charge_alert() return TRUE /obj/item/mod/core/plasma/check_charge(amount) return charge_amount() >= amount -/obj/item/mod/core/plasma/update_charge_alert() - var/remaining_plasma = charge_amount() / max_charge_amount() - switch(remaining_plasma) +/obj/item/mod/core/plasma/get_charge_icon_state() + switch(round(charge_amount() / max_charge_amount(), 0.01)) if(0.75 to INFINITY) - mod.wearer.clear_alert(ALERT_MODSUIT_CHARGE) + return "high" if(0.5 to 0.75) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell/plasma, 1) + return "mid" if(0.25 to 0.5) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell/plasma, 2) - if(0.01 to 0.25) - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/lowcell/plasma, 3) - else - mod.wearer.throw_alert(ALERT_MODSUIT_CHARGE, /atom/movable/screen/alert/emptycell/plasma) + return "low" + if(0.02 to 0.25) + return "very_low" + + return "empty" /obj/item/mod/core/plasma/proc/on_attackby(datum/source, obj/item/attacking_item, mob/user) SIGNAL_HANDLER diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index aabd872a40f2..32a8f2e6cbd1 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -225,7 +225,6 @@ if(!check_power(amount)) return FALSE mod.subtract_charge(amount) - mod.update_charge_alert() return TRUE /// Checks if there is enough power in the suit diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index 2523a6c2545a..ac144938ea7f 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -314,6 +314,12 @@ list_reagents = list(/datum/reagent/gravitum = 5) rename_with_volume = TRUE +/obj/item/reagent_containers/pill/ondansetron + name = "ondansetron pill" + desc = "Alleviates nausea. May cause drowsiness." + icon_state = "pill11" + list_reagents = list(/datum/reagent/medicine/ondansetron = 10) + // Pill styles for chem master /obj/item/reagent_containers/pill/style diff --git a/code/modules/reagents/withdrawal/generic_addictions.dm b/code/modules/reagents/withdrawal/generic_addictions.dm index 5c9dd636309c..e3103fe0f0e0 100644 --- a/code/modules/reagents/withdrawal/generic_addictions.dm +++ b/code/modules/reagents/withdrawal/generic_addictions.dm @@ -183,7 +183,7 @@ /datum/addiction/medicine/withdrawal_stage_1_process(mob/living/carbon/affected_carbon, seconds_per_tick) . = ..() - if(SPT_PROB(10, seconds_per_tick)) + if(SPT_PROB(1, seconds_per_tick)) affected_carbon.emote("cough") /datum/addiction/medicine/withdrawal_enters_stage_2(mob/living/carbon/affected_carbon) @@ -216,6 +216,9 @@ /datum/addiction/medicine/withdrawal_stage_2_process(mob/living/carbon/affected_carbon, seconds_per_tick) . = ..() + if(SPT_PROB(2, seconds_per_tick)) + affected_carbon.emote("cough") + var/datum/hallucination/fake_health_doll/hallucination = health_doll_ref?.resolve() if(QDELETED(hallucination)) health_doll_ref = null @@ -235,15 +238,14 @@ /datum/addiction/medicine/withdrawal_stage_3_process(mob/living/carbon/affected_carbon, seconds_per_tick) . = ..() + if(SPT_PROB(5, seconds_per_tick)) + affected_carbon.emote("cough") + var/datum/hallucination/fake_health_doll/hallucination = health_doll_ref?.resolve() if(!QDELETED(hallucination) && SPT_PROB(5, seconds_per_tick)) hallucination.increment_fake_damage() return - if(SPT_PROB(15, seconds_per_tick)) - affected_carbon.emote("cough") - return - if(SPT_PROB(65, seconds_per_tick)) return @@ -283,11 +285,11 @@ /datum/addiction/nicotine/withdrawal_stage_2_process(mob/living/carbon/affected_carbon, seconds_per_tick) . = ..() affected_carbon.set_jitter_if_lower(20 SECONDS * seconds_per_tick) - if(SPT_PROB(10, seconds_per_tick)) + if(SPT_PROB(2, seconds_per_tick)) affected_carbon.emote("cough") /datum/addiction/nicotine/withdrawal_stage_3_process(mob/living/carbon/affected_carbon, seconds_per_tick) . = ..() affected_carbon.set_jitter_if_lower(30 SECONDS * seconds_per_tick) - if(SPT_PROB(15, seconds_per_tick)) + if(SPT_PROB(5, seconds_per_tick)) affected_carbon.emote("cough") diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index ba3e86e1015e..357b69e59a2d 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -232,7 +232,7 @@ refresh_bleed_rate() /obj/item/bodypart/Destroy() - if(owner && !QDELETED(owner)) + if(!QDELETED(owner)) forced_removal(special = FALSE, dismembered = TRUE, move_to_floor = FALSE) update_owner(null) for(var/wound in wounds) @@ -250,6 +250,13 @@ return ..() +/obj/item/bodypart/dump_harddel_info() + var/list/all_part_info = list() + for(var/obj/item/organ/organ in src) + all_part_info += "| [organ]: [organ.dump_harddel_info()]" + + return "Owner: [owner || "null"] [jointext(all_part_info, "")]" + /obj/item/bodypart/ex_act(severity, target) if(owner) //trust me bro you dont want this return FALSE diff --git a/code/modules/surgery/bodyparts/head_hair_and_lips.dm b/code/modules/surgery/bodyparts/head_hair_and_lips.dm index 624370536f75..c6e141cf7a46 100644 --- a/code/modules/surgery/bodyparts/head_hair_and_lips.dm +++ b/code/modules/surgery/bodyparts/head_hair_and_lips.dm @@ -70,7 +70,7 @@ facial_hairstyle = human_head_owner.facial_hairstyle facial_hair_alpha = owner_species.facial_hair_alpha facial_hair_color = human_head_owner.facial_hair_color - fixed_hair_color = owner_species.fixed_hair_color //Can be null + fixed_hair_color = owner_species.get_fixed_hair_color(human_head_owner) //Can be null gradient_styles = human_head_owner.grad_style?.Copy() gradient_colors = human_head_owner.grad_color?.Copy() diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm index c657e89cefdb..2031ea4c7230 100644 --- a/code/modules/surgery/bodyparts/parts.dm +++ b/code/modules/surgery/bodyparts/parts.dm @@ -227,7 +227,7 @@ if(bodypart_disabled) owner.set_usable_hands(owner.usable_hands - 1) if(owner.stat < UNCONSCIOUS) - to_chat(owner, span_userdanger("You lose control of your [name]!")) + to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!")) if(held_index) owner.dropItemToGround(owner.get_item_for_held_index(held_index)) else if(!bodypart_disabled) @@ -324,7 +324,7 @@ if(bodypart_disabled) owner.set_usable_hands(owner.usable_hands - 1) if(owner.stat < UNCONSCIOUS) - to_chat(owner, span_userdanger("You lose control of your [name]!")) + to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!")) if(held_index) owner.dropItemToGround(owner.get_item_for_held_index(held_index)) else if(!bodypart_disabled) @@ -443,7 +443,7 @@ if(bodypart_disabled) owner.set_usable_legs(owner.usable_legs - 1) if(owner.stat < UNCONSCIOUS) - to_chat(owner, span_userdanger("You lose control of your [name]!")) + to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!")) else if(!bodypart_disabled) owner.set_usable_legs(owner.usable_legs + 1) @@ -532,7 +532,7 @@ if(bodypart_disabled) owner.set_usable_legs(owner.usable_legs - 1) if(owner.stat < UNCONSCIOUS) - to_chat(owner, span_userdanger("You lose control of your [name]!")) + to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!")) else if(!bodypart_disabled) owner.set_usable_legs(owner.usable_legs + 1) diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index 83758f920c4c..ab56084b8d1a 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -85,7 +85,7 @@ is_dimorphic = TRUE dmg_overlay_type = null burn_modifier = 0.5 // = 1/2x generic burn damage - head_flags = HEAD_ALL_FEATURES + head_flags = HEAD_EYECOLOR | HEAD_EYESPRITES | HEAD_HAIR | HEAD_FACIAL_HAIR /obj/item/bodypart/chest/jelly biological_state = (BIO_FLESH|BIO_BLOODED) @@ -120,60 +120,49 @@ burn_modifier = 0.5 // = 1/2x generic burn damage ///SLIME -/obj/item/bodypart/head/slime - biological_state = (BIO_FLESH|BIO_BLOODED) +/obj/item/bodypart/head/jelly/slime limb_id = SPECIES_SLIMEPERSON is_dimorphic = FALSE - head_flags = HEAD_ALL_FEATURES -/obj/item/bodypart/chest/slime - biological_state = (BIO_FLESH|BIO_BLOODED) +/obj/item/bodypart/chest/jelly/slime limb_id = SPECIES_SLIMEPERSON - is_dimorphic = TRUE - wing_types = list(/obj/item/organ/external/wings/functional/slime) -/obj/item/bodypart/arm/left/slime - biological_state = (BIO_FLESH|BIO_BLOODED) +/obj/item/bodypart/arm/left/jelly/slime limb_id = SPECIES_SLIMEPERSON -/obj/item/bodypart/arm/right/slime +/obj/item/bodypart/arm/right/jelly/slime biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_SLIMEPERSON -/obj/item/bodypart/leg/left/slime +/obj/item/bodypart/leg/left/jelly/slime biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_SLIMEPERSON -/obj/item/bodypart/leg/right/slime +/obj/item/bodypart/leg/right/jelly/slime biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_SLIMEPERSON ///LUMINESCENT -/obj/item/bodypart/head/luminescent - biological_state = (BIO_FLESH|BIO_BLOODED) +/obj/item/bodypart/head/jelly/luminescent limb_id = SPECIES_LUMINESCENT - is_dimorphic = TRUE head_flags = HEAD_ALL_FEATURES -/obj/item/bodypart/chest/luminescent - biological_state = (BIO_FLESH|BIO_BLOODED) +/obj/item/bodypart/chest/jelly/luminescent limb_id = SPECIES_LUMINESCENT - is_dimorphic = TRUE - wing_types = list(/obj/item/organ/external/wings/functional/slime) -/obj/item/bodypart/arm/left/luminescent +/obj/item/bodypart/arm/left/jelly/luminescent biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_LUMINESCENT -/obj/item/bodypart/arm/right/luminescent +/obj/item/bodypart/arm/right/jelly/luminescent biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_LUMINESCENT -/obj/item/bodypart/leg/left/luminescent +/obj/item/bodypart/leg/left/jelly/luminescent biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_LUMINESCENT -/obj/item/bodypart/leg/right/luminescent +/obj/item/bodypart/leg/right/jelly/luminescent biological_state = (BIO_FLESH|BIO_BLOODED) limb_id = SPECIES_LUMINESCENT diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 276f8b470231..2fc0f1711574 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -80,9 +80,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) after_eat = CALLBACK(src, PROC_REF(OnEatFrom))) /obj/item/organ/Destroy() - if(bodypart_owner && !owner && !QDELETED(bodypart_owner)) + if(isnull(owner) && !isnull(bodypart_owner)) bodypart_remove(bodypart_owner) - else if(owner) + else if(!isnull(owner)) // The special flag is important, because otherwise mobs can die // while undergoing transformation into different mobs. Remove(owner, special=TRUE) @@ -90,6 +90,9 @@ INITIALIZE_IMMEDIATE(/obj/item/organ) STOP_PROCESSING(SSobj, src) return ..() +/obj/item/organ/dump_harddel_info() + return "Owner: [owner || "null"] | Bodypart Owner: [bodypart_owner || "null"] | Loc: [loc || "nullspace"]" + /// Add a Trait to an organ that it will give its owner. /obj/item/organ/proc/add_organ_trait(trait) LAZYADD(organ_traits, trait) diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index ad48645e8936..e95cb94637e0 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -15,17 +15,16 @@ now_fixed = "Noise slowly begins filling your ears once more." low_threshold_cleared = "The ringing in your ears has died down." - // `deaf` measures "ticks" of deafness. While > 0, the person is unable - // to hear anything. + /// `deaf` measures "ticks" of deafness. While > 0, the person is unable to hear anything. var/deaf = 0 // `damage` in this case measures long term damage to the ears, if too high, // the person will not have either `deaf` or `ear_damage` decrease // without external aid (earmuffs, drugs) - //Resistance against loud noises + /// Resistance against loud noises var/bang_protect = 0 - // Multiplier for both long term and short term ear damage + /// Multiplier for both long term and short term ear damage var/damage_multiplier = 1 /obj/item/organ/internal/ears/on_life(seconds_per_tick, times_fired) @@ -34,28 +33,98 @@ to_chat(owner, span_warning("The ringing in your ears grows louder, blocking out any external noises for a moment.")) . = ..() - // if we have non-damage related deafness like mutations, quirks or clothing (earmuffs), don't bother processing here. Ear healing from earmuffs or chems happen elsewhere + // if we have non-damage related deafness like mutations, quirks or clothing (earmuffs), don't bother processing here. + // Ear healing from earmuffs or chems happen elsewhere if(HAS_TRAIT_NOT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE)) return + // no healing if failing + if(organ_flags & ORGAN_FAILING) + return + adjustEarDamage(0, -0.5 * seconds_per_tick) + if((damage > low_threshold) && SPT_PROB(damage / 60, seconds_per_tick)) + adjustEarDamage(0, 4) + SEND_SOUND(owner, sound('sound/weapons/flash_ring.ogg')) + +/obj/item/organ/internal/ears/apply_organ_damage(damage_amount, maximum, required_organ_flag) + . = ..() + update_temp_deafness() + +/obj/item/organ/internal/ears/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + update_temp_deafness() + +/obj/item/organ/internal/ears/on_mob_remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_MOB_SAY) + REMOVE_TRAIT(organ_owner, TRAIT_DEAF, EAR_DAMAGE) + +/** + * Snowflake proc to handle temporary deafness + * + * * ddmg: Handles normal organ damage + * * ddeaf: Handles temporary deafness, 1 ddeaf = 2 seconds of deafness, by default (with no multiplier) + */ +/obj/item/organ/internal/ears/proc/adjustEarDamage(ddmg = 0, ddeaf = 0) + if(owner.status_flags & GODMODE) + update_temp_deafness() + return + + var/mod_damage = ddmg > 0 ? (ddmg * damage_multiplier) : ddmg + if(mod_damage) + apply_organ_damage(mod_damage) + var/mod_deaf = ddeaf > 0 ? (ddeaf * damage_multiplier) : ddeaf + if(mod_deaf) + deaf = max(deaf + mod_deaf, 0) + update_temp_deafness() + +/// Updates status of deafness +/obj/item/organ/internal/ears/proc/update_temp_deafness() + // if we're failing we always have at least some deaf stacks (and thus deafness) + if(organ_flags & ORGAN_FAILING) + deaf = max(deaf, 1 * damage_multiplier) + + if(isnull(owner)) + return - if((organ_flags & ORGAN_FAILING)) - deaf = max(deaf, 1) // if we're failing we always have at least 1 deaf stack (and thus deafness) - else // only clear deaf stacks if we're not failing - deaf = max(deaf - (0.5 * seconds_per_tick), 0) - if((damage > low_threshold) && SPT_PROB(damage / 60, seconds_per_tick)) - adjustEarDamage(0, 4) - SEND_SOUND(owner, sound('sound/weapons/flash_ring.ogg')) + if(owner.status_flags & GODMODE) + deaf = 0 - if(deaf) - ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE) + if(deaf > 0) + if(!HAS_TRAIT_FROM(owner, TRAIT_DEAF, EAR_DAMAGE)) + RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(adjust_speech)) + ADD_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE) else REMOVE_TRAIT(owner, TRAIT_DEAF, EAR_DAMAGE) + UnregisterSignal(owner, COMSIG_MOB_SAY) -/obj/item/organ/internal/ears/proc/adjustEarDamage(ddmg, ddeaf) - if(owner.status_flags & GODMODE) +/// Being deafened by loud noises makes you shout +/obj/item/organ/internal/ears/proc/adjust_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + if(HAS_TRAIT_NOT_FROM(source, TRAIT_DEAF, EAR_DAMAGE)) return - set_organ_damage(clamp(damage + (ddmg * damage_multiplier), 0, maxHealth)) - deaf = max(deaf + (ddeaf * damage_multiplier), 0) + if(HAS_TRAIT(source, TRAIT_SIGN_LANG)) + return + + var/message = speech_args[SPEECH_MESSAGE] + // Replace only end-of-sentence punctuation with exclamation marks (hence the empty space) + // We don't wanna mess with things like ellipses + message = replacetext(message, ". ", "! ") + message = replacetext(message, "? ", "?! ") + // Special case for the last character + switch(copytext_char(message, -1)) + if(".") + if(copytext_char(message, -2) != "..") // Once again ignoring ellipses, let people trail off + message = copytext_char(message, 1, -1) + "!" + if("?") + message = copytext_char(message, 1, -1) + "?!" + if("!") + pass() + else + message += "!" + + speech_args[SPEECH_MESSAGE] = message + return COMPONENT_UPPERCASE_SPEECH /obj/item/organ/internal/ears/invincible damage_multiplier = 0 diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index 874ec320648b..f70519c74ed3 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -753,31 +753,85 @@ if(!HAS_TRAIT(breather, TRAIT_RESISTCOLD)) // COLD DAMAGE var/cold_modifier = breather.dna.species.coldmod + var/breath_effect_prob = 0 if(breath_temperature < cold_level_3_threshold) - breather.apply_damage(cold_level_3_damage*cold_modifier, cold_damage_type, spread_damage = TRUE) + breather.apply_damage(cold_level_3_damage * cold_modifier, cold_damage_type, spread_damage = TRUE) + breath_effect_prob = 100 if(breath_temperature > cold_level_3_threshold && breath_temperature < cold_level_2_threshold) - breather.apply_damage(cold_level_2_damage*cold_modifier, cold_damage_type, spread_damage = TRUE) + breather.apply_damage(cold_level_2_damage * cold_modifier, cold_damage_type, spread_damage = TRUE) + breath_effect_prob = 50 if(breath_temperature > cold_level_2_threshold && breath_temperature < cold_level_1_threshold) - breather.apply_damage(cold_level_1_damage*cold_modifier, cold_damage_type, spread_damage = TRUE) + breather.apply_damage(cold_level_1_damage * cold_modifier, cold_damage_type, spread_damage = TRUE) + breath_effect_prob = 25 if(breath_temperature < cold_level_1_threshold) - if(prob(20)) + if(prob(sqrt(breath_effect_prob) * 4)) to_chat(breather, span_warning("You feel [cold_message] in your [name]!")) + if(prob(50)) + breather.emote("shiver") + if(prob(breath_effect_prob)) + // Breathing into your mask, no particle. We can add fogged up glasses later + if(breather.is_mouth_covered()) + return + // Even though breathing via internals TECHNICALLY exhales into the environment, we'll still block it + if(breather.internal || breather.external) + return + emit_breath_particle(breather, /particles/fog/breath) if(!HAS_TRAIT(breather, TRAIT_RESISTHEAT)) // HEAT DAMAGE var/heat_modifier = breather.dna.species.heatmod + var/heat_message_prob = 0 if(breath_temperature > heat_level_1_threshold && breath_temperature < heat_level_2_threshold) - breather.apply_damage(heat_level_1_damage*heat_modifier, heat_damage_type, spread_damage = TRUE) + breather.apply_damage(heat_level_1_damage * heat_modifier, heat_damage_type, spread_damage = TRUE) + heat_message_prob = 100 if(breath_temperature > heat_level_2_threshold && breath_temperature < heat_level_3_threshold) - breather.apply_damage(heat_level_2_damage*heat_modifier, heat_damage_type, spread_damage = TRUE) + breather.apply_damage(heat_level_2_damage * heat_modifier, heat_damage_type, spread_damage = TRUE) + heat_message_prob = 50 if(breath_temperature > heat_level_3_threshold) - breather.apply_damage(heat_level_3_damage*heat_modifier, heat_damage_type, spread_damage = TRUE) + breather.apply_damage(heat_level_3_damage * heat_modifier, heat_damage_type, spread_damage = TRUE) + heat_message_prob = 25 if(breath_temperature > heat_level_1_threshold) - if(prob(20)) + if(prob(sqrt(heat_message_prob) * 4)) to_chat(breather, span_warning("You feel [hot_message] in your [name]!")) // The air you breathe out should match your body temperature breath.temperature = breather.bodytemperature +/// Creates a particle effect off the mouth of the passed mob. +/obj/item/organ/internal/lungs/proc/emit_breath_particle(mob/living/carbon/human/breather, particle_type) + ASSERT(ispath(particle_type, /particles)) + + var/obj/effect/abstract/particle_holder/holder = new(breather, particle_type) + var/particles/breath_particle = holder.particles + var/breath_dir = breather.dir + + var/list/particle_grav = list(0, 0.1, 0) + var/list/particle_pos = list(0, breather.get_mob_height() + 2, 0) + if(breath_dir & NORTH) + particle_grav[2] = 0.2 + breath_particle.rotation = pick(-45, 45) + // Layer it behind the mob since we're facing away from the camera + holder.pixel_w -= 4 + holder.pixel_y += 4 + if(breath_dir & WEST) + particle_grav[1] = -0.2 + particle_pos[1] = -5 + breath_particle.rotation = -45 + if(breath_dir & EAST) + particle_grav[1] = 0.2 + particle_pos[1] = 5 + breath_particle.rotation = 45 + if(breath_dir & SOUTH) + particle_grav[2] = 0.2 + breath_particle.rotation = pick(-45, 45) + // Shouldn't be necessary but just for parity + holder.pixel_w += 4 + holder.pixel_y -= 4 + + breath_particle.gravity = particle_grav + breath_particle.position = particle_pos + + QDEL_IN(holder, breath_particle.lifespan) + /obj/item/organ/internal/lungs/on_life(seconds_per_tick, times_fired) . = ..() if(failed && !(organ_flags & ORGAN_FAILING)) diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm index 6e985e26329b..56ebf09f44e0 100644 --- a/code/modules/surgery/organs/internal/stomach/_stomach.dm +++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm @@ -128,16 +128,10 @@ if(human.overeatduration < (200 SECONDS)) to_chat(human, span_notice("You feel fit again!")) REMOVE_TRAIT(human, TRAIT_FAT, OBESITY) - human.remove_movespeed_modifier(/datum/movespeed_modifier/obesity) - human.update_worn_undersuit() - human.update_worn_oversuit() else if(human.overeatduration >= (200 SECONDS)) to_chat(human, span_danger("You suddenly feel blubbery!")) ADD_TRAIT(human, TRAIT_FAT, OBESITY) - human.add_movespeed_modifier(/datum/movespeed_modifier/obesity) - human.update_worn_undersuit() - human.update_worn_oversuit() // nutrition decrease and satiety if (human.nutrition > 0 && human.stat != DEAD) @@ -189,18 +183,6 @@ if(CONFIG_GET(flag/disable_human_mood)) handle_hunger_slowdown(human) - // If we did anything more then just set and throw alerts here I would add bracketing - // But well, it is all we do, so there's not much point bothering with it you get me? - switch(nutrition) - if(NUTRITION_LEVEL_FULL to INFINITY) - human.throw_alert(ALERT_NUTRITION, /atom/movable/screen/alert/fat) - if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL) - human.clear_alert(ALERT_NUTRITION) - if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) - human.throw_alert(ALERT_NUTRITION, /atom/movable/screen/alert/hungry) - if(0 to NUTRITION_LEVEL_STARVING) - human.throw_alert(ALERT_NUTRITION, /atom/movable/screen/alert/starving) - ///for when mood is disabled and hunger should handle slowdowns /obj/item/organ/internal/stomach/proc/handle_hunger_slowdown(mob/living/carbon/human/human) var/hungry = (500 - human.nutrition) / 5 //So overeat would be 100 and default level would be 80 @@ -262,13 +244,16 @@ disgusted.throw_alert(ALERT_DISGUST, /atom/movable/screen/alert/disgusted) disgusted.add_mood_event("disgust", /datum/mood_event/disgusted) +/obj/item/organ/internal/stomach/Insert(mob/living/carbon/receiver, special, movement_flags) + . = ..() + receiver.hud_used?.hunger?.update_appearance() + /obj/item/organ/internal/stomach/Remove(mob/living/carbon/stomach_owner, special, movement_flags) if(ishuman(stomach_owner)) var/mob/living/carbon/human/human_owner = owner human_owner.clear_alert(ALERT_DISGUST) human_owner.clear_mood_event("disgust") - human_owner.clear_alert(ALERT_NUTRITION) - + stomach_owner.hud_used?.hunger?.update_appearance() return ..() /obj/item/organ/internal/stomach/bone diff --git a/code/modules/unit_tests/screenshot_humanoids.dm b/code/modules/unit_tests/screenshot_humanoids.dm index 63e2c1788a1f..1b7e39f03bb1 100644 --- a/code/modules/unit_tests/screenshot_humanoids.dm +++ b/code/modules/unit_tests/screenshot_humanoids.dm @@ -2,6 +2,8 @@ /datum/unit_test/screenshot_humanoids /datum/unit_test/screenshot_humanoids/Run() + var/list/testable_species = subtypesof(/datum/species) + // Test lizards as their own thing so we can get more coverage on their features var/mob/living/carbon/human/lizard = allocate(/mob/living/carbon/human/dummy/consistent) lizard.dna.features["mcolor"] = "#099" @@ -13,6 +15,7 @@ lizard.set_species(/datum/species/lizard) lizard.equipOutfit(/datum/outfit/job/engineer) test_screenshot("[/datum/species/lizard]", get_flat_icon_for_all_directions(lizard)) + testable_species -= /datum/species/lizard // let me have this var/mob/living/carbon/human/moth = allocate(/mob/living/carbon/human/dummy/consistent) @@ -22,9 +25,21 @@ moth.set_species(/datum/species/moth) moth.equipOutfit(/datum/outfit/job/cmo, visualsOnly = TRUE) test_screenshot("[/datum/species/moth]", get_flat_icon_for_all_directions(moth)) + testable_species -= /datum/species/moth + + // More in depth test for slimes since they have a lot going on + for (var/datum/species/slime_type as anything in typesof(/datum/species/jelly)) + var/mob/living/carbon/human/slime = allocate(/mob/living/carbon/human/dummy/consistent) + slime.dna.features["mcolor"] = COLOR_PINK + slime.hairstyle = "Bob Hair 2" + slime.hair_color = COLOR_RED // Should be forced to pink + slime.set_species(slime_type) + slime.equipOutfit(/datum/outfit/job/scientist/consistent) + test_screenshot("[slime_type]", get_flat_icon_for_all_directions(slime)) + testable_species -= slime_type // The rest of the species - for (var/datum/species/species_type as anything in subtypesof(/datum/species) - /datum/species/moth - /datum/species/lizard) + for (var/datum/species/species_type as anything in testable_species) test_screenshot("[species_type]", get_flat_icon_for_all_directions(make_dummy(species_type, /datum/outfit/job/assistant/consistent))) /datum/unit_test/screenshot_humanoids/proc/make_dummy(species, job_outfit) diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly.png index 56205255d879..34288a8a8e5d 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_luminescent.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_luminescent.png index d46cbea8cd86..641c9050ee46 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_luminescent.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_luminescent.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_slime.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_slime.png index 56205255d879..34288a8a8e5d 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_slime.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_slime.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_stargazer.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_stargazer.png index 8ebd5b8eea2d..eaee84fed494 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_stargazer.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_jelly_stargazer.png differ diff --git a/icons/effects/particles/smoke.dmi b/icons/effects/particles/smoke.dmi index 4a3239499b96..99123beeb59a 100644 Binary files a/icons/effects/particles/smoke.dmi and b/icons/effects/particles/smoke.dmi differ diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index dc95d505ce6b..e1c5db84d22a 100644 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/icons/hud/screen_gen.dmi b/icons/hud/screen_gen.dmi index 1484e3f4042a..9792124d1b68 100644 Binary files a/icons/hud/screen_gen.dmi and b/icons/hud/screen_gen.dmi differ diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi index 375de2462691..c877c2fcb882 100644 Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi index 20951b3264f0..5cb090978ae7 100644 Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ diff --git a/maplestation.dme b/maplestation.dme index 435f8a256f5c..a1b21e88b740 100644 --- a/maplestation.dme +++ b/maplestation.dme @@ -1434,6 +1434,7 @@ #include "code\datums\elements\openspace_item_click_handler.dm" #include "code\datums\elements\ore_collecting.dm" #include "code\datums\elements\organ_set_bonus.dm" +#include "code\datums\elements\permanent_fire_overlay.dm" #include "code\datums\elements\pet_bonus.dm" #include "code\datums\elements\plant_backfire.dm" #include "code\datums\elements\point_of_interest.dm" @@ -1702,6 +1703,7 @@ #include "code\datums\quirks\positive_quirks\skittish.dm" #include "code\datums\quirks\positive_quirks\spacer.dm" #include "code\datums\quirks\positive_quirks\spiritual.dm" +#include "code\datums\quirks\positive_quirks\strong_stomach.dm" #include "code\datums\quirks\positive_quirks\tagger.dm" #include "code\datums\quirks\positive_quirks\throwing_arm.dm" #include "code\datums\quirks\positive_quirks\voracious.dm" diff --git a/maplestation_modules/code/datums/pain/pain.dm b/maplestation_modules/code/datums/pain/pain.dm index 30689dcdcafd..e682fb900e79 100644 --- a/maplestation_modules/code/datums/pain/pain.dm +++ b/maplestation_modules/code/datums/pain/pain.dm @@ -832,8 +832,9 @@ /datum/pain/proc/on_parent_statchance(mob/source) SIGNAL_HANDLER - if(source.stat == DEAD && (datum_flags & DF_ISPROCESSING)) - STOP_PROCESSING(SSpain, src) + if(source.stat == DEAD) + if(datum_flags & DF_ISPROCESSING) + STOP_PROCESSING(SSpain, src) else START_PROCESSING(SSpain, src) diff --git a/maplestation_modules/code/modules/mob/living/blood.dm b/maplestation_modules/code/modules/mob/living/blood.dm index 7b467369172e..21f207d702be 100644 --- a/maplestation_modules/code/modules/mob/living/blood.dm +++ b/maplestation_modules/code/modules/mob/living/blood.dm @@ -223,10 +223,18 @@ PROCESSING_SUBSYSTEM_DEF(blood_drying) /datum/blood_type/crew/lizard name = "L" color = "#047200" // Some species of lizards have mutated green blood due to biliverdin build up + compatible_types = list(/datum/blood_type/crew/lizard/silver) + +/datum/blood_type/crew/lizard/silver + color = "#ffffff63" + compatible_types = list(/datum/blood_type/crew/lizard) + +/datum/blood_type/crew/lizard/silver/set_up_blood(obj/effect/decal/cleanable/blood/blood, new_splat) + blood.add_filter("silver_glint", 3, list("type" = "outline", "color" = "#c9c9c963", "size" = 1.5)) /datum/blood_type/crew/skrell name = "S" - color = "#009696" // Did you know octopi have blood blood, thanks to hemocyanin rather than hemoglobin? It binds to copper instead of Iron + color = "#009696" // Did you know octopi have blue blood, as it contains hemocyanin rather than hemoglobin? It binds to copper instead of Iron restoration_chem = /datum/reagent/copper /datum/blood_type/crew/ethereal diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index c2d2e9e01cb7..9059602ff19d 100644 --- a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -1,9 +1,16 @@ // -- Various slime people additions. -- /datum/species/jelly species_pain_mod = 0.5 - hair_color = "mutcolor" - hair_alpha = 150 + // Makes Jellypeople look like Slimepeople and not Stargazers mutanteyes = /obj/item/organ/internal/eyes + bodypart_overrides = list( + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/jelly/slime, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/jelly/slime, + BODY_ZONE_HEAD = /obj/item/bodypart/head/jelly/slime, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/jelly/slime, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/jelly/slime, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/jelly/slime, + ) /datum/species/jelly/prepare_human_for_preview(mob/living/carbon/human/human) human.dna.features["mcolor"] = COLOR_PINK @@ -40,64 +47,14 @@ "As of 2562, Nanotrasen is aware of less than 50 Slimepeople in existence, with 14 of these being under their employ." ) -// Changes jellypeople to look like slimepeople instead of stargazers -// (Because slimepeople are more customizable / less ugly) -/obj/item/bodypart/arm/left/jelly - limb_id = SPECIES_SLIMEPERSON - -/obj/item/bodypart/arm/right/jelly - limb_id = SPECIES_SLIMEPERSON - -/obj/item/bodypart/head/jelly - limb_id = SPECIES_SLIMEPERSON - is_dimorphic = FALSE - -/obj/item/bodypart/leg/left/jelly - limb_id = SPECIES_SLIMEPERSON - -/obj/item/bodypart/leg/right/jelly - limb_id = SPECIES_SLIMEPERSON - -/obj/item/bodypart/chest/jelly - limb_id = SPECIES_SLIMEPERSON - -/datum/species/jelly/luminescent - mutanteyes = /obj/item/organ/internal/eyes - -// Stargazers inherent jelly limbs so we gotta do this too /datum/species/jelly/stargazer + // Makes Stargazers look like Stargazers mutanteyes = /obj/item/organ/internal/eyes/jelly bodypart_overrides = list( - BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/stargazer, - BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/stargazer, - BODY_ZONE_HEAD = /obj/item/bodypart/head/stargazer, - BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/stargazer, - BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/stargazer, - BODY_ZONE_CHEST = /obj/item/bodypart/chest/stargazer, + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/jelly, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/jelly, + BODY_ZONE_HEAD = /obj/item/bodypart/head/jelly, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/jelly, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/jelly, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/jelly, ) - -/obj/item/bodypart/head/stargazer - limb_id = SPECIES_JELLYPERSON - is_dimorphic = TRUE - dmg_overlay_type = null - -/obj/item/bodypart/chest/stargazer - limb_id = SPECIES_JELLYPERSON - is_dimorphic = TRUE - dmg_overlay_type = null - -/obj/item/bodypart/arm/left/stargazer - limb_id = SPECIES_JELLYPERSON - dmg_overlay_type = null - -/obj/item/bodypart/arm/right/stargazer - limb_id = SPECIES_JELLYPERSON - dmg_overlay_type = null - -/obj/item/bodypart/leg/left/stargazer - limb_id = SPECIES_JELLYPERSON - dmg_overlay_type = null - -/obj/item/bodypart/leg/right/stargazer - limb_id = SPECIES_JELLYPERSON - dmg_overlay_type = null diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/reploid.dm b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/reploid.dm index 8b55c2f2f21e..7d6454f4a85f 100644 --- a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/reploid.dm +++ b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/reploid.dm @@ -19,7 +19,6 @@ /datum/species/reploid/on_species_gain(mob/living/carbon/C) . = ..() - C.set_safe_hunger_level() // Adds robot wings to chest wing options (if it is not already robotic) var/obj/item/bodypart/chest/chest = C.get_bodypart(BODY_ZONE_CHEST) if(!IS_ROBOTIC_LIMB(chest)) diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/silverscale.dm b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/silverscale.dm index 178b6bab326c..bed6e63e47c7 100644 --- a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/silverscale.dm +++ b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/silverscale.dm @@ -77,7 +77,7 @@ he_who_was_blessed_with_silver.add_filter("silver_glint", 2, list("type" = "outline", "color" = "#ffffff63", "size" = 2)) he_who_was_blessed_with_silver.physiology?.damage_resistance += 10 - + he_who_was_blessed_with_silver.dna.species.exotic_bloodtype = /datum/blood_type/crew/lizard/silver organ_owner.update_body(TRUE) /obj/item/organ/internal/tongue/lizard/silver/on_mob_remove(mob/living/carbon/organ_owner, special) @@ -106,6 +106,7 @@ old_eye_color_right = null he_who_has_been_outcast.physiology?.damage_resistance -= 10 + he_who_has_been_outcast.dna.species.exotic_bloodtype = initial(he_who_has_been_outcast.dna.species.exotic_bloodtype) organ_owner.update_body(TRUE) diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/synth/synth.dm b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/synth/synth.dm index 003bb57be166..c3e3eb93b240 100644 --- a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/synth/synth.dm +++ b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/synth/synth.dm @@ -74,7 +74,6 @@ /datum/species/synth/on_species_gain(mob/living/carbon/human/synth, datum/species/old_species) . = ..() synth.AddComponent(/datum/component/ion_storm_randomization) - synth.set_safe_hunger_level() if(limb_updates_on_change) RegisterSignal(synth, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(disguise_damage)) @@ -192,7 +191,7 @@ sexes = disguise_species.sexes name = disguise_species.name fixed_mut_color = disguise_species.fixed_mut_color - hair_color = disguise_species.hair_color + hair_color_mode = disguise_species.hair_color_mode if(isnull(synth.client?.prefs) || synth.client.prefs.read_preference(/datum/preference/choiced/synth_blood) == "As Disguise") exotic_bloodtype = disguise_species.exotic_bloodtype @@ -218,7 +217,7 @@ sexes = initial(sexes) name = initial(name) fixed_mut_color = initial(fixed_mut_color) - hair_color = initial(hair_color) + hair_color_mode = initial(hair_color_mode) exotic_bloodtype = /datum/blood_type/oil diff --git a/tgstation.dme b/tgstation.dme index 1fa80723aea0..e7fbbb706285 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1434,6 +1434,7 @@ #include "code\datums\elements\openspace_item_click_handler.dm" #include "code\datums\elements\ore_collecting.dm" #include "code\datums\elements\organ_set_bonus.dm" +#include "code\datums\elements\permanent_fire_overlay.dm" #include "code\datums\elements\pet_bonus.dm" #include "code\datums\elements\plant_backfire.dm" #include "code\datums\elements\point_of_interest.dm" @@ -1702,6 +1703,7 @@ #include "code\datums\quirks\positive_quirks\skittish.dm" #include "code\datums\quirks\positive_quirks\spacer.dm" #include "code\datums\quirks\positive_quirks\spiritual.dm" +#include "code\datums\quirks\positive_quirks\strong_stomach.dm" #include "code\datums\quirks\positive_quirks\tagger.dm" #include "code\datums\quirks\positive_quirks\throwing_arm.dm" #include "code\datums\quirks\positive_quirks\voracious.dm"