Skip to content

Commit 56f4960

Browse files
refactor action buttons (ParadiseSS13#29416)
* refactor action buttons * fix cult spell charge overlay * lewc review * update for a/mhelp buttons and xenobio organs * update for minebot * properly create button each time * directly add/remove unavail overlay for reasons * lewc review
1 parent f9016e6 commit 56f4960

115 files changed

Lines changed: 817 additions & 586 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

code/__DEFINES/action_defines.dm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,12 @@
44
#define UPDATE_BUTTON_BACKGROUND (1<<2)
55
#define UPDATE_BUTTON_OVERLAY (1<<3)
66
#define UPDATE_BUTTON_STATUS (1<<4)
7+
8+
// Defines for formatting cooldown actions for the stat panel.
9+
/// The stat panel the action is displayed in.
10+
#define PANEL_DISPLAY_PANEL "panel"
11+
/// The status shown in the stat panel.
12+
/// Can be stuff like "ready", "on cooldown", "active", "charges", "charge cost", etc.
13+
#define PANEL_DISPLAY_STATUS "status"
14+
/// The name shown in the stat panel.
15+
#define PANEL_DISPLAY_NAME "name"

code/__DEFINES/dcs/datum_signals.dm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@
8585
#define COMSIG_ACTION_REMOVED "action_removed"
8686
/// From /datum/action/Remove(): (datum/action)
8787
#define COMSIG_MOB_REMOVED_ACTION "mob_action_removed"
88-
88+
/// From /datum/action/apply_button_overlay()
89+
#define COMSIG_ACTION_OVERLAY_APPLY "action_overlay_applied"
8990

9091
// /datum/objective
9192

code/_onclick/hud/action_button.dm

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
/// The HUD this action button belongs to
1111
var/datum/hud/our_hud
1212

13+
/// The overlay we have overtop our button
14+
var/mutable_appearance/button_overlay
15+
/// The icon state of our active overlay, used to prevent re-applying identical overlays
16+
var/active_overlay_icon_state
17+
/// The icon state of our active underlay, used to prevent re-applying identical underlays
18+
var/active_underlay_icon_state
19+
1320
/// Where we are currently placed on the hud. SCRN_OBJ_DEFAULT asks the linked action what it thinks
1421
var/location = SCRN_OBJ_DEFAULT
1522
/// A unique bitflag, combined with the name of our linked action this lets us persistently remember any user changes to our position
@@ -212,10 +219,9 @@
212219
closeToolTip(usr)
213220
return ..()
214221

215-
/mob/proc/update_action_buttons_icon(status_only = FALSE)
216-
for(var/X in actions)
217-
var/datum/action/A = X
218-
A.UpdateButtons(status_only)
222+
/mob/proc/update_action_buttons_icon(update_flags = ALL, force = FALSE)
223+
for(var/datum/action/current_action as anything in actions)
224+
current_action.build_all_button_icons(update_flags, force)
219225

220226
//This is the proc used to update all the action buttons.
221227
/mob/proc/update_action_buttons(reload_screen)
@@ -227,7 +233,7 @@
227233

228234
for(var/datum/action/action as anything in actions)
229235
var/atom/movable/screen/movable/action_button/button = action.viewers[hud_used]
230-
action.UpdateButtons()
236+
action.build_all_button_icons()
231237
if(reload_screen)
232238
client.screen += button
233239
client.update_active_keybindings()

code/datums/actions/action.dm

Lines changed: 177 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@
33
var/desc = null
44
var/obj/target = null
55
var/check_flags = 0
6-
/// Icon that our button screen object overlay and background
7-
var/button_overlay_icon = 'icons/mob/actions/actions.dmi'
8-
/// Icon state of screen object overlay
9-
var/button_overlay_icon_state = ACTION_BUTTON_DEFAULT_OVERLAY
10-
/// Icon that our button screen object background will have
11-
var/button_background_icon = 'icons/mob/actions/actions.dmi'
6+
7+
/// This is the icon state state for the BACKGROUND underlay icon of the button
8+
/// (If set to ACTION_BUTTON_DEFAULT_BACKGROUND, uses the hud's default background)
9+
var/background_icon = 'icons/mob/actions/actions.dmi'
1210
/// Icon state of screen object background
13-
var/button_background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND
11+
var/background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND
12+
13+
/// This is the file for the icon that appears on the button
14+
var/button_icon = 'icons/mob/actions/actions.dmi'
15+
/// This is the icon state for the icon that appears on the button
16+
var/button_icon_state = ACTION_BUTTON_DEFAULT_OVERLAY
17+
18+
/// This is the file for any FOREGROUND overlay icons on the button (such as borders)
19+
var/overlay_icon = 'icons/mob/actions/actions.dmi'
20+
/// This is the icon state for any FOREGROUND overlay icons on the button (such as borders)
21+
var/overlay_icon_state
1422
var/buttontooltipstyle = ""
1523
var/transparent_when_unavailable = TRUE
1624
var/mob/owner
@@ -20,10 +28,21 @@
2028
var/list/viewers = list()
2129
/// Whether or not this will be shown to observers
2230
var/show_to_observers = TRUE
31+
/// Toggles whether this action is usable or not
32+
var/action_disabled = FALSE
33+
/// The appearance used as an overlay for when the action is unavailable
34+
var/mutable_appearance/unavailable_effect
2335

36+
/datum/action/New(target)
37+
link_to(target)
2438

25-
/datum/action/New(Target)
26-
target = Target
39+
/// Links the passed target to our action, registering any relevant signals
40+
/datum/action/proc/link_to(target_)
41+
target = target_
42+
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clear_ref), override = TRUE)
43+
44+
if(isatom(target))
45+
RegisterSignal(target, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_target_icon_update))
2746

