mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 01:34:01 +00:00
* Adds a brand-new HUD: The Trasen-Knox HUD! (#70474) Hello everyone! Today I bring to you all a fancy new HUD for you lot to enjoy: The Trasen-Knox HUD! Lovingly based off of the Common Core lore maintained by EOBGames! Well, what is it? Its a HUD with a special theming based on the old 1970's CRT screens that were commonplace at the time, including the iconic neon green and the beige plastic casing! This PR also includes a new behavior so that its possible for each active hand icon to be unique, instead of it being a sole highlighted icon as it was before. image And for those wondering, it does indeed account for multiple limbs! image Finally, as seen in the first screenshot, this PR adds the behavior to hide the HUD icon when you insert a piece of equipment into the slot. Thank you for adding the behavior, Fikou! This is all as a step for upgrading and changing how the HUDs we interact with on a daily basis can be upgraded in the future to be more appealing visually! That, and it is simply gorgeous :) PositiveEntropy, Fikou add: Adds a new HUD: The Trasen-Knox HUD! With a theming based on classic 1970s CRT screen computers! code: Added behavior for unique icons for active hands. code: Added behavior that makes it so when you equip an item, the icon behind it defaults to the blank template type. * Adds a brand-new HUD: The Trasen-Knox HUD! Co-authored-by: Imaginos16 <77556824+Imaginos16@users.noreply.github.com>
776 lines
29 KiB
Plaintext
776 lines
29 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'
|
|
))
|
|
|
|
//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
|
|
|
|
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/ammo_counter //SKYRAT EDIT ADDITION
|
|
|
|
var/atom/movable/screen/blobpwrdisplay
|
|
|
|
var/atom/movable/screen/alien_plasma_display
|
|
var/atom/movable/screen/alien_queen_finder
|
|
|
|
var/atom/movable/screen/combo/combo_display
|
|
|
|
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/throw_icon
|
|
var/atom/movable/screen/module_store_icon
|
|
|
|
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...)
|
|
var/list/screenoverlays = list() //the screen objects used as whole screen overlays (flash, damageoverlay, etc...)
|
|
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
|
|
|
|
/// 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
|
|
var/atom/movable/screen/screentip/screentip_text
|
|
|
|
/// 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
|
|
/// If this client is being shown atmos debug overlays or not
|
|
var/atmos_debug_overlays = FALSE
|
|
|
|
/// 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
|
|
var/atom/movable/screen/spacesuit
|
|
// subtypes can override this to force a specific UI style
|
|
var/ui_style
|
|
var/erp_ui_style //SKYRAT EDIT - ADDITION - ERP ICONS FIX
|
|
|
|
/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
|
|
|
|
toggle_palette = new()
|
|
toggle_palette.set_hud(src)
|
|
palette_down = new()
|
|
palette_down.set_hud(src)
|
|
palette_up = new()
|
|
palette_up.set_hud(src)
|
|
|
|
hand_slots = list()
|
|
|
|
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_text = new(null, src)
|
|
static_inventory += screentip_text
|
|
|
|
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)
|
|
|
|
AddComponent(/datum/component/zparallax, owner.client)
|
|
RegisterSignal(SSmapping, COMSIG_PLANE_OFFSET_INCREASE, .proc/on_plane_increase)
|
|
RegisterSignal(mymob, COMSIG_MOB_LOGIN, .proc/client_refresh)
|
|
RegisterSignal(mymob, COMSIG_MOB_LOGOUT, .proc/clear_client)
|
|
RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, .proc/update_sightflags)
|
|
update_sightflags(mymob, mymob.sight, NONE)
|
|
|
|
/datum/hud/proc/client_refresh(datum/source)
|
|
RegisterSignal(mymob.client, COMSIG_CLIENT_SET_EYE, .proc/on_eye_change)
|
|
on_eye_change(null, null, mymob.client.eye)
|
|
|
|
/datum/hud/proc/clear_client(datum/source)
|
|
if(mymob.canon_client)
|
|
UnregisterSignal(mymob.canon_client, COMSIG_CLIENT_SET_EYE)
|
|
|
|
/datum/hud/proc/on_eye_change(datum/source, atom/old_eye, atom/new_eye)
|
|
SIGNAL_HANDLER
|
|
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/eye_z_changed, override = TRUE)
|
|
eye_z_changed(new_eye)
|
|
|
|
/datum/hud/proc/update_sightflags(datum/source, new_sight, old_sight)
|
|
// 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
|
|
|
|
var/datum/plane_master_group/group = get_plane_group(PLANE_GROUP_MAIN)
|
|
group.transform_lower_turfs(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
|
|
var/turf/eye_turf = get_turf(eye)
|
|
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)
|
|
var/datum/plane_master_group/group = get_plane_group(PLANE_GROUP_MAIN)
|
|
if(group && should_use_scale())
|
|
group.transform_lower_turfs(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_NULL(module_store_icon)
|
|
QDEL_LIST(static_inventory)
|
|
|
|
inv_slots.Cut()
|
|
action_intent = null
|
|
zone_select = null
|
|
pull_icon = null
|
|
|
|
QDEL_LIST(toggleable_inventory)
|
|
QDEL_LIST(hotkeybuttons)
|
|
throw_icon = null
|
|
QDEL_LIST(infodisplay)
|
|
|
|
healths = null
|
|
stamina = null
|
|
healthdoll = null
|
|
spacesuit = null
|
|
blobpwrdisplay = null
|
|
alien_plasma_display = null
|
|
alien_queen_finder = null
|
|
combo_display = null
|
|
|
|
//SKYRAT EDIT START - SKYRAT HUD
|
|
wanted_lvl = null
|
|
// SKYRAT EDIT END - SKYRAT HUD
|
|
|
|
QDEL_LIST_ASSOC_VAL(master_groups)
|
|
QDEL_LIST_ASSOC_VAL(plane_master_controllers)
|
|
QDEL_LIST(screenoverlays)
|
|
mymob = null
|
|
|
|
QDEL_NULL(screentip_text)
|
|
|
|
return ..()
|
|
|
|
/datum/hud/proc/on_plane_increase(datum/source, old_max_offset, new_max_offset)
|
|
SIGNAL_HANDLER
|
|
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]
|
|
|
|
/mob/proc/create_mob_hud()
|
|
if(!client || hud_used)
|
|
return
|
|
set_hud_used(new hud_type(src))
|
|
update_sight()
|
|
SEND_SIGNAL(src, COMSIG_MOB_HUD_CREATED)
|
|
|
|
/mob/proc/set_hud_used(datum/hud/new_hud)
|
|
hud_used = new_hud
|
|
new_hud.build_action_groups()
|
|
|
|
//Version denotes which style should be displayed. blank or 0 means "next version"
|
|
/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
|
|
screenmob.client.screen = list()
|
|
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(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
|
|
|
|
screenmob.client.screen += toggle_palette
|
|
|
|
if(action_intent)
|
|
action_intent.screen_loc = initial(action_intent.screen_loc) //Restore intent selection to the original position
|
|
|
|
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
|
|
|
|
//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.
|
|
|
|
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
|
|
|
|
hud_version = display_hud_version
|
|
persistent_inventory_update(screenmob)
|
|
screenmob.update_action_buttons(1)
|
|
reorganize_alerts(screenmob)
|
|
screenmob.reload_fullscreen()
|
|
update_parallax_pref(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.hud_used.plane_masters_update()
|
|
|
|
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
|
|
hidden_inventory_update(screenmob)
|
|
|
|
/datum/hud/robot/show_hud(version = 0, mob/viewmob)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
update_robot_modules_display()
|
|
|
|
/datum/hud/proc/hidden_inventory_update()
|
|
return
|
|
|
|
/datum/hud/proc/persistent_inventory_update(mob/viewer)
|
|
if(!mymob)
|
|
return
|
|
|
|
/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/atom/item in static_inventory + toggleable_inventory + hotkeybuttons + infodisplay + screenoverlays + inv_slots)
|
|
if (item.icon == ui_style)
|
|
item.icon = new_ui_style
|
|
|
|
ui_style = new_ui_style
|
|
build_hand_slots()
|
|
|
|
//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."))
|
|
|
|
|
|
//(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()
|
|
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
|
|
hand_box.hud = src
|
|
static_inventory += hand_box
|
|
hand_box.update_appearance()
|
|
|
|
var/i = 1
|
|
for(var/atom/movable/screen/swap_hand/SH in static_inventory)
|
|
SH.screen_loc = ui_swaphand_position(mymob,!(i % 2) ? 2: 1)
|
|
i++
|
|
for(var/atom/movable/screen/human/equip/E in static_inventory)
|
|
E.screen_loc = ui_equip_position(mymob)
|
|
|
|
if(ismob(mymob) && mymob.hud_used == src)
|
|
show_hud(hud_version)
|
|
|
|
/datum/hud/proc/update_locked_slots()
|
|
return
|
|
|
|
/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
|
|
|
|
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.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, world.icon_size, our_client.view_size.getView()) // Asks for a location adjacent to our button that won't overflow the map
|
|
|
|
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
|
|
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()
|
|
|
|
/// Clears all currently visible landings
|
|
/datum/hud/proc/hide_landings()
|
|
listed_actions.clear_landing()
|
|
palette_actions.clear_landing()
|
|
|
|
// Updates any existing "owned" visuals, ensures they continue to be visible
|
|
/datum/hud/proc/update_our_owner()
|
|
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?.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)
|
|
// 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)
|
|
|
|
/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?.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] > world.icon_size && offsets[1] < view_size[1] && offsets[2] > world.icon_size && 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] > world.icon_size && offsets[1] < view_size[1] && offsets[2] > world.icon_size && 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
|