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 %

<img width="85" height="123" alt="image"
src="https://github.com/user-attachments/assets/ad660ba4-296c-4699-9ec4-03a6e7b97880"
/>
<img width="95" height="101" alt="image"
src="https://github.com/user-attachments/assets/12d4d9d3-cf2c-4c52-81aa-17ce538b9d48"
/>


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

🆑
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).
/🆑
This commit is contained in:
John Willard
2026-04-13 19:05:57 -04:00
committed by GitHub
parent 928df286a6
commit 774c07cd9b
30 changed files with 232 additions and 170 deletions
-5
View File
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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()
@@ -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.
@@ -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.
@@ -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
@@ -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
@@ -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))
@@ -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)