mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
[MIRROR] The final action buttons PR (#9324)
Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
196770e16f
commit
09f82b6fff
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
These defines specificy screen locations. For more information, see the byond documentation on the screen_loc var.
|
||||
|
||||
The short version:
|
||||
|
||||
Everything is encoded as strings because apparently that's how Byond rolls.
|
||||
|
||||
"1,1" is the bottom left square of the user's screen. This aligns perfectly with the turf grid.
|
||||
"1:2,3:4" is the square (1,3) with pixel offsets (+2, +4); slightly right and slightly above the turf grid.
|
||||
Pixel offsets are used so you don't perfectly hide the turf under them, that would be crappy.
|
||||
|
||||
The size of the user's screen is defined by client.view (indirectly by world.view), in our case "15x15".
|
||||
Therefore, the top right corner (except during admin shenanigans) is at "15,15"
|
||||
*/
|
||||
|
||||
#define ui_entire_screen "WEST,SOUTH to EAST,NORTH"
|
||||
|
||||
//Lower left, persistant menu
|
||||
#define ui_inventory "WEST:6,SOUTH:5"
|
||||
|
||||
//Lower center, persistant menu
|
||||
#define ui_sstore1 "WEST+2:10,SOUTH:5"
|
||||
#define ui_id "WEST+3:12,SOUTH:5"
|
||||
#define ui_belt "WEST+4:14,SOUTH:5"
|
||||
#define ui_back "CENTER-2:14,SOUTH:5"
|
||||
#define ui_rhand "CENTER-1:16,SOUTH:5"
|
||||
#define ui_lhand "CENTER:16,SOUTH:5"
|
||||
#define ui_equip "CENTER-1:16,SOUTH+1:5"
|
||||
#define ui_swaphand1 "CENTER-1:16,SOUTH+1:5"
|
||||
#define ui_swaphand2 "CENTER:16,SOUTH+1:5"
|
||||
#define ui_storage1 "CENTER+1:16,SOUTH:5"
|
||||
#define ui_storage2 "CENTER+2:16,SOUTH:5"
|
||||
|
||||
#define ui_alien_head "CENTER-3:12,SOUTH:5" //aliens
|
||||
#define ui_alien_oclothing "CENTER-2:14,SOUTH:5"//aliens
|
||||
|
||||
#define ui_inv1 "CENTER-1,SOUTH:5" //borgs
|
||||
#define ui_inv2 "CENTER,SOUTH:5" //borgs
|
||||
#define ui_inv3 "CENTER+1,SOUTH:5" //borgs
|
||||
#define ui_borg_store "CENTER+2,SOUTH:5" //borgs
|
||||
#define ui_borg_inventory "CENTER-2,SOUTH:5"//borgs
|
||||
|
||||
#define ui_monkey_mask "WEST+4:14,SOUTH:5" //monkey
|
||||
#define ui_monkey_back "WEST+5:14,SOUTH:5" //monkey
|
||||
|
||||
#define ui_construct_health "EAST:00,CENTER:15" //same height as humans, hugging the right border
|
||||
#define ui_construct_purge "EAST:00,CENTER-1:15"
|
||||
#define ui_construct_fire "EAST-1:16,CENTER+1:13" //above health, slightly to the left
|
||||
#define ui_construct_pull "EAST-1:28,SOUTH+1:10" //above the zone_sel icon
|
||||
#define ui_pai_comms "EAST-1:28,SOUTH+1:5"
|
||||
|
||||
//Lower right, persistant menu
|
||||
#define ui_dropbutton "EAST-4:22,SOUTH:5"
|
||||
#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
|
||||
#define ui_pull_resist "EAST-2:26,SOUTH+1:7"
|
||||
#define ui_acti "EAST-2:26,SOUTH:5"
|
||||
#define ui_movi "EAST-3:24,SOUTH:5"
|
||||
#define ui_zonesel "EAST-1:28,SOUTH:5"
|
||||
#define ui_acti_alt "EAST-1:28,SOUTH:5" //alternative intent switcher for when the interface is hidden (F12)
|
||||
|
||||
#define ui_borg_pull "EAST-4:24,SOUTH+1:7" //borgs
|
||||
#define ui_borg_radio "EAST-2:26,SOUTH+1:7" //borgs
|
||||
#define ui_borg_panel "EAST-1:28,SOUTH+1:7" //borgs
|
||||
#define ui_borg_module "EAST-3:24,SOUTH+1:5"//borgs
|
||||
#define ui_vtec_control "EAST-3:24,SOUTH:5"//borgs
|
||||
|
||||
#define ui_ai_core "SOUTH:6,WEST:16"
|
||||
#define ui_ai_camera_list "SOUTH:6,WEST+1:16"
|
||||
#define ui_ai_track_with_camera "SOUTH:6,WEST+2:16"
|
||||
#define ui_ai_camera_light "SOUTH:6,WEST+3:16"
|
||||
#define ui_ai_crew_monitor "SOUTH:6,WEST+4:16"
|
||||
#define ui_ai_crew_manifest "SOUTH:6,WEST+5:16"
|
||||
#define ui_ai_alerts "SOUTH:6,WEST+6:16"
|
||||
#define ui_ai_announcement "SOUTH:6,WEST+7:16"
|
||||
#define ui_ai_shuttle "SOUTH:6,WEST+8:16"
|
||||
#define ui_ai_state_laws "SOUTH:6,WEST+9:16"
|
||||
#define ui_ai_pda_send "SOUTH:6,WEST+10:16"
|
||||
#define ui_ai_pda_log "SOUTH:6,WEST+11:16"
|
||||
#define ui_ai_take_picture "SOUTH:6,WEST+12:16"
|
||||
#define ui_ai_view_images "SOUTH:6,WEST+13:16"
|
||||
#define ui_ai_multicam "SOUTH+1:6,WEST+11:16"
|
||||
#define ui_ai_add_multicam "SOUTH+1:6,WEST+12:16"
|
||||
#define ui_ai_updown "SOUTH+1:6,WEST+13:16"
|
||||
|
||||
//Upper-middle right (alerts)
|
||||
#define ui_alert1 "EAST-1:28,CENTER+5:27"
|
||||
#define ui_alert2 "EAST-1:28,CENTER+4:25"
|
||||
#define ui_alert3 "EAST-1:28,CENTER+3:23"
|
||||
#define ui_alert4 "EAST-1:28,CENTER+2:21"
|
||||
#define ui_alert5 "EAST-1:28,CENTER+1:19"
|
||||
|
||||
//Gun buttons
|
||||
#define ui_gun1 "EAST-2:26,SOUTH+2:7"
|
||||
#define ui_gun2 "EAST-1:28, SOUTH+3:7"
|
||||
#define ui_gun3 "EAST-2:26,SOUTH+3:7"
|
||||
#define ui_gun_select "EAST-1:28,SOUTH+2:7"
|
||||
#define ui_gun4 "EAST-3:24,SOUTH+2:7"
|
||||
|
||||
//Upper-middle right (damage indicators)
|
||||
#define ui_toxin "EAST-1:28,NORTH-2:27"
|
||||
#define ui_fire "EAST-1:28,NORTH-3:25"
|
||||
#define ui_oxygen "EAST-1:28,NORTH-4:23"
|
||||
#define ui_pressure "EAST-1:28,NORTH-5:21"
|
||||
|
||||
#define ui_alien_toxin "EAST-1:28,NORTH-2:25"
|
||||
#define ui_alien_fire "EAST-1:28,NORTH-3:25"
|
||||
#define ui_alien_oxygen "EAST-1:28,NORTH-4:25"
|
||||
|
||||
// Goes above HUD, mid-right
|
||||
#define ui_ammo_hud1 "EAST-1:28,CENTER+1:25"
|
||||
#define ui_ammo_hud2 "EAST-1:28,CENTER+2:27"
|
||||
#define ui_ammo_hud3 "EAST-1:28,CENTER+3:29"
|
||||
#define ui_ammo_hud4 "EAST-1:28,CENTER+4:31"
|
||||
|
||||
//Middle right (status indicators)
|
||||
#define ui_temp "EAST-1:28,CENTER-2:13"
|
||||
#define ui_health "EAST-1:28,CENTER-1:15"
|
||||
#define ui_internal "EAST-1:28,CENTER:17"
|
||||
#define ui_under_health "EAST-1:28,CENTER-2:13"
|
||||
//borgs
|
||||
#define ui_borg_health "EAST-1:28,CENTER-2:13" //borgs have the health display where humans have the pressure damage indicator.
|
||||
#define ui_borg_under_health "EAST-1:31,CENTER-3:13"
|
||||
#define ui_alien_health "EAST-1:28,CENTER-2:13" //aliens have the health display where humans have the pressure damage indicator.
|
||||
|
||||
#define ui_ling_chemical_display "EAST-1:28,CENTER-3:15"
|
||||
#define ui_wiz_energy_display "EAST-1:28,CENTER-3:15"
|
||||
//#define ui_wiz_instability_display "EAST-2:28,CENTER-3:15"
|
||||
#define ui_wiz_instability_display "EAST-1:28,NORTH-2:27"
|
||||
|
||||
//Pop-up inventory
|
||||
#define ui_shoes "WEST+1:8,SOUTH:5"
|
||||
|
||||
#define ui_iclothing "WEST:6,SOUTH+1:7"
|
||||
#define ui_oclothing "WEST+1:8,SOUTH+1:7"
|
||||
#define ui_gloves "WEST+2:10,SOUTH+1:7"
|
||||
|
||||
#define ui_glasses "WEST:6,SOUTH+2:9"
|
||||
#define ui_mask "WEST+1:8,SOUTH+2:9"
|
||||
#define ui_l_ear "WEST+2:10,SOUTH+2:9"
|
||||
#define ui_r_ear "WEST+2:10,SOUTH+3:11"
|
||||
|
||||
#define ui_head "WEST+1:8,SOUTH+3:11"
|
||||
|
||||
//Intent small buttons
|
||||
#define ui_help_small "EAST-3:8,SOUTH:1"
|
||||
#define ui_disarm_small "EAST-3:15,SOUTH:18"
|
||||
#define ui_grab_small "EAST-3:32,SOUTH:18"
|
||||
#define ui_harm_small "EAST-3:39,SOUTH:1"
|
||||
|
||||
//#define ui_swapbutton "6:-16,1:5" //Unused
|
||||
|
||||
//#define ui_headset "SOUTH,8"
|
||||
#define ui_hand "CENTER-1:14,SOUTH:5"
|
||||
#define ui_hstore1 "CENTER-2,CENTER-2"
|
||||
//#define ui_resist "EAST+1,SOUTH-1"
|
||||
#define ui_sleep "EAST+1, NORTH-13"
|
||||
#define ui_rest "EAST+1, NORTH-14"
|
||||
|
||||
|
||||
#define ui_iarrowleft "SOUTH-1,EAST-4"
|
||||
#define ui_iarrowright "SOUTH-1,EAST-2"
|
||||
|
||||
#define ui_spell_master "EAST-2:16,NORTH-1:26"
|
||||
#define ui_genetic_master "EAST-1:16,NORTH-3:16"
|
||||
|
||||
// Ghost ones
|
||||
// CHOMPedit
|
||||
#define ui_ghost_returntomenu "SOUTH:6,CENTER-4:24"
|
||||
// CHOMPedit
|
||||
#define ui_ghost_vr "SOUTH: 6,CENTER-3:24"
|
||||
#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:24"
|
||||
#define ui_ghost_orbit "SOUTH:6,CENTER-1:24"
|
||||
#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24"
|
||||
#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
|
||||
#define ui_ghost_pai "SOUTH: 6,CENTER+2:24"
|
||||
#define ui_ghost_updown "SOUTH: 6,CENTER+3:24"
|
||||
|
||||
// NIF Soulcatcher guest ones
|
||||
#define ui_nifsc_reenter "SOUTH:6,CENTER-3:24"
|
||||
#define ui_nifsc_arproj "SOUTH:6,CENTER-2:24"
|
||||
#define ui_nifsc_jumptoowner "SOUTH:6,CENTER-1:24"
|
||||
#define ui_nifsc_nme "SOUTH:6,CENTER:24"
|
||||
#define ui_nifsc_nsay "SOUTH:6,CENTER+1:24"
|
||||
|
||||
// Rig panel
|
||||
#define ui_rig_deco1 "WEST:-7, SOUTH+5"
|
||||
#define ui_rig_deco2 "WEST:-7, SOUTH+6"
|
||||
#define ui_rig_pwr "WEST+1:-7, SOUTH+6"
|
||||
#define ui_rig_health "WEST+1:-7, SOUTH+6"
|
||||
#define ui_rig_air "WEST+1:-7, SOUTH+5"
|
||||
#define ui_rig_airtoggle "WEST+1:-7, SOUTH+5"
|
||||
#define ui_rig_deco1_f "WEST+2:-7, SOUTH+5"
|
||||
#define ui_rig_deco2_f "WEST+2:-7, SOUTH+6"
|
||||
|
||||
// Mech panel
|
||||
#define ui_mech_deco1 "WEST:-7, SOUTH+8"
|
||||
#define ui_mech_deco2 "WEST:-7, SOUTH+9"
|
||||
#define ui_mech_pwr "WEST+1:-7, SOUTH+9"
|
||||
#define ui_mech_health "WEST+1:-7, SOUTH+9"
|
||||
#define ui_mech_air "WEST+1:-7, SOUTH+8"
|
||||
#define ui_mech_airtoggle "WEST+1:-7, SOUTH+8"
|
||||
#define ui_mech_deco1_f "WEST+2:-7, SOUTH+8"
|
||||
#define ui_mech_deco2_f "WEST+2:-7, SOUTH+9"
|
||||
|
||||
#define ui_smallquad "EAST-4:22,SOUTH:5"
|
||||
@@ -1,3 +0,0 @@
|
||||
#define ui_shadekin_display "EAST-1:28,CENTER-3:15"
|
||||
#define ui_lleill_display "EAST-1:28,CENTER-3:15"
|
||||
#define ui_xenochimera_danger_display "EAST-1:28,CENTER-3:15"
|
||||
@@ -1,96 +1,130 @@
|
||||
/**
|
||||
* # Action system
|
||||
*
|
||||
* A simple base for an modular behavior attached to atom or datum.
|
||||
*/
|
||||
/datum/action
|
||||
/// The name of the action
|
||||
var/name = "Generic Action"
|
||||
var/desc = null
|
||||
|
||||
var/atom/movable/target = null
|
||||
|
||||
/// The description of what the action does, shown in button tooltips
|
||||
var/desc
|
||||
/// The target the action is attached to. If the target datum is deleted, the action is as well.
|
||||
/// Set in New() via the proc link_to(). PLEASE set a target if you're making an action
|
||||
var/datum/target
|
||||
/// Where any buttons we create should be by default. Accepts screen_loc and location defines
|
||||
var/default_button_position = SCRN_OBJ_IN_LIST
|
||||
/// This is who currently owns the action, and most often, this is who is using the action if it is triggered
|
||||
/// This can be the same as "target" but is not ALWAYS the same - this is set and unset with Grant() and Remove()
|
||||
var/mob/owner
|
||||
/// Flags that will determine of the owner / user of the action can... use the action
|
||||
var/check_flags = NONE
|
||||
var/processing = FALSE
|
||||
|
||||
var/obj/screen/movable/action_button/button = null
|
||||
|
||||
var/button_icon = 'icons/mob/actions.dmi'
|
||||
var/background_icon_state = "bg_default"
|
||||
var/buttontooltipstyle = ""
|
||||
/// Whether the button becomes transparent when it can't be used or just reddened
|
||||
var/transparent_when_unavailable = TRUE
|
||||
/// List of all mobs that are viewing our action button -> A unique movable for them to view.
|
||||
var/list/viewers = list()
|
||||
/// If TRUE, this action button will be shown to observers / other mobs who view from this action's owner's eyes.
|
||||
/// Used in [/mob/proc/show_other_mob_action_buttons]
|
||||
/// (Not really, this behavior is unimplemented)
|
||||
var/show_to_observers = TRUE
|
||||
|
||||
var/icon_icon = 'icons/mob/actions.dmi'
|
||||
/// The style the button's tooltips appear to be
|
||||
var/buttontooltipstyle = ""
|
||||
|
||||
/// This is the file for the BACKGROUND underlay icon of the button
|
||||
var/background_icon = 'icons/mob/actions/backgrounds.dmi'
|
||||
/// This is the icon state state for the BACKGROUND underlay icon of the button
|
||||
/// (If set to ACTION_BUTTON_DEFAULT_BACKGROUND, uses the hud's default background)
|
||||
var/background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND
|
||||
|
||||
/// This is the file for the icon that appears on the button
|
||||
var/button_icon = 'icons/mob/actions.dmi'
|
||||
/// This is the icon state for the icon that appears on the button
|
||||
var/button_icon_state = "default"
|
||||
|
||||
var/mob/owner
|
||||
/// This is the file for any FOREGROUND overlay icons on the button (such as borders)
|
||||
var/overlay_icon = 'icons/mob/actions/backgrounds.dmi'
|
||||
/// This is the icon state for any FOREGROUND overlay icons on the button (such as borders)
|
||||
var/overlay_icon_state
|
||||
|
||||
/datum/action/New(Target)
|
||||
link_to(Target)
|
||||
button = new
|
||||
button.linked_action = src
|
||||
button.name = name
|
||||
button.actiontooltipstyle = buttontooltipstyle
|
||||
if(desc)
|
||||
button.desc = desc
|
||||
|
||||
/// Links the passed target to our action, registering any relevant signals
|
||||
/datum/action/proc/link_to(Target)
|
||||
target = Target
|
||||
RegisterSignal(Target, COMSIG_ATOM_UPDATED_ICON, .proc/OnUpdatedIcon)
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clear_ref), override = TRUE)
|
||||
|
||||
if(isatom(target))
|
||||
RegisterSignal(target, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_target_icon_update))
|
||||
|
||||
// if(istype(target, /datum/mind))
|
||||
// RegisterSignal(target, COMSIG_MIND_TRANSFERRED, .proc/on_target_mind_swapped)
|
||||
|
||||
/datum/action/Destroy()
|
||||
if(owner)
|
||||
Remove(owner)
|
||||
target = null
|
||||
QDEL_NULL(button)
|
||||
QDEL_LIST_ASSOC_VAL(viewers)
|
||||
return ..()
|
||||
|
||||
/datum/action/proc/Grant(mob/M)
|
||||
if(M)
|
||||
if(owner)
|
||||
if(owner == M)
|
||||
return
|
||||
Remove(owner)
|
||||
owner = M
|
||||
|
||||
/// Signal proc that clears any references based on the owner or target deleting
|
||||
/// If the owner's deleted, we will simply remove from them, but if the target's deleted, we will self-delete
|
||||
/datum/action/proc/clear_ref(datum/ref)
|
||||
SIGNAL_HANDLER
|
||||
if(ref == owner)
|
||||
Remove(owner)
|
||||
if(ref == target)
|
||||
qdel(src)
|
||||
|
||||
// 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
|
||||
/// Grants the action to the passed mob, making it the owner
|
||||
/datum/action/proc/Grant(mob/grant_to)
|
||||
if(!grant_to)
|
||||
Remove(owner)
|
||||
return
|
||||
if(owner)
|
||||
if(owner == grant_to)
|
||||
return
|
||||
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
|
||||
SEND_SIGNAL(src, COMSIG_ACTION_GRANTED, grant_to)
|
||||
SEND_SIGNAL(grant_to, COMSIG_MOB_GRANTED_ACTION, src)
|
||||
owner = grant_to
|
||||
RegisterSignal(owner, COMSIG_PARENT_QDELETING, PROC_REF(clear_ref), override = TRUE)
|
||||
|
||||
/datum/action/proc/Trigger()
|
||||
GiveAction(grant_to)
|
||||
|
||||
/// Remove the passed mob from being owner of our action
|
||||
/datum/action/proc/Remove(mob/remove_from)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
for(var/datum/hud/hud in viewers)
|
||||
if(!hud.mymob)
|
||||
continue
|
||||
HideFrom(hud.mymob)
|
||||
LAZYREMOVE(remove_from.actions, src) // We aren't always properly inserted into the viewers list, gotta make sure that action's cleared
|
||||
viewers = list()
|
||||
|
||||
if(owner)
|
||||
SEND_SIGNAL(src, COMSIG_ACTION_REMOVED, owner)
|
||||
SEND_SIGNAL(owner, COMSIG_MOB_REMOVED_ACTION, src)
|
||||
|
||||
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
|
||||
if(target == owner)
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(clear_ref))
|
||||
|
||||
owner = null
|
||||
|
||||
/// Actually triggers the effects of the action.
|
||||
/// Called when the on-screen button is clicked, for example.
|
||||
/datum/action/proc/Trigger(trigger_flags)
|
||||
if(!IsAvailable())
|
||||
return FALSE
|
||||
if(SEND_SIGNAL(src, COMSIG_ACTION_TRIGGER, src) & COMPONENT_ACTION_BLOCK_TRIGGER)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/// Whether our action is currently available to use or not
|
||||
/datum/action/proc/IsAvailable()
|
||||
if(!owner)
|
||||
return FALSE
|
||||
@@ -108,147 +142,207 @@
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE)
|
||||
/// Builds / updates all buttons we have shared or given out
|
||||
/datum/action/proc/build_all_button_icons(update_flags = ALL, force)
|
||||
for(var/datum/hud/hud as anything in viewers)
|
||||
build_button_icon(viewers[hud], update_flags, force)
|
||||
|
||||
/**
|
||||
* Builds the icon of the button.
|
||||
*
|
||||
* Concept:
|
||||
* - Underlay (Background icon)
|
||||
* - Icon (button icon)
|
||||
* - Maptext
|
||||
* - Overlay (Background border)
|
||||
*
|
||||
* button - which button we are modifying the icon of
|
||||
* force - whether we're forcing a full update
|
||||
*/
|
||||
/datum/action/proc/build_button_icon(obj/screen/movable/action_button/button, update_flags = ALL, force = FALSE)
|
||||
if(!button)
|
||||
return
|
||||
|
||||
if(update_flags & UPDATE_BUTTON_NAME)
|
||||
update_button_name(button, force)
|
||||
|
||||
if(update_flags & UPDATE_BUTTON_BACKGROUND)
|
||||
apply_button_background(button, force)
|
||||
|
||||
if(update_flags & UPDATE_BUTTON_ICON)
|
||||
apply_button_icon(button, force)
|
||||
|
||||
if(update_flags & UPDATE_BUTTON_OVERLAY)
|
||||
apply_button_overlay(button, force)
|
||||
|
||||
if(update_flags & UPDATE_BUTTON_STATUS)
|
||||
update_button_status(button, force)
|
||||
|
||||
/**
|
||||
* Updates the name and description of the button to match our action name and discription.
|
||||
*
|
||||
* current_button - what button are we editing?
|
||||
* force - whether an update is forced regardless of existing status
|
||||
*/
|
||||
/datum/action/proc/update_button_name(obj/screen/movable/action_button/button, force = FALSE)
|
||||
button.name = name
|
||||
if(desc)
|
||||
button.desc = desc
|
||||
|
||||
/**
|
||||
* Creates the background underlay for the button
|
||||
*
|
||||
* current_button - what button are we editing?
|
||||
* force - whether an update is forced regardless of existing status
|
||||
*/
|
||||
/datum/action/proc/apply_button_background(obj/screen/movable/action_button/current_button, force = FALSE)
|
||||
if(!background_icon || !background_icon_state || (current_button.active_underlay_icon_state == background_icon_state && !force))
|
||||
return
|
||||
|
||||
// What icons we use for our background
|
||||
var/list/icon_settings = list(
|
||||
// The icon file
|
||||
"bg_icon" = background_icon,
|
||||
// The icon state, if is_action_active() returns FALSE
|
||||
"bg_state" = background_icon_state,
|
||||
// The icon state, if is_action_active() returns TRUE
|
||||
"bg_state_active" = background_icon_state,
|
||||
)
|
||||
|
||||
// If background_icon_state is ACTION_BUTTON_DEFAULT_BACKGROUND instead use our hud's action button scheme
|
||||
if(background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND && owner?.hud_used)
|
||||
icon_settings = owner.hud_used.get_action_buttons_icons()
|
||||
|
||||
// Determine which icon to use
|
||||
var/used_icon_key = is_action_active(current_button) ? "bg_state_active" : "bg_state"
|
||||
|
||||
// Make the underlay
|
||||
current_button.underlays.Cut()
|
||||
current_button.underlays += image(icon = icon_settings["bg_icon"], icon_state = icon_settings[used_icon_key])
|
||||
current_button.active_underlay_icon_state = icon_settings[used_icon_key]
|
||||
|
||||
/**
|
||||
* Applies our button icon and icon state to the button
|
||||
*
|
||||
* current_button - what button are we editing?
|
||||
* force - whether an update is forced regardless of existing status
|
||||
*/
|
||||
/datum/action/proc/apply_button_icon(obj/screen/movable/action_button/current_button, force = FALSE)
|
||||
if(!button_icon || !button_icon_state || (current_button.icon_state == button_icon_state && !force))
|
||||
return
|
||||
|
||||
current_button.icon = button_icon
|
||||
current_button.icon_state = button_icon_state
|
||||
|
||||
/**
|
||||
* Applies any overlays to our button
|
||||
*
|
||||
* current_button - what button are we editing?
|
||||
* force - whether an update is forced regardless of existing status
|
||||
*/
|
||||
/datum/action/proc/apply_button_overlay(obj/screen/movable/action_button/current_button, force = FALSE)
|
||||
SEND_SIGNAL(src, COMSIG_ACTION_OVERLAY_APPLY, current_button, force)
|
||||
|
||||
if(!overlay_icon || !overlay_icon_state || (current_button.active_overlay_icon_state == overlay_icon_state && !force))
|
||||
return
|
||||
|
||||
current_button.cut_overlay(current_button.button_overlay)
|
||||
current_button.button_overlay = mutable_appearance(icon = overlay_icon, icon_state = overlay_icon_state)
|
||||
current_button.add_overlay(current_button.button_overlay)
|
||||
current_button.active_overlay_icon_state = overlay_icon_state
|
||||
|
||||
/**
|
||||
* Any other miscellaneous "status" updates within the action button is handled here,
|
||||
* such as redding out when unavailable or modifying maptext.
|
||||
*
|
||||
* current_button - what button are we editing?
|
||||
* force - whether an update is forced regardless of existing status
|
||||
*/
|
||||
/datum/action/proc/update_button_status(obj/screen/movable/action_button/current_button, force = FALSE)
|
||||
if(IsAvailable())
|
||||
current_button.color = rgb(255,255,255,255)
|
||||
else
|
||||
current_button.color = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0)
|
||||
|
||||
/// Gives our action to the passed viewer.
|
||||
/// Puts our action in their actions list and shows them the button.
|
||||
/datum/action/proc/GiveAction(mob/viewer)
|
||||
var/datum/hud/our_hud = viewer.hud_used
|
||||
if(viewers[our_hud]) // Already have a copy of us? go away
|
||||
return
|
||||
|
||||
LAZYOR(viewer.actions, src) // Move this in
|
||||
ShowTo(viewer)
|
||||
|
||||
/// Adds our action button to the screen of the passed viewer.
|
||||
/datum/action/proc/ShowTo(mob/viewer)
|
||||
var/datum/hud/our_hud = viewer.hud_used
|
||||
if(!our_hud || viewers[our_hud]) // There's no point in this if you have no hud in the first place
|
||||
return
|
||||
|
||||
var/obj/screen/movable/action_button/button = create_button()
|
||||
SetId(button, viewer)
|
||||
|
||||
button.our_hud = our_hud
|
||||
viewers[our_hud] = button
|
||||
if(viewer.client)
|
||||
viewer.client.screen += button
|
||||
|
||||
button.load_position(viewer)
|
||||
viewer.update_action_buttons()
|
||||
|
||||
/// Removes our action from the passed viewer.
|
||||
/datum/action/proc/HideFrom(mob/viewer)
|
||||
var/datum/hud/our_hud = viewer.hud_used
|
||||
var/obj/screen/movable/action_button/button = viewers[our_hud]
|
||||
LAZYREMOVE(viewer.actions, src)
|
||||
if(button)
|
||||
if(!status_only)
|
||||
button.name = name
|
||||
button.desc = desc
|
||||
qdel(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
|
||||
/// Creates an action button movable for the passed mob, and returns it.
|
||||
/datum/action/proc/create_button()
|
||||
var/obj/screen/movable/action_button/button = new()
|
||||
button.linked_action = src
|
||||
build_button_icon(button, ALL, TRUE)
|
||||
return button
|
||||
|
||||
if(button.icon != button_icon)
|
||||
button.icon = button_icon
|
||||
if(button.icon_state != background_icon_state)
|
||||
button.icon_state = background_icon_state
|
||||
/datum/action/proc/SetId(obj/screen/movable/action_button/our_button, mob/owner)
|
||||
//button id generation
|
||||
var/bitfield = 0
|
||||
for(var/datum/action/action in owner.actions)
|
||||
if(action == src) // This could be us, which is dumb
|
||||
continue
|
||||
var/obj/screen/movable/action_button/button = action.viewers[owner.hud_used]
|
||||
if(action.name == name && button.id)
|
||||
bitfield |= button.id
|
||||
|
||||
ApplyIcon(button, force)
|
||||
bitfield = ~bitfield // Flip our possible ids, so we can check if we've found a unique one
|
||||
for(var/i in 0 to 23) // We get 24 possible bitflags in dm
|
||||
var/bitflag = 1 << i // Shift us over one
|
||||
if(bitfield & bitflag)
|
||||
our_button.id = bitflag
|
||||
return
|
||||
|
||||
if(!IsAvailable())
|
||||
button.color = transparent_when_unavailable ? rgb(128, 0, 0, 128) : rgb(128, 0, 0)
|
||||
else
|
||||
button.color = rgb(255, 255, 255, 255)
|
||||
return TRUE
|
||||
|
||||
/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()
|
||||
/// Updates our buttons if our target's icon was updated
|
||||
/// Still never triggered lmao
|
||||
/datum/action/proc/on_target_icon_update(datum/source, updates, updated)
|
||||
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
|
||||
var/update_flag = NONE
|
||||
var/forced = FALSE
|
||||
if(updates & UPDATE_ICON_STATE)
|
||||
update_flag |= UPDATE_BUTTON_ICON
|
||||
forced = TRUE
|
||||
if(updates & UPDATE_OVERLAYS)
|
||||
update_flag |= UPDATE_BUTTON_OVERLAY
|
||||
forced = TRUE
|
||||
if(updates & (UPDATE_NAME|UPDATE_DESC))
|
||||
update_flag |= UPDATE_BUTTON_NAME
|
||||
// Status is not relevant, and background is not relevant. Neither will change
|
||||
|
||||
/datum/action/item_action/New(Target)
|
||||
. = ..()
|
||||
var/obj/item/I = target
|
||||
LAZYADD(I.actions, src)
|
||||
// Force the update if an icon state or overlay change was done
|
||||
build_all_button_icons(update_flag, forced)
|
||||
|
||||
/datum/action/item_action/Destroy()
|
||||
var/obj/item/I = target
|
||||
LAZYREMOVE(I.actions, src)
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/Trigger()
|
||||
if(!..())
|
||||
return 0
|
||||
if(target)
|
||||
var/obj/item/I = target
|
||||
I.ui_action_click(owner, src.type)
|
||||
return 1
|
||||
|
||||
/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 = NONE
|
||||
var/active = 0
|
||||
|
||||
/datum/action/innate/Trigger()
|
||||
if(!..())
|
||||
return 0
|
||||
if(!active)
|
||||
Activate()
|
||||
else
|
||||
Deactivate()
|
||||
return 1
|
||||
|
||||
/datum/action/innate/proc/Activate()
|
||||
return
|
||||
|
||||
/datum/action/innate/proc/Deactivate()
|
||||
return
|
||||
|
||||
//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/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)
|
||||
/// Checks if our action is actively selected. Used for selecting icons primarily.
|
||||
/datum/action/proc/is_action_active(obj/screen/movable/action_button/current_button)
|
||||
return FALSE
|
||||
|
||||
212
code/_onclick/hud/action/action_group.dm
Normal file
212
code/_onclick/hud/action/action_group.dm
Normal file
@@ -0,0 +1,212 @@
|
||||
/datum/action_group
|
||||
/// The hud we're owned by
|
||||
var/datum/hud/owner
|
||||
/// The actions we're managing
|
||||
var/list/obj/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/obj/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(obj/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(obj/screen/action)
|
||||
actions -= action
|
||||
if(!QDELING(src))
|
||||
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/obj/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(obj/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(obj/screen/action, index)
|
||||
. = ..()
|
||||
var/obj/screen/button_palette/palette = owner.toggle_palette
|
||||
palette.play_item_added()
|
||||
|
||||
/datum/action_group/palette/remove_action(obj/screen/action)
|
||||
. = ..()
|
||||
var/obj/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/obj/screen/button_palette/palette = owner.toggle_palette
|
||||
var/obj/screen/palette_scroll/scroll_down = owner.palette_down
|
||||
var/obj/screen/palette_scroll/scroll_up = owner.palette_up
|
||||
if(!palette || !scroll_down || !scroll_up)
|
||||
return
|
||||
|
||||
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/obj/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
|
||||
41
code/_onclick/hud/action/action_group_helpers.dm
Normal file
41
code/_onclick/hud/action/action_group_helpers.dm
Normal file
@@ -0,0 +1,41 @@
|
||||
/// Generates visual landings for all groups that the button is not a memeber of
|
||||
/datum/hud/proc/generate_landings(obj/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/obj/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/obj/screen/movable/action_button/button = action.viewers[src]
|
||||
if(!button)
|
||||
action.ShowTo(mymob)
|
||||
button = action.viewers[src]
|
||||
position_action(button, button.location)
|
||||
73
code/_onclick/hud/action/action_item_overlay.dm
Normal file
73
code/_onclick/hud/action/action_item_overlay.dm
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Apply to an action to allow it to take an item
|
||||
* and apply it as an overlay of the action button
|
||||
*/
|
||||
/datum/component/action_item_overlay
|
||||
/// Weakref to what item the component uses to apply as an overlay.
|
||||
var/datum/weakref/item_ref
|
||||
/// Callback that dictates what item the component uses to apply as an overlay.
|
||||
var/datum/callback/item_callback
|
||||
|
||||
/// The appearance of the item we've applied
|
||||
var/mutable_appearance/item_appearance
|
||||
|
||||
/datum/component/action_item_overlay/Initialize(atom/movable/item, datum/callback/item_callback)
|
||||
if(!istype(parent, /datum/action))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
if(!item && !item_callback)
|
||||
stack_trace("[type] created without a reference item or an item callback - one or the other is required.")
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.item_ref = WEAKREF(item)
|
||||
src.item_callback = item_callback
|
||||
|
||||
/datum/component/action_item_overlay/Destroy(force, silent)
|
||||
item_ref = null
|
||||
QDEL_NULL(item_callback)
|
||||
item_appearance = null
|
||||
return ..()
|
||||
|
||||
/datum/component/action_item_overlay/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_ACTION_OVERLAY_APPLY, PROC_REF(on_overlays_applied))
|
||||
|
||||
var/datum/action/parent_action = parent
|
||||
parent_action.build_all_button_icons(UPDATE_BUTTON_OVERLAY)
|
||||
|
||||
/datum/component/action_item_overlay/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_ACTION_OVERLAY_APPLY)
|
||||
|
||||
// If we're being unregistered / deleted but our parent is sticking around,
|
||||
// force an overlay update to get rid of our item appearance
|
||||
if(!QDELING(parent))
|
||||
var/datum/action/parent_action = parent
|
||||
parent_action.build_all_button_icons(UPDATE_BUTTON_OVERLAY)
|
||||
|
||||
/// Signal proc for [COMSIG_ACTION_OVERLAY_APPLY], applies the item appearance if possible.
|
||||
/datum/component/action_item_overlay/proc/on_overlays_applied(datum/action/source, obj/screen/movable/action_button/current_button, force)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// We're in the middle of being removed / deleted, remove our associated overlay
|
||||
if(QDELING(src) && item_appearance)
|
||||
current_button.cut_overlay(item_appearance)
|
||||
item_appearance = null
|
||||
return
|
||||
|
||||
var/atom/movable/muse = item_callback?.Invoke() || item_ref?.resolve()
|
||||
if(!istype(muse))
|
||||
return
|
||||
|
||||
if(item_appearance)
|
||||
// For caching purposes, we will try not to update if we don't need to
|
||||
if(!force && item_appearance.icon == muse.icon && item_appearance.icon_state == muse.icon_state)
|
||||
return
|
||||
current_button.cut_overlay(item_appearance)
|
||||
|
||||
var/mutable_appearance/muse_appearance = new(muse.appearance)
|
||||
muse_appearance.plane = FLOAT_PLANE
|
||||
muse_appearance.layer = FLOAT_LAYER
|
||||
muse_appearance.pixel_x = 0
|
||||
muse_appearance.pixel_y = 0
|
||||
|
||||
current_button.add_overlay(muse_appearance)
|
||||
item_appearance = muse_appearance
|
||||
@@ -1,39 +1,48 @@
|
||||
|
||||
/obj/screen/movable/action_button
|
||||
var/datum/action/linked_action
|
||||
var/datum/hud/our_hud
|
||||
var/actiontooltipstyle = ""
|
||||
screen_loc = null
|
||||
icon = null // we don't use the base icon at all, just underlays and overlays
|
||||
|
||||
var/button_icon_state
|
||||
var/appearance_cache
|
||||
/// 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
|
||||
|
||||
/obj/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 ..()
|
||||
|
||||
/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
|
||||
// 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
|
||||
|
||||
/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 ..()
|
||||
if(linked_action)
|
||||
if(linked_action.viewers[user.hud_used])
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/screen/movable/action_button/Click(location,control,params)
|
||||
if(!can_use(usr))
|
||||
@@ -41,108 +50,40 @@
|
||||
|
||||
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()
|
||||
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)
|
||||
var/datum/hud/our_hud = usr.hud_used
|
||||
our_hud.position_action(src, SCRN_OBJ_DEFAULT)
|
||||
return TRUE
|
||||
if(!usr.checkClickCooldown())
|
||||
return
|
||||
usr.setClickCooldown(1)
|
||||
linked_action.Trigger()
|
||||
var/trigger_flags
|
||||
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
||||
trigger_flags |= TRIGGER_SECONDARY_ACTION
|
||||
linked_action.Trigger(trigger_flags = trigger_flags)
|
||||
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 = 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)
|
||||
// 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
|
||||
/obj/screen/movable/action_button/MouseDrag(atom/over_object, src_location, over_location, src_control, over_control, params)
|
||||
. = ..()
|
||||
if(!can_use(usr))
|
||||
return FALSE
|
||||
return
|
||||
if(IS_WEAKREF_OF(over_object, last_hovored_ref))
|
||||
return
|
||||
|
||||
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
|
||||
var/atom/old_object
|
||||
if(last_hovored_ref)
|
||||
old_object = last_hovored_ref.resolve()
|
||||
else // If there is 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)
|
||||
|
||||
usr.hud_used.action_buttons_hidden = !usr.hud_used.action_buttons_hidden
|
||||
if(old_object)
|
||||
old_object.MouseExited(over_location, over_control, params)
|
||||
|
||||
hidden = usr.hud_used.action_buttons_hidden
|
||||
if(hidden)
|
||||
name = "Show Buttons"
|
||||
else
|
||||
name = "Hide Buttons"
|
||||
update_icon()
|
||||
usr.update_action_buttons()
|
||||
|
||||
/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"
|
||||
update_icon()
|
||||
|
||||
/obj/screen/movable/action_button/hide_toggle/update_icon()
|
||||
cut_overlays()
|
||||
add_overlay(mutable_appearance(icon, hidden ? "show" : "hide"))
|
||||
// add_overlay(mutable_appearance(hide_icon, hidden ? show_state : hide_state))
|
||||
last_hovored_ref = WEAKREF(over_object)
|
||||
over_object?.MouseEntered(over_location, over_control, params)
|
||||
|
||||
/obj/screen/movable/action_button/MouseEntered(location, control, params)
|
||||
. = ..()
|
||||
@@ -151,89 +92,391 @@
|
||||
|
||||
/obj/screen/movable/action_button/MouseExited(location, control, params)
|
||||
closeToolTip(usr)
|
||||
return ..()
|
||||
|
||||
// /datum/hud/proc/get_action_buttons_icons()
|
||||
// . = list()
|
||||
// .["bg_icon"] = ui_style_icon
|
||||
// .["bg_state"] = "template"
|
||||
/obj/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, /obj/screen/action_landing))
|
||||
var/obj/screen/action_landing/reserve = over_object
|
||||
reserve.hit_by(src)
|
||||
our_hud.hide_landings()
|
||||
save_position()
|
||||
return
|
||||
|
||||
// //TODO : Make these fit theme
|
||||
// .["toggle_icon"] = 'icons/mob/actions.dmi'
|
||||
// .["toggle_hide"] = "hide"
|
||||
// .["toggle_show"] = "show"
|
||||
our_hud.hide_landings()
|
||||
if(istype(over_object, /obj/screen/button_palette) || istype(over_object, /obj/screen/palette_scroll))
|
||||
our_hud.position_action(src, SCRN_OBJ_IN_PALETTE)
|
||||
save_position()
|
||||
return
|
||||
if(istype(over_object, /obj/screen/movable/action_button))
|
||||
var/obj/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()
|
||||
|
||||
//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(status_only)
|
||||
/obj/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
|
||||
|
||||
//This is the proc used to update all the action buttons.
|
||||
/mob/proc/update_action_buttons(reload_screen)
|
||||
LAZYSET(user.client.prefs.action_button_screen_locs, "[name]_[id]", position_info)
|
||||
|
||||
/obj/screen/movable/action_button/proc/load_position()
|
||||
var/mob/user = our_hud.mymob
|
||||
if(!user)
|
||||
return
|
||||
var/position_info = LAZYACCESS(user.client?.prefs?.action_button_screen_locs, "[name]_[id]") || SCRN_OBJ_DEFAULT
|
||||
user.hud_used.position_action(src, position_info)
|
||||
|
||||
/obj/screen/movable/action_button/proc/dump_save()
|
||||
var/mob/user = our_hud.mymob
|
||||
if(!user?.client)
|
||||
return
|
||||
LAZYREMOVE(user.client.prefs.action_button_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"] = 'icons/mob/actions/backgrounds.dmi' // ui_style // TODO: Implement hud-specific icon stuff
|
||||
.["bg_state"] = "bg_default"
|
||||
.["bg_state_active"] = "bg_default_on"
|
||||
|
||||
|
||||
/**
|
||||
* 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 toggled to minimal
|
||||
return
|
||||
|
||||
var/button_number = 0
|
||||
|
||||
if(hud_used.action_buttons_hidden)
|
||||
for(var/datum/action/A in actions)
|
||||
A.button.screen_loc = null
|
||||
if(reload_screen)
|
||||
client.screen += A.button
|
||||
else
|
||||
for(var/datum/action/A in actions)
|
||||
button_number++
|
||||
A.UpdateButtonIcon()
|
||||
var/obj/screen/movable/action_button/B = A.button
|
||||
if(!B.moved)
|
||||
B.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number)
|
||||
else
|
||||
B.screen_loc = B.moved
|
||||
if(reload_screen)
|
||||
client.screen += B
|
||||
|
||||
if(!button_number)
|
||||
hud_used.hide_actions_toggle?.screen_loc = null
|
||||
return
|
||||
|
||||
// not exactly sure how this happens but it does
|
||||
if(hud_used.hide_actions_toggle)
|
||||
if(!hud_used.hide_actions_toggle.moved)
|
||||
hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1)
|
||||
else
|
||||
hud_used.hide_actions_toggle.screen_loc = hud_used.hide_actions_toggle.moved
|
||||
for(var/datum/action/action as anything in actions)
|
||||
var/obj/screen/movable/action_button/button = action.viewers[hud_used]
|
||||
action.build_all_button_icons()
|
||||
if(reload_screen)
|
||||
client.screen += hud_used.hide_actions_toggle
|
||||
client.screen += button
|
||||
|
||||
#define AB_WEST_OFFSET 4
|
||||
#define AB_NORTH_OFFSET 26
|
||||
#define AB_MAX_COLUMNS 10
|
||||
if(reload_screen)
|
||||
hud_used.update_our_owner()
|
||||
// This holds the logic for the palette buttons
|
||||
hud_used?.palette_actions?.refresh_actions()
|
||||
|
||||
/datum/hud/proc/ButtonNumberToScreenCoords(var/number) // TODO : Make this zero-indexed for readabilty
|
||||
var/row = round((number-1)/AB_MAX_COLUMNS)
|
||||
var/col = ((number - 1)%(AB_MAX_COLUMNS)) + 1
|
||||
/**
|
||||
* 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
|
||||
|
||||
var/coord_col = "+[col-1]"
|
||||
var/coord_col_offset = AB_WEST_OFFSET+2*col
|
||||
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))
|
||||
|
||||
var/coord_row = "[-1 - row]"
|
||||
var/coord_row_offset = AB_NORTH_OFFSET
|
||||
/**
|
||||
* 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))
|
||||
|
||||
return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:[coord_row_offset]"
|
||||
/// 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
|
||||
|
||||
/datum/hud/proc/SetButtonCoords(var/obj/screen/button,var/number)
|
||||
var/row = round((number-1)/AB_MAX_COLUMNS)
|
||||
var/col = ((number - 1)%(AB_MAX_COLUMNS)) + 1
|
||||
var/x_offset = 32*(col-1) + AB_WEST_OFFSET + 2*col
|
||||
var/y_offset = -32*(row+1) + AB_NORTH_OFFSET
|
||||
if(!action.show_to_observers)
|
||||
return
|
||||
action.GiveAction(src)
|
||||
|
||||
var/matrix/M = matrix()
|
||||
M.Translate(x_offset,y_offset)
|
||||
button.transform = M
|
||||
/// 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
|
||||
|
||||
#undef AB_WEST_OFFSET
|
||||
#undef AB_NORTH_OFFSET
|
||||
#undef AB_MAX_COLUMNS
|
||||
action.HideFrom(src)
|
||||
|
||||
// Button Palette
|
||||
// A new way to interact with actions
|
||||
|
||||
/obj/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
|
||||
|
||||
/obj/screen/button_palette/Destroy()
|
||||
if(our_hud)
|
||||
our_hud.mymob?.client?.screen -= src
|
||||
our_hud.toggle_palette = null
|
||||
our_hud = null
|
||||
return ..()
|
||||
|
||||
/obj/screen/button_palette/Initialize(mapload)
|
||||
. = ..()
|
||||
// update_appearance()
|
||||
update_name()
|
||||
|
||||
/obj/screen/button_palette/proc/set_hud(datum/hud/our_hud)
|
||||
src.our_hud = our_hud
|
||||
refresh_owner()
|
||||
|
||||
// /obj/screen/button_palette/update_name(updates)
|
||||
/obj/screen/button_palette/proc/update_name()
|
||||
// . = ..()
|
||||
if(expanded)
|
||||
name = "Hide Buttons"
|
||||
else
|
||||
name = "Show Buttons"
|
||||
|
||||
/obj/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"
|
||||
|
||||
/obj/screen/button_palette/MouseEntered(location, control, params)
|
||||
. = ..()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
show_tooltip(params)
|
||||
|
||||
/obj/screen/button_palette/MouseExited()
|
||||
closeToolTip(usr)
|
||||
return ..()
|
||||
|
||||
/obj/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))
|
||||
|
||||
/obj/screen/button_palette/proc/play_item_added()
|
||||
color_for_now(GLOB.palette_added_matrix)
|
||||
|
||||
/obj/screen/button_palette/proc/play_item_removed()
|
||||
color_for_now(GLOB.palette_removed_matrix)
|
||||
|
||||
/obj/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)
|
||||
|
||||
/obj/screen/button_palette/proc/remove_color(list/to_remove)
|
||||
color_timer_id = null
|
||||
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, to_remove)
|
||||
|
||||
/obj/screen/button_palette/proc/can_use(mob/user)
|
||||
if(isobserver(user))
|
||||
// var/mob/dead/observer/O = user
|
||||
// return !O.observetarget
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
/obj/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/obj/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)
|
||||
|
||||
/obj/screen/button_palette/proc/clicked_while_open(datum/source, atom/target, atom/location, control, params, mob/user)
|
||||
if(istype(target, /obj/screen/movable/action_button) || istype(target, /obj/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)
|
||||
|
||||
/obj/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()
|
||||
update_name()
|
||||
|
||||
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
|
||||
|
||||
/obj/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
|
||||
|
||||
/obj/screen/palette_scroll/proc/can_use(mob/user)
|
||||
if(isobserver(user))
|
||||
// var/mob/dead/observer/O = user
|
||||
// return !O.observetarget
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
/obj/screen/palette_scroll/proc/set_hud(datum/hud/our_hud)
|
||||
src.our_hud = our_hud
|
||||
refresh_owner()
|
||||
|
||||
/obj/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"]
|
||||
|
||||
/obj/screen/palette_scroll/Click(location, control, params)
|
||||
if(!can_use(usr))
|
||||
return
|
||||
our_hud.palette_actions.scroll(scroll_direction)
|
||||
|
||||
/obj/screen/palette_scroll/MouseEntered(location, control, params)
|
||||
. = ..()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
openToolTip(usr, src, params, title = name, content = desc)
|
||||
|
||||
/obj/screen/palette_scroll/MouseExited()
|
||||
closeToolTip(usr)
|
||||
return ..()
|
||||
|
||||
/obj/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
|
||||
|
||||
/obj/screen/palette_scroll/down/Destroy()
|
||||
if(our_hud)
|
||||
our_hud.mymob?.client?.screen -= src
|
||||
our_hud.palette_down = null
|
||||
our_hud = null
|
||||
return ..()
|
||||
|
||||
/obj/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
|
||||
|
||||
/obj/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
|
||||
/obj/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
|
||||
|
||||
/obj/screen/action_landing/Destroy()
|
||||
if(owner)
|
||||
owner.landing = null
|
||||
owner?.owner?.mymob?.client?.screen -= src
|
||||
owner.refresh_actions()
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/obj/screen/action_landing/proc/set_owner(datum/action_group/owner)
|
||||
src.owner = owner
|
||||
refresh_owner()
|
||||
|
||||
/obj/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
|
||||
/obj/screen/action_landing/proc/hit_by(obj/screen/movable/action_button/button)
|
||||
var/datum/hud/our_hud = owner.owner
|
||||
our_hud.position_action(button, owner.location)
|
||||
|
||||
20
code/_onclick/hud/action/innate_action.dm
Normal file
20
code/_onclick/hud/action/innate_action.dm
Normal file
@@ -0,0 +1,20 @@
|
||||
//Preset for general and toggled actions
|
||||
/datum/action/innate
|
||||
check_flags = NONE
|
||||
/// Whether we're active or not, if we're a innate - toggle action.
|
||||
var/active = 0
|
||||
|
||||
/datum/action/innate/Trigger(trigger_flags)
|
||||
if(!..())
|
||||
return 0
|
||||
if(!active)
|
||||
Activate()
|
||||
else
|
||||
Deactivate()
|
||||
return 1
|
||||
|
||||
/datum/action/innate/proc/Activate()
|
||||
return
|
||||
|
||||
/datum/action/innate/proc/Deactivate()
|
||||
return
|
||||
37
code/_onclick/hud/action/item_action.dm
Normal file
37
code/_onclick/hud/action/item_action.dm
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
//Presets for item actions
|
||||
/datum/action/item_action
|
||||
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
|
||||
// If you want to override the normal icon being the item
|
||||
// then change this to an icon state
|
||||
button_icon_state = null
|
||||
|
||||
/datum/action/item_action/New(Target)
|
||||
. = ..()
|
||||
|
||||
// If our button state is null, use the target's icon instead
|
||||
if(target && isnull(button_icon_state))
|
||||
AddComponent(/datum/component/action_item_overlay, target)
|
||||
|
||||
/datum/action/item_action/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(!. || !target)
|
||||
return
|
||||
|
||||
if(var_name == NAMEOF(src, button_icon_state))
|
||||
// If someone vv's our icon either add or remove the component
|
||||
if(isnull(var_name))
|
||||
AddComponent(/datum/component/action_item_overlay, target)
|
||||
else
|
||||
qdel(GetComponent(/datum/component/action_item_overlay))
|
||||
|
||||
/datum/action/item_action/Trigger(trigger_flags)
|
||||
if(!..())
|
||||
return 0
|
||||
if(target)
|
||||
var/obj/item/item_target = target
|
||||
item_target.ui_action_click(owner, src.type)
|
||||
return 1
|
||||
|
||||
/datum/action/item_action/hands_free
|
||||
check_flags = AB_CHECK_CONSCIOUS
|
||||
49
code/_onclick/hud/action/positioning.dm
Normal file
49
code/_onclick/hud/action/positioning.dm
Normal file
@@ -0,0 +1,49 @@
|
||||
/datum/hud/proc/position_action(obj/screen/movable/action_button/button, position)
|
||||
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)
|
||||
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(obj/screen/movable/action_button/button, obj/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) // 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(obj/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
|
||||
@@ -4,7 +4,6 @@
|
||||
/datum/action/item_action/activate/New(Target, name)
|
||||
. = ..()
|
||||
src.name = name
|
||||
button.name = name
|
||||
|
||||
// Specific names
|
||||
/datum/action/item_action/toggle_grippers
|
||||
|
||||
@@ -186,8 +186,15 @@ var/list/global_huds = list(
|
||||
var/list/miniobjs
|
||||
var/list/obj/screen/hotkeybuttons
|
||||
|
||||
var/obj/screen/movable/action_button/hide_toggle/hide_actions_toggle
|
||||
var/action_buttons_hidden = 0
|
||||
var/obj/screen/button_palette/toggle_palette
|
||||
var/obj/screen/palette_scroll/down/palette_down
|
||||
var/obj/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/list/slot_info
|
||||
|
||||
var/icon/ui_style
|
||||
@@ -205,9 +212,19 @@ var/list/global_huds = list(
|
||||
..()
|
||||
|
||||
/datum/hud/Destroy()
|
||||
. = ..()
|
||||
if(mymob.hud_used == src)
|
||||
mymob.hud_used = null
|
||||
|
||||
QDEL_NULL_LIST(minihuds)
|
||||
QDEL_NULL(hide_actions_toggle)
|
||||
|
||||
// Actions
|
||||
QDEL_NULL(toggle_palette)
|
||||
QDEL_NULL(palette_down)
|
||||
QDEL_NULL(palette_up)
|
||||
QDEL_NULL(palette_actions)
|
||||
QDEL_NULL(listed_actions)
|
||||
QDEL_LIST(floating_actions)
|
||||
|
||||
grab_intent = null
|
||||
hurt_intent = null
|
||||
disarm_intent = null
|
||||
@@ -232,6 +249,8 @@ var/list/global_huds = list(
|
||||
ammo_hud_list = null
|
||||
mymob = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/hud/proc/hidden_inventory_update()
|
||||
if(!mymob) return
|
||||
if(ishuman(mymob))
|
||||
@@ -323,10 +342,15 @@ var/list/global_huds = list(
|
||||
return 0
|
||||
|
||||
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
|
||||
|
||||
// Past this point, mymob.hud_used is set
|
||||
|
||||
toggle_palette = new()
|
||||
toggle_palette.set_hud(src)
|
||||
palette_down = new()
|
||||
palette_down.set_hud(src)
|
||||
palette_up = new()
|
||||
palette_up.set_hud(src)
|
||||
|
||||
persistant_inventory_update()
|
||||
mymob.reload_fullscreen() // Reload any fullscreen overlays this mob has.
|
||||
@@ -340,6 +364,11 @@ var/list/global_huds = list(
|
||||
HUD.ui_style = ui_style2icon(client?.prefs?.UI_style)
|
||||
HUD.ui_color = client?.prefs?.UI_style_color
|
||||
HUD.ui_alpha = client?.prefs?.UI_style_alpha
|
||||
set_hud_used(HUD)
|
||||
|
||||
/mob/proc/set_hud_used(datum/hud/new_hud)
|
||||
hud_used = new_hud
|
||||
new_hud.build_action_groups()
|
||||
|
||||
/datum/hud/proc/apply_minihud(var/datum/mini_hud/MH)
|
||||
if(MH in minihuds)
|
||||
@@ -409,7 +438,7 @@ var/list/global_huds = list(
|
||||
|
||||
hud_used?.action_intent.screen_loc = ui_acti //Restore intent selection to the original position
|
||||
client.screen += zone_sel //This one is a special snowflake
|
||||
client.screen += hud_used.hide_actions_toggle
|
||||
client.screen += hud_used.toggle_palette
|
||||
|
||||
hud_used.hidden_inventory_update()
|
||||
hud_used.persistant_inventory_update()
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
/obj/screen/movable
|
||||
mouse_drag_pointer = 'icons/effects/mouse_pointers/screen_drag.dmi'
|
||||
var/snap2grid = FALSE
|
||||
// TODO: Check if these can safely be deleted
|
||||
var/moved = FALSE
|
||||
var/locked = FALSE
|
||||
var/x_off = -16
|
||||
|
||||
Reference in New Issue
Block a user