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