mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-04 13:45:25 +01:00
329921639a
## About The Pull Request
- Rewrites how action button icons are generated.
- Prior, generated an action button icon was fairly simplistic and
didn't allow for many changes. Someone recently added the option for
overlays to be generated over action buttons, but the framework was very
weak.
- Now, action button icon generation is split across multiple procs,
like atom icon updates.
- The background of action buttons are underlays
- The actual icon of the action button is the icon and icon state of the
action button movable
- The rim / border of the button is an overlay, layered overtop the
button.
- Allows observers to see what action buttons a mob has. They even
update in real time! And no, the observers cannot click on them.
## Why It's Good For The Game
- Runechat text of action buttons are no longer hidden behind the actual
icon. This was very ugly with cooldown actions, as the cooldown text was
hidden behind a lot of spell icons.
- Cuts down on a lot of icon duplication.
- Gives much finer control over action button icons
- Saves a bit of processing from generating full action button icons
when not necessary. Not implemented in many places, but is in some.


## Changelog
🆑 Melbert
add: Observers can now see what action buttons an observed mob has. No,
you can't click them. And no it doesn't show EVERY action.
refactor: Refactored how action button icons are generated. Some actions
will now use a colored border when active instead of just turning green.
Cooldown text will also appear on the top layer of actions too. If you
see any funky lookin' icons (namely their borders), let me know.
refactor: Bluespace Golem's teleport action is now a cooldown action.
fix: Construct actions go to the middle of the screen like expected.
/🆑
472 lines
15 KiB
Plaintext
472 lines
15 KiB
Plaintext
/atom/movable/screen/movable/action_button
|
|
var/datum/action/linked_action
|
|
var/datum/hud/our_hud
|
|
var/actiontooltipstyle = ""
|
|
screen_loc = null
|
|
|
|
/// The icon state of our active overlay, used to prevent re-applying identical overlays
|
|
var/active_overlay_icon_state
|
|
/// The icon state of our active underlay, used to prevent re-applying identical underlays
|
|
var/active_underlay_icon_state
|
|
/// The overlay we have overtop our button
|
|
var/mutable_appearance/button_overlay
|
|
|
|
/// Where we are currently placed on the hud. SCRN_OBJ_DEFAULT asks the linked action what it thinks
|
|
var/location = SCRN_OBJ_DEFAULT
|
|
/// A unique bitflag, combined with the name of our linked action this lets us persistently remember any user changes to our position
|
|
var/id
|
|
/// A weakref of the last thing we hovered over
|
|
/// God I hate how dragging works
|
|
var/datum/weakref/last_hovored_ref
|
|
|
|
/atom/movable/screen/movable/action_button/Destroy()
|
|
if(our_hud)
|
|
var/mob/viewer = our_hud.mymob
|
|
our_hud.hide_action(src)
|
|
viewer?.client?.screen -= src
|
|
linked_action.viewers -= our_hud
|
|
viewer.update_action_buttons()
|
|
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
|
|
if(dead_mob.observetarget) // Observers can only click on action buttons if they're not observing something
|
|
return FALSE
|
|
|
|
if(linked_action)
|
|
if(linked_action.viewers[user.hud_used])
|
|
return TRUE
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/atom/movable/screen/movable/action_button/Click(location,control,params)
|
|
if (!can_use(usr))
|
|
return FALSE
|
|
|
|
var/list/modifiers = params2list(params)
|
|
if(LAZYACCESS(modifiers, SHIFT_CLICK))
|
|
var/datum/hud/our_hud = usr.hud_used
|
|
our_hud.position_action(src, SCRN_OBJ_DEFAULT)
|
|
return TRUE
|
|
if(usr.next_click > world.time)
|
|
return
|
|
usr.next_click = world.time + 1
|
|
var/trigger_flags
|
|
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
|
trigger_flags |= TRIGGER_SECONDARY_ACTION
|
|
linked_action.Trigger(trigger_flags = trigger_flags)
|
|
return TRUE
|
|
|
|
// Entered and Exited won't fire while you're dragging something, because you're still "holding" it
|
|
// Very much byond logic, but I want nice behavior, so we fake it with drag
|
|
/atom/movable/screen/movable/action_button/MouseDrag(atom/over_object, src_location, over_location, src_control, over_control, params)
|
|
. = ..()
|
|
if(!can_use(usr))
|
|
return
|
|
if(IS_WEAKREF_OF(over_object, last_hovored_ref))
|
|
return
|
|
var/atom/old_object
|
|
if(last_hovored_ref)
|
|
old_object = last_hovored_ref?.resolve()
|
|
else // If there's no current ref, we assume it was us. We also treat this as our "first go" location
|
|
old_object = src
|
|
var/datum/hud/our_hud = usr.hud_used
|
|
our_hud?.generate_landings(src)
|
|
|
|
if(old_object)
|
|
old_object.MouseExited(over_location, over_control, params)
|
|
|
|
last_hovored_ref = WEAKREF(over_object)
|
|
over_object.MouseEntered(over_location, over_control, params)
|
|
|
|
/atom/movable/screen/movable/action_button/MouseEntered(location, control, params)
|
|
. = ..()
|
|
if(!QDELETED(src))
|
|
openToolTip(usr, src, params, title = name, content = desc, theme = actiontooltipstyle)
|
|
|
|
/atom/movable/screen/movable/action_button/MouseExited(location, control, params)
|
|
closeToolTip(usr)
|
|
return ..()
|
|
|
|
/atom/movable/screen/movable/action_button/MouseDrop(over_object)
|
|
last_hovored_ref = null
|
|
if(!can_use(usr))
|
|
return
|
|
var/datum/hud/our_hud = usr.hud_used
|
|
if(over_object == src)
|
|
our_hud.hide_landings()
|
|
return
|
|
if(istype(over_object, /atom/movable/screen/action_landing))
|
|
var/atom/movable/screen/action_landing/reserve = over_object
|
|
reserve.hit_by(src)
|
|
our_hud.hide_landings()
|
|
save_position()
|
|
return
|
|
|
|
our_hud.hide_landings()
|
|
if(istype(over_object, /atom/movable/screen/button_palette) || istype(over_object, /atom/movable/screen/palette_scroll))
|
|
our_hud.position_action(src, SCRN_OBJ_IN_PALETTE)
|
|
save_position()
|
|
return
|
|
if(istype(over_object, /atom/movable/screen/movable/action_button))
|
|
var/atom/movable/screen/movable/action_button/button = over_object
|
|
our_hud.position_action_relative(src, button)
|
|
save_position()
|
|
return
|
|
. = ..()
|
|
our_hud.position_action(src, screen_loc)
|
|
save_position()
|
|
|
|
/atom/movable/screen/movable/action_button/proc/save_position()
|
|
var/mob/user = our_hud.mymob
|
|
if(!user?.client)
|
|
return
|
|
var/position_info = ""
|
|
switch(location)
|
|
if(SCRN_OBJ_FLOATING)
|
|
position_info = screen_loc
|
|
if(SCRN_OBJ_IN_LIST)
|
|
position_info = SCRN_OBJ_IN_LIST
|
|
if(SCRN_OBJ_IN_PALETTE)
|
|
position_info = SCRN_OBJ_IN_PALETTE
|
|
|
|
user.client.prefs.action_buttons_screen_locs["[name]_[id]"] = position_info
|
|
|
|
/atom/movable/screen/movable/action_button/proc/load_position()
|
|
var/mob/user = our_hud.mymob
|
|
if(!user)
|
|
return
|
|
var/position_info = user.client?.prefs?.action_buttons_screen_locs["[name]_[id]"] || SCRN_OBJ_DEFAULT
|
|
user.hud_used.position_action(src, position_info)
|
|
|
|
/atom/movable/screen/movable/action_button/proc/dump_save()
|
|
var/mob/user = our_hud.mymob
|
|
if(!user?.client)
|
|
return
|
|
user.client.prefs.action_buttons_screen_locs -= "[name]_[id]"
|
|
|
|
/**
|
|
* This is a silly proc used in hud code code to determine what icon and icon state we should be using
|
|
* for hud elements (such as action buttons) that don't have their own icon and icon state set.
|
|
*
|
|
* It returns a list, which is pretty much just a struct of info
|
|
*/
|
|
/datum/hud/proc/get_action_buttons_icons()
|
|
. = list()
|
|
.["bg_icon"] = ui_style
|
|
.["bg_state"] = "template"
|
|
.["bg_state_active"] = "template_active"
|
|
|
|
/**
|
|
* Updates all action buttons this mob has.
|
|
*
|
|
* Arguments:
|
|
* * update_flags - Which flags of the action should we update
|
|
* * force - Force buttons update even if the given button icon state has not changed
|
|
*/
|
|
/mob/proc/update_mob_action_buttons(update_flags = ALL, force = FALSE)
|
|
for(var/datum/action/current_action as anything in actions)
|
|
current_action.build_all_button_icons(update_flags, force)
|
|
|
|
/**
|
|
* This proc handles adding all of the mob's actions to their screen
|
|
*
|
|
* If you just need to update existing buttons, use [/mob/proc/update_mob_action_buttons]!
|
|
*
|
|
* Arguments:
|
|
* * update_flags - reload_screen - bool, if TRUE, this proc will add the button to the screen of the passed mob as well
|
|
*/
|
|
/mob/proc/update_action_buttons(reload_screen = FALSE)
|
|
if(!hud_used || !client)
|
|
return
|
|
|
|
if(hud_used.hud_shown != HUD_STYLE_STANDARD)
|
|
return
|
|
|
|
for(var/datum/action/action as anything in actions)
|
|
var/atom/movable/screen/movable/action_button/button = action.viewers[hud_used]
|
|
action.build_all_button_icons()
|
|
if(reload_screen)
|
|
client.screen += button
|
|
|
|
if(reload_screen)
|
|
hud_used.update_our_owner()
|
|
// This holds the logic for the palette buttons
|
|
hud_used.palette_actions.refresh_actions()
|
|
|
|
/**
|
|
* Show (most) of the another mob's action buttons to this mob
|
|
*
|
|
* Used for observers viewing another mob's screen
|
|
*/
|
|
/mob/proc/show_other_mob_action_buttons(mob/take_from)
|
|
if(!hud_used || !client)
|
|
return
|
|
|
|
for(var/datum/action/action as anything in take_from.actions)
|
|
if(!action.show_to_observers)
|
|
continue
|
|
action.GiveAction(src)
|
|
RegisterSignal(take_from, COMSIG_MOB_GRANTED_ACTION, PROC_REF(on_observing_action_granted))
|
|
RegisterSignal(take_from, COMSIG_MOB_REMOVED_ACTION, PROC_REF(on_observing_action_removed))
|
|
|
|
/**
|
|
* Hide another mob's action buttons from this mob
|
|
*
|
|
* Used for observers viewing another mob's screen
|
|
*/
|
|
/mob/proc/hide_other_mob_action_buttons(mob/take_from)
|
|
for(var/datum/action/action as anything in take_from.actions)
|
|
action.HideFrom(src)
|
|
UnregisterSignal(take_from, list(COMSIG_MOB_GRANTED_ACTION, COMSIG_MOB_REMOVED_ACTION))
|
|
|
|
/// Signal proc for [COMSIG_MOB_GRANTED_ACTION] - If we're viewing another mob's action buttons,
|
|
/// we need to update with any newly added buttons granted to the mob.
|
|
/mob/proc/on_observing_action_granted(mob/living/source, datum/action/action)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!action.show_to_observers)
|
|
return
|
|
action.GiveAction(src)
|
|
|
|
/// Signal proc for [COMSIG_MOB_REMOVED_ACTION] - If we're viewing another mob's action buttons,
|
|
/// we need to update with any removed buttons from the mob.
|
|
/mob/proc/on_observing_action_removed(mob/living/source, datum/action/action)
|
|
SIGNAL_HANDLER
|
|
|
|
action.HideFrom(src)
|
|
|
|
/atom/movable/screen/button_palette
|
|
desc = "<b>Drag</b> buttons to move them<br><b>Shift-click</b> any button to reset it<br><b>Alt-click</b> this to reset all buttons"
|
|
icon = 'icons/hud/64x16_actions.dmi'
|
|
icon_state = "screen_gen_palette"
|
|
screen_loc = ui_action_palette
|
|
var/datum/hud/our_hud
|
|
var/expanded = FALSE
|
|
/// Id of any currently running timers that set our color matrix
|
|
var/color_timer_id
|
|
|
|
/atom/movable/screen/button_palette/Destroy()
|
|
if(our_hud)
|
|
our_hud.mymob?.client?.screen -= src
|
|
our_hud.toggle_palette = null
|
|
our_hud = null
|
|
return ..()
|
|
|
|
/atom/movable/screen/button_palette/Initialize(mapload)
|
|
. = ..()
|
|
update_appearance()
|
|
|
|
/atom/movable/screen/button_palette/proc/set_hud(datum/hud/our_hud)
|
|
src.our_hud = our_hud
|
|
refresh_owner()
|
|
|
|
/atom/movable/screen/button_palette/update_name(updates)
|
|
. = ..()
|
|
if(expanded)
|
|
name = "Hide Buttons"
|
|
else
|
|
name = "Show Buttons"
|
|
|
|
/atom/movable/screen/button_palette/proc/refresh_owner()
|
|
var/mob/viewer = our_hud.mymob
|
|
if(viewer.client)
|
|
viewer.client.screen |= src
|
|
|
|
var/list/settings = our_hud.get_action_buttons_icons()
|
|
var/ui_icon = "[settings["bg_icon"]]"
|
|
var/list/ui_segments = splittext(ui_icon, ".")
|
|
var/list/ui_paths = splittext(ui_segments[1], "/")
|
|
var/ui_name = ui_paths[length(ui_paths)]
|
|
|
|
icon_state = "[ui_name]_palette"
|
|
|
|
/atom/movable/screen/button_palette/MouseEntered(location, control, params)
|
|
. = ..()
|
|
if(QDELETED(src))
|
|
return
|
|
show_tooltip(params)
|
|
|
|
/atom/movable/screen/button_palette/MouseExited()
|
|
closeToolTip(usr)
|
|
return ..()
|
|
|
|
/atom/movable/screen/button_palette/proc/show_tooltip(params)
|
|
openToolTip(usr, src, params, title = name, content = desc)
|
|
|
|
GLOBAL_LIST_INIT(palette_added_matrix, list(0.4,0.5,0.2,0, 0,1.4,0,0, 0,0.4,0.6,0, 0,0,0,1, 0,0,0,0))
|
|
GLOBAL_LIST_INIT(palette_removed_matrix, list(1.4,0,0,0, 0.7,0.4,0,0, 0.4,0,0.6,0, 0,0,0,1, 0,0,0,0))
|
|
|
|
/atom/movable/screen/button_palette/proc/play_item_added()
|
|
color_for_now(GLOB.palette_added_matrix)
|
|
|
|
/atom/movable/screen/button_palette/proc/play_item_removed()
|
|
color_for_now(GLOB.palette_removed_matrix)
|
|
|
|
/atom/movable/screen/button_palette/proc/color_for_now(list/color)
|
|
if(color_timer_id)
|
|
return
|
|
add_atom_colour(color, TEMPORARY_COLOUR_PRIORITY) //We unfortunately cannot animate matrix colors. Curse you lummy it would be ~~non~~trivial to interpolate between the two valuessssssssss
|
|
color_timer_id = addtimer(CALLBACK(src, PROC_REF(remove_color), color), 2 SECONDS)
|
|
|
|
/atom/movable/screen/button_palette/proc/remove_color(list/to_remove)
|
|
color_timer_id = null
|
|
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, to_remove)
|
|
|
|
/atom/movable/screen/button_palette/proc/can_use(mob/user)
|
|
if (isobserver(user))
|
|
var/mob/dead/observer/O = user
|
|
return !O.observetarget
|
|
return TRUE
|
|
|
|
/atom/movable/screen/button_palette/Click(location, control, params)
|
|
if(!can_use(usr))
|
|
return
|
|
|
|
var/list/modifiers = params2list(params)
|
|
|
|
if(LAZYACCESS(modifiers, ALT_CLICK))
|
|
for(var/datum/action/action as anything in usr.actions) // Reset action positions to default
|
|
for(var/datum/hud/hud as anything in action.viewers)
|
|
var/atom/movable/screen/movable/action_button/button = action.viewers[hud]
|
|
hud.position_action(button, SCRN_OBJ_DEFAULT)
|
|
to_chat(usr, span_notice("Action button positions have been reset."))
|
|
return TRUE
|
|
|
|
set_expanded(!expanded)
|
|
|
|
/atom/movable/screen/button_palette/proc/clicked_while_open(datum/source, atom/target, atom/location, control, params, mob/user)
|
|
if(istype(target, /atom/movable/screen/movable/action_button) || istype(target, /atom/movable/screen/palette_scroll) || target == src) // If you're clicking on an action button, or us, you can live
|
|
return
|
|
set_expanded(FALSE)
|
|
if(source)
|
|
UnregisterSignal(source, COMSIG_CLIENT_CLICK)
|
|
|
|
/atom/movable/screen/button_palette/proc/set_expanded(new_expanded)
|
|
var/datum/action_group/our_group = our_hud.palette_actions
|
|
if(!length(our_group.actions)) //Looks dumb, trust me lad
|
|
new_expanded = FALSE
|
|
if(expanded == new_expanded)
|
|
return
|
|
|
|
expanded = new_expanded
|
|
our_group.refresh_actions()
|
|
update_appearance()
|
|
|
|
if(!usr.client)
|
|
return
|
|
|
|
if(expanded)
|
|
RegisterSignal(usr.client, COMSIG_CLIENT_CLICK, PROC_REF(clicked_while_open))
|
|
else
|
|
UnregisterSignal(usr.client, COMSIG_CLIENT_CLICK)
|
|
|
|
closeToolTip(usr) //Our tooltips are now invalid, can't seem to update them in one frame, so here, just close them
|
|
|
|
/atom/movable/screen/palette_scroll
|
|
icon = 'icons/hud/screen_gen.dmi'
|
|
screen_loc = ui_palette_scroll
|
|
/// How should we move the palette's actions?
|
|
/// Positive scrolls down the list, negative scrolls back
|
|
var/scroll_direction = 0
|
|
var/datum/hud/our_hud
|
|
|
|
/atom/movable/screen/palette_scroll/proc/can_use(mob/user)
|
|
if (isobserver(user))
|
|
var/mob/dead/observer/O = user
|
|
return !O.observetarget
|
|
return TRUE
|
|
|
|
/atom/movable/screen/palette_scroll/proc/set_hud(datum/hud/our_hud)
|
|
src.our_hud = our_hud
|
|
refresh_owner()
|
|
|
|
/atom/movable/screen/palette_scroll/proc/refresh_owner()
|
|
var/mob/viewer = our_hud.mymob
|
|
if(viewer.client)
|
|
viewer.client.screen |= src
|
|
|
|
var/list/settings = our_hud.get_action_buttons_icons()
|
|
icon = settings["bg_icon"]
|
|
|
|
/atom/movable/screen/palette_scroll/Click(location, control, params)
|
|
if(!can_use(usr))
|
|
return
|
|
our_hud.palette_actions.scroll(scroll_direction)
|
|
|
|
/atom/movable/screen/palette_scroll/MouseEntered(location, control, params)
|
|
. = ..()
|
|
if(QDELETED(src))
|
|
return
|
|
openToolTip(usr, src, params, title = name, content = desc)
|
|
|
|
/atom/movable/screen/palette_scroll/MouseExited()
|
|
closeToolTip(usr)
|
|
return ..()
|
|
|
|
/atom/movable/screen/palette_scroll/down
|
|
name = "Scroll Down"
|
|
desc = "<b>Click</b> on this to scroll the actions above down"
|
|
icon_state = "scroll_down"
|
|
scroll_direction = 1
|
|
|
|
/atom/movable/screen/palette_scroll/down/Destroy()
|
|
if(our_hud)
|
|
our_hud.mymob?.client?.screen -= src
|
|
our_hud.palette_down = null
|
|
our_hud = null
|
|
return ..()
|
|
|
|
/atom/movable/screen/palette_scroll/up
|
|
name = "Scroll Up"
|
|
desc = "<b>Click</b> 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?.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"
|
|
desc = "<b>Drag and drop</b> a button into this spot<br>to add it to the group"
|
|
icon = 'icons/hud/screen_gen.dmi'
|
|
icon_state = "reserved"
|
|
// We want our whole 32x32 space to be clickable, so dropping's forgiving
|
|
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
|
var/datum/action_group/owner
|
|
|
|
/atom/movable/screen/action_landing/Destroy()
|
|
if(owner)
|
|
owner.landing = null
|
|
owner?.owner?.mymob?.client?.screen -= src
|
|
owner.refresh_actions()
|
|
owner = null
|
|
return ..()
|
|
|
|
/atom/movable/screen/action_landing/proc/set_owner(datum/action_group/owner)
|
|
src.owner = owner
|
|
refresh_owner()
|
|
|
|
/atom/movable/screen/action_landing/proc/refresh_owner()
|
|
var/datum/hud/our_hud = owner.owner
|
|
var/mob/viewer = our_hud.mymob
|
|
if(viewer.client)
|
|
viewer.client.screen |= src
|
|
|
|
var/list/settings = our_hud.get_action_buttons_icons()
|
|
icon = settings["bg_icon"]
|
|
|
|
/// Reacts to having a button dropped on it
|
|
/atom/movable/screen/action_landing/proc/hit_by(atom/movable/screen/movable/action_button/button)
|
|
var/datum/hud/our_hud = owner.owner
|
|
our_hud.position_action(button, owner.location)
|