Files

705 lines
27 KiB
Plaintext

/*
The hud datum
Used to show and hide huds for all the different mob types,
including inventories and item quick actions.
*/
// The default UI style is the first one in the list
GLOBAL_LIST_INIT(available_ui_styles, list(
"Midnight" = 'icons/hud/screen_midnight.dmi',
"Retro" = 'icons/hud/screen_retro.dmi',
"Plasmafire" = 'icons/hud/screen_plasmafire.dmi',
"Slimecore" = 'icons/hud/screen_slimecore.dmi',
"Operative" = 'icons/hud/screen_operative.dmi',
"Clockwork" = 'icons/hud/screen_clockwork.dmi',
"Glass" = 'icons/hud/screen_glass.dmi',
"Trasen-Knox" = 'icons/hud/screen_trasenknox.dmi',
"Detective" = 'icons/hud/screen_detective.dmi',
"Blue - 98" = 'modular_zubbers/master_files/icons/hud/screen_blue98.dmi' // Bubber Addition
))
//SKYRAT EDIT - ADDITION - ERP ICONS FIX
GLOBAL_LIST_INIT(available_erp_ui_styles, list(
"Midnight" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/midnight.dmi',
"Retro" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/retro.dmi',
"Plasmafire" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/plasmafire.dmi',
"Slimecore" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/slimecore.dmi',
"Operative" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/operative.dmi',
"Clockwork" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/clockwork.dmi',
"Glass" = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/inventory_icons/glass.dmi'
))
//SKYRAT EDIT - ADDITION - ERP ICONS FIX - END
/proc/ui_style2icon(ui_style)
return GLOB.available_ui_styles[ui_style] || GLOB.available_ui_styles[GLOB.available_ui_styles[1]]
//SKYRAT EDIT - ADDITION - ERP ICONS FIX
/proc/erp_ui_style2icon(ui_style)
return GLOB.available_erp_ui_styles[ui_style] || GLOB.available_erp_ui_styles[GLOB.available_erp_ui_styles[1]]
//SKYRAT EDIT - ADDITION - ERP ICONS FIX - END
/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/atom/movable/screen/ammo_counter //SKYRAT EDIT ADDITION
var/atom/movable/screen/text/activation_text/activation // BUBBER EDIT ADDITION
var/hotkey_ui_hidden = FALSE
/// 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()
///Assoc list of controller groups, associated with key string group name with value of the plane master controller ref
var/list/atom/movable/plane_master_controller/plane_master_controllers = list()
/// Think of multiz as a stack of z levels. Each index in that stack has its own group of plane masters
/// This variable is the plane offset our mob/client is currently "on"
/// We use it to track what we should show/not show
/// 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
/// 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
/// had with a proc call, especially on one of the hottest procs in the
/// game (MouseEntered).
var/screentips_enabled = SCREENTIP_PREFERENCE_ENABLED
/// Whether to use text or images for click hints.
/// Same behavior as `screentips_enabled`--very hot, updated when the preference is updated.
var/screentip_images = TRUE
/// If this client is being shown atmos debug overlays or not
var/atmos_debug_overlays = FALSE
///Boolean on whether they need at least a healths or healdoll, for unit tests.
var/needs_health_indicator = TRUE
/// The color to use for the screentips.
/// This is updated by the preference for cheaper reads than would be
/// had with a proc call, especially on one of the hottest procs in the game (MouseEntered).
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 = 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 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
var/erp_ui_style //SKYRAT EDIT - ADDITION - ERP ICONS FIX
/// Can be changed in initialize_screen_objects() to add/remove slots
var/list/datum/inventory_slot/default_inventory_slots = null
/// Alist of slot_id -> inventory_slot datum of our slots
/// These are singletons, do not modify individual slots!
var/list/datum/inventory_slot/inventory_slots = alist()
/// 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,
/// and avoid needing to make changes to all idk 300 consumers if we want to change the appearance
var/list/asset_refs_for_reuse = list()
/datum/hud/New(mob/owner)
mymob = owner
if (!ui_style)
// 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))
erp_ui_style = erp_ui_style2icon(owner.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)) //SKYRAT EDIT - ADDITION - ERP ICONS FIX
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)
var/datum/plane_master_group/main/main_group = new(PLANE_GROUP_MAIN)
main_group.attach_to(src)
var/datum/preferences/preferences = owner?.client?.prefs
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 = add_screen_object(/atom/movable/screen/screentip, HUD_MOB_SCREENTIP)
activation = new(null, src) // BUBBER EDIT ADDITION
add_screen_object(activation, null, HUD_GROUP_STATIC) // BUBBER EDIT ADDITION
for(var/mytype in subtypesof(/atom/movable/plane_master_controller))
var/atom/movable/plane_master_controller/controller_instance = new mytype(null,src)
plane_master_controllers[controller_instance.name] = controller_instance
owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness)
// Register onto the global spacelight appearances
// So they can be render targeted by anything in the world
for(var/obj/starlight_appearance/starlight as anything in GLOB.starlight_objects)
register_reuse(starlight)
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, PROC_REF(on_plane_increase))
RegisterSignal(mymob, COMSIG_MOB_LOGIN, PROC_REF(client_refresh))
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
inventory_slots.Cut()
QDEL_LIST_ASSOC_VAL(screen_objects)
QDEL_LIST_ASSOC_VAL(master_groups)
QDEL_LIST_ASSOC_VAL(plane_master_controllers)
mymob = null
QDEL_NULL(activation) // BUBBER EDIT ADDITION
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, ui_loc)
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
RegisterSignal(client, COMSIG_CLIENT_SET_EYE, PROC_REF(on_eye_change))
on_eye_change(null, null, client.eye)
/datum/hud/proc/clear_client(datum/source)
SIGNAL_HANDLER
if(mymob.canon_client)
UnregisterSignal(mymob.canon_client, COMSIG_CLIENT_SET_EYE)
/datum/hud/proc/on_viewdata_update(datum/source, view)
SIGNAL_HANDLER
view_audit_buttons()
/datum/hud/proc/on_eye_change(datum/source, atom/old_eye, atom/new_eye)
SIGNAL_HANDLER
SEND_SIGNAL(src, COMSIG_HUD_EYE_CHANGED, old_eye, new_eye)
if(old_eye)
UnregisterSignal(old_eye, COMSIG_MOVABLE_Z_CHANGED)
if(new_eye)
// By the time logout runs, the client's eye has already changed
// There's just no log of the old eye, so we need to override
// :sadkirby:
RegisterSignal(new_eye, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(eye_z_changed), override = TRUE)
eye_z_changed(new_eye)
/datum/hud/proc/update_sightflags(datum/source, new_sight, old_sight)
SIGNAL_HANDLER
// If neither the old and new flags can see turfs but not objects, don't transform the turfs
// This is to ensure parallax works when you can't see holder objects
if(should_sight_scale(new_sight) == should_sight_scale(old_sight))
return
for(var/group_key in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
group.build_planes_offset(src, current_plane_offset)
/datum/hud/proc/should_use_scale()
return should_sight_scale(mymob.sight)
/datum/hud/proc/should_sight_scale(sight_flags)
return (sight_flags & (SEE_TURFS | SEE_OBJS)) != SEE_TURFS
/datum/hud/proc/eye_z_changed(atom/eye)
SIGNAL_HANDLER
update_parallax_pref() // If your eye changes z level, so should your parallax prefs
var/turf/eye_turf = get_turf(eye)
if(!eye_turf)
return
SEND_SIGNAL(src, COMSIG_HUD_Z_CHANGED, eye_turf.z)
var/new_offset = GET_TURF_PLANE_OFFSET(eye_turf)
if(current_plane_offset == new_offset)
return
var/old_offset = current_plane_offset
current_plane_offset = new_offset
SEND_SIGNAL(src, COMSIG_HUD_OFFSET_CHANGED, old_offset, new_offset)
for(var/group_key in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
group.build_planes_offset(src, new_offset)
/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)
register_reuse(GLOB.starlight_objects[i + 1])
build_plane_groups(old_max_offset + 1, new_max_offset)
/// Creates the required plane masters to fill out new z layers (because each "level" of multiz gets its own plane master set)
/datum/hud/proc/build_plane_groups(starting_offset, ending_offset)
for(var/group_key in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
group.build_plane_masters(starting_offset, ending_offset)
/// Returns the plane master that matches the input plane from the passed in group
/datum/hud/proc/get_plane_master(plane, group_key = PLANE_GROUP_MAIN)
var/plane_key = "[plane]"
var/datum/plane_master_group/group = master_groups[group_key]
return group.plane_masters[plane_key]
/// Returns a list of all plane masters that match the input true plane, drawn from the passed in group (ignores z layer offsets)
/datum/hud/proc/get_true_plane_masters(true_plane, group_key = PLANE_GROUP_MAIN)
var/list/atom/movable/screen/plane_master/masters = list()
for(var/plane in TRUE_PLANE_TO_OFFSETS(true_plane))
masters += get_plane_master(plane, group_key)
return masters
/// Returns all the planes belonging to the passed in group key
/datum/hud/proc/get_planes_from(group_key)
var/datum/plane_master_group/group = master_groups[group_key]
return group.plane_masters
/// Returns the corresponding plane group datum if one exists
/datum/hud/proc/get_plane_group(key)
return master_groups[key]
///Creates the mob's visible HUD, returns FALSE if it can't, TRUE if it did.
/mob/proc/create_mob_hud()
if(!client || hud_used)
return FALSE
set_hud_used(new hud_type(src))
update_sight()
SEND_SIGNAL(src, COMSIG_MOB_HUD_CREATED)
return TRUE
/mob/proc/set_hud_used(datum/hud/new_hud)
hud_used = new_hud
new_hud.build_action_groups()
/**
* Shows this hud's hud to some mob
*
* Arguments
* * version - denotes which style should be displayed. blank or 0 means "next version"
* * 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))
return FALSE
var/mob/screenmob = viewmob || mymob
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
display_hud_version = hud_version + 1
// 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
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/combattoggle/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
// Do not show open storages to viewers, they get their own storage UIs
if (length(group_storage) && viewmob == mymob)
screenmob.client.screen += group_storage
screenmob.client.screen += palette
//Restore intent selection to the original position
if(action_intent)
action_intent.screen_loc = action_intent.default_screen_location
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/i in 1 to length(mymob.held_items))
var/atom/movable/screen/hand = screen_objects[HUD_KEY_HAND_SLOT(i)]
if (hand)
screenmob.client.screen += hand
if(action_intent)
//move this to the alternative position, where zone_select usually is.
action_intent.screen_loc = ui_acti_alt
if (HUD_STYLE_NOHUD)
hud_shown = FALSE
hud_version = display_hud_version
inventory_update(screenmob)
// Gives all of the actions the screenmob owes to their hud
screenmob.update_action_buttons(TRUE)
// Handles alerts - the things on the right side of the screen
reorganize_alerts(screenmob)
screenmob.reload_fullscreen()
if(screenmob == mymob)
update_parallax_pref()
else
viewmob.hud_used.update_parallax_pref()
update_reuse(screenmob)
// ensure observers get an accurate and up-to-date view
if (!viewmob)
plane_masters_update()
for(var/M in mymob.observers)
show_hud(hud_version, M)
else if (viewmob.hud_used)
viewmob.hide_other_mob_action_buttons(mymob)
viewmob.hud_used.plane_masters_update()
viewmob.show_other_mob_action_buttons(mymob)
SEND_SIGNAL(screenmob, COMSIG_MOB_HUD_REFRESHED, src)
return TRUE
/datum/hud/proc/plane_masters_update()
for(var/group_key in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
// Plane masters are always shown to OUR mob, never to observers
group.refresh_hud()
/datum/hud/human/show_hud(version = 0,mob/viewmob)
. = ..()
if(!.)
return
var/mob/screenmob = viewmob || mymob
inventory_update(screenmob)
/* BUBBER EDIT REMOVAL - We use a different lobby hud
/datum/hud/new_player/show_hud(version = 0, mob/viewmob)
. = ..()
if(.)
show_station_trait_buttons()
*/
/datum/hud/proc/inventory_update(mob/viewer)
if (isnull(mymob))
return
for (var/slot_id in inventory_slots)
var/datum/inventory_slot/slot = inventory_slots[slot_id]
if (!isnull(slot))
slot.update_inventory_slot(src, mymob)
/datum/hud/proc/update_inventory_slot(slot_id, ...)
if(isnull(mymob))
return
var/datum/inventory_slot/slot = inventory_slots[slot_id]
if (!isnull(slot))
var/list/slot_args = list(src, mymob) + args.Copy(2)
slot.update_inventory_slot(arglist(slot_args))
/datum/hud/proc/update_ui_style(new_ui_style)
// do nothing if overridden by a subtype or already on that style
if (initial(ui_style) || ui_style == new_ui_style)
return
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(update_hud = TRUE)
/datum/hud/proc/register_reuse(atom/movable/screen/reuse)
asset_refs_for_reuse += WEAKREF(reuse)
mymob?.client?.screen += reuse
/datum/hud/proc/unregister_reuse(atom/movable/screen/reuse)
asset_refs_for_reuse -= WEAKREF(reuse)
mymob?.client?.screen -= reuse
/datum/hud/proc/update_reuse(mob/show_to)
for(var/datum/weakref/screen_ref as anything in asset_refs_for_reuse)
var/atom/movable/screen/reuse = screen_ref.resolve()
if(isnull(reuse))
asset_refs_for_reuse -= screen_ref
continue
show_to.client?.screen += reuse
//Triggered when F12 is pressed (Unless someone changed something in the DMF)
/mob/verb/button_pressed_F12()
set name = "F12"
set hidden = TRUE
if(hud_used && client)
hud_used.show_hud() //Shows the next hud preset
to_chat(usr, span_info("Switched HUD mode. Press F12 to toggle."))
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)
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_state = "hand_[mymob.held_index_to_dir(i)]"
hand_box.held_index = i
hand_box.update_appearance()
var/num_of_swaps = 0
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 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 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(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 default_inventory_slots
/datum/hud/proc/create_inventory_slots()
var/list/created_paths = default_inventory_slots
if (ispath(created_paths))
created_paths = valid_subtypesof(created_paths)
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)
inventory_slots[inv_slot.slot_id] = inv_slot
// Lets add an abstract "hands" slot to ourselves for native handling
inventory_slots[ITEM_SLOT_HANDS] = GLOB.inventory_slot_datums[/datum/inventory_slot/hands]
inventory_update()
/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
position_action(button, button.linked_action.default_button_position)
return
if(SCRN_OBJ_IN_LIST)
listed_actions.insert_action(button)
if(SCRN_OBJ_IN_PALETTE)
palette_actions.insert_action(button)
if(SCRN_OBJ_INSERT_FIRST)
listed_actions.insert_action(button, index = 1)
position = SCRN_OBJ_IN_LIST
else // If we don't have it as a define, this is a screen_loc, and we should be floating
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
/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
// 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
/// Removes the passed in action from its current position on the screen
/datum/hud/proc/hide_action(atom/movable/screen/movable/action_button/button)
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()
/// Ensures all of our buttons are properly within the bounds of our client's view, moves them if they're not
/datum/hud/proc/view_audit_buttons()
var/our_view = mymob?.canon_client?.view
if(!our_view)
return
listed_actions.check_against_view()
palette_actions.check_against_view()
for(var/atom/movable/screen/movable/action_button/floating_button as anything in floating_actions)
var/list/current_offsets = screen_loc_to_offset(floating_button.screen_loc, our_view)
// We set the view arg here, so the output will be properly hemm'd in by our new view
floating_button.screen_loc = offset_to_screen_loc(current_offsets[1], current_offsets[2], view = our_view)
/// Generates and fills new action groups with our mob's current actions
/datum/hud/proc/build_action_groups()
listed_actions = new(src)
palette_actions = new(src)
floating_actions = list()
for(var/datum/action/action as anything in mymob.actions)
var/atom/movable/screen/movable/action_button/button = action.viewers[src]
if(!button)
action.ShowTo(mymob)
else
position_action(button, button.location)