diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm
index eccfb7d30a1..57f148b02b4 100644
--- a/code/__defines/flags.dm
+++ b/code/__defines/flags.dm
@@ -55,4 +55,4 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
// Flags for do_after/do_mob exclusivity.
#define TASK_TARGET_EXCLUSIVE (1<<1)
#define TASK_USER_EXCLUSIVE (1<<2)
-#define TASK_ALL_EXCLUSIVE TASK_TARGET_EXCLUSIVE | TASK_USER_EXCLUSIVE
\ No newline at end of file
+#define TASK_ALL_EXCLUSIVE TASK_TARGET_EXCLUSIVE | TASK_USER_EXCLUSIVE
diff --git a/code/__defines/span_vr.dm b/code/__defines/span_vr.dm
index 1412420fe23..c87d062b0ef 100644
--- a/code/__defines/span_vr.dm
+++ b/code/__defines/span_vr.dm
@@ -241,3 +241,5 @@
// Just used downstream
#define span_wingdings(str) ("" + str + "")
+
+#define span_maptext(str) ("" + str + "")
diff --git a/code/_onclick/hud/action/action.dm b/code/_onclick/hud/action/action.dm
index 7e0c3ece55e..d1f4441c7db 100644
--- a/code/_onclick/hud/action/action.dm
+++ b/code/_onclick/hud/action/action.dm
@@ -1,19 +1,36 @@
/datum/action
var/name = "Generic Action"
+ var/desc = null
+
var/atom/movable/target = null
- var/check_flags = 0
- var/processing = 0
+
+ var/check_flags = NONE
+ var/processing = FALSE
+
var/obj/screen/movable/action_button/button = null
+
var/button_icon = 'icons/mob/actions.dmi'
- var/button_icon_state = "default"
var/background_icon_state = "bg_default"
- var/mob/living/owner
+ var/buttontooltipstyle = ""
+ var/transparent_when_unavailable = TRUE
+
+ var/icon_icon = 'icons/mob/actions.dmi'
+ var/button_icon_state = "default"
+
+ var/mob/owner
/datum/action/New(Target)
- target = Target
+ link_to(Target)
button = new
button.linked_action = src
button.name = name
+ button.actiontooltipstyle = buttontooltipstyle
+ if(desc)
+ button.desc = desc
+
+/datum/action/proc/link_to(Target)
+ target = Target
+ RegisterSignal(Target, COMSIG_ATOM_UPDATED_ICON, .proc/OnUpdatedIcon)
/datum/action/Destroy()
if(owner)
@@ -22,84 +39,128 @@
QDEL_NULL(button)
return ..()
-/datum/action/proc/Grant(mob/living/L)
- if(owner)
- if(owner == L)
- return
- Remove(owner)
- owner = L
- L.actions += src
- if(L.client)
- L.client.screen += button
- L.update_action_buttons(TRUE)
+/datum/action/proc/Grant(mob/M)
+ if(M)
+ if(owner)
+ if(owner == M)
+ return
+ Remove(owner)
+ owner = M
-/datum/action/proc/Remove(mob/living/L)
- if(L.client)
- L.client.screen -= button
- button.moved = FALSE
- L.actions -= src
- L.update_action_buttons(TRUE)
+
+ // button id generation
+ var/counter = 0
+ var/bitfield = 0
+ for(var/datum/action/A as anything in M.actions)
+ if(A.name == name && A.button.id)
+ counter += 1
+ bitfield |= A.button.id
+ bitfield = !bitfield
+ var/bitflag = 1
+ for(var/i in 1 to (counter + 1))
+ if(bitfield & bitflag)
+ button.id = bitflag
+ break
+ bitflag *= 2
+
+ LAZYADD(M.actions, src)
+ if(M.client)
+ M.client.screen += button
+ button.locked = /* M.client.prefs.buttons_locked || */ button.id ? LAZYACCESS(M.client.prefs.action_button_screen_locs, "[name]_[button.id]") : FALSE //even if its not defaultly locked we should remember we locked it before
+ button.moved = button.id ? LAZYACCESS(M.client.prefs.action_button_screen_locs, "[name]_[button.id]") : FALSE
+ M.update_action_buttons(TRUE)
+ else
+ Remove(owner)
+
+/datum/action/proc/Remove(mob/M)
+ if(M)
+ if(M.client)
+ M.client.screen -= button
+ button.moved = FALSE
+ LAZYREMOVE(M.actions, src)
+ M.update_action_buttons(TRUE)
owner = null
+ button.moved = FALSE //so the button appears in its normal position when given to another owner.
+ button.locked = FALSE
+ button.id = null
/datum/action/proc/Trigger()
if(!IsAvailable())
- return 0
- return 1
-
-/datum/action/process()
- return
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER)
+ return FALSE
+ return TRUE
/datum/action/proc/IsAvailable()
if(!owner)
- return 0
+ return FALSE
if(check_flags & AB_CHECK_RESTRAINED)
if(owner.restrained())
- return 0
+ return FALSE
if(check_flags & AB_CHECK_STUNNED)
if(owner.stunned)
- return 0
+ return FALSE
if(check_flags & AB_CHECK_LYING)
if(owner.lying)
- return 0
+ return FALSE
if(check_flags & AB_CHECK_CONSCIOUS)
if(owner.stat)
- return 0
- return 1
+ return FALSE
+ return TRUE
-/datum/action/proc/UpdateButtonIcon()
+/datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE)
if(button)
- button.icon = button_icon
- button.icon_state = background_icon_state
+ if(!status_only)
+ button.name = name
+ button.desc = desc
- ApplyIcon(button)
+ // if(owner && owner.hud_used && background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND)
+ // var/list/settings = owner.hud_used.get_action_buttons_icons()
+ // if(button.icon != settings["bg_icon"])
+ // button.icon = settings["bg_icon"]
+ // if(button.icon_state != settings["bg_state"])
+ // button.icon_state = settings["bg_state"]
+ // else
+
+ if(button.icon != button_icon)
+ button.icon = button_icon
+ if(button.icon_state != background_icon_state)
+ button.icon_state = background_icon_state
+
+ ApplyIcon(button, force)
if(!IsAvailable())
- button.color = rgb(128, 0, 0, 128)
+ button.color = transparent_when_unavailable ? rgb(128, 0, 0, 128) : rgb(128, 0, 0)
else
button.color = rgb(255, 255, 255, 255)
- return 1
+ return TRUE
-/datum/action/proc/ApplyIcon(obj/screen/movable/action_button/current_button)
- current_button.cut_overlays()
- if(button_icon && button_icon_state)
- var/image/img
- img = image(button_icon, current_button, button_icon_state)
- img.pixel_x = 0
- img.pixel_y = 0
- current_button.add_overlay(img)
+/datum/action/proc/ApplyIcon(obj/screen/movable/action_button/current_button, force = FALSE)
+ if(icon_icon && button_icon_state && ((current_button.button_icon_state != button_icon_state) || force))
+ current_button.cut_overlays(TRUE)
+ current_button.add_overlay(mutable_appearance(icon_icon, button_icon_state))
+ current_button.button_icon_state = button_icon_state
+
+// Currently never triggered
+/datum/action/proc/OnUpdatedIcon()
+ SIGNAL_HANDLER
+ UpdateButtonIcon()
//Presets for item actions
/datum/action/item_action
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
+ button_icon_state = null
+ // If you want to override the normal icon being the item
+ // then change this to an icon state
/datum/action/item_action/New(Target)
. = ..()
var/obj/item/I = target
- I.actions += src
+ LAZYADD(I.actions, src)
/datum/action/item_action/Destroy()
var/obj/item/I = target
- I.actions -= src
+ LAZYREMOVE(I.actions, src)
return ..()
/datum/action/item_action/Trigger()
@@ -110,22 +171,28 @@
I.ui_action_click(owner, src.type)
return 1
-/datum/action/item_action/ApplyIcon(obj/screen/movable/action_button/current_button)
- current_button.cut_overlays()
- if(target)
+/datum/action/item_action/ApplyIcon(obj/screen/movable/action_button/current_button, force)
+ if(button_icon && button_icon_state)
+ // If set, use the custom icon that we set instead
+ // of the item appearence
+ return ..()
+ else if(target && ((current_button.appearance_cache != target.appearance) || force))
var/mutable_appearance/ma = new(target.appearance)
ma.plane = FLOAT_PLANE
ma.layer = FLOAT_LAYER
ma.pixel_x = 0
ma.pixel_y = 0
+
+ current_button.cut_overlays()
current_button.add_overlay(ma)
+ current_button.appearance_cache = target.appearance
/datum/action/item_action/hands_free
check_flags = AB_CHECK_CONSCIOUS
/datum/action/innate
- check_flags = 0
+ check_flags = NONE
var/active = 0
/datum/action/innate/Trigger()
@@ -143,14 +210,45 @@
/datum/action/innate/proc/Deactivate()
return
-//Preset for action that call specific procs (consider innate).
-/datum/action/generic
- check_flags = 0
- var/procname
+//Preset for an action with a cooldown
+/datum/action/cooldown
+ check_flags = NONE
+ transparent_when_unavailable = FALSE
+ var/cooldown_time = 0
+ var/next_use_time = 0
-/datum/action/generic/Trigger()
- if(!..())
- return 0
- if(target && procname)
- call(target, procname)(usr)
- return 1
+/datum/action/cooldown/New()
+ ..()
+ button.maptext = ""
+ button.maptext_x = 8
+ button.maptext_y = 0
+ button.maptext_width = 24
+ button.maptext_height = 12
+
+/datum/action/cooldown/IsAvailable()
+ return next_use_time <= world.time
+
+/datum/action/cooldown/proc/StartCooldown()
+ next_use_time = world.time + cooldown_time
+ button.maptext = span_maptext(span_bold("[round(cooldown_time/10, 0.1)]"))
+ UpdateButtonIcon()
+ START_PROCESSING(SSfastprocess, src)
+
+/datum/action/cooldown/process()
+ if(!owner)
+ button.maptext = ""
+ STOP_PROCESSING(SSfastprocess, src)
+ var/timeleft = max(next_use_time - world.time, 0)
+ if(timeleft == 0)
+ button.maptext = ""
+ UpdateButtonIcon()
+ STOP_PROCESSING(SSfastprocess, src)
+ else
+ button.maptext = span_maptext(span_bold("[round(timeleft/10, 0.1)]"))
+
+/datum/action/cooldown/Grant(mob/M)
+ ..()
+ if(owner)
+ UpdateButtonIcon()
+ if(next_use_time > world.time)
+ START_PROCESSING(SSfastprocess, src)
diff --git a/code/_onclick/hud/action/action_screen_objects.dm b/code/_onclick/hud/action/action_screen_objects.dm
index f4b10fc001d..fd64a38d571 100644
--- a/code/_onclick/hud/action/action_screen_objects.dm
+++ b/code/_onclick/hud/action/action_screen_objects.dm
@@ -1,26 +1,107 @@
/obj/screen/movable/action_button
var/datum/action/linked_action
+ var/actiontooltipstyle = ""
screen_loc = null
+ var/button_icon_state
+ var/appearance_cache
+
+ var/id
+
+/obj/screen/movable/action_button/proc/can_use(mob/user)
+ if(linked_action)
+ return linked_action.owner == user
+ else if(isobserver(user))
+ // var/mob/dead/observer/O = user
+ // return !O.observetarget
+ return TRUE
+ else
+ return TRUE
+
+/obj/screen/movable/action_button/MouseDrop(over_object)
+ if(!can_use(usr))
+ return
+ if((istype(over_object, /obj/screen/movable/action_button) && !istype(over_object, /obj/screen/movable/action_button/hide_toggle)))
+ if(locked)
+ to_chat(usr, span_warning("Action button \"[name]\" is locked, unlock it first."))
+ return
+ var/obj/screen/movable/action_button/B = over_object
+ var/list/actions = usr.actions
+ actions.Swap(actions.Find(src.linked_action), actions.Find(B.linked_action))
+ moved = FALSE
+ B.moved = FALSE
+ usr.update_action_buttons()
+ else
+ return ..()
+
/obj/screen/movable/action_button/Click(location,control,params)
+ if(!can_use(usr))
+ return
+
var/list/modifiers = params2list(params)
- if(modifiers["shift"])
+ if(LAZYACCESS(modifiers, SHIFT_CLICK))
+ if(locked)
+ to_chat(usr, span_warning("Action button \"[name]\" is locked, unlock it first."))
+ return TRUE
moved = FALSE
usr.update_action_buttons()
- return 1
+ return TRUE
+ if(LAZYACCESS(modifiers, CTRL_CLICK))
+ locked = !locked
+ to_chat(usr, span_notice("Action button \"[name]\" [locked ? "" : "un"]locked."))
+ if(id && usr?.client?.prefs) // try to (un)remember position
+ LAZYSET(usr.client.prefs.action_button_screen_locs, "[name]_[id]", locked ? moved : null)
+ return TRUE
if(!usr.checkClickCooldown())
return
+ usr.setClickCooldown(1)
linked_action.Trigger()
- return 1
+ return TRUE
+
//Hide/Show Action Buttons ... Button
/obj/screen/movable/action_button/hide_toggle
name = "Hide Buttons"
+ desc = "Shift-click any button to reset its position, and Control-click it to lock it in place. Alt-click this button reset all buttons to their default positions."
icon = 'icons/mob/actions.dmi'
icon_state = "bg_default"
- var/hidden = 0
+ var/hidden = FALSE
+
+// /obj/screen/movable/action_button/hide_toggle/Initialize()
+// . = ..()
+// var/static/list/icon_cache = list()
+
+// var/cache_key = "[hide_icon][hide_state]"
+// hide_appearance = icon_cache[cache_key]
+// if(!hide_appearance)
+// hide_appearance = icon_cache[cache_key] = mutable_appearance(hide_icon, hide_state)
+// cache_key = "[hide_icon][show_state]"
+// show_appearance = icon_cache[cache_key]
+// if(!show_appearance)
+// show_appearance = icon_cache[cache_key] = mutable_appearance(hide_icon, show_state)
+
+/obj/screen/movable/action_button/hide_toggle/Click(location, control, params)
+ if(!can_use(usr))
+ return FALSE
+
+ var/list/modifiers = params2list(params)
+ if(LAZYACCESS(modifiers, SHIFT_CLICK))
+ if(locked)
+ to_chat(usr, span_warning("Action button \"[name]\" is locked, unlock it first."))
+ return TRUE
+ moved = FALSE
+ usr.update_action_buttons(TRUE)
+ return TRUE
+ if(LAZYACCESS(modifiers, CTRL_CLICK))
+ locked = !locked
+ to_chat(usr, span_notice("Action button \"[name]\" [locked ? "" : "un"]locked."))
+ if(id && usr?.client?.prefs) // try to (un)remember position
+ LAZYSET(usr.client.prefs.action_button_screen_locs, "[name]_[id]", locked ? moved : null)
+ return TRUE
+ if(LAZYACCESS(modifiers, ALT_CLICK))
+ AltClick(usr)
+ return TRUE
-/obj/screen/movable/action_button/hide_toggle/Click()
usr.hud_used.action_buttons_hidden = !usr.hud_used.action_buttons_hidden
hidden = usr.hud_used.action_buttons_hidden
@@ -28,41 +109,67 @@
name = "Show Buttons"
else
name = "Hide Buttons"
- UpdateIcon()
+ update_icon()
usr.update_action_buttons()
-/obj/screen/movable/action_button/hide_toggle/proc/InitialiseIcon(var/mob/living/user)
+/obj/screen/movable/action_button/hide_toggle/AltClick(mob/user)
+ for(var/datum/action/A as anything in user.actions)
+ var/obj/screen/movable/action_button/B = A.button
+ B.moved = FALSE
+ if(B.id && usr?.client?.prefs)
+ LAZYSET(usr.client.prefs.action_button_screen_locs, "[name]_[B.id]", null)
+ // B.locked = usr.client.prefs.buttons_locked
+ // locked = usr.client.prefs.buttons_locked
+ if(id && usr?.client?.prefs)
+ LAZYSET(usr.client.prefs.action_button_screen_locs, "[name]_[id]", null)
+ moved = FALSE
+ user.update_action_buttons(TRUE)
+ to_chat(user, span_notice("Action button positions have been reset."))
+
+/obj/screen/movable/action_button/hide_toggle/proc/InitialiseIcon(mob/user)
+ // var/settings = owner_hud.get_action_buttons_icons()
+ // icon = settings["bg_icon"]
+ // icon_state = settings["bg_state"]
+ // hide_icon = settings["toggle_icon"]
+ // hide_state = settings["toggle_hide"]
+ // show_state = settings["toggle_show"]
if(isalien(user))
icon_state = "bg_alien"
else
icon_state = "bg_default"
- UpdateIcon()
+ update_icon()
-/obj/screen/movable/action_button/hide_toggle/proc/UpdateIcon()
+/obj/screen/movable/action_button/hide_toggle/update_icon()
cut_overlays()
- var/image/img = image(icon, src, hidden ? "show" : "hide")
- add_overlay(img)
+ add_overlay(mutable_appearance(icon, hidden ? "show" : "hide"))
+ // add_overlay(mutable_appearance(hide_icon, hidden ? show_state : hide_state))
/obj/screen/movable/action_button/MouseEntered(location, control, params)
- openToolTip(usr, src, params, title = name, content = desc)
+ . = ..()
+ if(!QDELETED(src))
+ openToolTip(usr, src, params, title = name, content = desc, theme = actiontooltipstyle)
/obj/screen/movable/action_button/MouseExited(location, control, params)
closeToolTip(usr)
-//used to update the buttons icon.
-/mob/proc/update_action_buttons_icon()
- return
+// /datum/hud/proc/get_action_buttons_icons()
+// . = list()
+// .["bg_icon"] = ui_style_icon
+// .["bg_state"] = "template"
-/mob/living/update_action_buttons_icon()
+// //TODO : Make these fit theme
+// .["toggle_icon"] = 'icons/mob/actions.dmi'
+// .["toggle_hide"] = "hide"
+// .["toggle_show"] = "show"
+
+//used to update the buttons icon.
+/mob/proc/update_action_buttons_icon(status_only = FALSE)
for(var/X in actions)
var/datum/action/A = X
- A.UpdateButtonIcon()
+ A.UpdateButtonIcon(status_only)
-//This is the proc used to update all the action buttons. Properly defined in /mob/living/
+//This is the proc used to update all the action buttons.
/mob/proc/update_action_buttons(reload_screen)
- return
-
-/mob/living/update_action_buttons(reload_screen)
if(!hud_used || !client)
return
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 7ba7bba1e1e..c2b54a001bf 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -325,6 +325,8 @@ var/list/global_huds = list(
mymob.create_mob_hud(src)
hide_actions_toggle = new()
hide_actions_toggle.InitialiseIcon(mymob)
+ // if(mymob.client)
+ // hide_actions_toggle.locked = mymob.client.prefs.buttons_locked
persistant_inventory_update()
mymob.reload_fullscreen() // Reload any fullscreen overlays this mob has.
diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm
index 3816ad885b5..daa82352e4d 100644
--- a/code/_onclick/hud/movable_screen_objects.dm
+++ b/code/_onclick/hud/movable_screen_objects.dm
@@ -12,6 +12,7 @@
mouse_drag_pointer = 'icons/effects/mouse_pointers/screen_drag.dmi'
var/snap2grid = FALSE
var/moved = FALSE
+ var/locked = FALSE
var/x_off = -16
var/y_off = -16
@@ -23,6 +24,8 @@
/obj/screen/movable/MouseDrop(over_object, src_location, over_location, src_control, over_control, params)
+ if(locked) // no! i am locked! begone!
+ return
var/position = mouse_params_to_position(params)
if(!position)
return
diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm
index 0ce8eaf5901..40568d4fd1c 100644
--- a/code/game/mecha/mecha_actions.dm
+++ b/code/game/mecha/mecha_actions.dm
@@ -64,6 +64,7 @@
/datum/action/innate/mecha
check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUNNED | AB_CHECK_CONSCIOUS
button_icon = 'icons/effects/actions_mecha.dmi'
+ icon_icon = 'icons/effects/actions_mecha.dmi'
var/obj/mecha/chassis
/datum/action/innate/mecha/Destroy()
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 1a31dfa1995..d66bb898744 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -35,8 +35,8 @@
var/max_pressure_protection // Set this variable if the item protects its wearer against high pressures below an upper bound. Keep at null to disable protection.
var/min_pressure_protection // Set this variable if the item protects its wearer against low pressures above a lower bound. Keep at null to disable protection. 0 represents protection against hard vacuum.
- var/list/actions = list() //list of /datum/action's that this item has.
- var/list/actions_types = list() //list of paths of action datums to give to the item on New().
+ var/list/actions //list of /datum/action's that this item has.
+ var/list/actions_types //list of paths of action datums to give to the item on New().
//This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc.
//It should be used purely for appearance. For gameplay effects caused by items covering body parts, use body_parts_covered.
@@ -335,8 +335,7 @@
if(zoom)
zoom() //binoculars, scope, etc
appearance_flags &= ~NO_CLIENT_COLOR
- for(var/X in actions)
- var/datum/action/A = X
+ for(var/datum/action/A as anything in actions)
A.Remove(user)
// called just as an item is picked up (loc is not yet changed)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index f28c224168b..a6fbe938813 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -173,6 +173,10 @@ var/list/preferences_datums = list()
var/examine_text_mode = 0 // Just examine text, include usage (description_info), switch to examine panel.
var/multilingual_mode = 0 // Default behaviour, delimiter-key-space, delimiter-key-delimiter, off
+ // THIS IS NOT SAVED
+ // WE JUST HAVE NOWHERE ELSE TO STORE IT
+ var/list/action_button_screen_locs
+
var/list/volume_channels = list()
///If they are currently in the process of swapping slots, don't let them open 999 windows for it and get confused
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 729119068ec..3c3e5e790ab 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -973,6 +973,7 @@
if(lying != lying_prev)
lying_prev = lying
update_transform()
+ update_action_buttons_icon(status_only = TRUE)
//VOREStation Add
if(lying && LAZYLEN(buckled_mobs))
for(var/mob/living/L as anything in buckled_mobs)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 697337a26da..db9a065e46f 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -39,7 +39,6 @@
var/mob_always_swap = 0
var/mob/living/cameraFollow = null
- var/list/datum/action/actions = list()
var/tod = null // Time of death
var/update_slimes = 1
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 050738cb953..d8db6a7394b 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -238,3 +238,5 @@
var/datum/focus //What receives our keyboard inputs. src by default
/// dict of custom stat tabs with data
var/list/list/misc_tabs = list()
+
+ var/list/datum/action/actions