2847
/datum/action/proc/should_draw_cooldown()
2948
return !IsAvailable()
@@ -79,10 +98,10 @@
7998
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
8099
owner = null
81100

82-
/datum/action/proc/UpdateButtons(status_only, force)
83-
for(var/datum/hud/hud in viewers)
84-
var/atom/movable/screen/movable/button = viewers[hud]
85-
UpdateButton(button, status_only, force)
101+
/// Builds / updates all buttons we have shared or given out
102+
/datum/action/proc/build_all_button_icons(update_flags = ALL, force)
103+
for(var/datum/hud/hud as anything in viewers)
104+
build_button_icon(viewers[hud], update_flags, force)
86105

87106
/datum/action/proc/Trigger(left_click = TRUE)
88107
if(!IsAvailable())
@@ -123,31 +142,60 @@
123142
return FALSE
124143
return TRUE
125144

126-
/datum/action/proc/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force = FALSE)
145+
/**
146+
* Builds the icon of the button.
147+
*
148+
* Concept:
149+
* - Underlay (Background icon)
150+
* - Icon (button icon)
151+
* - Maptext
152+
* - Overlay (Background border)
153+
*
154+
* button - which button we are modifying the icon of
155+
* force - whether we're forcing a full update
156+
*/
157+
/datum/action/proc/build_button_icon(atom/movable/screen/movable/action_button/button, update_flags = ALL, force = FALSE)
127158
if(!button)
128159
return
129-
if(!status_only)
130-
button.name = name
131-
if(desc)
132-
button.desc = "[desc] [initial(button.desc)]"
133-
if(owner?.hud_used && button_background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND)
134-
var/list/settings = owner.hud_used.get_action_buttons_icons()
135-
if(button.icon != settings["bg_icon"])
136-
button.icon = settings["bg_icon"]
137-
if(button.icon_state != settings["bg_state"])
138-
button.icon_state = settings["bg_state"]
139-
else
140-
if(button.icon != button_background_icon)
141-
button.icon = button_background_icon
142-
if(button.icon_state != button_background_icon_state)
143-
button.icon_state = button_background_icon_state
144160

161+
if(update_flags & UPDATE_BUTTON_NAME)
162+
update_button_name(button, force)
163+
164+
if(update_flags & UPDATE_BUTTON_BACKGROUND)
165+
apply_button_background(button, force)
166+
167+
if(update_flags & UPDATE_BUTTON_ICON)
168+
apply_button_icon(button, force)
169+
170+
if(update_flags & UPDATE_BUTTON_OVERLAY)
145171
apply_button_overlay(button, force)
146172

