From 16aef3a2fd640e6cd97f5704aaaf5959d75921c3 Mon Sep 17 00:00:00 2001
From: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Date: Thu, 2 Apr 2026 21:20:55 +0200
Subject: [PATCH] Completely refactors HUD element management and datumizes
inventory HUDs (#95119)
## About The Pull Request
This is a port/revival of Kapu's
https://github.com/DaedalusDock/daedalusdock/pull/883
By god, please TM this for a while, as HUDs are rather volatile and I
might've missed something (also the original PR had harddel issues, so
we should probably be on the lookout for those)
Instead of being stored in a metric ton of separate variables, all HUD
elements are now kept in a ``key -> element`` assoc list, and separate
category lists have been turned into a single ``group_key -> list of
elements`` assoc list for easier management.
This massively simplifies HUD creation and management, and allows us to
sanely dynamically modify HUDs without having to keep track of our
elements ourselves (harddel fuel)
I've also noticed that plasma vessels had... interesting, to say the
least, way of managing their HUD and in humans were unable to display
it, which I've changed (the element itself is displayed below stamina in
non-aliens, as latter occupies the spot where you'd normally see it)
Also fixes a bunch of minor unlikely to occur issues with HUD not
updating when it should've sometimes.
## Why It's Good For The Game
The two most important results of this is that A) we can fix the issue
with items larger than 32x32 not displaying properly in inventories (in
a separate PR) and B) this paves the way for datumized inventory slots,
although that is a separate nightmare
Some of this code is also actually over a decade old, and is an absolute
nightmare to work with.
## Changelog
:cl:
qol: Non-aliens with an implanted plasma vessel now see their plasma
level in their HUD instead of just the stat panel
refactor: Refactored the entirety of HUD management code, report if
anything breaks!
/:cl:
---------
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
---
code/__DEFINES/hud.dm | 165 +++-
code/__DEFINES/sight.dm | 1 +
code/_onclick/hud/action_group.dm | 209 +++++
code/_onclick/hud/ai.dm | 365 +--------
code/_onclick/hud/alien.dm | 142 +---
code/_onclick/hud/alien_larva.dm | 64 +-
code/_onclick/hud/blob_overmind.dm | 208 +----
code/_onclick/hud/drones.dm | 45 +-
code/_onclick/hud/generic_dextrous.dm | 84 +-
code/_onclick/hud/ghost.dm | 208 +----
code/_onclick/hud/guardian.dm | 193 +----
code/_onclick/hud/hud.dm | 602 +++++---------
code/_onclick/hud/human.dm | 473 ++++-------
code/_onclick/hud/inventory_slot.dm | 35 +
code/_onclick/hud/living.dm | 46 +-
code/_onclick/hud/new_player.dm | 739 +-----------------
code/_onclick/hud/ooze.dm | 12 +-
code/_onclick/hud/pai.dm | 33 +
code/_onclick/hud/parallax/parallax.dm | 2 -
code/_onclick/hud/perks.dm | 35 -
.../rendering/plane_masters/_plane_master.dm | 3 -
code/_onclick/hud/revenanthud.dm | 15 +-
code/_onclick/hud/robot.dm | 266 +------
.../hud/{ => screen_objects}/action_button.dm | 35 +-
code/_onclick/hud/screen_objects/ai.dm | 250 ++++++
.../hud/{ => screen_objects}/alert.dm | 0
code/_onclick/hud/screen_objects/alien.dm | 24 +
code/_onclick/hud/screen_objects/blob.dm | 164 ++++
.../hud/{ => screen_objects}/credits.dm | 0
.../hud/{ => screen_objects}/fullscreen.dm | 0
code/_onclick/hud/screen_objects/ghost.dm | 149 ++++
code/_onclick/hud/screen_objects/guardian.dm | 68 ++
code/_onclick/hud/screen_objects/human.dm | 77 ++
.../hud/{ => screen_objects}/map_popups.dm | 0
.../hud/{ => screen_objects}/map_view.dm | 0
.../movable_screen_objects.dm | 0
.../_onclick/hud/screen_objects/new_player.dm | 724 +++++++++++++++++
.../hud/screen_objects/pai.dm} | 124 +--
code/_onclick/hud/screen_objects/perks.dm | 29 +
.../picture_in_picture.dm | 0
.../hud/{ => screen_objects}/radial.dm | 0
.../{ => screen_objects}/radial_persistent.dm | 0
code/_onclick/hud/screen_objects/robot.dm | 113 +++
.../screen_object_holder.dm | 0
.../{ => screen_objects}/screen_objects.dm | 64 +-
.../hud/{ => screen_objects}/screentip.dm | 0
.../voidwalker.dm} | 29 +-
code/_onclick/hud/soulscythe.dm | 5 +-
code/_onclick/hud/voidwalker.dm | 14 +
code/datums/actions/action.dm | 2 +-
code/datums/components/crafting/crafting.dm | 31 +-
code/datums/components/food/edible.dm | 4 +-
code/datums/components/fov_handler.dm | 5 +-
code/datums/components/life_link.dm | 5 +-
code/datums/components/style/style.dm | 40 +-
.../datums/elements/attack_zone_randomiser.dm | 4 +-
code/datums/keybinding/living.dm | 8 +-
code/datums/martial/_martial.dm | 26 +-
code/datums/mood.dm | 22 +-
.../status_effects/debuffs/screwy_hud.dm | 2 +-
code/datums/storage/storage.dm | 16 +-
code/datums/wounds/bones.dm | 2 +-
code/datums/wounds/cranial_fissure.dm | 2 +-
code/datums/wounds/slash.dm | 2 +-
.../machinery/computer/operating_computer.dm | 4 +-
.../machinery/computer/records/records.dm | 2 +-
code/game/objects/items/devices/multitool.dm | 22 +-
code/modules/antagonists/blob/overmind.dm | 22 +-
.../blood_worm/blood_worm_host_handling.dm | 16 +-
.../antagonists/blood_worm/blood_worm_mob.dm | 3 -
.../antagonists/changeling/changeling.dm | 51 +-
.../changeling/powers/tiny_prick.dm | 17 +-
.../antagonists/cult/datums/cult_team.dm | 3 +-
.../heretic/heretic_living_heart.dm | 25 +-
.../heretic/items/heretic_armor.dm | 36 +-
.../equipment/spellbook_entries/perks.dm | 36 +-
code/modules/antagonists/wizard/wizard.dm | 2 -
code/modules/client/preferences.dm | 4 +-
.../clothing/spacesuits/_spacesuits.dm | 24 +-
code/modules/engineering/tools/multitool.dm | 11 -
code/modules/escape_menu/escape_menu.dm | 3 -
code/modules/mob/dead/observer/observer.dm | 5 +-
code/modules/mob/inventory.dm | 10 +-
.../living/basic/blob_minions/blobbernaut.dm | 11 +-
.../basic/space_fauna/revenant/_revenant.dm | 2 +-
.../living/carbon/alien/adult/alien_powers.dm | 7 +-
.../living/carbon/alien/adult/caste/hunter.dm | 4 +-
.../modules/mob/living/carbon/alien/organs.dm | 53 +-
.../modules/mob/living/carbon/alien/screen.dm | 53 +-
code/modules/mob/living/carbon/carbon.dm | 31 +-
.../mob/living/carbon/carbon_update_icons.dm | 10 +-
.../mob/living/carbon/human/_species.dm | 6 +-
code/modules/mob/living/carbon/human/human.dm | 2 +-
.../carbon/human/species_types/vampire.dm | 14 +-
code/modules/mob/living/living.dm | 54 +-
code/modules/mob/living/living_defense.dm | 3 +-
code/modules/mob/living/living_fov.dm | 2 +
.../mob/living/living_item_handling.dm | 4 +-
code/modules/mob/living/silicon/death.dm | 19 +-
.../mob/living/silicon/robot/inventory.dm | 55 +-
code/modules/mob/living/silicon/robot/life.dm | 37 +-
.../modules/mob/living/silicon/robot/robot.dm | 19 +-
.../mob/living/silicon/robot/robot_defines.dm | 15 -
.../mob/living/simple_animal/hostile/ooze.dm | 10 +-
code/modules/mob/mob.dm | 12 +-
code/modules/mob/mob_movement.dm | 28 +-
code/modules/mod/mod_control.dm | 8 +-
.../reagents/reagent_containers/cups/_cup.dm | 4 +-
.../research/xenobiology/xenobio_camera.dm | 69 +-
.../surgery/bodyparts/dismemberment.dm | 2 +-
code/modules/surgery/bodyparts/parts.dm | 6 +-
.../organs/internal/stomach/_stomach.dm | 8 +-
code/modules/unit_tests/unequip_defib.dm | 2 +-
code/modules/unit_tests/unit_test.dm | 5 +-
icons/hud/screen_changeling.dmi | Bin 6755 -> 7108 bytes
icons/mob/actions/actions_changeling.dmi | Bin 24497 -> 11025 bytes
tgstation.dme | 45 +-
117 files changed, 3218 insertions(+), 3663 deletions(-)
create mode 100644 code/_onclick/hud/action_group.dm
create mode 100644 code/_onclick/hud/inventory_slot.dm
create mode 100644 code/_onclick/hud/pai.dm
delete mode 100644 code/_onclick/hud/perks.dm
rename code/_onclick/hud/{ => screen_objects}/action_button.dm (97%)
create mode 100644 code/_onclick/hud/screen_objects/ai.dm
rename code/_onclick/hud/{ => screen_objects}/alert.dm (100%)
create mode 100644 code/_onclick/hud/screen_objects/alien.dm
create mode 100644 code/_onclick/hud/screen_objects/blob.dm
rename code/_onclick/hud/{ => screen_objects}/credits.dm (100%)
rename code/_onclick/hud/{ => screen_objects}/fullscreen.dm (100%)
create mode 100644 code/_onclick/hud/screen_objects/ghost.dm
create mode 100644 code/_onclick/hud/screen_objects/guardian.dm
create mode 100644 code/_onclick/hud/screen_objects/human.dm
rename code/_onclick/hud/{ => screen_objects}/map_popups.dm (100%)
rename code/_onclick/hud/{ => screen_objects}/map_view.dm (100%)
rename code/_onclick/hud/{ => screen_objects}/movable_screen_objects.dm (100%)
create mode 100644 code/_onclick/hud/screen_objects/new_player.dm
rename code/{modules/pai/hud.dm => _onclick/hud/screen_objects/pai.dm} (57%)
create mode 100644 code/_onclick/hud/screen_objects/perks.dm
rename code/_onclick/hud/{ => screen_objects}/picture_in_picture.dm (100%)
rename code/_onclick/hud/{ => screen_objects}/radial.dm (100%)
rename code/_onclick/hud/{ => screen_objects}/radial_persistent.dm (100%)
create mode 100644 code/_onclick/hud/screen_objects/robot.dm
rename code/_onclick/hud/{ => screen_objects}/screen_object_holder.dm (100%)
rename code/_onclick/hud/{ => screen_objects}/screen_objects.dm (96%)
rename code/_onclick/hud/{ => screen_objects}/screentip.dm (100%)
rename code/_onclick/hud/{voidwalkerhud.dm => screen_objects/voidwalker.dm} (73%)
create mode 100644 code/_onclick/hud/voidwalker.dm
delete mode 100644 code/modules/engineering/tools/multitool.dm
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index afea0d10c93..75e42833694 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -22,6 +22,155 @@
/// Largest viewport possible, required for checking if someone can see an object
#define LARGEST_VIEWPORT_SIZE WIDESCREEN_VIEWPORT_SIZE
+// Hud group keys
+/// Static elements that are always present in standard hud
+#define HUD_GROUP_STATIC 1
+/// Inventory elements toggled by the inventory switch
+#define HUD_GROUP_TOGGLEABLE_INVENTORY 2
+/// Info display HUD elements
+#define HUD_GROUP_INFO 3
+/// Permanently on-screen elements, regardless of your HUD status
+#define HUD_GROUP_SCREEN_OVERLAYS 4
+/// Hotkey buttons
+#define HUD_GROUP_HOTKEYS 5
+/// Open storages and items in them
+#define HUD_GROUP_STORAGE 6
+/// Total amount of screen groups in use
+#define SCREEN_GROUP_AMT 6
+
+// Hud keys for accessing hud objects
+#define HUD_MOB_TOGGLE_PALETTE "mob_toggle_palette"
+#define HUD_MOB_PALETTE_UP "mob_palette_up"
+#define HUD_MOB_PALETTE_DOWN "mob_palette_down"
+#define HUD_MOB_SCREENTIP "mob_screentips"
+#define HUD_MOB_HEALTH "mob_health"
+#define HUD_MOB_LANGUAGE_MENU "mob_language"
+#define HUD_MOB_NAVIGATE_MENU "mob_navigate"
+#define HUD_MOB_INTENTS "mob_intents"
+#define HUD_MOB_MOVE_INTENT "mob_move_intent"
+#define HUD_MOB_ZONE_SELECTOR "mob_zonesel"
+#define HUD_MOB_PULL "mob_pull"
+#define HUD_MOB_DROP "mob_drop"
+#define HUD_MOB_THROW "mob_throw"
+#define HUD_MOB_HUNGER "mob_hunger"
+#define HUD_MOB_STAMINA "mob_stamina"
+#define HUD_MOB_SPACESUIT "mob_spacesuit"
+#define HUD_MOB_COMBO "mob_combo"
+#define HUD_MOB_RESIST "mob_resist"
+#define HUD_MOB_REST "mob_rest"
+#define HUD_MOB_SLEEP "mob_sleep"
+#define HUD_MOB_AREA_CREATOR "mob_area_creator"
+#define HUD_MOB_SWAPHAND_1 "mob_swaphand1"
+#define HUD_MOB_SWAPHAND_2 "mob_swaphand2"
+#define HUD_MOB_CRAFTING_MENU "mob_crafting"
+#define HUD_MOB_FLOOR_CHANGER "mob_floor_changer"
+#define HUD_MOB_BLOOD_LEVEL "mob_blood_level"
+#define HUD_MOB_FOV_BLOCKER "mob_fov_blocker"
+#define HUD_MOB_STYLE_METER "mob_style_meter"
+#define HUD_MOB_MOOD "mob_mood"
+#define HUD_MOB_HEALTHDOLL "mob_healthdoll"
+#define HUD_MOB_MEMORIES "mob_memories"
+
+#define HUD_OOZE_NUTRITION_DISPLAY "ooze_nutrition_display"
+
+#define HUD_HUMAN_TOGGLE_INVENTORY "human_toggle_inventory"
+
+#define HUD_NEW_PLAYER_START_NOW "new_player_start_now"
+#define HUD_NEW_PLAYER_SIGN_UP "newp_layer_sign_up"
+#define HUD_KEY_NEW_PLAYER(slot) "newplayer_hud:[slot]"
+
+#define HUD_SILICON_TAKE_IMAGE "silicon_camera"
+#define HUD_SILICON_TABLET "silicon_tablet"
+#define HUD_SILICON_ALERTS "silicon_alerts"
+
+#define HUD_CYBORG_LAMP "cyborg_lamp"
+#define HUD_CYBORG_HANDS "cyborg_module"
+#define HUD_CYBORG_RADIO "cyborg_radio"
+#define HUD_CYBORG_DEATH "cyborg_death"
+#define HUD_KEY_CYBORG_MODULE(slot) "cyborg_module:[slot]"
+
+#define HUD_AI_FLOOR_INDICATOR "ai_floor_indicator"
+#define HUD_AI_GO_UP "ai_go_up"
+#define HUD_AI_GO_DOWN "ai_go_down"
+#define HUD_AI_AICORE "ai_core"
+#define HUD_AI_CAMERA_LIST "ai_camera_list"
+#define HUD_AI_CAMERA_TRACK "ai_camera_track"
+#define HUD_AI_CAMERA_LIGHT "ai_camera_light"
+#define HUD_AI_CREW_MONITOR "ai_crew_monitor"
+#define HUD_AI_CREW_MANIFEST "ai_crew_manifest"
+#define HUD_AI_ANNOUNCEMENT "ai_announce"
+#define HUD_AI_CALL_SHUTTLE "ai_shuttle"
+#define HUD_AI_STATE_LAWS "ai_state_laws"
+#define HUD_AI_TAKE_IMAGE "ai_take_image"
+#define HUD_AI_IMAGE_VIEW "ai_view_image"
+#define HUD_AI_SENSORS "ai_sensors"
+#define HUD_AI_MULTICAM "ai_view_multicam"
+#define HUD_AI_ADD_MULTICAM "ai_add_multicam"
+
+#define HUD_PAI_SOFTWARE "paisoftware"
+#define HUD_PAI_SHELL "paishell"
+#define HUD_PAI_CHASSIS "paichassis"
+#define HUD_PAI_NEWSCASTER "painewscaster"
+#define HUD_PAI_HOST_MONITOR "paimonitor"
+#define HUD_PAI_GPS "paigps"
+
+#define HUD_GHOST_SPAWNERS "ghost_spawners"
+#define HUD_GHOST_ORBIT "ghost_orbit"
+#define HUD_GHOST_REENTER_CORPSE "ghost_corpse"
+#define HUD_GHOST_TELEPORT "ghost_teleport"
+#define HUD_GHOST_PAI "ghost_pai"
+#define HUD_GHOST_MINIGAMES "ghost_minigames"
+#define HUD_GHOST_DNR "ghost_dnr"
+#define HUD_GHOST_SETTINGS "ghost_settings"
+#define HUD_KEY_GHOST_HUDBOX(slot) "ghost_hudbox:[slot]"
+
+#define HUD_ALIEN_QUEEN_FINDER "alien_queen_finder"
+#define HUD_ALIEN_PLASMA_DISPLAY "alien_plasma_display"
+#define HUD_ALIEN_HUNTER_LEAP "alien_hunter_leap"
+
+#define HUD_BLOB_POWER_DISPLAY "blob_power_display"
+#define HUD_BLOB_JUMP_TO_CORE "blob_jump_to_core"
+#define HUD_BLOB_JUMP_TO_NODE "blob_jump_to_node"
+#define HUD_BLOB_BLOBBERNAUT "blob_blobbernaut"
+#define HUD_BLOB_RESOURCES "blob_resources"
+#define HUD_BLOB_NODE "blob_node"
+#define HUD_BLOB_FACTORY "blob_factory"
+#define HUD_BLOB_READAPT "blob_readapt"
+#define HUD_BLOB_RELOCATE "blob_relocate"
+
+#define HUD_BLOBBERNAUT_OVERMIND "blobbernaut_overmind"
+
+#define HUD_GUARDIAN_MANIFEST "guardian_manifest"
+#define HUD_GUARDIAN_RECALL "guardian_recall"
+#define HUD_GUARDIAN_LIGHT "guardian_light"
+#define HUD_GUARDIAN_COMMUNICATE "guardian_communicate"
+#define HUD_GUARDIAN_TOGGLE "guardian_toggle"
+
+#define HUD_VOIDWALKER_SPACE_CAMO "voidwalker_space_camo"
+#define HUD_VOIDWALKER_VOID_JUMP "voidwalker_void_jump"
+
+#define HUD_CHANGELING_CHEMS "changeling_chems"
+#define HUD_CHANGELING_STING "changeling_sting"
+
+#define HUD_CULTIST_ARROW "cultist_arrow"
+
+#define HUD_HERETIC_ARROW "heretic_arrow"
+#define HUD_HERETIC_MOON_HEALTH "heretic_moon_health"
+
+#define HUD_WIZARD_COMPACT_PERKS "wizard_compact_perks"
+#define HUD_WIZARD_PERK(slot) "wizard_perk:[slot]"
+
+/// Converts item slots to hud keys
+#define HUD_KEY_ITEM_SLOT(slot) "item_slot:[slot]"
+/// Converts item slots to hud keys as a compiler constant
+#define HUD_KEY_ITEM_SLOT_CONST(slot) "item_slot:" + #slot
+/// Hand2hudkey
+#define HUD_KEY_HAND_SLOT(slot) "hand_slot:[slot]"
+
+#define HUD_MULTITOOL_ARROW "multitool_arrow"
+
+#define HUD_XENOBIO_CONSOLE "xenobio_console"
+
/*
These defines specificy screen locations. For more information, see the byond documentation on the screen_loc var.
@@ -63,7 +212,7 @@
#define ui_combo "CENTER+4:24,SOUTH+1:7" //combo meter for martial arts
//Lower right, persistent menu
-#define ui_rest "EAST-1:28,SOUTH+1:7"
+#define ui_below_throw "EAST-1:28,SOUTH+1:7"
#define ui_drop_throw "EAST-1:28,SOUTH+1:24"
#define ui_above_throw "EAST-1:28,SOUTH+1:41"
#define ui_above_movement "EAST-2:26,SOUTH+1:7"
@@ -71,7 +220,6 @@
#define ui_above_intent "EAST-3:24, SOUTH+1:7"
#define ui_movi "EAST-2:26,SOUTH:5"
#define ui_acti "EAST-3:24,SOUTH:5"
-#define ui_combat_toggle "EAST-3:24,SOUTH:5"
#define ui_floor_change "BOTTOM+1:8,RIGHT-1:28"
#define ui_zonesel "EAST-1:28,SOUTH:5"
#define ui_acti_alt "EAST-1:28,SOUTH:5" //alternative intent switcher for when the interface is hidden (F12)
@@ -123,7 +271,7 @@
#define ui_human_navigate "EAST-3:7,SOUTH+1:7"
#define ui_human_language "EAST-3:7,SOUTH+1:24"
#define ui_human_area "EAST-3:24,SOUTH+1:24"
-#define ui_human_memories_area "EAST-4:22,SOUTH+1:7"
+#define ui_human_memories_menu "EAST-4:22,SOUTH+1:7"
//Drones
#define ui_drone_drop "CENTER+1:18,SOUTH:5"
@@ -158,6 +306,7 @@
#define ui_alien_navigate_menu "EAST-4:6,SOUTH:5"
#define ui_alien_memories_menu "EAST-4:22,SOUTH:1:20"
#define ui_alien_floor_change "EAST-3:24,SOUTH:20"
+#define ui_alienplasmadisplay_human "EAST,CENTER-5:8"
//AI
#define ui_ai_core "BOTTOM:6,RIGHT-4"
@@ -206,12 +355,12 @@
#define ui_ghost_spawners_menu "SOUTH:6,CENTER-3:24"
#define ui_ghost_orbit "SOUTH:6,CENTER-2:24"
#define ui_ghost_reenter_corpse "SOUTH:6,CENTER-1:24"
-#define ui_dnr "SOUTH:6,CENTER:24"
+#define ui_ghost_dnr "SOUTH:6,CENTER:24"
#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
-#define ui_ghost_settings "SOUTH: 6, CENTER+2:24"
-#define ui_ghost_minigames "SOUTH: 6, CENTER+3:24"
-#define ui_ghost_language_menu "SOUTH: 6, CENTER+4:22"
-#define ui_ghost_floor_changer "SOUTH: 6, CENTER+4:7"
+#define ui_ghost_settings "SOUTH:6,CENTER+2:24"
+#define ui_ghost_minigames "SOUTH:6,CENTER+3:24"
+#define ui_ghost_language_menu "SOUTH:6,CENTER+4:22"
+#define ui_ghost_floor_changer "SOUTH:6,CENTER+4:7"
//Voidwalker
#define ui_voidwalker_left_of_hands "CENTER+-2:16,SOUTH+0:5"
diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm
index 0a14db75ef9..ab88e6e2c30 100644
--- a/code/__DEFINES/sight.dm
+++ b/code/__DEFINES/sight.dm
@@ -76,4 +76,5 @@
#define INVISIBILITY_SOURCE_INVISIMIN "invisimin"
#define INVISIBILITY_SOURCE_STEALTHMODE "stealthmode"
+#define INVISIBILITY_SOURCE_SLEEP_HUD_BUTTON "sleep_hud_button"
diff --git a/code/_onclick/hud/action_group.dm b/code/_onclick/hud/action_group.dm
new file mode 100644
index 00000000000..a8766516b23
--- /dev/null
+++ b/code/_onclick/hud/action_group.dm
@@ -0,0 +1,209 @@
+/datum/action_group
+ /// The hud we're owned by
+ var/datum/hud/owner
+ /// The actions we're managing
+ var/list/atom/movable/screen/movable/action_button/actions
+ /// The initial vertical offset of our action buttons
+ var/north_offset = 0
+ /// The pixel vertical offset of our action buttons
+ var/pixel_north_offset = 0
+ /// Max amount of buttons we can have per row
+ /// Indexes at 1
+ var/column_max = 0
+ /// How far "ahead" of the first row we start. Lets us "scroll" our rows
+ /// Indexes at 1
+ var/row_offset = 0
+ /// How many rows of actions we can have at max before we just stop hiding
+ /// Indexes at 1
+ var/max_rows = INFINITY
+ /// The screen location we go by
+ var/location
+ /// Our landing screen object
+ var/atom/movable/screen/action_landing/landing
+
+/datum/action_group/New(datum/hud/owner)
+ ..()
+ actions = list()
+ src.owner = owner
+
+/datum/action_group/Destroy()
+ owner = null
+ QDEL_NULL(landing)
+ QDEL_LIST(actions)
+ return ..()
+
+/datum/action_group/proc/insert_action(atom/movable/screen/action, index)
+ if(action in actions)
+ if(actions[index] == action)
+ return
+ actions -= action // Don't dupe, come on
+ if(!index)
+ index = length(actions) + 1
+ index = min(length(actions) + 1, index)
+ actions.Insert(index, action)
+ refresh_actions()
+
+/datum/action_group/proc/remove_action(atom/movable/screen/action)
+ actions -= action
+ refresh_actions()
+
+/datum/action_group/proc/refresh_actions()
+
+ // We don't use size() here because landings are not canon
+ var/total_rows = ROUND_UP(length(actions) / column_max)
+ total_rows -= max_rows // Lets get the amount of rows we're off from our max
+ row_offset = clamp(row_offset, 0, total_rows) // You're not allowed to offset so far that we have a row of blank space
+
+ var/button_number = 0
+ for(var/atom/movable/screen/button as anything in actions)
+ var/postion = ButtonNumberToScreenCoords(button_number )
+ button.screen_loc = postion
+ button_number++
+
+ if(landing)
+ var/postion = ButtonNumberToScreenCoords(button_number, landing = TRUE) // Need a good way to count buttons off screen, but allow this to display in the right place if it's being placed with no concern for dropdown
+ landing.screen_loc = postion
+ button_number++
+
+/// Accepts a number represeting our position in the group, indexes at 0 to make the math nicer
+/datum/action_group/proc/ButtonNumberToScreenCoords(number, landing = FALSE)
+ var/row = round(number / column_max)
+ row -= row_offset // If you're less then 0, you don't get to render, this lets us "scroll" rows ya feel?
+ if(row < 0)
+ return null
+
+ // Could use >= here, but I think it's worth noting that the two start at different places, since row is based on number here
+ if(row > max_rows - 1)
+ if(!landing) // If you're not a landing, go away please. thx
+ return null
+ // We always want to render landings, even if their action button can't be displayed.
+ // So we set a row equal to the max amount of rows + 1. Willing to overrun that max slightly to properly display the landing spot
+ row = max_rows // Remembering that max_rows indexes at 1, and row indexes at 0
+
+ // We're going to need to set our column to match the first item in the last row, so let's set number properly now
+ number = row * column_max
+
+ var/visual_row = row + north_offset
+ var/coord_row = visual_row ? "-[visual_row]" : "+0"
+
+ var/visual_column = number % column_max
+ var/coord_col = "+[visual_column]"
+ var/coord_col_offset = 4 + 2 * (visual_column + 1)
+ return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:-[pixel_north_offset]"
+
+/datum/action_group/proc/check_against_view()
+ var/owner_view = owner?.mymob?.canon_client?.view
+ if(!owner_view)
+ return
+ // Unlikey as it is, we may have been changed. Want to start from our target position and fail down
+ column_max = initial(column_max)
+ // Convert our viewer's view var into a workable offset
+ var/list/view_size = view_to_pixels(owner_view)
+
+ // We're primarially concerned about width here, if someone makes us 1x2000 I wish them a swift and watery death
+ var/furthest_screen_loc = ButtonNumberToScreenCoords(column_max - 1)
+ var/list/offsets = screen_loc_to_offset(furthest_screen_loc, owner_view)
+ if(offsets[1] > ICON_SIZE_X && offsets[1] < view_size[1] && offsets[2] > ICON_SIZE_Y && offsets[2] < view_size[2]) // We're all good
+ return
+
+ for(column_max in column_max - 1 to 1 step -1) // Yes I could do this by unwrapping ButtonNumberToScreenCoords, but I don't feel like it
+ var/tested_screen_loc = ButtonNumberToScreenCoords(column_max)
+ offsets = screen_loc_to_offset(tested_screen_loc, owner_view)
+ // We've found a valid max length, pack it in
+ if(offsets[1] > ICON_SIZE_X && offsets[1] < view_size[1] && offsets[2] > ICON_SIZE_Y && offsets[2] < view_size[2])
+ break
+ // Use our newly resized column max
+ refresh_actions()
+
+/// Returns the amount of objects we're storing at the moment
+/datum/action_group/proc/size()
+ var/amount = length(actions)
+ if(landing)
+ amount += 1
+ return amount
+
+/datum/action_group/proc/index_of(atom/movable/screen/get_location)
+ return actions.Find(get_location)
+
+/// Generates a landing object that can be dropped on to join this group
+/datum/action_group/proc/generate_landing()
+ if(landing)
+ return
+ landing = new()
+ landing.set_owner(src)
+ refresh_actions()
+
+/// Clears any landing objects we may currently have
+/datum/action_group/proc/clear_landing()
+ QDEL_NULL(landing)
+
+/datum/action_group/proc/update_landing()
+ if(!landing)
+ return
+ landing.refresh_owner()
+
+/datum/action_group/proc/scroll(amount)
+ row_offset += amount
+ refresh_actions()
+
+/datum/action_group/palette
+ north_offset = 2
+ column_max = 3
+ max_rows = 3
+ location = SCRN_OBJ_IN_PALETTE
+
+/datum/action_group/palette/insert_action(atom/movable/screen/action, index)
+ . = ..()
+ var/atom/movable/screen/button_palette/palette = owner.screen_objects[HUD_MOB_TOGGLE_PALETTE]
+ palette.play_item_added()
+
+/datum/action_group/palette/remove_action(atom/movable/screen/action)
+ . = ..()
+ var/atom/movable/screen/button_palette/palette = owner.screen_objects[HUD_MOB_TOGGLE_PALETTE]
+ palette.play_item_removed()
+ if(!length(actions))
+ palette.set_expanded(FALSE)
+
+/datum/action_group/palette/refresh_actions()
+ var/atom/movable/screen/button_palette/palette = owner.screen_objects[HUD_MOB_TOGGLE_PALETTE]
+ var/atom/movable/screen/palette_scroll/scroll_down = owner.screen_objects[HUD_MOB_PALETTE_DOWN]
+ var/atom/movable/screen/palette_scroll/scroll_up = owner.screen_objects[HUD_MOB_PALETTE_UP]
+
+ var/actions_above = round((owner.listed_actions.size() - 1) / owner.listed_actions.column_max)
+ north_offset = initial(north_offset) + actions_above
+
+ palette.screen_loc = ui_action_palette_offset(actions_above)
+ var/action_count = length(owner?.mymob?.actions)
+ var/our_row_count = round((length(actions) - 1) / column_max)
+ if(!action_count)
+ palette.screen_loc = null
+
+ if(palette.expanded && action_count && our_row_count >= max_rows)
+ scroll_down.screen_loc = ui_palette_scroll_offset(actions_above)
+ scroll_up.screen_loc = ui_palette_scroll_offset(actions_above)
+ else
+ scroll_down.screen_loc = null
+ scroll_up.screen_loc = null
+
+ return ..()
+
+/datum/action_group/palette/ButtonNumberToScreenCoords(number, landing)
+ var/atom/movable/screen/button_palette/palette = owner.screen_objects[HUD_MOB_TOGGLE_PALETTE]
+ if(palette.expanded)
+ return ..()
+
+ if(!landing)
+ return null
+
+ // We only render the landing in this case, so we force it to be the second item displayed (Second rather then first since it looks nicer)
+ // Remember the number var indexes at 0
+ return ..(1 + (row_offset * column_max), landing)
+
+/datum/action_group/listed
+ pixel_north_offset = 6
+ column_max = 10
+ location = SCRN_OBJ_IN_LIST
+
+/datum/action_group/listed/refresh_actions()
+ . = ..()
+ owner?.palette_actions.refresh_actions() // We effect them, so we gotta refresh em
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index 728439e5fad..84e303a8894 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -1,345 +1,26 @@
-/atom/movable/screen/ai
- icon = 'icons/hud/screen_ai.dmi'
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/ai/Click()
- if(isobserver(usr) || usr.incapacitated)
- return TRUE
-
-/atom/movable/screen/ai/aicore
- name = "AI core"
- icon_state = "ai_core"
-
-/atom/movable/screen/ai/aicore/Click()
- if(isobserver(usr))
- return
- var/mob/living/silicon/ai/AI = usr
- AI.view_core()
-
-/atom/movable/screen/ai/camera_list
- name = "Show Camera List"
- icon_state = "camera"
-
-/atom/movable/screen/ai/camera_list/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.show_camera_list()
-
-/atom/movable/screen/ai/camera_track
- name = "Track With Camera"
- icon_state = "track"
-
-/atom/movable/screen/ai/camera_track/Click()
- . = ..()
- if(.)
- return
- var/mob/living/silicon/ai/AI = usr
- AI.ai_camera_track()
-
-/atom/movable/screen/ai/camera_light
- name = "Toggle Camera Light"
- icon_state = "camera_light"
-
-/atom/movable/screen/ai/camera_light/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.toggle_camera_light()
-
-/atom/movable/screen/ai/modpc
- name = "Messenger"
- icon_state = "pda_send"
- var/mob/living/silicon/ai/robot
-
-/atom/movable/screen/ai/modpc/Click()
- . = ..()
- if(.)
- return
- robot.modularInterface?.interact(robot)
-
-/atom/movable/screen/ai/crew_monitor
- name = "Crew Monitoring Console"
- icon_state = "crew_monitor"
-
-/atom/movable/screen/ai/crew_monitor/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- GLOB.crewmonitor.show(AI,AI)
-
-/atom/movable/screen/ai/crew_manifest
- name = "Crew Manifest"
- icon_state = "manifest"
-
-/atom/movable/screen/ai/crew_manifest/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.ai_roster()
-
-/atom/movable/screen/ai/alerts
- name = "Show Alerts"
- icon_state = "alerts"
-
-/atom/movable/screen/ai/alerts/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.alert_control.ui_interact(AI)
-
-/atom/movable/screen/ai/announcement
- name = "Make Vox Announcement"
- icon_state = "announcement"
-
-/atom/movable/screen/ai/announcement/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.announcement()
-
-/atom/movable/screen/ai/call_shuttle
- name = "Call Emergency Shuttle"
- icon_state = "call_shuttle"
-
-/atom/movable/screen/ai/call_shuttle/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.ai_call_shuttle()
-
-/atom/movable/screen/ai/state_laws
- name = "State Laws"
- icon_state = "state_laws"
-
-/atom/movable/screen/ai/state_laws/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.checklaws()
-
-/atom/movable/screen/ai/image_take
- name = "Take Image"
- icon_state = "take_picture"
-
-/atom/movable/screen/ai/image_take/Click()
- if(..())
- return
- if(isAI(usr))
- var/mob/living/silicon/ai/AI = usr
- AI.aicamera.toggle_camera_mode(usr)
- else if(iscyborg(usr))
- var/mob/living/silicon/robot/R = usr
- R.aicamera.toggle_camera_mode(usr)
-
-/atom/movable/screen/ai/image_view
- name = "View Images"
- icon_state = "view_images"
-
-/atom/movable/screen/ai/image_view/Click()
- if(..())
- return
- if(isAI(usr))
- var/mob/living/silicon/ai/AI = usr
- AI.aicamera.viewpictures(usr)
-
-/atom/movable/screen/ai/sensors
- name = "Sensor Augmentation"
- icon_state = "ai_sensor"
-
-/atom/movable/screen/ai/sensors/Click()
- if(..())
- return
- var/mob/living/silicon/S = usr
- S.toggle_sensors()
-
-/atom/movable/screen/ai/multicam
- name = "Multicamera Mode"
- icon_state = "multicam"
-
-/atom/movable/screen/ai/multicam/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.toggle_multicam()
-
-/atom/movable/screen/ai/add_multicam
- name = "New Camera"
- icon_state = "new_cam"
-
-/atom/movable/screen/ai/add_multicam/Click()
- if(..())
- return
- var/mob/living/silicon/ai/AI = usr
- AI.drop_new_multicam()
-
-/atom/movable/screen/ai/floor_indicator
- icon_state = "zindicator"
- screen_loc = ui_ai_floor_indicator
-
-/atom/movable/screen/ai/floor_indicator/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- if(istype(hud_owner))
- RegisterSignal(hud_owner, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(update_z))
- update_z()
-
-/atom/movable/screen/ai/floor_indicator/proc/update_z(datum/hud/source)
- SIGNAL_HANDLER
- var/mob/living/silicon/ai/ai = get_mob() //if you use this for anyone else i will find you
- if(isnull(ai))
- return
- var/turf/locturf = isturf(ai.loc) ? get_turf(ai.eyeobj) : get_turf(ai) //must be a var cuz error
- var/ai_z = locturf.z
- var/text = "Level
[ai_z]"
- if(SSmapping.level_trait(ai_z, ZTRAIT_STATION))
- text = "Floor
[ai_z - 1]"
- else if (SSmapping.level_trait(ai_z, ZTRAIT_NOPHASE))
- text = "ERROR"
- maptext = MAPTEXT_TINY_UNICODE("
[text]
")
-
-/atom/movable/screen/ai/go_up
- name = "go up"
- icon_state = "up"
- screen_loc = ui_ai_godownup
-
-/atom/movable/screen/ai/go_up/Initialize(mapload)
- . = ..()
- register_context()
-
-/atom/movable/screen/ai/go_up/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
- context[SCREENTIP_CONTEXT_LMB] = "Go up a floor"
- return CONTEXTUAL_SCREENTIP_SET
-
-/atom/movable/screen/ai/go_up/Click(location,control,params)
- var/mob/ai = get_mob() //the core
- flick("uppressed",src)
- if(!isturf(ai.loc) || usr != ai) //aicard and stuff
- return
- ai.up()
-
-/atom/movable/screen/ai/go_up/down
- name = "go down"
- icon_state = "down"
-
-/atom/movable/screen/ai/go_up/down/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
- context[SCREENTIP_CONTEXT_LMB] = "Go down a floor"
- return CONTEXTUAL_SCREENTIP_SET
-
-/atom/movable/screen/ai/go_up/down/Click(location,control,params)
- var/mob/ai = get_mob() //the core
- flick("downpressed",src)
- if(!isturf(ai.loc) || usr != ai) //aicard and stuff
- return
- ai.down()
-
/datum/hud/ai
ui_style = 'icons/hud/screen_ai.dmi'
-/datum/hud/ai/New(mob/owner)
- ..()
- var/atom/movable/screen/using
- var/mob/living/silicon/ai/myai = mymob
-
-// Language menu
- using = new /atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_ai_language_menu
- static_inventory += using
-
-// Memories
- using = new /atom/movable/screen/memories(null, src)
- using.icon = ui_style
- using.screen_loc = ui_ai_memories_menu
- static_inventory += using
-
-// Z-level floor change
- using = new /atom/movable/screen/ai/floor_indicator(null, src) //These come with their own predefined screen locs
- static_inventory += using
- using = new /atom/movable/screen/ai/go_up(null, src)
- static_inventory += using
- using = new /atom/movable/screen/ai/go_up/down(null, src)
- static_inventory += using
-
-//AI core
- using = new /atom/movable/screen/ai/aicore(null, src)
- using.screen_loc = ui_ai_core
- static_inventory += using
-
-//Camera list
- using = new /atom/movable/screen/ai/camera_list(null, src)
- using.screen_loc = ui_ai_camera_list
- static_inventory += using
-
-//Track
- using = new /atom/movable/screen/ai/camera_track(null, src)
- using.screen_loc = ui_ai_track_with_camera
- static_inventory += using
-
-//Camera light
- using = new /atom/movable/screen/ai/camera_light(null, src)
- using.screen_loc = ui_ai_camera_light
- static_inventory += using
-
-//Crew Monitoring
- using = new /atom/movable/screen/ai/crew_monitor(null, src)
- using.screen_loc = ui_ai_crew_monitor
- static_inventory += using
-
-//Crew Manifest
- using = new /atom/movable/screen/ai/crew_manifest(null, src)
- using.screen_loc = ui_ai_crew_manifest
- static_inventory += using
-
-//Alerts
- using = new /atom/movable/screen/ai/alerts(null, src)
- using.screen_loc = ui_ai_alerts
- static_inventory += using
-
-//Announcement
- using = new /atom/movable/screen/ai/announcement(null, src)
- using.screen_loc = ui_ai_announcement
- static_inventory += using
-
-//Shuttle
- using = new /atom/movable/screen/ai/call_shuttle(null, src)
- using.screen_loc = ui_ai_shuttle
- static_inventory += using
-
-//Laws
- using = new /atom/movable/screen/ai/state_laws(null, src)
- using.screen_loc = ui_ai_state_laws
- static_inventory += using
-
-// Modular Interface
- using = new /atom/movable/screen/ai/modpc(null, src)
- using.screen_loc = ui_ai_mod_int
- static_inventory += using
- var/atom/movable/screen/ai/modpc/tabletbutton = using
- tabletbutton.robot = myai
-
-//Take image
- using = new /atom/movable/screen/ai/image_take(null, src)
- using.screen_loc = ui_ai_take_picture
- static_inventory += using
-
-//View images
- using = new /atom/movable/screen/ai/image_view(null, src)
- using.screen_loc = ui_ai_view_images
- static_inventory += using
-
-//Medical/Security sensors
- using = new /atom/movable/screen/ai/sensors(null, src)
- using.screen_loc = ui_ai_sensor
- static_inventory += using
-
-//Multicamera mode
- using = new /atom/movable/screen/ai/multicam(null, src)
- using.screen_loc = ui_ai_multicam
- static_inventory += using
-
-//Add multicamera camera
- using = new /atom/movable/screen/ai/add_multicam(null, src)
- using.screen_loc = ui_ai_add_multicam
- static_inventory += using
+/datum/hud/ai/initialize_screen_objects()
+ . = ..()
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, HUD_GROUP_STATIC, ui_style, ui_ai_language_menu)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_ai_memories_menu)
+ add_screen_object(/atom/movable/screen/ai/floor_indicator, HUD_AI_FLOOR_INDICATOR)
+ add_screen_object(/atom/movable/screen/ai/go_up, HUD_AI_GO_UP)
+ add_screen_object(/atom/movable/screen/ai/go_up/down, HUD_AI_GO_DOWN)
+ add_screen_object(/atom/movable/screen/ai/aicore, HUD_AI_AICORE)
+ add_screen_object(/atom/movable/screen/ai/camera_list, HUD_AI_CAMERA_LIST)
+ add_screen_object(/atom/movable/screen/ai/camera_track, HUD_AI_CAMERA_TRACK)
+ add_screen_object(/atom/movable/screen/ai/camera_light, HUD_AI_CAMERA_LIGHT)
+ add_screen_object(/atom/movable/screen/ai/crew_monitor, HUD_AI_CREW_MONITOR)
+ add_screen_object(/atom/movable/screen/ai/crew_manifest, HUD_AI_CREW_MANIFEST)
+ add_screen_object(/atom/movable/screen/ai/alerts, HUD_SILICON_ALERTS)
+ add_screen_object(/atom/movable/screen/ai/announcement, HUD_AI_ANNOUNCEMENT)
+ add_screen_object(/atom/movable/screen/ai/call_shuttle, HUD_AI_CALL_SHUTTLE)
+ add_screen_object(/atom/movable/screen/ai/state_laws, HUD_AI_STATE_LAWS)
+ add_screen_object(/atom/movable/screen/ai/image_take, HUD_AI_TAKE_IMAGE)
+ add_screen_object(/atom/movable/screen/ai/image_view, HUD_AI_IMAGE_VIEW)
+ add_screen_object(/atom/movable/screen/ai/sensors, HUD_AI_SENSORS)
+ add_screen_object(/atom/movable/screen/ai/multicam, HUD_AI_MULTICAM)
+ add_screen_object(/atom/movable/screen/ai/add_multicam, HUD_AI_ADD_MULTICAM)
+ add_screen_object(/atom/movable/screen/ai/modpc, HUD_SILICON_TABLET)
diff --git a/code/_onclick/hud/alien.dm b/code/_onclick/hud/alien.dm
index 822fe712850..4ea86de55e7 100644
--- a/code/_onclick/hud/alien.dm
+++ b/code/_onclick/hud/alien.dm
@@ -1,136 +1,36 @@
-/atom/movable/screen/alien
- icon = 'icons/hud/screen_alien.dmi'
-
-/atom/movable/screen/alien/leap
- name = "toggle leap"
- icon_state = "leap_off"
-
-/atom/movable/screen/alien/leap/Click()
- if(isalienhunter(usr))
- var/mob/living/carbon/alien/adult/hunter/AH = usr
- AH.toggle_leap()
-
-/atom/movable/screen/alien/plasma_display
- name = "plasma stored"
- icon_state = "power_display"
- screen_loc = ui_alienplasmadisplay
-
-/atom/movable/screen/alien/alien_queen_finder
- name = "queen sense"
- desc = "Allows you to sense the general direction of your Queen."
- icon_state = "queen_finder"
- screen_loc = ui_alien_queen_finder
-
/datum/hud/alien
ui_style = 'icons/hud/screen_alien.dmi'
-/datum/hud/alien/New(mob/living/carbon/alien/adult/owner)
- ..()
-
- var/atom/movable/screen/using
-
-//equippable shit
-
-//hands
+/datum/hud/alien/initialize_screen_objects()
+ . = ..()
build_hand_slots()
-//begin buttons
-
- using = new /atom/movable/screen/swap_hand(null, src)
- using.icon = ui_style
+ var/atom/movable/screen/using
+ using = add_screen_object(/atom/movable/screen/swap_hand, HUD_MOB_SWAPHAND_1, HUD_GROUP_STATIC, ui_style, ui_swaphand_position(mymob, 1))
using.icon_state = "swap_1"
- using.screen_loc = ui_swaphand_position(owner, 1)
- static_inventory += using
-
- using = new /atom/movable/screen/swap_hand(null, src)
- using.icon = ui_style
+ using = add_screen_object(/atom/movable/screen/swap_hand, HUD_MOB_SWAPHAND_2, HUD_GROUP_STATIC, ui_style, ui_swaphand_position(mymob, 2))
using.icon_state = "swap_2"
- using.screen_loc = ui_swaphand_position(owner, 2)
- static_inventory += using
- action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
- action_intent.icon = ui_style
- action_intent.screen_loc = ui_combat_toggle
- static_inventory += action_intent
+ add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/floor_changer, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_alien_floor_change)
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, HUD_GROUP_STATIC, ui_style, ui_alien_language_menu)
+ add_screen_object(/atom/movable/screen/navigate, HUD_MOB_NAVIGATE_MENU, HUD_GROUP_STATIC, ui_style, ui_alien_navigate_menu)
+ add_screen_object(/atom/movable/screen/zone_sel/alien, HUD_MOB_ZONE_SELECTOR)
+ add_screen_object(/atom/movable/screen/drop, HUD_MOB_DROP, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style, ui_above_movement)
+ add_screen_object(/atom/movable/screen/resist, HUD_MOB_RESIST, HUD_GROUP_HOTKEYS, ui_style)
+ add_screen_object(/atom/movable/screen/throw_catch, HUD_MOB_THROW, HUD_GROUP_HOTKEYS, ui_style)
+ add_screen_object(/atom/movable/screen/rest, HUD_MOB_REST, HUD_GROUP_HOTKEYS, ui_style)
+ add_screen_object(/atom/movable/screen/sleep, HUD_MOB_SLEEP, HUD_GROUP_HOTKEYS, ui_style)
+ add_screen_object(/atom/movable/screen/healths/alien, HUD_MOB_HEALTH, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/alien/plasma_display, HUD_ALIEN_PLASMA_DISPLAY, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_alien_memories_menu)
if(isalienhunter(mymob))
- var/mob/living/carbon/alien/adult/hunter/H = mymob
- H.leap_icon = new /atom/movable/screen/alien/leap()
- H.leap_icon.screen_loc = ui_alien_storage_r
- static_inventory += H.leap_icon
-
- floor_change = new /atom/movable/screen/floor_changer(null, src)
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_alien_floor_change
- static_inventory += floor_change
-
- using = new/atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_alien_language_menu
- static_inventory += using
-
- using = new /atom/movable/screen/navigate(null, src)
- using.icon = ui_style
- using.screen_loc = ui_alien_navigate_menu
- static_inventory += using
-
- using = new /atom/movable/screen/memories(null, src)
- using.icon = ui_style
- using.screen_loc = ui_alien_memories_menu
- static_inventory += using
-
- using = new /atom/movable/screen/drop(null, src)
- using.icon = ui_style
- using.screen_loc = ui_drop_throw
- static_inventory += using
-
- resist_icon = new /atom/movable/screen/resist(null, src)
- resist_icon.icon = ui_style
- resist_icon.screen_loc = ui_above_movement
- resist_icon.update_appearance()
- hotkeybuttons += resist_icon
-
- throw_icon = new /atom/movable/screen/throw_catch(null, src)
- throw_icon.icon = ui_style
- throw_icon.screen_loc = ui_drop_throw
- hotkeybuttons += throw_icon
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_above_movement
- static_inventory += pull_icon
-
- rest_icon = new /atom/movable/screen/rest(null, src)
- rest_icon.icon = ui_style
- rest_icon.screen_loc = ui_rest
- rest_icon.update_appearance()
- static_inventory += rest_icon
-
- sleep_icon = new /atom/movable/screen/sleep(null, src)
- sleep_icon.icon = ui_style
- sleep_icon.screen_loc = ui_above_throw
-
-//begin indicators
-
- healths = new /atom/movable/screen/healths/alien(null, src)
- infodisplay += healths
-
- alien_plasma_display = new /atom/movable/screen/alien/plasma_display(null, src)
- infodisplay += alien_plasma_display
+ add_screen_object(/atom/movable/screen/alien/leap, HUD_ALIEN_HUNTER_LEAP, HUD_GROUP_STATIC, ui_style)
if(!isalienqueen(mymob))
- alien_queen_finder = new /atom/movable/screen/alien/alien_queen_finder(null, src)
- infodisplay += alien_queen_finder
-
- zone_select = new /atom/movable/screen/zone_sel/alien(null, src)
- zone_select.update_appearance()
- static_inventory += zone_select
-
- for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory))
- if(inv.slot_id)
- inv_slots[TOBITSHIFT(inv.slot_id) + 1] = inv
- inv.update_appearance()
+ add_screen_object(/atom/movable/screen/alien/plasma_display, HUD_ALIEN_QUEEN_FINDER, HUD_GROUP_INFO)
/datum/hud/alien/persistent_inventory_update()
if(!mymob)
diff --git a/code/_onclick/hud/alien_larva.dm b/code/_onclick/hud/alien_larva.dm
index 3331ac8c790..25ae3ba34bb 100644
--- a/code/_onclick/hud/alien_larva.dm
+++ b/code/_onclick/hud/alien_larva.dm
@@ -1,54 +1,16 @@
/datum/hud/larva
ui_style = 'icons/hud/screen_alien.dmi'
-/datum/hud/larva/New(mob/owner)
- ..()
- var/atom/movable/screen/using
-
- action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
- action_intent.icon = ui_style
- action_intent.screen_loc = ui_combat_toggle
- static_inventory += action_intent
-
- floor_change = new /atom/movable/screen/floor_changer(null, src)
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_alien_floor_change
- static_inventory += floor_change
-
- healths = new /atom/movable/screen/healths/alien(null, src)
- infodisplay += healths
-
- alien_queen_finder = new /atom/movable/screen/alien/alien_queen_finder(null, src)
- infodisplay += alien_queen_finder
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = 'icons/hud/screen_alien.dmi'
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_above_movement
- hotkeybuttons += pull_icon
-
- rest_icon = new /atom/movable/screen/rest(null, src)
- rest_icon.icon = ui_style
- rest_icon.screen_loc = ui_rest
- rest_icon.update_appearance()
- static_inventory += rest_icon
-
- sleep_icon = new /atom/movable/screen/sleep(null, src)
- sleep_icon.icon = ui_style
- sleep_icon.screen_loc = ui_drop_throw
-
- using = new/atom/movable/screen/language_menu(null, src)
- using.screen_loc = ui_alien_language_menu
- static_inventory += using
-
- using = new /atom/movable/screen/navigate(null, src)
- using.screen_loc = ui_alien_navigate_menu
- static_inventory += using
-
- using = new /atom/movable/screen/memories(null, src)
- using.screen_loc = ui_alien_memories_menu
- static_inventory += using
-
- zone_select = new /atom/movable/screen/zone_sel/alien(null, src)
- zone_select.update_appearance()
- static_inventory += zone_select
+/datum/hud/larva/initialize_screen_objects()
+ . = ..()
+ add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/floor_changer, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_alien_floor_change)
+ add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/rest, HUD_MOB_REST, HUD_GROUP_HOTKEYS, ui_style)
+ add_screen_object(/atom/movable/screen/sleep, HUD_MOB_SLEEP, HUD_GROUP_HOTKEYS, ui_style, ui_drop_throw)
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, ui_loc = ui_alien_language_menu)
+ add_screen_object(/atom/movable/screen/navigate, HUD_MOB_NAVIGATE_MENU, ui_loc = ui_alien_navigate_menu)
+ add_screen_object(/atom/movable/screen/zone_sel/alien, HUD_MOB_ZONE_SELECTOR)
+ add_screen_object(/atom/movable/screen/healths/alien, HUD_MOB_HEALTH, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/alien/alien_queen_finder, HUD_ALIEN_QUEEN_FINDER, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_alien_memories_menu)
diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm
index 6b5213c027b..cf92ff67b47 100644
--- a/code/_onclick/hud/blob_overmind.dm
+++ b/code/_onclick/hud/blob_overmind.dm
@@ -1,198 +1,12 @@
-
-/atom/movable/screen/blob
- icon = 'icons/hud/blob.dmi'
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/blob/MouseEntered(location,control,params)
+/datum/hud/blob_overmind/initialize_screen_objects()
. = ..()
- openToolTip(usr,src,params,title = name,content = desc, theme = "blob")
-
-/atom/movable/screen/blob/MouseExited()
- closeToolTip(usr)
-
-/atom/movable/screen/blob/jump_to_node
- icon_state = "ui_tonode"
- name = "Jump to Node"
- desc = "Moves your camera to a selected blob node."
-
-/atom/movable/screen/blob/jump_to_node/Click()
- if(!isovermind(usr))
- return FALSE
- var/mob/eye/blob/blob = usr
- blob.jump_to_node()
-
-/atom/movable/screen/blob/jump_to_core
- icon_state = "ui_tocore"
- name = "Jump to Core"
- desc = "Moves your camera to your blob core."
-
-/atom/movable/screen/blob/jump_to_core/MouseEntered(location,control,params)
- if(hud?.mymob && isovermind(hud.mymob))
- var/mob/eye/blob/B = hud.mymob
- if(!B.placed)
- name = "Place Blob Core"
- desc = "Attempt to place your blob core at this location."
- else
- name = initial(name)
- desc = initial(desc)
- return ..()
-
-/atom/movable/screen/blob/jump_to_core/Click()
- if(!isovermind(usr))
- return FALSE
- var/mob/eye/blob/blob = usr
- if(!blob.placed)
- blob.place_blob_core(BLOB_NORMAL_PLACEMENT)
- blob.transport_core()
-
-/atom/movable/screen/blob/blobbernaut
- icon_state = "ui_blobbernaut"
- // Name and description get given their proper values on Initialize()
- name = "Produce Blobbernaut (ERROR)"
- desc = "Produces a strong, smart blobbernaut from a factory blob for (ERROR) resources.
The factory blob used will become fragile and unable to produce spores."
-
-/atom/movable/screen/blob/blobbernaut/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- name = "Produce Blobbernaut ([BLOBMOB_BLOBBERNAUT_RESOURCE_COST])"
- desc = "Produces a strong, smart blobbernaut from a factory blob for [BLOBMOB_BLOBBERNAUT_RESOURCE_COST] resources.
The factory blob used will become fragile and unable to produce spores."
-
-/atom/movable/screen/blob/blobbernaut/Click()
- if(!isovermind(usr))
- return FALSE
- var/mob/eye/blob/blob = usr
- blob.create_blobbernaut()
-
-/atom/movable/screen/blob/resource_blob
- icon_state = "ui_resource"
- // Name and description get given their proper values on Initialize()
- name = "Produce Resource Blob (ERROR)"
- desc = "Produces a resource blob for ERROR resources.
Resource blobs will give you resources every few seconds."
-
-/atom/movable/screen/blob/resource_blob/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- name = "Produce Resource Blob ([BLOB_STRUCTURE_RESOURCE_COST])"
- desc = "Produces a resource blob for [BLOB_STRUCTURE_RESOURCE_COST] resources.
Resource blobs will give you resources every few seconds."
-
-/atom/movable/screen/blob/resource_blob/Click()
- if(!isovermind(usr))
- return FALSE
- var/mob/eye/blob/blob = usr
- blob.create_special(BLOB_STRUCTURE_RESOURCE_COST, /obj/structure/blob/special/resource, BLOB_RESOURCE_MIN_DISTANCE, TRUE)
-
-/atom/movable/screen/blob/node_blob
- icon_state = "ui_node"
- // Name and description get given their proper values on Initialize()
- name = "Produce Node Blob (ERROR)"
- desc = "Produces a node blob for ERROR resources.
Node blobs will expand and activate nearby resource and factory blobs."
-
-/atom/movable/screen/blob/node_blob/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- name = "Produce Node Blob ([BLOB_STRUCTURE_NODE_COST])"
- desc = "Produces a node blob for [BLOB_STRUCTURE_NODE_COST] resources.
Node blobs will expand and activate nearby resource and factory blobs."
-
-/atom/movable/screen/blob/node_blob/Click()
- if(!isovermind(usr))
- return FALSE
- var/mob/eye/blob/blob = usr
- blob.create_special(BLOB_STRUCTURE_NODE_COST, /obj/structure/blob/special/node, BLOB_NODE_MIN_DISTANCE, FALSE)
-
-/atom/movable/screen/blob/factory_blob
- icon_state = "ui_factory"
- // Name and description get given their proper values on Initialize()
- name = "Produce Factory Blob (ERROR)"
- desc = "Produces a factory blob for ERROR resources.
Factory blobs will produce spores every few seconds."
-
-/atom/movable/screen/blob/factory_blob/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- name = "Produce Factory Blob ([BLOB_STRUCTURE_FACTORY_COST])"
- desc = "Produces a factory blob for [BLOB_STRUCTURE_FACTORY_COST] resources.
Factory blobs will produce spores every few seconds."
-
-/atom/movable/screen/blob/factory_blob/Click()
- if(!isovermind(usr))
- return FALSE
- var/mob/eye/blob/blob = usr
- blob.create_special(BLOB_STRUCTURE_FACTORY_COST, /obj/structure/blob/special/factory, BLOB_FACTORY_MIN_DISTANCE, TRUE)
-
-/atom/movable/screen/blob/readapt_strain
- icon_state = "ui_chemswap"
- // Description gets given its proper values on Initialize()
- name = "Readapt Strain"
- desc = "Allows you to choose a new strain from ERROR random choices for ERROR resources."
-
-/atom/movable/screen/blob/readapt_strain/MouseEntered(location,control,params)
- if(hud?.mymob && isovermind(hud.mymob))
- var/mob/eye/blob/B = hud.mymob
- if(B.free_strain_rerolls)
- name = "[initial(name)] (FREE)"
- desc = "Randomly rerolls your strain for free."
- else
- name = "[initial(name)] ([BLOB_POWER_REROLL_COST])"
- desc = "Allows you to choose a new strain from [BLOB_POWER_REROLL_CHOICES] random choices for [BLOB_POWER_REROLL_COST] resources."
- return ..()
-
-/atom/movable/screen/blob/readapt_strain/Click()
- if(isovermind(usr))
- var/mob/eye/blob/B = usr
- B.strain_reroll()
-
-/atom/movable/screen/blob/relocate_core
- icon_state = "ui_swap"
- // Name and description get given their proper values on Initialize()
- name = "Relocate Core (ERROR)"
- desc = "Swaps a node and your core for ERROR resources."
-
-/atom/movable/screen/blob/relocate_core/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- name = "Relocate Core ([BLOB_POWER_RELOCATE_COST])"
- desc = "Swaps a node and your core for [BLOB_POWER_RELOCATE_COST] resources."
-
-/atom/movable/screen/blob/relocate_core/Click()
- if(isovermind(usr))
- var/mob/eye/blob/B = usr
- B.relocate_core()
-
-/atom/movable/screen/blob_power_display
- name = "blob power"
- icon_state = "block"
- screen_loc = ui_health
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- plane = ABOVE_HUD_PLANE
-
-/datum/hud/blob_overmind/New(mob/owner)
- ..()
- var/atom/movable/screen/using
-
- healths = new /atom/movable/screen/healths/blob(null, src)
- infodisplay += healths
-
- using = new /atom/movable/screen/blob/jump_to_node(null, src)
- using.screen_loc = ui_inventory
- static_inventory += using
-
- using = new /atom/movable/screen/blob/jump_to_core(null, src)
- using.screen_loc = ui_zonesel
- static_inventory += using
-
- using = new /atom/movable/screen/blob/blobbernaut(null, src)
- using.screen_loc = ui_belt
- static_inventory += using
-
- using = new /atom/movable/screen/blob/resource_blob(null, src)
- using.screen_loc = ui_back
- static_inventory += using
-
- using = new /atom/movable/screen/blob/node_blob(null, src)
- using.screen_loc = ui_hand_position(RIGHT_HANDS)
- static_inventory += using
-
- using = new /atom/movable/screen/blob/factory_blob(null, src)
- using.screen_loc = ui_hand_position(LEFT_HANDS)
- static_inventory += using
-
- using = new /atom/movable/screen/blob/readapt_strain(null, src)
- using.screen_loc = ui_storage1
- static_inventory += using
-
- using = new /atom/movable/screen/blob/relocate_core(null, src)
- using.screen_loc = ui_storage2
- static_inventory += using
+ add_screen_object(/atom/movable/screen/healths/blob, HUD_MOB_HEALTH, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/blob/blob_power, HUD_BLOB_POWER_DISPLAY, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/blob/jump_to_node, HUD_BLOB_JUMP_TO_NODE)
+ add_screen_object(/atom/movable/screen/blob/jump_to_core, HUD_BLOB_JUMP_TO_CORE)
+ add_screen_object(/atom/movable/screen/blob/blobbernaut, HUD_BLOB_BLOBBERNAUT)
+ add_screen_object(/atom/movable/screen/blob/resource_blob, HUD_BLOB_RESOURCES)
+ add_screen_object(/atom/movable/screen/blob/node_blob, HUD_BLOB_NODE, ui_loc = ui_hand_position(RIGHT_HANDS))
+ add_screen_object(/atom/movable/screen/blob/factory_blob, HUD_BLOB_FACTORY, ui_loc = ui_hand_position(LEFT_HANDS))
+ add_screen_object(/atom/movable/screen/blob/readapt_strain, HUD_BLOB_READAPT)
+ add_screen_object(/atom/movable/screen/blob/relocate_core, HUD_BLOB_RELOCATE)
diff --git a/code/_onclick/hud/drones.dm b/code/_onclick/hud/drones.dm
index ad3604a19e7..e7694d5d660 100644
--- a/code/_onclick/hud/drones.dm
+++ b/code/_onclick/hud/drones.dm
@@ -1,30 +1,5 @@
-/datum/hud/dextrous/drone/New(mob/owner)
- ..()
- var/atom/movable/screen/inventory/inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "internal storage"
- inv_box.icon = ui_style
- inv_box.icon_state = "suit_storage"
-// inv_box.icon_full = "template"
- inv_box.screen_loc = ui_drone_storage
- inv_box.slot_id = ITEM_SLOT_DEX_STORAGE
- static_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "head/mask"
- inv_box.icon = ui_style
- inv_box.icon_state = "mask"
-// inv_box.icon_full = "template"
- inv_box.screen_loc = ui_drone_head
- inv_box.slot_id = ITEM_SLOT_HEAD
- static_inventory += inv_box
-
- for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory))
- if(inv.slot_id)
- inv_slots[TOBITSHIFT(inv.slot_id) + 1] = inv
- inv.update_appearance()
-
+/datum/hud/dextrous/drone
+ inventory_slots = /datum/inventory_slot/drone
/datum/hud/dextrous/drone/persistent_inventory_update()
if(!mymob)
@@ -41,5 +16,19 @@
else
drone.internal_storage?.screen_loc = null
drone.head?.screen_loc = null
-
..()
+
+/datum/inventory_slot/drone
+ abstract_type = /datum/inventory_slot/drone
+
+/datum/inventory_slot/drone/storage
+ name = "internal storage"
+ icon_state = "suit_storage"
+ slot_id = ITEM_SLOT_DEX_STORAGE
+ screen_loc = ui_drone_storage
+
+/datum/inventory_slot/drone/head
+ name = "head/mask"
+ icon_state = "mask"
+ slot_id = ITEM_SLOT_HEAD
+ screen_loc = ui_drone_head
diff --git a/code/_onclick/hud/generic_dextrous.dm b/code/_onclick/hud/generic_dextrous.dm
index b7f7f7af739..0799edc98ba 100644
--- a/code/_onclick/hud/generic_dextrous.dm
+++ b/code/_onclick/hud/generic_dextrous.dm
@@ -1,70 +1,36 @@
//Used for normal mobs that have hands.
-/datum/hud/dextrous/New(mob/living/owner)
- ..()
+/datum/hud/dextrous/initialize_screen_objects()
+ . = ..()
var/atom/movable/screen/using
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_drone_pull
- static_inventory += pull_icon
-
- build_hand_slots()
-
- using = new /atom/movable/screen/drop(null, src)
- using.icon = ui_style
- using.screen_loc = ui_swaphand_position(owner, 1)
- static_inventory += using
-
- using = new /atom/movable/screen/swap_hand(null, src)
- using.icon = ui_style
+ add_screen_object(/atom/movable/screen/drop, HUD_MOB_DROP, HUD_GROUP_STATIC, ui_style, ui_swaphand_position(mymob, 1))
+ using = add_screen_object(/atom/movable/screen/swap_hand, HUD_MOB_SWAPHAND_2, HUD_GROUP_STATIC, ui_style, ui_swaphand_position(mymob, 2))
using.icon_state = "act_swap"
- using.screen_loc = ui_swaphand_position(owner, 2)
- static_inventory += using
-
- action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
- action_intent.icon = ui_style
- action_intent.screen_loc = ui_movi
- static_inventory += action_intent
-
- floor_change = new /atom/movable/screen/floor_changer(null, src)
- floor_change.icon = 'icons/hud/screen_midnight.dmi'
- static_inventory += floor_change
-
- if(HAS_TRAIT(owner, TRAIT_CAN_THROW_ITEMS))
- throw_icon = new /atom/movable/screen/throw_catch(null, src)
- throw_icon.icon = ui_style
- throw_icon.screen_loc = ui_drop_throw
- static_inventory += throw_icon
-
- zone_select = new /atom/movable/screen/zone_sel(null, src)
- zone_select.icon = ui_style
- zone_select.update_appearance()
- static_inventory += zone_select
-
- using = new /atom/movable/screen/area_creator(null, src)
- using.icon = ui_style
- static_inventory += using
-
- healthdoll = new /atom/movable/screen/healthdoll/living(null, src)
- infodisplay += healthdoll
mymob.canon_client?.clear_screen()
- for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory))
- if(inv.slot_id)
- inv_slots[TOBITSHIFT(inv.slot_id) + 1] = inv
- inv.update_appearance()
+ 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_STATIC, ui_style, ui_movi)
+ 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)
/datum/hud/dextrous/persistent_inventory_update()
if(!mymob)
return
- var/mob/living/D = mymob
+ var/mob/living/owner = mymob
if(hud_version != HUD_STYLE_NOHUD)
- for(var/obj/item/I in D.held_items)
- I.screen_loc = ui_hand_position(D.get_held_index_of_item(I))
- D.client.screen += I
- else
- for(var/obj/item/I in D.held_items)
- I.screen_loc = null
- D.client.screen -= I
+ for(var/obj/item/held in owner.held_items)
+ held.screen_loc = ui_hand_position(owner.get_held_index_of_item(held))
+ owner.client.screen += held
+ return
+
+ for(var/obj/item/held in owner.held_items)
+ held.screen_loc = null
+ owner.client.screen -= held
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index 14b332d0c3c..b8f5e611e37 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -1,198 +1,22 @@
-/atom/movable/screen/ghost
- icon = 'icons/hud/screen_ghost.dmi'
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/ghost/MouseEntered(location, control, params)
- . = ..()
- flick(icon_state + "_anim", src)
-
-/atom/movable/screen/ghost/spawners_menu
- name = "Spawners menu"
- icon_state = "spawners"
-
-/atom/movable/screen/ghost/spawners_menu/Click()
- var/mob/dead/observer/observer = usr
- observer.open_spawners_menu()
-
-/atom/movable/screen/ghost/orbit
- name = "Orbit"
- icon_state = "orbit"
-
-/atom/movable/screen/ghost/orbit/Click()
- GLOB.orbit_menu.show(usr)
-
-/atom/movable/screen/ghost/reenter_corpse
- name = "Reenter corpse"
- icon_state = "reenter_corpse"
-
-/atom/movable/screen/ghost/reenter_corpse/Click()
- var/mob/dead/observer/G = usr
- G.reenter_corpse()
-
-/atom/movable/screen/ghost/dnr
- name = "Do Not Resuscitate"
- icon_state = "dnr"
-
-/atom/movable/screen/ghost/dnr/Click()
- var/mob/dead/observer/dnring = usr
- dnring.do_not_resuscitate()
-
-/atom/movable/screen/ghost/teleport
- name = "Teleport"
- icon_state = "teleport"
-
-/atom/movable/screen/ghost/teleport/Click()
- var/mob/dead/observer/G = usr
- G.dead_tele()
-
-/atom/movable/screen/ghost/settings
- name = "Ghost Settings"
- icon_state = "settings"
-
-/atom/movable/screen/ghost/settings/MouseEntered(location, control, params)
- . = ..()
- flick(icon_state + "_anim", src)
-
-/atom/movable/screen/ghost/settings/Click()
- GLOB.ghost_menu.ui_interact(usr)
-
-/atom/movable/screen/ghost/minigames_menu
- name ="Minigames"
- icon_state = "minigames"
-
-/atom/movable/screen/ghost/minigames_menu/Click()
- var/mob/dead/observer/observer = usr
- observer.open_minigames_menu()
-
-/atom/movable/screen/ghost/hudbox
- icon_state = "smallbox"
- abstract_type = /atom/movable/screen/ghost/hudbox
- /// Icon state used for the overlay representing this hudbox
- var/hud_icon_state
- /// The flag this hudbox toggles
- var/relevant_flag
-
-/atom/movable/screen/ghost/hudbox/update_overlays()
- . = ..()
- . += hud_icon_state
-
-/atom/movable/screen/ghost/hudbox/update_icon_state()
- . = ..()
- var/mob/dead/observer/observer = hud?.mymob
- if(!istype(observer))
- return
-
- icon_state = "smallbox[is_active(observer) ? "_active" : ""]"
-
-/atom/movable/screen/ghost/hudbox/proc/is_active(mob/dead/observer/observer)
- return (observer.ghost_hud_flags & relevant_flag)
-
-/atom/movable/screen/ghost/hudbox/Click(location, control, params)
- var/mob/dead/observer/observer = usr
- switch(relevant_flag)
- if(GHOST_DARKNESS_LEVEL)
- observer.toggle_darkness()
- if(GHOST_TRAY)
- observer.tray_view()
- else
- observer.toggle_ghost_hud_flag(relevant_flag)
-
- update_appearance(UPDATE_ICON_STATE)
-
-/atom/movable/screen/ghost/hudbox/health_scanner
- name = "Health Scanner"
- desc = "Toggles your ability to health scan mobs on click."
- hud_icon_state = "health_vision"
- relevant_flag = GHOST_HEALTH
-
-/atom/movable/screen/ghost/hudbox/chem_scanner
- name = "Chem Scanner"
- desc = "Toggles your ability to chemical scan mobs on click."
- hud_icon_state = "chem_vision"
- relevant_flag = GHOST_CHEM
-
-/atom/movable/screen/ghost/hudbox/gas_scanner
- name = "Gas Scanner"
- desc = "Toggles your ability to gas scan objects on click."
- hud_icon_state = "atmos_vision"
- relevant_flag = GHOST_GAS
-
-/atom/movable/screen/ghost/hudbox/ghost
- name = "Ghost Vision"
- desc = "Toggles whether you can see other ghosts."
- hud_icon_state = "ghost_vision"
- relevant_flag = GHOST_VISION
-
-/atom/movable/screen/ghost/hudbox/data_huds
- name = "Data HUDs"
- desc = "Toggles the display of data HUDs (health, security, diagnostics, etc)."
- hud_icon_state = "data_vision"
- relevant_flag = GHOST_DATA_HUDS
-
-/atom/movable/screen/ghost/hudbox/tray_icon
- name = "Tray View"
- desc = "Shows the t-ray view of the area around your ghost."
- hud_icon_state = "tray_vision"
- relevant_flag = GHOST_TRAY
-
-/atom/movable/screen/ghost/hudbox/darkness_level
- name = "Darkness Level"
- desc = "Cycles through different darkness levels for ghost vision."
- hud_icon_state = "darkness_vision"
- relevant_flag = GHOST_DARKNESS_LEVEL
/datum/hud/ghost
ui_style = 'icons/hud/screen_ghost.dmi'
-/datum/hud/ghost/New(mob/owner)
- ..()
- var/atom/movable/screen/using
-
- using = new /atom/movable/screen/ghost/spawners_menu(null, src)
- using.screen_loc = ui_ghost_spawners_menu
- static_inventory += using
-
- using = new /atom/movable/screen/ghost/orbit(null, src)
- using.screen_loc = ui_ghost_orbit
- static_inventory += using
-
- using = new /atom/movable/screen/ghost/reenter_corpse(null, src)
- using.screen_loc = ui_ghost_reenter_corpse
- static_inventory += using
-
- using = new /atom/movable/screen/ghost/dnr(null, src)
- using.screen_loc = ui_dnr
- static_inventory += using
-
- using = new /atom/movable/screen/ghost/teleport(null, src)
- using.screen_loc = ui_ghost_teleport
- static_inventory += using
-
- using = new /atom/movable/screen/ghost/settings(null, src)
- using.screen_loc = ui_ghost_settings
- static_inventory += using
-
- using = new /atom/movable/screen/ghost/minigames_menu(null, src)
- using.screen_loc = ui_ghost_minigames
- static_inventory += using
-
- using = new /atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_ghost_language_menu
- static_inventory += using
-
- floor_change = new /atom/movable/screen/floor_changer/vertical(null, src)
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_ghost_floor_changer
- static_inventory += floor_change
+/datum/hud/ghost/initialize_screen_objects()
+ . = ..()
+ add_screen_object(/atom/movable/screen/ghost/spawners_menu, HUD_GHOST_SPAWNERS)
+ add_screen_object(/atom/movable/screen/ghost/orbit, HUD_GHOST_ORBIT)
+ add_screen_object(/atom/movable/screen/ghost/reenter_corpse, HUD_GHOST_REENTER_CORPSE)
+ add_screen_object(/atom/movable/screen/ghost/dnr, HUD_GHOST_DNR)
+ add_screen_object(/atom/movable/screen/ghost/teleport, HUD_GHOST_TELEPORT)
+ add_screen_object(/atom/movable/screen/ghost/settings, HUD_GHOST_SETTINGS)
+ add_screen_object(/atom/movable/screen/ghost/minigames_menu, HUD_GHOST_MINIGAMES)
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, HUD_GROUP_STATIC, ui_style, ui_ghost_language_menu)
+ add_screen_object(/atom/movable/screen/floor_changer/vertical, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_ghost_floor_changer)
var/list/hudboxes = valid_subtypesof(/atom/movable/screen/ghost/hudbox)
for(var/i in 1 to length(hudboxes))
- var/hudbox_type = hudboxes[i]
- var/atom/movable/screen/ghost/hudbox/hudbox = new hudbox_type(null, src)
- hudbox.screen_loc = position_hudbox(i - 1)
- static_inventory += hudbox
- hudbox.update_appearance()
+ add_screen_object(hudboxes[i], HUD_KEY_GHOST_HUDBOX(i), ui_loc = position_hudbox(i - 1))
/datum/hud/ghost/proc/position_hudbox(i)
var/row = floor(i / 3)
@@ -211,15 +35,15 @@
return
var/mob/screenmob = viewmob || mymob
if(screenmob.client.prefs.read_preference(/datum/preference/toggle/ghost_hud))
- screenmob.client.screen |= static_inventory
- for(var/atom/movable/screen/ghost/hudbox/hud in static_inventory)
+ screenmob.client.screen |= screen_groups[HUD_GROUP_STATIC]
+ for(var/atom/movable/screen/ghost/hudbox/hud in screen_groups[HUD_GROUP_STATIC])
hud.update_appearance()
else
- screenmob.client.screen -= static_inventory
+ screenmob.client.screen -= screen_groups[HUD_GROUP_STATIC]
//We should only see observed mob alerts.
/datum/hud/ghost/reorganize_alerts(mob/viewmob)
var/mob/dead/observer/O = mymob
if (istype(O) && O.observetarget)
return
- . = ..()
+ return ..()
diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm
index 68a480d47f8..cabe261b14e 100644
--- a/code/_onclick/hud/guardian.dm
+++ b/code/_onclick/hud/guardian.dm
@@ -1,169 +1,54 @@
/datum/hud/guardian
ui_style = 'icons/hud/guardian.dmi'
+ inventory_slots = list(/datum/inventory_slot/guardian_storage)
-/datum/hud/guardian/New(mob/living/basic/guardian/owner)
- ..()
- var/atom/movable/screen/using
+/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)
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_living_pull
- static_inventory += pull_icon
+ add_screen_object(/atom/movable/screen/healths/guardian, HUD_MOB_HEALTH, HUD_GROUP_INFO)
- healths = new /atom/movable/screen/healths/guardian(null, src)
- infodisplay += healths
+ var/mob/living/basic/guardian/owner = mymob
+ if (istype(owner))
+ add_screen_object(owner.toggle_button_type, HUD_GUARDIAN_TOGGLE, ui_loc = ui_storage1)
- using = new /atom/movable/screen/guardian/manifest(null, src)
- using.screen_loc = ui_hand_position(RIGHT_HANDS)
- static_inventory += using
+/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)
- using = new /atom/movable/screen/guardian/recall(null, src)
- using.screen_loc = ui_hand_position(LEFT_HANDS)
- static_inventory += using
+ add_screen_object(/atom/movable/screen/healths/guardian, HUD_MOB_HEALTH, HUD_GROUP_INFO)
- using = new owner.toggle_button_type(null, src)
- using.screen_loc = ui_storage1
- static_inventory += using
-
- using = new /atom/movable/screen/guardian/toggle_light(null, src)
- using.screen_loc = ui_inventory
- static_inventory += using
-
- using = new /atom/movable/screen/guardian/communicate(null, src)
- using.screen_loc = ui_back
- static_inventory += using
-
-/datum/hud/dextrous/guardian/New(mob/living/basic/guardian/owner) //for a dextrous guardian
- ..()
- var/atom/movable/screen/using
- if(istype(owner, /mob/living/basic/guardian/dextrous))
- var/atom/movable/screen/inventory/inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "internal storage"
- inv_box.icon = ui_style
- inv_box.icon_state = "suit_storage"
- inv_box.screen_loc = ui_id
- inv_box.slot_id = ITEM_SLOT_DEX_STORAGE
- static_inventory += inv_box
-
- using = new /atom/movable/screen/guardian/communicate(null, src)
- using.screen_loc = ui_sstore1
- static_inventory += using
-
- else
-
- using = new /atom/movable/screen/guardian/communicate(null, src)
- using.screen_loc = ui_id
- static_inventory += using
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_living_pull
- static_inventory += pull_icon
-
- healths = new /atom/movable/screen/healths/guardian(null, src)
- infodisplay += healths
-
- using = new /atom/movable/screen/guardian/manifest(null, src)
- using.screen_loc = ui_belt
- static_inventory += using
-
- using = new /atom/movable/screen/guardian/recall(null, src)
- using.screen_loc = ui_back
- static_inventory += using
-
- using = new owner.toggle_button_type(null, src)
- using.screen_loc = ui_storage2
- static_inventory += using
-
- using = new /atom/movable/screen/guardian/toggle_light(null, src)
- using.screen_loc = ui_inventory
- static_inventory += using
+ 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
- if(istype(mymob, /mob/living/basic/guardian/dextrous))
- 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.client.screen += dex_guardian.internal_storage
- else
- if(dex_guardian.internal_storage)
- dex_guardian.internal_storage.screen_loc = null
+ if(!istype(mymob, /mob/living/basic/guardian/dextrous))
+ return ..()
- ..()
+ 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.client.screen += dex_guardian.internal_storage
+ else
+ if(dex_guardian.internal_storage)
+ dex_guardian.internal_storage.screen_loc = null
+ return ..()
-/atom/movable/screen/guardian
- icon = 'icons/hud/guardian.dmi'
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/guardian/manifest
- icon_state = "manifest"
- name = "Manifest"
- desc = "Spring forth into battle!"
-
-/atom/movable/screen/guardian/manifest/Click()
- if(isguardian(usr))
- var/mob/living/basic/guardian/user = usr
- user.manifest()
-
-
-/atom/movable/screen/guardian/recall
- icon_state = "recall"
- name = "Recall"
- desc = "Return to your user."
-
-/atom/movable/screen/guardian/recall/Click()
- if(isguardian(usr))
- var/mob/living/basic/guardian/user = usr
- user.recall()
-
-/atom/movable/screen/guardian/toggle_mode
- icon_state = "toggle"
- name = "Toggle Mode"
- desc = "Switch between ability modes."
-
-/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
- icon_state = "stealth"
- name = "Toggle Stealth"
- desc = "Enter or exit stealth."
-
-/atom/movable/screen/guardian/toggle_mode/gases
- icon_state = "gases"
- name = "Toggle Gas"
- desc = "Switch between possible gases."
-
-/atom/movable/screen/guardian/communicate
- icon_state = "communicate"
- name = "Communicate"
- desc = "Communicate telepathically with your user."
-
-/atom/movable/screen/guardian/communicate/Click()
- if(isguardian(usr))
- var/mob/living/basic/guardian/user = usr
- user.communicate()
-
-
-/atom/movable/screen/guardian/toggle_light
- icon_state = "light"
- name = "Toggle Light"
- desc = "Glow like star dust."
-
-/atom/movable/screen/guardian/toggle_light/Click()
- if(isguardian(usr))
- var/mob/living/basic/guardian/user = usr
- user.toggle_light()
+/datum/inventory_slot/guardian_storage
+ name = "internal storage"
+ icon_state = "suit_storage"
+ slot_id = ITEM_SLOT_DEX_STORAGE
+ screen_loc = ui_id
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 0f722a1bc11..88eb2d5a6f1 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -22,34 +22,15 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/datum/hud
var/mob/mymob
+ /// Used for the HUD toggle (F12)
+ var/hud_shown = TRUE
+ /// Current displayed version of the HUD
+ var/hud_version = HUD_STYLE_STANDARD
+ /// Equipped item inventory
+ var/inventory_shown = FALSE
+ /// This is to hide the buttons that can be used via hotkeys. (hotkeybuttons list of buttons)
+ var/hotkey_ui_hidden = FALSE
- var/hud_shown = TRUE //Used for the HUD toggle (F12)
- var/hud_version = HUD_STYLE_STANDARD //Current displayed version of the HUD
- var/inventory_shown = FALSE //Equipped item inventory
- var/hotkey_ui_hidden = FALSE //This is to hide the buttons that can be used via hotkeys. (hotkeybuttons list of buttons)
-
- var/atom/movable/screen/alien_plasma_display
- var/atom/movable/screen/alien_queen_finder
-
- var/atom/movable/screen/action_intent
- var/atom/movable/screen/zone_select
- var/atom/movable/screen/pull_icon
- var/atom/movable/screen/rest_icon
- var/atom/movable/screen/sleep_icon
- var/atom/movable/screen/throw_icon
- var/atom/movable/screen/resist_icon
- var/atom/movable/screen/floor_change
-
- var/list/static_inventory = list() //the screen objects which are static
- var/list/toggleable_inventory = list() //the screen objects which can be hidden
- var/list/atom/movable/screen/hotkeybuttons = list() //the buttons that can be used via hotkeys
- var/list/infodisplay = list() //the screen objects that display mob info (health, alien plasma, etc...)
- /// Screen objects that never exit view.
- var/list/always_visible_inventory = list()
- var/list/inv_slots[SLOTS_AMT] // /atom/movable/screen/inventory objects, ordered by their slot ID.
- var/list/hand_slots // /atom/movable/screen/inventory/hand objects, assoc list of "[held_index]" = object
- /// storages (and its content) currently open by mob
- var/list/open_containers = list()
/// Assoc list of key => "plane master groups"
/// This is normally just the main window, but it'll occasionally contain things like spyglasses windows
var/list/datum/plane_master_group/master_groups = list()
@@ -62,8 +43,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/// Goes from 0 to the max (z level stack size - 1)
var/current_plane_offset = 0
- ///UI for screentips that appear when you mouse over things
- var/atom/movable/screen/screentip/screentip_text
+ /// UI for screentips that appear when you mouse over things
+ /// Stored directly as it is used in very hot MouseEntered code
+ var/atom/movable/screen/screentip/screentip_text = null
/// Whether or not screentips are enabled.
/// This is updated by the preference for cheaper reads than would be
@@ -78,26 +60,27 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/// 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).
- var/screentip_color
-
- var/atom/movable/screen/button_palette/toggle_palette
- var/atom/movable/screen/palette_scroll/down/palette_down
- var/atom/movable/screen/palette_scroll/up/palette_up
-
- var/datum/action_group/palette/palette_actions
- var/datum/action_group/listed/listed_actions
- var/list/floating_actions
-
- var/atom/movable/screen/healths
- var/atom/movable/screen/stamina
- var/atom/movable/screen/healthdoll/healthdoll
- var/atom/movable/screen/spacesuit/spacesuit_hud
- var/atom/movable/screen/hunger/hunger
+ /// had with a proc call, especially on one of the hottest procs in the game (MouseEntered).
+ var/screentip_color = null
+ var/datum/action_group/palette/palette_actions = null
+ var/datum/action_group/listed/listed_actions = null
+ var/list/floating_actions = null
/// Subtypes can override this to force a specific UI style
- var/ui_style
+ var/ui_style = null
+ /// Assoc list of all screen objects we hold by their key
+ var/list/atom/movable/screen/screen_objects = list()
+ /// List of screen objects by their screen group
+ var/list/screen_groups[SCREEN_GROUP_AMT]
+ /// List of all inventory slot screen objects by their slot ID. Some slots are fake and will be missing from here!
+ var/list/inv_slots[SLOTS_AMT]
+ /// List of hand slot objects, kept separate from the rest of inventory as mobs can have varying amount of hands
+ var/list/atom/movable/screen/inventory/hand/hand_slots = null
+
+ /// List of typepaths of /datum/inventory_slot which will be used to automatically create inventory slot UI elements
+ /// If assigned a typepath instead of a list, it will instead use all valid subtypes of said typepath
+ /// Safe to change in initialize_screen_objects() but not later
+ var/list/inventory_slots = null
/// List of weakrefs to objects that we add to our screen that we don't expect to DO anything
/// They typically use * in their render target. They exist solely so we can reuse them,
@@ -111,12 +94,9 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
// will fall back to the default if any of these are null
ui_style = ui_style2icon(owner.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
- toggle_palette = new()
- toggle_palette.set_hud(src)
- palette_down = new()
- palette_down.set_hud(src)
- palette_up = new()
- palette_up.set_hud(src)
+ add_screen_object(/atom/movable/screen/button_palette, HUD_MOB_TOGGLE_PALETTE)
+ add_screen_object(/atom/movable/screen/palette_scroll/down, HUD_MOB_PALETTE_DOWN)
+ add_screen_object(/atom/movable/screen/palette_scroll/up, HUD_MOB_PALETTE_UP)
hand_slots = list()
@@ -127,8 +107,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
screentip_color = preferences?.read_preference(/datum/preference/color/screentip_color)
screentips_enabled = preferences?.read_preference(/datum/preference/choiced/enable_screentips)
screentip_images = preferences?.read_preference(/datum/preference/toggle/screentip_images)
- screentip_text = new(null, src)
- static_inventory += screentip_text
+ screentip_text = add_screen_object(/atom/movable/screen/screentip, HUD_MOB_SCREENTIP)
for(var/mytype in subtypesof(/atom/movable/plane_master_controller))
var/atom/movable/plane_master_controller/controller_instance = new mytype(null,src)
@@ -146,8 +125,73 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
RegisterSignal(mymob, COMSIG_MOB_LOGOUT, PROC_REF(clear_client))
RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, PROC_REF(update_sightflags))
RegisterSignal(mymob, COMSIG_VIEWDATA_UPDATE, PROC_REF(on_viewdata_update))
+
+ initialize_screen_objects()
+ create_inventory_slots()
+ update_locked_slots()
+
update_sightflags(mymob, mymob.sight, NONE)
+/datum/hud/Destroy()
+ if(mymob.hud_used == src)
+ mymob.hud_used = null
+
+ QDEL_NULL(palette_actions)
+ QDEL_NULL(listed_actions)
+ QDEL_LIST(floating_actions)
+ screentip_text = null
+ screen_groups = null
+ inv_slots.Cut()
+ hand_slots.Cut()
+ QDEL_LIST_ASSOC_VAL(screen_objects)
+ QDEL_LIST_ASSOC_VAL(master_groups)
+ QDEL_LIST_ASSOC_VAL(plane_master_controllers)
+ mymob = null
+
+ return ..()
+
+/// Creates and registers a managed screen object
+/datum/hud/proc/add_screen_object(atom/movable/screen/new_object, hud_key, group_key = HUD_GROUP_STATIC, ui_icon, ui_loc, update_screen = FALSE)
+ if (ispath(new_object))
+ new_object = new new_object(null, src)
+
+ if (isnull(hud_key))
+ hud_key = REF(new_object)
+
+ if (!isnull(ui_icon))
+ new_object.icon = ui_icon
+
+ if (!isnull(ui_loc))
+ new_object.screen_loc = ui_loc
+
+ new_object.hud_key = hud_key
+ if (screen_objects[hud_key])
+ CRASH("Attempted to add a new [new_object] screen object to the [src] hud while an object with the same key [hud_key] is already present!")
+
+ screen_objects[hud_key] = new_object
+
+ if (group_key)
+ LAZYADD(screen_groups[group_key], new_object)
+ new_object.hud_group_key = group_key
+
+ if (update_screen)
+ show_hud(hud_version)
+ return new_object
+
+/// Removes a screen object and refreshes the hud. Can just be passed a key.
+/datum/hud/proc/remove_screen_object(atom/movable/screen/to_remove, update = TRUE)
+ if (!istype(to_remove))
+ to_remove = screen_objects[to_remove]
+ if (!to_remove)
+ return
+ qdel(to_remove)
+ if (update && !QDELETED(src))
+ show_hud(hud_version)
+
+/// Proc for children to spawn their screen object in
+/datum/hud/proc/initialize_screen_objects()
+ return
+
/datum/hud/proc/client_refresh(datum/source)
SIGNAL_HANDLER
var/client/client = mymob.canon_client
@@ -212,53 +256,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/datum/plane_master_group/group = master_groups[group_key]
group.build_planes_offset(src, new_offset)
-/datum/hud/Destroy()
- if(mymob.hud_used == src)
- mymob.hud_used = null
-
- QDEL_NULL(toggle_palette)
- QDEL_NULL(palette_down)
- QDEL_NULL(palette_up)
- QDEL_NULL(palette_actions)
- QDEL_NULL(listed_actions)
- QDEL_LIST(floating_actions)
-
- QDEL_LIST(static_inventory)
-
- // all already deleted by static inventory clear
- inv_slots.Cut()
- action_intent = null
- zone_select = null
- pull_icon = null
- rest_icon = null
- sleep_icon = null
- floor_change = null
- hand_slots.Cut()
-
- QDEL_LIST(toggleable_inventory)
- QDEL_LIST(hotkeybuttons)
- throw_icon = null
- resist_icon = null
- QDEL_LIST(infodisplay)
- open_containers = null
-
- healths = null
- stamina = null
- healthdoll = null
- spacesuit_hud = null
- hunger = null
- alien_plasma_display = null
- alien_queen_finder = null
-
- QDEL_LIST_ASSOC_VAL(master_groups)
- QDEL_LIST_ASSOC_VAL(plane_master_controllers)
- QDEL_LIST(always_visible_inventory)
- mymob = null
-
- QDEL_NULL(screentip_text)
-
- return ..()
-
/datum/hud/proc/on_plane_increase(datum/source, old_max_offset, new_max_offset)
SIGNAL_HANDLER
for(var/i in old_max_offset + 1 to new_max_offset)
@@ -314,75 +311,73 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
* * viewmob - what mob to show the hud to. Can be this hud's mob, can be another mob, can be null (will use this hud's mob if so)
*/
/datum/hud/proc/show_hud(version = 0, mob/viewmob)
- if(!ismob(mymob))
+ if (!ismob(mymob))
return FALSE
+
var/mob/screenmob = viewmob || mymob
- if(!screenmob.client)
+ if (!screenmob.client)
return FALSE
// This code is the absolute fucking worst, I want it to go die in a fire
// Seriously, why
+ // I'm sorry
screenmob.client.clear_screen()
screenmob.client.apply_clickcatcher()
var/display_hud_version = version
- if(!display_hud_version) //If 0 or blank, display the next hud version
+ if (!display_hud_version) // If 0 or blank, display the next hud version
display_hud_version = hud_version + 1
- if(display_hud_version > HUD_VERSIONS) //If the requested version number is greater than the available versions, reset back to the first version
- display_hud_version = 1
- switch(display_hud_version)
- if(HUD_STYLE_STANDARD) //Default HUD
- hud_shown = TRUE //Governs behavior of other procs
- if(static_inventory.len)
- screenmob.client.screen += static_inventory
- if(toggleable_inventory.len && screenmob.hud_used && screenmob.hud_used.inventory_shown)
- screenmob.client.screen += toggleable_inventory
- if(hotkeybuttons.len && !hotkey_ui_hidden)
- screenmob.client.screen += hotkeybuttons
- if(infodisplay.len)
- screenmob.client.screen += infodisplay
- if(always_visible_inventory.len)
- screenmob.client.screen += always_visible_inventory
- if(open_containers.len && screenmob == mymob) // Don't show open inventories to ghosts
- list_clear_nulls(open_containers)
- screenmob.client.screen += open_containers
- screenmob.client.screen += toggle_palette
+ // If the requested version number is greater than the available versions, reset back to the first version
+ if (display_hud_version > HUD_VERSIONS)
+ display_hud_version = HUD_STYLE_STANDARD
- if(HUD_STYLE_REDUCED) //Reduced HUD
- hud_shown = FALSE //Governs behavior of other procs
- if(static_inventory.len)
- screenmob.client.screen -= static_inventory
- if(toggleable_inventory.len)
- screenmob.client.screen -= toggleable_inventory
- if(hotkeybuttons.len)
- screenmob.client.screen -= hotkeybuttons
- if(infodisplay.len)
- screenmob.client.screen += infodisplay
- if(always_visible_inventory.len)
- screenmob.client.screen += always_visible_inventory
+ var/list/group_static = screen_groups[HUD_GROUP_STATIC]
+ var/list/group_toggleable = screen_groups[HUD_GROUP_TOGGLEABLE_INVENTORY]
+ var/list/group_hotkeys = screen_groups[HUD_GROUP_HOTKEYS]
+ var/list/group_info = screen_groups[HUD_GROUP_INFO]
+ var/list/group_screen = screen_groups[HUD_GROUP_SCREEN_OVERLAYS]
+ var/list/group_storage = screen_groups[HUD_GROUP_STORAGE]
+
+ // Screen overlays get added regardless of the HUD state
+ if (length(group_screen))
+ screenmob.client.screen += group_screen
+
+ var/atom/movable/screen/button_palette/palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
+ var/atom/movable/screen/action_intent = screen_objects[HUD_MOB_INTENTS]
+
+ switch (display_hud_version)
+ if (HUD_STYLE_STANDARD)
+ hud_shown = TRUE
+
+ if (length(group_static))
+ screenmob.client.screen += group_static
+ if (length(group_toggleable) && screenmob.hud_used && screenmob.hud_used.inventory_shown)
+ screenmob.client.screen += group_toggleable
+ if (length(group_hotkeys) && !hotkey_ui_hidden)
+ screenmob.client.screen += group_hotkeys
+ if (length(group_info))
+ screenmob.client.screen += group_info
+ if (length(group_storage))
+ screenmob.client.screen += group_storage
+
+ screenmob.client.screen += palette
+
+ if (HUD_STYLE_REDUCED)
+ hud_shown = FALSE
+
+ if (length(group_info))
+ screenmob.client.screen += group_info
+
+ // Hands are apart of the static group but still should be presetn in the reduced mode
+ for (var/atom/movable/screen/hand in hand_slots)
+ screenmob.client.screen += hand
- //These ones are a part of 'static_inventory', 'toggleable_inventory' or 'hotkeybuttons' but we want them to stay
- for(var/h in hand_slots)
- var/atom/movable/screen/hand = hand_slots[h]
- if(hand)
- screenmob.client.screen += hand
if(action_intent)
- screenmob.client.screen += action_intent //we want the intent switcher visible
- action_intent.screen_loc = ui_acti_alt //move this to the alternative position, where zone_select usually is.
+ screenmob.client.screen += action_intent
- if(HUD_STYLE_NOHUD) //No HUD
- hud_shown = FALSE //Governs behavior of other procs
- if(static_inventory.len)
- screenmob.client.screen -= static_inventory
- if(toggleable_inventory.len)
- screenmob.client.screen -= toggleable_inventory
- if(hotkeybuttons.len)
- screenmob.client.screen -= hotkeybuttons
- if(infodisplay.len)
- screenmob.client.screen -= infodisplay
- if(always_visible_inventory.len)
- screenmob.client.screen += always_visible_inventory
+ if (HUD_STYLE_NOHUD)
+ hud_shown = FALSE
hud_version = display_hud_version
persistent_inventory_update(screenmob)
@@ -442,12 +437,13 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
if (initial(ui_style) || ui_style == new_ui_style)
return
- for(var/atom/item in static_inventory + toggleable_inventory + hotkeybuttons + infodisplay + always_visible_inventory + inv_slots)
- if (item.icon == ui_style)
+ for(var/hud_key in screen_objects)
+ var/atom/item = screen_objects[hud_key]
+ if (item?.icon == ui_style)
item.icon = new_ui_style
ui_style = new_ui_style
- build_hand_slots()
+ build_hand_slots(update_hud = TRUE)
/datum/hud/proc/register_reuse(atom/movable/screen/reuse)
asset_refs_for_reuse += WEAKREF(reuse)
@@ -476,62 +472,80 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
else
to_chat(usr, span_warning("This mob type does not use a HUD."))
+/// Rebuilds our mob's hand slot screen elements
+/datum/hud/proc/build_hand_slots(update_hud = FALSE)
+ QDEL_LIST(hand_slots)
+ hand_slots = new /list(length(mymob.held_items))
-//(re)builds the hand ui slots, throwing away old ones
-//not really worth jugglying existing ones so we just scrap+rebuild
-//9/10 this is only called once per mob and only for 2 hands
-/datum/hud/proc/build_hand_slots()
- for(var/h in hand_slots)
- var/atom/movable/screen/inventory/hand/H = hand_slots[h]
- if(H)
- static_inventory -= H
- hand_slots = list()
- var/atom/movable/screen/inventory/hand/hand_box
- for(var/i in 1 to mymob.held_items.len)
- hand_box = new /atom/movable/screen/inventory/hand(null, src)
+ for(var/i in 1 to length(mymob.held_items))
+ var/atom/movable/screen/inventory/hand/hand_box = add_screen_object(/atom/movable/screen/inventory/hand, HUD_KEY_HAND_SLOT(i), HUD_GROUP_STATIC, ui_style, ui_hand_position(i))
hand_box.name = mymob.get_held_index_name(i)
- hand_box.icon = ui_style
hand_box.icon_state = "hand_[mymob.held_index_to_dir(i)]"
- hand_box.screen_loc = ui_hand_position(i)
hand_box.held_index = i
- hand_slots["[i]"] = hand_box
- static_inventory += hand_box
hand_box.update_appearance()
+ hand_slots[i] = hand_box
var/num_of_swaps = 0
- for(var/atom/movable/screen/swap_hand/swap_hands in static_inventory)
+ for(var/atom/movable/screen/swap_hand/swap_hands in screen_groups[HUD_GROUP_STATIC])
num_of_swaps += 1
var/hand_num = 1
- for(var/atom/movable/screen/swap_hand/swap_hands in static_inventory)
+ for(var/atom/movable/screen/swap_hand/swap_hands in screen_groups[HUD_GROUP_STATIC])
var/hand_ind = RIGHT_HANDS
if (num_of_swaps > 1)
hand_ind = IS_RIGHT_INDEX(hand_num) ? LEFT_HANDS : RIGHT_HANDS
swap_hands.screen_loc = ui_swaphand_position(mymob, hand_ind)
hand_num += 1
+
hand_num = 1
- for(var/atom/movable/screen/drop/swap_hands in static_inventory)
+
+ for(var/atom/movable/screen/drop/swap_hands in screen_groups[HUD_GROUP_STATIC])
var/hand_ind = LEFT_HANDS
if (num_of_swaps > 1)
hand_ind = IS_LEFT_INDEX(hand_num) ? LEFT_HANDS : RIGHT_HANDS
swap_hands.screen_loc = ui_swaphand_position(mymob, hand_ind)
hand_num += 1
- if(ismob(mymob) && mymob.hud_used == src)
+ if(update_hud && mymob?.hud_used == src)
show_hud(hud_version)
/// Handles dimming inventory slots that a mob can't equip items to in their current state
/datum/hud/proc/update_locked_slots()
return
+/// Creates inventory slot screen elements based on our assigned inventory_slots
+/datum/hud/proc/create_inventory_slots()
+ var/list/created_paths = inventory_slots
+ if (ispath(inventory_slots))
+ created_paths = valid_subtypesof(inventory_slots)
+
+ for (var/datum/inventory_slot/slot_type as anything in created_paths)
+ var/datum/inventory_slot/inv_slot = GLOB.inventory_slot_datums[slot_type]
+ if (!inv_slot)
+ stack_trace("[src] attempted to use an invalid inventory slot: [slot_type]")
+ continue
+ inv_slot.create_element(src)
+
+ update_inventory_slots()
+
+/// Updates all of our inventory slots
+/// Avoid calling directly in favor of specific update procs
+/datum/hud/proc/update_inventory_slots()
+ for(var/atom/movable/screen/inventory/inv in screen_groups[HUD_GROUP_STATIC] + screen_groups[HUD_GROUP_TOGGLEABLE_INVENTORY])
+ if(inv.slot_id)
+ inv_slots[TOBITSHIFT(inv.slot_id) + 1] = inv
+ inv.update_appearance()
+
/datum/hud/proc/position_action(atom/movable/screen/movable/action_button/button, position)
// This is kinda a hack, I'm sorry.
// Basically, FLOATING is never a valid position to pass into this proc. It exists as a generic marker for manually positioned buttons
// Not as a position to target
if(position == SCRN_OBJ_FLOATING)
return
+
if(button.location != SCRN_OBJ_DEFAULT)
hide_action(button)
+
switch(position)
if(SCRN_OBJ_DEFAULT) // Reset to the default
button.dump_save() // Nuke any existing saves
@@ -548,6 +562,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
floating_actions += button
button.screen_loc = position
position = SCRN_OBJ_FLOATING
+ var/atom/movable/screen/button_palette/toggle_palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
toggle_palette.update_state()
button.location = position
@@ -555,18 +570,24 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/datum/hud/proc/position_action_relative(atom/movable/screen/movable/action_button/button, atom/movable/screen/movable/action_button/relative_to)
if(button.location != SCRN_OBJ_DEFAULT)
hide_action(button)
+
switch(relative_to.location)
if(SCRN_OBJ_IN_LIST)
listed_actions.insert_action(button, listed_actions.index_of(relative_to))
+
if(SCRN_OBJ_IN_PALETTE)
palette_actions.insert_action(button, palette_actions.index_of(relative_to))
+
if(SCRN_OBJ_FLOATING) // If we don't have it as a define, this is a screen_loc, and we should be floating
floating_actions += button
var/client/our_client = mymob.canon_client
if(!our_client)
position_action(button, button.linked_action.default_button_position)
return
- button.screen_loc = get_valid_screen_location(relative_to.screen_loc, ICON_SIZE_ALL, our_client.view_size.getView()) // Asks for a location adjacent to our button that won't overflow the map
+
+ // Asks for a location adjacent to our button that won't overflow the map
+ button.screen_loc = get_valid_screen_location(relative_to.screen_loc, ICON_SIZE_ALL, our_client.view_size.getView())
+ var/atom/movable/screen/button_palette/toggle_palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
toggle_palette.update_state()
button.location = relative_to.location
@@ -576,32 +597,44 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
switch(button.location)
if(SCRN_OBJ_DEFAULT) // Invalid
CRASH("We just tried to hide an action buttion that somehow has the default position as its location, you done fucked up")
+
if(SCRN_OBJ_FLOATING)
floating_actions -= button
+ var/atom/movable/screen/button_palette/toggle_palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
toggle_palette.update_state()
+
if(SCRN_OBJ_IN_LIST)
listed_actions.remove_action(button)
+
if(SCRN_OBJ_IN_PALETTE)
palette_actions.remove_action(button)
+
button.screen_loc = null
/// Generates visual landings for all groups that the button is not a memeber of
/datum/hud/proc/generate_landings(atom/movable/screen/movable/action_button/button)
listed_actions.generate_landing()
palette_actions.generate_landing()
+ var/atom/movable/screen/button_palette/toggle_palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
toggle_palette.activate_landing()
/// Clears all currently visible landings
/datum/hud/proc/hide_landings()
listed_actions.clear_landing()
palette_actions.clear_landing()
+ var/atom/movable/screen/button_palette/toggle_palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
toggle_palette.disable_landing()
// Updates any existing "owned" visuals, ensures they continue to be visible
/datum/hud/proc/update_our_owner()
+ var/atom/movable/screen/button_palette/toggle_palette = screen_objects[HUD_MOB_TOGGLE_PALETTE]
+ var/atom/movable/screen/palette_scroll/palette_down = screen_objects[HUD_MOB_PALETTE_DOWN]
+ var/atom/movable/screen/palette_scroll/palette_up = screen_objects[HUD_MOB_PALETTE_UP]
+
toggle_palette.refresh_owner()
palette_down.refresh_owner()
palette_up.refresh_owner()
+
listed_actions.update_landing()
palette_actions.update_landing()
@@ -628,214 +661,3 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
action.ShowTo(mymob)
else
position_action(button, button.location)
-
-/datum/action_group
- /// The hud we're owned by
- var/datum/hud/owner
- /// The actions we're managing
- var/list/atom/movable/screen/movable/action_button/actions
- /// The initial vertical offset of our action buttons
- var/north_offset = 0
- /// The pixel vertical offset of our action buttons
- var/pixel_north_offset = 0
- /// Max amount of buttons we can have per row
- /// Indexes at 1
- var/column_max = 0
- /// How far "ahead" of the first row we start. Lets us "scroll" our rows
- /// Indexes at 1
- var/row_offset = 0
- /// How many rows of actions we can have at max before we just stop hiding
- /// Indexes at 1
- var/max_rows = INFINITY
- /// The screen location we go by
- var/location
- /// Our landing screen object
- var/atom/movable/screen/action_landing/landing
-
-/datum/action_group/New(datum/hud/owner)
- ..()
- actions = list()
- src.owner = owner
-
-/datum/action_group/Destroy()
- owner = null
- QDEL_NULL(landing)
- QDEL_LIST(actions)
- return ..()
-
-/datum/action_group/proc/insert_action(atom/movable/screen/action, index)
- if(action in actions)
- if(actions[index] == action)
- return
- actions -= action // Don't dupe, come on
- if(!index)
- index = length(actions) + 1
- index = min(length(actions) + 1, index)
- actions.Insert(index, action)
- refresh_actions()
-
-/datum/action_group/proc/remove_action(atom/movable/screen/action)
- actions -= action
- refresh_actions()
-
-/datum/action_group/proc/refresh_actions()
-
- // We don't use size() here because landings are not canon
- var/total_rows = ROUND_UP(length(actions) / column_max)
- total_rows -= max_rows // Lets get the amount of rows we're off from our max
- row_offset = clamp(row_offset, 0, total_rows) // You're not allowed to offset so far that we have a row of blank space
-
- var/button_number = 0
- for(var/atom/movable/screen/button as anything in actions)
- var/postion = ButtonNumberToScreenCoords(button_number )
- button.screen_loc = postion
- button_number++
-
- if(landing)
- var/postion = ButtonNumberToScreenCoords(button_number, landing = TRUE) // Need a good way to count buttons off screen, but allow this to display in the right place if it's being placed with no concern for dropdown
- landing.screen_loc = postion
- button_number++
-
-/// Accepts a number represeting our position in the group, indexes at 0 to make the math nicer
-/datum/action_group/proc/ButtonNumberToScreenCoords(number, landing = FALSE)
- var/row = round(number / column_max)
- row -= row_offset // If you're less then 0, you don't get to render, this lets us "scroll" rows ya feel?
- if(row < 0)
- return null
-
- // Could use >= here, but I think it's worth noting that the two start at different places, since row is based on number here
- if(row > max_rows - 1)
- if(!landing) // If you're not a landing, go away please. thx
- return null
- // We always want to render landings, even if their action button can't be displayed.
- // So we set a row equal to the max amount of rows + 1. Willing to overrun that max slightly to properly display the landing spot
- row = max_rows // Remembering that max_rows indexes at 1, and row indexes at 0
-
- // We're going to need to set our column to match the first item in the last row, so let's set number properly now
- number = row * column_max
-
- var/visual_row = row + north_offset
- var/coord_row = visual_row ? "-[visual_row]" : "+0"
-
- var/visual_column = number % column_max
- var/coord_col = "+[visual_column]"
- var/coord_col_offset = 4 + 2 * (visual_column + 1)
- return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:-[pixel_north_offset]"
-
-/datum/action_group/proc/check_against_view()
- var/owner_view = owner?.mymob?.canon_client?.view
- if(!owner_view)
- return
- // Unlikey as it is, we may have been changed. Want to start from our target position and fail down
- column_max = initial(column_max)
- // Convert our viewer's view var into a workable offset
- var/list/view_size = view_to_pixels(owner_view)
-
- // We're primarially concerned about width here, if someone makes us 1x2000 I wish them a swift and watery death
- var/furthest_screen_loc = ButtonNumberToScreenCoords(column_max - 1)
- var/list/offsets = screen_loc_to_offset(furthest_screen_loc, owner_view)
- if(offsets[1] > ICON_SIZE_X && offsets[1] < view_size[1] && offsets[2] > ICON_SIZE_Y && offsets[2] < view_size[2]) // We're all good
- return
-
- for(column_max in column_max - 1 to 1 step -1) // Yes I could do this by unwrapping ButtonNumberToScreenCoords, but I don't feel like it
- var/tested_screen_loc = ButtonNumberToScreenCoords(column_max)
- offsets = screen_loc_to_offset(tested_screen_loc, owner_view)
- // We've found a valid max length, pack it in
- if(offsets[1] > ICON_SIZE_X && offsets[1] < view_size[1] && offsets[2] > ICON_SIZE_Y && offsets[2] < view_size[2])
- break
- // Use our newly resized column max
- refresh_actions()
-
-/// Returns the amount of objects we're storing at the moment
-/datum/action_group/proc/size()
- var/amount = length(actions)
- if(landing)
- amount += 1
- return amount
-
-/datum/action_group/proc/index_of(atom/movable/screen/get_location)
- return actions.Find(get_location)
-
-/// Generates a landing object that can be dropped on to join this group
-/datum/action_group/proc/generate_landing()
- if(landing)
- return
- landing = new()
- landing.set_owner(src)
- refresh_actions()
-
-/// Clears any landing objects we may currently have
-/datum/action_group/proc/clear_landing()
- QDEL_NULL(landing)
-
-/datum/action_group/proc/update_landing()
- if(!landing)
- return
- landing.refresh_owner()
-
-/datum/action_group/proc/scroll(amount)
- row_offset += amount
- refresh_actions()
-
-/datum/action_group/palette
- north_offset = 2
- column_max = 3
- max_rows = 3
- location = SCRN_OBJ_IN_PALETTE
-
-/datum/action_group/palette/insert_action(atom/movable/screen/action, index)
- . = ..()
- var/atom/movable/screen/button_palette/palette = owner.toggle_palette
- palette.play_item_added()
-
-/datum/action_group/palette/remove_action(atom/movable/screen/action)
- . = ..()
- var/atom/movable/screen/button_palette/palette = owner.toggle_palette
- palette.play_item_removed()
- if(!length(actions))
- palette.set_expanded(FALSE)
-
-/datum/action_group/palette/refresh_actions()
- var/atom/movable/screen/button_palette/palette = owner.toggle_palette
- var/atom/movable/screen/palette_scroll/scroll_down = owner.palette_down
- var/atom/movable/screen/palette_scroll/scroll_up = owner.palette_up
-
- var/actions_above = round((owner.listed_actions.size() - 1) / owner.listed_actions.column_max)
- north_offset = initial(north_offset) + actions_above
-
- palette.screen_loc = ui_action_palette_offset(actions_above)
- var/action_count = length(owner?.mymob?.actions)
- var/our_row_count = round((length(actions) - 1) / column_max)
- if(!action_count)
- palette.screen_loc = null
-
- if(palette.expanded && action_count && our_row_count >= max_rows)
- scroll_down.screen_loc = ui_palette_scroll_offset(actions_above)
- scroll_up.screen_loc = ui_palette_scroll_offset(actions_above)
- else
- scroll_down.screen_loc = null
- scroll_up.screen_loc = null
-
- return ..()
-
-/datum/action_group/palette/ButtonNumberToScreenCoords(number, landing)
- var/atom/movable/screen/button_palette/palette = owner.toggle_palette
- if(palette.expanded)
- return ..()
-
- if(!landing)
- return null
-
- // We only render the landing in this case, so we force it to be the second item displayed (Second rather then first since it looks nicer)
- // Remember the number var indexes at 0
- return ..(1 + (row_offset * column_max), landing)
-
-
-/datum/action_group/listed
- pixel_north_offset = 6
- column_max = 10
- location = SCRN_OBJ_IN_LIST
-
-/datum/action_group/listed/refresh_actions()
- . = ..()
- owner?.palette_actions.refresh_actions() // We effect them, so we gotta refresh em
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 4e6ef6f0e2b..d8d55dccf16 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -1,328 +1,38 @@
-/atom/movable/screen/human
- icon = 'icons/hud/screen_midnight.dmi'
+/datum/hud/human
+ inventory_slots = /datum/inventory_slot/human
-/atom/movable/screen/human/toggle
- name = "toggle"
- icon_state = "toggle"
- base_icon_state = "toggle"
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/human/toggle/Click()
-
- var/mob/targetmob = usr
-
- if(isobserver(usr))
- if(ishuman(usr.client.eye) && (usr.client.eye != usr))
- var/mob/M = usr.client.eye
- targetmob = M
-
- if(usr.hud_used.inventory_shown && targetmob.hud_used)
- usr.hud_used.inventory_shown = FALSE
- usr.client.screen -= targetmob.hud_used.toggleable_inventory
- else
- usr.hud_used.inventory_shown = TRUE
- usr.client.screen += targetmob.hud_used.toggleable_inventory
-
- targetmob.hud_used.hidden_inventory_update(usr)
- update_appearance()
-
-/atom/movable/screen/human/toggle/update_icon_state()
- icon_state = "[base_icon_state][hud?.inventory_shown ? "_active" : ""]"
- return ..()
-
-/atom/movable/screen/ling
- icon = 'icons/hud/screen_changeling.dmi'
-
-/atom/movable/screen/ling/chems
- name = "chemical storage"
- icon_state = "power_display"
- screen_loc = ui_lingchemdisplay
- ///Boolean on whether a mouse is being hovered over us right now.
- var/hovering = FALSE
-
-/atom/movable/screen/ling/chems/Click(location, control, params)
+/datum/hud/human/initialize_screen_objects()
. = ..()
- to_chat(usr, span_notice("Shows you how many chemicals you have. While hovering over this, it will show the max amount of chemicals you can hold."))
-
-/atom/movable/screen/ling/chems/MouseEntered(location,control,params)
- if(usr != get_mob())
- return
- var/datum/antagonist/changeling/antagonist_datum = IS_CHANGELING(hud.mymob)
- if(!antagonist_datum)
- return
- . = ..()
- hovering = TRUE
- antagonist_datum.update_chemical_hud()
-
-/atom/movable/screen/ling/chems/MouseExited(location, control, params)
- if(usr != get_mob())
- return
- var/datum/antagonist/changeling/antagonist_datum = IS_CHANGELING(hud.mymob)
- if(!antagonist_datum)
- return
- . = ..()
- hovering = FALSE
- antagonist_datum.update_chemical_hud(antagonist_datum.chem_charges)
-
-/atom/movable/screen/ling/sting
- name = "current sting"
- screen_loc = ui_lingstingdisplay
- invisibility = INVISIBILITY_ABSTRACT
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/ling/sting/Click()
- if(isobserver(usr))
- return
- var/mob/living/carbon/carbon_user = usr
- carbon_user.unset_sting()
-
-/datum/hud/human/New(mob/living/carbon/human/owner)
- ..()
-
var/atom/movable/screen/using
- var/atom/movable/screen/inventory/inv_box
-
- using = new /atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_human_language
- static_inventory += using
-
- using = new /atom/movable/screen/navigate(null, src)
- using.icon = ui_style
- using.screen_loc = ui_human_navigate
- static_inventory += using
-
- using = new /atom/movable/screen/area_creator(null, src)
- using.icon = ui_style
- using.screen_loc = ui_human_area
- static_inventory += using
-
- using = new /atom/movable/screen/memories(null, src)
- using.icon = ui_style
- using.screen_loc = ui_human_memories_area
- static_inventory += using
-
- action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
- action_intent.icon = ui_style
- action_intent.screen_loc = ui_combat_toggle
- static_inventory += action_intent
-
- floor_change = new /atom/movable/screen/floor_changer/vertical(null, src)
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_human_floor_changer
- static_inventory += floor_change
-
- using = new /atom/movable/screen/mov_intent(null, src)
- using.icon = ui_style
- using.icon_state = (owner.move_intent == MOVE_INTENT_RUN ? "running" : "walking")
- using.screen_loc = ui_movi
- static_inventory += using
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "uniform"
- inv_box.icon = ui_style
- inv_box.slot_id = ITEM_SLOT_ICLOTHING
- inv_box.icon_state = "uniform"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_iclothing
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "suit"
- inv_box.icon = ui_style
- inv_box.slot_id = ITEM_SLOT_OCLOTHING
- inv_box.icon_state = "suit"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_oclothing
- toggleable_inventory += inv_box
-
+ // Static elements
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, HUD_GROUP_STATIC, ui_style, ui_human_language)
+ add_screen_object(/atom/movable/screen/navigate, HUD_MOB_NAVIGATE_MENU, HUD_GROUP_STATIC, ui_style, ui_human_navigate)
+ add_screen_object(/atom/movable/screen/area_creator, HUD_MOB_AREA_CREATOR, HUD_GROUP_STATIC, ui_style, ui_human_area)
+ add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/floor_changer/vertical, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_human_floor_changer)
+ add_screen_object(/atom/movable/screen/mov_intent, HUD_MOB_MOVE_INTENT, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/drop, HUD_MOB_DROP, HUD_GROUP_STATIC, ui_style, ui_swaphand_position(mymob, 1))
+ add_screen_object(/atom/movable/screen/human/toggle, HUD_HUMAN_TOGGLE_INVENTORY, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/rest, HUD_MOB_REST, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/sleep, HUD_MOB_SLEEP, HUD_GROUP_HOTKEYS, ui_style, ui_above_throw)
+ add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style, ui_above_movement_top)
+ add_screen_object(/atom/movable/screen/zone_sel, HUD_MOB_ZONE_SELECTOR, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_human_memories_menu)
build_hand_slots()
- using = new /atom/movable/screen/drop(null, src)
- using.icon = ui_style
- using.screen_loc = ui_swaphand_position(owner, 1)
- static_inventory += using
-
- using = new /atom/movable/screen/swap_hand(null, src)
- using.icon = ui_style
+ using = add_screen_object(/atom/movable/screen/swap_hand, HUD_MOB_SWAPHAND_2, HUD_GROUP_STATIC, ui_style, ui_swaphand_position(mymob, 2))
using.icon_state = "act_swap"
- using.screen_loc = ui_swaphand_position(owner, 2)
- static_inventory += using
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "id"
- inv_box.icon = ui_style
- inv_box.icon_state = "id"
- inv_box.icon_full = "template_small"
- inv_box.screen_loc = ui_id
- inv_box.slot_id = ITEM_SLOT_ID
- static_inventory += inv_box
+ // Hotkey buttons
+ add_screen_object(/atom/movable/screen/resist, HUD_MOB_RESIST, HUD_GROUP_HOTKEYS, ui_style)
+ add_screen_object(/atom/movable/screen/throw_catch, HUD_MOB_THROW, HUD_GROUP_HOTKEYS, ui_style)
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "mask"
- inv_box.icon = ui_style
- inv_box.icon_state = "mask"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_mask
- inv_box.slot_id = ITEM_SLOT_MASK
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "neck"
- inv_box.icon = ui_style
- inv_box.icon_state = "neck"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_neck
- inv_box.slot_id = ITEM_SLOT_NECK
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "back"
- inv_box.icon = ui_style
- inv_box.icon_state = "back"
- inv_box.icon_full = "template_small"
- inv_box.screen_loc = ui_back
- inv_box.slot_id = ITEM_SLOT_BACK
- static_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "left pocket"
- inv_box.icon = ui_style
- inv_box.icon_state = "pocket"
- inv_box.icon_full = "template_small"
- inv_box.screen_loc = ui_storage1
- inv_box.slot_id = ITEM_SLOT_LPOCKET
- static_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "right pocket"
- inv_box.icon = ui_style
- inv_box.icon_state = "pocket"
- inv_box.icon_full = "template_small"
- inv_box.screen_loc = ui_storage2
- inv_box.slot_id = ITEM_SLOT_RPOCKET
- static_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "suit storage"
- inv_box.icon = ui_style
- inv_box.icon_state = "suit_storage"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_sstore1
- inv_box.slot_id = ITEM_SLOT_SUITSTORE
- static_inventory += inv_box
-
- resist_icon = new /atom/movable/screen/resist(null, src)
- resist_icon.icon = ui_style
- resist_icon.screen_loc = ui_above_movement
- hotkeybuttons += resist_icon
-
- using = new /atom/movable/screen/human/toggle(null, src)
- using.icon = ui_style
- using.screen_loc = ui_inventory
- static_inventory += using
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "gloves"
- inv_box.icon = ui_style
- inv_box.icon_state = "gloves"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_gloves
- inv_box.slot_id = ITEM_SLOT_GLOVES
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "eyes"
- inv_box.icon = ui_style
- inv_box.icon_state = "glasses"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_glasses
- inv_box.slot_id = ITEM_SLOT_EYES
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "ears"
- inv_box.icon = ui_style
- inv_box.icon_state = "ears"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_ears
- inv_box.slot_id = ITEM_SLOT_EARS
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "head"
- inv_box.icon = ui_style
- inv_box.icon_state = "head"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_head
- inv_box.slot_id = ITEM_SLOT_HEAD
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "shoes"
- inv_box.icon = ui_style
- inv_box.icon_state = "shoes"
- inv_box.icon_full = "template"
- inv_box.screen_loc = ui_shoes
- inv_box.slot_id = ITEM_SLOT_FEET
- toggleable_inventory += inv_box
-
- inv_box = new /atom/movable/screen/inventory(null, src)
- inv_box.name = "belt"
- inv_box.icon = ui_style
- inv_box.icon_state = "belt"
- inv_box.icon_full = "template_small"
- inv_box.screen_loc = ui_belt
- inv_box.slot_id = ITEM_SLOT_BELT
- static_inventory += inv_box
-
- throw_icon = new /atom/movable/screen/throw_catch(null, src)
- throw_icon.icon = ui_style
- throw_icon.screen_loc = ui_drop_throw
- hotkeybuttons += throw_icon
-
- rest_icon = new /atom/movable/screen/rest(null, src)
- rest_icon.icon = ui_style
- rest_icon.screen_loc = ui_rest
- rest_icon.update_appearance()
- static_inventory += rest_icon
-
- sleep_icon = new /atom/movable/screen/sleep(null, src)
- sleep_icon.icon = ui_style
- sleep_icon.screen_loc = ui_above_throw
-
- spacesuit_hud = new /atom/movable/screen/spacesuit(null, src)
- infodisplay += spacesuit_hud
-
- healths = new /atom/movable/screen/healths(null, src)
- infodisplay += healths
-
- hunger = new /atom/movable/screen/hunger(null, src)
- infodisplay += hunger
-
- healthdoll = new /atom/movable/screen/healthdoll/human(null, src)
- infodisplay += healthdoll
-
- stamina = new /atom/movable/screen/stamina(null, src)
- infodisplay += stamina
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.screen_loc = ui_above_movement_top
- pull_icon.update_appearance()
- static_inventory += pull_icon
-
- zone_select = new /atom/movable/screen/zone_sel(null, src)
- zone_select.icon = ui_style
- zone_select.update_appearance()
- static_inventory += zone_select
-
- for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory))
- if(inv.slot_id)
- inv_slots[TOBITSHIFT(inv.slot_id) + 1] = inv
- inv.update_appearance()
-
- update_locked_slots()
+ // Info
+ add_screen_object(/atom/movable/screen/spacesuit, HUD_MOB_SPACESUIT, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/healthdoll/human, HUD_MOB_HEALTHDOLL, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/stamina, HUD_MOB_STAMINA, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/healths, HUD_MOB_HEALTH, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/hunger, HUD_MOB_HUNGER, HUD_GROUP_INFO)
/datum/hud/human/update_locked_slots()
if(!mymob)
@@ -355,8 +65,9 @@
if(eyes?.no_glasses)
blocked_slots |= ITEM_SLOT_EYES
- for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory))
- if(!inv.slot_id)
+ for(var/slot_key in screen_objects)
+ var/atom/movable/screen/inventory/inv = screen_objects[slot_key]
+ if(!istype(inv) || !inv.slot_id)
continue
inv.alpha = (blocked_slots & inv.slot_id) ? 128 : initial(inv.alpha)
@@ -415,8 +126,6 @@
if(H.head)
screenmob.client.screen -= H.head
-
-
/datum/hud/human/persistent_inventory_update(mob/viewer)
if(!mymob)
return
@@ -468,15 +177,131 @@
I.screen_loc = null
screenmob.client.screen -= I
-
/mob/living/carbon/human/verb/toggle_hotkey_verbs()
set category = "OOC"
set name = "Toggle hotkey buttons"
set desc = "This disables or enables the user interface buttons which can be used with hotkeys."
if(hud_used.hotkey_ui_hidden)
- client.screen += hud_used.hotkeybuttons
+ client.screen += hud_used.screen_groups[HUD_GROUP_HOTKEYS]
hud_used.hotkey_ui_hidden = FALSE
else
- client.screen -= hud_used.hotkeybuttons
+ client.screen -= hud_used.screen_groups[HUD_GROUP_HOTKEYS]
hud_used.hotkey_ui_hidden = TRUE
+
+/datum/inventory_slot/human
+ abstract_type = /datum/inventory_slot/human
+
+/datum/inventory_slot/human/uniform
+ name = "uniform"
+ slot_id = ITEM_SLOT_ICLOTHING
+ icon_state = "uniform"
+ icon_full = "template"
+ screen_loc = ui_iclothing
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/suit
+ name = "suit"
+ slot_id = ITEM_SLOT_OCLOTHING
+ icon_state = "suit"
+ icon_full = "template"
+ screen_loc = ui_oclothing
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/id
+ name = "id"
+ icon_state = "id"
+ icon_full = "template_small"
+ screen_loc = ui_id
+ slot_id = ITEM_SLOT_ID
+
+/datum/inventory_slot/human/mask
+ name = "mask"
+ icon_state = "mask"
+ icon_full = "template"
+ screen_loc = ui_mask
+ slot_id = ITEM_SLOT_MASK
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/neck
+ name = "neck"
+ icon_state = "neck"
+ icon_full = "template"
+ screen_loc = ui_neck
+ slot_id = ITEM_SLOT_NECK
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/back
+ name = "back"
+ icon_state = "back"
+ icon_full = "template_small"
+ screen_loc = ui_back
+ slot_id = ITEM_SLOT_BACK
+
+/datum/inventory_slot/human/l_pocket
+ name = "left pocket"
+ icon_state = "pocket"
+ icon_full = "template_small"
+ screen_loc = ui_storage1
+ slot_id = ITEM_SLOT_LPOCKET
+
+/datum/inventory_slot/human/r_pocket
+ name = "right pocket"
+ icon_state = "pocket"
+ icon_full = "template_small"
+ screen_loc = ui_storage2
+ slot_id = ITEM_SLOT_RPOCKET
+
+/datum/inventory_slot/human/suit_storage
+ name = "suit storage"
+ icon_state = "suit_storage"
+ icon_full = "template"
+ screen_loc = ui_sstore1
+ slot_id = ITEM_SLOT_SUITSTORE
+
+/datum/inventory_slot/human/gloves
+ name = "gloves"
+ icon_state = "gloves"
+ icon_full = "template"
+ screen_loc = ui_gloves
+ slot_id = ITEM_SLOT_GLOVES
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/eyes
+ name = "eyes"
+ icon_state = "glasses"
+ icon_full = "template"
+ screen_loc = ui_glasses
+ slot_id = ITEM_SLOT_EYES
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/ears
+ name = "ears"
+ icon_state = "ears"
+ icon_full = "template"
+ screen_loc = ui_ears
+ slot_id = ITEM_SLOT_EARS
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/head
+ name = "head"
+ icon_state = "head"
+ icon_full = "template"
+ screen_loc = ui_head
+ slot_id = ITEM_SLOT_HEAD
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/shoes
+ name = "shoes"
+ icon_state = "shoes"
+ icon_full = "template"
+ screen_loc = ui_shoes
+ slot_id = ITEM_SLOT_FEET
+ screen_group = HUD_GROUP_TOGGLEABLE_INVENTORY
+
+/datum/inventory_slot/human/belt
+ name = "belt"
+ icon_state = "belt"
+ icon_full = "template_small"
+ screen_loc = ui_belt
+ slot_id = ITEM_SLOT_BELT
diff --git a/code/_onclick/hud/inventory_slot.dm b/code/_onclick/hud/inventory_slot.dm
new file mode 100644
index 00000000000..af3307be551
--- /dev/null
+++ b/code/_onclick/hud/inventory_slot.dm
@@ -0,0 +1,35 @@
+GLOBAL_LIST_INIT(inventory_slot_datums, initialize_inventory_slots())
+
+/proc/initialize_inventory_slots()
+ var/list/slot_types = list()
+ for (var/slot_type in valid_subtypesof(/datum/inventory_slot))
+ slot_types[slot_type] = new slot_type()
+ return slot_types
+
+/// Inventory slot datum (singleton) which holds data about an inventory screen element
+/datum/inventory_slot
+ abstract_type = /datum/inventory_slot
+ /// Name assigned to the screen element
+ var/name = "error"
+ /// Should the slot inherit HUD's ui_style?
+ var/inherit_style = TRUE
+ /// Slot ID of the element
+ var/slot_id = NONE
+ /// Icon state of the element with nothing in it/unassigned icon_full
+ var/icon_state = "template"
+ /// Icon state of the element when it has an item in it
+ var/icon_full = "template"
+ /// Location of the element
+ var/screen_loc = null
+ /// Screen group to which the element is assigned
+ var/screen_group = HUD_GROUP_STATIC
+ /// Typepath of the screen element
+ var/screen_type = /atom/movable/screen/inventory
+
+/datum/inventory_slot/proc/create_element(datum/hud/hud)
+ var/atom/movable/screen/inventory/inv_box = hud.add_screen_object(screen_type, HUD_KEY_ITEM_SLOT(slot_id), screen_group, inherit_style ? hud.ui_style : null, screen_loc)
+ inv_box.name = name
+ inv_box.icon_state = icon_state
+ inv_box.icon_full = icon_full
+ inv_box.slot_id = slot_id
+ return inv_box
diff --git a/code/_onclick/hud/living.dm b/code/_onclick/hud/living.dm
index e9237d6dbb5..cd1575908e0 100644
--- a/code/_onclick/hud/living.dm
+++ b/code/_onclick/hud/living.dm
@@ -1,37 +1,9 @@
-/datum/hud/living/New(mob/living/owner)
- ..()
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_living_pull
- static_inventory += pull_icon
-
- action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
- action_intent.icon = ui_style
- action_intent.screen_loc = ui_basic_combat_toggle
- static_inventory += action_intent
-
- floor_change = new /atom/movable/screen/floor_changer(null, src)
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_floor_change
- static_inventory += floor_change
-
- var/atom/movable/screen/using
-
- using = new /atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_basic_language_menu
- static_inventory += using
-
- using = new /atom/movable/screen/memories(null, src)
- using.icon = ui_style
- using.screen_loc = ui_basic_memories_menu
- static_inventory += using
-
- //mob health doll assumes whatever sprite the mob is
- healthdoll = new /atom/movable/screen/healthdoll/living(null, src)
- infodisplay += healthdoll
-
- stamina = new /atom/movable/screen/stamina(null, src)
- infodisplay += stamina
+/datum/hud/living/initialize_screen_objects()
+ . = ..()
+ add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style, ui_living_pull)
+ add_screen_object(/atom/movable/screen/combattoggle/flashy, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style, ui_basic_combat_toggle)
+ add_screen_object(/atom/movable/screen/healthdoll/living, HUD_MOB_HEALTHDOLL, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/stamina, HUD_MOB_STAMINA, HUD_GROUP_INFO)
+ add_screen_object(/atom/movable/screen/floor_changer, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_floor_change)
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, HUD_GROUP_STATIC, ui_style, ui_basic_language_menu)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_basic_memories_menu)
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index 0b247aaa7da..fa991657de7 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -1,5 +1,3 @@
-#define SHUTTER_MOVEMENT_DURATION 0.4 SECONDS
-#define SHUTTER_WAIT_DURATION 0.2 SECONDS
/// Maximum number of station trait buttons we will display, please think hard before creating scenarios where there are more than this
#define MAX_STATION_TRAIT_BUTTONS_VERTICAL 3
#define TRAIT_BUTTON_Y_ORIGIN 397
@@ -18,13 +16,11 @@
if (!owner?.client || owner.client.interviewee)
return
- var/list/buttons = subtypesof(/atom/movable/screen/lobby)
- for (var/atom/movable/screen/lobby/lobbyscreen as anything in buttons)
+ for (var/atom/movable/screen/lobby/lobbyscreen as anything in subtypesof(/atom/movable/screen/lobby))
if (!initial(lobbyscreen.always_available))
continue
- lobbyscreen = new lobbyscreen(our_hud = src)
+ lobbyscreen = add_screen_object(lobbyscreen, HUD_KEY_NEW_PLAYER(lobbyscreen))
lobbyscreen.SlowInit()
- static_inventory += lobbyscreen
if (!lobbyscreen.always_shown)
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_COLLAPSED, TYPE_PROC_REF(/atom/movable/screen/lobby, collapse_button))
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_EXPANDED, TYPE_PROC_REF(/atom/movable/screen/lobby, expand_button))
@@ -32,9 +28,8 @@
if (!owner.client.is_localhost())
return
- var/atom/movable/screen/lobby/button/start_now/start_button = new(our_hud = src)
+ var/atom/movable/screen/lobby/button/start_now/start_button = add_screen_object(/atom/movable/screen/lobby/button/start_now, HUD_NEW_PLAYER_START_NOW)
start_button.SlowInit()
- static_inventory += start_button
start_button.RegisterSignal(src, COMSIG_HUD_LOBBY_COLLAPSED, TYPE_PROC_REF(/atom/movable/screen/lobby, collapse_button))
start_button.RegisterSignal(src, COMSIG_HUD_LOBBY_EXPANDED, TYPE_PROC_REF(/atom/movable/screen/lobby, expand_button))
@@ -52,9 +47,8 @@
continue
if(LAZYACCESS(shown_station_trait_buttons, trait))
continue
- var/atom/movable/screen/lobby/button/sign_up/sign_up_button = new(our_hud = src)
+ var/atom/movable/screen/lobby/button/sign_up/sign_up_button = add_screen_object(/atom/movable/screen/lobby/button/sign_up, HUD_NEW_PLAYER_SIGN_UP)
trait.setup_lobby_button(sign_up_button)
- static_inventory |= sign_up_button
LAZYSET(shown_station_trait_buttons, trait, sign_up_button)
RegisterSignal(trait, COMSIG_QDELETING, PROC_REF(remove_station_trait_button))
@@ -93,734 +87,9 @@
return
LAZYREMOVE(shown_station_trait_buttons, trait)
UnregisterSignal(trait, COMSIG_QDELETING)
- static_inventory -= button
qdel(button)
place_station_trait_buttons()
-/atom/movable/screen/lobby
- plane = SPLASHSCREEN_PLANE
- layer = LOBBY_MENU_LAYER
- screen_loc = "TOP,CENTER"
- /// Whether this HUD element can be hidden from the client's "screen" (moved off-screen) or not
- var/always_shown = FALSE
- /// If true we will create this button every time the HUD is generated
- var/always_available = TRUE
-
-///Set the HUD in New, as lobby screens are made before Atoms are Initialized.
-/atom/movable/screen/lobby/New(loc, datum/hud/our_hud, ...)
- set_new_hud(our_hud)
- return ..()
-
-///Run sleeping actions after initialize
-/atom/movable/screen/lobby/proc/SlowInit()
- return
-
-///Animates moving the button off-screen
-/atom/movable/screen/lobby/proc/collapse_button()
- SIGNAL_HANDLER
- //wait for the shutter to come down
- animate(src, transform = transform, time = SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
- //then pull the buttons up with the shutter
- animate(transform = transform.Translate(x = 0, y = 146), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
-
-///Animates moving the button back into place
-/atom/movable/screen/lobby/proc/expand_button()
- SIGNAL_HANDLER
- //the buttons are off-screen, so we sync them up to come down with the shutter
- animate(src, transform = matrix(), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_OUT)
-
-/atom/movable/screen/lobby/background
- icon = 'icons/hud/lobby/background.dmi'
- icon_state = "background"
- layer = LOBBY_BACKGROUND_LAYER
- screen_loc = "TOP,CENTER:-61"
-
-/atom/movable/screen/lobby/button
- mouse_over_pointer = MOUSE_HAND_POINTER
- ///Is the button currently enabled?
- VAR_PROTECTED/enabled = TRUE
- ///Is the button currently being hovered over with the mouse?
- var/highlighted = FALSE
- ///Should this button play the select sound?
- var/select_sound_play = TRUE
-
-/atom/movable/screen/lobby/button/Click(location, control, params)
- if(usr != get_mob())
- return
-
- if(!usr.client || usr.client.interviewee)
- return
-
- . = ..()
-
- if(!enabled)
- return
- flick("[base_icon_state]_pressed", src)
- if(select_sound_play)
- var/sound/ui_select_sound = sound('sound/misc/menu/ui_select1.ogg')
- ui_select_sound.frequency = get_rand_frequency_low_range()
- SEND_SOUND(hud.mymob, ui_select_sound)
- update_appearance(UPDATE_ICON)
- return TRUE
-
-/atom/movable/screen/lobby/button/MouseEntered(location,control,params)
- if(usr != get_mob())
- return
-
- if(!usr.client || usr.client.interviewee)
- return
-
- . = ..()
- highlighted = TRUE
- update_appearance(UPDATE_ICON)
-
-/atom/movable/screen/lobby/button/MouseExited()
- if(usr != get_mob())
- return
-
- if(!usr.client || usr.client.interviewee)
- return
-
- . = ..()
- highlighted = FALSE
- update_appearance(UPDATE_ICON)
-
-/atom/movable/screen/lobby/button/update_icon(updates)
- . = ..()
- if(!enabled)
- icon_state = "[base_icon_state]_disabled"
- return
- else if(highlighted)
- icon_state = "[base_icon_state]_highlighted"
- return
- icon_state = base_icon_state
-
-///Updates the button's status: TRUE to enable interaction with the button, FALSE to disable
-/atom/movable/screen/lobby/button/proc/set_button_status(status)
- if(status == enabled)
- return FALSE
- enabled = status
- update_appearance(UPDATE_ICON)
- mouse_over_pointer = enabled ? MOUSE_HAND_POINTER : MOUSE_INACTIVE_POINTER
- return TRUE
-
-///Prefs menu
-/atom/movable/screen/lobby/button/character_setup
- name = "View Character Setup"
- screen_loc = "TOP:-70,CENTER:-54"
- icon = 'icons/hud/lobby/character_setup.dmi'
- icon_state = "character_setup_disabled"
- base_icon_state = "character_setup"
- enabled = FALSE
-
-/atom/movable/screen/lobby/button/character_setup/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- // We need IconForge and the assets to be ready before allowing the menu to open
- if(SSearly_assets.initialized == INITIALIZATION_INNEW_REGULAR || SSatoms.initialized == INITIALIZATION_INNEW_REGULAR)
- flick("[base_icon_state]_enabled", src)
- set_button_status(TRUE)
- else
- set_button_status(FALSE)
- RegisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_character_setup))
- RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_character_setup))
-
-/atom/movable/screen/lobby/button/character_setup/Click(location, control, params)
- . = ..()
- if(!.)
- return
-
- var/datum/preferences/preferences = hud.mymob.canon_client.prefs
- preferences.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES
- preferences.update_static_data(usr)
- preferences.ui_interact(usr)
-
-/atom/movable/screen/lobby/button/character_setup/proc/enable_character_setup()
- SIGNAL_HANDLER
- flick("[base_icon_state]_enabled", src)
- set_button_status(TRUE)
- UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
- UnregisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE)
-
-///Button that appears before the game has started
-/atom/movable/screen/lobby/button/ready
- name = "Toggle Readiness"
- screen_loc = "TOP:-8,CENTER:-65"
- icon = 'icons/hud/lobby/ready.dmi'
- icon_state = "not_ready"
- base_icon_state = "not_ready"
-
-/atom/movable/screen/lobby/button/ready/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- switch(SSticker.current_state)
- if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(hide_ready_button))
- if(GAME_STATE_SETTING_UP)
- set_button_status(FALSE)
- RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(show_ready_button))
- else
- set_button_status(FALSE)
-
-/atom/movable/screen/lobby/button/ready/proc/hide_ready_button()
- SIGNAL_HANDLER
- set_button_status(FALSE)
- UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
- RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(show_ready_button))
-
-/atom/movable/screen/lobby/button/ready/proc/show_ready_button()
- SIGNAL_HANDLER
- set_button_status(TRUE)
- UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(hide_ready_button))
-
-/atom/movable/screen/lobby/button/ready/Click(location, control, params)
- . = ..()
- if(!.)
- return
- var/mob/dead/new_player/new_player = hud.mymob
-
- // switch based on the user, if they aren't ready then we change them to ready, and vice versa
- if(new_player.ready == PLAYER_NOT_READY)
- new_player.auto_deadmin_on_ready_or_latejoin()
- new_player.ready = PLAYER_READY_TO_PLAY
- base_icon_state = "ready"
- else
- new_player.ready = PLAYER_NOT_READY
- base_icon_state = "not_ready"
-
- update_appearance(UPDATE_ICON)
- SEND_SIGNAL(hud, COMSIG_HUD_PLAYER_READY_TOGGLE)
-
-///Shown when the game has started
-/atom/movable/screen/lobby/button/join
- name = "Join Game"
- screen_loc = "TOP:-13,CENTER:-58"
- icon = 'icons/hud/lobby/join.dmi'
- icon_state = "" //Default to not visible
- base_icon_state = "join_game"
- enabled = null // set in init
-
-/atom/movable/screen/lobby/button/join/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- switch(SSticker.current_state)
- if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
- set_button_status(FALSE)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(show_join_button))
- if(GAME_STATE_SETTING_UP)
- set_button_status(TRUE)
- RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(hide_join_button))
- else
- set_button_status(TRUE)
-
-/atom/movable/screen/lobby/button/join/Click(location, control, params)
- . = ..()
- if(!.)
- return
-
- if(!SSticker?.IsRoundInProgress())
- to_chat(hud.mymob, span_boldwarning("The round is either not ready, or has already finished..."))
- return
-
- //Determines Relevent Population Cap
- var/relevant_cap
- var/hard_popcap = CONFIG_GET(number/hard_popcap)
- var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
- if(hard_popcap && extreme_popcap)
- relevant_cap = min(hard_popcap, extreme_popcap)
- else
- relevant_cap = max(hard_popcap, extreme_popcap)
-
- var/mob/dead/new_player/new_player = hud.mymob
-
- if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(new_player.key) in GLOB.admin_datums)))
- to_chat(new_player, span_danger("[CONFIG_GET(string/hard_popcap_message)]"))
-
- var/queue_position = SSticker.queued_players.Find(new_player)
- if(queue_position == 1)
- to_chat(new_player, span_notice("You are next in line to join the game. You will be notified when a slot opens up."))
- else if(queue_position)
- to_chat(new_player, span_notice("There are [queue_position-1] players in front of you in the queue to join the game."))
- else
- SSticker.queued_players += new_player
- to_chat(new_player, span_notice("You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len]."))
- return
-
- new_player.auto_deadmin_on_ready_or_latejoin()
-
- if(!LAZYACCESS(params2list(params), CTRL_CLICK))
- GLOB.latejoin_menu.ui_interact(new_player)
- else
- to_chat(new_player, span_warning("Opening emergency fallback late join menu! If THIS doesn't show, ahelp immediately!"))
- GLOB.latejoin_menu.fallback_ui(new_player)
-
-
-/atom/movable/screen/lobby/button/join/proc/show_join_button()
- SIGNAL_HANDLER
- set_button_status(TRUE)
- UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
- RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(hide_join_button))
-
-/atom/movable/screen/lobby/button/join/proc/hide_join_button()
- SIGNAL_HANDLER
- set_button_status(FALSE)
- UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(show_join_button))
-
-/atom/movable/screen/lobby/button/observe
- name = "Observe"
- screen_loc = "TOP:-40,CENTER:-54"
- icon = 'icons/hud/lobby/observe.dmi'
- icon_state = "observe_disabled"
- base_icon_state = "observe"
- enabled = null // set in init
-
-/atom/movable/screen/lobby/button/observe/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- if(SSticker.current_state > GAME_STATE_STARTUP)
- set_button_status(TRUE)
- else
- set_button_status(FALSE)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, PROC_REF(enable_observing))
-
-/atom/movable/screen/lobby/button/observe/Click(location, control, params)
- . = ..()
- if(!.)
- return
- var/mob/dead/new_player/new_player = hud.mymob
- new_player.make_me_an_observer()
-
-/atom/movable/screen/lobby/button/observe/proc/enable_observing()
- SIGNAL_HANDLER
- flick("[base_icon_state]_enabled", src)
- set_button_status(TRUE)
- UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME)
-
-//Subtype the bottom buttons away so the collapse/expand shutter goes behind them
-/atom/movable/screen/lobby/button/bottom
- layer = LOBBY_BOTTOM_BUTTON_LAYER
- icon = 'icons/hud/lobby/bottom_buttons.dmi'
-
-/atom/movable/screen/lobby/button/bottom/settings
- name = "View Game Preferences"
- icon_state = "settings_disabled"
- base_icon_state = "settings"
- screen_loc = "TOP:-122,CENTER:+29"
- enabled = FALSE
-
-/atom/movable/screen/lobby/button/bottom/settings/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- // We need IconForge and the assets to be ready before allowing the menu to open
- if(SSearly_assets.initialized == INITIALIZATION_INNEW_REGULAR || SSatoms.initialized == INITIALIZATION_INNEW_REGULAR)
- set_button_status(TRUE)
- else
- set_button_status(FALSE)
- RegisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_settings))
- RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_settings))
-
-/atom/movable/screen/lobby/button/bottom/settings/Click(location, control, params)
- . = ..()
- if(!.)
- return
-
- var/datum/preferences/preferences = hud.mymob.canon_client.prefs
- preferences.current_window = PREFERENCE_TAB_GAME_PREFERENCES
- preferences.update_static_data(usr)
- preferences.ui_interact(usr)
-
-/atom/movable/screen/lobby/button/bottom/settings/proc/enable_settings()
- SIGNAL_HANDLER
- set_button_status(TRUE)
- UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
- UnregisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE)
-
-/atom/movable/screen/lobby/button/bottom/changelog_button
- name = "View Changelog"
- icon_state = "changelog"
- base_icon_state = "changelog"
- screen_loc ="TOP:-122,CENTER:+57"
-
-/atom/movable/screen/lobby/button/bottom/changelog_button/Click(location, control, params)
- . = ..()
- usr.client?.changelog()
-
-/atom/movable/screen/lobby/button/bottom/crew_manifest
- name = "View Crew Manifest"
- icon_state = "crew_manifest"
- base_icon_state = "crew_manifest"
- screen_loc = "TOP:-122,CENTER:+2"
-
-/atom/movable/screen/lobby/button/bottom/crew_manifest/Click(location, control, params)
- . = ..()
- if(!.)
- return
- var/mob/dead/new_player/new_player = hud.mymob
- new_player.ViewManifest()
-
-/atom/movable/screen/lobby/button/bottom/poll
- name = "View Available Polls"
- icon_state = "poll"
- base_icon_state = "poll"
- screen_loc = "TOP:-122,CENTER:-26"
- ///Whether the button should have a New Poll notification overlay
- var/new_poll = FALSE
-
-/atom/movable/screen/lobby/button/bottom/poll/SlowInit(mapload)
- . = ..()
- if(!usr)
- return
- var/mob/dead/new_player/new_player = usr
- if(is_guest_key(new_player.key))
- set_button_status(FALSE)
- return
- if(!SSdbcore.Connect())
- set_button_status(FALSE)
- return
- var/isadmin = FALSE
- if(new_player.client?.holder)
- isadmin = TRUE
- var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({"
- SELECT id FROM [format_table_name("poll_question")]
- WHERE (adminonly = 0 OR :isadmin = 1)
- AND Now() BETWEEN starttime AND endtime
- AND deleted = 0
- AND id NOT IN (
- SELECT pollid FROM [format_table_name("poll_vote")]
- WHERE ckey = :ckey
- AND deleted = 0
- )
- AND id NOT IN (
- SELECT pollid FROM [format_table_name("poll_textreply")]
- WHERE ckey = :ckey
- AND deleted = 0
- )
- "}, list("isadmin" = isadmin, "ckey" = new_player.ckey))
- if(!query_get_new_polls.Execute())
- qdel(query_get_new_polls)
- set_button_status(FALSE)
- return
- if(query_get_new_polls.NextRow())
- new_poll = TRUE
- else
- new_poll = FALSE
- update_appearance(UPDATE_OVERLAYS)
- qdel(query_get_new_polls)
- if(QDELETED(new_player))
- set_button_status(FALSE)
- return
-
-/atom/movable/screen/lobby/button/bottom/poll/update_overlays()
- . = ..()
- if(new_poll)
- . += mutable_appearance('icons/hud/lobby/poll_overlay.dmi', "new_poll")
-
-/atom/movable/screen/lobby/button/bottom/poll/Click(location, control, params)
- . = ..()
- if(!.)
- return
- var/mob/dead/new_player/new_player = hud.mymob
- new_player.handle_player_polling()
-
-/// A generic "sign up" button used by station traits
-/atom/movable/screen/lobby/button/sign_up
- icon = 'icons/hud/lobby/signup_button.dmi'
- icon_state = "signup"
- base_icon_state = "signup"
- always_available = FALSE
-
-/atom/movable/screen/lobby/button/sign_up/MouseEntered(location, control, params)
- . = ..()
- if(QDELETED(src) || !desc)
- return
- openToolTip(usr, tip_src = src, params = params, title = name, content = desc,)
-
-/atom/movable/screen/lobby/button/sign_up/MouseExited()
- . = ..()
- closeToolTip(usr)
-
-/atom/movable/screen/lobby/button/collapse
- name = "Collapse Lobby Menu"
- icon = 'icons/hud/lobby/collapse_expand.dmi'
- icon_state = "collapse"
- base_icon_state = "collapse"
- layer = LOBBY_BELOW_MENU_LAYER
- screen_loc = "TOP:-82,CENTER:-54"
- always_shown = TRUE
-
- var/blip_enabled = TRUE
-
-/atom/movable/screen/lobby/button/collapse/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- switch(SSticker.current_state)
- if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(disable_blip))
- RegisterSignal(hud, COMSIG_HUD_PLAYER_READY_TOGGLE, PROC_REF(on_player_ready_toggle))
- if(GAME_STATE_SETTING_UP)
- blip_enabled = FALSE
- RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(enable_blip))
- else
- blip_enabled = FALSE
-
- add_overlay(get_blip_overlay())
- update_icon(UPDATE_OVERLAYS)
-
-/atom/movable/screen/lobby/button/collapse/update_overlays()
- . = ..()
- . += get_blip_overlay()
-
-/atom/movable/screen/lobby/button/collapse/Click(location, control, params)
- . = ..()
- if(!.)
- return
-
- if(!istype(hud, /datum/hud/new_player))
- return
- var/datum/hud/new_player/our_hud = hud
- base_icon_state = our_hud.menu_hud_status ? "expand" : "collapse"
- name = "[our_hud.menu_hud_status ? "Expand" : "Collapse"] Lobby Menu"
- set_button_status(FALSE)
-
- //get the shutter object used by our hud
- var/atom/movable/screen/lobby/shutter/menu_shutter = locate(/atom/movable/screen/lobby/shutter) in hud.static_inventory
-
- //animate the shutter
- menu_shutter.setup_shutter_animation()
- //animate bottom buttons' movement
- if(our_hud.menu_hud_status)
- collapse_menu()
- else
- expand_menu()
- our_hud.menu_hud_status = !our_hud.menu_hud_status
-
- //re-enable clicking the button when the shutter animation finishes
- //we use sleep here so it can work during game setup, as addtimer would not work until the game would finish setting up
- sleep(2 * SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
- set_button_status(TRUE)
-
-///Proc to update the ready blip state upon new player's ready status change
-/atom/movable/screen/lobby/button/collapse/proc/on_player_ready_toggle()
- SIGNAL_HANDLER
- update_appearance(UPDATE_ICON)
-
-///Returns a ready blip overlay depending on the player's ready state
-/atom/movable/screen/lobby/button/collapse/proc/get_blip_overlay()
- var/blip_icon_state = "ready_blip"
- if(blip_enabled && hud)
- var/mob/dead/new_player/new_player = hud.mymob
- blip_icon_state += "_[new_player.is_ready_to_play() ? "" : "not_"]ready"
- else
- blip_icon_state += "_disabled"
- var/mutable_appearance/ready_blip = mutable_appearance(icon, blip_icon_state)
- return ready_blip
-
-///Disables the ready blip; makes us listen for the setup error to re-enable the blip
-/atom/movable/screen/lobby/button/collapse/proc/disable_blip()
- SIGNAL_HANDLER
- blip_enabled = FALSE
- UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
- RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(enable_blip))
- update_appearance(UPDATE_ICON)
-
-///Enables the ready blip; makes us listen for the setup completion and game start to disable the blip
-/atom/movable/screen/lobby/button/collapse/proc/enable_blip()
- SIGNAL_HANDLER
- blip_enabled = TRUE
- UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
- RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(disable_blip))
- update_appearance(UPDATE_ICON)
-
-///Moves the button to the top of the screen, leaving only the screen part in view
-///Sends a signal on the hud for the menu hud elements to listen to
-/atom/movable/screen/lobby/button/collapse/proc/collapse_menu()
- SEND_SIGNAL(hud, COMSIG_HUD_LOBBY_COLLAPSED)
- //wait for the shutter to come down
- animate(src, transform = transform, time = SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
- //then pull the button up with the shutter and leave it on the edge of the screen
- animate(transform = transform.Translate(x = 0, y = 134), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
- SEND_SOUND(hud.mymob, sound('sound/misc/menu/menu_rollup1.ogg'))
-
-///Extends the button back to its usual spot
-///Sends a signal on the hud for the menu hud elements to listen to
-/atom/movable/screen/lobby/button/collapse/proc/expand_menu()
- SEND_SIGNAL(hud, COMSIG_HUD_LOBBY_EXPANDED)
- animate(src, transform = matrix(), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_OUT)
- SEND_SOUND(hud.mymob, sound('sound/misc/menu/menu_rolldown1.ogg'))
-
-/atom/movable/screen/lobby/shutter
- icon = 'icons/hud/lobby/shutter.dmi'
- icon_state = "shutter"
- base_icon_state = "shutter"
- screen_loc = "TOP:+143,CENTER:-73" //"home" position is off-screen
- layer = LOBBY_SHUTTER_LAYER
- always_shown = TRUE
-
-///Sets up the shutter pulling down and up. It's the same animation for both collapsing and expanding the menu.
-/atom/movable/screen/lobby/shutter/proc/setup_shutter_animation()
- //bring down the shutter
- animate(src, transform = transform.Translate(x = 0, y = -143), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_OUT)
-
- //wait a little bit before bringing the shutter up
- animate(transform = transform, time = SHUTTER_WAIT_DURATION)
-
- //pull the shutter back off-screen
- animate(transform = matrix(), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
-
-/// LOCALHOST ONLY - Start Now button
-/atom/movable/screen/lobby/button/start_now
- name = "Start Now (LOCALHOST ONLY)"
- screen_loc = "TOP:-146,CENTER:-54"
- icon = 'icons/hud/lobby/start_now.dmi'
- icon_state = "start_now"
- base_icon_state = "start_now"
- always_available = FALSE
- select_sound_play = FALSE
-
-/atom/movable/screen/lobby/button/start_now/Click(location, control, params)
- . = ..()
- if(!. || !usr.client.is_localhost() || !check_rights_for(usr.client, R_SERVER))
- return
- SEND_SOUND(hud.mymob, sound('sound/effects/cartoon_sfx/cartoon_splat.ogg', volume = 50))
- SSticker.start_immediately = TRUE
- if(SSticker.current_state == GAME_STATE_STARTUP)
- to_chat(usr, span_admin("The server is still setting up, but the round will be started as soon as possible."))
-
-#define OVERLAY_X_DIFF 12
-#define OVERLAY_Y_DIFF 5
-
-///Lobby screen that appears before the game has started showing how many players there are and who is ready.
-/atom/movable/screen/lobby/new_player_info
- name = "New Player Info"
- screen_loc = "EAST-3,CENTER:140"
- icon = 'icons/hud/lobby/newplayer.dmi'
- icon_state = null //we only show up when we get update appearance called, cause we need our overlay to not look bad.
- base_icon_state = "newplayer"
- maptext_height = 75
- maptext_width = 80
- maptext_x = OVERLAY_X_DIFF
- maptext_y = OVERLAY_Y_DIFF
-
- ///Boolean on whether or not we should have our static overlay, so we 'turn' the TV off when collapsing.
- var/show_static = TRUE
- ///Static mutable appearance of a job icon we're displaying on the TV for overflow job.
- var/static/mutable_appearance/job_overlay
-
-/atom/movable/screen/lobby/new_player_info/Initialize(mapload, datum/hud/hud_owner)
- . = ..()
- START_PROCESSING(SSnewplayer_info, src)
- update_text()
-
- //only go if the station trait is selected, otherwise it'll show "Assistant" every round.
- var/datum/station_trait/overflow_job_bureaucracy/overflow_job = locate() in SSstation.station_traits
- if(overflow_job && isnull(job_overlay))
- var/icon/job_icon = get_job_hud_icon(SSjob.overflow_role)
- if(isnull(job_icon))
- stack_trace("[SSjob.overflow_role::title] was selected as the Job Overflow but has no icon!")
- else
- job_icon.Scale(ICON_SIZE_X / 2, ICON_SIZE_X / 2)
- job_overlay = mutable_appearance(job_icon, offset_spokesman = src, alpha = 120, layer = src.layer+0.02)
- var/matrix/job_matrix = matrix()
- job_matrix.Translate(6, 14) //this is completely arbitruary just what I thought looked good.
- job_overlay.transform = job_matrix
-
- update_appearance(UPDATE_ICON)
-
-/atom/movable/screen/lobby/new_player_info/Destroy()
- STOP_PROCESSING(SSnewplayer_info, src)
- return ..()
-
-/atom/movable/screen/lobby/new_player_info/MouseEntered(location, control, params)
- . = ..()
- if(QDELETED(src) || isnull(job_overlay))
- return
- openToolTip(usr, src, params, title = "[SSjob.overflow_role::title] overflow", content = "The overflow for the round has been set as [SSjob.overflow_role::title].")
-
-/atom/movable/screen/lobby/new_player_info/MouseExited()
- closeToolTip(usr)
- return ..()
-
-/atom/movable/screen/lobby/new_player_info/update_icon_state()
- . = ..()
- icon_state = base_icon_state
-
-/atom/movable/screen/lobby/new_player_info/update_overlays()
- . = ..()
- . += mutable_appearance(icon, "[base_icon_state]_overlay", layer = src.layer+0.01)
- if(!show_static)
- return .
- if(job_overlay)
- . += job_overlay
- . += mutable_appearance(icon, "static_base", alpha = 20, layer = src.layer+0.03)
- //we have this in a separate file because `generate_icon_alpha_mask` puts lighting even on non-existent pixels,
- //giving the icon a weird background color.
- var/mutable_appearance/scanline = mutable_appearance(generate_icon_alpha_mask('icons/hud/lobby/newplayer_scanline.dmi', "scanline"), alpha = 20, layer = src.layer+0.04)
- scanline.pixel_y = OVERLAY_X_DIFF
- scanline.pixel_x = OVERLAY_Y_DIFF
- . += scanline
-
-/atom/movable/screen/lobby/new_player_info/process(seconds_per_tick)
- update_text()
-
-/atom/movable/screen/lobby/new_player_info/collapse_button()
- show_static = FALSE
- update_text()
- //to be in sync with parent, we'll turn the TV off in this time instead.
- animate(src, appearance = update_appearance(UPDATE_ICON), time = SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
- //we go to the right, not up
- animate(transform = transform.Translate(x = 146, y = 0), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
-
-/atom/movable/screen/lobby/new_player_info/expand_button()
- . = ..()
- show_static = TRUE
- update_appearance(UPDATE_ICON)
- update_text()
-
-/atom/movable/screen/lobby/new_player_info/proc/update_text()
- if(!hud || !show_static)
- maptext = null
- return
-
- var/round_started = SSticker.HasRoundStarted()
- if(!MC_RUNNING())
- maptext = MAPTEXT("[(round_started ? null : "Starting in [time_remaining_str()]
")]Loading...")
- return
- if(SSticker.IsPostgame())
- maptext = MAPTEXT("Game ended,
\
- restart soon")
- return
-
- var/new_maptext
- if(round_started)
- new_maptext = "[SSmapping.current_map.map_name]
\
- [LAZYLEN(GLOB.clients)] player\s online
\
- [ROUND_TIME()] in
"
- new_maptext += ""
- else
-
- if(hud.mymob.client?.holder)
- new_maptext = "Starting in [time_remaining_str()]
\
- [LAZYLEN(GLOB.clients)] player\s
\
- [SSticker.totalPlayersReady] players ready
\
- [SSticker.total_admins_ready] / [length(GLOB.admins)] admins ready"
- else
- new_maptext = "[time_remaining_str()]
\
- [LAZYLEN(GLOB.clients)] player\s"
-
- maptext = MAPTEXT(new_maptext)
-
-/atom/movable/screen/lobby/new_player_info/proc/time_remaining_str()
- var/time_remaining = SSticker.GetTimeLeft()
- if(time_remaining > 0)
- time_remaining = "[round(time_remaining/10)]s"
- else if(time_remaining == -10)
- time_remaining = "DELAYED"
- else
- time_remaining = "SOON"
-
- return time_remaining
-
-#undef OVERLAY_X_DIFF
-#undef OVERLAY_Y_DIFF
-
-#undef SHUTTER_MOVEMENT_DURATION
-#undef SHUTTER_WAIT_DURATION
#undef MAX_STATION_TRAIT_BUTTONS_VERTICAL
#undef TRAIT_BUTTON_Y_ORIGIN
#undef TRAIT_BUTTON_X_ORIGIN
diff --git a/code/_onclick/hud/ooze.dm b/code/_onclick/hud/ooze.dm
index 0962b58126a..e796df33b4a 100644
--- a/code/_onclick/hud/ooze.dm
+++ b/code/_onclick/hud/ooze.dm
@@ -1,14 +1,8 @@
///Hud type with targeting dol and a nutrition bar
-/datum/hud/ooze/New(mob/living/owner)
+/datum/hud/ooze/initialize_screen_objects()
. = ..()
-
- zone_select = new /atom/movable/screen/zone_sel(null, src)
- zone_select.icon = ui_style
- zone_select.update_appearance()
- static_inventory += zone_select
-
- alien_plasma_display = new /atom/movable/screen/ooze_nutrition_display(null, src) //Just going to use the alien plasma display because making new vars for each object is braindead.
- infodisplay += alien_plasma_display
+ add_screen_object(/atom/movable/screen/zone_sel, HUD_MOB_ZONE_SELECTOR, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/ooze_nutrition_display, HUD_OOZE_NUTRITION_DISPLAY)
/atom/movable/screen/ooze_nutrition_display
icon = 'icons/hud/screen_alien.dmi'
diff --git a/code/_onclick/hud/pai.dm b/code/_onclick/hud/pai.dm
new file mode 100644
index 00000000000..74da5962b37
--- /dev/null
+++ b/code/_onclick/hud/pai.dm
@@ -0,0 +1,33 @@
+
+/datum/hud/pai
+ ui_style = 'icons/hud/screen_pai.dmi'
+
+/datum/hud/pai/initialize_screen_objects()
+ . = ..()
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, ui_style, ui_pai_language_menu)
+ add_screen_object(/atom/movable/screen/navigate, HUD_MOB_NAVIGATE_MENU, ui_style, ui_pai_navigate_menu)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_pai_memories_menu)
+
+ add_screen_object(/atom/movable/screen/pai/software, HUD_PAI_SOFTWARE)
+ add_screen_object(/atom/movable/screen/pai/shell, HUD_PAI_SHELL)
+ add_screen_object(/atom/movable/screen/pai/chassis, HUD_PAI_CHASSIS)
+ add_screen_object(/atom/movable/screen/pai/rest, HUD_MOB_REST)
+ add_screen_object(/atom/movable/screen/pai/light, HUD_CYBORG_LAMP)
+ add_screen_object(/atom/movable/screen/pai/newscaster, HUD_PAI_NEWSCASTER)
+ add_screen_object(/atom/movable/screen/pai/host_monitor, HUD_PAI_HOST_MONITOR)
+ add_screen_object(/atom/movable/screen/pai/crew_manifest, HUD_AI_CREW_MANIFEST)
+ add_screen_object(/atom/movable/screen/pai/state_laws, HUD_AI_STATE_LAWS)
+ add_screen_object(/atom/movable/screen/pai/internal_gps, HUD_PAI_GPS)
+ add_screen_object(/atom/movable/screen/pai/image_take, HUD_AI_TAKE_IMAGE)
+ add_screen_object(/atom/movable/screen/pai/image_view, HUD_AI_IMAGE_VIEW)
+ add_screen_object(/atom/movable/screen/pai/radio, HUD_CYBORG_RADIO)
+ add_screen_object(/atom/movable/screen/pai/modpc, HUD_SILICON_TABLET)
+
+ update_software_buttons()
+
+/datum/hud/pai/proc/update_software_buttons()
+ var/mob/living/silicon/pai/owner = mymob
+ for(var/button_key in screen_objects)
+ var/atom/movable/screen/pai/button = screen_objects[button_key]
+ if(istype(button) && button.required_software)
+ button.color = owner.installed_software.Find(button.required_software) ? null : COLOR_GRAY
diff --git a/code/_onclick/hud/parallax/parallax.dm b/code/_onclick/hud/parallax/parallax.dm
index 8a2227db588..c29e45dfde7 100644
--- a/code/_onclick/hud/parallax/parallax.dm
+++ b/code/_onclick/hud/parallax/parallax.dm
@@ -201,7 +201,6 @@
hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE)
// Root object for parallax, all parallax layers are drawn onto this and it manages them
-INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home)
/atom/movable/screen/parallax_home
icon = null
blend_mode = BLEND_ADD
@@ -293,7 +292,6 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home)
QDEL_LIST(parallax_layers_cached)
// We need parallax to always pass its args down into initialize, so we immediate init it
-INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
/atom/movable/screen/parallax_layer
icon = 'icons/effects/parallax.dmi'
var/speed = 1
diff --git a/code/_onclick/hud/perks.dm b/code/_onclick/hud/perks.dm
deleted file mode 100644
index 22b08906b66..00000000000
--- a/code/_onclick/hud/perks.dm
+++ /dev/null
@@ -1,35 +0,0 @@
-/atom/movable/screen/perk
- icon = 'icons/hud/perks.dmi'
- name = "blank"
- icon_state = "blank"
-
-/atom/movable/screen/perk/more
- name = "more"
- icon_state = "more"
- // When compact we don't show our perk huds in ui.
- var/compact = FALSE
- // List of all perks that we see on hud.
- var/list/perks_on_hud = list()
- // List to remember all perks we hide when compact it.
- var/list/perks_compacted = list()
-
-/atom/movable/screen/perk/more/Click(location, control, params)
- . = ..()
- if(!isliving(usr))
- return
- var/mob/living/usr_is_living = usr
- var/datum/antagonist/wizard/wizard_datum = usr_is_living.mind.has_antag_datum(/datum/antagonist/wizard)
- if(!wizard_datum)
- return
- compact = !compact
- var/datum/hud/user_hud = usr_is_living.hud_used
- if(compact)
- for(var/atom/movable/screen/perk/perk_on_hud as anything in perks_on_hud)
- perks_compacted += perk_on_hud
- user_hud.infodisplay -= perk_on_hud
- user_hud.show_hud(user_hud.hud_version)
- else
- for(var/atom/movable/screen/perk/perk_compacted as anything in perks_compacted)
- user_hud.infodisplay += perk_compacted
- perks_compacted -= perk_compacted
- user_hud.show_hud(user_hud.hud_version)
diff --git a/code/_onclick/hud/rendering/plane_masters/_plane_master.dm b/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
index 02d1f0ca3b4..c1efa44f8a3 100644
--- a/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
+++ b/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
@@ -1,6 +1,3 @@
-// I hate this place
-INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master)
-
/atom/movable/screen/plane_master
screen_loc = "CENTER"
icon_state = "blank"
diff --git a/code/_onclick/hud/revenanthud.dm b/code/_onclick/hud/revenanthud.dm
index c47ea4c2418..49d89fb159e 100644
--- a/code/_onclick/hud/revenanthud.dm
+++ b/code/_onclick/hud/revenanthud.dm
@@ -1,14 +1,7 @@
/datum/hud/revenant
ui_style = 'icons/hud/screen_gen.dmi'
-/datum/hud/revenant/New(mob/owner)
- ..()
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = ui_style
- pull_icon.update_appearance()
- pull_icon.screen_loc = ui_living_pull
- static_inventory += pull_icon
-
- healths = new /atom/movable/screen/healths/revenant(null, src)
- infodisplay += healths
+/datum/hud/revenant/initialize_screen_objects()
+ . = ..()
+ add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_STATIC, ui_style, ui_living_pull)
+ add_screen_object(/atom/movable/screen/healths/revenant, HUD_MOB_HEALTH, HUD_GROUP_INFO)
diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm
index 498d58dccb2..53b146b5b03 100644
--- a/code/_onclick/hud/robot.dm
+++ b/code/_onclick/hud/robot.dm
@@ -1,184 +1,38 @@
-/atom/movable/screen/robot
- icon = 'icons/hud/screen_cyborg.dmi'
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/robot/module
- name = "cyborg module"
- icon_state = "nomod"
-
-/atom/movable/screen/robot/Click(location, control, params)
- if(isobserver(usr))
- return TRUE
-
-/atom/movable/screen/robot/module/Click(location, control, params)
- . = ..()
- var/mob/living/silicon/robot/robot_owner = hud.mymob
- if(robot_owner.model.type == /obj/item/robot_model)
- if(.)
- return
- robot_owner.pick_model()
- return
- var/list/modifiers = params2list(params)
- if(robot_owner.module_active && !LAZYACCESS(modifiers, RIGHT_CLICK) && !.)
- robot_owner.uneq_active()
- return
-
- //observers can look at borg's inventories, so we ignore parent return value here, unlike everywhere else.
- if(usr.active_storage == robot_owner.model.atom_storage)
- robot_owner.model.atom_storage.hide_contents(usr)
- else
- robot_owner.model.atom_storage.open_storage(usr)
- return TRUE
-
-/atom/movable/screen/robot/module1
- name = "module1"
- icon_state = "inv1"
-
-/atom/movable/screen/robot/module1/Click()
- if(..())
- return
- var/mob/living/silicon/robot/R = usr
- R.toggle_module(1)
-
-/atom/movable/screen/robot/module2
- name = "module2"
- icon_state = "inv2"
-
-/atom/movable/screen/robot/module2/Click()
- if(..())
- return
- var/mob/living/silicon/robot/R = usr
- R.toggle_module(2)
-
-/atom/movable/screen/robot/module3
- name = "module3"
- icon_state = "inv3"
-
-/atom/movable/screen/robot/module3/Click()
- if(..())
- return
- var/mob/living/silicon/robot/R = usr
- R.toggle_module(3)
-
-/atom/movable/screen/robot/radio
- name = "radio"
- icon_state = "radio"
-
-/atom/movable/screen/robot/radio/Click()
- if(..())
- return
- var/mob/living/silicon/robot/R = usr
- R.radio.interact(R)
-
/datum/hud/robot
ui_style = 'icons/hud/screen_cyborg.dmi'
-/datum/hud/robot/New(mob/owner)
- ..()
+/datum/hud/robot/initialize_screen_objects()
+ . = ..()
// i, Robit
var/mob/living/silicon/robot/robit = mymob
- var/atom/movable/screen/using
+ add_screen_object(/atom/movable/screen/language_menu, HUD_MOB_LANGUAGE_MENU, HUD_GROUP_STATIC, ui_style, ui_borg_language_menu)
+ add_screen_object(/atom/movable/screen/navigate, HUD_MOB_NAVIGATE_MENU, HUD_GROUP_STATIC, ui_style, ui_borg_navigate_menu)
+ add_screen_object(/atom/movable/screen/memories, HUD_MOB_MEMORIES, HUD_GROUP_STATIC, ui_style, ui_borg_memories_menu)
+ add_screen_object(/atom/movable/screen/robot/radio, HUD_CYBORG_RADIO)
-// Language
- using = new/atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_borg_language_menu
- static_inventory += using
+ for (var/i in BORG_CHOOSE_MODULE_ONE to BORG_CHOOSE_MODULE_THREE)
+ var/atom/movable/screen/robot/module_slot/module = add_screen_object(/atom/movable/screen/robot/module_slot, HUD_KEY_CYBORG_MODULE(i))
+ module.set_slot(i)
-// Navigation
- using = new /atom/movable/screen/navigate(null, src)
- using.icon = ui_style
- using.screen_loc = ui_borg_navigate_menu
- static_inventory += using
+ add_screen_object(/atom/movable/screen/robot/lamp, HUD_CYBORG_LAMP)
+ add_screen_object(/atom/movable/screen/ai/image_take, HUD_SILICON_TAKE_IMAGE, ui_loc = ui_borg_camera)
-// Memories
- using = new /atom/movable/screen/memories(null, src)
- using.icon = ui_style
- using.screen_loc = ui_borg_memories_menu
- static_inventory += using
-
-//Radio
- using = new /atom/movable/screen/robot/radio(null, src)
- using.screen_loc = ui_borg_radio
- static_inventory += using
-
-//Module select
- if(!robit.inv1)
- robit.inv1 = new /atom/movable/screen/robot/module1(null, src)
- robit.inv1.screen_loc = ui_inv1
- static_inventory += robit.inv1
-
- if(!robit.inv2)
- robit.inv2 = new /atom/movable/screen/robot/module2(null, src)
- robit.inv2.screen_loc = ui_inv2
- static_inventory += robit.inv2
-
- if(!robit.inv3)
- robit.inv3 = new /atom/movable/screen/robot/module3(null, src)
- robit.inv3.screen_loc = ui_inv3
- static_inventory += robit.inv3
-
-//End of module select
- using = new /atom/movable/screen/robot/lamp(null, src)
- using.screen_loc = ui_borg_lamp
- static_inventory += using
- robit.lampButton = using
- var/atom/movable/screen/robot/lamp/lampscreen = using
- lampscreen.robot = robit
-
-//Photography stuff
- using = new /atom/movable/screen/ai/image_take(null, src)
- using.screen_loc = ui_borg_camera
- static_inventory += using
-
-//Borg Integrated Tablet
- using = new /atom/movable/screen/robot/modpc(null, src)
- using.screen_loc = ui_borg_tablet
- static_inventory += using
- robit.interfaceButton = using
+ var/atom/movable/screen/robot/modpc/tablet = add_screen_object(/atom/movable/screen/robot/modpc, HUD_SILICON_TABLET, HUD_GROUP_STATIC)
if(robit.modularInterface)
// Just trust me
robit.modularInterface.vis_flags |= VIS_INHERIT_PLANE
- using.vis_contents += robit.modularInterface
- var/atom/movable/screen/robot/modpc/tabletbutton = using
- tabletbutton.robot = robit
+ tablet.vis_contents += robit.modularInterface
-//Alerts
- using = new /atom/movable/screen/robot/alerts(null, src)
- using.screen_loc = ui_borg_alerts
- static_inventory += using
+ add_screen_object(/atom/movable/screen/robot/alerts, HUD_SILICON_ALERTS)
+ add_screen_object(/atom/movable/screen/combattoggle/robot, HUD_MOB_INTENTS)
+ add_screen_object(/atom/movable/screen/floor_changer, HUD_MOB_FLOOR_CHANGER, HUD_GROUP_STATIC, ui_style, ui_borg_floor_changer)
+ add_screen_object(/atom/movable/screen/zone_sel/robot, HUD_MOB_ZONE_SELECTOR)
+ var/atom/movable/screen/robot/module/module = add_screen_object(/atom/movable/screen/robot/module, HUD_CYBORG_HANDS, ui_loc = ui_borg_module)
+ module.icon_state = robit.model ? robit.model.model_select_icon : "nomod"
- //Combat Mode
- action_intent = new /atom/movable/screen/combattoggle/robot(null, src)
- action_intent.icon = ui_style
- action_intent.screen_loc = ui_borg_intents
- static_inventory += action_intent
+ add_screen_object(/atom/movable/screen/healths/robot, HUD_MOB_HEALTH, HUD_GROUP_INFO)
- floor_change = new /atom/movable/screen/floor_changer(null, src)
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_borg_floor_changer
- static_inventory += floor_change
-
-//Health
- healths = new /atom/movable/screen/healths/robot(null, src)
- infodisplay += healths
-
-//Installed Module
- robit.hands = new /atom/movable/screen/robot/module(null, src)
- robit.hands.icon_state = robit.model ? robit.model.model_select_icon : "nomod"
- robit.hands.screen_loc = ui_borg_module
- static_inventory += robit.hands
-
- pull_icon = new /atom/movable/screen/pull(null, src)
- pull_icon.icon = 'icons/hud/screen_cyborg.dmi'
- pull_icon.screen_loc = ui_borg_pull
- pull_icon.update_appearance()
- hotkeybuttons += pull_icon
-
-
- zone_select = new /atom/movable/screen/zone_sel/robot(null, src)
- zone_select.update_appearance()
- static_inventory += zone_select
+ add_screen_object(/atom/movable/screen/pull, HUD_MOB_PULL, HUD_GROUP_HOTKEYS, ui_style, ui_borg_pull)
/datum/hud/robot/persistent_inventory_update(mob/viewer)
if(!mymob)
@@ -186,74 +40,18 @@
var/mob/living/silicon/robot/R = mymob
var/mob/screenmob = viewer || R
-
- if(screenmob.hud_used)
- if(screenmob.hud_used.hud_shown)
- for(var/i in 1 to R.held_items.len)
- var/obj/item/I = R.held_items[i]
- if(I)
- switch(i)
- if(BORG_CHOOSE_MODULE_ONE)
- I.screen_loc = ui_inv1
- if(BORG_CHOOSE_MODULE_TWO)
- I.screen_loc = ui_inv2
- if(BORG_CHOOSE_MODULE_THREE)
- I.screen_loc = ui_inv3
- else
- return
- screenmob.client.screen += I
- else
- for(var/obj/item/I in R.held_items)
- screenmob.client.screen -= I
-
-/atom/movable/screen/robot/lamp
- name = "headlamp"
- icon_state = "lamp_off"
- base_icon_state = "lamp"
- var/mob/living/silicon/robot/robot
-
-/atom/movable/screen/robot/lamp/Click()
- . = ..()
- if(.)
+ if(!screenmob.hud_used)
return
- robot?.toggle_headlamp()
- update_appearance()
-/atom/movable/screen/robot/lamp/update_icon_state()
- icon_state = "[base_icon_state]_[robot?.lamp_enabled ? "on" : "off"]"
- return ..()
-
-/atom/movable/screen/robot/lamp/Destroy()
- if(robot)
- robot.lampButton = null
- robot = null
- return ..()
-
-/atom/movable/screen/robot/modpc
- name = "Modular Interface"
- icon_state = "template"
- var/mob/living/silicon/robot/robot
-
-/atom/movable/screen/robot/modpc/Click()
- . = ..()
- if(.)
+ if(!screenmob.hud_used.hud_shown)
+ for(var/obj/item/I in R.held_items)
+ screenmob.client.screen -= I
return
- robot.modularInterface?.interact(robot)
-/atom/movable/screen/robot/modpc/Destroy()
- if(robot)
- robot.interfaceButton = null
- robot = null
- return ..()
-
-/atom/movable/screen/robot/alerts
- name = "Alert Panel"
- icon = 'icons/hud/screen_ai.dmi'
- icon_state = "alerts"
-
-/atom/movable/screen/robot/alerts/Click()
- . = ..()
- if(.)
- return
- var/mob/living/silicon/robot/borgo = usr
- borgo.alert_control.ui_interact(borgo)
+ for(var/i in 1 to length(R.held_items))
+ var/obj/item/I = R.held_items[i]
+ if(!I)
+ continue
+ var/atom/movable/screen/robot/module_slot/slot = screen_objects[HUD_KEY_CYBORG_MODULE(i)]
+ I.screen_loc = slot.screen_loc
+ screenmob.client.screen += I
diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/screen_objects/action_button.dm
similarity index 97%
rename from code/_onclick/hud/action_button.dm
rename to code/_onclick/hud/screen_objects/action_button.dm
index 88508c11e47..0782aabd5c0 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/screen_objects/action_button.dm
@@ -31,10 +31,13 @@
viewer?.client?.screen -= src
linked_action.viewers -= our_hud
viewer.update_action_buttons()
+ our_hud.screen_objects -= hud_key
+ our_hud.screen_groups?[hud_group_key] -= src
our_hud = null
linked_action = null
return ..()
+
/atom/movable/screen/movable/action_button/proc/can_use(mob/user)
if(isobserver(user))
var/mob/dead/observer/dead_mob = user
@@ -278,15 +281,17 @@
var/color_timer_id
/atom/movable/screen/button_palette/Destroy()
- if(our_hud)
+ if (our_hud)
our_hud.mymob?.canon_client?.screen -= src
- our_hud.toggle_palette = null
+ our_hud.screen_objects -= hud_key
+ our_hud.screen_groups?[hud_group_key] -= src
our_hud = null
return ..()
/atom/movable/screen/button_palette/Initialize(mapload, datum/hud/hud_owner)
. = ..()
update_appearance()
+ set_hud(hud_owner)
/atom/movable/screen/button_palette/proc/set_hud(datum/hud/our_hud)
src.our_hud = our_hud
@@ -419,6 +424,18 @@ GLOBAL_LIST_INIT(palette_removed_matrix, list(1.4,0,0,0, 0.7,0.4,0,0, 0.4,0,0.6,
var/scroll_direction = 0
var/datum/hud/our_hud
+/atom/movable/screen/palette_scroll/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ set_hud(hud_owner)
+
+/atom/movable/screen/palette_scroll/Destroy()
+ if (our_hud)
+ our_hud.mymob?.canon_client?.screen -= src
+ our_hud.screen_objects -= hud_key
+ our_hud.screen_groups?[hud_group_key] -= src
+ our_hud = null
+ return ..()
+
/atom/movable/screen/palette_scroll/proc/can_use(mob/user)
if (isobserver(user))
var/mob/dead/observer/O = user
@@ -458,26 +475,12 @@ GLOBAL_LIST_INIT(palette_removed_matrix, list(1.4,0,0,0, 0.7,0.4,0,0, 0.4,0,0.6,
icon_state = "scroll_down"
scroll_direction = 1
-/atom/movable/screen/palette_scroll/down/Destroy()
- if(our_hud)
- our_hud.mymob?.canon_client?.screen -= src
- our_hud.palette_down = null
- our_hud = null
- return ..()
-
/atom/movable/screen/palette_scroll/up
name = "Scroll Up"
desc = "Click on this to scroll the actions above up"
icon_state = "scroll_up"
scroll_direction = -1
-/atom/movable/screen/palette_scroll/up/Destroy()
- if(our_hud)
- our_hud.mymob?.canon_client?.screen -= src
- our_hud.palette_up = null
- our_hud = null
- return ..()
-
/// Exists so you have a place to put your buttons when you move them around
/atom/movable/screen/action_landing
name = "Button Space"
diff --git a/code/_onclick/hud/screen_objects/ai.dm b/code/_onclick/hud/screen_objects/ai.dm
new file mode 100644
index 00000000000..ffa18a01e68
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/ai.dm
@@ -0,0 +1,250 @@
+/atom/movable/screen/ai
+ icon = 'icons/hud/screen_ai.dmi'
+ mouse_over_pointer = MOUSE_HAND_POINTER
+
+/atom/movable/screen/ai/Click()
+ if(isobserver(usr) || usr.incapacitated)
+ return TRUE
+
+/atom/movable/screen/ai/aicore
+ name = "AI core"
+ icon_state = "ai_core"
+ screen_loc = ui_ai_core
+
+/atom/movable/screen/ai/aicore/Click()
+ if(!isAI(usr))
+ return
+
+ var/mob/living/silicon/ai/AI = usr
+ AI.view_core()
+
+/atom/movable/screen/ai/camera_list
+ name = "Show Camera List"
+ icon_state = "camera"
+ screen_loc = ui_ai_camera_list
+
+/atom/movable/screen/ai/camera_list/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.show_camera_list()
+
+/atom/movable/screen/ai/camera_track
+ name = "Track With Camera"
+ icon_state = "track"
+ screen_loc = ui_ai_track_with_camera
+
+/atom/movable/screen/ai/camera_track/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.ai_camera_track()
+
+/atom/movable/screen/ai/camera_light
+ name = "Toggle Camera Light"
+ icon_state = "camera_light"
+ screen_loc = ui_ai_camera_light
+
+/atom/movable/screen/ai/camera_light/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.toggle_camera_light()
+
+/atom/movable/screen/ai/modpc
+ name = "Messenger"
+ icon_state = "pda_send"
+ screen_loc = ui_ai_mod_int
+
+/atom/movable/screen/ai/modpc/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.modularInterface?.interact(AI)
+
+/atom/movable/screen/ai/crew_monitor
+ name = "Crew Monitoring Console"
+ icon_state = "crew_monitor"
+ screen_loc = ui_ai_crew_monitor
+
+/atom/movable/screen/ai/crew_monitor/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ GLOB.crewmonitor.show(AI,AI)
+
+/atom/movable/screen/ai/crew_manifest
+ name = "Crew Manifest"
+ icon_state = "manifest"
+ screen_loc = ui_ai_crew_manifest
+
+/atom/movable/screen/ai/crew_manifest/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.ai_roster()
+
+/atom/movable/screen/ai/alerts
+ name = "Show Alerts"
+ icon_state = "alerts"
+ screen_loc = ui_ai_alerts
+
+/atom/movable/screen/ai/alerts/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.alert_control.ui_interact(AI)
+
+/atom/movable/screen/ai/announcement
+ name = "Make Vox Announcement"
+ icon_state = "announcement"
+ screen_loc = ui_ai_announcement
+
+/atom/movable/screen/ai/announcement/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.announcement()
+
+/atom/movable/screen/ai/call_shuttle
+ name = "Call Emergency Shuttle"
+ icon_state = "call_shuttle"
+ screen_loc = ui_ai_shuttle
+
+/atom/movable/screen/ai/call_shuttle/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.ai_call_shuttle()
+
+/atom/movable/screen/ai/state_laws
+ name = "State Laws"
+ icon_state = "state_laws"
+ screen_loc = ui_ai_state_laws
+
+/atom/movable/screen/ai/state_laws/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.checklaws()
+
+/atom/movable/screen/ai/image_take
+ name = "Take Image"
+ icon_state = "take_picture"
+ screen_loc = ui_ai_take_picture
+
+/atom/movable/screen/ai/image_take/Click()
+ if(..())
+ return
+ if(isAI(usr))
+ var/mob/living/silicon/ai/AI = usr
+ AI.aicamera.toggle_camera_mode(usr)
+ else if(iscyborg(usr))
+ var/mob/living/silicon/robot/R = usr
+ R.aicamera.toggle_camera_mode(usr)
+
+/atom/movable/screen/ai/image_view
+ name = "View Images"
+ icon_state = "view_images"
+ screen_loc = ui_ai_view_images
+
+/atom/movable/screen/ai/image_view/Click()
+ if(..())
+ return
+ if(isAI(usr))
+ var/mob/living/silicon/ai/AI = usr
+ AI.aicamera.viewpictures(usr)
+
+/atom/movable/screen/ai/sensors
+ name = "Sensor Augmentation"
+ icon_state = "ai_sensor"
+ screen_loc = ui_ai_sensor
+
+/atom/movable/screen/ai/sensors/Click()
+ if(..())
+ return
+ var/mob/living/silicon/S = usr
+ S.toggle_sensors()
+
+/atom/movable/screen/ai/multicam
+ name = "Multicamera Mode"
+ icon_state = "multicam"
+ screen_loc = ui_ai_multicam
+
+/atom/movable/screen/ai/multicam/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.toggle_multicam()
+
+/atom/movable/screen/ai/add_multicam
+ name = "New Camera"
+ icon_state = "new_cam"
+ screen_loc = ui_ai_add_multicam
+
+/atom/movable/screen/ai/add_multicam/Click()
+ if(..())
+ return
+ var/mob/living/silicon/ai/AI = usr
+ AI.drop_new_multicam()
+
+/atom/movable/screen/ai/floor_indicator
+ icon_state = "zindicator"
+ screen_loc = ui_ai_floor_indicator
+
+/atom/movable/screen/ai/floor_indicator/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ if(istype(hud_owner))
+ RegisterSignal(hud_owner, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(update_z))
+ update_z()
+
+/atom/movable/screen/ai/floor_indicator/proc/update_z(datum/hud/source)
+ SIGNAL_HANDLER
+ var/mob/living/silicon/ai/ai = get_mob() //if you use this for anyone else i will find you
+ if(isnull(ai))
+ return
+ var/turf/locturf = isturf(ai.loc) ? get_turf(ai.eyeobj) : get_turf(ai) //must be a var cuz error
+ var/ai_z = locturf.z
+ var/text = "Level
[ai_z]"
+ if(SSmapping.level_trait(ai_z, ZTRAIT_STATION))
+ text = "Floor
[ai_z - 1]"
+ else if (SSmapping.level_trait(ai_z, ZTRAIT_NOPHASE))
+ text = "ERROR"
+ maptext = MAPTEXT_TINY_UNICODE("[text]
")
+
+/atom/movable/screen/ai/go_up
+ name = "go up"
+ icon_state = "up"
+ screen_loc = ui_ai_godownup
+
+/atom/movable/screen/ai/go_up/Initialize(mapload)
+ . = ..()
+ register_context()
+
+/atom/movable/screen/ai/go_up/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ context[SCREENTIP_CONTEXT_LMB] = "Go up a floor"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/atom/movable/screen/ai/go_up/Click(location,control,params)
+ var/mob/ai = get_mob() //the core
+ flick("uppressed",src)
+ if(!isturf(ai.loc) || usr != ai) //aicard and stuff
+ return
+ ai.up()
+
+/atom/movable/screen/ai/go_up/down
+ name = "go down"
+ icon_state = "down"
+
+/atom/movable/screen/ai/go_up/down/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ context[SCREENTIP_CONTEXT_LMB] = "Go down a floor"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/atom/movable/screen/ai/go_up/down/Click(location,control,params)
+ var/mob/ai = get_mob() //the core
+ flick("downpressed",src)
+ if(!isturf(ai.loc) || usr != ai) //aicard and stuff
+ return
+ ai.down()
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/screen_objects/alert.dm
similarity index 100%
rename from code/_onclick/hud/alert.dm
rename to code/_onclick/hud/screen_objects/alert.dm
diff --git a/code/_onclick/hud/screen_objects/alien.dm b/code/_onclick/hud/screen_objects/alien.dm
new file mode 100644
index 00000000000..d99cdc55b35
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/alien.dm
@@ -0,0 +1,24 @@
+/atom/movable/screen/alien
+ icon = 'icons/hud/screen_alien.dmi'
+
+/atom/movable/screen/alien/leap
+ name = "toggle leap"
+ icon_state = "leap_off"
+ screen_loc = ui_alien_storage_r
+
+/atom/movable/screen/alien/leap/Click()
+ if(!isalienhunter(usr))
+ return
+ var/mob/living/carbon/alien/adult/hunter/AH = usr
+ AH.toggle_leap()
+
+/atom/movable/screen/alien/plasma_display
+ name = "plasma stored"
+ icon_state = "power_display"
+ screen_loc = ui_alienplasmadisplay
+
+/atom/movable/screen/alien/alien_queen_finder
+ name = "queen sense"
+ desc = "Allows you to sense the general direction of your Queen."
+ icon_state = "queen_finder"
+ screen_loc = ui_alien_queen_finder
diff --git a/code/_onclick/hud/screen_objects/blob.dm b/code/_onclick/hud/screen_objects/blob.dm
new file mode 100644
index 00000000000..3f78d94854f
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/blob.dm
@@ -0,0 +1,164 @@
+/atom/movable/screen/blob
+ icon = 'icons/hud/blob.dmi'
+ mouse_over_pointer = MOUSE_HAND_POINTER
+
+/atom/movable/screen/blob/MouseEntered(location,control,params)
+ . = ..()
+ openToolTip(usr,src,params,title = name,content = desc, theme = "blob")
+
+/atom/movable/screen/blob/MouseExited()
+ closeToolTip(usr)
+
+/atom/movable/screen/blob/jump_to_node
+ name = "Jump to Node"
+ desc = "Moves your camera to a selected blob node."
+ icon_state = "ui_tonode"
+ screen_loc = ui_inventory
+
+/atom/movable/screen/blob/jump_to_node/Click()
+ if(!isovermind(usr))
+ return FALSE
+ var/mob/eye/blob/blob = usr
+ blob.jump_to_node()
+
+/atom/movable/screen/blob/jump_to_core
+ name = "Jump to Core"
+ desc = "Moves your camera to your blob core."
+ icon_state = "ui_tocore"
+ screen_loc = ui_zonesel
+
+/atom/movable/screen/blob/jump_to_core/MouseEntered(location,control,params)
+ if(hud?.mymob && isovermind(hud.mymob))
+ var/mob/eye/blob/B = hud.mymob
+ if(!B.placed)
+ name = "Place Blob Core"
+ desc = "Attempt to place your blob core at this location."
+ else
+ name = initial(name)
+ desc = initial(desc)
+ return ..()
+
+/atom/movable/screen/blob/jump_to_core/Click()
+ if(!isovermind(usr))
+ return FALSE
+ var/mob/eye/blob/blob = usr
+ if(!blob.placed)
+ blob.place_blob_core(BLOB_NORMAL_PLACEMENT)
+ blob.transport_core()
+
+/atom/movable/screen/blob/blobbernaut
+ // Name and description get given their proper values on Initialize()
+ name = "Produce Blobbernaut (ERROR)"
+ desc = "Produces a strong, smart blobbernaut from a factory blob for (ERROR) resources.
The factory blob used will become fragile and unable to produce spores."
+ icon_state = "ui_blobbernaut"
+ screen_loc = ui_belt
+
+/atom/movable/screen/blob/blobbernaut/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ name = "Produce Blobbernaut ([BLOBMOB_BLOBBERNAUT_RESOURCE_COST])"
+ desc = "Produces a strong, smart blobbernaut from a factory blob for [BLOBMOB_BLOBBERNAUT_RESOURCE_COST] resources.
The factory blob used will become fragile and unable to produce spores."
+
+/atom/movable/screen/blob/blobbernaut/Click()
+ if(!isovermind(usr))
+ return FALSE
+ var/mob/eye/blob/blob = usr
+ blob.create_blobbernaut()
+
+/atom/movable/screen/blob/resource_blob
+ // Name and description get given their proper values on Initialize()
+ name = "Produce Resource Blob (ERROR)"
+ desc = "Produces a resource blob for ERROR resources.
Resource blobs will give you resources every few seconds."
+ icon_state = "ui_resource"
+ screen_loc = ui_back
+
+/atom/movable/screen/blob/resource_blob/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ name = "Produce Resource Blob ([BLOB_STRUCTURE_RESOURCE_COST])"
+ desc = "Produces a resource blob for [BLOB_STRUCTURE_RESOURCE_COST] resources.
Resource blobs will give you resources every few seconds."
+
+/atom/movable/screen/blob/resource_blob/Click()
+ if(!isovermind(usr))
+ return FALSE
+ var/mob/eye/blob/blob = usr
+ blob.create_special(BLOB_STRUCTURE_RESOURCE_COST, /obj/structure/blob/special/resource, BLOB_RESOURCE_MIN_DISTANCE, TRUE)
+
+/atom/movable/screen/blob/node_blob
+ // Name and description get given their proper values on Initialize()
+ name = "Produce Node Blob (ERROR)"
+ desc = "Produces a node blob for ERROR resources.
Node blobs will expand and activate nearby resource and factory blobs."
+ icon_state = "ui_node"
+
+/atom/movable/screen/blob/node_blob/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ name = "Produce Node Blob ([BLOB_STRUCTURE_NODE_COST])"
+ desc = "Produces a node blob for [BLOB_STRUCTURE_NODE_COST] resources.
Node blobs will expand and activate nearby resource and factory blobs."
+
+/atom/movable/screen/blob/node_blob/Click()
+ if(!isovermind(usr))
+ return FALSE
+ var/mob/eye/blob/blob = usr
+ blob.create_special(BLOB_STRUCTURE_NODE_COST, /obj/structure/blob/special/node, BLOB_NODE_MIN_DISTANCE, FALSE)
+
+/atom/movable/screen/blob/factory_blob
+ // Name and description get given their proper values on Initialize()
+ name = "Produce Factory Blob (ERROR)"
+ desc = "Produces a factory blob for ERROR resources.
Factory blobs will produce spores every few seconds."
+ icon_state = "ui_factory"
+
+/atom/movable/screen/blob/factory_blob/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ name = "Produce Factory Blob ([BLOB_STRUCTURE_FACTORY_COST])"
+ desc = "Produces a factory blob for [BLOB_STRUCTURE_FACTORY_COST] resources.
Factory blobs will produce spores every few seconds."
+
+/atom/movable/screen/blob/factory_blob/Click()
+ if(!isovermind(usr))
+ return FALSE
+ var/mob/eye/blob/blob = usr
+ blob.create_special(BLOB_STRUCTURE_FACTORY_COST, /obj/structure/blob/special/factory, BLOB_FACTORY_MIN_DISTANCE, TRUE)
+
+/atom/movable/screen/blob/readapt_strain
+ // Description gets given its proper values on Initialize()
+ name = "Readapt Strain"
+ desc = "Allows you to choose a new strain from ERROR random choices for ERROR resources."
+ icon_state = "ui_chemswap"
+ screen_loc = ui_storage1
+
+/atom/movable/screen/blob/readapt_strain/MouseEntered(location,control,params)
+ if(hud?.mymob && isovermind(hud.mymob))
+ var/mob/eye/blob/B = hud.mymob
+ if(B.free_strain_rerolls)
+ name = "[initial(name)] (FREE)"
+ desc = "Randomly rerolls your strain for free."
+ else
+ name = "[initial(name)] ([BLOB_POWER_REROLL_COST])"
+ desc = "Allows you to choose a new strain from [BLOB_POWER_REROLL_CHOICES] random choices for [BLOB_POWER_REROLL_COST] resources."
+ return ..()
+
+/atom/movable/screen/blob/readapt_strain/Click()
+ if(isovermind(usr))
+ var/mob/eye/blob/B = usr
+ B.strain_reroll()
+
+/atom/movable/screen/blob/relocate_core
+ // Name and description get given their proper values on Initialize()
+ name = "Relocate Core (ERROR)"
+ desc = "Swaps a node and your core for ERROR resources."
+ icon_state = "ui_swap"
+ screen_loc = ui_storage2
+
+/atom/movable/screen/blob/relocate_core/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ name = "Relocate Core ([BLOB_POWER_RELOCATE_COST])"
+ desc = "Swaps a node and your core for [BLOB_POWER_RELOCATE_COST] resources."
+
+/atom/movable/screen/blob/relocate_core/Click()
+ if(isovermind(usr))
+ var/mob/eye/blob/B = usr
+ B.relocate_core()
+
+/atom/movable/screen/blob/blob_power
+ name = "blob power"
+ icon_state = "block"
+ screen_loc = ui_health
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ plane = ABOVE_HUD_PLANE
diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/screen_objects/credits.dm
similarity index 100%
rename from code/_onclick/hud/credits.dm
rename to code/_onclick/hud/screen_objects/credits.dm
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/screen_objects/fullscreen.dm
similarity index 100%
rename from code/_onclick/hud/fullscreen.dm
rename to code/_onclick/hud/screen_objects/fullscreen.dm
diff --git a/code/_onclick/hud/screen_objects/ghost.dm b/code/_onclick/hud/screen_objects/ghost.dm
new file mode 100644
index 00000000000..532817e558a
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/ghost.dm
@@ -0,0 +1,149 @@
+/atom/movable/screen/ghost
+ icon = 'icons/hud/screen_ghost.dmi'
+ mouse_over_pointer = MOUSE_HAND_POINTER
+
+/atom/movable/screen/ghost/MouseEntered(location, control, params)
+ . = ..()
+ flick(icon_state + "_anim", src)
+
+/atom/movable/screen/ghost/spawners_menu
+ name = "Spawners menu"
+ icon_state = "spawners"
+ screen_loc = ui_ghost_spawners_menu
+
+/atom/movable/screen/ghost/spawners_menu/Click()
+ var/mob/dead/observer/observer = usr
+ observer.open_spawners_menu()
+
+/atom/movable/screen/ghost/orbit
+ name = "Orbit"
+ icon_state = "orbit"
+ screen_loc = ui_ghost_orbit
+
+/atom/movable/screen/ghost/orbit/Click()
+ GLOB.orbit_menu.show(usr)
+
+/atom/movable/screen/ghost/reenter_corpse
+ name = "Reenter corpse"
+ icon_state = "reenter_corpse"
+ screen_loc = ui_ghost_reenter_corpse
+
+/atom/movable/screen/ghost/reenter_corpse/Click()
+ var/mob/dead/observer/G = usr
+ G.reenter_corpse()
+
+/atom/movable/screen/ghost/dnr
+ name = "Do Not Resuscitate"
+ icon_state = "dnr"
+ screen_loc = ui_ghost_dnr
+
+/atom/movable/screen/ghost/dnr/Click()
+ var/mob/dead/observer/dnring = usr
+ dnring.do_not_resuscitate()
+
+/atom/movable/screen/ghost/teleport
+ name = "Teleport"
+ icon_state = "teleport"
+ screen_loc = ui_ghost_teleport
+
+/atom/movable/screen/ghost/teleport/Click()
+ var/mob/dead/observer/G = usr
+ G.dead_tele()
+
+/atom/movable/screen/ghost/settings
+ name = "Ghost Settings"
+ icon_state = "settings"
+ screen_loc = ui_ghost_settings
+
+/atom/movable/screen/ghost/settings/MouseEntered(location, control, params)
+ . = ..()
+ flick(icon_state + "_anim", src)
+
+/atom/movable/screen/ghost/settings/Click()
+ GLOB.ghost_menu.ui_interact(usr)
+
+/atom/movable/screen/ghost/minigames_menu
+ name ="Minigames"
+ icon_state = "minigames"
+ screen_loc = ui_ghost_minigames
+
+/atom/movable/screen/ghost/minigames_menu/Click()
+ var/mob/dead/observer/observer = usr
+ observer.open_minigames_menu()
+
+/atom/movable/screen/ghost/hudbox
+ icon_state = "smallbox"
+ abstract_type = /atom/movable/screen/ghost/hudbox
+ /// Icon state used for the overlay representing this hudbox
+ var/hud_icon_state
+ /// The flag this hudbox toggles
+ var/relevant_flag
+
+/atom/movable/screen/ghost/hudbox/update_overlays()
+ . = ..()
+ . += hud_icon_state
+
+/atom/movable/screen/ghost/hudbox/update_icon_state()
+ . = ..()
+ var/mob/dead/observer/observer = hud?.mymob
+ if(!istype(observer))
+ return
+
+ icon_state = "smallbox[is_active(observer) ? "_active" : ""]"
+
+/atom/movable/screen/ghost/hudbox/proc/is_active(mob/dead/observer/observer)
+ return (observer.ghost_hud_flags & relevant_flag)
+
+/atom/movable/screen/ghost/hudbox/Click(location, control, params)
+ var/mob/dead/observer/observer = usr
+ switch(relevant_flag)
+ if(GHOST_DARKNESS_LEVEL)
+ observer.toggle_darkness()
+ if(GHOST_TRAY)
+ observer.tray_view()
+ else
+ observer.toggle_ghost_hud_flag(relevant_flag)
+
+ update_appearance(UPDATE_ICON)
+
+/atom/movable/screen/ghost/hudbox/health_scanner
+ name = "Health Scanner"
+ desc = "Toggles your ability to health scan mobs on click."
+ hud_icon_state = "health_vision"
+ relevant_flag = GHOST_HEALTH
+
+/atom/movable/screen/ghost/hudbox/chem_scanner
+ name = "Chem Scanner"
+ desc = "Toggles your ability to chemical scan mobs on click."
+ hud_icon_state = "chem_vision"
+ relevant_flag = GHOST_CHEM
+
+/atom/movable/screen/ghost/hudbox/gas_scanner
+ name = "Gas Scanner"
+ desc = "Toggles your ability to gas scan objects on click."
+ hud_icon_state = "atmos_vision"
+ relevant_flag = GHOST_GAS
+
+/atom/movable/screen/ghost/hudbox/ghost
+ name = "Ghost Vision"
+ desc = "Toggles whether you can see other ghosts."
+ hud_icon_state = "ghost_vision"
+ relevant_flag = GHOST_VISION
+
+/atom/movable/screen/ghost/hudbox/data_huds
+ name = "Data HUDs"
+ desc = "Toggles the display of data HUDs (health, security, diagnostics, etc)."
+ hud_icon_state = "data_vision"
+ relevant_flag = GHOST_DATA_HUDS
+
+/atom/movable/screen/ghost/hudbox/tray_icon
+ name = "Tray View"
+ desc = "Shows the t-ray view of the area around your ghost."
+ hud_icon_state = "tray_vision"
+ relevant_flag = GHOST_TRAY
+
+/atom/movable/screen/ghost/hudbox/darkness_level
+ name = "Darkness Level"
+ desc = "Cycles through different darkness levels for ghost vision."
+ hud_icon_state = "darkness_vision"
+ relevant_flag = GHOST_DARKNESS_LEVEL
diff --git a/code/_onclick/hud/screen_objects/guardian.dm b/code/_onclick/hud/screen_objects/guardian.dm
new file mode 100644
index 00000000000..41282bc45ed
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/guardian.dm
@@ -0,0 +1,68 @@
+/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/human.dm b/code/_onclick/hud/screen_objects/human.dm
new file mode 100644
index 00000000000..b69ceb2143e
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/human.dm
@@ -0,0 +1,77 @@
+/atom/movable/screen/human
+ icon = 'icons/hud/screen_midnight.dmi'
+
+/atom/movable/screen/human/toggle
+ name = "toggle"
+ icon_state = "toggle"
+ base_icon_state = "toggle"
+ mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_inventory
+
+/atom/movable/screen/human/toggle/Click()
+ var/mob/targetmob = usr
+
+ if(isobserver(usr))
+ if(ishuman(usr.client.eye) && (usr.client.eye != usr))
+ var/mob/M = usr.client.eye
+ targetmob = M
+
+ if(usr.hud_used.inventory_shown && targetmob.hud_used)
+ usr.hud_used.inventory_shown = FALSE
+ usr.client.screen -= targetmob.hud_used.screen_groups[HUD_GROUP_TOGGLEABLE_INVENTORY]
+ else
+ usr.hud_used.inventory_shown = TRUE
+ usr.client.screen += targetmob.hud_used.screen_groups[HUD_GROUP_TOGGLEABLE_INVENTORY]
+
+ targetmob.hud_used.hidden_inventory_update(usr)
+ update_appearance()
+
+/atom/movable/screen/human/toggle/update_icon_state()
+ icon_state = "[base_icon_state][hud?.inventory_shown ? "_active" : ""]"
+ return ..()
+
+/atom/movable/screen/ling
+ icon = 'icons/hud/screen_changeling.dmi'
+
+/atom/movable/screen/ling/chems
+ name = "chemical storage"
+ icon_state = "power_display"
+ screen_loc = ui_lingchemdisplay
+ ///Boolean on whether a mouse is being hovered over us right now.
+ var/hovering = FALSE
+
+/atom/movable/screen/ling/chems/Click(location, control, params)
+ . = ..()
+ to_chat(usr, span_notice("Shows you how many chemicals you have. While hovering over this, it will show the max amount of chemicals you can hold."))
+
+/atom/movable/screen/ling/chems/MouseEntered(location,control,params)
+ if(usr != get_mob())
+ return
+ var/datum/antagonist/changeling/antagonist_datum = IS_CHANGELING(hud.mymob)
+ if(!antagonist_datum)
+ return
+ . = ..()
+ hovering = TRUE
+ antagonist_datum.update_chemical_hud()
+
+/atom/movable/screen/ling/chems/MouseExited(location, control, params)
+ if(usr != get_mob())
+ return
+ var/datum/antagonist/changeling/antagonist_datum = IS_CHANGELING(hud.mymob)
+ if(!antagonist_datum)
+ return
+ . = ..()
+ hovering = FALSE
+ antagonist_datum.update_chemical_hud(antagonist_datum.chem_charges)
+
+/atom/movable/screen/ling/sting
+ name = "current sting"
+ screen_loc = ui_lingstingdisplay
+ invisibility = INVISIBILITY_ABSTRACT
+ mouse_over_pointer = MOUSE_HAND_POINTER
+
+/atom/movable/screen/ling/sting/Click()
+ if(isobserver(usr))
+ return
+ var/mob/living/carbon/carbon_user = usr
+ carbon_user.unset_sting()
diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/screen_objects/map_popups.dm
similarity index 100%
rename from code/_onclick/hud/map_popups.dm
rename to code/_onclick/hud/screen_objects/map_popups.dm
diff --git a/code/_onclick/hud/map_view.dm b/code/_onclick/hud/screen_objects/map_view.dm
similarity index 100%
rename from code/_onclick/hud/map_view.dm
rename to code/_onclick/hud/screen_objects/map_view.dm
diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/screen_objects/movable_screen_objects.dm
similarity index 100%
rename from code/_onclick/hud/movable_screen_objects.dm
rename to code/_onclick/hud/screen_objects/movable_screen_objects.dm
diff --git a/code/_onclick/hud/screen_objects/new_player.dm b/code/_onclick/hud/screen_objects/new_player.dm
new file mode 100644
index 00000000000..5a17abfc5d9
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/new_player.dm
@@ -0,0 +1,724 @@
+#define SHUTTER_MOVEMENT_DURATION 0.4 SECONDS
+#define SHUTTER_WAIT_DURATION 0.2 SECONDS
+
+/atom/movable/screen/lobby
+ plane = SPLASHSCREEN_PLANE
+ layer = LOBBY_MENU_LAYER
+ screen_loc = "TOP,CENTER"
+ /// Whether this HUD element can be hidden from the client's "screen" (moved off-screen) or not
+ var/always_shown = FALSE
+ /// If true we will create this button every time the HUD is generated
+ var/always_available = TRUE
+
+///Set the HUD in New, as lobby screens are made before Atoms are Initialized.
+/atom/movable/screen/lobby/New(loc, datum/hud/our_hud, ...)
+ set_new_hud(our_hud)
+ return ..()
+
+///Run sleeping actions after initialize
+/atom/movable/screen/lobby/proc/SlowInit()
+ return
+
+///Animates moving the button off-screen
+/atom/movable/screen/lobby/proc/collapse_button()
+ SIGNAL_HANDLER
+ //wait for the shutter to come down
+ animate(src, transform = transform, time = SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
+ //then pull the buttons up with the shutter
+ animate(transform = transform.Translate(x = 0, y = 146), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
+
+///Animates moving the button back into place
+/atom/movable/screen/lobby/proc/expand_button()
+ SIGNAL_HANDLER
+ //the buttons are off-screen, so we sync them up to come down with the shutter
+ animate(src, transform = matrix(), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_OUT)
+
+/atom/movable/screen/lobby/background
+ icon = 'icons/hud/lobby/background.dmi'
+ icon_state = "background"
+ layer = LOBBY_BACKGROUND_LAYER
+ screen_loc = "TOP,CENTER:-61"
+
+/atom/movable/screen/lobby/button
+ mouse_over_pointer = MOUSE_HAND_POINTER
+ ///Is the button currently enabled?
+ VAR_PROTECTED/enabled = TRUE
+ ///Is the button currently being hovered over with the mouse?
+ var/highlighted = FALSE
+ ///Should this button play the select sound?
+ var/select_sound_play = TRUE
+
+/atom/movable/screen/lobby/button/Click(location, control, params)
+ if(usr != get_mob())
+ return
+
+ if(!usr.client || usr.client.interviewee)
+ return
+
+ . = ..()
+
+ if(!enabled)
+ return
+ flick("[base_icon_state]_pressed", src)
+ if(select_sound_play)
+ var/sound/ui_select_sound = sound('sound/misc/menu/ui_select1.ogg')
+ ui_select_sound.frequency = get_rand_frequency_low_range()
+ SEND_SOUND(hud.mymob, ui_select_sound)
+ update_appearance(UPDATE_ICON)
+ return TRUE
+
+/atom/movable/screen/lobby/button/MouseEntered(location,control,params)
+ if(usr != get_mob())
+ return
+
+ if(!usr.client || usr.client.interviewee)
+ return
+
+ . = ..()
+ highlighted = TRUE
+ update_appearance(UPDATE_ICON)
+
+/atom/movable/screen/lobby/button/MouseExited()
+ if(usr != get_mob())
+ return
+
+ if(!usr.client || usr.client.interviewee)
+ return
+
+ . = ..()
+ highlighted = FALSE
+ update_appearance(UPDATE_ICON)
+
+/atom/movable/screen/lobby/button/update_icon(updates)
+ . = ..()
+ if(!enabled)
+ icon_state = "[base_icon_state]_disabled"
+ return
+ if(highlighted)
+ icon_state = "[base_icon_state]_highlighted"
+ return
+ icon_state = base_icon_state
+
+///Updates the button's status: TRUE to enable interaction with the button, FALSE to disable
+/atom/movable/screen/lobby/button/proc/set_button_status(status)
+ if(status == enabled)
+ return FALSE
+ enabled = status
+ update_appearance(UPDATE_ICON)
+ mouse_over_pointer = enabled ? MOUSE_HAND_POINTER : MOUSE_INACTIVE_POINTER
+ return TRUE
+
+///Prefs menu
+/atom/movable/screen/lobby/button/character_setup
+ name = "View Character Setup"
+ screen_loc = "TOP:-70,CENTER:-54"
+ icon = 'icons/hud/lobby/character_setup.dmi'
+ icon_state = "character_setup_disabled"
+ base_icon_state = "character_setup"
+ enabled = FALSE
+
+/atom/movable/screen/lobby/button/character_setup/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ // We need IconForge and the assets to be ready before allowing the menu to open
+ if(SSearly_assets.initialized == INITIALIZATION_INNEW_REGULAR || SSatoms.initialized == INITIALIZATION_INNEW_REGULAR)
+ flick("[base_icon_state]_enabled", src)
+ set_button_status(TRUE)
+ else
+ set_button_status(FALSE)
+ RegisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_character_setup))
+ RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_character_setup))
+
+/atom/movable/screen/lobby/button/character_setup/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+
+ var/datum/preferences/preferences = hud.mymob.canon_client.prefs
+ preferences.current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES
+ preferences.update_static_data(usr)
+ preferences.ui_interact(usr)
+
+/atom/movable/screen/lobby/button/character_setup/proc/enable_character_setup()
+ SIGNAL_HANDLER
+ flick("[base_icon_state]_enabled", src)
+ set_button_status(TRUE)
+ UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
+ UnregisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE)
+
+///Button that appears before the game has started
+/atom/movable/screen/lobby/button/ready
+ name = "Toggle Readiness"
+ screen_loc = "TOP:-8,CENTER:-65"
+ icon = 'icons/hud/lobby/ready.dmi'
+ icon_state = "not_ready"
+ base_icon_state = "not_ready"
+
+/atom/movable/screen/lobby/button/ready/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ switch(SSticker.current_state)
+ if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(hide_ready_button))
+ if(GAME_STATE_SETTING_UP)
+ set_button_status(FALSE)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(show_ready_button))
+ else
+ set_button_status(FALSE)
+
+/atom/movable/screen/lobby/button/ready/proc/hide_ready_button()
+ SIGNAL_HANDLER
+ set_button_status(FALSE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(show_ready_button))
+
+/atom/movable/screen/lobby/button/ready/proc/show_ready_button()
+ SIGNAL_HANDLER
+ set_button_status(TRUE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(hide_ready_button))
+
+/atom/movable/screen/lobby/button/ready/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+
+ // switch based on the user, if they aren't ready then we change them to ready, and vice versa
+ if(new_player.ready == PLAYER_NOT_READY)
+ new_player.auto_deadmin_on_ready_or_latejoin()
+ new_player.ready = PLAYER_READY_TO_PLAY
+ base_icon_state = "ready"
+ else
+ new_player.ready = PLAYER_NOT_READY
+ base_icon_state = "not_ready"
+
+ update_appearance(UPDATE_ICON)
+ SEND_SIGNAL(hud, COMSIG_HUD_PLAYER_READY_TOGGLE)
+
+///Shown when the game has started
+/atom/movable/screen/lobby/button/join
+ name = "Join Game"
+ screen_loc = "TOP:-13,CENTER:-58"
+ icon = 'icons/hud/lobby/join.dmi'
+ icon_state = "" //Default to not visible
+ base_icon_state = "join_game"
+ enabled = null // set in init
+
+/atom/movable/screen/lobby/button/join/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ switch(SSticker.current_state)
+ if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
+ set_button_status(FALSE)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(show_join_button))
+ if(GAME_STATE_SETTING_UP)
+ set_button_status(TRUE)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(hide_join_button))
+ else
+ set_button_status(TRUE)
+
+/atom/movable/screen/lobby/button/join/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+
+ if(!SSticker?.IsRoundInProgress())
+ to_chat(hud.mymob, span_boldwarning("The round is either not ready, or has already finished..."))
+ return
+
+ //Determines Relevent Population Cap
+ var/relevant_cap
+ var/hard_popcap = CONFIG_GET(number/hard_popcap)
+ var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
+ if(hard_popcap && extreme_popcap)
+ relevant_cap = min(hard_popcap, extreme_popcap)
+ else
+ relevant_cap = max(hard_popcap, extreme_popcap)
+
+ var/mob/dead/new_player/new_player = hud.mymob
+
+ if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(new_player.key) in GLOB.admin_datums)))
+ to_chat(new_player, span_danger("[CONFIG_GET(string/hard_popcap_message)]"))
+
+ var/queue_position = SSticker.queued_players.Find(new_player)
+ if(queue_position == 1)
+ to_chat(new_player, span_notice("You are next in line to join the game. You will be notified when a slot opens up."))
+ else if(queue_position)
+ to_chat(new_player, span_notice("There are [queue_position-1] players in front of you in the queue to join the game."))
+ else
+ SSticker.queued_players += new_player
+ to_chat(new_player, span_notice("You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len]."))
+ return
+
+ new_player.auto_deadmin_on_ready_or_latejoin()
+
+ if(!LAZYACCESS(params2list(params), CTRL_CLICK))
+ GLOB.latejoin_menu.ui_interact(new_player)
+ else
+ to_chat(new_player, span_warning("Opening emergency fallback late join menu! If THIS doesn't show, ahelp immediately!"))
+ GLOB.latejoin_menu.fallback_ui(new_player)
+
+
+/atom/movable/screen/lobby/button/join/proc/show_join_button()
+ SIGNAL_HANDLER
+ set_button_status(TRUE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(hide_join_button))
+
+/atom/movable/screen/lobby/button/join/proc/hide_join_button()
+ SIGNAL_HANDLER
+ set_button_status(FALSE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(show_join_button))
+
+/atom/movable/screen/lobby/button/observe
+ name = "Observe"
+ screen_loc = "TOP:-40,CENTER:-54"
+ icon = 'icons/hud/lobby/observe.dmi'
+ icon_state = "observe_disabled"
+ base_icon_state = "observe"
+ enabled = null // set in init
+
+/atom/movable/screen/lobby/button/observe/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ if(SSticker.current_state > GAME_STATE_STARTUP)
+ set_button_status(TRUE)
+ else
+ set_button_status(FALSE)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, PROC_REF(enable_observing))
+
+/atom/movable/screen/lobby/button/observe/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ new_player.make_me_an_observer()
+
+/atom/movable/screen/lobby/button/observe/proc/enable_observing()
+ SIGNAL_HANDLER
+ flick("[base_icon_state]_enabled", src)
+ set_button_status(TRUE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME)
+
+//Subtype the bottom buttons away so the collapse/expand shutter goes behind them
+/atom/movable/screen/lobby/button/bottom
+ layer = LOBBY_BOTTOM_BUTTON_LAYER
+ icon = 'icons/hud/lobby/bottom_buttons.dmi'
+
+/atom/movable/screen/lobby/button/bottom/settings
+ name = "View Game Preferences"
+ icon_state = "settings_disabled"
+ base_icon_state = "settings"
+ screen_loc = "TOP:-122,CENTER:+29"
+ enabled = FALSE
+
+/atom/movable/screen/lobby/button/bottom/settings/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ // We need IconForge and the assets to be ready before allowing the menu to open
+ if(SSearly_assets.initialized == INITIALIZATION_INNEW_REGULAR || SSatoms.initialized == INITIALIZATION_INNEW_REGULAR)
+ set_button_status(TRUE)
+ else
+ set_button_status(FALSE)
+ RegisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_settings))
+ RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(enable_settings))
+
+/atom/movable/screen/lobby/button/bottom/settings/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+
+ var/datum/preferences/preferences = hud.mymob.canon_client.prefs
+ preferences.current_window = PREFERENCE_TAB_GAME_PREFERENCES
+ preferences.update_static_data(usr)
+ preferences.ui_interact(usr)
+
+/atom/movable/screen/lobby/button/bottom/settings/proc/enable_settings()
+ SIGNAL_HANDLER
+ set_button_status(TRUE)
+ UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
+ UnregisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE)
+
+/atom/movable/screen/lobby/button/bottom/changelog_button
+ name = "View Changelog"
+ icon_state = "changelog"
+ base_icon_state = "changelog"
+ screen_loc ="TOP:-122,CENTER:+57"
+
+/atom/movable/screen/lobby/button/bottom/changelog_button/Click(location, control, params)
+ . = ..()
+ usr.client?.changelog()
+
+/atom/movable/screen/lobby/button/bottom/crew_manifest
+ name = "View Crew Manifest"
+ icon_state = "crew_manifest"
+ base_icon_state = "crew_manifest"
+ screen_loc = "TOP:-122,CENTER:+2"
+
+/atom/movable/screen/lobby/button/bottom/crew_manifest/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ new_player.ViewManifest()
+
+/atom/movable/screen/lobby/button/bottom/poll
+ name = "View Available Polls"
+ icon_state = "poll"
+ base_icon_state = "poll"
+ screen_loc = "TOP:-122,CENTER:-26"
+ ///Whether the button should have a New Poll notification overlay
+ var/new_poll = FALSE
+
+/atom/movable/screen/lobby/button/bottom/poll/SlowInit(mapload)
+ . = ..()
+ if(!usr)
+ return
+ var/mob/dead/new_player/new_player = usr
+ if(is_guest_key(new_player.key))
+ set_button_status(FALSE)
+ return
+ if(!SSdbcore.Connect())
+ set_button_status(FALSE)
+ return
+ var/isadmin = FALSE
+ if(new_player.client?.holder)
+ isadmin = TRUE
+ var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({"
+ SELECT id FROM [format_table_name("poll_question")]
+ WHERE (adminonly = 0 OR :isadmin = 1)
+ AND Now() BETWEEN starttime AND endtime
+ AND deleted = 0
+ AND id NOT IN (
+ SELECT pollid FROM [format_table_name("poll_vote")]
+ WHERE ckey = :ckey
+ AND deleted = 0
+ )
+ AND id NOT IN (
+ SELECT pollid FROM [format_table_name("poll_textreply")]
+ WHERE ckey = :ckey
+ AND deleted = 0
+ )
+ "}, list("isadmin" = isadmin, "ckey" = new_player.ckey))
+ if(!query_get_new_polls.Execute())
+ qdel(query_get_new_polls)
+ set_button_status(FALSE)
+ return
+ if(query_get_new_polls.NextRow())
+ new_poll = TRUE
+ else
+ new_poll = FALSE
+ update_appearance(UPDATE_OVERLAYS)
+ qdel(query_get_new_polls)
+ if(QDELETED(new_player))
+ set_button_status(FALSE)
+ return
+
+/atom/movable/screen/lobby/button/bottom/poll/update_overlays()
+ . = ..()
+ if(new_poll)
+ . += mutable_appearance('icons/hud/lobby/poll_overlay.dmi', "new_poll")
+
+/atom/movable/screen/lobby/button/bottom/poll/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ new_player.handle_player_polling()
+
+/// A generic "sign up" button used by station traits
+/atom/movable/screen/lobby/button/sign_up
+ icon = 'icons/hud/lobby/signup_button.dmi'
+ icon_state = "signup"
+ base_icon_state = "signup"
+ always_available = FALSE
+
+/atom/movable/screen/lobby/button/sign_up/MouseEntered(location, control, params)
+ . = ..()
+ if(QDELETED(src) || !desc)
+ return
+ openToolTip(usr, tip_src = src, params = params, title = name, content = desc,)
+
+/atom/movable/screen/lobby/button/sign_up/MouseExited()
+ . = ..()
+ closeToolTip(usr)
+
+/atom/movable/screen/lobby/button/collapse
+ name = "Collapse Lobby Menu"
+ icon = 'icons/hud/lobby/collapse_expand.dmi'
+ icon_state = "collapse"
+ base_icon_state = "collapse"
+ layer = LOBBY_BELOW_MENU_LAYER
+ screen_loc = "TOP:-82,CENTER:-54"
+ always_shown = TRUE
+
+ var/blip_enabled = TRUE
+
+/atom/movable/screen/lobby/button/collapse/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ switch(SSticker.current_state)
+ if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(disable_blip))
+ RegisterSignal(hud, COMSIG_HUD_PLAYER_READY_TOGGLE, PROC_REF(on_player_ready_toggle))
+ if(GAME_STATE_SETTING_UP)
+ blip_enabled = FALSE
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(enable_blip))
+ else
+ blip_enabled = FALSE
+
+ add_overlay(get_blip_overlay())
+ update_icon(UPDATE_OVERLAYS)
+
+/atom/movable/screen/lobby/button/collapse/update_overlays()
+ . = ..()
+ . += get_blip_overlay()
+
+/atom/movable/screen/lobby/button/collapse/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+
+ if(!istype(hud, /datum/hud/new_player))
+ return
+ var/datum/hud/new_player/our_hud = hud
+ base_icon_state = our_hud.menu_hud_status ? "expand" : "collapse"
+ name = "[our_hud.menu_hud_status ? "Expand" : "Collapse"] Lobby Menu"
+ set_button_status(FALSE)
+
+ //get the shutter object used by our hud
+ var/atom/movable/screen/lobby/shutter/menu_shutter = locate(/atom/movable/screen/lobby/shutter) in hud.screen_groups[HUD_GROUP_STATIC]
+
+ //animate the shutter
+ menu_shutter.setup_shutter_animation()
+ //animate bottom buttons' movement
+ if(our_hud.menu_hud_status)
+ collapse_menu()
+ else
+ expand_menu()
+ our_hud.menu_hud_status = !our_hud.menu_hud_status
+
+ //re-enable clicking the button when the shutter animation finishes
+ //we use sleep here so it can work during game setup, as addtimer would not work until the game would finish setting up
+ sleep(2 * SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
+ set_button_status(TRUE)
+
+///Proc to update the ready blip state upon new player's ready status change
+/atom/movable/screen/lobby/button/collapse/proc/on_player_ready_toggle()
+ SIGNAL_HANDLER
+ update_appearance(UPDATE_ICON)
+
+///Returns a ready blip overlay depending on the player's ready state
+/atom/movable/screen/lobby/button/collapse/proc/get_blip_overlay()
+ var/blip_icon_state = "ready_blip"
+ if(blip_enabled && hud)
+ var/mob/dead/new_player/new_player = hud.mymob
+ blip_icon_state += "_[new_player.is_ready_to_play() ? "" : "not_"]ready"
+ else
+ blip_icon_state += "_disabled"
+ var/mutable_appearance/ready_blip = mutable_appearance(icon, blip_icon_state)
+ return ready_blip
+
+///Disables the ready blip; makes us listen for the setup error to re-enable the blip
+/atom/movable/screen/lobby/button/collapse/proc/disable_blip()
+ SIGNAL_HANDLER
+ blip_enabled = FALSE
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, PROC_REF(enable_blip))
+ update_appearance(UPDATE_ICON)
+
+///Enables the ready blip; makes us listen for the setup completion and game start to disable the blip
+/atom/movable/screen/lobby/button/collapse/proc/enable_blip()
+ SIGNAL_HANDLER
+ blip_enabled = TRUE
+ UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(disable_blip))
+ update_appearance(UPDATE_ICON)
+
+///Moves the button to the top of the screen, leaving only the screen part in view
+///Sends a signal on the hud for the menu hud elements to listen to
+/atom/movable/screen/lobby/button/collapse/proc/collapse_menu()
+ SEND_SIGNAL(hud, COMSIG_HUD_LOBBY_COLLAPSED)
+ //wait for the shutter to come down
+ animate(src, transform = transform, time = SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
+ //then pull the button up with the shutter and leave it on the edge of the screen
+ animate(transform = transform.Translate(x = 0, y = 134), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
+ SEND_SOUND(hud.mymob, sound('sound/misc/menu/menu_rollup1.ogg'))
+
+///Extends the button back to its usual spot
+///Sends a signal on the hud for the menu hud elements to listen to
+/atom/movable/screen/lobby/button/collapse/proc/expand_menu()
+ SEND_SIGNAL(hud, COMSIG_HUD_LOBBY_EXPANDED)
+ animate(src, transform = matrix(), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_OUT)
+ SEND_SOUND(hud.mymob, sound('sound/misc/menu/menu_rolldown1.ogg'))
+
+/atom/movable/screen/lobby/shutter
+ icon = 'icons/hud/lobby/shutter.dmi'
+ icon_state = "shutter"
+ base_icon_state = "shutter"
+ screen_loc = "TOP:+143,CENTER:-73" //"home" position is off-screen
+ layer = LOBBY_SHUTTER_LAYER
+ always_shown = TRUE
+
+///Sets up the shutter pulling down and up. It's the same animation for both collapsing and expanding the menu.
+/atom/movable/screen/lobby/shutter/proc/setup_shutter_animation()
+ //bring down the shutter
+ animate(src, transform = transform.Translate(x = 0, y = -143), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_OUT)
+
+ //wait a little bit before bringing the shutter up
+ animate(transform = transform, time = SHUTTER_WAIT_DURATION)
+
+ //pull the shutter back off-screen
+ animate(transform = matrix(), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
+
+/// LOCALHOST ONLY - Start Now button
+/atom/movable/screen/lobby/button/start_now
+ name = "Start Now (LOCALHOST ONLY)"
+ screen_loc = "TOP:-146,CENTER:-54"
+ icon = 'icons/hud/lobby/start_now.dmi'
+ icon_state = "start_now"
+ base_icon_state = "start_now"
+ always_available = FALSE
+ select_sound_play = FALSE
+
+/atom/movable/screen/lobby/button/start_now/Click(location, control, params)
+ . = ..()
+ if(!. || !usr.client.is_localhost() || !check_rights_for(usr.client, R_SERVER))
+ return
+ SEND_SOUND(hud.mymob, sound('sound/effects/cartoon_sfx/cartoon_splat.ogg', volume = 50))
+ SSticker.start_immediately = TRUE
+ if(SSticker.current_state == GAME_STATE_STARTUP)
+ to_chat(usr, span_admin("The server is still setting up, but the round will be started as soon as possible."))
+
+#define OVERLAY_X_DIFF 12
+#define OVERLAY_Y_DIFF 5
+
+///Lobby screen that appears before the game has started showing how many players there are and who is ready.
+/atom/movable/screen/lobby/new_player_info
+ name = "New Player Info"
+ screen_loc = "EAST-3,CENTER:140"
+ icon = 'icons/hud/lobby/newplayer.dmi'
+ icon_state = null //we only show up when we get update appearance called, cause we need our overlay to not look bad.
+ base_icon_state = "newplayer"
+ maptext_height = 75
+ maptext_width = 80
+ maptext_x = OVERLAY_X_DIFF
+ maptext_y = OVERLAY_Y_DIFF
+
+ ///Boolean on whether or not we should have our static overlay, so we 'turn' the TV off when collapsing.
+ var/show_static = TRUE
+ ///Static mutable appearance of a job icon we're displaying on the TV for overflow job.
+ var/static/mutable_appearance/job_overlay
+
+/atom/movable/screen/lobby/new_player_info/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ START_PROCESSING(SSnewplayer_info, src)
+ update_text()
+
+ //only go if the station trait is selected, otherwise it'll show "Assistant" every round.
+ var/datum/station_trait/overflow_job_bureaucracy/overflow_job = locate() in SSstation.station_traits
+ if(overflow_job && isnull(job_overlay))
+ var/icon/job_icon = get_job_hud_icon(SSjob.overflow_role)
+ if(isnull(job_icon))
+ stack_trace("[SSjob.overflow_role::title] was selected as the Job Overflow but has no icon!")
+ else
+ job_icon.Scale(ICON_SIZE_X / 2, ICON_SIZE_X / 2)
+ job_overlay = mutable_appearance(job_icon, offset_spokesman = src, alpha = 120, layer = src.layer+0.02)
+ var/matrix/job_matrix = matrix()
+ job_matrix.Translate(6, 14) //this is completely arbitruary just what I thought looked good.
+ job_overlay.transform = job_matrix
+
+ update_appearance(UPDATE_ICON)
+
+/atom/movable/screen/lobby/new_player_info/Destroy()
+ STOP_PROCESSING(SSnewplayer_info, src)
+ return ..()
+
+/atom/movable/screen/lobby/new_player_info/MouseEntered(location, control, params)
+ . = ..()
+ if(QDELETED(src) || isnull(job_overlay))
+ return
+ openToolTip(usr, src, params, title = "[SSjob.overflow_role::title] overflow", content = "The overflow for the round has been set as [SSjob.overflow_role::title].")
+
+/atom/movable/screen/lobby/new_player_info/MouseExited()
+ closeToolTip(usr)
+ return ..()
+
+/atom/movable/screen/lobby/new_player_info/update_icon_state()
+ . = ..()
+ icon_state = base_icon_state
+
+/atom/movable/screen/lobby/new_player_info/update_overlays()
+ . = ..()
+ . += mutable_appearance(icon, "[base_icon_state]_overlay", layer = src.layer+0.01)
+ if(!show_static)
+ return .
+ if(job_overlay)
+ . += job_overlay
+ . += mutable_appearance(icon, "static_base", alpha = 20, layer = src.layer+0.03)
+ //we have this in a separate file because `generate_icon_alpha_mask` puts lighting even on non-existent pixels,
+ //giving the icon a weird background color.
+ var/mutable_appearance/scanline = mutable_appearance(generate_icon_alpha_mask('icons/hud/lobby/newplayer_scanline.dmi', "scanline"), alpha = 20, layer = src.layer+0.04)
+ scanline.pixel_y = OVERLAY_X_DIFF
+ scanline.pixel_x = OVERLAY_Y_DIFF
+ . += scanline
+
+/atom/movable/screen/lobby/new_player_info/process(seconds_per_tick)
+ update_text()
+
+/atom/movable/screen/lobby/new_player_info/collapse_button()
+ show_static = FALSE
+ update_text()
+ //to be in sync with parent, we'll turn the TV off in this time instead.
+ animate(src, appearance = update_appearance(UPDATE_ICON), time = SHUTTER_MOVEMENT_DURATION + SHUTTER_WAIT_DURATION)
+ //we go to the right, not up
+ animate(transform = transform.Translate(x = 146, y = 0), time = SHUTTER_MOVEMENT_DURATION, easing = CUBIC_EASING|EASE_IN)
+
+/atom/movable/screen/lobby/new_player_info/expand_button()
+ . = ..()
+ show_static = TRUE
+ update_appearance(UPDATE_ICON)
+ update_text()
+
+/atom/movable/screen/lobby/new_player_info/proc/update_text()
+ if(!hud || !show_static)
+ maptext = null
+ return
+
+ var/round_started = SSticker.HasRoundStarted()
+ if(!MC_RUNNING())
+ maptext = MAPTEXT("[(round_started ? null : "Starting in [time_remaining_str()]
")]Loading...")
+ return
+
+ if(SSticker.IsPostgame())
+ maptext = MAPTEXT("Game ended,
\
+ restart soon")
+ return
+
+ var/new_maptext
+ if(round_started)
+ new_maptext = "[SSmapping.current_map.map_name]
\
+ [LAZYLEN(GLOB.clients)] player\s online
\
+ [ROUND_TIME()] in
"
+ new_maptext += ""
+ else
+ if(hud.mymob.client?.holder)
+ new_maptext = "Starting in [time_remaining_str()]
\
+ [LAZYLEN(GLOB.clients)] player\s
\
+ [SSticker.totalPlayersReady] players ready
\
+ [SSticker.total_admins_ready] / [length(GLOB.admins)] admins ready"
+ else
+ new_maptext = "[time_remaining_str()]
\
+ [LAZYLEN(GLOB.clients)] player\s"
+
+ maptext = MAPTEXT(new_maptext)
+
+/atom/movable/screen/lobby/new_player_info/proc/time_remaining_str()
+ var/time_remaining = SSticker.GetTimeLeft()
+ if(time_remaining > 0)
+ return "[round(time_remaining/10)]s"
+ if(time_remaining == -10)
+ return "DELAYED"
+ return "SOON"
+
+#undef OVERLAY_X_DIFF
+#undef OVERLAY_Y_DIFF
+
+#undef SHUTTER_MOVEMENT_DURATION
+#undef SHUTTER_WAIT_DURATION
diff --git a/code/modules/pai/hud.dm b/code/_onclick/hud/screen_objects/pai.dm
similarity index 57%
rename from code/modules/pai/hud.dm
rename to code/_onclick/hud/screen_objects/pai.dm
index fb1fd483116..0965787ef42 100644
--- a/code/modules/pai/hud.dm
+++ b/code/_onclick/hud/screen_objects/pai.dm
@@ -17,6 +17,7 @@
/atom/movable/screen/pai/software
name = "Software Interface"
icon_state = "pai"
+ screen_loc = ui_pai_software
/atom/movable/screen/pai/software/Click()
if(!..())
@@ -27,6 +28,7 @@
/atom/movable/screen/pai/shell
name = "Toggle Holoform"
icon_state = "pai_holoform"
+ screen_loc = ui_pai_shell
/atom/movable/screen/pai/shell/Click()
if(!..())
@@ -40,6 +42,7 @@
/atom/movable/screen/pai/chassis
name = "Holochassis Appearance Composite"
icon_state = "pai_chassis"
+ screen_loc = ui_pai_chassis
/atom/movable/screen/pai/chassis/Click()
if(!..())
@@ -50,6 +53,7 @@
/atom/movable/screen/pai/rest
name = "Rest"
icon_state = "pai_rest"
+ screen_loc = ui_pai_rest
/atom/movable/screen/pai/rest/Click()
if(!..())
@@ -60,6 +64,7 @@
/atom/movable/screen/pai/light
name = "Toggle Integrated Lights"
icon_state = "light"
+ screen_loc = ui_pai_light
/atom/movable/screen/pai/light/Click()
if(!..())
@@ -70,6 +75,7 @@
/atom/movable/screen/pai/newscaster
name = "pAI Newscaster"
icon_state = "newscaster"
+ screen_loc = ui_pai_newscaster
required_software = "Newscaster"
/atom/movable/screen/pai/newscaster/Click()
@@ -81,6 +87,7 @@
/atom/movable/screen/pai/host_monitor
name = "Host Health Scan"
icon_state = "host_monitor"
+ screen_loc = ui_pai_host_monitor
required_software = "Host Scan"
/atom/movable/screen/pai/host_monitor/Click(location, control, params)
@@ -99,6 +106,7 @@
/atom/movable/screen/pai/crew_manifest
name = "Crew Manifest"
icon_state = "manifest"
+ screen_loc = ui_pai_crew_manifest
required_software = "Crew Manifest"
/atom/movable/screen/pai/crew_manifest/Click()
@@ -110,6 +118,7 @@
/atom/movable/screen/pai/state_laws
name = "State Laws"
icon_state = "state_laws"
+ screen_loc = ui_pai_state_laws
/atom/movable/screen/pai/state_laws/Click()
if(!..())
@@ -120,18 +129,21 @@
/atom/movable/screen/pai/modpc
name = "Messenger"
icon_state = "pda_send"
+ screen_loc = ui_pai_mod_int
required_software = "Digital Messenger"
- var/mob/living/silicon/pai/pAI
/atom/movable/screen/pai/modpc/Click()
. = ..()
if(!.) // this works for some reason.
return
- pAI.modularInterface?.interact(pAI)
+ var/mob/living/silicon/pai/pAI = usr
+ if (istype(pAI))
+ pAI.modularInterface?.interact(pAI)
/atom/movable/screen/pai/internal_gps
name = "Internal GPS"
icon_state = "internal_gps"
+ screen_loc = ui_pai_internal_gps
required_software = "Internal GPS"
/atom/movable/screen/pai/internal_gps/Click()
@@ -146,6 +158,7 @@
/atom/movable/screen/pai/image_take
name = "Take Image"
icon_state = "take_picture"
+ screen_loc = ui_pai_take_picture
required_software = "Photography Module"
/atom/movable/screen/pai/image_take/Click()
@@ -158,6 +171,7 @@
/atom/movable/screen/pai/image_view
name = "View Images"
icon_state = "view_images"
+ screen_loc = ui_pai_view_images
required_software = "Photography Module"
/atom/movable/screen/pai/image_view/Click()
@@ -170,6 +184,7 @@
name = "radio"
icon = 'icons/hud/screen_cyborg.dmi'
icon_state = "radio"
+ screen_loc = ui_pai_radio
/atom/movable/screen/pai/radio/Click()
if(!..())
@@ -177,109 +192,4 @@
var/mob/living/silicon/pai/pAI = usr
pAI.radio.interact(usr)
-/datum/hud/pai
- ui_style = 'icons/hud/screen_pai.dmi'
-
-/datum/hud/pai/New(mob/living/silicon/pai/owner)
- ..()
- var/atom/movable/screen/using
-
-// Software menu
- using = new /atom/movable/screen/pai/software(null, src)
- using.screen_loc = ui_pai_software
- static_inventory += using
-
-// Holoform
- using = new /atom/movable/screen/pai/shell(null, src)
- using.screen_loc = ui_pai_shell
- static_inventory += using
-
-// Chassis Select Menu
- using = new /atom/movable/screen/pai/chassis(null, src)
- using.screen_loc = ui_pai_chassis
- static_inventory += using
-
-// Rest
- using = new /atom/movable/screen/pai/rest(null, src)
- using.screen_loc = ui_pai_rest
- static_inventory += using
-
-// Integrated Light
- using = new /atom/movable/screen/pai/light(null, src)
- using.screen_loc = ui_pai_light
- static_inventory += using
-
-// Newscaster
- using = new /atom/movable/screen/pai/newscaster(null, src)
- using.screen_loc = ui_pai_newscaster
- static_inventory += using
-
-// Language menu
- using = new /atom/movable/screen/language_menu(null, src)
- using.icon = ui_style
- using.screen_loc = ui_pai_language_menu
- static_inventory += using
-
-// Memories
- using = new /atom/movable/screen/memories(null, src)
- using.icon = ui_style
- using.screen_loc = ui_pai_memories_menu
- static_inventory += using
-
-// Navigation
- using = new /atom/movable/screen/navigate(null, src)
- using.icon = ui_style
- using.screen_loc = ui_pai_navigate_menu
- static_inventory += using
-
-// Host Monitor
- using = new /atom/movable/screen/pai/host_monitor(null, src)
- using.screen_loc = ui_pai_host_monitor
- static_inventory += using
-
-// Crew Manifest
- using = new /atom/movable/screen/pai/crew_manifest(null, src)
- using.screen_loc = ui_pai_crew_manifest
- static_inventory += using
-
-// Laws
- using = new /atom/movable/screen/pai/state_laws(null, src)
- using.screen_loc = ui_pai_state_laws
- static_inventory += using
-
-// Modular Interface
- using = new /atom/movable/screen/pai/modpc(null, src)
- using.screen_loc = ui_pai_mod_int
- static_inventory += using
- var/atom/movable/screen/pai/modpc/tablet_button = using
- tablet_button.pAI = mymob
-
-// Internal GPS
- using = new /atom/movable/screen/pai/internal_gps(null, src)
- using.screen_loc = ui_pai_internal_gps
- static_inventory += using
-
-// Take image
- using = new /atom/movable/screen/pai/image_take(null, src)
- using.screen_loc = ui_pai_take_picture
- static_inventory += using
-
-// View images
- using = new /atom/movable/screen/pai/image_view(null, src)
- using.screen_loc = ui_pai_view_images
- static_inventory += using
-
-// Radio
- using = new /atom/movable/screen/pai/radio(null, src)
- using.screen_loc = ui_pai_radio
- static_inventory += using
-
- update_software_buttons()
-
-/datum/hud/pai/proc/update_software_buttons()
- var/mob/living/silicon/pai/owner = mymob
- for(var/atom/movable/screen/pai/button in static_inventory)
- if(button.required_software)
- button.color = owner.installed_software.Find(button.required_software) ? null : COLOR_GRAY
-
#undef PAI_MISSING_SOFTWARE_MESSAGE
diff --git a/code/_onclick/hud/screen_objects/perks.dm b/code/_onclick/hud/screen_objects/perks.dm
new file mode 100644
index 00000000000..5d6be963b02
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/perks.dm
@@ -0,0 +1,29 @@
+/atom/movable/screen/perk
+ icon = 'icons/hud/perks.dmi'
+ name = "blank"
+ icon_state = "blank"
+
+/atom/movable/screen/perk/more
+ name = "more"
+ icon_state = "more"
+ // When compact we don't show our perk huds in ui.
+ var/compact = FALSE
+
+/atom/movable/screen/perk/more/Click(location, control, params)
+ . = ..()
+ if(!isliving(usr))
+ return
+ var/mob/living/usr_is_living = usr
+ var/datum/antagonist/wizard/wizard_datum = usr_is_living.mind.has_antag_datum(/datum/antagonist/wizard)
+ if(!wizard_datum)
+ return
+ compact = !compact
+ var/datum/hud/user_hud = usr_is_living.hud_used
+ for (var/perk_id in 1 to length(wizard_datum.perks))
+ var/atom/movable/screen/perk/perk = user_hud.screen_objects[HUD_WIZARD_PERK(perk_id)]
+ if (!perk) // ??
+ continue
+ if (compact)
+ perk.SetInvisibility(INVISIBILITY_ABSTRACT, type)
+ else
+ perk.RemoveInvisibility(type)
diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/screen_objects/picture_in_picture.dm
similarity index 100%
rename from code/_onclick/hud/picture_in_picture.dm
rename to code/_onclick/hud/screen_objects/picture_in_picture.dm
diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/screen_objects/radial.dm
similarity index 100%
rename from code/_onclick/hud/radial.dm
rename to code/_onclick/hud/screen_objects/radial.dm
diff --git a/code/_onclick/hud/radial_persistent.dm b/code/_onclick/hud/screen_objects/radial_persistent.dm
similarity index 100%
rename from code/_onclick/hud/radial_persistent.dm
rename to code/_onclick/hud/screen_objects/radial_persistent.dm
diff --git a/code/_onclick/hud/screen_objects/robot.dm b/code/_onclick/hud/screen_objects/robot.dm
new file mode 100644
index 00000000000..9dd39e8d4d6
--- /dev/null
+++ b/code/_onclick/hud/screen_objects/robot.dm
@@ -0,0 +1,113 @@
+/atom/movable/screen/robot
+ icon = 'icons/hud/screen_cyborg.dmi'
+ mouse_over_pointer = MOUSE_HAND_POINTER
+
+/atom/movable/screen/robot/module
+ name = "cyborg module"
+ icon_state = "nomod"
+
+/atom/movable/screen/robot/Click(location, control, params)
+ if(isobserver(usr))
+ return TRUE
+
+/atom/movable/screen/robot/module/Click(location, control, params)
+ . = ..()
+ var/mob/living/silicon/robot/robot_owner = hud.mymob
+ if(robot_owner.model.type == /obj/item/robot_model)
+ if(.)
+ return
+ robot_owner.pick_model()
+ return
+
+ var/list/modifiers = params2list(params)
+ if(robot_owner.module_active && !LAZYACCESS(modifiers, RIGHT_CLICK) && !.)
+ robot_owner.uneq_active()
+ return
+
+ // Observers can look at borg's inventories, so we ignore parent return value here, unlike everywhere else.
+ if(usr.active_storage == robot_owner.model.atom_storage)
+ robot_owner.model.atom_storage.hide_contents(usr)
+ else
+ robot_owner.model.atom_storage.open_storage(usr)
+ return TRUE
+
+/atom/movable/screen/robot/module_slot
+ name = "module"
+ icon_state = "inv1"
+ base_icon_state = "inv1"
+ /// Slot number of the module
+ var/slot_num = 1
+
+/atom/movable/screen/robot/module_slot/proc/set_slot(slot_num)
+ src.slot_num = slot_num
+ name = "module[slot_num]"
+ base_icon_state = "inv[slot_num]"
+ icon_state = base_icon_state
+
+ switch(slot_num)
+ if(BORG_CHOOSE_MODULE_ONE)
+ screen_loc = ui_inv1
+ if(BORG_CHOOSE_MODULE_TWO)
+ screen_loc = ui_inv2
+ if(BORG_CHOOSE_MODULE_THREE)
+ screen_loc = ui_inv3
+
+/atom/movable/screen/robot/module_slot/Click()
+ if(..())
+ return
+ var/mob/living/silicon/robot/R = usr
+ R.toggle_module(slot_num)
+
+/atom/movable/screen/robot/radio
+ name = "radio"
+ icon_state = "radio"
+ screen_loc = ui_borg_radio
+
+/atom/movable/screen/robot/radio/Click()
+ if(..())
+ return
+ var/mob/living/silicon/robot/R = usr
+ R.radio.interact(R)
+
+/atom/movable/screen/robot/lamp
+ name = "headlamp"
+ icon_state = "lamp_off"
+ base_icon_state = "lamp"
+ screen_loc = ui_borg_lamp
+
+/atom/movable/screen/robot/lamp/Click()
+ . = ..()
+ if(.)
+ return
+ var/mob/living/silicon/robot/user = usr
+ user.toggle_headlamp()
+ update_appearance()
+
+/atom/movable/screen/robot/lamp/update_icon_state()
+ icon_state = "[base_icon_state]_[astype(hud?.mymob, /mob/living/silicon/robot)?.lamp_enabled ? "on" : "off"]"
+ return ..()
+
+/atom/movable/screen/robot/modpc
+ name = "Modular Interface"
+ icon_state = "template"
+ screen_loc = ui_borg_tablet
+
+/atom/movable/screen/robot/modpc/Click()
+ . = ..()
+ if(.)
+ return
+ var/mob/living/silicon/robot/user = usr
+ user.modularInterface?.interact(user)
+
+/atom/movable/screen/robot/alerts
+ name = "Alert Panel"
+ icon = 'icons/hud/screen_ai.dmi'
+ icon_state = "alerts"
+ screen_loc = ui_borg_alerts
+
+/atom/movable/screen/robot/alerts/Click()
+ . = ..()
+ if(.)
+ return
+ var/mob/living/silicon/robot/borgo = usr
+ borgo.alert_control.ui_interact(borgo)
diff --git a/code/_onclick/hud/screen_object_holder.dm b/code/_onclick/hud/screen_objects/screen_object_holder.dm
similarity index 100%
rename from code/_onclick/hud/screen_object_holder.dm
rename to code/_onclick/hud/screen_objects/screen_object_holder.dm
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects/screen_objects.dm
similarity index 96%
rename from code/_onclick/hud/screen_objects.dm
rename to code/_onclick/hud/screen_objects/screen_objects.dm
index 2514376f911..fc4c8be9e4f 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects/screen_objects.dm
@@ -1,3 +1,4 @@
+INITIALIZE_IMMEDIATE(/atom/movable/screen) // I hate this place
/*
Screen objects
Todo: improve/re-implement
@@ -34,6 +35,10 @@
*/
var/del_on_map_removal = TRUE
+ /// A key for cleaning up references in hud datums
+ var/hud_group_key
+ var/hud_key
+
/// If FALSE, this will not be cleared when calling /client/clear_screen()
var/clear_with_screen = TRUE
/// If TRUE, clicking the screen element will fall through and perform a default "Click" call
@@ -50,6 +55,10 @@
set_new_hud(hud_owner)
/atom/movable/screen/Destroy()
+ if(hud)
+ if (hud_group_key && hud.screen_groups?[hud_group_key])
+ hud.screen_groups?[hud_group_key] -= src
+ hud.screen_objects -= hud_key
master_ref = null
hud = null
return ..()
@@ -57,6 +66,7 @@
/atom/movable/screen/Click(location, control, params)
if(flags_1 & INITIALIZED_1)
SEND_SIGNAL(src, COMSIG_SCREEN_ELEMENT_CLICK, location, control, params, usr)
+
if(default_click)
return ..()
@@ -101,7 +111,6 @@
maptext_width = 480
/atom/movable/screen/swap_hand
- plane = HUD_PLANE
name = "swap hand"
mouse_over_pointer = MOUSE_HAND_POINTER
@@ -187,7 +196,6 @@
var/icon_full
/// The overlay when hovering over with an item in your hand
var/image/object_overlay
- plane = HUD_PLANE
/atom/movable/screen/inventory/Click(location, control, params)
// At this point in client Click() code we have passed the 1/10 sec check and little else
@@ -304,6 +312,7 @@
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "storage_close"
mouse_over_pointer = MOUSE_HAND_POINTER
+ hud_group_key = HUD_GROUP_STORAGE
/atom/movable/screen/close/Initialize(mapload, datum/hud/hud_owner, new_master)
. = ..()
@@ -320,7 +329,6 @@
name = "drop"
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "act_drop"
- plane = HUD_PLANE
mouse_over_pointer = MOUSE_HAND_POINTER
/atom/movable/screen/drop/Click()
@@ -331,7 +339,7 @@
name = "toggle combat mode"
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "combat_off"
- screen_loc = ui_combat_toggle
+ screen_loc = ui_acti
mouse_over_pointer = MOUSE_HAND_POINTER
/atom/movable/screen/combattoggle/Initialize(mapload, datum/hud/hud_owner)
@@ -470,6 +478,11 @@
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "running"
mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_movi
+
+/atom/movable/screen/mov_intent/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ update_appearance(UPDATE_ICON_STATE)
/atom/movable/screen/mov_intent/Click()
toggle(usr)
@@ -496,6 +509,11 @@
icon_state = "pull"
base_icon_state = "pull"
mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_living_pull
+
+/atom/movable/screen/pull/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ update_appearance(UPDATE_ICON_STATE)
/atom/movable/screen/pull/Click()
if(isobserver(usr))
@@ -511,8 +529,8 @@
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "act_resist"
base_icon_state = "act_resist"
- plane = HUD_PLANE
mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_above_movement
/atom/movable/screen/resist/Click()
flick("[base_icon_state]_on", src)
@@ -525,8 +543,12 @@
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "act_rest"
base_icon_state = "act_rest"
- plane = HUD_PLANE
mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_below_throw
+
+/atom/movable/screen/rest/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ update_appearance(UPDATE_ICON_STATE)
/atom/movable/screen/rest/Click()
if(isliving(usr))
@@ -545,32 +567,38 @@
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "act_sleep"
base_icon_state = "act_sleep"
- plane = HUD_PLANE
mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_above_throw
+
+/atom/movable/screen/sleep/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ // Invisible by default
+ SetInvisibility(INVISIBILITY_ABSTRACT, "sleep")
/atom/movable/screen/sleep/Click()
if(!isliving(usr) || HAS_TRAIT(usr, TRAIT_KNOCKEDOUT))
return
- if(usr.client?.prefs.read_preference(/datum/preference/toggle/remove_double_click))
- var/tgui_answer = tgui_alert(usr, "You sure you want to sleep for a while?", "Sleeping", list("Yes", "No"))
- if(tgui_answer == "Yes" && !HAS_TRAIT(usr, TRAIT_KNOCKEDOUT))
- var/mob/living/L = usr
- L.SetSleeping(400)
- else
+ if(!usr.client?.prefs.read_preference(/datum/preference/toggle/remove_double_click))
flick("[base_icon_state]_flick", src)
+ return
+
+ var/tgui_answer = tgui_alert(usr, "You sure you want to sleep for a while?", "Sleeping", list("Yes", "No"))
+ if(tgui_answer == "Yes" && !HAS_TRAIT(usr, TRAIT_KNOCKEDOUT))
+ var/mob/living/L = usr
+ L.Sleeping(400)
/atom/movable/screen/sleep/DblClick(location, control, params)
if(!isliving(usr) || usr.client?.prefs.read_preference(/datum/preference/toggle/remove_double_click))
return
if(isliving(usr))
var/mob/living/L = usr
- L.SetSleeping(400)
+ L.Sleeping(400)
/atom/movable/screen/storage
name = "storage"
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "storage_cell"
- plane = HUD_PLANE
+ hud_group_key = HUD_GROUP_STORAGE
/atom/movable/screen/storage/Initialize(mapload, datum/hud/hud_owner, new_master)
. = ..()
@@ -638,6 +666,7 @@
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "act_throw"
mouse_over_pointer = MOUSE_HAND_POINTER
+ screen_loc = ui_drop_throw
/atom/movable/screen/throw_catch/Click()
if(isliving(usr))
@@ -653,6 +682,10 @@
var/static/list/hover_overlays_cache = list()
var/hovering
+/atom/movable/screen/zone_sel/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ update_appearance()
+
/atom/movable/screen/zone_sel/Click(location, control,params)
if(isobserver(usr))
return
@@ -941,7 +974,6 @@
plane = SPLASHSCREEN_PLANE
var/client/holder
-INITIALIZE_IMMEDIATE(/atom/movable/screen/splash)
/atom/movable/screen/splash/Initialize(mapload, datum/hud/hud_owner, client/C, visible, use_previous_title)
. = ..()
diff --git a/code/_onclick/hud/screentip.dm b/code/_onclick/hud/screen_objects/screentip.dm
similarity index 100%
rename from code/_onclick/hud/screentip.dm
rename to code/_onclick/hud/screen_objects/screentip.dm
diff --git a/code/_onclick/hud/voidwalkerhud.dm b/code/_onclick/hud/screen_objects/voidwalker.dm
similarity index 73%
rename from code/_onclick/hud/voidwalkerhud.dm
rename to code/_onclick/hud/screen_objects/voidwalker.dm
index 7ba892f7d7a..6d718ac58d7 100644
--- a/code/_onclick/hud/voidwalkerhud.dm
+++ b/code/_onclick/hud/screen_objects/voidwalker.dm
@@ -1,30 +1,3 @@
-/datum/hud/dextrous/voidwalker
- ui_style = 'icons/hud/screen_voidwalker.dmi'
-
-/datum/hud/dextrous/voidwalker/New(mob/living/owner)
- . = ..()
-
- floor_change.icon = ui_style
- floor_change.screen_loc = ui_rest
- static_inventory += floor_change
-
- resist_icon = new /atom/movable/screen/resist(null, src)
- resist_icon.icon = ui_style
- resist_icon.screen_loc = ui_voidwalker_left_of_hands
- resist_icon.update_appearance()
-
- action_intent = new /atom/movable/screen/combattoggle/flashy/voidwalker(null, src)
- action_intent.icon = ui_style
- static_inventory += action_intent
-
- var/atom/movable/screen/space_camo_toggle = new /atom/movable/screen/space_camo(null, src)
- space_camo_toggle.screen_loc = ui_above_movement
- static_inventory += space_camo_toggle
-
- var/atom/movable/screen/vomit_jump/vomit = new /atom/movable/screen/vomit_jump(null, src)
- vomit.screen_loc = ui_mood
- static_inventory += vomit
-
/// This exists because for some reason only the combat indicator screen_loc is constantly set to initial
/atom/movable/screen/combattoggle/flashy/voidwalker
screen_loc = ui_movi
@@ -33,6 +6,7 @@
name = "space camouflage toggle"
icon = 'icons/hud/screen_voidwalker.dmi'
icon_state = "camo_toggle"
+ screen_loc = ui_above_movement
/// Wheter or not we're toggled on or off
var/invisibility_toggle = TRUE
@@ -57,6 +31,7 @@
name = "vomit tracker"
icon = 'icons/hud/screen_voidwalker.dmi'
icon_state = "template"
+ screen_loc = ui_mood
/// So we can sort of loop through it
var/index = 1
diff --git a/code/_onclick/hud/soulscythe.dm b/code/_onclick/hud/soulscythe.dm
index 8c7a3359c8a..e290d960e56 100644
--- a/code/_onclick/hud/soulscythe.dm
+++ b/code/_onclick/hud/soulscythe.dm
@@ -1,4 +1,3 @@
-/datum/hud/soulscythe/New(mob/living/basic/soulscythe/owner)
+/datum/hud/soulscythe/initialize_screen_objects()
. = ..()
- var/atom/movable/screen/using = new /atom/movable/screen/blood_level(null, src)
- static_inventory += using
+ add_screen_object(/atom/movable/screen/blood_level, HUD_MOB_BLOOD_LEVEL, HUD_GROUP_INFO)
diff --git a/code/_onclick/hud/voidwalker.dm b/code/_onclick/hud/voidwalker.dm
new file mode 100644
index 00000000000..7fe95fc3f28
--- /dev/null
+++ b/code/_onclick/hud/voidwalker.dm
@@ -0,0 +1,14 @@
+/datum/hud/dextrous/voidwalker
+ ui_style = 'icons/hud/screen_voidwalker.dmi'
+
+/datum/hud/dextrous/voidwalker/initialize_screen_objects()
+ . = ..()
+ var/atom/movable/screen/floor_change = screen_objects[HUD_MOB_FLOOR_CHANGER]
+ floor_change.icon = ui_style
+ floor_change.screen_loc = ui_below_throw
+
+ add_screen_object(/atom/movable/screen/resist, HUD_MOB_RESIST, HUD_GROUP_HOTKEYS, ui_style, ui_voidwalker_left_of_hands)
+ add_screen_object(/atom/movable/screen/combattoggle/flashy/voidwalker, HUD_MOB_INTENTS, HUD_GROUP_STATIC, ui_style)
+ add_screen_object(/atom/movable/screen/space_camo, HUD_VOIDWALKER_SPACE_CAMO)
+ add_screen_object(/atom/movable/screen/vomit_jump, HUD_VOIDWALKER_VOID_JUMP)
+
diff --git a/code/datums/actions/action.dm b/code/datums/actions/action.dm
index 64f2f2a6c2b..b5993e1894a 100644
--- a/code/datums/actions/action.dm
+++ b/code/datums/actions/action.dm
@@ -373,7 +373,7 @@
/// Creates an action button movable for the passed mob, and returns it.
/datum/action/proc/create_button()
- var/atom/movable/screen/movable/action_button/button = new()
+ var/atom/movable/screen/movable/action_button/button = owner.hud_used.add_screen_object(/atom/movable/screen/movable/action_button)
button.linked_action = src
button.allow_observer_click = allow_observer_click
build_button_icon(button, ALL, TRUE)
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index d8b6103e338..a042bfe7a02 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -15,20 +15,29 @@
/datum/component/personal_crafting/Initialize(screen_loc_override)
src.screen_loc_override = screen_loc_override
- if(ismob(parent))
- RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, PROC_REF(create_mob_button))
-/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/user_client)
+/datum/component/personal_crafting/RegisterWithParent()
+ if(!ismob(parent))
+ return
+
+ var/mob/user = parent
+ RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
+ if(user.hud_used)
+ on_hud_created()
+
+/datum/component/personal_crafting/UnregisterFromParent()
+ if(!ismob(parent))
+ return
+ var/mob/user = parent
+ UnregisterSignal(parent, COMSIG_MOB_HUD_CREATED)
+ user.hud_used?.remove_screen_object(HUD_MOB_CRAFTING_MENU)
+
+/datum/component/personal_crafting/proc/on_hud_created(datum/source)
SIGNAL_HANDLER
- var/datum/hud/hud = user.hud_used
- var/atom/movable/screen/craft/craft_ui = new()
- craft_ui.icon = hud.ui_style
- if (screen_loc_override)
- craft_ui.screen_loc = screen_loc_override
- hud.static_inventory += craft_ui
- user_client.screen += craft_ui
- RegisterSignal(craft_ui, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(component_ui_interact))
+ var/mob/user = parent
+ var/atom/movable/screen/screen_obj = user.hud_used.add_screen_object(/atom/movable/screen/craft, HUD_MOB_CRAFTING_MENU, HUD_GROUP_STATIC, user.hud_used.ui_style, screen_loc_override, update_screen = TRUE)
+ RegisterSignal(screen_obj, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(component_ui_interact))
#define COOKING TRUE
#define CRAFTING FALSE
diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm
index b58ef88a3dc..465b6ae2066 100644
--- a/code/datums/components/food/edible.dm
+++ b/code/datums/components/food/edible.dm
@@ -504,7 +504,9 @@ Behavior that's still missing from this component that original food items had t
var/fraction = 0.3
fraction = min(bite_consumption / owner.reagents.total_volume, 1)
owner.reagents.trans_to(eater, bite_consumption, transferred_by = feeder, methods = INGEST)
- eater.hud_used?.hunger?.update_hunger_bar()
+ var/atom/movable/screen/hunger/hunger_bar = eater.hud_used?.screen_objects[HUD_MOB_HUNGER]
+ if (istype(hunger_bar))
+ hunger_bar.update_hunger_bar()
bitecount++
checkLiked(fraction, eater)
diff --git a/code/datums/components/fov_handler.dm b/code/datums/components/fov_handler.dm
index df56df1ff6e..cd3061291a2 100644
--- a/code/datums/components/fov_handler.dm
+++ b/code/datums/components/fov_handler.dm
@@ -90,8 +90,6 @@
/datum/component/fov_handler/proc/remove_mask()
var/mob/parent_mob = parent
var/client/parent_client = parent_mob.client
- // Prevents stupid ass hard deletes
- parent_mob.hud_used.always_visible_inventory -= blocker_mask
if(!parent_client) //Love client volatility!!
return
applied_mask = FALSE
@@ -104,7 +102,8 @@
return
applied_mask = TRUE
parent_client.screen += blocker_mask
- parent_mob.hud_used.always_visible_inventory += blocker_mask
+ parent_mob.hud_used.screen_objects += blocker_mask
+ parent_mob.hud_used.screen_groups[HUD_GROUP_SCREEN_OVERLAYS] += blocker_mask
/// When a direction of the user changes, so do the masks
/datum/component/fov_handler/proc/on_dir_change(mob/source, old_dir, new_dir)
diff --git a/code/datums/components/life_link.dm b/code/datums/components/life_link.dm
index a79758122c5..d1371467682 100644
--- a/code/datums/components/life_link.dm
+++ b/code/datums/components/life_link.dm
@@ -118,8 +118,9 @@
mob_parent.overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity)
else
mob_parent.clear_fullscreen("brute")
- if(mob_parent.hud_used?.healths)
- mob_parent.hud_used.healths.maptext = MAPTEXT("[round(healthpercent, 0.5)]%
")
+
+ if(mob_parent.hud_used?.screen_objects[HUD_MOB_HEALTH])
+ mob_parent.hud_used.screen_objects[HUD_MOB_HEALTH].maptext = MAPTEXT("[round(healthpercent, 0.5)]%
")
/// Update our health on the medical hud
/datum/component/life_link/proc/update_med_hud_health(mob/living/mob_parent)
diff --git a/code/datums/components/style/style.dm b/code/datums/components/style/style.dm
index 6e1147a7ba3..04ea7b3654c 100644
--- a/code/datums/components/style/style.dm
+++ b/code/datums/components/style/style.dm
@@ -74,23 +74,7 @@
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
- var/mob/mob_parent = parent
-
- meter = new()
- meter_image = new()
- meter.vis_contents += meter_image
- meter_image.add_filter("meter_mask", 1, list(type = "alpha", icon = icon('icons/hud/style_meter.dmi', "style_meter"), flags = MASK_INVERSE))
- meter.update_appearance()
- meter_image.update_appearance()
-
- update_screen()
-
- if(mob_parent.hud_used)
- mob_parent.hud_used.static_inventory += meter
- mob_parent.hud_used.show_hud(mob_parent.hud_used.hud_version)
-
START_PROCESSING(SSdcs, src)
-
if(multitooled)
src.multitooled = multitooled
@@ -107,7 +91,11 @@
RegisterSignal(parent, COMSIG_LIVING_DEFUSED_GIBTONITE, PROC_REF(on_gibtonite_defuse))
RegisterSignal(parent, COMSIG_LIVING_CRUSHER_DETONATE, PROC_REF(on_crusher_detonate))
RegisterSignal(parent, COMSIG_LIVING_DISCOVERED_GEYSER, PROC_REF(on_geyser_discover))
+ RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
ADD_TRAIT(parent, TRAIT_MINING_PARRYING, STYLE_TRAIT)
+ var/mob/mob_parent = parent
+ if (mob_parent.hud_used)
+ on_hud_created()
/datum/component/style/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_USER_PRE_ITEM_ATTACK)
@@ -121,16 +109,31 @@
UnregisterSignal(parent, COMSIG_LIVING_DEFUSED_GIBTONITE)
UnregisterSignal(parent, COMSIG_LIVING_CRUSHER_DETONATE)
UnregisterSignal(parent, COMSIG_LIVING_DISCOVERED_GEYSER)
+ UnregisterSignal(parent, COMSIG_MOB_HUD_CREATED)
REMOVE_TRAIT(parent, TRAIT_MINING_PARRYING, STYLE_TRAIT)
/datum/component/style/Destroy(force)
STOP_PROCESSING(SSdcs, src)
+ QDEL_NULL(meter)
+ QDEL_NULL(meter_image)
var/mob/mob_parent = parent
if(mob_parent.hud_used)
- mob_parent.hud_used.static_inventory -= meter
mob_parent.hud_used.show_hud(mob_parent.hud_used.hud_version)
return ..()
+
+/datum/component/style/proc/on_hud_created(datum/source)
+ SIGNAL_HANDLER
+
+ var/mob/owner = parent
+ meter = owner.hud_used.add_screen_object(/atom/movable/screen/style_meter_background, HUD_MOB_STYLE_METER, update_screen = TRUE)
+ meter_image = new()
+ meter.vis_contents += meter_image
+ meter_image.add_filter("meter_mask", 1, list(type = "alpha", icon = icon('icons/hud/style_meter.dmi', "style_meter"), flags = MASK_INVERSE))
+ meter.update_appearance()
+ meter_image.update_appearance()
+ update_screen()
+
/datum/component/style/process(seconds_per_tick)
point_multiplier = round(max(point_multiplier - 0.2 * seconds_per_tick, 1), 0.1)
change_points(-5 * seconds_per_tick * ROUND_UP((style_points + 1) / 200), use_multiplier = FALSE)
@@ -195,6 +198,9 @@
mob_parent.balloon_alert(mob_parent, "hotswapping disabled")
rank = rank_changed
+
+ if(!meter)
+ return
meter.maptext = "[format_rank_string(rank)][generate_multiplier()][generate_actions()]"
meter.maptext_y = initial(meter.maptext_y) - 12 * length(actions)
update_meter(point_to_rank(), go_back)
diff --git a/code/datums/elements/attack_zone_randomiser.dm b/code/datums/elements/attack_zone_randomiser.dm
index 1e364265bab..efd7648bc7c 100644
--- a/code/datums/elements/attack_zone_randomiser.dm
+++ b/code/datums/elements/attack_zone_randomiser.dm
@@ -21,14 +21,14 @@
SIGNAL_HANDLER
if (!iscarbon(target))
return
- if (!isnull(source.mind) && !isnull(source.hud_used?.zone_select))
+ if (!isnull(source.mind) && !isnull(source.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]))
return
var/mob/living/living_target = target
var/list/blacklist_zones = GLOB.all_body_zones - valid_attack_zones
var/new_zone = living_target.get_random_valid_zone(blacklisted_parts = blacklist_zones, bypass_warning = TRUE)
if (isnull(new_zone))
new_zone = BODY_ZONE_CHEST
- var/atom/movable/screen/zone_sel/zone_selector = source.hud_used?.zone_select
+ var/atom/movable/screen/zone_sel/zone_selector = source.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]
if (isnull(zone_selector))
source.zone_selected = new_zone
else
diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm
index 42eb96bf5da..d9d12020ef2 100644
--- a/code/datums/keybinding/living.dm
+++ b/code/datums/keybinding/living.dm
@@ -18,8 +18,8 @@
return
var/mob/living/owner = user.mob
owner.resist()
- if (owner.hud_used?.resist_icon)
- owner.hud_used.resist_icon.icon_state = "[owner.hud_used.resist_icon.base_icon_state]_on"
+ if (owner.hud_used?.screen_objects[HUD_MOB_RESIST])
+ owner.hud_used.screen_objects[HUD_MOB_RESIST].icon_state = "[owner.hud_used.screen_objects[HUD_MOB_RESIST].base_icon_state]_on"
return TRUE
/datum/keybinding/living/resist/up(client/user, turf/target)
@@ -27,8 +27,8 @@
if(.)
return
var/mob/living/owner = user.mob
- if (owner.hud_used?.resist_icon)
- owner.hud_used.resist_icon.icon_state = owner.hud_used.resist_icon.base_icon_state
+ if (owner.hud_used?.screen_objects[HUD_MOB_RESIST])
+ owner.hud_used.screen_objects[HUD_MOB_RESIST].icon_state = owner.hud_used.screen_objects[HUD_MOB_RESIST].base_icon_state
/datum/keybinding/living/look_up
hotkey_keys = list("L")
diff --git a/code/datums/martial/_martial.dm b/code/datums/martial/_martial.dm
index 4a0c9c84b30..1281d028b22 100644
--- a/code/datums/martial/_martial.dm
+++ b/code/datums/martial/_martial.dm
@@ -24,8 +24,6 @@
var/smashes_tables = FALSE
/// If TRUE, a combo meter will be displayed on the HUD for the current streak
var/display_combos = FALSE
- ///The Combo HUD given to display comboes, if we're set to display them.
- var/atom/movable/screen/combo/combo_display
/// The length of time until streaks are auto-reset.
var/combo_timer = 6 SECONDS
/// Timer ID for the combo reset timer.
@@ -276,8 +274,11 @@
streak += element
if(length(streak) > max_streak_length)
streak = copytext(streak, 1 + length(streak[1]))
- if(display_combos)
- timerid = addtimer(CALLBACK(src, PROC_REF(reset_streak), null, FALSE), combo_timer, TIMER_UNIQUE | TIMER_STOPPABLE)
+ if(!display_combos)
+ return
+ timerid = addtimer(CALLBACK(src, PROC_REF(reset_streak), null, FALSE), combo_timer, TIMER_UNIQUE | TIMER_STOPPABLE)
+ var/atom/movable/screen/combo/combo_display = holder.hud_used?.screen_objects[HUD_MOB_COMBO]
+ if(istype(combo_display))
combo_display.update_icon_state(streak, combo_timer - 2 SECONDS)
/**
@@ -292,7 +293,8 @@
deltimer(timerid)
current_target = WEAKREF(new_target)
streak = ""
- if(display_combos && update_icon)
+ var/atom/movable/screen/combo/combo_display = holder.hud_used?.screen_objects[HUD_MOB_COMBO]
+ if(istype(combo_display) && display_combos && update_icon)
combo_display.update_icon_state(streak)
/datum/martial_art/proc/smash_table(mob/living/source, mob/living/pushed_mob, obj/structure/table/table)
@@ -383,10 +385,9 @@
RegisterSignal(new_holder, COMSIG_LIVING_GRAB, PROC_REF(attempt_grab))
RegisterSignals(new_holder, list(COMSIG_LIVING_TABLE_SLAMMING, COMSIG_LIVING_TABLE_LIMB_SLAMMING), PROC_REF(smash_table))
if(display_combos)
+ RegisterSignal(new_holder, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
if(new_holder.hud_used)
on_hud_created(new_holder)
- else
- RegisterSignal(new_holder, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
/**
* Called when this martial art is removed from a mob.
@@ -397,19 +398,12 @@
if(help_verb)
remove_verb(remove_from, help_verb)
UnregisterSignal(remove_from, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_LIVING_GRAB, COMSIG_LIVING_TABLE_SLAMMING, COMSIG_LIVING_TABLE_LIMB_SLAMMING))
- if(!isnull(combo_display))
- var/datum/hud/hud_used = remove_from.hud_used
- hud_used.infodisplay -= combo_display
- hud_used.show_hud(hud_used.hud_version)
- QDEL_NULL(combo_display)
+ remove_from.hud_used?.remove_screen_object(HUD_MOB_COMBO)
///Gives the owner of the martial art the combo HUD.
/datum/martial_art/proc/on_hud_created(mob/source)
SIGNAL_HANDLER
- var/datum/hud/hud_used = source.hud_used
- combo_display = new(null, hud_used)
- hud_used.infodisplay += combo_display
- hud_used.show_hud(hud_used.hud_version)
+ source.hud_used.add_screen_object(/atom/movable/screen/combo, HUD_MOB_COMBO, HUD_GROUP_INFO, update_screen = TRUE)
/mob/living/proc/verb_switch_style()
set name = "Swap Style"
diff --git a/code/datums/mood.dm b/code/datums/mood.dm
index 6ee75391517..afc7824380f 100644
--- a/code/datums/mood.dm
+++ b/code/datums/mood.dm
@@ -36,8 +36,6 @@
var/sanity_level = SANITY_LEVEL_NEUTRAL
/// Is the owner being punished for low mood? if so, how much?
var/insanity_effect = 0
- /// The screen object for the current mood level
- var/atom/movable/screen/mood/mood_screen_object
/// List of mood events currently active on this datum
var/list/mood_events = list()
@@ -308,7 +306,11 @@
/// Updates the mob's mood icon
/datum/mood/proc/update_mood_icon()
- if (!(mob_parent.client || mob_parent.hud_used) || isnull(mood_screen_object))
+ if (!mob_parent.client || !mob_parent.hud_used)
+ return
+
+ var/atom/movable/screen/mood/mood_screen_object = mob_parent.hud_used.screen_objects[HUD_MOB_MOOD]
+ if (!istype(mood_screen_object))
return
mood_screen_object.cut_overlays()
@@ -356,9 +358,8 @@
SIGNAL_HANDLER
var/datum/hud/hud = mob_parent.hud_used
- mood_screen_object = new
+ var/atom/movable/screen/mood/mood_screen_object = hud.add_screen_object(/atom/movable/screen/mood, HUD_MOB_MOOD, HUD_GROUP_INFO, update_screen = TRUE)
mood_screen_object.color = "#4b96c4"
- hud.infodisplay += mood_screen_object
RegisterSignal(hud, COMSIG_QDELETING, PROC_REF(unmodify_hud))
RegisterSignal(mood_screen_object, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(hud_click))
@@ -366,12 +367,17 @@
/datum/mood/proc/unmodify_hud(datum/source)
SIGNAL_HANDLER
+ var/datum/hud/hud = mob_parent.hud_used
+ if (!hud)
+ return
+
+ var/atom/movable/screen/mood/mood_screen_object = hud.screen_objects[HUD_MOB_MOOD]
if(!mood_screen_object)
return
- var/datum/hud/hud = mob_parent.hud_used
- if(hud?.infodisplay)
- hud.infodisplay -= mood_screen_object
+
QDEL_NULL(mood_screen_object)
+ if (!QDELETED(hud))
+ hud.show_hud(hud.hud_version)
UnregisterSignal(hud, COMSIG_QDELETING)
/// Handles clicking on the mood HUD object
diff --git a/code/datums/status_effects/debuffs/screwy_hud.dm b/code/datums/status_effects/debuffs/screwy_hud.dm
index 10c20112160..4779db579c6 100644
--- a/code/datums/status_effects/debuffs/screwy_hud.dm
+++ b/code/datums/status_effects/debuffs/screwy_hud.dm
@@ -40,7 +40,7 @@
if(other_screwy_hud.priority > priority)
return
- source.hud_used.healths.icon_state = override_icon
+ source.hud_used?.screen_objects[HUD_MOB_HEALTH]?.icon_state = override_icon
return COMPONENT_OVERRIDE_HEALTH_HUD
/datum/status_effect/grouped/screwy_hud/fake_dead
diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm
index baab88cf239..472f9c6baa6 100644
--- a/code/datums/storage/storage.dm
+++ b/code/datums/storage/storage.dm
@@ -718,9 +718,12 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
SIGNAL_HANDLER
for(var/mob/user as anything in is_using)
- user.hud_used?.open_containers -= gone
+ if (user.hud_used?.screen_groups[HUD_GROUP_STORAGE])
+ user.hud_used.screen_groups[HUD_GROUP_STORAGE] -= gone
+
if(!user.client)
continue
+
var/client/cuser = user.client
cuser.screen -= gone
@@ -1086,9 +1089,10 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
LAZYOR(is_using, to_show)
- to_show.hud_used.open_containers |= storage_interfaces[to_show].list_ui_elements()
+ // Don't add to screen_objects as that one gets its contents actually deleted
+ LAZYOR(to_show.hud_used.screen_groups[HUD_GROUP_STORAGE], storage_interfaces[to_show].list_ui_elements())
to_show.client.screen |= storage_interfaces[to_show].list_ui_elements()
- to_show.hud_used.open_containers |= real_location.contents
+ LAZYOR(to_show.hud_used.screen_groups[HUD_GROUP_STORAGE], real_location.contents)
to_show.client.screen |= real_location.contents
return TRUE
@@ -1116,9 +1120,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
if(to_hide.client)
to_hide.client.screen -= storage_interfaces[to_hide].list_ui_elements()
to_hide.client.screen -= real_location.contents
- if(to_hide.hud_used)
- to_hide.hud_used.open_containers -= storage_interfaces[to_hide].list_ui_elements()
- to_hide.hud_used.open_containers -= real_location.contents
+ if(to_hide.hud_used.screen_groups[HUD_GROUP_STORAGE])
+ to_hide.hud_used.screen_groups[HUD_GROUP_STORAGE] -= storage_interfaces[to_hide].list_ui_elements()
+ to_hide.hud_used.screen_groups[HUD_GROUP_STORAGE] -= real_location.contents
QDEL_NULL(storage_interfaces[to_hide])
storage_interfaces -= to_hide
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index f6ba5f3acb9..695af1eaef9 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -286,7 +286,7 @@
/datum/wound/blunt/bone/moderate/try_handling(mob/living/user)
if(user.usable_hands <= 0 || user.pulling != victim)
return FALSE
- if(!isnull(user.hud_used?.zone_select) && user.zone_selected != limb.body_zone)
+ if(!isnull(user.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]) && user.zone_selected != limb.body_zone)
return FALSE
if(user.grab_state == GRAB_PASSIVE)
diff --git a/code/datums/wounds/cranial_fissure.dm b/code/datums/wounds/cranial_fissure.dm
index e26e3a97d9d..9a2fd0eb439 100644
--- a/code/datums/wounds/cranial_fissure.dm
+++ b/code/datums/wounds/cranial_fissure.dm
@@ -87,7 +87,7 @@
if (user.usable_hands <= 0 || user.combat_mode)
return FALSE
- if(!isnull(user.hud_used?.zone_select) && (user.zone_selected != BODY_ZONE_HEAD && user.zone_selected != BODY_ZONE_PRECISE_EYES))
+ if(!isnull(user.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]) && (user.zone_selected != BODY_ZONE_HEAD && user.zone_selected != BODY_ZONE_PRECISE_EYES))
return FALSE
if (victim.body_position != LYING_DOWN)
diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm
index 68f6d685ae8..5ed2e8695b7 100644
--- a/code/datums/wounds/slash.dm
+++ b/code/datums/wounds/slash.dm
@@ -181,7 +181,7 @@
/datum/wound/slash/flesh/try_handling(mob/living/user)
if(user.pulling != victim || !HAS_TRAIT(user, TRAIT_WOUND_LICKER) || !victim.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
return FALSE
- if(!isnull(user.hud_used?.zone_select) && user.zone_selected != limb.body_zone)
+ if(!isnull(user.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]) && user.zone_selected != limb.body_zone)
return FALSE
if(DOING_INTERACTION_WITH_TARGET(user, victim))
diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm
index 416f4c16fac..79d9b9279e0 100644
--- a/code/game/machinery/computer/operating_computer.dm
+++ b/code/game/machinery/computer/operating_computer.dm
@@ -148,7 +148,7 @@
if(!zone_found)
return
- var/atom/movable/screen/zone_sel/selector = user.hud_used?.zone_select
+ var/atom/movable/screen/zone_sel/selector = user.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector?.set_selected_zone(zone_found, user, FALSE)
LAZYREMOVE(zone_on_open, WEAKREF(user))
if(!LAZYLEN(zone_on_open))
@@ -257,7 +257,7 @@
if("change_zone")
if(params["new_zone"] in (GLOB.all_body_zones + GLOB.all_precise_body_zones))
target_zone = params["new_zone"]
- var/atom/movable/screen/zone_sel/selector = ui.user.hud_used?.zone_select
+ var/atom/movable/screen/zone_sel/selector = ui.user.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector?.set_selected_zone(params["new_zone"], ui.user, FALSE)
update_static_data_for_all_viewers()
return TRUE
diff --git a/code/game/machinery/computer/records/records.dm b/code/game/machinery/computer/records/records.dm
index 2f6e429c772..a860e66d4f7 100644
--- a/code/game/machinery/computer/records/records.dm
+++ b/code/game/machinery/computer/records/records.dm
@@ -111,7 +111,7 @@
if(user.client?.screen_maps[assigned_view])
return
- var/atom/movable/screen/map_view/char_preview/new_view = new(null, src)
+ var/atom/movable/screen/map_view/char_preview/new_view = new(null, null, src)
new_view.generate_view(assigned_view)
new_view.display_to(user, window)
return new_view
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index 6a3ec38c3da..c58e46e7478 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -97,19 +97,27 @@
user.balloon_alert(user, balloon_message)
var/datum/hud/user_hud = user.hud_used
- if(!user_hud || !istype(user_hud, /datum/hud) || !islist(user_hud.infodisplay))
+ if(!user_hud)
return
- var/atom/movable/screen/multitool_arrow/arrow = new(null, user_hud)
+ var/atom/movable/screen/multitool_arrow/arrow = user_hud.add_screen_object(/atom/movable/screen/multitool_arrow, HUD_MULTITOOL_ARROW, HUD_GROUP_INFO, update_screen = TRUE)
arrow.color = arrow_color
- arrow.screen_loc = around_player
arrow.transform = matrix(dir2angle(dir), MATRIX_ROTATE)
-
- user_hud.infodisplay += arrow
- user_hud.show_hud(user_hud.hud_version)
-
QDEL_IN(arrow, 1.5 SECONDS)
+/atom/movable/screen/multitool_arrow
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "multitool_arrow"
+ pixel_x = -32
+ pixel_y = -32
+ screen_loc = around_player
+
+/atom/movable/screen/multitool_arrow/Destroy()
+ var/datum/hud/our_hud = hud
+ . = ..()
+ if (!QDELETED(our_hud))
+ INVOKE_ASYNC(our_hud, TYPE_PROC_REF(/datum/hud, show_hud), our_hud.hud_version)
+
/obj/item/multitool/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] puts the [src] to [user.p_their()] chest. It looks like [user.p_theyre()] trying to pulse [user.p_their()] heart off!"))
return OXYLOSS//there's a reason it wasn't recommended by doctors
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index 2fc737cb365..e6f8ad51e22 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -61,9 +61,6 @@ GLOBAL_LIST_EMPTY(blob_nodes)
/// The list of strains the blob can reroll for.
var/list/strain_choices
- ///The HUD given to blobs with their power
- var/atom/movable/screen/blob_power_display/blob_power_hud
-
/mob/eye/blob/Initialize(mapload, starting_points = OVERMIND_STARTING_POINTS)
ADD_TRAIT(src, TRAIT_BLOB_ALLY, INNATE_TRAIT)
validate_location()
@@ -85,14 +82,6 @@ GLOBAL_LIST_EMPTY(blob_nodes)
START_PROCESSING(SSobj, src)
GLOB.blob_telepathy_mobs |= src
-/mob/eye/blob/create_mob_hud()
- . = ..()
- if(!.)
- return
- blob_power_hud = new(null, src)
- hud_used.infodisplay += blob_power_hud
- hud_used.show_hud(hud_used.hud_version)
-
/mob/eye/blob/proc/validate_location()
var/turf/T = get_turf(src)
if(is_valid_turf(T))
@@ -263,7 +252,6 @@ GLOBAL_LIST_EMPTY(blob_nodes)
/mob/eye/blob/Destroy()
QDEL_NULL(blobstrain)
- QDEL_NULL(blob_power_hud)
// Clear references immediately without iterating to avoid blocking
all_blobs = null
@@ -304,15 +292,15 @@ GLOBAL_LIST_EMPTY(blob_nodes)
return FALSE
var/current_health = round((blob_core.get_integrity() / blob_core.max_integrity) * 100)
var/new_maptext = MAPTEXT("[current_health]%
")
- hud_used.healths.maptext = new_maptext
+ hud_used.screen_objects[HUD_MOB_HEALTH].maptext = new_maptext
for(var/mob/living/basic/blob_minion/blobbernaut/blobbernaut in blob_mobs)
- if(isnull(blobbernaut.overmind_hud))
- continue
- blobbernaut.overmind_hud.maptext = new_maptext
+ var/atom/movable/screen/overmind_hud = blobbernaut.hud_used?.screen_objects[HUD_BLOBBERNAUT_OVERMIND]
+ if (overmind_hud)
+ overmind_hud.maptext = new_maptext
/mob/eye/blob/proc/add_points(points)
blob_points = clamp(blob_points + points, 0, max_blob_points)
- blob_power_hud.maptext = MAPTEXT("[round(blob_points)]
")
+ hud_used.screen_objects[HUD_BLOB_POWER_DISPLAY].maptext = MAPTEXT("[round(blob_points)]
")
/mob/eye/blob/say(
message,
diff --git a/code/modules/antagonists/blood_worm/blood_worm_host_handling.dm b/code/modules/antagonists/blood_worm/blood_worm_host_handling.dm
index 67543842cd0..2b87fd4c9f6 100644
--- a/code/modules/antagonists/blood_worm/blood_worm_host_handling.dm
+++ b/code/modules/antagonists/blood_worm/blood_worm_host_handling.dm
@@ -132,7 +132,7 @@
host.remove_blood_volume_modifier(REF(src))
sync_health(already_ejecting = TRUE)
- remove_host_hud()
+ host.hud_used?.remove_screen_object(HUD_MOB_BLOOD_LEVEL)
host.set_blood_volume(0)
@@ -245,16 +245,8 @@
UnregisterSignal(host, COMSIG_MOB_HUD_CREATED)
var/datum/hud/hud = host.hud_used
- blood_display = new(null, hud)
- hud.infodisplay += blood_display
- hud.show_hud(hud.hud_version)
-
-/mob/living/basic/blood_worm/proc/remove_host_hud()
- var/datum/hud/hud = host.hud_used
-
- if (!hud)
- QDEL_NULL(blood_display)
+ var/atom/movable/screen/blood_level/blood_display = hud.screen_objects[HUD_MOB_BLOOD_LEVEL]
+ if (blood_display)
return
- hud.infodisplay -= blood_display
- QDEL_NULL(blood_display)
+ blood_display = hud.add_screen_object(/atom/movable/screen/blood_level, HUD_MOB_BLOOD_LEVEL, HUD_GROUP_INFO, update_screen = TRUE)
diff --git a/code/modules/antagonists/blood_worm/blood_worm_mob.dm b/code/modules/antagonists/blood_worm/blood_worm_mob.dm
index a3a960abcd9..6fe2a41391f 100644
--- a/code/modules/antagonists/blood_worm/blood_worm_mob.dm
+++ b/code/modules/antagonists/blood_worm/blood_worm_mob.dm
@@ -62,9 +62,6 @@
/// This mob is always dead as it's just a mind holder.
var/mob/living/blood_worm_host/backseat
- /// The blood display on the left side of the screen, which is shown to the blood worm while in a host, if any.
- var/atom/movable/screen/blood_level/blood_display
-
// Innate and shared actions
/// Typed, please initialize with a proper action subtype. (empty = no action)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index ad138d35d56..1fdf17ef8d7 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -71,11 +71,6 @@
/// A reference to our cellular emporium action (which opens the UI for the datum).
var/datum/action/cellular_emporium/emporium_action
- /// UI displaying how many chems we have
- var/atom/movable/screen/ling/chems/lingchemdisplay
- /// UI displayng our currently active sting
- var/atom/movable/screen/ling/sting/lingstingdisplay
-
/// The name of our "hive" that our ling came from. Flavor.
var/hive_name
@@ -144,23 +139,14 @@
RegisterSignal(living_mob, COMSIG_LIVING_LIFE, PROC_REF(on_life))
RegisterSignal(living_mob, COMSIG_LIVING_POST_FULLY_HEAL, PROC_REF(on_fullhealed))
RegisterSignal(living_mob, COMSIG_CARBON_GAIN_ORGAN, PROC_REF(new_brain))
+ RegisterSignal(living_mob, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
RegisterSignals(living_mob, list(COMSIG_MOB_MIDDLECLICKON, COMSIG_MOB_ALTCLICKON), PROC_REF(on_click_sting))
ADD_TRAIT(living_mob, TRAIT_FAKE_SOULLESS, CHANGELING_TRAIT)
ADD_TRAIT(living_mob, TRAIT_BRAINLESS_CARBON, CHANGELING_TRAIT)
ADD_TRAIT(living_mob, TRAIT_CHANGELING_HIVEMIND, CHANGELING_TRAIT)
- if(living_mob.hud_used)
- var/datum/hud/hud_used = living_mob.hud_used
-
- lingchemdisplay = new /atom/movable/screen/ling/chems(null, hud_used)
- hud_used.infodisplay += lingchemdisplay
-
- lingstingdisplay = new /atom/movable/screen/ling/sting(null, hud_used)
- hud_used.infodisplay += lingstingdisplay
-
- hud_used.show_hud(hud_used.hud_version)
- else
- RegisterSignal(living_mob, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
+ if (living_mob.hud_used)
+ on_hud_created()
make_brain_decoy(living_mob)
@@ -193,14 +179,8 @@
SIGNAL_HANDLER
var/datum/hud/ling_hud = owner.current.hud_used
-
- lingchemdisplay = new(null, ling_hud)
- ling_hud.infodisplay += lingchemdisplay
-
- lingstingdisplay = new(null, ling_hud)
- ling_hud.infodisplay += lingstingdisplay
-
- ling_hud.show_hud(ling_hud.hud_version)
+ ling_hud.add_screen_object(/atom/movable/screen/ling/chems, HUD_CHANGELING_CHEMS, HUD_GROUP_INFO)
+ ling_hud.add_screen_object(/atom/movable/screen/ling/sting, HUD_CHANGELING_STING, HUD_GROUP_INFO, update_screen = TRUE)
/datum/antagonist/changeling/proc/new_brain(mob/living/carbon/ling, obj/item/organ/new_brain)
SIGNAL_HANDLER
@@ -228,14 +208,12 @@
for(var/mob/eye/imaginary_friend/hivemind/hivemind_member in living_mob.imaginary_group)
qdel(hivemind_member)
- if(living_mob.hud_used)
- var/datum/hud/hud_used = living_mob.hud_used
-
- hud_used.infodisplay -= lingchemdisplay
- hud_used.infodisplay -= lingstingdisplay
- QDEL_NULL(lingchemdisplay)
- QDEL_NULL(lingstingdisplay)
+ if(!living_mob.hud_used)
+ return
+ var/datum/hud/hud_used = living_mob.hud_used
+ hud_used.remove_screen_object(HUD_CHANGELING_CHEMS, update = FALSE)
+ hud_used.remove_screen_object(HUD_CHANGELING_STING)
// The old body's brain still remains a decoy, I guess?
/datum/antagonist/changeling/on_removal()
@@ -351,12 +329,13 @@
///Updates the Changeling's chemical HUD to display the information we want it to (chem charges, or max if hovered over).
/datum/antagonist/changeling/proc/update_chemical_hud(amount)
- if(isnull(lingchemdisplay))
+ var/atom/movable/screen/ling/chems/chems = owner.current?.hud_used?.screen_objects[HUD_CHANGELING_CHEMS]
+ if(isnull(chems))
return
- if(lingchemdisplay.hovering)
- lingchemdisplay.maptext = FORMAT_CHEM_MAX_TEXT(total_chem_storage)
+ if(chems.hovering)
+ chems.maptext = FORMAT_CHEM_MAX_TEXT(total_chem_storage)
else
- lingchemdisplay.maptext = FORMAT_CHEM_CHARGES_TEXT(amount)
+ chems.maptext = FORMAT_CHEM_CHARGES_TEXT(amount)
/*
* Remove changeling powers from the current Changeling's purchased_powers list.
diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm
index 91d7f8ba059..3368417c2c6 100644
--- a/code/modules/antagonists/changeling/powers/tiny_prick.dm
+++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm
@@ -1,7 +1,8 @@
/datum/action/changeling/sting//parent path, not meant for users afaik
- name = "Tiny Prick"
+ name = "Tiny Prick" //cellularemporium uses `nameToIconState` to button icon state must match this, on top of matching the hud below.
desc = "Stabby stabby"
category = "stings"
+ button_icon_state = "sting_null" //This must be equal to the icon state for `/atom/movable/screen/ling/sting`
/datum/action/changeling/sting/Trigger(mob/clicker, trigger_flags)
var/mob/user = owner
@@ -21,16 +22,20 @@
var/datum/antagonist/changeling/changeling = IS_CHANGELING(user)
changeling.chosen_sting = src
- changeling.lingstingdisplay.icon_state = button_icon_state
- changeling.lingstingdisplay.SetInvisibility(0, id=type)
+ var/atom/movable/screen/ling/sting/sting = user.hud_used?.screen_objects[HUD_CHANGELING_STING]
+ if (sting)
+ sting.icon_state = button_icon_state
+ sting.SetInvisibility(0, id=type)
/datum/action/changeling/sting/proc/unset_sting(mob/user)
to_chat(user, span_warning("We retract our sting, we can't sting anyone for now."))
var/datum/antagonist/changeling/changeling = IS_CHANGELING(user)
changeling.chosen_sting = null
- changeling.lingstingdisplay.icon_state = null
- changeling.lingstingdisplay.RemoveInvisibility(type)
+ var/atom/movable/screen/ling/sting/sting = user.hud_used?.screen_objects[HUD_CHANGELING_STING]
+ if (sting)
+ sting.icon_state = null
+ sting.RemoveInvisibility(type)
/mob/living/carbon/proc/unset_sting()
if(mind)
@@ -71,7 +76,7 @@
For complex humanoids, the transformation is temporarily, but the duration is paused while the victim is dead or in stasis. \
For more simple humanoids, such as monkeys, the transformation is permanent. \
Does not provide a warning to others. Mutations will not be transferred."
- button_icon_state = "sting_transform"
+ button_icon_state = "transformation_sting"
chemical_cost = 33 // Low enough that you can sting only two people in quick succession
dna_cost = 2
/// A reference to our active profile, which we grab DNA from
diff --git a/code/modules/antagonists/cult/datums/cult_team.dm b/code/modules/antagonists/cult/datums/cult_team.dm
index 130683a9046..939938f621c 100644
--- a/code/modules/antagonists/cult/datums/cult_team.dm
+++ b/code/modules/antagonists/cult/datums/cult_team.dm
@@ -169,7 +169,8 @@
SEND_SOUND(cultist.current, sound(SFX_HALLUCINATION_OVER_HERE, 0, 1, 75))
cultist.current.client.images += blood_target_image
if (cultist.current.hud_used)
- new /atom/movable/screen/navigate_arrow(null, cultist.current.hud_used, get_turf(new_target), COLOR_CULT_RED)
+ var/atom/movable/screen/navigate_arrow/arrow = cultist.current.hud_used.add_screen_object(/atom/movable/screen/navigate_arrow, HUD_CULTIST_ARROW, HUD_GROUP_INFO, update_screen = TRUE)
+ arrow.start_effect(get_turf(new_target), COLOR_CULT_RED)
if(duration != INFINITY)
blood_target_reset_timer = addtimer(CALLBACK(src, PROC_REF(unset_blood_target)), duration, TIMER_STOPPABLE)
diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm
index f8f18a8188e..78256b12a63 100644
--- a/code/modules/antagonists/heretic/heretic_living_heart.dm
+++ b/code/modules/antagonists/heretic/heretic_living_heart.dm
@@ -249,7 +249,8 @@
arrow_color = COLOR_RED
if(owner.hud_used)
- new /atom/movable/screen/navigate_arrow(null, owner.hud_used, their_turf, arrow_color)
+ var/atom/movable/screen/navigate_arrow/arrow = owner.hud_used.add_screen_object(/atom/movable/screen/navigate_arrow, HUD_HERETIC_ARROW, HUD_GROUP_INFO, update_screen = TRUE)
+ arrow.start_effect(their_turf, arrow_color)
if(ismob(tracked_thing))
var/mob/tracked_mob = tracked_thing
@@ -265,25 +266,21 @@
pixel_x = -32
pixel_y = -32
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ screen_loc = around_player
-/atom/movable/screen/navigate_arrow/Initialize(mapload, datum/hud/hud_owner, turf/tracked_turf, arrow_color)
- . = ..()
+/atom/movable/screen/navigate_arrow/proc/start_effect(turf/tracked_turf, arrow_color)
var/mob/owner = get_mob()
if (owner)
animate(src, transform = matrix(get_angle(owner, tracked_turf), MATRIX_ROTATE), 0.2 SECONDS)
- screen_loc = around_player
color = arrow_color
- if (hud)
- hud.infodisplay += src
- hud.show_hud(hud.hud_version)
addtimer(CALLBACK(src, PROC_REF(end_effect)), 1.6 SECONDS)
+/atom/movable/screen/navigate_arrow/Destroy()
+ var/datum/hud/our_hud = hud
+ . = ..()
+ if (!QDELETED(our_hud))
+ INVOKE_ASYNC(our_hud, TYPE_PROC_REF(/datum/hud, show_hud), our_hud.hud_version)
+
/atom/movable/screen/navigate_arrow/proc/end_effect()
icon_state = "navigate_arrow_disappear"
- addtimer(CALLBACK(src, PROC_REF(null_arrow)), 0.4 SECONDS)
-
-/atom/movable/screen/navigate_arrow/proc/null_arrow()
- if (hud)
- hud.infodisplay -= src
- hud.show_hud(hud.hud_version)
- qdel(src)
+ QDEL_IN(src, 0.4 SECONDS)
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index 8e496353fae..20cdbed7a9a 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -533,8 +533,6 @@
TRAIT_PACIFISM,
TRAIT_NOHUNGER
)
- /// Hud that gets shown to the wearer, gives a rough estimate of their current brain damage
- var/atom/movable/screen/moon_health/health_hud
/// Boolean if you are brain dead so the sound doesn't spam during the delay
var/braindead = FALSE
//---- Messages that get sent when someone wearing the moon robes is attacked
@@ -624,8 +622,7 @@
if(our_brain)
REMOVE_TRAIT(our_brain, TRAIT_BRAIN_DAMAGE_NODEATH, REF(src))
braindead = FALSE
- if(health_hud in user.hud_used.infodisplay)
- on_hud_remove(user)
+ on_hud_remove(user)
/obj/item/clothing/suit/hooded/cultrobes/eldritch/moon/proc/on_apply_modifiers(mob/living/user, damage_mods, damage, damagetype, def_zone, sharpness, attack_direction, attacking_item)
SIGNAL_HANDLER
@@ -655,35 +652,30 @@
/obj/item/clothing/suit/hooded/cultrobes/eldritch/moon/proc/on_hud_created(mob/living/carbon/human/wearer)
SIGNAL_HANDLER
var/datum/hud/original_hud = wearer.hud_used
- // Remove the old health elements
- var/list/to_remove = list(/atom/movable/screen/stamina, /atom/movable/screen/healths, /atom/movable/screen/healthdoll/human)
- for(var/removing in original_hud.infodisplay)
- if(is_type_in_list(removing, to_remove))
- original_hud.infodisplay -= removing
- QDEL_NULL(removing)
+
+ for(var/removing in list(HUD_MOB_STAMINA, HUD_MOB_HEALTH, HUD_MOB_HEALTHDOLL))
+ var/atom/movable/screen/to_remove = original_hud.screen_objects[removing]
+ if (to_remove)
+ to_remove.SetInvisibility(INVISIBILITY_ABSTRACT, type)
wearer.mob_mood.unmodify_hud()
// Add the moon health hud element
- health_hud = new(null, original_hud)
- original_hud.infodisplay += health_hud
- original_hud.show_hud(original_hud.hud_version)
+ original_hud.add_screen_object(/atom/movable/screen/moon_health, HUD_HERETIC_MOON_HEALTH, HUD_GROUP_INFO, update_screen = TRUE)
UnregisterSignal(wearer, COMSIG_MOB_HUD_CREATED)
signal_registered -= COMSIG_MOB_HUD_CREATED
/// Removes the HUD element from the wearer
/obj/item/clothing/suit/hooded/cultrobes/eldritch/moon/proc/on_hud_remove(mob/living/carbon/human/wearer)
var/datum/hud/original_hud = wearer.hud_used
- original_hud.infodisplay -= health_hud
- QDEL_NULL(health_hud)
+ QDEL_NULL(original_hud.screen_objects[HUD_HERETIC_MOON_HEALTH])
// Restore the old health elements
- var/atom/movable/screen/stamina/stamina_hud = new(null, original_hud)
- var/atom/movable/screen/healths/old_health_hud = new(null, original_hud)
- var/atom/movable/screen/healthdoll/human/health_doll_hud = new(null, original_hud)
- original_hud.infodisplay += stamina_hud
- original_hud.infodisplay += old_health_hud
- original_hud.infodisplay += health_doll_hud
+ for(var/restoring in list(HUD_MOB_STAMINA, HUD_MOB_HEALTH, HUD_MOB_HEALTHDOLL))
+ var/atom/movable/screen/to_restore = original_hud.screen_objects[restoring]
+ if (to_restore)
+ to_restore.RemoveInvisibility(type)
+
+ // Updates HUD on its own
wearer.mob_mood.modify_hud()
- original_hud.show_hud(original_hud.hud_version)
/obj/item/clothing/suit/hooded/cultrobes/eldritch/moon/can_mob_unequip(mob/user)
if(!ishuman(user))
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
index 087f9b6a16b..d676c284daa 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
@@ -7,44 +7,28 @@
requires_wizard_garb = FALSE
/// Icon that will be shown on wizard hud when purchasing a perk.
var/hud_icon
- /// Type path to perks hud.
- var/atom/movable/screen/perk/hud_to_show = /atom/movable/screen/perk
/datum/spellbook_entry/perks/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
var/datum/antagonist/wizard/wizard_datum = user.mind.has_antag_datum(/datum/antagonist/wizard)
if(wizard_datum)
wizard_datum.perks += src
- create_hud_icon(user, wizard_datum)
+ RegisterSignal(user, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
+ if (user.hud_used)
+ on_hud_created()
to_chat(user, span_notice("You got a new perk: [src.name]."))
log_purchase(user.key)
return TRUE
-/datum/spellbook_entry/perks/proc/create_compact_button(mob/living/carbon/human/user, datum/antagonist/wizard/wizard_datum)
- var/datum/hud/user_hud = user.hud_used
- wizard_datum.compact_button = new /atom/movable/screen/perk/more(null, user_hud)
- wizard_datum.compact_button.screen_loc = ui_perk_position(0)
- user_hud.infodisplay += wizard_datum.compact_button
- user_hud.show_hud(user_hud.hud_version)
-
-/datum/spellbook_entry/perks/proc/create_hud_icon(mob/living/carbon/human/user, datum/antagonist/wizard/wizard_datum)
- if(user.hud_used)
- var/datum/hud/user_hud = user.hud_used
- if(isnull(wizard_datum.compact_button))
- create_compact_button(user, wizard_datum)
- hud_to_show = new hud_to_show(null, user_hud)
- hud_to_show.name = name
- hud_to_show.icon_state = hud_icon
- hud_to_show.screen_loc = ui_perk_position(wizard_datum.perks.len)
- user_hud.infodisplay += hud_to_show
- user_hud.show_hud(user_hud.hud_version)
- wizard_datum.compact_button.perks_on_hud += hud_to_show
- else
- RegisterSignal(user, COMSIG_MOB_HUD_CREATED, wizard_datum, PROC_REF(on_hud_created))
-
/datum/spellbook_entry/perks/proc/on_hud_created(mob/living/carbon/human/user, datum/antagonist/wizard/wizard_datum)
SIGNAL_HANDLER
- create_hud_icon(user, wizard_datum)
+ var/datum/hud/user_hud = user.hud_used
+ if(!user_hud.screen_objects[HUD_WIZARD_COMPACT_PERKS])
+ user_hud.add_screen_object(/atom/movable/screen/perk/more, HUD_WIZARD_COMPACT_PERKS, HUD_GROUP_INFO, ui_loc = ui_perk_position(0), update_screen = TRUE)
+
+ var/atom/movable/screen/perk/perk = user_hud.add_screen_object(/atom/movable/screen/perk, HUD_WIZARD_PERK(length(wizard_datum.perks)), HUD_GROUP_INFO, ui_loc = ui_perk_position(length(wizard_datum.perks)), update_screen = TRUE)
+ perk.name = name
+ perk.icon_state = hud_icon
/datum/spellbook_entry/perks/fourhands
name = "Four Hands"
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index ce38b05c426..d76d90a35c8 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -28,8 +28,6 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
var/datum/action/cooldown/grand_ritual/ritual
/// Perks that wizard learn
var/list/perks = list()
- /// Button that hide perks hud.
- var/atom/movable/screen/perk/more/compact_button
/datum/antagonist/wizard_minion
name = "Wizard Minion"
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index dd280d120f5..af6789d02f4 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -295,7 +295,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return TRUE
/datum/preferences/proc/create_character_preview_view(mob/user)
- character_preview_view = new(null, src)
+ character_preview_view = new(null, null, src)
character_preview_view.generate_view("character_preview_[REF(character_preview_view)]")
character_preview_view.update_body()
@@ -348,7 +348,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/// Whether we show current job clothes or nude/loadout only
var/show_job_clothes = TRUE
-/atom/movable/screen/map_view/char_preview/Initialize(mapload, datum/preferences/preferences)
+/atom/movable/screen/map_view/char_preview/Initialize(mapload, datum/hud/hud_owner, datum/preferences/preferences)
. = ..()
src.preferences = preferences
diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm
index d5283c7c20c..f4535059a05 100644
--- a/code/modules/clothing/spacesuits/_spacesuits.dm
+++ b/code/modules/clothing/spacesuits/_spacesuits.dm
@@ -123,7 +123,9 @@
STOP_PROCESSING(SSobj, src)
var/mob/living/carbon/human/human_user = user
if(istype(human_user) && human_user.hud_used)
- human_user.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_NO_ICON)
+ var/atom/movable/screen/spacesuit/spacesuit_hud = human_user.hud_used.screen_objects[HUD_MOB_SPACESUIT]
+ if (spacesuit_hud)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_NO_ICON)
// Space Suit temperature regulation and power usage
/obj/item/clothing/suit/space/process(seconds_per_tick)
@@ -159,7 +161,9 @@
QDEL_NULL(cell)
var/mob/living/carbon/human/human = src.loc
if(istype(human) && human.hud_used)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_NO_ICON)
+ var/atom/movable/screen/spacesuit/spacesuit_hud = human.hud_used.screen_objects[HUD_MOB_SPACESUIT]
+ if (spacesuit_hud)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_NO_ICON)
STOP_PROCESSING(SSobj, src)
return ..()
@@ -306,8 +310,12 @@
if(!show_hud || human.wear_suit != src || isnull(human.hud_used))
return
+ var/atom/movable/screen/spacesuit/spacesuit_hud = human.hud_used.screen_objects[HUD_MOB_SPACESUIT]
+ if(!spacesuit_hud)
+ return
+
if(!cell)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_MISSING, 0, thermal_on)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_MISSING, 0, thermal_on)
return
var/cell_percent = cell.percent()
@@ -315,17 +323,17 @@
// if there is, whethere the cell's capacity indicates high, medium or low
// charge based on it.
if(cell.charge < THERMAL_REGULATOR_COST)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_EMPTY, cell_percent, thermal_on)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_EMPTY, cell_percent, thermal_on)
return
switch(cell_percent)
if(0 to 20)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_VERY_LOW, cell_percent, thermal_on)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_VERY_LOW, cell_percent, thermal_on)
if(21 to 40)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_LOW, cell_percent, thermal_on)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_LOW, cell_percent, thermal_on)
if(41 to 60)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_MID, cell_percent, thermal_on)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_MID, cell_percent, thermal_on)
if(61 to 100)
- human.hud_used.spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_HIGH, cell_percent, thermal_on)
+ spacesuit_hud.update_spacesuit_hud_icon(SPACESUIT_CELL_HIGH, cell_percent, thermal_on)
// zap the cell if we get hit with an emp
/obj/item/clothing/suit/space/emp_act(severity)
diff --git a/code/modules/engineering/tools/multitool.dm b/code/modules/engineering/tools/multitool.dm
deleted file mode 100644
index 47f4857ffc3..00000000000
--- a/code/modules/engineering/tools/multitool.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/atom/movable/screen/multitool_arrow
- icon = 'icons/effects/96x96.dmi'
- icon_state = "multitool_arrow"
- pixel_x = -32
- pixel_y = -32
-
-/atom/movable/screen/multitool_arrow/Destroy()
- if(hud)
- hud.infodisplay -= src
- INVOKE_ASYNC(hud, TYPE_PROC_REF(/datum/hud, show_hud), hud.hud_version)
- return ..()
diff --git a/code/modules/escape_menu/escape_menu.dm b/code/modules/escape_menu/escape_menu.dm
index 842d70a8c77..0042a6f7332 100644
--- a/code/modules/escape_menu/escape_menu.dm
+++ b/code/modules/escape_menu/escape_menu.dm
@@ -151,9 +151,6 @@ GLOBAL_LIST_EMPTY(escape_menus)
plane = ESCAPE_MENU_PLANE
clear_with_screen = FALSE
-// The escape menu can be opened before SSatoms
-INITIALIZE_IMMEDIATE(/atom/movable/screen/escape_menu)
-
#undef PAGE_HOME
#undef PAGE_LEAVE_BODY
#undef PAGE_QUIT_GAME
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 80d383faba9..1a8e2a7c00f 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -644,8 +644,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
else
remove_data_huds()
update_sight()
- for(var/atom/movable/screen/ghost/hudbox/hud in hud_used?.static_inventory)
- if(hud.relevant_flag & toggled)
+ for(var/hud_key in hud_used?.screen_objects)
+ var/atom/movable/screen/ghost/hudbox/hud = hud_used.screen_objects[hud_key]
+ if(istype(hud) && (hud.relevant_flag & toggled))
hud.update_appearance(UPDATE_ICON_STATE)
// This is the ghost's follow verb with an argument
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 4c57ced89bb..5fd9172e160 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -675,9 +675,13 @@
dropItemToGround(held_items[i])
held_items.len = amt
- if(hud_used)
- hud_used.build_hand_slots()
- hud_used.healthdoll.update_body_zones()
+ if(!hud_used)
+ return
+
+ hud_used.build_hand_slots(update_hud = TRUE)
+ var/atom/movable/screen/healthdoll/doll = hud_used.screen_objects[HUD_MOB_HEALTHDOLL]
+ if(doll)
+ doll.update_body_zones()
//GetAllContents that is reasonable and not stupid
/mob/living/proc/get_all_gear(equipment_flags = INCLUDE_ACCESSORIES|INCLUDE_PROSTHETICS, recursive = TRUE)
diff --git a/code/modules/mob/living/basic/blob_minions/blobbernaut.dm b/code/modules/mob/living/basic/blob_minions/blobbernaut.dm
index bbc9c1be16f..703fd0d4d1d 100644
--- a/code/modules/mob/living/basic/blob_minions/blobbernaut.dm
+++ b/code/modules/mob/living/basic/blob_minions/blobbernaut.dm
@@ -27,8 +27,6 @@
mob_size = MOB_SIZE_LARGE
ai_controller = /datum/ai_controller/basic_controller/blobbernaut
loot = null
- ///The HUD given to blobbernauts, updated by the Blob itself
- var/atom/movable/screen/healths/blob/overmind/overmind_hud
///The overlay for veins.
var/mutable_appearance/vein_overlay
///The overlay for our eyes
@@ -53,11 +51,6 @@
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
update_appearance()
-
-/mob/living/basic/blob_minion/blobbernaut/Destroy()
- QDEL_NULL(overmind_hud)
- return ..()
-
/mob/living/basic/blob_minion/blobbernaut/death(gibbed)
flick("[icon_state]_death", src)
playsound(src, 'sound/mobs/non-humanoids/blobmob/blobbernaut_death.ogg', 100, TRUE)
@@ -68,9 +61,7 @@
. = ..()
if(!.)
return
- overmind_hud = new(null, hud_used)
- hud_used.infodisplay += overmind_hud
- hud_used.show_hud(hud_used.hud_version)
+ hud_used.add_screen_object(/atom/movable/screen/healths/blob/overmind, HUD_BLOBBERNAUT_OVERMIND, HUD_GROUP_INFO, update_screen = TRUE)
/mob/living/basic/blob_minion/blobbernaut/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain)
. = ..()
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 436f8e27c89..ea32e7e5d6d 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
@@ -182,7 +182,7 @@
essencecolor = "#9A5ACB" //oh boy you've got a lot of essence
else if(essence <= 0)
essencecolor = "#1D2953" //oh jeez you're dying
- hud_used.healths.maptext = MAPTEXT("[essence]E
")
+ hud_used.screen_objects[HUD_MOB_HEALTH]?.maptext = MAPTEXT("[essence]E
")
/mob/living/basic/revenant/say(
message,
diff --git a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
index 928b5e16bdd..5633d54fb53 100644
--- a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
@@ -390,12 +390,7 @@ Doesn't work on other aliens/AI.*/
var/obj/item/organ/alien/plasmavessel/vessel = get_organ_by_type(/obj/item/organ/alien/plasmavessel)
if(!vessel)
return FALSE
- vessel.stored_plasma = max(vessel.stored_plasma + amount,0)
- vessel.stored_plasma = min(vessel.stored_plasma, vessel.max_plasma) //upper limit of max_plasma, lower limit of 0
+ vessel.adjust_plasma(amount)
for(var/datum/action/cooldown/alien/ability in actions)
ability.build_all_button_icons()
return TRUE
-
-/mob/living/carbon/alien/adjustPlasma(amount)
- . = ..()
- updatePlasmaDisplay()
diff --git a/code/modules/mob/living/carbon/alien/adult/caste/hunter.dm b/code/modules/mob/living/carbon/alien/adult/caste/hunter.dm
index c056f663849..995235d5fa2 100644
--- a/code/modules/mob/living/carbon/alien/adult/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/adult/caste/hunter.dm
@@ -18,7 +18,6 @@
ORGAN_SLOT_EXTERNAL_TAIL = /obj/item/organ/tail/xeno,
)
- var/atom/movable/screen/leap_icon = null
///How fast does our pounce move us?
var/pounce_speed = 2
@@ -26,7 +25,8 @@
/mob/living/carbon/alien/adult/hunter/proc/toggle_leap(message = 1)
leap_on_click = !leap_on_click
- leap_icon.icon_state = "leap_[leap_on_click ? "on":"off"]"
+ var/atom/movable/screen/leap_icon = hud_used?.screen_objects[HUD_ALIEN_HUNTER_LEAP]
+ leap_icon?.icon_state = "leap_[leap_on_click ? "on":"off"]"
update_icons()
if(message)
to_chat(src, span_noticealien("You will now [leap_on_click ? "leap at":"slash at"] enemies!"))
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index f5705c88494..ebbacdf2c02 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -56,38 +56,55 @@
//Instantly healing to max health in a single tick would be silly. If it takes 8 seconds to fire, then something's fucked.
var/delta_time_capped = min(delta_time, 8)
//If there are alien weeds on the ground then heal if needed or give some plasma
- if(locate(/obj/structure/alien/weeds) in owner.loc)
- if(owner.health >= owner.maxHealth)
- owner.adjustPlasma(plasma_rate * delta_time)
- else
- var/heal_amt = heal_rate
- if(!isalien(owner))
- heal_amt *= 0.2
- owner.adjustPlasma(0.5 * plasma_rate * delta_time_capped)
- owner.adjust_brute_loss(-heal_amt * delta_time_capped)
- owner.adjust_fire_loss(-heal_amt * delta_time_capped)
- owner.adjust_oxy_loss(-heal_amt * delta_time_capped)
- else
+ if(!(locate(/obj/structure/alien/weeds) in owner.loc))
owner.adjustPlasma(0.1 * plasma_rate * delta_time)
+ return
+
+ if(owner.health >= owner.maxHealth)
+ owner.adjustPlasma(plasma_rate * delta_time)
+ return
+
+ var/heal_amt = heal_rate
+ if(!isalien(owner))
+ heal_amt *= 0.2
+ owner.adjustPlasma(0.5 * plasma_rate * delta_time_capped)
+ owner.adjust_brute_loss(-heal_amt * delta_time_capped)
+ owner.adjust_fire_loss(-heal_amt * delta_time_capped)
+ owner.adjust_oxy_loss(-heal_amt * delta_time_capped)
+
+/obj/item/organ/alien/plasmavessel/proc/adjust_plasma(amount)
+ stored_plasma = clamp(stored_plasma + amount, 0, max_plasma)
+ update_plasma_display()
+
+/obj/item/organ/alien/plasmavessel/proc/on_hud_created(mob/source)
+ SIGNAL_HANDLER
+
+ if (!owner.hud_used.screen_objects[HUD_ALIEN_PLASMA_DISPLAY])
+ owner.hud_used.add_screen_object(/atom/movable/screen/alien/plasma_display, HUD_ALIEN_PLASMA_DISPLAY, HUD_GROUP_INFO, ui_loc = istype(owner.hud_used, /datum/hud/alien) ? null : ui_alienplasmadisplay_human, update_screen = TRUE)
+ update_plasma_display()
/obj/item/organ/alien/plasmavessel/on_mob_insert(mob/living/carbon/organ_owner)
. = ..()
- if(isalien(organ_owner))
- var/mob/living/carbon/alien/target_alien = organ_owner
- target_alien.updatePlasmaDisplay()
RegisterSignal(organ_owner, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))
+ RegisterSignal(organ_owner, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
+ if (organ_owner.hud_used)
+ on_hud_created()
/obj/item/organ/alien/plasmavessel/on_mob_remove(mob/living/carbon/organ_owner)
. = ..()
- if(isalien(organ_owner))
- var/mob/living/carbon/alien/organ_owner_alien = organ_owner
- organ_owner_alien.updatePlasmaDisplay()
UnregisterSignal(organ_owner, COMSIG_MOB_GET_STATUS_TAB_ITEMS)
+ UnregisterSignal(organ_owner, COMSIG_MOB_HUD_CREATED)
+ organ_owner.hud_used?.remove_screen_object(HUD_ALIEN_PLASMA_DISPLAY)
/obj/item/organ/alien/plasmavessel/proc/get_status_tab_item(mob/living/carbon/source, list/items)
SIGNAL_HANDLER
items += "Plasma Stored: [stored_plasma]/[max_plasma]"
+/obj/item/organ/alien/plasmavessel/proc/update_plasma_display()
+ owner.hud_used?.screen_objects[HUD_ALIEN_PLASMA_DISPLAY]?.maptext = MAPTEXT( \
+ "[round(owner.getPlasma())]
" \
+ )
+
#define QUEEN_DEATH_DEBUFF_DURATION 2400
/obj/item/organ/alien/hivenode
diff --git a/code/modules/mob/living/carbon/alien/screen.dm b/code/modules/mob/living/carbon/alien/screen.dm
index ca75bb2d789..1526f78a320 100644
--- a/code/modules/mob/living/carbon/alien/screen.dm
+++ b/code/modules/mob/living/carbon/alien/screen.dm
@@ -1,33 +1,30 @@
-/mob/living/carbon/alien/proc/updatePlasmaDisplay()
- if(hud_used) //clientless aliens
- hud_used.alien_plasma_display.maptext = MAPTEXT("[round(getPlasma())]
")
-
-/mob/living/carbon/alien/larva/updatePlasmaDisplay()
- return
-
/mob/living/carbon/alien/proc/findQueen()
- if(hud_used)
- hud_used.alien_queen_finder.cut_overlays()
- var/mob/queen = get_alien_type(/mob/living/carbon/alien/adult/royal/queen)
- if(!queen)
- return
- var/turf/Q = get_turf(queen)
- var/turf/A = get_turf(src)
- if(Q.z != A.z) //The queen is on a different Z level, we cannot sense that far.
- return
- var/Qdir = get_dir(src, Q)
- var/Qdist = get_dist(src, Q)
- var/finder_icon = "finder_center" //Overlay showed when adjacent to or on top of the queen!
- switch(Qdist)
- if(2 to 7)
- finder_icon = "finder_near"
- if(8 to 20)
- finder_icon = "finder_med"
- if(21 to INFINITY)
- finder_icon = "finder_far"
- var/image/finder_eye = image('icons/hud/screen_alien.dmi', finder_icon, dir = Qdir)
- hud_used.alien_queen_finder.add_overlay(finder_eye)
+ if(!hud_used)
+ return
+ var/atom/movable/screen/alien/alien_queen_finder/finder = hud_used.screen_objects[HUD_ALIEN_QUEEN_FINDER]
+ if (!finder)
+ return
+ finder.cut_overlays()
+ var/mob/queen = get_alien_type(/mob/living/carbon/alien/adult/royal/queen)
+ if(!queen)
+ return
+ var/turf/Q = get_turf(queen)
+ var/turf/A = get_turf(src)
+ if(Q.z != A.z) //The queen is on a different Z level, we cannot sense that far.
+ return
+ var/Qdir = get_dir(src, Q)
+ var/Qdist = get_dist(src, Q)
+ var/finder_icon = "finder_center" //Overlay showed when adjacent to or on top of the queen!
+ switch(Qdist)
+ if(2 to 7)
+ finder_icon = "finder_near"
+ if(8 to 20)
+ finder_icon = "finder_med"
+ if(21 to INFINITY)
+ finder_icon = "finder_far"
+ var/image/finder_eye = image('icons/hud/screen_alien.dmi', finder_icon, dir = Qdir)
+ finder.add_overlay(finder_eye)
/mob/living/carbon/alien/adult/royal/queen/findQueen()
return //Queen already knows where she is. Hopefully.
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 514e7035cba..924227483a9 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -635,11 +635,16 @@
clear_fullscreen("brute")
/mob/living/carbon/update_health_hud(shown_health_amount)
- if(!client || !hud_used?.healths)
+ if(!client || !hud_used)
+ return
+
+ var/atom/movable/screen/healths/health_hud = hud_used.screen_objects[HUD_MOB_HEALTH]
+
+ if (!health_hud)
return
if(stat == DEAD)
- hud_used.healths.icon_state = "health7"
+ health_hud.icon_state = "health7"
return
if(SEND_SIGNAL(src, COMSIG_CARBON_UPDATING_HEALTH_HUD, shown_health_amount) & COMPONENT_OVERRIDE_HEALTH_HUD)
@@ -649,25 +654,11 @@
shown_health_amount = health
if(shown_health_amount >= maxHealth)
- hud_used.healths.icon_state = "health0"
-
- else if(shown_health_amount > maxHealth * 0.8)
- hud_used.healths.icon_state = "health1"
-
- else if(shown_health_amount > maxHealth * 0.6)
- hud_used.healths.icon_state = "health2"
-
- else if(shown_health_amount > maxHealth * 0.4)
- hud_used.healths.icon_state = "health3"
-
- else if(shown_health_amount > maxHealth*0.2)
- hud_used.healths.icon_state = "health4"
-
- else if(shown_health_amount > 0)
- hud_used.healths.icon_state = "health5"
-
+ health_hud.icon_state = "health0"
+ else if(shown_health_amount > 0 && maxHealth > 0)
+ health_hud.icon_state = "health[6 - ceil(shown_health_amount / (maxHealth * 0.2))]"
else
- hud_used.healths.icon_state = "health6"
+ health_hud.icon_state = "health6"
/mob/living/carbon/set_health(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 b759c290efe..fb1ceda9558 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -417,11 +417,11 @@
//update whether handcuffs appears on our hud.
/mob/living/carbon/proc/update_hud_handcuffed()
- if(hud_used)
- for(var/hand in hud_used.hand_slots)
- var/atom/movable/screen/inventory/hand/H = hud_used.hand_slots[hand]
- if(H)
- H.update_appearance()
+ if(!hud_used)
+ return
+
+ for(var/atom/movable/screen/inventory/hand/hand in hud_used.hand_slots)
+ hand.update_appearance()
//update whether our head item appears on our hud.
/mob/living/carbon/proc/update_hud_head(obj/item/I)
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 75dbe2f24e9..72f9968d250 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -381,7 +381,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
human_who_gained_species.butcher_results = knife_butcher_results?.Copy()
//update body zones to match what they are supposed to have
- human_who_gained_species.hud_used?.healthdoll.update_body_zones()
+ var/atom/movable/screen/healthdoll/doll = human_who_gained_species.hud_used?.screen_objects[HUD_MOB_HEALTHDOLL]
+ if (doll)
+ doll.update_body_zones()
if(old_species.type != type)
replace_body(human_who_gained_species, src)
@@ -433,7 +435,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
human_who_gained_species.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS
//we don't allow it to update during species transition, so update it now
- human_who_gained_species.hud_used?.healthdoll.update_appearance()
+ human_who_gained_species.hud_used?.screen_objects[HUD_MOB_HEALTHDOLL]?.update_appearance()
/**
* Proc called when a carbon is no longer this species.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index bfac199095d..9d78211d91f 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -690,7 +690,7 @@
. = ..()
// Handles changing limb colors and stuff
if(!(living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS))
- hud_used.healthdoll?.update_appearance()
+ hud_used.screen_objects[HUD_MOB_HEALTHDOLL]?.update_appearance()
/mob/living/carbon/human/fully_heal(heal_flags = HEAL_ALL)
if(heal_flags & HEAL_NEGATIVE_MUTATIONS)
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 0539776acbe..6013c6d2b92 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -21,8 +21,6 @@
mutanttongue = /obj/item/organ/tongue/vampire
///some starter text sent to the vampire initially, because vampires have shit to do to stay alive
var/info_text = "You are a Vampire. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability."
- /// UI displaying how much blood we have
- var/atom/movable/screen/blood_level/blood_display
/datum/species/human/vampire/check_roundstart_eligible()
if(check_holidays(HALLOWEEN))
@@ -35,17 +33,14 @@
new_vampire.skin_tone = "albino"
new_vampire.update_body(0)
RegisterSignal(new_vampire, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
+ RegisterSignal(new_vampire, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
if(new_vampire.hud_used)
on_hud_created(new_vampire)
- else
- RegisterSignal(new_vampire, COMSIG_MOB_HUD_CREATED, PROC_REF(on_hud_created))
/datum/species/human/vampire/on_species_loss(mob/living/carbon/human/old_vampire, datum/species/new_species, pref_load)
. = ..()
UnregisterSignal(old_vampire, COMSIG_ATOM_ATTACKBY)
- if(blood_display)
- old_vampire.hud_used.infodisplay -= blood_display
- QDEL_NULL(blood_display)
+ old_vampire.hud_used?.remove_screen_object(HUD_MOB_BLOOD_LEVEL)
/datum/species/human/vampire/spec_life(mob/living/carbon/human/vampire, seconds_per_tick)
. = ..()
@@ -72,10 +67,7 @@
///Gives the blood HUD to the vampire so they always know how much blood they have.
/datum/species/human/vampire/proc/on_hud_created(mob/source)
SIGNAL_HANDLER
- var/datum/hud/blood_hud = source.hud_used
- blood_display = new(null, blood_hud)
- blood_hud.infodisplay += blood_display
- blood_hud.show_hud(blood_hud.hud_version)
+ source.hud_used.add_screen_object(/atom/movable/screen/blood_level, HUD_MOB_BLOOD_LEVEL, HUD_GROUP_INFO, update_screen = TRUE)
/datum/species/human/vampire/proc/on_attackby(mob/living/source, obj/item/attacking_item, mob/living/attacker, list/modifiers, list/attack_modifiers)
SIGNAL_HANDLER
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 9d6ca3159d6..a0a1ae072cc 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -761,15 +761,17 @@
/mob/living/update_rest_hud_icon()
. = ..()
- if(!.)
+ if(!. || !hud_used)
return FALSE
- if(!hud_used.sleep_icon || HAS_TRAIT(src, TRAIT_SLEEPIMMUNE))
+
+ var/atom/movable/screen/sleep/sleep_icon = hud_used.screen_objects[HUD_MOB_SLEEP]
+ if(!sleep_icon || HAS_TRAIT(src, TRAIT_SLEEPIMMUNE))
return TRUE
+
if(resting || HAS_TRAIT(src, TRAIT_FLOORED))
- hud_used.static_inventory |= hud_used.sleep_icon
+ sleep_icon.RemoveInvisibility(INVISIBILITY_SOURCE_SLEEP_HUD_BUTTON)
else
- hud_used.static_inventory -= hud_used.sleep_icon
- hud_used.show_hud(hud_used.hud_version)
+ sleep_icon.SetInvisibility(INVISIBILITY_ABSTRACT, INVISIBILITY_SOURCE_SLEEP_HUD_BUTTON)
return TRUE
//Recursive function to find everything a mob is holding. Really shitty proc tbh.
@@ -833,8 +835,8 @@
/mob/living/update_health_hud()
var/severity = 0
var/healthpercent = (health/maxHealth) * 100
- if(hud_used?.healthdoll) //to really put you in the boots of a simplemob
- var/atom/movable/screen/healthdoll/living/livingdoll = hud_used.healthdoll
+ var/atom/movable/screen/healthdoll/living/livingdoll = hud_used?.screen_objects[HUD_MOB_HEALTHDOLL]
+ if(istype(livingdoll)) //to really put you in the boots of a simplemob
switch(healthpercent)
if(100 to INFINITY)
severity = 0
@@ -859,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))
+
if(severity > 0)
overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity)
else
@@ -1430,32 +1433,27 @@
update_stamina_hud()
/mob/living/update_stamina_hud(shown_stamina_loss)
- if(!client || !hud_used?.stamina)
+ if(!client || !hud_used)
+ return
+
+ var/atom/movable/screen/stamina/stamina = hud_used.screen_objects[HUD_MOB_STAMINA]
+ if (!stamina)
+ return
+
+ if(stat == DEAD)
+ stamina.icon_state = "stamina_dead"
return
var/stam_crit_threshold = maxHealth - crit_threshold
+ if(shown_stamina_loss == null)
+ shown_stamina_loss = get_stamina_loss()
- if(stat == DEAD)
- hud_used.stamina.icon_state = "stamina_dead"
+ if(shown_stamina_loss >= stam_crit_threshold)
+ stamina.icon_state = "stamina_crit"
+ else if(shown_stamina_loss > 0 && maxHealth > 0)
+ stamina.icon_state = "stamina_[6 - ceil(shown_stamina_loss / (maxHealth * 0.2))]"
else
-
- if(shown_stamina_loss == null)
- shown_stamina_loss = get_stamina_loss()
-
- if(shown_stamina_loss >= stam_crit_threshold)
- hud_used.stamina.icon_state = "stamina_crit"
- else if(shown_stamina_loss > maxHealth*0.8)
- hud_used.stamina.icon_state = "stamina_5"
- else if(shown_stamina_loss > maxHealth*0.6)
- hud_used.stamina.icon_state = "stamina_4"
- else if(shown_stamina_loss > maxHealth*0.4)
- hud_used.stamina.icon_state = "stamina_3"
- else if(shown_stamina_loss > maxHealth*0.2)
- hud_used.stamina.icon_state = "stamina_2"
- else if(shown_stamina_loss > 0)
- hud_used.stamina.icon_state = "stamina_1"
- else
- hud_used.stamina.icon_state = "stamina_full"
+ stamina.icon_state = "stamina_full"
/mob/living/carbon/alien/update_stamina()
return
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 68bcf1fee29..701b118ebdc 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -225,8 +225,7 @@
. = combat_mode
combat_mode = new_mode
SEND_SIGNAL(src, COMSIG_COMBAT_MODE_TOGGLED)
- if(hud_used?.action_intent)
- hud_used.action_intent.update_appearance()
+ hud_used?.screen_objects[HUD_MOB_INTENTS]?.update_appearance()
if(silent || !client?.prefs.read_preference(/datum/preference/toggle/sound_combatmode))
return
if(combat_mode)
diff --git a/code/modules/mob/living/living_fov.dm b/code/modules/mob/living/living_fov.dm
index a16cf111133..8c6c0833fbf 100644
--- a/code/modules/mob/living/living_fov.dm
+++ b/code/modules/mob/living/living_fov.dm
@@ -131,3 +131,5 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = FIELD_OF_VISION_BLOCKER_PLANE
screen_loc = "BOTTOM,LEFT"
+ hud_key = HUD_MOB_FOV_BLOCKER
+ hud_group_key = HUD_GROUP_SCREEN_OVERLAYS
diff --git a/code/modules/mob/living/living_item_handling.dm b/code/modules/mob/living/living_item_handling.dm
index 482e0099491..24b3e2692d1 100644
--- a/code/modules/mob/living/living_item_handling.dm
+++ b/code/modules/mob/living/living_item_handling.dm
@@ -17,7 +17,7 @@
return
throw_mode = THROW_MODE_DISABLED
if(hud_used)
- hud_used.throw_icon.icon_state = "act_throw"
+ hud_used.screen_objects[HUD_MOB_THROW]?.icon_state = "act_throw"
SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode)
@@ -26,7 +26,7 @@
return
throw_mode = mode
if(hud_used)
- hud_used.throw_icon.icon_state = "act_throw_on"
+ hud_used.screen_objects[HUD_MOB_THROW]?.icon_state = "act_throw_on"
SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode)
/mob/proc/throw_item(atom/target)
diff --git a/code/modules/mob/living/silicon/death.dm b/code/modules/mob/living/silicon/death.dm
index 8aff9b59278..1522555af70 100644
--- a/code/modules/mob/living/silicon/death.dm
+++ b/code/modules/mob/living/silicon/death.dm
@@ -8,12 +8,10 @@
/mob/living/silicon/set_stat(new_stat)
. = ..()
- if(. != DEAD)
+ if(. != DEAD || !hud_used)
return
// Clean up hud element used for the death sequence
- for(var/atom/movable/screen/cyborg_death/deathhud in hud_used?.always_visible_inventory)
- hud_used.always_visible_inventory -= deathhud
- qdel(deathhud)
+ hud_used.remove_screen_object(HUD_CYBORG_DEATH)
/mob/living/silicon/death(gibbed)
diag_hud_set_status()
@@ -38,10 +36,8 @@
if(get_oxy_loss() > 100)
cause_of_death = "Critically low power."
- var/atom/movable/screen/cyborg_death/deathhud = new(null, hud_used, cause_of_death)
- hud_used.always_visible_inventory += deathhud
- hud_used.show_hud(hud_used.hud_version)
- deathhud.run_animation()
+ var/atom/movable/screen/cyborg_death/deathhud = hud_used.add_screen_object(/atom/movable/screen/cyborg_death, HUD_CYBORG_DEATH, HUD_GROUP_SCREEN_OVERLAYS, update_screen = TRUE)
+ deathhud.run_animation(cause_of_death)
#define WARNING_ROBOT(text) ("" + text + "")
#define DANGER_ROBOT(text) ("" + text + "")
@@ -88,13 +84,10 @@
WARNING_ROBOT("WARNI-"),
)
-/atom/movable/screen/cyborg_death/Initialize(mapload, datum/hud/hud_owner, cause_of_death = "Unidentified kernel error.")
- . = ..()
- messages.Insert(1, WARNING_ROBOT("WARNING: [cause_of_death]"))
-
-/atom/movable/screen/cyborg_death/proc/run_animation()
+/atom/movable/screen/cyborg_death/proc/run_animation(cause_of_death = "Unidentified kernel error.")
set waitfor = FALSE
+ messages.Insert(1, WARNING_ROBOT("WARNING: [cause_of_death]"))
if(prob(1))
messages[length(messages)] = SENTIENT_ROBOT(pick("I don't want to go.", "I don't feel good.", "I don't want to die."))
diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm
index d7a320c6e67..30790c176b5 100644
--- a/code/modules/mob/living/silicon/robot/inventory.dm
+++ b/code/modules/mob/living/silicon/robot/inventory.dm
@@ -77,16 +77,17 @@
. = ..()
if(isnull(client) || isnull(hud_used) || hud_used.hud_version == HUD_STYLE_NOHUD)
return
- var/turf/our_turf = get_turf(src)
- for(var/obj/item/held in held_items)
+ var/turf/our_turf = get_turf(src)
+ for (var/held_index in 1 to length(held_items))
+ var/obj/item/held = held_items[held_index]
+ if (!held)
+ continue
SET_PLANE(held, ABOVE_HUD_PLANE, our_turf)
- if(held_items[BORG_CHOOSE_MODULE_ONE] == held)
- held.screen_loc = inv1.screen_loc
- else if(held_items[BORG_CHOOSE_MODULE_TWO] == held)
- held.screen_loc = inv2.screen_loc
- else if(held_items[BORG_CHOOSE_MODULE_THREE] == held)
- held.screen_loc = inv3.screen_loc
+ var/atom/movable/screen/robot/module_slot/slot = hud_used.screen_objects[HUD_KEY_CYBORG_MODULE(held_index)]
+ if (!slot) //??
+ continue
+ held.screen_loc = slot.screen_loc
client.screen |= held
/mob/living/silicon/robot/put_in_hand_check(obj/item/item_equipping)
@@ -111,7 +112,6 @@
if(disabled_modules & BORG_MODULE_ALL_DISABLED)
return FALSE
- inv1.icon_state = "[initial(inv1.icon_state)] +b"
disabled_modules |= BORG_MODULE_ALL_DISABLED
playsound(src, 'sound/machines/warning-buzzer.ogg', 75, TRUE, TRUE)
@@ -127,7 +127,6 @@
if(disabled_modules & BORG_MODULE_TWO_DISABLED)
return FALSE
- inv2.icon_state = "[initial(inv2.icon_state)] +b"
disabled_modules |= BORG_MODULE_TWO_DISABLED
playsound(src, 'sound/machines/warning-buzzer.ogg', 60, TRUE, TRUE)
@@ -138,13 +137,15 @@
if(disabled_modules & BORG_MODULE_THREE_DISABLED)
return FALSE
- inv3.icon_state = "[initial(inv3.icon_state)] +b"
disabled_modules |= BORG_MODULE_THREE_DISABLED
playsound(src, 'sound/machines/warning-buzzer.ogg', 50, TRUE, TRUE)
audible_message(span_warning("[src] sounds an alarm! \"SYSTEM ERROR: Module [module_num] OFFLINE.\""))
to_chat(src, span_userdanger("SYSTEM ERROR: Module [module_num] OFFLINE."))
+ var/atom/movable/screen/robot/module_slot/module = hud_used?.screen_objects[HUD_KEY_CYBORG_MODULE(module_num)]
+ if(module)
+ module.icon_state = "[module.base_icon_state] +b"
return TRUE
/**
@@ -169,26 +170,27 @@
if(!(disabled_modules & BORG_MODULE_ALL_DISABLED))
return FALSE
- inv1.icon_state = initial(inv1.icon_state)
disabled_modules &= ~BORG_MODULE_ALL_DISABLED
if(builtInCamera)
builtInCamera.camera_enabled = TRUE
to_chat(src, span_notice("You hear your built in security camera focus adjust as it comes back online!"))
+
if(BORG_CHOOSE_MODULE_TWO)
if(!(disabled_modules & BORG_MODULE_TWO_DISABLED))
return FALSE
- inv2.icon_state = initial(inv2.icon_state)
disabled_modules &= ~BORG_MODULE_TWO_DISABLED
+
if(BORG_CHOOSE_MODULE_THREE)
if(!(disabled_modules & BORG_MODULE_THREE_DISABLED))
return FALSE
- inv3.icon_state = initial(inv3.icon_state)
disabled_modules &= ~BORG_MODULE_THREE_DISABLED
to_chat(src, span_notice("ERROR CLEARED: Module [module_num] back online."))
-
+ var/atom/movable/screen/robot/module_slot/module = hud_used?.screen_objects[HUD_KEY_CYBORG_MODULE(module_num)]
+ if(module)
+ module.icon_state = module.base_icon_state
return TRUE
/**
@@ -268,16 +270,9 @@
if(is_invalid_module_number(module_num) || !held_items[module_num]) //If the slot number is invalid, or there's nothing there, we have nothing to equip
return FALSE
- switch(module_num)
- if(BORG_CHOOSE_MODULE_ONE)
- if(module_active != held_items[module_num])
- inv1.icon_state = "[initial(inv1.icon_state)] +a"
- if(BORG_CHOOSE_MODULE_TWO)
- if(module_active != held_items[module_num])
- inv2.icon_state = "[initial(inv2.icon_state)] +a"
- if(BORG_CHOOSE_MODULE_THREE)
- if(module_active != held_items[module_num])
- inv3.icon_state = "[initial(inv3.icon_state)] +a"
+ var/atom/movable/screen/robot/module_slot/module = hud_used?.screen_objects[HUD_KEY_CYBORG_MODULE(module_num)]
+ if(module && module_active != held_items[module_num])
+ module.icon_state = "[module.base_icon_state] +a"
module_active = held_items[module_num]
return TRUE
@@ -287,13 +282,9 @@
* * module_num - the slot number being de-selected
*/
/mob/living/silicon/robot/proc/deselect_module(module_num)
- switch(module_num)
- if(BORG_CHOOSE_MODULE_ONE)
- inv1.icon_state = initial(inv1.icon_state)
- if(BORG_CHOOSE_MODULE_TWO)
- inv2.icon_state = initial(inv2.icon_state)
- if(BORG_CHOOSE_MODULE_THREE)
- inv3.icon_state = initial(inv3.icon_state)
+ var/atom/movable/screen/robot/module_slot/module = hud_used?.screen_objects[HUD_KEY_CYBORG_MODULE(module_num)]
+ if(module)
+ module.icon_state = module.base_icon_state
module_active = null
return TRUE
diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm
index d428c275171..a4da3bd1c80 100644
--- a/code/modules/mob/living/silicon/robot/life.dm
+++ b/code/modules/mob/living/silicon/robot/life.dm
@@ -37,22 +37,27 @@
/mob/living/silicon/robot/update_health_hud()
if(!client || !hud_used)
return
- if(hud_used.healths)
- if(stat != DEAD)
- if(health >= maxHealth)
- hud_used.healths.icon_state = "health0"
- else if(health > maxHealth*0.6)
- hud_used.healths.icon_state = "health2"
- else if(health > maxHealth*0.2)
- hud_used.healths.icon_state = "health3"
- else if(health > -maxHealth*0.2)
- hud_used.healths.icon_state = "health4"
- else if(health > -maxHealth*0.6)
- hud_used.healths.icon_state = "health5"
- else
- hud_used.healths.icon_state = "health6"
- else
- hud_used.healths.icon_state = "health7"
+
+ var/atom/movable/screen/healths/healths = hud_used.screen_objects[HUD_MOB_HEALTH]
+ if(!healths)
+ return
+
+ if(stat == DEAD)
+ healths.icon_state = "health7"
+ return
+
+ if(health >= maxHealth)
+ healths.icon_state = "health0"
+ else if(health > maxHealth*0.6)
+ healths.icon_state = "health2"
+ else if(health > maxHealth*0.2)
+ healths.icon_state = "health3"
+ else if(health > -maxHealth*0.2)
+ healths.icon_state = "health4"
+ else if(health > -maxHealth*0.6)
+ healths.icon_state = "health5"
+ else
+ healths.icon_state = "health6"
/mob/living/silicon/robot/proc/update_cell_hud_icon()
if(cell)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index b132ce92987..429b66c1a34 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -20,10 +20,6 @@
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_GOT_DAMPENED), PROC_REF(on_dampen))
- inv1 = new /atom/movable/screen/robot/module1()
- inv2 = new /atom/movable/screen/robot/module2()
- inv3 = new /atom/movable/screen/robot/module3()
-
previous_health = health
if(ispath(cell))
@@ -131,10 +127,6 @@
QDEL_NULL(model)
QDEL_NULL(eye_lights)
QDEL_NULL(hat_overlay)
- QDEL_NULL(inv1)
- QDEL_NULL(inv2)
- QDEL_NULL(inv3)
- QDEL_NULL(hands)
QDEL_NULL(spark_system)
QDEL_NULL(alert_control)
QDEL_LIST(upgrades)
@@ -513,19 +505,21 @@
if(!COOLDOWN_FINISHED(src, disabled_time))
balloon_alert(src, "disrupted!")
return FALSE
+
if(!(update_color && lamp_enabled) && (turn_off || lamp_enabled || update_color || !lamp_functional || stat || low_power_mode))
set_light_on(lamp_functional && stat != DEAD && lamp_doom) //If the lamp isn't broken and borg isn't dead, doomsday borgs cannot disable their light fully.
set_light_color(COLOR_RED) //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen
set_light_range(1) //Again, like above, this only takes effect when the light is forced on by doomsday mode.
lamp_enabled = FALSE
- lampButton?.update_appearance()
+ hud_used?.screen_objects[HUD_CYBORG_LAMP]?.update_appearance()
update_icons()
return
+
set_light_range(max(MINIMUM_USEFUL_LIGHT_RANGE, lamp_intensity))
set_light_color(lamp_doom ? COLOR_RED : lamp_color) //Red for doomsday killborgs, borg's choice otherwise
set_light_on(TRUE)
lamp_enabled = TRUE
- lampButton?.update_appearance()
+ hud_used?.screen_objects[HUD_CYBORG_LAMP]?.update_appearance()
update_icons()
///Completely deconstructs the borg, dropping the MMI/posibrain, removing applied upgrades and stripping the exoskeleton of all limbs,
@@ -762,18 +756,15 @@
/mob/living/silicon/robot/proc/update_module_innate()
designation = model.name
- if(hands)
- hands.icon_state = model.model_select_icon
+ hud_used?.screen_objects[HUD_CYBORG_HANDS]?.icon_state = model.model_select_icon
REMOVE_TRAITS_IN(src, MODEL_TRAIT)
if(length(model.model_traits))
add_traits(model.model_traits, MODEL_TRAIT)
hat_offset = model.hat_offset
-
INVOKE_ASYNC(src, PROC_REF(updatename))
-
/mob/living/silicon/robot/proc/place_on_head(obj/item/new_hat)
if(hat)
hat.forceMove(get_turf(src))
diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm
index 2cf30e5a5d9..c4cf75317d9 100644
--- a/code/modules/mob/living/silicon/robot/robot_defines.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defines.dm
@@ -69,30 +69,15 @@
// Overlay for borg hat
var/mutable_appearance/hat_overlay
-
- // Hud
- var/atom/movable/screen/inv1 = null
- var/atom/movable/screen/inv2 = null
- var/atom/movable/screen/inv3 = null
- var/atom/movable/screen/hands = null
-
- ///Lamp button reference
- var/atom/movable/screen/robot/lamp/lampButton
-
- ///The reference to the built-in tablet that borgs carry.
- var/atom/movable/screen/robot/modpc/interfaceButton
-
var/sight_mode = 0
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD)
-
// Modules (tool slots)
var/obj/item/module_active = null
///For checking which modules are disabled or not.
var/disabled_modules
-
// Status
var/mob/living/silicon/ai/connected_ai = null
diff --git a/code/modules/mob/living/simple_animal/hostile/ooze.dm b/code/modules/mob/living/simple_animal/hostile/ooze.dm
index aec9457ecae..4bb2858bce1 100644
--- a/code/modules/mob/living/simple_animal/hostile/ooze.dm
+++ b/code/modules/mob/living/simple_animal/hostile/ooze.dm
@@ -85,7 +85,9 @@
///Does ooze_nutrition + supplied amount and clamps it within 0 and 500
/mob/living/simple_animal/hostile/ooze/proc/adjust_ooze_nutrition(amount)
ooze_nutrition = clamp(ooze_nutrition + amount, 0, 500)
- updateNutritionDisplay()
+ hud_used?.screen_objects[HUD_OOZE_NUTRITION_DISPLAY]?.maptext = MAPTEXT( \
+ "[round(ooze_nutrition)]
" \
+ )
///Tries to transfer the atoms reagents then delete it
/mob/living/simple_animal/hostile/ooze/proc/eat_atom(atom/eat_target, silent)
@@ -98,12 +100,6 @@
to_chat(src, span_warning("[eat_target] cannot be eaten!"))
return FALSE
-///Updates the display that shows the mobs nutrition
-/mob/living/simple_animal/hostile/ooze/proc/updateNutritionDisplay()
- if(hud_used) //clientless oozes
- hud_used.alien_plasma_display.maptext = MAPTEXT("[round(ooze_nutrition)]
")
-
-
///* Gelatinious Ooze code below *\\\\
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 4a8c75c0b11..dfdce3c3292 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -766,13 +766,13 @@
///Update the pulling hud icon
/mob/proc/update_pull_hud_icon()
- hud_used?.pull_icon?.update_appearance()
+ hud_used?.screen_objects[HUD_MOB_PULL]?.update_appearance()
///Update the resting hud icon
/mob/proc/update_rest_hud_icon()
if(!hud_used)
return FALSE
- hud_used.rest_icon?.update_appearance()
+ hud_used.screen_objects[HUD_MOB_REST]?.update_appearance()
return TRUE
/**
@@ -943,10 +943,10 @@
active_hand_index = held_index
if(hud_used)
var/atom/movable/screen/inventory/hand/held_location
- held_location = hud_used.hand_slots["[previous_index]"]
+ held_location = hud_used.hand_slots[previous_index]
if(!isnull(held_location))
held_location.update_appearance()
- held_location = hud_used.hand_slots["[held_index]"]
+ held_location = hud_used.hand_slots[held_index]
if(!isnull(held_location))
held_location.update_appearance()
return TRUE
@@ -1501,7 +1501,9 @@
/// Updates nutrition related effects
/mob/living/proc/update_nutrition()
mob_mood?.update_nutrition_moodlets()
- hud_used?.hunger?.update_hunger_bar()
+ var/atom/movable/screen/hunger/hunger_bar = hud_used?.screen_objects[HUD_MOB_HUNGER]
+ if (hunger_bar)
+ hunger_bar.update_hunger_bar()
SEND_SIGNAL(src, COMSIG_LIVING_UPDATE_NUTRITION)
/// Apply a proper movespeed modifier based on items we have equipped
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index d461d9458cb..f0e07051a14 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -398,7 +398,7 @@
///Validate the client's mob has a valid zone selected
/client/proc/check_has_body_select()
- return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /atom/movable/screen/zone_sel)
+ return istype(mob?.hud_used?.screen_objects[HUD_MOB_ZONE_SELECTOR], /atom/movable/screen/zone_sel)
/**
* Hidden verbs to set desired body target zone
@@ -423,7 +423,7 @@
else
next_in_line = BODY_ZONE_HEAD
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(next_in_line, mob)
///Hidden verb to target the head, unbound by default.
@@ -434,7 +434,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_HEAD, mob)
///Hidden verb to target the eyes, bound to 7
@@ -445,7 +445,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_PRECISE_EYES, mob)
///Hidden verb to target the mouth, bound to 9
@@ -456,7 +456,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_PRECISE_MOUTH, mob)
///Hidden verb to target the right arm, bound to 4
@@ -467,7 +467,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_R_ARM, mob)
///Hidden verb to target the chest, bound to 5
@@ -478,7 +478,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_CHEST, mob)
///Hidden verb to target the left arm, bound to 6
@@ -489,7 +489,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_L_ARM, mob)
///Hidden verb to target the right leg, bound to 1
@@ -500,7 +500,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_R_LEG, mob)
///Hidden verb to target the groin, bound to 2
@@ -511,7 +511,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_PRECISE_GROIN, mob)
///Hidden verb to target the left leg, bound to 3
@@ -522,7 +522,7 @@
if(!check_has_body_select())
return
- var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select
+ var/atom/movable/screen/zone_sel/selector = mob.hud_used.screen_objects[HUD_MOB_ZONE_SELECTOR]
selector.set_selected_zone(BODY_ZONE_L_LEG, mob)
///Verb to toggle the walk or run status
@@ -544,11 +544,9 @@
move_intent = MOVE_INTENT_WALK
else
move_intent = MOVE_INTENT_RUN
- if(hud_used?.static_inventory)
- for(var/atom/movable/screen/mov_intent/selector in hud_used.static_inventory)
- selector.update_appearance()
- update_move_intent_slowdown()
+ hud_used?.screen_objects[HUD_MOB_MOVE_INTENT]?.update_appearance()
+ update_move_intent_slowdown()
SEND_SIGNAL(src, COMSIG_MOVE_INTENT_TOGGLED)
///Moves a mob upwards in z level
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index e9e1f1a05fe..7f6fff2e89e 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -524,7 +524,9 @@
module.on_unequip()
UnregisterSignal(wearer, list(COMSIG_ATOM_EXITED, COMSIG_SPECIES_GAIN, COMSIG_MOB_CLICKON))
SEND_SIGNAL(src, COMSIG_MOD_WEARER_UNSET, wearer)
- wearer.hud_used?.spacesuit_hud.update_spacesuit_hud_icon()
+ var/atom/movable/screen/spacesuit/spacesuit_hud = wearer.hud_used?.screen_objects[HUD_MOB_SPACESUIT]
+ if (spacesuit_hud)
+ spacesuit_hud.update_spacesuit_hud_icon()
wearer = null
/obj/item/mod/control/proc/get_sealed_slots(list/parts)
@@ -725,7 +727,9 @@
else
state_to_use = core.get_charge_icon_state()
- wearer.hud_used.spacesuit_hud.update_spacesuit_hud_icon(state_to_use || SPACESUIT_NO_ICON)
+ var/atom/movable/screen/spacesuit/spacesuit_hud = wearer.hud_used?.screen_objects[HUD_MOB_SPACESUIT]
+ if (spacesuit_hud)
+ spacesuit_hud.update_spacesuit_hud_icon(state_to_use || SPACESUIT_NO_ICON)
/obj/item/mod/control/proc/update_speed()
var/total_slowdown = 0
diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm
index 953fb3d248a..e19880d6a66 100644
--- a/code/modules/reagents/reagent_containers/cups/_cup.dm
+++ b/code/modules/reagents/reagent_containers/cups/_cup.dm
@@ -138,7 +138,9 @@
SEND_SIGNAL(src, COMSIG_GLASS_DRANK, target_mob, user)
var/fraction = min(gulp_size / reagents.total_volume, 1)
reagents.trans_to(target_mob, gulp_size, transferred_by = user, methods = reagent_consumption_method)
- user.hud_used?.hunger?.update_hunger_bar()
+ var/atom/movable/screen/hunger/hunger_bar = user.hud_used?.screen_objects[HUD_MOB_HUNGER]
+ if (istype(hunger_bar))
+ hunger_bar.update_hunger_bar()
checkLiked(fraction, target_mob)
playsound(target_mob, consumption_sound, rand(10, 50), TRUE)
var/list/datum/disease/diseases_to_add
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index 65fcb3ffc38..b26e759c218 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -46,8 +46,6 @@
var/max_slimes = 5
/// The amount of monkey cubes inside this machine.
var/stored_monkeys = 0
- /// The HUD for this console.
- var/atom/movable/screen/xenobio_console/xeno_hud
/obj/machinery/computer/camera_advanced/xenobio/Initialize(mapload)
. = ..()
@@ -60,8 +58,6 @@
actions += new /datum/action/innate/hotkey_help(src)
stored_slimes = list()
- xeno_hud = new(null, src)
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
register_context()
/obj/machinery/computer/camera_advanced/xenobio/post_machine_initialize()
@@ -77,7 +73,6 @@
var/mob/living/basic/slime/stored_slime = thing
stored_slime.forceMove(drop_location())
stored_slimes.Cut()
- QDEL_NULL(xeno_hud)
return ..()
/obj/machinery/computer/camera_advanced/xenobio/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
@@ -115,8 +110,8 @@
RegisterSignal(user, COMSIG_CLICK_SHIFT, PROC_REF(user_shift_click))
if(!user.hud_used)
return
- user.hud_used.static_inventory += xeno_hud
- user.hud_used.show_hud(user.hud_used.hud_version)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used.add_screen_object(/atom/movable/screen/xenobio_console, HUD_XENOBIO_CONSOLE, update_screen = TRUE)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
/obj/machinery/computer/camera_advanced/xenobio/remove_eye_control(mob/living/user)
UnregisterSignal(user, list(
@@ -124,10 +119,7 @@
COMSIG_MOB_ALTCLICKON,
COMSIG_CLICK_SHIFT,
))
- if(user.hud_used)
- if(xeno_hud)
- user.hud_used.static_inventory -= xeno_hud
- user.hud_used.show_hud(user.hud_used.hud_version)
+ user.hud_used?.remove_screen_object(HUD_XENOBIO_CONSOLE)
return ..()
/obj/machinery/computer/camera_advanced/xenobio/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
@@ -156,14 +148,18 @@
balloon_alert(user, "inserted")
current_potion = used_potion
- xeno_hud.update_potion(current_potion)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.update_potion(current_potion)
return ITEM_INTERACT_SUCCESS
/// Handles inserting a monkey cube into the console.
/obj/machinery/computer/camera_advanced/xenobio/proc/monkeycube_act(mob/living/user, obj/item/food/monkeycube/used_cube)
stored_monkeys += 1
balloon_alert(user, "[stored_monkeys] cube\s stored")
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
qdel(used_cube)
return ITEM_INTERACT_SUCCESS
@@ -180,7 +176,9 @@
return ITEM_INTERACT_BLOCKING
balloon_alert(user, "[stored_monkeys] cube\s stored")
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
return ITEM_INTERACT_SUCCESS
/obj/machinery/computer/camera_advanced/xenobio/multitool_act(mob/living/user, obj/item/multitool/tool)
@@ -212,19 +210,24 @@
return TRUE
///Places every slime in storage on target turf
-/obj/machinery/computer/camera_advanced/xenobio/proc/slime_place(turf/open/target_turf)
+/obj/machinery/computer/camera_advanced/xenobio/proc/slime_place(turf/open/target_turf, mob/living/user)
spit_out(stored_slimes, target_turf)
if(stored_slimes.len <= 0)
return
+
if(stored_slimes.len == 1)
target_turf.visible_message(span_notice("The slime is spat out!"))
else
target_turf.visible_message(span_notice("[stored_slimes.len] slimes are spat out!"))
+
for(var/mob/living/basic/slime/stored_slime in stored_slimes)
stored_slime.forceMove(target_turf)
REMOVE_TRAIT(stored_slime, TRAIT_STASIS, XENOBIO_CONSOLE_TRAIT)
stored_slime.handle_slime_stasis()
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
+
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
///Places every slime not controlled by a player into the internal storage, respecting its limits
///Returns TRUE to signal it hitting the limit, in case its being called from a loop and we want it to stop
@@ -233,13 +236,16 @@
// It's possible for this proc to be called on a slime that's already being picked up,
// so we need to check whether we already have to avoid duplicate entries.
return FALSE
+
if(stored_slimes.len >= max_slimes)
to_chat(user, span_warning("Slime storage is full."))
target_slime.balloon_alert(user, "storage full")
return TRUE
+
if(target_slime.ckey)
to_chat(user, span_warning("The slime wiggled free!"))
return FALSE
+
if(target_slime.buckled)
target_slime.stop_feeding(silent = TRUE)
target_slime.visible_message(span_notice("The slime gets sucked up!"))
@@ -247,8 +253,10 @@
target_slime.forceMove(src)
stored_slimes += target_slime
ADD_TRAIT(target_slime, TRAIT_STASIS, XENOBIO_CONSOLE_TRAIT)
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
return FALSE
///Places one monkey, if possible
@@ -261,13 +269,16 @@
var/mob/living/carbon/human/species/monkey/food = new /mob/living/carbon/human/species/monkey(target_turf, TRUE, user)
if (QDELETED(food))
return
+
food.apply_status_effect(/datum/status_effect/slime_food, user)
ADD_TRAIT(food, TRAIT_SPAWNED_MOB, INNATE_TRAIT)
-
stored_monkeys--
stored_monkeys = round(stored_monkeys, 0.1) //Prevents rounding errors
spit_out(food, target_turf)
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
+
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
/// Check whether we can recycle monkeys at all. Optionally, displays a balloon alert over a target atom for feedback.
/obj/machinery/computer/camera_advanced/xenobio/proc/can_recycle_monkeys(mob/living/user, atom/target_atom)
@@ -302,7 +313,7 @@
for(var/mob/living/carbon/human/target_human in target_turf)
if(!can_recycle_target_monkey(target_human))
continue
- recycle_monkey(target_human)
+ recycle_monkey(target_human, user)
monkey_found = TRUE
return monkey_found
@@ -314,11 +325,11 @@
if(!can_recycle_monkeys(user, target_human))
return FALSE
- recycle_monkey(target_human)
+ recycle_monkey(target_human, user)
return TRUE
/// Recycles a given monkey.
-/obj/machinery/computer/camera_advanced/xenobio/proc/recycle_monkey(mob/living/carbon/human/target_monkey)
+/obj/machinery/computer/camera_advanced/xenobio/proc/recycle_monkey(mob/living/carbon/human/target_monkey, mob/living/user)
var/obj/machinery/monkey_recycler/connected_recycler = connected_recycler_ref?.resolve()
if(isnull(connected_recycler))
return
@@ -328,7 +339,9 @@
connected_recycler.use_energy(500 JOULES)
stored_monkeys += connected_recycler.cube_production
stored_monkeys = round(stored_monkeys, 0.1) //Prevents rounding errors
- xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.on_update_hud(LAZYLEN(stored_slimes), stored_monkeys, max_slimes)
qdel(target_monkey)
/datum/action/innate/slime_place
@@ -346,7 +359,7 @@
if(!xeno_console.validate_turf(owner, eye_turf))
return
- xeno_console.slime_place(eye_turf)
+ xeno_console.slime_place(eye_turf, owner)
/datum/action/innate/slime_pick_up
name = "Pick up Slime"
@@ -443,7 +456,9 @@
for(var/mob/living/basic/slime/potioned_slime in eye_turf)
xeno_console.spit_atom(xeno_console.current_potion, eye_turf)
xeno_console.current_potion.interact_with_slime(potioned_slime, living_owner)
- xeno_console.xeno_hud.update_potion(xeno_console.current_potion)
+ var/atom/movable/screen/xenobio_console/xeno_hud = owner.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.update_potion(xeno_console.current_potion)
break
/datum/action/innate/hotkey_help
@@ -485,7 +500,9 @@
spit_atom(current_potion, slime_turf)
INVOKE_ASYNC(current_potion, TYPE_PROC_REF(/obj/item/slimepotion/slime, attack), target_slime, user)
- xeno_hud.update_potion(current_potion)
+ var/atom/movable/screen/xenobio_console/xeno_hud = user.hud_used?.screen_objects[HUD_XENOBIO_CONSOLE]
+ if(xeno_hud)
+ xeno_hud.update_potion(current_potion)
/// Handles console user shift-clicking, forwards to other procs based on target type.
/obj/machinery/computer/camera_advanced/xenobio/proc/user_shift_click(mob/living/user, atom/target)
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 72108859d3e..d0a3d18cfa2 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -204,7 +204,7 @@
arm_owner.set_handcuffed(null)
arm_owner.dropItemToGround(lost_cuffs, force = TRUE)
if(arm_owner.hud_used)
- var/atom/movable/screen/inventory/hand/associated_hand = arm_owner.hud_used.hand_slots["[held_index]"]
+ var/atom/movable/screen/inventory/hand/associated_hand = arm_owner.hud_used.hand_slots[held_index]
associated_hand?.update_appearance()
if(arm_owner.num_hands == 0)
arm_owner.dropItemToGround(arm_owner.gloves, force = TRUE)
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 7c112950265..9b0f56c6a58 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -211,7 +211,7 @@
if(!old_owner.hud_used)
return
- var/atom/movable/screen/inventory/hand/hand = old_owner.hud_used.hand_slots["[held_index]"]
+ var/atom/movable/screen/inventory/hand/hand = old_owner.hud_used.hand_slots[held_index]
hand?.update_appearance()
/// We need to add hand hud items and appearance, so do that here
@@ -230,7 +230,7 @@
if(!new_owner.hud_used)
return
- var/atom/movable/screen/inventory/hand/hand = new_owner.hud_used.hand_slots["[held_index]"]
+ var/atom/movable/screen/inventory/hand/hand = new_owner.hud_used.hand_slots[held_index]
hand?.update_appearance()
/obj/item/bodypart/arm/set_disabled(new_disabled)
@@ -249,7 +249,7 @@
owner.set_usable_hands(owner.usable_hands + 1)
if(owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
+ var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots[held_index]
hand_screen_object?.update_appearance()
/datum/status_effect/arm_speed_penalty
diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm
index f8cb50193d5..7429f072f53 100644
--- a/code/modules/surgery/organs/internal/stomach/_stomach.dm
+++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm
@@ -388,7 +388,9 @@
/obj/item/organ/stomach/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
- receiver.hud_used?.hunger?.update_hunger_bar()
+ var/atom/movable/screen/hunger/hunger_bar = receiver.hud_used?.screen_objects[HUD_MOB_HUNGER]
+ if(hunger_bar)
+ hunger_bar.update_hunger_bar()
RegisterSignal(receiver, COMSIG_CARBON_VOMITED, PROC_REF(on_vomit))
RegisterSignal(receiver, COMSIG_HUMAN_GOT_PUNCHED, PROC_REF(on_punched))
@@ -397,7 +399,9 @@
var/mob/living/carbon/human/human_owner = stomach_owner
human_owner.clear_alert(ALERT_DISGUST)
human_owner.clear_mood_event("disgust")
- stomach_owner.hud_used?.hunger?.update_hunger_bar()
+ var/atom/movable/screen/hunger/hunger_bar = stomach_owner.hud_used?.screen_objects[HUD_MOB_HUNGER]
+ if(hunger_bar)
+ hunger_bar.update_hunger_bar()
UnregisterSignal(stomach_owner, list(COMSIG_CARBON_VOMITED, COMSIG_HUMAN_GOT_PUNCHED))
return ..()
diff --git a/code/modules/unit_tests/unequip_defib.dm b/code/modules/unit_tests/unequip_defib.dm
index e2db399f2de..910cf599af2 100644
--- a/code/modules/unit_tests/unequip_defib.dm
+++ b/code/modules/unit_tests/unequip_defib.dm
@@ -12,7 +12,7 @@
usr = dummy // mouse drop still uses usr
- defib.MouseDrop(dummy.hud_used.hand_slots["1"])
+ defib.MouseDrop(dummy.hud_used.hand_slots[1])
if(!dummy.is_holding(defib))
TEST_FAIL("The dummy failed to remove the defib from their back via mouse drop.")
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 74f14ecea49..289bfc097bc 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -351,13 +351,12 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
returnable_list += typesof(/obj/structure/transport/linear)
// Runtimes if the associated machinery does not exist, but not the base type
returnable_list += subtypesof(/obj/machinery/airlock_controller)
- // Always ought to have an associated escape menu. Any references it could possibly hold would need one regardless.
- returnable_list += subtypesof(/atom/movable/screen/escape_menu)
// Can't spawn openspace above nothing, it'll get pissy at me
returnable_list += typesof(/turf/open/space/openspace)
returnable_list += typesof(/turf/open/openspace)
returnable_list += typesof(/obj/item/robot_model) // These should never be spawned outside of a robot.
-
+ //A lot of these depend on a hud datum to function and should not be created in a vacuum
+ returnable_list += typesof(/atom/movable/screen)
return returnable_list
/proc/RunUnitTests()
diff --git a/icons/hud/screen_changeling.dmi b/icons/hud/screen_changeling.dmi
index c3bab062ac497d18ace3cdca24db46e1291f0c8e..30c3df2994825731c7c2ab28459d1c489e4c4e6d 100644
GIT binary patch
literal 7108
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
literal 6755
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
FV1iZXBzOP-
diff --git a/icons/mob/actions/actions_changeling.dmi b/icons/mob/actions/actions_changeling.dmi
index a721505a8c07d8f20c1182cc6270ee7ce8c5b75b..aae88cd0b5ccb9e1cafc6b0640c8e7631c51a08e 100644
GIT binary patch
literal 11025
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