From 774c07cd9bd44872b386567e8b9d1a0bb3e98981 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:05:57 -0400 Subject: [PATCH] Basic mob stat panel gutting (#95543) ## About The Pull Request Changes Guardian HUD to use action buttons instead of HUD elements, which means their buttons 1. Can now be moved 2. Can be binded Other Holoparasite HUD changes: 1- Moves some special buttons like explosive's explosion ability to be where the "Toggle Mode" button typically is, to be consistent with some other modes (assassin, gas) that also works like their unique ability. 2- Gives non-dextrous holoparasites the ability to move floors, since they float. 3- Fixes dextrous holoparasites having 2 health HUDs 4- Moves "Check Guardian Type" verb to a new action button meant to mimick the Antag UI Info button. Gives Soulscythes a Combat mode indicator Locks Revenant's combat mode indicator (cause it does nothing) Adds another hoverable HUD element, basic mobs can now hover over their healthdoll to see their Health % image image Adds a Unit Test to ensure all basic mobs have a HUD that shows some form of health & combat mode (unless unnecessary like Revenants), to not lose any information from basic mob conversion now that they lose the stat panel. ## Why It's Good For The Game This is yet another supplement of https://github.com/tgstation/tgstation/pull/95383 - Removing stat panel entries for all basic mobs is a great move towards lessening the reliance of mobs on the stat panel. This is also just good QoL for Holoparasites, especially since their stat panel had 2 entries for "Master HP" and your "Own" HP, despite Holoparasites not even having their own HP at all. ## Changelog :cl: qol: Holoparasites' abilities are now action buttons, so you can rebind or move them. qol: Holoparasites now have floor change buttons. fix: Holoparasites and Soulscythes now have a combat mode indicator. fix: Dextrous holoparasites now have their inventory slot again while non-dextrous had it taken away (not that they could use it). /:cl: --- code/__DEFINES/hud.dm | 7 ++ code/__DEFINES/traits/declarations.dm | 2 +- code/_onclick/hud/generic_dextrous.dm | 12 ++- code/_onclick/hud/guardian.dm | 35 +++----- code/_onclick/hud/hud.dm | 3 + code/_onclick/hud/screen_objects/alert.dm | 6 -- code/_onclick/hud/screen_objects/guardian.dm | 68 ---------------- .../hud/screen_objects/screen_objects.dm | 33 +++++++- code/_onclick/hud/soulscythe.dm | 4 + code/_onclick/hud/voidwalker.dm | 1 + code/datums/actions/mobs/guardians.dm | 77 ++++++++++++++++++ code/datums/components/life_link.dm | 9 +- code/modules/mob/living/basic/basic.dm | 5 -- code/modules/mob/living/basic/boss/boss.dm | 1 + .../mob/living/basic/guardian/guardian.dm | 29 +++++-- .../basic/guardian/guardian_types/assassin.dm | 43 +++------- .../basic/guardian/guardian_types/charger.dm | 5 +- .../basic/guardian/guardian_types/dextrous.dm | 15 +++- .../guardian/guardian_types/explosive.dm | 1 + .../basic/guardian/guardian_types/gaseous.dm | 2 +- .../guardian/guardian_types/protector.dm | 2 +- .../basic/guardian/guardian_types/ranged.dm | 3 +- .../living/basic/guardian/guardian_verbs.dm | 9 -- .../basic/space_fauna/revenant/_revenant.dm | 2 +- code/modules/mob/living/living.dm | 1 + code/modules/mob/mob_defines.dm | 2 +- code/modules/unit_tests/huds.dm | 23 ++++++ icons/hud/64x16_actions.dmi | Bin 3443 -> 4854 bytes icons/hud/guardian.dmi | Bin 13153 -> 13719 bytes tgstation.dme | 2 +- 30 files changed, 232 insertions(+), 170 deletions(-) delete mode 100644 code/_onclick/hud/screen_objects/guardian.dm create mode 100644 code/datums/actions/mobs/guardians.dm diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 7bbd3b5fc6c..d00c7ac066e 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -361,6 +361,13 @@ #define ui_ghost_language_menu "SOUTH:6,CENTER+4:22" #define ui_ghost_floor_changer "SOUTH:6,CENTER+4:7" +//Holoparasites +#define ui_guardian_floor_changer "EAST-1:28,SOUTH+1:7" +#define ui_guardian_communication "CENTER-2:16,SOUTH+0:5" +#define ui_guardian_manifest "CENTER-1:16,SOUTH+0:5" +#define ui_guardian_recall "CENTER+0:16,SOUTH+0:5" +#define ui_guardian_special "CENTER+1:16,SOUTH+0:5" + //Voidwalker #define ui_voidwalker_left_of_hands "CENTER+-2:16,SOUTH+0:5" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index e3c4bdd8f77..77b96215a44 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1527,7 +1527,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai * (This may be changed later but I chose to do it this way to avoid messing up interactions which require combat mode) */ #define TRAIT_COMBAT_MODE_SKIP_INTERACTION "combat_mode_skip_interaction" -// bars change of combat mode +///Prevents the user from swapping combat intent. #define TRAIT_COMBAT_MODE_LOCK "combat_mode_lock" ///A "fake" effect that should not be subject to normal effect removal methods (like the effect remover component) diff --git a/code/_onclick/hud/generic_dextrous.dm b/code/_onclick/hud/generic_dextrous.dm index 8a7950cfe63..a0faa532533 100644 --- a/code/_onclick/hud/generic_dextrous.dm +++ b/code/_onclick/hud/generic_dextrous.dm @@ -1,3 +1,9 @@ +/datum/hud/dextrous + ///Boolean on whether to give the generic combat indicator + var/give_generic_combat = TRUE + ///Boolean on whether to give a health doll. + var/give_health_doll = TRUE + //Used for normal mobs that have hands. /datum/hud/dextrous/initialize_screen_objects() . = ..() @@ -11,12 +17,14 @@ build_hand_slots() add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style, ui_below_throw) - add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_INFO, ui_style, ui_movi) + if(give_generic_combat) + add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_INFO, ui_style, ui_movi) + if(give_health_doll) + add_screen_object(/atom/movable/screen/healthdoll/living, HUD_MOB_HEALTHDOLL, HUD_GROUP_INFO) add_screen_object(/atom/movable/screen/floor_changer, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_above_movement) add_screen_object(/atom/movable/screen/zone_sel, HUD_MOB_ZONE_SELECTOR, HUD_GROUP_STATIC, ui_style) add_screen_object(/atom/movable/screen/area_creator, HUD_MOB_AREA_CREATOR, HUD_GROUP_STATIC, ui_style) add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style) - add_screen_object(/atom/movable/screen/healthdoll/living, HUD_MOB_HEALTHDOLL, HUD_GROUP_INFO) if(HAS_TRAIT(mymob, TRAIT_CAN_THROW_ITEMS)) add_screen_object(/atom/movable/screen/throw_catch, HUD_MOB_THROW, HUD_GROUP_HOTKEYS, ui_style, ui_drop_throw) diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index cabe261b14e..18b1b985ccb 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -1,35 +1,19 @@ -/datum/hud/guardian - ui_style = 'icons/hud/guardian.dmi' - inventory_slots = list(/datum/inventory_slot/guardian_storage) - /datum/hud/guardian/initialize_screen_objects() . = ..() - add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style) - add_screen_object(/atom/movable/screen/guardian/manifest, HUD_GUARDIAN_MANIFEST, ui_loc = ui_hand_position(RIGHT_HANDS)) - add_screen_object(/atom/movable/screen/guardian/recall, HUD_GUARDIAN_RECALL, ui_loc = ui_hand_position(LEFT_HANDS)) - add_screen_object(/atom/movable/screen/guardian/toggle_light, HUD_GUARDIAN_LIGHT) - add_screen_object(/atom/movable/screen/guardian/communicate, HUD_GUARDIAN_COMMUNICATE) - + add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, 'icons/hud/guardian.dmi') + add_screen_object(/atom/movable/screen/floor_changer, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_below_throw) add_screen_object(/atom/movable/screen/healths/guardian, HUD_MOB_HEALTH, HUD_GROUP_INFO) + add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style, ui_loc = ui_zonesel) - var/mob/living/basic/guardian/owner = mymob - if (istype(owner)) - add_screen_object(owner.toggle_button_type, HUD_GUARDIAN_TOGGLE, ui_loc = ui_storage1) +///Dextrous subtype for only dextrous holoparasites. Can hold things hence the inventory slot. +/datum/hud/dextrous/guardian + inventory_slots = list(/datum/inventory_slot/guardian_storage) + give_health_doll = FALSE /datum/hud/dextrous/guardian/initialize_screen_objects() . = ..() - add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style) - add_screen_object(/atom/movable/screen/guardian/communicate, HUD_GUARDIAN_COMMUNICATE, ui_loc = ui_sstore1) - add_screen_object(/atom/movable/screen/guardian/manifest, HUD_GUARDIAN_MANIFEST, ui_loc = ui_belt) - add_screen_object(/atom/movable/screen/guardian/recall, HUD_GUARDIAN_RECALL, ui_loc = ui_back) - add_screen_object(/atom/movable/screen/guardian/toggle_light, HUD_GUARDIAN_LIGHT) - add_screen_object(/atom/movable/screen/healths/guardian, HUD_MOB_HEALTH, HUD_GROUP_INFO) - var/mob/living/basic/guardian/owner = mymob - if (istype(owner)) - add_screen_object(owner.toggle_button_type, HUD_GUARDIAN_TOGGLE, ui_loc = ui_storage2) - /datum/hud/dextrous/guardian/persistent_inventory_update() if(!mymob) return @@ -40,7 +24,7 @@ var/mob/living/basic/guardian/dextrous/dex_guardian = mymob if(hud_shown) if(dex_guardian.internal_storage) - dex_guardian.internal_storage.screen_loc = ui_id + dex_guardian.internal_storage.screen_loc = ui_back dex_guardian.client.screen += dex_guardian.internal_storage else if(dex_guardian.internal_storage) @@ -51,4 +35,5 @@ name = "internal storage" icon_state = "suit_storage" slot_id = ITEM_SLOT_DEX_STORAGE - screen_loc = ui_id + screen_loc = ui_back + diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 3ac3685874d..4f917217706 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -58,6 +58,8 @@ GLOBAL_LIST_INIT(available_ui_styles, list( /// If this client is being shown atmos debug overlays or not var/atmos_debug_overlays = FALSE + ///Boolean on whether they need at least a healths or healdoll, for unit tests. + var/needs_health_indicator = TRUE /// The color to use for the screentips. /// This is updated by the preference for cheaper reads than would be /// had with a proc call, especially on one of the hottest procs in the game (MouseEntered). @@ -303,6 +305,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list( hud_used = new_hud new_hud.build_action_groups() + /** * Shows this hud's hud to some mob * diff --git a/code/_onclick/hud/screen_objects/alert.dm b/code/_onclick/hud/screen_objects/alert.dm index ca4fcc22f80..6a79bf6b0e9 100644 --- a/code/_onclick/hud/screen_objects/alert.dm +++ b/code/_onclick/hud/screen_objects/alert.dm @@ -754,12 +754,6 @@ //GUARDIANS -/atom/movable/screen/alert/canstealth - name = "Stealth Ready" - desc = "You are ready to enter stealth!" - icon_state = "guardian_canstealth" - alerttooltipstyle = "parasite" - /atom/movable/screen/alert/status_effect/instealth name = "In Stealth" desc = "You are in stealth and your next attack will do bonus damage!" diff --git a/code/_onclick/hud/screen_objects/guardian.dm b/code/_onclick/hud/screen_objects/guardian.dm deleted file mode 100644 index 41282bc45ed..00000000000 --- a/code/_onclick/hud/screen_objects/guardian.dm +++ /dev/null @@ -1,68 +0,0 @@ -/atom/movable/screen/guardian - icon = 'icons/hud/guardian.dmi' - mouse_over_pointer = MOUSE_HAND_POINTER - -/atom/movable/screen/guardian/manifest - name = "Manifest" - desc = "Spring forth into battle!" - icon_state = "manifest" - -/atom/movable/screen/guardian/manifest/Click() - if(isguardian(usr)) - var/mob/living/basic/guardian/user = usr - user.manifest() - -/atom/movable/screen/guardian/recall - name = "Recall" - desc = "Return to your user." - icon_state = "recall" - -/atom/movable/screen/guardian/recall/Click() - if(isguardian(usr)) - var/mob/living/basic/guardian/user = usr - user.recall() - -/atom/movable/screen/guardian/toggle_mode - name = "Toggle Mode" - desc = "Switch between ability modes." - icon_state = "toggle" - -/atom/movable/screen/guardian/toggle_mode/Click() - if(isguardian(usr)) - var/mob/living/basic/guardian/user = usr - user.toggle_modes() - -/atom/movable/screen/guardian/toggle_mode/inactive - icon_state = "notoggle" //greyed out so it doesn't look like it'll work - -/atom/movable/screen/guardian/toggle_mode/assassin - name = "Toggle Stealth" - desc = "Enter or exit stealth." - icon_state = "stealth" - -/atom/movable/screen/guardian/toggle_mode/gases - name = "Toggle Gas" - desc = "Switch between possible gases." - icon_state = "gases" - -/atom/movable/screen/guardian/communicate - name = "Communicate" - desc = "Communicate telepathically with your user." - icon_state = "communicate" - screen_loc = ui_back - -/atom/movable/screen/guardian/communicate/Click() - if(isguardian(usr)) - var/mob/living/basic/guardian/user = usr - user.communicate() - -/atom/movable/screen/guardian/toggle_light - name = "Toggle Light" - desc = "Glow like star dust." - icon_state = "light" - screen_loc = ui_inventory - -/atom/movable/screen/guardian/toggle_light/Click() - if(isguardian(usr)) - var/mob/living/basic/guardian/user = usr - user.toggle_light() diff --git a/code/_onclick/hud/screen_objects/screen_objects.dm b/code/_onclick/hud/screen_objects/screen_objects.dm index e21dd0bc2dc..7631674c460 100644 --- a/code/_onclick/hud/screen_objects/screen_objects.dm +++ b/code/_onclick/hud/screen_objects/screen_objects.dm @@ -864,7 +864,6 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen) // I hate this place /atom/movable/screen/healthdoll name = "health doll" screen_loc = ui_healthdoll - mouse_over_pointer = MOUSE_HAND_POINTER /atom/movable/screen/healthdoll/Click() if (iscarbon(usr)) @@ -877,9 +876,41 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen) // I hate this place /atom/movable/screen/healthdoll/living icon_state = "fullhealth0" screen_loc = ui_living_healthdoll + ///The image we create on New to use for showing mob's HP on hover. It uses a separate image as to not be cut off by `alpha_mask_filter`. + var/image/health_overlay + ///Boolean on whether a mouse is being hovered over us right now. + var/hovering = FALSE var/filtered = FALSE //so we don't repeatedly create the mask of the mob every update +/atom/movable/screen/healthdoll/living/New(loc, ...) + . = ..() + health_overlay = image(loc = src, layer = src.layer+0.1) + +/atom/movable/screen/healthdoll/living/Destroy() + QDEL_NULL(health_overlay) + return ..() + +/atom/movable/screen/healthdoll/living/update_overlays() + . = ..() + if(hovering) + . |= health_overlay + +/atom/movable/screen/healthdoll/living/MouseEntered(location,control,params) + if(usr != get_mob()) + return + . = ..() + hovering = TRUE + update_appearance(UPDATE_ICON) + +/atom/movable/screen/healthdoll/living/MouseExited(location, control, params) + if(usr != get_mob()) + return + . = ..() + hovering = FALSE + update_appearance(UPDATE_ICON) + /atom/movable/screen/healthdoll/human + mouse_over_pointer = MOUSE_HAND_POINTER /// Tracks components of our doll, each limb is a separate atom in our vis_contents VAR_PRIVATE/list/atom/movable/screen/limbs /// Lazylist, tracks all body zones that are wounded currently diff --git a/code/_onclick/hud/soulscythe.dm b/code/_onclick/hud/soulscythe.dm index e290d960e56..53b2d850011 100644 --- a/code/_onclick/hud/soulscythe.dm +++ b/code/_onclick/hud/soulscythe.dm @@ -1,3 +1,7 @@ +/datum/hud/soulscythe + needs_health_indicator = FALSE //we use blood level instead. + /datum/hud/soulscythe/initialize_screen_objects() . = ..() add_screen_object(/atom/movable/screen/blood_level, HUD_MOB_BLOOD_LEVEL, HUD_GROUP_INFO) + add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style, ui_loc = ui_zonesel) diff --git a/code/_onclick/hud/voidwalker.dm b/code/_onclick/hud/voidwalker.dm index c6da1e0bf10..5460ee90a5b 100644 --- a/code/_onclick/hud/voidwalker.dm +++ b/code/_onclick/hud/voidwalker.dm @@ -1,5 +1,6 @@ /datum/hud/dextrous/voidwalker ui_style = 'icons/hud/screen_voidwalker.dmi' + give_generic_combat = FALSE //we give our own version below, here. /datum/hud/dextrous/voidwalker/initialize_screen_objects() . = ..() diff --git a/code/datums/actions/mobs/guardians.dm b/code/datums/actions/mobs/guardians.dm new file mode 100644 index 00000000000..ff912c5300e --- /dev/null +++ b/code/datums/actions/mobs/guardians.dm @@ -0,0 +1,77 @@ +/datum/action/cooldown/guardian + button_icon = 'icons/hud/guardian.dmi' + +/datum/action/cooldown/guardian/IsAvailable(feedback) + . = ..() + if(!.) + return . + return !!isguardian(owner) + +/datum/action/cooldown/guardian/check_type + name = "Check Type" + desc = "A reminder on what your abilities are." + //this is based off of the antag ui icon, if that changes then change this too please. + button_icon_state = /datum/action/antag_info::button_icon_state + default_button_position = SCRN_OBJ_INSERT_FIRST + +/datum/action/cooldown/guardian/check_type/Activate(atom/target) + . = ..() + to_chat(owner, astype(owner, /mob/living/basic/guardian)?.playstyle_string) + +/datum/action/cooldown/guardian/communicate + name = "Communicate" + desc = "Communicate telepathically with your user." + button_icon_state = "communicate" + default_button_position = ui_guardian_communication + +/datum/action/cooldown/guardian/communicate/Activate() + astype(owner, /mob/living/basic/guardian)?.communicate() + +/datum/action/cooldown/guardian/manifest + name = "Manifest" + desc = "Spring forth into battle!" + button_icon_state = "manifest" + default_button_position = ui_guardian_manifest + +/datum/action/cooldown/guardian/manifest/Activate() + astype(owner, /mob/living/basic/guardian)?.manifest() + +/datum/action/cooldown/guardian/recall + name = "Recall" + desc = "Return to your user." + button_icon_state = "recall" + default_button_position = ui_guardian_recall + +/datum/action/cooldown/guardian/recall/Activate() + astype(owner, /mob/living/basic/guardian)?.recall() + +/datum/action/cooldown/guardian/toggle_light + name = "Toggle Light" + desc = "Glow like star dust." + button_icon_state = "light" + +/datum/action/cooldown/guardian/toggle_light/Activate() + astype(owner, /mob/living/basic/guardian)?.toggle_light() + +/datum/action/cooldown/guardian/toggle_mode + name = "Toggle Mode" + desc = "Switch between ability modes." + button_icon_state = "toggle" + default_button_position = ui_guardian_special + +/datum/action/cooldown/guardian/toggle_mode/Activate() + astype(owner, /mob/living/basic/guardian)?.toggle_modes() + +/datum/action/cooldown/guardian/toggle_mode/assassin + name = "Toggle Stealth" + desc = "Enter or exit stealth." + button_icon_state = "stealth" + transparent_when_unavailable = TRUE + +/datum/action/cooldown/guardian/toggle_mode/assassin/is_action_active(atom/movable/screen/movable/action_button/current_button) + return owner.has_status_effect(/datum/status_effect/guardian_stealth) + +/datum/action/cooldown/guardian/toggle_mode/gases + name = "Toggle Gas" + desc = "Switch between possible gases." + button_icon_state = "gases" diff --git a/code/datums/components/life_link.dm b/code/datums/components/life_link.dm index d1371467682..026826e4fea 100644 --- a/code/datums/components/life_link.dm +++ b/code/datums/components/life_link.dm @@ -25,14 +25,13 @@ RegisterSignal(parent, COMSIG_CARBON_LIMB_DAMAGED, PROC_REF(on_limb_damage)) RegisterSignals(parent, COMSIG_LIVING_ADJUST_STANDARD_DAMAGE_TYPES, PROC_REF(on_damage_adjusted)) RegisterSignal(parent, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(on_health_updated)) - RegisterSignal(parent, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(on_status_tab_updated)) if (!isnull(host)) var/mob/living/living_parent = parent living_parent.updatehealth() /datum/component/life_link/UnregisterFromParent() unregister_host() - UnregisterSignal(parent, list(COMSIG_CARBON_LIMB_DAMAGED, COMSIG_LIVING_HEALTH_UPDATE, COMSIG_MOB_GET_STATUS_TAB_ITEMS) + COMSIG_LIVING_ADJUST_STANDARD_DAMAGE_TYPES) + UnregisterSignal(parent, list(COMSIG_CARBON_LIMB_DAMAGED, COMSIG_LIVING_HEALTH_UPDATE) + COMSIG_LIVING_ADJUST_STANDARD_DAMAGE_TYPES) /datum/component/life_link/InheritComponent(datum/component/new_comp, i_am_original, mob/living/host, datum/callback/on_passed_damage, datum/callback/on_linked_death) register_host(host) @@ -133,12 +132,6 @@ else mob_parent.set_hud_image_state(STATUS_HUD, "hudhealthy") -/// When our status tab updates, draw how much HP our host has in there -/datum/component/life_link/proc/on_status_tab_updated(mob/living/source, list/items) - SIGNAL_HANDLER - var/healthpercent = health_percentage(host) - items += "Host Health: [round(healthpercent, 0.5)]%" - /// Called when our host dies, we should die too /datum/component/life_link/proc/on_host_died(mob/living/source, gibbed) SIGNAL_HANDLER diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index bb89e69b42c..0392cfcc6bc 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -293,11 +293,6 @@ return return relaydrive(user, direction) -/mob/living/basic/get_status_tab_items() - . = ..() - . += "Health: [round((health / maxHealth) * 100)]%" - . += "Combat Mode: [combat_mode ? "On" : "Off"]" - /mob/living/basic/compare_sentience_type(compare_type) return sentience_type == compare_type diff --git a/code/modules/mob/living/basic/boss/boss.dm b/code/modules/mob/living/basic/boss/boss.dm index 31a7d2379a9..4ffd7961de3 100644 --- a/code/modules/mob/living/basic/boss/boss.dm +++ b/code/modules/mob/living/basic/boss/boss.dm @@ -4,6 +4,7 @@ /mob/living/basic/boss combat_mode = TRUE status_flags = NONE + abstract_type = /mob/living/basic/boss sentience_type = SENTIENCE_BOSS mob_biotypes = MOB_ORGANIC|MOB_SPECIAL faction = list(FACTION_MINING, FACTION_BOSS) diff --git a/code/modules/mob/living/basic/guardian/guardian.dm b/code/modules/mob/living/basic/guardian/guardian.dm index fd84556c99d..9c2592f9748 100644 --- a/code/modules/mob/living/basic/guardian/guardian.dm +++ b/code/modules/mob/living/basic/guardian/guardian.dm @@ -34,7 +34,6 @@ attack_sound = 'sound/items/weapons/punch1.ogg' attack_verb_continuous = "punches" attack_verb_simple = "punch" - combat_mode = TRUE obj_damage = 40 melee_damage_lower = 15 melee_damage_upper = 15 @@ -53,8 +52,8 @@ /// Coloured overlay we apply var/mutable_appearance/overlay - /// Which toggle button the HUD uses. - var/toggle_button_type = /atom/movable/screen/guardian/toggle_mode/inactive + /// Which toggle button the guardian has. Won't get one if it's null. + var/toggle_button_type = null /// Name used by the guardian creator. var/creator_name = "Error" /// Description used by the guardian creator. @@ -76,12 +75,20 @@ /// Cooldown between the summoner resetting the guardian's client. COOLDOWN_DECLARE(resetting_cooldown) - /// List of actions we give to our summoner + /// List of actions we give to our summoner. var/static/list/control_actions = list( /datum/action/cooldown/mob_cooldown/guardian_comms, /datum/action/cooldown/mob_cooldown/recall_guardian, /datum/action/cooldown/mob_cooldown/replace_guardian, ) + /// List of actions we give to ourselves. + var/static/list/self_actions = list( + /datum/action/cooldown/guardian/check_type, + /datum/action/cooldown/guardian/toggle_light, + /datum/action/cooldown/guardian/communicate, + /datum/action/cooldown/guardian/manifest, + /datum/action/cooldown/guardian/recall, + ) /mob/living/basic/guardian/Initialize(mapload, datum/guardian_fluff/theme) . = ..() @@ -90,10 +97,10 @@ theme?.apply(src) AddElement(/datum/element/death_drops, /obj/effect/temp_visual/guardian/phase/out) AddElement(/datum/element/simple_flying) - AddComponent(/datum/component/basic_inhands) // life link update_appearance(UPDATE_ICON) manifest_effects() + create_actions() /mob/living/basic/guardian/Destroy() GLOB.parasites -= src @@ -102,6 +109,18 @@ cut_summoner(different_person = TRUE) return ..() +///Creates the guardian's default action buttons and sets them to go in their proper location. +///Subtypes overwrite this for special ability types and whatnot. +/mob/living/basic/guardian/proc/create_actions() + for (var/action_type in self_actions + toggle_button_type) + if(isnull(action_type)) //no toggle button type + continue + if (locate(action_type) in actions) + continue + var/datum/action/new_action = new action_type(src) + new_action.Grant(src) + update_action_buttons() + /mob/living/basic/guardian/update_overlays() . = ..() . += overlay diff --git a/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm b/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm index 19f98a70f40..377c3fec0b7 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm @@ -1,5 +1,3 @@ -#define CAN_STEALTH_ALERT "can_stealth" - /** * Can enter stealth mode to become invisible and deal bonus damage on their next attack, an ambush predator. */ @@ -17,57 +15,44 @@ creator_name = "Assassin" creator_desc = "Does medium damage and takes full damage, but can enter stealth, causing its next attack to do massive damage and ignore armor. However, it becomes briefly unable to recall after attacking from stealth." creator_icon = "assassin" - toggle_button_type = /atom/movable/screen/guardian/toggle_mode/assassin + toggle_button_type = /datum/action/cooldown/guardian/toggle_mode/assassin /// How long to put stealth on cooldown if we are forced out? var/stealth_cooldown_time = 16 SECONDS - /// Cooldown for the stealth toggle. - COOLDOWN_DECLARE(stealth_cooldown) /mob/living/basic/guardian/assassin/Initialize(mapload, datum/guardian_fluff/theme) . = ..() - show_can_stealth() RegisterSignal(src, COMSIG_GUARDIAN_ASSASSIN_REVEALED, PROC_REF(on_forced_unstealth)) // Toggle stealth /mob/living/basic/guardian/assassin/toggle_modes() var/stealthed = has_status_effect(/datum/status_effect/guardian_stealth) + var/datum/action/cooldown/guardian/toggle_mode/assassin/stealth_ability = locate() in actions if (stealthed) to_chat(src, span_bolddanger("You exit stealth.")) remove_status_effect(/datum/status_effect/guardian_stealth) - show_can_stealth() + if(stealth_ability) + stealth_ability.build_all_button_icons() return - if (COOLDOWN_FINISHED(src, stealth_cooldown)) - if (!is_deployed()) - to_chat(src, span_bolddanger("You have to be manifested to enter stealth!")) - return - apply_status_effect(/datum/status_effect/guardian_stealth) - clear_alert(CAN_STEALTH_ALERT) + if (!is_deployed()) + to_chat(src, span_bolddanger("You have to be manifested to enter stealth!")) return - to_chat(src, span_bolddanger("You cannot yet enter stealth, wait another [DisplayTimeText(COOLDOWN_TIMELEFT(src, stealth_cooldown))]!")) - -/mob/living/basic/guardian/assassin/get_status_tab_items() - . = ..() - if(!COOLDOWN_FINISHED(src, stealth_cooldown)) - . += "Stealth Cooldown Remaining: [DisplayTimeText(COOLDOWN_TIMELEFT(src, stealth_cooldown))]" + apply_status_effect(/datum/status_effect/guardian_stealth) + if(stealth_ability) + stealth_ability.build_all_button_icons() /// Called when we are removed from stealth involuntarily /mob/living/basic/guardian/assassin/proc/on_forced_unstealth(mob/living/source) SIGNAL_HANDLER visible_message(span_danger("\The [src] suddenly appears!")) COOLDOWN_START(src, manifest_cooldown, 4 SECONDS) - COOLDOWN_START(src, stealth_cooldown, stealth_cooldown_time) - addtimer(CALLBACK(src, PROC_REF(show_can_stealth)), stealth_cooldown_time) - -/// Displays an alert letting us know that we can enter stealth -/mob/living/basic/guardian/assassin/proc/show_can_stealth() - if(!COOLDOWN_FINISHED(src, stealth_cooldown)) - return - throw_alert(CAN_STEALTH_ALERT, /atom/movable/screen/alert/canstealth) + var/datum/action/cooldown/guardian/toggle_mode/assassin/stealth_ability = locate() in actions + if(stealth_ability) + stealth_ability.StartCooldownSelf(stealth_cooldown_time) /// Status effect which makes us sneakier and do bonus damage /datum/status_effect/guardian_stealth id = "guardian_stealth" - alert_type = /atom/movable/screen/alert/status_effect/instealth + alert_type = null /// Damage added in stealth mode. var/damage_bonus = 35 /// Our wound bonus when in stealth mode. Allows you to actually cause wounds, unlike normal. @@ -112,5 +97,3 @@ SIGNAL_HANDLER SEND_SIGNAL(owner, COMSIG_GUARDIAN_ASSASSIN_REVEALED) qdel(src) - -#undef CAN_STEALTH_ALERT diff --git a/code/modules/mob/living/basic/guardian/guardian_types/charger.dm b/code/modules/mob/living/basic/guardian/guardian_types/charger.dm index 10ef9c34d53..2d21803c25f 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/charger.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/charger.dm @@ -11,13 +11,11 @@ creator_name = "Charger" creator_desc = "Moves very fast, does medium damage on attack, can be ridden and can charge at targets, damaging the first target hit and forcing them to drop any items they are holding." creator_icon = "charger" + toggle_button_type = /datum/action/cooldown/mob_cooldown/charge/basic_charge/guardian /mob/living/basic/guardian/charger/Initialize(mapload, datum/guardian_fluff/theme) . = ..() AddElement(/datum/element/ridable, /datum/component/riding/creature/guardian) - var/datum/action/cooldown/mob_cooldown/charge/basic_charge/guardian/charge = new(src) - charge.Grant(src) - charge.set_click_ability(src) /// Guardian charger's charging attack, it knocks items out of people's hands /datum/action/cooldown/mob_cooldown/charge/basic_charge/guardian @@ -28,6 +26,7 @@ button_icon_state = "speed" background_icon = 'icons/hud/guardian.dmi' background_icon_state = "base" + default_button_position = ui_guardian_special charge_delay = 0 recoil_duration = 0 charge_damage = 20 diff --git a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm index 206b7b79e12..fc4ec678837 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm @@ -15,6 +15,7 @@ /mob/living/basic/guardian/dextrous/Initialize(mapload, datum/guardian_fluff/theme) . = ..() + AddComponent(/datum/component/basic_inhands) add_traits(list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP), ROUNDSTART_TRAIT) AddElement(/datum/element/dextrous, hud_type = hud_type, can_throw = TRUE) AddComponent(/datum/component/personal_crafting) @@ -24,6 +25,18 @@ dropItemToGround(internal_storage) return ..() +/mob/living/basic/guardian/dextrous/create_actions() + for (var/action_type in self_actions) + if(isnull(action_type)) //no toggle button type + continue + if (locate(action_type) in actions) + continue + var/datum/action/new_action = new action_type(src) + //Show up at the top left like usual. + new_action.default_button_position = /datum/action::default_button_position + new_action.Grant(src) + update_action_buttons() + /mob/living/basic/guardian/dextrous/examine(mob/user) . = ..() if(isnull(internal_storage) || (internal_storage.item_flags & ABSTRACT)) @@ -95,7 +108,7 @@ /mob/living/basic/guardian/dextrous/proc/update_inv_internal_storage() if(isnull(internal_storage) || isnull(client) || !hud_used?.hud_shown) return - internal_storage.screen_loc = ui_id + internal_storage.screen_loc = ui_back client.screen += internal_storage /mob/living/basic/guardian/dextrous/regenerate_icons() diff --git a/code/modules/mob/living/basic/guardian/guardian_types/explosive.dm b/code/modules/mob/living/basic/guardian/guardian_types/explosive.dm index b3c0684571d..49c15f62f40 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/explosive.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/explosive.dm @@ -37,6 +37,7 @@ cooldown_time = 20 SECONDS background_icon = 'icons/hud/guardian.dmi' background_icon_state = "base" + default_button_position = ui_guardian_special /// After this amount of time passses, bomb deactivates. var/decay_time = 1 MINUTES /// Static list of signals that activate the bomb. diff --git a/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm b/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm index 8bf00ef4362..a70bd01f43b 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm @@ -9,7 +9,7 @@ creator_name = "Gaseous" creator_desc = "Creates sparks on touch and continuously expels a gas of its choice. Automatically extinguishes the user if they catch on fire." creator_icon = "gaseous" - toggle_button_type = /atom/movable/screen/guardian/toggle_mode/gases + toggle_button_type = /datum/action/cooldown/guardian/toggle_mode/gases /// Ability we use to select gases var/datum/action/cooldown/mob_cooldown/expel_gas/gas /// Rate of temperature stabilization per second. diff --git a/code/modules/mob/living/basic/guardian/guardian_types/protector.dm b/code/modules/mob/living/basic/guardian/guardian_types/protector.dm index 45d746b2335..229732a30f8 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/protector.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/protector.dm @@ -9,7 +9,7 @@ creator_name = "Protector" creator_desc = "Causes you to teleport to it when out of range, unlike other parasites. Has two modes; Combat, where it does and takes medium damage, and Protection, where it does and takes almost no damage but moves slightly slower." creator_icon = "protector" - toggle_button_type = /atom/movable/screen/guardian/toggle_mode + toggle_button_type = /datum/action/cooldown/guardian/toggle_mode /// Action which toggles our shield var/datum/action/cooldown/mob_cooldown/protector_shield/shield diff --git a/code/modules/mob/living/basic/guardian/guardian_types/ranged.dm b/code/modules/mob/living/basic/guardian/guardian_types/ranged.dm index 6752c52a5af..126ca95da2c 100644 --- a/code/modules/mob/living/basic/guardian/guardian_types/ranged.dm +++ b/code/modules/mob/living/basic/guardian/guardian_types/ranged.dm @@ -13,7 +13,7 @@ creator_desc = "Has two modes. Ranged; which fires a constant stream of weak, armor-ignoring projectiles. Scout; where it cannot attack, but can move through walls and is quite hard to see. Can lay surveillance snares, which alert it when crossed, in either mode." creator_icon = "ranged" see_invisible = SEE_INVISIBLE_LIVING - toggle_button_type = /atom/movable/screen/guardian/toggle_mode + toggle_button_type = /datum/action/cooldown/guardian/toggle_mode /mob/living/basic/guardian/ranged/Initialize(mapload, datum/guardian_fluff/theme) . = ..() @@ -120,6 +120,7 @@ button_icon_state = "eye" background_icon = 'icons/hud/guardian.dmi' background_icon_state = "base" + default_button_position = ui_guardian_special cooldown_time = 2 SECONDS melee_cooldown_time = 0 click_to_activate = FALSE diff --git a/code/modules/mob/living/basic/guardian/guardian_verbs.dm b/code/modules/mob/living/basic/guardian/guardian_verbs.dm index c63dffd4316..dc95097953c 100644 --- a/code/modules/mob/living/basic/guardian/guardian_verbs.dm +++ b/code/modules/mob/living/basic/guardian/guardian_verbs.dm @@ -43,15 +43,6 @@ to_chat(src, span_notice("You deactivate your light.")) set_light_on(FALSE) - -/// Prints what type of guardian we are and what we can do. -/mob/living/basic/guardian/verb/check_type() - set name = "Check Guardian Type" - set category = "Guardian" - set desc = "Check what type you are." - to_chat(src, playstyle_string) - - /// Speak with our boss at a distance /mob/living/basic/guardian/proc/communicate() if (isnull(summoner)) diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm index ea32e7e5d6d..3bd92b48950 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm @@ -95,7 +95,7 @@ /mob/living/basic/revenant/Initialize(mapload) . = ..() AddElement(/datum/element/simple_flying) - add_traits(list(TRAIT_SPACEWALK, TRAIT_SIXTHSENSE, TRAIT_FREE_HYPERSPACE_MOVEMENT, TRAIT_SEE_BLESSED_TILES, TRAIT_IGNORE_ELEVATION), INNATE_TRAIT) + add_traits(list(TRAIT_COMBAT_MODE_LOCK, TRAIT_SPACEWALK, TRAIT_SIXTHSENSE, TRAIT_FREE_HYPERSPACE_MOVEMENT, TRAIT_SEE_BLESSED_TILES, TRAIT_IGNORE_ELEVATION), INNATE_TRAIT) grant_actions_by_list(abilities) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fb7af94d3c4..5ee30cec038 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -861,6 +861,7 @@ mob_mask = icon('icons/hud/screen_gen.dmi', health_doll_icon_state) //swap to something generic if they have no special doll livingdoll.add_filter("mob_shape_mask", 1, alpha_mask_filter(icon = mob_mask)) livingdoll.add_filter("inset_drop_shadow", 2, drop_shadow_filter(size = -1)) + livingdoll.health_overlay.maptext = MAPTEXT("
[round(healthpercent, 1)]%
") if(severity > 0) overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index d43edf33d74..43585ef7886 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -191,7 +191,7 @@ var/list/client_colours = list() /// List of filter names used in the past client color update for cleanup var/list/color_filter_store = list() - var/hud_type = /datum/hud + var/datum/hud/hud_type = /datum/hud var/datum/focus //What receives our keyboard inputs. src by default diff --git a/code/modules/unit_tests/huds.dm b/code/modules/unit_tests/huds.dm index 8aadb770260..ab2a21474bb 100644 --- a/code/modules/unit_tests/huds.dm +++ b/code/modules/unit_tests/huds.dm @@ -16,3 +16,26 @@ REMOVE_TRAIT(dummy, TRAIT_SECURITY_HUD, TRAIT_SOURCE_UNIT_TESTS) TEST_ASSERT(!testhud.hud_users_all_z_levels[dummy], "HUD not removed when trait of HUD was removed") + +///We're gonna give every HUD type to a mob to see if they are missing action intent/health doll. +///This destroys the HUD of the mob we're using (but it doesn't matter cause it's a test) +/datum/unit_test/verify_basic_huds + +/datum/unit_test/verify_basic_huds/Run() + for(var/mob/living/basic/mobs as anything in subtypesof(/mob/living/basic)) + if(mobs::abstract_type == mobs) + continue + var/mob/living/basic/dummy = allocate(mobs) + var/mob_hud_type = mobs::hud_type + var/datum/hud/initialized_hud = new mob_hud_type(dummy) + //mobs that don't use combat mode don't need it. + var/atom/movable/screen/action_intent = initialized_hud.screen_objects[HUD_MOB_INTENTS] + if(!HAS_TRAIT(dummy, TRAIT_COMBAT_MODE_LOCK) && isnull(action_intent)) + TEST_FAIL("[dummy] using [initialized_hud.type] does not have an Action Intent.") + //Mobs that need a health indicator should have at least a healthdoll or healths. + var/atom/movable/screen/healthdoll/healthdoll = initialized_hud.screen_objects[HUD_MOB_HEALTHDOLL] + var/atom/movable/screen/healths/healths = initialized_hud.screen_objects[HUD_MOB_HEALTH] + if(initialized_hud.needs_health_indicator && isnull(healthdoll) && isnull(healths)) + TEST_FAIL("[dummy] using [initialized_hud.type] does not have a Health Doll or Health HUD.") + //Because we're not setting hud_used, the HUD doesn't delete with the mob, let's clear it here. + qdel(initialized_hud) diff --git a/icons/hud/64x16_actions.dmi b/icons/hud/64x16_actions.dmi index 6a54c8e4bb36db58bda8503b4a7c7b229ef25a35..9b8e936d32d0715d9170522d3ecf607588acdfd9 100644 GIT binary patch literal 4854 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 uW7RL|%R7QFxSgH>G_WuBJs$-4- diff --git a/icons/hud/guardian.dmi b/icons/hud/guardian.dmi index 6b6c03912dd7d66d9df8a6df3e0ac27a2e1e2c2e..e39b6681f69c269d8a542970a5ddfa18533f3980 100644 GIT binary patch literal 13719 zcmZv@WmFtZ*9F?RI|O%!V8MgCyN2KpAh_G$?#_d|L$KiP?g4_k2Ztbo-pTv@xp%G0 zs_E`3s%E-QAKCkynMhS-88l=fWB>rrOH@}$2n!Hwrq;+59@9*tCfFU*I_t&A;epHt6)C>zwO5{nRAmwg5OjhaT z*l3-dSNDqJU9Ess!4f&&>L&{O6hpJs08o;i{yQbgZo-Y8Axf1iOCMT{qrw4m&G}s5 zEgRIjGV#IFSkpJBj_R=pjVs+q)sTinZM2 zoSd9dE4p>n^qI?>7Q}Pg_X;Cy;b%n*48RQf_~kRD4Lzm#>BkK@@7gW$A(qOjDsP4G z-q}2tKgFlNtDn1C{%GWZE+1G6?k~46faSOSc`l$lIVD|oIqVz=_?4Dh(z>IQ<;$VZ zazKCoZSe8_TwwOsZ(x-=XBJge9plE?!t^;=Wu68{r|9+9FZ?SuVqO4vd)Vu0T^(0B z(~ye(uVL85`KOxpy~S)TmpbIFmwz5uTG?10^I6C7C@ z0=uEYVeN$y|E}}KAUA6MsUlXoNa;-fOc-Eb25EGCo0x&zL2sDTIsONtXS(9Q_~)k* zO+A{|6Z;y#5_2gNJIR&1hWJ|Uh@WskKZoAN-wNS=p$RJ2Y}XQ#f> zE*?I9(VCCw>y3t!ArP_jhU+>mlm86ZdjkoWc~;DjP5BPo@iv^JnQkR~ojoGBjU~4u za7KB8e857uY2JS@MX$Y$UcE`lJ&N!FKZ~+XL|*Q{gFDFm`-egG``^JmuOkhaEDBpi z0NtCF$nDGH0n$*n4ANvqwUnDu!MJfP9&N#h(sRj-RD<(>*3*hCl{WC(b@O89y#5P2 zcGBUjeoc}$!^9585RLFo`oK>vuzd&JTYK_+MYstc+Zy0S@%Su|_opRIILiOk9^nF7 z^lU;FcCC(75{#JZN$dajfIbs@`k@PMKv8-QFm`pt9U2;vRZ@~tQNfUwAxtcy5e>R1 zv$FR@(LhLlK5%-=sCRo-=(Jl#ml8Z6_kyO-970?XIrMgN{Qk_AEby)tOPh6=e@l}f zqDAe|-nHE7ME$d&!GtGk$F<|%SQ(9@K@R0s94W&}bM=h- zUm68lB;LXaxo6UNK?ee*c_^hj2TDI_Du!<)wmihrGBQ~74kO3M43G|FXxEaau;PuS z_#{9kmp2BvWJE`jmBh@ocW=8-g0XKm1e=ZI`5?F?_$1Wj>J_w@E~G-ML2EthRD;r_ z9+7-~fIBoWX~Egv(edRbW6A>P zgJ?%@8hBw@`+?o$ zEewd(k`J@xodp?+paQB@M`jZ|RUw50wEJaZzx-Y+4q@vN@*~Qi$#CH`wY7?2qcpx~ zC5o#B&SMWCPwi#mWsY$Bxzh$nLU+EjAUG`<5JLgi*l^iL4Onby0C3T6Lv8rB7MviP zeseod@x69dum-8=MV)_^dv_=Ztk+08t={zSl}WttVAqYmA!Oj}yxsyfY=jKZOhab4 z#(GkwqKF`HsNx#yRi{Ay0Atz5>0d^5`^=1tf!~{wyhfmB9@ma~7850~R1AK&voYi) z_!2(Q7(QzN#%mzp>Q;T=n0kg-+GuSvTXZc)R#r(=M+YCk1LZAROIcep{qR*TKH0q* zy$w?f7`9nsONuQPT7CcT7-rj_bRHTA8wskynZygVYd<*mZ)1A>*$ z?Rv{+?4zZ>zCgg%j<3i;@TkBo<@Pclfu72Nb=*M=Ts>)^5|Tf6Y3z!UjZaJ@CMNEr zVJ7bg3cmS7@82153@XfW6bHXi$MCR!7rB;yW0)$m;P%f4K1o4=jp zoGC{955CvIui<|kSvkFMbW&414uWYNPA7F|sBfQens>R1bT4zj6`+-y63^1Vc-F5` zw4bEJKPMl-;$;gRT`Qh2UwoTL^WGG=)3D9#P=EnWZqvEWf_uT%haRcVsg!^>q9{Xr zXgTqBRs70!oLT#{+EHcfb-ad4**l;JX7GPRh0>dO4X8Vz})`@CBfvqz6= zrOpJV*?uFc$!6)vV5{AYna6fHpsVUZzMI7|;(>e_6?jB-*n8jg1$vKD)FR_=;Ob5! zM5Wst#o9Le!G67Uf>>XhztB$QIE{`1pFVw>U0Fe*fKhqZ3+1$whIGb~eztL<$G_v# zeCxpOQVQSdU%&-?W0F2;L$?IBZdb0<@8L08HxaRQsxq;7S(M3qjKw!(NUhYgCK2>x z_%^nS_gYX+CpI)YApz8NrmEGrlzyIkQ=@!)JmHBKuaGo3#QpY`f>$oIh8oKQm)QxA z+JstIT}>*|W*_=d8H>KELT!2L)uifZ?yH9!vq+^xdp#ylRNQyAG#4`wvUPjM02NP6 z@VGm(FvYtC-C>4HOwCCS}<&uYKGRAE@W(Mm_gIS%+2yK2xprj2FEdglkWTF>Bn_z-h>;hM@&_+ z5g(2id5*VEh;K?vFTsX1=^`vq9KElBgOd7umI;Z8fu*I&wksB?oiDJRgBT18 z#D@~HP?5W@hrIZ5ItB(TnZt3UYb&1n!ZlQ3raj}YBb0!y;3>zH5!3s8!Q%H_d%Y$a zd-+u^Kveq;)u-`+bFw+Eu(TB3!=V+<5P#nDJ&jnKv4_ud5}Y<}Ny0n%6uaA+Olin) zk3YEt4NNZ>(o86NY#^$MRm22or-hW38kTD^-4n*f#2BwORwRrwkl#@Yivqgr(Zxa( z*8UsqZnGz@kmh+@5Pmf+a9OuR=6y_==egzg%%WIDS)ZnIx9QV|(RxI>wP{arKz&6? z(G&i(e3_GUIL)_zbOZyaYiK|L9~5W}-^~is20{CCJdw&HjTqkE-U)em@uj77g7=`Z zK^CNqTKeJu$!q02VH|a$bx+!vv%j5J;uR^S5*nh|G6gZoP`yWo@2(JRweIxVB8rNWa;`I}uRlH+RnP{3s>Zd1B9#BT&N@_w4%abo6gcafwnYRKUQ6D?)?+ z4Xy;bk~H?uqzuaXKSW`Y5L~K$do(k$6GeTq(5Pu{VIiLleB$J!rPJtoa2&z{XRqAq zmnpxdR%W?%AOnY!=CVt|$0qYU%QiB~63b47^C!#hn{lK9#)8I{Ti3Z??_~`=0}1&n ze*75B@z{(M0^I{c+L302v5u+nqN-3(7OoP0V$GSEnZ~1zyQ7J85Fts>nRN@YLL{>6 zIh8Z%3o$sywm-hoa?oP3P#$N?7Zo)%VUpP0@pYxmL;~)%7+;`pxRLL@T;1HpQm#L( z*IQKR3DBY$vc*>JrKWcnQ&v4T98O7|pTHBuO7m@yW%GMMH(2gV6v>YmEU{_%|9mg6 z-f}kNv0LTx$QGx>;r4+F#ofEKIcC&cyaUGWDN8pV7T;BoQ?WDmCB1^=BXe`fCet}c z!C$;9o0?KD&d}+SV;6J^grV4ehCzt$zl?>Qlx7DmXonP9h8^43*x{k7?Rw)UmLl z0tqNm-UnEdQU*EA*|n1NSuN*LmoQ}KC#`~(6rM>`1g}FrE;~5S8q&oVJof#NK=-f& z@h;k5BnAH0cMhH1EY^F{Yn6kATmgH|63d>U2n4LoPdX-znTvxIsBzViq)%f4=Ap{$ zFzxXHy)bu|v>y#!SwGSF*MC!nmt#Bo2Jru0P4ugPmn(&Fn!mLi#b?#0Nt@V9NXYN6 z)T*K}Gc$AK%O)Tu9{AToLSh;{aMEpcO!V=7L-k5J5!LU}94N(;2alk8c4=3os__JT zuSte69+TPqQmx%cb_%^UgMYT&e^g~_U84ho`W*=KseNIZo`_A(%tX#oijyHbw-~c& zD52zDgv*CXfjtu{MoybJaBS1JeTrlv4WTFw;BM;wL!YRc&hJtxdCZH?-L#0cEn}0GHxU*sZWGp7* zLH{cm3Uq2(JjgO4#Oz)r!oSIfTNV*Kve%bxCQCLdJZ2^@kGx0A=b_H3Gn$x@fdsG< zpkf0ABqWk=KiY7C{h3UKrgZD$7oS5t7ncTJW&s>hQc`w4mEjW0QVjspc9Nh~w+asr zkCKrQX>0R_kj>$g{iwn0r(Y2Y#?%iQ}r6yW57qPo*+N#g0Jw@h~dkrIe z(`nM!oht6ah4Bs65)*(Zb%=xVRcq?DPb@A;cgCne-rAZ@8l+LRC@U)~>gC1%XxBw=I7VLW zz+Hb`tzC6IfDnSGN>u=hcXhS`&xUmLb~utMTb_V7i$X8jrpgC`Cn6?J6nd}$qL(|} zVmzJpudwu6{Df@X5r&6{^XI=8Xfs`H@NVUs4W63OgD#d5mQ>S)eLrU_(|0?! zY$pt98yVG3PfewcFcQ@NsH`*<{!xKaXO*e*Yj#!?Ml{!n=k9LD84y)a;6wuqYw@)V z8g-nzkpYe5A;$gVi<6|5=H_-jv*DlyWxQ+L-!%qnEpDgUufz_WedvAPEG?;7SYjU$ zo?DeI6x5SE1kE0Wk(VKmB-tR&4Pr&4~ZOItPZj>%B}xx_g_3 z72D&pGs*5g|Lts1St$RAekv!^CGnNL5-@$dFrt59*IhPDk*;U5GxC769O4TIo+>;4=cJA1895;)t%%4tH4-F~S*Ywxc9 z+R|^!Q&e7#Xwg9UwZ(oY>e~`Hp;m-rtm^eT_HhVwc^Z;pzt!#QePL8^#I|GEZ{1W_ zR)zq9B+>Y6(i$#ZnF6J23;MOwqoV;E_A(ISd*&{%_ly(K;(2Wf2_1j`{;m5na{m36 zlItEc#f>5uj-eyl(J>QDB8eAFTBeehWau&ljp4tpj_M2t$CgddfH8lPQSleIi)`vV zN40W+qImVCLx;mn?{oLl8?1N7%wE-{it(RDSKGIZDuCc8la}?%)qtGh+-YKNhJ5@= z`1eA;z_VKYCX#=I$i4*z(td(l=8=NkVD|HAMBUv#5E0LK;~~SDe`3T%hWpoPXlO`Z zk7e#2uJM01><5j&Sn&eGm;nGqtWLoI44BPa9NJ~H~Z zYg1Dyh=^92$$>Mk*GtTs+c_@d@0T!Y;y|kqwSlYfj*-BO*JU1mc+<%d0vAy%kNu+D zsvEwAkVLii-a>`O@ySWx#DOVj5`v_{rz~zWNLf;pDG-~W7qIzTt!U(Y#rRcW!!64 zRf68?b!fT}nY>D12!))T*>?W@r2xsVgh2SRe-4^y*M<)7mZjI<*9W&Pz8j}kCEos< zgERnohJIh%ktU>ZJ~Q)lk(EBS^w66-gV3a5h28(nheX6LTWfMC8o$AAWPM#<8>#I( zoVqYHC7k|GPpO{Z+a@e2T!sH7RG77YTq$Pp#CE+eUq_`lN{gE(!SVtzaj~Ph&!^Yj zehRfz0`x*y0K_L>^}=9m4`7H$dV7C{WSThG*#jyonXI^oAnVcAw%&ea7G~`_ezy7| z-Z{>q$wTS6&Q+vNW;~YkZT7p{s)_ov_9SJOp9KVbYvBC$ySJ+rAj;tz zF{~xY;h37lKrWJ_dd|E{xF|m(>juHLwCx))5v2UiuzJ-WAPwT=a&mH(A)JYmZXTBG70_g3T4H`#judi zg|w=-zgSF5EC{`m^y{kXR69?BFmZ4=T--|yi)_6KG305QpvcHbVPpdIpbA(ctRdCL zi0_@d~SbI z+Vk^!=~C4&_1Lig1*(#@cZTIdYRH`>On!y7qvK?8d8Y4h1L`QLg^Ix;TYd)9VWGtV zz%rB-)yYe%dk3j}Ns?Pj$gjgaYLcc*#E2CSek2pDGIn*&xz{MGxzwY2qqPdM82^yIC5KKfcv*wm!e*rK57 zJwk^9yxjPEoIa_*XmO(cQ)A+fIoJi@kd7smhb)y*I0aF+Bz4WEM?2A8qa6qe)n`pp zAy!2dUlV~;2GVS`^jq)C25k%=sL#2>0HtLuHZc)PmIj+IXIdC|f?RX)@($Ryn7P^_ zYW!-1s@ifqUxG+br$!GwC$8d-D2s@l&%8d-txuh>gq3~T_$D(mh*~BNu_gt@B2S^) z*sP=0-eNT%zWcq~UZUL~DLtLw#z(xyHvj$08!}WhLuR$fIOmjLU04KunyeW;)4knwTpixp$*QE$(9!+e z8(as6fZPbrB>$TNrGrCyz*7!=O>8g$o!J>y-))TL7@E0ZPP!QO0&*`5-Rc~LAHQ;9 zUY(t?E&j$-t7EHc9{AY0Y1Vn)1Gdha0iVp5r5JzUW0b0uF@9$Sn&-lb7&TIL| za~h=@AiqF;>FF-}Jdk6sSjfhFyFsozDGa0(`xQv64v0RiF`TqTHeuQNi`PE~T-_sN zBMzQDuRg4htGeq1J$#(}ykaODrBf>g4t%xs$jf6}kNTngq4e`6bFj4Q;CcHfeac1`N7%<-RfIli#dN6&sz|i%;;D3K?KjZ`QHONgb zz7nmx`QSvR`x-q9Kf`Pic%U(jrKoi06|K`|t2h?2xo;pW?4AuK21uFE*d?Cc=yBP`{ImNJOI^4*BhV!cvAZbs=tsOYBn201$>85qpA{HgP8waTPI zn(HhcAuZWA&+sZ^6KB>sg}_mcgU>j01g=$Is4hOP-@2jyys*H{+tK0})|LqGCx*or z5~zD`C|0Jeztni;4B7az1FV1VUMtU~CmLFODr`Kfa5>f8u3G!1&+b~kLiiAB^bmAB zaefcN^ga9jv({Sdf1=N`A=c!;o85EK$CWqLrx9Ui;i`l$2M?n&{sr$6C*&grZU!}G zJ2*(>(Bu2KKk_DMCjP_kvuH4xG?5sYWGc>P3{X@yS-GJtf9X^&?2Nk^-+i%+Z~Vy9 zpy7HGXZiJU9N%{1_|m0&0qV-8CGqw6!igxG*uDqvLo}Sk0F18ZA&O6011cn{ptWp8 zfrMWdz+cC)>A7IqY3@*X9C-7T@f+ES0m&1@m47$sebdoAJs4K2-=N-8Y()t&YOQ3j zcvlZv-fFT*Q$cI8TWc0BaJ=BYa4TRhcm(1Df{~CC+N?$okO=BrDLK^8(xSuGqz0^; z#Hy4+*QCtYDFd}cZhyJo53#y`8ekro1Lm8iD4EHja>h}0lkdp@LA&lban0FV(tLT@ zkct{8Mn=Y~)(oeaw>LlYZ->2m@TFVMYOlFdZ|Wl|#2c5Qs6KF*)N6TLuYf|-2UG0= ztR^?QJkW4aR9Y}}_iGvN-^=dzYXaVvw`ZO|Pyyu1H9{}jzlAS+#ZA^9|CLRTgkd%P zl;v(L4tK70U`EHsgA|p;Sj*CToN&>}g#!pV(X7CoQqC9dVV)DciZIqvEXup$GfbP* zNHSo=@?4bta#+XH0Z7_j!{HRzzHkn9ncIbXJZ2;Bwc-l;atb7HW01cdzmvKJj#|+3s`=@&Iy)-FI4Y^L}4oyds@Rh$1_;8uTtSDmHRu zL6RPDQ;``2b$WAib8(ZCA6pF+1%1v^tav>;i-izn6MeZRu$8moF{Il9ETg*@c3#%_ zo-5!W>buNm?FezPp6k6aCvfEzJbhLldwB_$3VS**=rn|4hM_&z1N2LKbDmMB<8doxz4Q3L(B1sS{RH9_Bro_Ww|X}H*@%a=hO8z>yEQ59g~*JWsH3Dcr(8^wb_cSB z@`SS8qF%OWSmJy2q{*QV55I|5XhN%sS7n8^w|~yI{J?2)(fLLA`t#?{#;@zNPFpoM z)2-VGg3m=)t3EynY>t*}^Y9#h(9#EkLqV2?*jm?+`sPvK+Fz9Qt;+k_W+5Un}%Wu zD*whxYWX1VBf~DE?OJnnJOintT2rf6vOL@HQ8?1;Vbyoq5tQ9Eq(r9t#ZMrBjNgYK zlDA_8tY11qOgr%(r<;d^3a_)v&D`=bEJWaZrE?~C?nAvg0Rj&k<1NO*w@D43TmT@? z=LShQ#>Rpdt?j+M-)n@_12DF@V@>bD)Y8&wb;;}F7ZP!XqcY!P%2L&uq^D0SmQv6; z#yA(jRp<8MXQ1fWkk*XO;`Lh|z_34GgDA*-x6*@c48FR8-)D#!S`sT++1d2}J(;IW zhWRwz&Hjm>oqaZVcv#w9s7?`mbl-{nz1&TIT&DN6P?0WKuf&3+gkB{F!m3BcH0bNB z=BQ-9rOf(jvwcYGZ9$GR^9juOqCuHFHhZmw@SN##`)S9$V{UU3eQ>T< z+H(Wz-hF8Hmv-%cry;FUv^%4qHvo;T}toK~)JBtvmjh-2^2wA{g5R5!B{ z6dSD%V!9vug!A-b&CK|x{I&`t#&)jz)#Y-Ouos_y_Uz~A(`CS+-{H#d?BY@n;=R53 zCzuNz+e^Hw*3-peUzyclfZddM0K%qKHSC~y-@>c97qGb38&3=yA=Sph-MVt$w>0@x zd*-Dd3ob|_^wern(AZ?wYbuj(CBwz>ArOV%C;a&g@p}12UEWPwWB~~1B%-;L9q|JF zJF==VHvW99@Zlc`2$lL?F>L!UHahAXmOMUs+>;cY7xnn$Z#I6%5r8e5KS@YQ3EAlI zFuNz&YTlgQ%=)OF5g9AOXCBDai>qJn?C9&IuSK(6MH5MW;nGHgH){#v`RV_)Qn=xA z&Eek6u@||T2QL^#+vj=8ulNt=v*HrZ=@D60@W}yQo!CXH@S9(1SzW zE7-bHDci{~zjXD%Kc9ctYX1OtdGj@IX%6=!_!alTq|M79oUI|{6U zLq6(PffPe7jB+W7?c-r!V1yh&B%_?>&r{Y=0c~{2%0krm(QAL)EMugbpE?qx$$@|3 zgmBAHic}7!#QSG97RE(H#1{9PZk;M$AO;kv8P~L1 z)9JwtpKD{DjGi9Ruyh9ZZ>1rYpe}>7viY7~k*B4AXhRd|c!&=~83Lax!wL=>5?UwgqJH+;xf#8~E#{j!7N zF5g0m4J;ug7fHOHw8Ny6;>GS$($}=geg9B|41NL!#KnLcd?E8oUDR; z=$buFj>@sJJY3jh5JrFxWpU2$WzAZ;?mjq_4|R5JO-t1yWqE$^h3+kz-YDK|XGek_ z)6m(1Ug|6@p^naHp#Ow@&OZqrp!Nq^>G^TqFzO}?_=6 zGFaEHg$ZD5v>uBQ+yaNFXT1CLcN^|Vxtb!29=@WXVbmQ7b8?t(>lXz<95CjX&^kI2 zA#ZmQs4t-B=9@x z5HUV&*Ph#CB`@^~{Sa^s<5zGayNk1%kU_ZW)9?5&LM>!g5%c{Bi7|h|4pK)TwIn_b zZhkFn6WIxE&d&vreavCD^B;_CoY^ABoFC~YUV8=U2T7tse0Tag<7AG?BXva-J;d9sL)a3l-0ywY-H_sA}pDFAx6jylzbw7z7RPAMw`6 z*)9Cl&l_N4D8t4Aj>{!cIrNbS5eWv0sib=e=GcEmax2~?k2jJUmkm1$c3&urm=#_G z8rfHm>h>qux ziTVu6?1=8-t)VmsOPCM5Em!;7Ve$Bo`z{FmwR@;^ToAp1AL$DSf7@H#7s<0qGeQhm zf5D#;iRYnggWW4tn5M2jDhm*IR*s1fn{?z;CqXndO4jY5)^l$=2~{s~+LOE;La6k5 ztfZCdOUt7hQ)opz4nrztPoz-SLZm03o4!4Orc}R8WyeHf(}6vQi>^Q=_xZN{ z!LKt4*BV5nO7`}pSBtD54yomPZ@h>hEGRy)^Qew=t~iH!0UlF5&#FSLj>o9(^!+16 zF%C8P19gnM48sBu14#r^{<6X$l`0h2F32iDd@@%RWWGKu9-=y8ONCj!L|pFS)Anox zhs2O_;)vqhhA*gpWq`K6^%Bp#Dv9gthLosMZibE4lAy}pw*O(`D+A(j#38=VPdU8G z2VW{oJMMTc4AXu3nIM(^>%+8}Kv8l7K5m-8a_Y|s!7iVMiGtnO>3%*@V^;ksI=|lu ze(xL_9hsuf3)zz@7dmwWZ57&y)`txGguGBv3J-sUySn`#-)SXLuEagZib0ulJHd8; zR+P#{iMLOUeFr0nD2vQ^UkHXnaV3il$DHo(3sr3Z5GN{NzVNw{=bD1ME3g=7$V*x5_^xl&l zJ;X-f3>*-M>Owv&`BxEa_$KO9h*rdo2X+^8>1#i5kQ%xYySV+T}%LnB9kd>3}osWDYK&DuO+Mc??wWtGN;kK2$gCSP`vg zvZZGzq;r732E|*U%SD?i0WaHLM%L&|IA-BD<^{nkyU0)@B*YFh0$V3}E<}cA8j58q zM|&SuBx-|VQd_-FT~DsxyEl~P(Z+?pi($nnlEO7EB5)H##F7YNq<4b5Tq%e4-VRkF z4syebP{qL%2dIsF^sDkT|G~GMt#`t8un~c=)D8XGB$`B6d1DCXsb6nG4cpUyCG@Os z!M=c~XbUjGk2L!u2mY{gm2cp0G)EAqdG&=Dn`KSh3u)nFRO5-u3K$AT%j4zL2X21^ zOQYYBvhnKc#~D*tH2jzNL>1c`kGnsMTtQ@u=zXgt}_C2v;mQ9fd<9W#teGzFy#K|pMl>+M!2^cso28h@w!Gl=O?L%j` zmywNjOvN;&Y?2JhjTDl{qR6-`DzJgNSt6L?Q!A&FG{X0W2 zt<6!|B8E}k8Zf=G+-X{$$s2pb!@E0bs z+3UUhE!rU5_F#Yfj$mF&Al;zDc2{z!EFEI8*6A~lH#3R1#; zPl1{aR5$(am0-kum>6k8@rrTTx5ATXGz)5?1Ck}5-myP+O7J1l#Dm@^bAGI*q|A`C zRI@`Pmi-+OGL^HW3LW@SNVGsui3Xa#j9xrc?(@qEtN0?t*onL53z``!5WM&~>;J6~@2?`%5rS&lpBNkamw5dlFGc)^hPK%jOsKa? zROkDQUf8a~ja2Im4ShvvWKvM?KxLPj+EqbM6GJI23d!{63#q51b|FdFi{+V3`qPcs z%%mPu%RL{x8}7O%WO?{V>$dPh?0T89cU4|-QXQVO~Isq0=p@~zFF!*PH10jnhK+M4k5sq#r^3OhW! zPA55gCZ{Owb;?ZN(3Y?#zW?*K0ni(MJ*KAcU_H``UwS)0XU1sUNU`un{W#Y3(9m;9 zNF1Zrt|K5$XPgfw2aAihBAu`!ZtHyLX@{UrU+4!b()QrTb4LHLd076k-5(J2q@o$%{p^Usb#O7gS@1{Xv;5 zh>=3)4pExi5BDJkZFk_&Tn&2|SCs@}5HYrCK_TKAv*0Kat4LD@L@B7y6wC-l<6^{& zfR`b@WHIqx|6pn8f;vYm*@_VF?gDpdQ)C8oF=+U&2LAks2^|DJovaz6@_6<$ybG?i z?h#K=L-$5IOiFw`MWM!$WDabeR4;BEBKdrhNTUWmlOnB7A&5L!gB`ezF>}HQ`1&$` zl|c6YlY-=0Kp-MbBOCJjW~5#b(GM^7+$k^U3Y?bP>!BPYX_k#D(n45NX$n6FuH{QB ziiN*aT3e!j20eNJRK`dmc&cxTcY~C&qV?`6vxfo&FLC!DzKeTdN0GCNYl*Uos|}<> zM^T>@w2o1bx0EShM$NG4ukA2U_%pC3{GTu|h77E4ocfe>Y^h+cwWNa_@z@vKO%$Pq zu-$FpBSDa+AzeC(`jxP$lpkP zK4cCPm2Wz@6M+K8$-EL?S;BtGkfHKJ_Oh3m@Hs<%#Nkqknk_ITN`XnZZ#em4F^<_= zNw!f0=vIGy*;05i1l369U0Z*VJaPM-gRM`9KOm()2OC2N`KgFliMR97boX?-1{TYN z59Cs~fXi%xgKf8xKL7tycY>6b0`p9zG=?(2p5A?JenGXfJ6yg|97IF}Mkg{N|7(W- zF9fHHTjpyPA;c2t2ZvL`meDKHv!^2E6^ukr6lT=xH|kCQcahvAw6+-cjzJ`4`B;}D T_XF}vD?m<4S+YjlIOzWYmEURT literal 13153 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