173+
if(update_flags & UPDATE_BUTTON_STATUS)
174+
update_button_status(button, force)
175+
176+
/**
177+
* Updates the name and description of the button to match our action name and discription.
178+
*
179+
* button - what button are we editing?
180+
* force - whether an update is forced regardless of existing status
181+
*/
182+
/datum/action/proc/update_button_name(atom/movable/screen/movable/action_button/button, force = FALSE)
183+
button.name = name
184+
if(desc)
185+
button.desc = desc
186+
187+
/**
188+
* Any other miscellaneous "status" updates within the action button is handled here,
189+
* such as redding out when unavailable or modifying maptext.
190+
*
191+
* button - what button are we editing?
192+
* force - whether an update is forced regardless of existing status
193+
*/
194+
/datum/action/proc/update_button_status(atom/movable/screen/movable/action_button/button, force = FALSE)
195+
button.overlays -= unavailable_effect
196+
button.maptext = ""
147197
if(should_draw_cooldown())
148198
apply_unavailable_effect(button)
149-
else
150-
return TRUE
151199

152200
//Give our action button to the player
153201
/datum/action/proc/GiveAction(mob/viewer)
@@ -164,7 +212,7 @@
164212
return
165213

166214

167-
var/atom/movable/screen/movable/action_button/button = CreateButton()
215+
var/atom/movable/screen/movable/action_button/button = create_button()
168216
SetId(button, viewer)
169217

170218
button.our_hud = our_hud
@@ -186,18 +234,18 @@
186234
qdel(button)
187235

188236

189-
/datum/action/proc/CreateButton()
237+
/datum/action/proc/create_button()
190238
var/atom/movable/screen/movable/action_button/button = new()
191239
button.linked_action = src
192-
button.name = name
240+
build_button_icon(button, ALL, force = TRUE)
241+
// TODO: Pull all of this into the build button structure somehow later
193242
button.actiontooltipstyle = buttontooltipstyle
194243
var/list/our_description = list()
195244
our_description += desc
196245
our_description += button.desc
197246
button.desc = our_description.Join(" ")
198247
return button
199248

200-
201249
/datum/action/proc/SetId(atom/movable/screen/movable/action_button/our_button, mob/owner)
202250
//button id generation
203251
var/bitfield = 0
@@ -215,20 +263,97 @@
215263
our_button.id = bitflag
216264
return
217265

218-
/datum/action/proc/apply_unavailable_effect(atom/movable/screen/movable/action_button/B)
219-
var/image/img = image('icons/mob/screen_white.dmi', icon_state = "template")
220-
img.alpha = 200
221-
img.appearance_flags = RESET_COLOR | RESET_ALPHA
222-
img.color = "#000000"
223-
img.plane = FLOAT_PLANE + 1
224-
B.add_overlay(img)
225-
226-
227-
/datum/action/proc/apply_button_overlay(atom/movable/screen/movable/action_button/current_button)
228-
current_button.cut_overlays()
229-
if(button_overlay_icon && button_overlay_icon_state)
230-
var/image/img = image(button_overlay_icon, current_button, button_overlay_icon_state)
231-
img.appearance_flags = RESET_COLOR | RESET_ALPHA
232-
img.pixel_x = 0
233-
img.pixel_y = 0
234-
current_button.add_overlay(img)
266+
/datum/action/proc/apply_unavailable_effect(atom/movable/screen/movable/action_button/button)
267+
if(isnull(unavailable_effect))
268+
unavailable_effect = mutable_appearance('icons/mob/screen_white.dmi', icon_state = "template")
269+
unavailable_effect.alpha = 200
270+
unavailable_effect.appearance_flags = RESET_COLOR | RESET_ALPHA
271+
unavailable_effect.color = "#000000"
272+
unavailable_effect.plane = FLOAT_PLANE + 1
273+
button.overlays |= unavailable_effect
274+
275+
/**
276+
* Applies any overlays to our button
277+
*
278+
* button - what button are we editing?
279+
* force - whether an update is forced regardless of existing status
280+
*/
281+
/datum/action/proc/apply_button_overlay(atom/movable/screen/movable/action_button/button, force = FALSE)
282+
SEND_SIGNAL(src, COMSIG_ACTION_OVERLAY_APPLY, button, force)
283+
284+
if(!overlay_icon || !overlay_icon_state || (button.active_overlay_icon_state == overlay_icon_state && !force))
285+
return
286+
287+
button.cut_overlay(button.button_overlay)
288+
button.button_overlay = mutable_appearance(icon = overlay_icon, icon_state = overlay_icon_state, appearance_flags = (RESET_COLOR|RESET_ALPHA))
289+
button.add_overlay(button.button_overlay)
290+
button.active_overlay_icon_state = overlay_icon_state
291+
292+
/// Checks if our action is actively selected. Used for selecting icons primarily.
293+
/datum/action/proc/is_action_active(atom/movable/screen/movable/action_button/button)
294+
return FALSE
295+
296+
/**
297+
* Creates the background underlay for the button
298+
*
299+
* button - what button are we editing?
300+
* force - whether an update is forced regardless of existing status
301+
*/
302+
/datum/action/proc/apply_button_background(atom/movable/screen/movable/action_button/button, force = FALSE)
303+
if(!background_icon || !background_icon_state || (button.active_underlay_icon_state == background_icon_state && !force))
304+
return
305+
306+
// What icons we use for our background
307+
var/list/icon_settings = list(
308+
// The icon file
309+
"bg_icon" = background_icon,
310+
// The icon state, if is_action_active() returns FALSE
311+
"bg_state" = background_icon_state,
312+
// The icon state, if is_action_active() returns TRUE
313+
"bg_state_active" = background_icon_state,
314+
)
315+
316+
// If background_icon_state is ACTION_BUTTON_DEFAULT_BACKGROUND instead use our hud's action button scheme
317+
if(background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND && owner?.hud_used)
318+
icon_settings = owner.hud_used.get_action_buttons_icons()
319+
320+
// Determine which icon to use
321+
var/used_icon_key = is_action_active(button) ? "bg_state_active" : "bg_state"
322+
323+
// Make the underlay
324+
button.underlays.Cut()
325+
var/image/underlay = image(icon = icon_settings["bg_icon"], icon_state = icon_settings[used_icon_key])
326+
button.underlays += underlay
327+
button.active_underlay_icon_state = icon_settings[used_icon_key]
328+
329+
/**
330+
* Applies our button icon and icon state to the button
331+
*
332+
* button - what button are we editing?
333+
* force - whether an update is forced regardless of existing status
334+
*/
335+
/datum/action/proc/apply_button_icon(atom/movable/screen/movable/action_button/button, force = FALSE)
336+
if(!button_icon || !button_icon_state || (button.icon_state == button_icon_state && !force))
337+
return
338+
339+
button.icon = button_icon
340+
button.icon_state = button_icon_state
341+
342+
/// Updates our buttons if our target's icon was updated
343+
/datum/action/proc/on_target_icon_update(datum/source, updates, updated)
344+
SIGNAL_HANDLER // COMSIG_ATOM_UPDATED_ICON
345+
346+
var/update_flag = NONE
347+
var/forced = FALSE
348+
if(updates & UPDATE_ICON_STATE)
349+
update_flag |= UPDATE_BUTTON_ICON
350+
forced = TRUE
351+
if(updates & UPDATE_OVERLAYS)
352+
update_flag |= UPDATE_BUTTON_OVERLAY
353+
forced = TRUE
354+
if(updates & (UPDATE_NAME|UPDATE_DESC))
355+
update_flag |= UPDATE_BUTTON_NAME
356+
// Status is not relevant, and background is not relevant. Neither will change
357+
358+
// Force the update if an icon state or overlay change was done
359+
build_all_button_icons(update_flag, forced)

0 commit comments

Comments
 (0)