diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm
index 261c13c6673..2f8ebc83665 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm
@@ -1,8 +1,10 @@
-// Atom mouse signals. Format:
+// mouse signals. Format:
// When the signal is called: (signal arguments)
// All signals send the source datum of the signal as the first argument
-///from base of atom/Click(): (location, control, params, mob/user)
+///from base of client/Click(): (atom/target, atom/location, control, params, mob/user)
+#define COMSIG_CLIENT_CLICK "atom_client_click"
+///from base of atom/Click(): (atom/location, control, params, mob/user)
#define COMSIG_CLICK "atom_click"
///from base of atom/ShiftClick(): (/mob)
#define COMSIG_CLICK_SHIFT "shift_click"
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index 60b7c308be4..200c152630b 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -14,3 +14,202 @@
#define APPEARANCE_UI_IGNORE_ALPHA (RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA|PIXEL_SCALE)
/// Used for HUD objects
#define APPEARANCE_UI (RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|PIXEL_SCALE)
+
+/*
+ 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.
+
+ In addition, the keywords NORTH, SOUTH, EAST, WEST and CENTER can be used to represent their respective
+ screen borders. NORTH-1, for example, is the row just below the upper edge. Useful if you want your
+ UI to scale with screen size.
+
+ 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"
+*/
+
+/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5)
+ var/x_off = i % 2 ? 0 : -1
+ var/y_off = round((i-1) / 2)
+ return"CENTER+[x_off]:16,SOUTH+[y_off]:5"
+
+/proc/ui_equip_position(mob/M)
+ var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5)
+ return "CENTER:-16,SOUTH+[y_off+1]:5"
+
+/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5)
+ var/x_off = which == 1 ? -1 : 0
+ var/y_off = round((M.held_items.len-1) / 2)
+ return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5"
+
+//Lower left, persistent menu
+#define ui_inventory "WEST:6,SOUTH:5"
+
+//Middle left indicators
+#define ui_lingchemdisplay "WEST,CENTER-1:15"
+#define ui_lingstingdisplay "WEST:6,CENTER-3:11"
+
+//Lower center, persistent menu
+#define ui_sstore1 "CENTER-5:10,SOUTH:5"
+#define ui_id "CENTER-4:12,SOUTH:5"
+#define ui_belt "CENTER-3:14,SOUTH:5"
+#define ui_back "CENTER-2:14,SOUTH:5"
+#define ui_storage1 "CENTER+1:18,SOUTH:5"
+#define ui_storage2 "CENTER+2:20,SOUTH:5"
+#define ui_combo "CENTER+4:24,SOUTH+1:7" //combo meter for martial arts
+
+//Lower right, persistent menu
+#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
+#define ui_above_movement "EAST-2:26,SOUTH+1:7"
+#define ui_above_intent "EAST-3:24, SOUTH+1:7"
+#define ui_movi "EAST-2:26,SOUTH:5"
+#define ui_acti "EAST-3:24,SOUTH:5"
+#define ui_combat_toggle "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_crafting "EAST-4:22,SOUTH:5"
+#define ui_building "EAST-4:22,SOUTH:21"
+#define ui_language_menu "EAST-4:6,SOUTH:21"
+#define ui_skill_menu "EAST-4:22,SOUTH:5"
+
+//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"
+
+//Upper left (action buttons)
+#define ui_action_palette "WEST+0:23,NORTH-1:5"
+#define ui_action_palette_offset(north_offset) ("WEST+0:23,NORTH-[1+north_offset]:5")
+
+#define ui_palette_scroll "WEST+1:8,NORTH-6:28"
+#define ui_palette_scroll_offset(north_offset) ("WEST+1:8,NORTH-[6+north_offset]:28")
+
+//Middle right (status indicators)
+#define ui_healthdoll "EAST-1:28,CENTER-2:13"
+#define ui_health "EAST-1:28,CENTER-1:15"
+#define ui_internal "EAST-1:28,CENTER+1:17"
+#define ui_mood "EAST-1:28,CENTER:17"
+#define ui_spacesuit "EAST-1:28,CENTER-4:10"
+#define ui_stamina "EAST-1:28,CENTER-3:10"
+
+//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+3:11"
+#define ui_mask "WEST+1:8,SOUTH+2:9"
+#define ui_ears "WEST+2:10,SOUTH+2:9"
+#define ui_neck "WEST:6,SOUTH+2:9"
+#define ui_head "WEST+1:8,SOUTH+3:11"
+
+//Generic living
+#define ui_living_pull "EAST-1:28,CENTER-3:15"
+#define ui_living_healthdoll "EAST-1:28,CENTER-1:15"
+
+//Monkeys
+#define ui_monkey_head "CENTER-5:13,SOUTH:5"
+#define ui_monkey_mask "CENTER-4:14,SOUTH:5"
+#define ui_monkey_neck "CENTER-3:15,SOUTH:5"
+#define ui_monkey_back "CENTER-2:16,SOUTH:5"
+
+//Drones
+#define ui_drone_drop "CENTER+1:18,SOUTH:5"
+#define ui_drone_pull "CENTER+2:2,SOUTH:5"
+#define ui_drone_storage "CENTER-2:14,SOUTH:5"
+#define ui_drone_head "CENTER-3:14,SOUTH:5"
+
+//Cyborgs
+#define ui_borg_health "EAST-1:28,CENTER-1:15"
+#define ui_borg_pull "EAST-2:26,SOUTH+1:7"
+#define ui_borg_radio "EAST-1:28,SOUTH+1:7"
+#define ui_borg_intents "EAST-2:26,SOUTH:5"
+#define ui_borg_lamp "CENTER-3:16, SOUTH:5"
+#define ui_borg_tablet "CENTER-4:16, SOUTH:5"
+#define ui_inv1 "CENTER-2:16,SOUTH:5"
+#define ui_inv2 "CENTER-1 :16,SOUTH:5"
+#define ui_inv3 "CENTER :16,SOUTH:5"
+#define ui_borg_module "CENTER+1:16,SOUTH:5"
+#define ui_borg_store "CENTER+2:16,SOUTH:5"
+#define ui_borg_camera "CENTER+3:21,SOUTH:5"
+#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
+#define ui_borg_language_menu "CENTER+4:19,SOUTH+1:6"
+
+//Aliens
+#define ui_alien_health "EAST,CENTER-1:15"
+#define ui_alienplasmadisplay "EAST,CENTER-2:15"
+#define ui_alien_queen_finder "EAST,CENTER-3:15"
+#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"
+#define ui_alien_language_menu "EAST-4:20,SOUTH:5"
+
+//AI
+#define ui_ai_core "SOUTH:6,WEST"
+#define ui_ai_camera_list "SOUTH:6,WEST+1"
+#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
+#define ui_ai_camera_light "SOUTH:6,WEST+3"
+#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
+#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
+#define ui_ai_alerts "SOUTH:6,WEST+6"
+#define ui_ai_announcement "SOUTH:6,WEST+7"
+#define ui_ai_shuttle "SOUTH:6,WEST+8"
+#define ui_ai_state_laws "SOUTH:6,WEST+9"
+#define ui_ai_pda_send "SOUTH:6,WEST+10"
+#define ui_ai_pda_log "SOUTH:6,WEST+11"
+#define ui_ai_take_picture "SOUTH:6,WEST+12"
+#define ui_ai_view_images "SOUTH:6,WEST+13"
+#define ui_ai_sensor "SOUTH:6,WEST+14"
+#define ui_ai_multicam "SOUTH+1:6,WEST+13"
+#define ui_ai_add_multicam "SOUTH+1:6,WEST+14"
+#define ui_ai_language_menu "SOUTH+1:8,WEST+11:30"
+
+//pAI
+#define ui_pai_software "SOUTH:6,WEST"
+#define ui_pai_shell "SOUTH:6,WEST+1"
+#define ui_pai_chassis "SOUTH:6,WEST+2"
+#define ui_pai_rest "SOUTH:6,WEST+3"
+#define ui_pai_light "SOUTH:6,WEST+4"
+#define ui_pai_newscaster "SOUTH:6,WEST+5"
+#define ui_pai_host_monitor "SOUTH:6,WEST+6"
+#define ui_pai_crew_manifest "SOUTH:6,WEST+7"
+#define ui_pai_state_laws "SOUTH:6,WEST+8"
+#define ui_pai_pda_send "SOUTH:6,WEST+9"
+#define ui_pai_pda_log "SOUTH:6,WEST+10"
+#define ui_pai_internal_gps "SOUTH:6,WEST+11"
+#define ui_pai_take_picture "SOUTH:6,WEST+12"
+#define ui_pai_view_images "SOUTH:6,WEST+13"
+#define ui_pai_radio "SOUTH:6,WEST+14"
+#define ui_pai_language_menu "SOUTH+1:8,WEST+13:31"
+
+//Ghosts
+#define ui_ghost_spawners_menu "SOUTH:6,CENTER-3:24"
+#define ui_ghost_orbit "SOUTH:6,CENTER-2:24"
+#define ui_ghost_reenter_corpse "SOUTH:6,CENTER-1:24"
+#define ui_ghost_teleport "SOUTH:6,CENTER:24"
+#define ui_ghost_pai "SOUTH: 6, CENTER+1:24"
+#define ui_ghost_minigames "SOUTH: 6, CENTER+2:24"
+#define ui_ghost_language_menu "SOUTH: 22, CENTER+3:8"
+
+//Blobbernauts
+#define ui_blobbernaut_overmind_health "EAST-1:28,CENTER+0:19"
+
+//Families
+#define ui_wanted_lvl "NORTH,11"
+
+// Defines relating to action button positions
+
+/// Whatever the base action datum thinks is best
+#define SCRN_OBJ_DEFAULT "default"
+/// Floating somewhere on the hud, not in any predefined place
+#define SCRN_OBJ_FLOATING "floating"
+/// In the list of buttons stored at the top of the screen
+#define SCRN_OBJ_IN_LIST "list"
+/// In the collapseable palette
+#define SCRN_OBJ_IN_PALETTE "palette"
diff --git a/code/__DEFINES/~skyrat_defines/hud.dm b/code/__DEFINES/~skyrat_defines/hud.dm
new file mode 100644
index 00000000000..abc82a6d8bc
--- /dev/null
+++ b/code/__DEFINES/~skyrat_defines/hud.dm
@@ -0,0 +1,18 @@
+// ERP stuff
+#define ui_vagina "WEST+1:8,SOUTH+4:14"
+#define ui_vagina_down "WEST+1:8,SOUTH+1:8"
+#define ui_anus "WEST+2:10,SOUTH+4:14"
+#define ui_anus_down "WEST+2:10,SOUTH+1:8"
+#define ui_nipples "WEST:6,SOUTH+5:17"
+#define ui_nipples_down "WEST:6,SOUTH+2:11"
+#define ui_penis "WEST+1:8,SOUTH+5:17"
+#define ui_penis_down "WEST+1:8,SOUTH+2:11"
+#define ui_erp_inventory "WEST:6,SOUTH+1:8"
+#define ui_erp_inventory_up "WEST:6,SOUTH+4:14"
+
+// Cyborg PDA
+#define ui_borg_pda_send "CENTER+5:21,SOUTH:5" // To the right of the alert panel
+#define ui_borg_pda_log "CENTER+6:21,SOUTH:5"
+
+// Ammo counter
+#define ui_ammocounter "EAST-1:28,CENTER-5:9"
diff --git a/code/__HELPERS/screen_objs.dm b/code/__HELPERS/screen_objs.dm
new file mode 100644
index 00000000000..aa86b6d5795
--- /dev/null
+++ b/code/__HELPERS/screen_objs.dm
@@ -0,0 +1,91 @@
+/// Takes a screen loc string in the format
+/// "+-left-offset:+-pixel,+-bottom-offset:+-pixel"
+/// Where the :pixel is optional, and returns
+/// A list in the format (x_offset, y_offset)
+/// We require context to get info out of screen locs that contain relative info, so NORTH, SOUTH, etc
+/proc/screen_loc_to_offset(screen_loc, view)
+ if(!screen_loc)
+ return list(64, 64)
+ var/list/view_size = view_to_pixels(view)
+ var/x = 0
+ var/y = 0
+ // Time to parse for directional relative offsets
+ if(findtext(screen_loc, "EAST")) // If you're starting from the east, we start from the east too
+ x += view_size[1]
+ if(findtext(screen_loc, "WEST")) // HHHHHHHHHHHHHHHHHHHHHH WEST is technically a 1 tile offset from the start. Shoot me please
+ x += world.icon_size
+ if(findtext(screen_loc, "NORTH"))
+ y += view_size[2]
+ if(findtext(screen_loc, "SOUTH"))
+ y += world.icon_size
+ // Cut out everything we just parsed
+ screen_loc = cut_relative_direction(screen_loc)
+
+ var/list/x_and_y = splittext(screen_loc, ",")
+ var/list/x_pack = splittext(x_and_y[1], ":")
+ var/list/y_pack = splittext(x_and_y[2], ":")
+ x += text2num(x_pack[1]) * world.icon_size
+ y += text2num(y_pack[1]) * world.icon_size
+
+ if(length(x_pack) > 1)
+ x += text2num(x_pack[2])
+ if(length(y_pack) > 1)
+ y += text2num(y_pack[2])
+ return list(x, y)
+
+/// Takes a list in the form (x_offset, y_offset)
+/// And converts it to a screen loc string
+/// Accepts an optional view string/size to force the screen_loc around, so it can't go out of scope
+/proc/offset_to_screen_loc(x_offset, y_offset, view = null)
+ if(view)
+ var/list/view_bounds = view_to_pixels(view)
+ x_offset = clamp(x_offset, world.icon_size, view_bounds[1])
+ y_offset = clamp(y_offset, world.icon_size, view_bounds[2])
+
+ // Round with no argument is floor, so we get the non pixel offset here
+ var/x = round(x_offset / world.icon_size)
+ var/pixel_x = x_offset % world.icon_size
+ var/y = round(y_offset / world.icon_size)
+ var/pixel_y = y_offset % world.icon_size
+
+ var/list/generated_loc = list()
+ generated_loc += "[x]"
+ if(pixel_x)
+ generated_loc += ":[pixel_x]"
+ generated_loc += ",[y]"
+ if(pixel_y)
+ generated_loc += ":[pixel_y]"
+ return jointext(generated_loc, "")
+
+/**
+ * Returns a valid location to place a screen object without overflowing the viewport
+ *
+ * * target: The target location as a purely number based screen_loc string "+-left-offset:+-pixel,+-bottom-offset:+-pixel"
+ * * target_offset: The amount we want to offset the target location by. We explictly don't care about direction here, we will try all 4
+ * * view: The view variable of the client we're doing this for. We use this to get the size of the screen
+ *
+ * Returns a screen loc representing the valid location
+**/
+/proc/get_valid_screen_location(target_loc, target_offset, view)
+ var/list/offsets = screen_loc_to_offset(target_loc)
+ var/base_x = offsets[1]
+ var/base_y = offsets[2]
+
+ var/list/view_size = view_to_pixels(view)
+
+ // Bias to the right, down, left, and then finally up
+ if(base_x + target_offset < view_size[1])
+ return offset_to_screen_loc(base_x + target_offset, base_y, view)
+ if(base_y - target_offset > world.icon_size)
+ return offset_to_screen_loc(base_x, base_y - target_offset, view)
+ if(base_x - target_offset > world.icon_size)
+ return offset_to_screen_loc(base_x - target_offset, base_y, view)
+ if(base_y + target_offset < view_size[2])
+ return offset_to_screen_loc(base_x, base_y + target_offset, view)
+ stack_trace("You passed in a scren location {[target_loc]} and offset {[target_offset]} that can't be fit in the viewport Width {[view_size[1]]}, Height {[view_size[2]]}. what did you do lad")
+ return null // The fuck did you do lad
+
+/// Takes a screen_loc string and cut out any directions like NORTH or SOUTH
+/proc/cut_relative_direction(fragment)
+ var/static/regex/regex = regex(@"([A-Z])\w+", "g")
+ return regex.Replace(fragment, "")
diff --git a/code/__HELPERS/view.dm b/code/__HELPERS/view.dm
index 4a4587e1550..6a4210bc535 100644
--- a/code/__HELPERS/view.dm
+++ b/code/__HELPERS/view.dm
@@ -6,6 +6,15 @@
var/list/viewrangelist = splittext(view,"x")
return list(text2num(viewrangelist[1]), text2num(viewrangelist[2]))
+/// Takes a string or num view, and converts it to pixel width/height in a list(pixel_width, pixel_height)
+/proc/view_to_pixels(view)
+ if(!view)
+ return list(0, 0)
+ var/list/view_info = getviewsize(view)
+ view_info[1] *= world.icon_size
+ view_info[2] *= world.icon_size
+ return view_info
+
/proc/in_view_range(mob/user, atom/A)
var/list/view_range = getviewsize(user.client.view)
var/turf/source = get_turf(user)
diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm
index d8000fb81c8..d0d280dc194 100644
--- a/code/_onclick/drag_drop.dm
+++ b/code/_onclick/drag_drop.dm
@@ -92,26 +92,26 @@
if (LAZYACCESS(modifiers, MIDDLE_CLICK))
if (src_object && src_location != over_location)
middragtime = world.time
- middragatom = src_object
+ middle_drag_atom_ref = WEAKREF(src_object)
else
middragtime = 0
- middragatom = null
+ middle_drag_atom_ref = null
mouseParams = params
- mouseLocation = over_location
- mouseObject = over_object
+ mouse_location_ref = WEAKREF(over_location)
+ mouse_object_ref = WEAKREF(over_object)
if(selected_target[1] && over_object?.IsAutoclickable())
selected_target[1] = over_object
selected_target[2] = params
if(active_mousedown_item)
active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
SEND_SIGNAL(src, COMSIG_CLIENT_MOUSEDRAG, src_object, over_object, src_location, over_location, src_control, over_control, params)
-
+ return ..()
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
return
-/client/MouseDrop(src_object, over_object, src_location, over_location, src_control, over_control, params)
- if (middragatom == src_object)
+/client/MouseDrop(atom/src_object, atom/over_object, atom/src_location, atom/over_location, src_control, over_control, params)
+ if (IS_WEAKREF_OF(src_object, middle_drag_atom_ref))
middragtime = 0
- middragatom = null
+ middle_drag_atom_ref = null
..()
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
deleted file mode 100644
index 1f8302a9f7b..00000000000
--- a/code/_onclick/hud/_defines.dm
+++ /dev/null
@@ -1,199 +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.
-
- In addition, the keywords NORTH, SOUTH, EAST, WEST and CENTER can be used to represent their respective
- screen borders. NORTH-1, for example, is the row just below the upper edge. Useful if you want your
- UI to scale with screen size.
-
- 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"
-*/
-
-/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5)
- var/x_off = -(!(i % 2))
- var/y_off = round((i-1) / 2)
- return"CENTER+[x_off]:16,SOUTH+[y_off]:5"
-
-/proc/ui_equip_position(mob/M)
- var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5)
- return "CENTER:-16,SOUTH+[y_off+1]:5"
-
-/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5)
- var/x_off = which == 1 ? -1 : 0
- var/y_off = round((M.held_items.len-1) / 2)
- return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5"
-
-//Lower left, persistent menu
-#define ui_inventory "WEST:6,SOUTH:5"
-
-//Middle left indicators
-#define ui_lingchemdisplay "WEST,CENTER-1:15"
-#define ui_lingstingdisplay "WEST:6,CENTER-3:11"
-
-//Lower center, persistent menu
-#define ui_sstore1 "CENTER-5:10,SOUTH:5"
-#define ui_id "CENTER-4:12,SOUTH:5"
-#define ui_belt "CENTER-3:14,SOUTH:5"
-#define ui_back "CENTER-2:14,SOUTH:5"
-#define ui_storage1 "CENTER+1:18,SOUTH:5"
-#define ui_storage2 "CENTER+2:20,SOUTH:5"
-#define ui_combo "CENTER+4:24,SOUTH+1:7" //combo meter for martial arts
-//SKYRAT EDIT ADDITION BEGIN - ERP_SLOT_SYSTEM
-#define ui_vagina "WEST+1:8,SOUTH+4:14"
-#define ui_vagina_down "WEST+1:8,SOUTH+1:8"
-#define ui_anus "WEST+2:10,SOUTH+4:14"
-#define ui_anus_down "WEST+2:10,SOUTH+1:8"
-#define ui_nipples "WEST:6,SOUTH+5:17"
-#define ui_nipples_down "WEST:6,SOUTH+2:11"
-#define ui_penis "WEST+1:8,SOUTH+5:17"
-#define ui_penis_down "WEST+1:8,SOUTH+2:11"
-#define ui_erp_inventory "WEST:6,SOUTH+1:8"
-#define ui_erp_inventory_up "WEST:6,SOUTH+4:14"
-//SKYRAT EDI ADDITION END
-
-//Lower right, persistent menu
-#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
-#define ui_above_movement "EAST-2:26,SOUTH+1:7"
-#define ui_above_intent "EAST-3:24, SOUTH+1:7"
-#define ui_movi "EAST-2:26,SOUTH:5"
-#define ui_acti "EAST-3:24,SOUTH:5"
-#define ui_combat_toggle "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_crafting "EAST-4:22,SOUTH:5"
-#define ui_building "EAST-4:22,SOUTH:21"
-#define ui_language_menu "EAST-4:6,SOUTH:21"
-#define ui_skill_menu "EAST-4:22,SOUTH:5"
-
-//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"
-
-//Middle right (status indicators)
-#define ui_healthdoll "EAST-1:28,CENTER-2:13"
-#define ui_health "EAST-1:28,CENTER-1:15"
-#define ui_internal "EAST-1:28,CENTER+1:17"
-#define ui_mood "EAST-1:28,CENTER:17"
-#define ui_spacesuit "EAST-1:28,CENTER-4:10"
-#define ui_stamina "EAST-1:28,CENTER-3:10"
-
-//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+3:11"
-#define ui_mask "WEST+1:8,SOUTH+2:9"
-#define ui_ears "WEST+2:10,SOUTH+2:9"
-#define ui_neck "WEST:6,SOUTH+2:9"
-#define ui_head "WEST+1:8,SOUTH+3:11"
-
-//Generic living
-#define ui_living_pull "EAST-1:28,CENTER-3:15"
-#define ui_living_healthdoll "EAST-1:28,CENTER-1:15"
-
-//Monkeys
-#define ui_monkey_head "CENTER-5:13,SOUTH:5"
-#define ui_monkey_mask "CENTER-4:14,SOUTH:5"
-#define ui_monkey_neck "CENTER-3:15,SOUTH:5"
-#define ui_monkey_back "CENTER-2:16,SOUTH:5"
-
-//Drones
-#define ui_drone_drop "CENTER+1:18,SOUTH:5"
-#define ui_drone_pull "CENTER+2:2,SOUTH:5"
-#define ui_drone_storage "CENTER-2:14,SOUTH:5"
-#define ui_drone_head "CENTER-3:14,SOUTH:5"
-
-//Cyborgs
-#define ui_borg_health "EAST-1:28,CENTER-1:15"
-#define ui_borg_pull "EAST-2:26,SOUTH+1:7"
-#define ui_borg_radio "EAST-1:28,SOUTH+1:7"
-#define ui_borg_intents "EAST-2:26,SOUTH:5"
-#define ui_borg_lamp "CENTER-3:16, SOUTH:5"
-#define ui_borg_tablet "CENTER-4:16, SOUTH:5"
-#define ui_inv1 "CENTER-2:16,SOUTH:5"
-#define ui_inv2 "CENTER-1 :16,SOUTH:5"
-#define ui_inv3 "CENTER :16,SOUTH:5"
-#define ui_borg_module "CENTER+1:16,SOUTH:5"
-#define ui_borg_store "CENTER+2:16,SOUTH:5"
-#define ui_borg_camera "CENTER+3:21,SOUTH:5"
-#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
-#define ui_borg_language_menu "CENTER+4:19,SOUTH+1:6"
-// SKYRAT EDIT ADDITION BEGIN - Cyborg PDA
-#define ui_borg_pda_send "CENTER+5:21,SOUTH:5" // To the right of the alert panel
-#define ui_borg_pda_log "CENTER+6:21,SOUTH:5"
-// SKYRAT EDIT ADDITION END
-
-//Aliens
-#define ui_alien_health "EAST,CENTER-1:15"
-#define ui_alienplasmadisplay "EAST,CENTER-2:15"
-#define ui_alien_queen_finder "EAST,CENTER-3:15"
-#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"
-#define ui_alien_language_menu "EAST-4:20,SOUTH:5"
-
-//AI
-#define ui_ai_core "SOUTH:6,WEST"
-#define ui_ai_camera_list "SOUTH:6,WEST+1"
-#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
-#define ui_ai_camera_light "SOUTH:6,WEST+3"
-#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
-#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
-#define ui_ai_alerts "SOUTH:6,WEST+6"
-#define ui_ai_announcement "SOUTH:6,WEST+7"
-#define ui_ai_shuttle "SOUTH:6,WEST+8"
-#define ui_ai_state_laws "SOUTH:6,WEST+9"
-#define ui_ai_pda_send "SOUTH:6,WEST+10"
-#define ui_ai_pda_log "SOUTH:6,WEST+11"
-#define ui_ai_take_picture "SOUTH:6,WEST+12"
-#define ui_ai_view_images "SOUTH:6,WEST+13"
-#define ui_ai_sensor "SOUTH:6,WEST+14"
-#define ui_ai_multicam "SOUTH+1:6,WEST+13"
-#define ui_ai_add_multicam "SOUTH+1:6,WEST+14"
-#define ui_ai_language_menu "SOUTH+1:8,WEST+11:30"
-
-//pAI
-#define ui_pai_software "SOUTH:6,WEST"
-#define ui_pai_shell "SOUTH:6,WEST+1"
-#define ui_pai_chassis "SOUTH:6,WEST+2"
-#define ui_pai_rest "SOUTH:6,WEST+3"
-#define ui_pai_light "SOUTH:6,WEST+4"
-#define ui_pai_newscaster "SOUTH:6,WEST+5"
-#define ui_pai_host_monitor "SOUTH:6,WEST+6"
-#define ui_pai_crew_manifest "SOUTH:6,WEST+7"
-#define ui_pai_state_laws "SOUTH:6,WEST+8"
-#define ui_pai_pda_send "SOUTH:6,WEST+9"
-#define ui_pai_pda_log "SOUTH:6,WEST+10"
-#define ui_pai_internal_gps "SOUTH:6,WEST+11"
-#define ui_pai_take_picture "SOUTH:6,WEST+12"
-#define ui_pai_view_images "SOUTH:6,WEST+13"
-#define ui_pai_radio "SOUTH:6,WEST+14"
-#define ui_pai_language_menu "SOUTH+1:8,WEST+13:31"
-
-//Ghosts
-#define ui_ghost_spawners_menu "SOUTH:6,CENTER-3:24"
-#define ui_ghost_orbit "SOUTH:6,CENTER-2:24"
-#define ui_ghost_reenter_corpse "SOUTH:6,CENTER-1:24"
-#define ui_ghost_teleport "SOUTH:6,CENTER:24"
-#define ui_ghost_pai "SOUTH: 6, CENTER+1:24"
-#define ui_ghost_minigames "SOUTH: 6, CENTER+2:24"
-#define ui_ghost_language_menu "SOUTH: 22, CENTER+3:8"
-
-//Blobbernauts
-#define ui_blobbernaut_overmind_health "EAST-1:28,CENTER+0:19"
-
-//Families
-#define ui_wanted_lvl "NORTH,11"
-
-//SKYRAT HUD DEFINES BELOW
-#define ui_ammocounter "EAST-1:28,CENTER-5:9"
diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm
index ffd1a480b12..29b10891574 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/action_button.dm
@@ -2,22 +2,36 @@
/atom/movable/screen/movable/action_button
var/datum/action/linked_action
+ var/datum/hud/our_hud
var/actiontooltipstyle = ""
screen_loc = null
var/button_icon_state
var/appearance_cache
+ /// 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
- var/ordered = TRUE //If the button gets placed into the default bar
+ /// A weakref of the last thing we hovered over
+ /// God I hate how dragging works
+ var/datum/weakref/last_hovored_ref
+
+/atom/movable/screen/movable/action_button/Destroy()
+ if(our_hud)
+ var/mob/viewer = our_hud.mymob
+ our_hud.hide_action(src)
+ viewer?.client?.screen -= src
+ linked_action.viewers -= our_hud
+ viewer.update_action_buttons()
+ our_hud = null
+ linked_action = null
+ return ..()
/atom/movable/screen/movable/action_button/proc/can_use(mob/user)
if(linked_action)
- if(linked_action.owner == user)
+ if(linked_action.viewers[user.hud_used])
return TRUE
- for(var/datum/weakref/reference as anything in linked_action.sharers)
- if(IS_WEAKREF_OF(user, reference))
- return TRUE
return FALSE
else if (isobserver(user))
var/mob/dead/observer/O = user
@@ -25,41 +39,14 @@
else
return TRUE
-/atom/movable/screen/movable/action_button/MouseDrop(over_object)
- if(!can_use(usr))
- return
- if((istype(over_object, /atom/movable/screen/movable/action_button) && !istype(over_object, /atom/movable/screen/movable/action_button/hide_toggle)))
- if(locked)
- to_chat(usr, span_warning("Action button \"[name]\" is locked, unlock it first."))
- return
- var/atom/movable/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
- ordered = TRUE
- B.moved = FALSE
- B.ordered = TRUE
- usr.update_action_buttons()
- else
- return ..()
-
/atom/movable/screen/movable/action_button/Click(location,control,params)
if (!can_use(usr))
return FALSE
var/list/modifiers = params2list(params)
if(LAZYACCESS(modifiers, SHIFT_CLICK))
- if(locked)
- to_chat(usr, span_warning("Action button \"[name]\" is locked, unlock it first."))
- return TRUE
- moved = 0
- usr.update_action_buttons() //redraw buttons that are no longer considered "moved"
- return TRUE
- if(LAZYACCESS(modifiers, CTRL_CLICK))
- locked = !locked
- to_chat(usr, span_notice("Action button \"[name]\" [locked ? "" : "un"]locked."))
- if(id && usr.client) //try to (un)remember position
- usr.client.prefs.action_buttons_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.next_click > world.time)
return
@@ -70,127 +57,105 @@
linked_action.Trigger(trigger_flags = trigger_flags)
return TRUE
-//Hide/Show Action Buttons ... Button
-/atom/movable/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 to reset all buttons to their default positions."
- icon = 'icons/hud/actions.dmi'
- icon_state = "bg_default"
- var/hidden = FALSE
- var/hide_icon = 'icons/hud/actions.dmi'
- var/hide_state = "hide"
- var/show_state = "show"
- var/mutable_appearance/hide_appearance
- var/mutable_appearance/show_appearance
-
-/atom/movable/screen/movable/action_button/hide_toggle/Initialize(mapload)
+// Entered and Exited won't fire while you're dragging something, because you're still "holding" it
+// Very much byond logic, but I want nice behavior, so we fake it with drag
+/atom/movable/screen/movable/action_button/MouseDrag(atom/over_object, src_location, over_location, src_control, over_control, params)
. = ..()
- 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)
-
-
-/atom/movable/screen/movable/action_button/hide_toggle/Click(location,control,params)
- if (!can_use(usr))
+ if(!can_use(usr))
return
+ if(IS_WEAKREF_OF(over_object, last_hovored_ref))
+ return
+ var/atom/old_object
+ if(last_hovored_ref)
+ old_object = last_hovored_ref?.resolve()
+ else // If there's no current ref, we assume it was us. We also treat this as our "first go" location
+ old_object = src
+ var/datum/hud/our_hud = usr.hud_used
+ our_hud?.generate_landings(src)
- 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) //try to (un)remember position
- usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = locked ? moved : null
- return TRUE
- if(LAZYACCESS(modifiers, ALT_CLICK))
- var/buttons_locked = usr.client.prefs.read_preference(/datum/preference/toggle/buttons_locked)
- for(var/V in usr.actions)
- var/datum/action/A = V
- var/atom/movable/screen/movable/action_button/B = A.button
- B.moved = FALSE
- if(B.id && usr.client)
- usr.client.prefs.action_buttons_screen_locs["[B.name]_[B.id]"] = null
- B.locked = buttons_locked
- locked = buttons_locked
- moved = FALSE
- if(id && usr.client)
- usr.client.prefs.action_buttons_screen_locs["[name]_[id]"] = null
- usr.update_action_buttons(TRUE)
- to_chat(usr, span_notice("Action button positions have been reset."))
- return TRUE
- 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_appearance()
- usr.update_action_buttons()
+ last_hovored_ref = WEAKREF(over_object)
+ over_object.MouseEntered(over_location, over_control, params)
-/atom/movable/screen/movable/action_button/hide_toggle/AltClick(mob/user)
- for(var/V in user.actions)
- var/datum/action/A = V
- var/atom/movable/screen/movable/action_button/B = A.button
- B.moved = FALSE
- if(moved)
- moved = FALSE
- user.update_action_buttons(TRUE)
- to_chat(user, span_notice("Action button positions have been reset."))
-
-
-/atom/movable/screen/movable/action_button/hide_toggle/proc/InitialiseIcon(datum/hud/owner_hud)
- 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"]
- update_appearance()
-
-/atom/movable/screen/movable/action_button/hide_toggle/update_overlays()
- . = ..()
- . += hidden ? show_appearance : hide_appearance
-
-/atom/movable/screen/movable/action_button/MouseEntered(location,control,params)
+/atom/movable/screen/movable/action_button/MouseEntered(location, control, params)
. = ..()
if(!QDELETED(src))
- openToolTip(usr,src,params,title = name,content = desc,theme = actiontooltipstyle)
+ openToolTip(usr, src, params, title = name, content = desc, theme = actiontooltipstyle)
-
-/atom/movable/screen/movable/action_button/MouseExited()
+/atom/movable/screen/movable/action_button/MouseExited(location, control, params)
closeToolTip(usr)
+ return ..()
+
+/atom/movable/screen/movable/action_button/MouseDrop(over_object)
+ last_hovored_ref = null
+ if(!can_use(usr))
+ return
+ var/datum/hud/our_hud = usr.hud_used
+ if(over_object == src)
+ our_hud.hide_landings()
+ return
+ if(istype(over_object, /atom/movable/screen/action_landing))
+ var/atom/movable/screen/action_landing/reserve = over_object
+ reserve.hit_by(src)
+ our_hud.hide_landings()
+ save_position()
+ return
+
+ our_hud.hide_landings()
+ if(istype(over_object, /atom/movable/screen/button_palette) || istype(over_object, /atom/movable/screen/palette_scroll))
+ our_hud.position_action(src, SCRN_OBJ_IN_PALETTE)
+ save_position()
+ return
+ if(istype(over_object, /atom/movable/screen/movable/action_button))
+ var/atom/movable/screen/movable/action_button/button = over_object
+ our_hud.position_action_relative(src, button)
+ save_position()
+ return
+ . = ..()
+ our_hud.position_action(src, screen_loc)
+ save_position()
+
+/atom/movable/screen/movable/action_button/proc/save_position()
+ var/mob/user = our_hud.mymob
+ if(!user?.client)
+ return
+ var/position_info = ""
+ switch(location)
+ if(SCRN_OBJ_FLOATING)
+ position_info = screen_loc
+ if(SCRN_OBJ_IN_LIST)
+ position_info = SCRN_OBJ_IN_LIST
+ if(SCRN_OBJ_IN_PALETTE)
+ position_info = SCRN_OBJ_IN_PALETTE
+
+ user.client.prefs.action_buttons_screen_locs["[name]_[id]"] = position_info
+
+/atom/movable/screen/movable/action_button/proc/load_position()
+ var/mob/user = our_hud.mymob
+ if(!user)
+ return
+ var/position_info = user.client?.prefs?.action_buttons_screen_locs["[name]_[id]"] || SCRN_OBJ_DEFAULT
+ user.hud_used.position_action(src, position_info)
+
+/atom/movable/screen/movable/action_button/proc/dump_save()
+ var/mob/user = our_hud.mymob
+ if(!user?.client)
+ return
+ user.client.prefs.action_buttons_screen_locs -= "[name]_[id]"
/datum/hud/proc/get_action_buttons_icons()
. = list()
.["bg_icon"] = ui_style
.["bg_state"] = "template"
- //TODO : Make these fit theme
- .["toggle_icon"] = 'icons/hud/actions.dmi'
- .["toggle_hide"] = "hide"
- .["toggle_show"] = "show"
-
//see human and alien hud for specific implementations.
/mob/proc/update_action_buttons_icon(status_only = FALSE)
for(var/X in actions)
var/datum/action/A = X
- A.UpdateButtonIcon(status_only)
+ A.UpdateButtons(status_only)
//This is the proc used to update all the action buttons.
/mob/proc/update_action_buttons(reload_screen)
@@ -200,58 +165,243 @@
if(hud_used.hud_shown != HUD_STYLE_STANDARD)
return
- var/button_number = 0
+ for(var/datum/action/action as anything in actions)
+ var/atom/movable/screen/movable/action_button/button = action.viewers[hud_used]
+ action.UpdateButtons()
+ if(reload_screen)
+ client.screen += button
- 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)
- A.UpdateButtonIcon()
- var/atom/movable/screen/movable/action_button/B = A.button
- if(B.ordered)
- button_number++
- if(B.moved)
- B.screen_loc = B.moved
- else
- B.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number)
- if(reload_screen)
- client.screen += B
-
- if(!button_number)
- hud_used.hide_actions_toggle.screen_loc = null
- return
-
- 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
if(reload_screen)
- client.screen += hud_used.hide_actions_toggle
+ hud_used.update_our_owner()
+ // This holds the logic for the palette buttons
+ hud_used.palette_actions.refresh_actions()
+/atom/movable/screen/button_palette
+ desc = "Drag buttons to move them
Shift-click any button to reset it
Alt-click this to reset all buttons"
+ icon = 'icons/hud/64x16_actions.dmi'
+ icon_state = "screen_gen_palette"
+ screen_loc = ui_action_palette
+ var/datum/hud/our_hud
+ var/expanded = FALSE
+ /// Id of any currently running timers that set our color matrix
+ var/color_timer_id
+/atom/movable/screen/button_palette/Destroy()
+ if(our_hud)
+ our_hud.mymob?.client?.screen -= src
+ our_hud.toggle_palette = null
+ our_hud = null
+ return ..()
-#define AB_MAX_COLUMNS 10
+/atom/movable/screen/button_palette/Initialize(mapload)
+ . = ..()
+ update_appearance()
-/datum/hud/proc/ButtonNumberToScreenCoords(number) // TODO : Make this zero-indexed for readabilty
- var/row = round((number - 1)/AB_MAX_COLUMNS)
- var/col = ((number - 1)%(AB_MAX_COLUMNS)) + 1
+/atom/movable/screen/button_palette/proc/set_hud(datum/hud/our_hud)
+ src.our_hud = our_hud
+ refresh_owner()
- var/coord_col = "+[col-1]"
- var/coord_col_offset = 4 + 2 * col
+/atom/movable/screen/button_palette/update_name(updates)
+ . = ..()
+ if(expanded)
+ name = "Hide Buttons"
+ else
+ name = "Show Buttons"
- var/coord_row = "[row ? -row : "+0"]"
+/atom/movable/screen/button_palette/proc/refresh_owner()
+ var/mob/viewer = our_hud.mymob
+ if(viewer.client)
+ viewer.client.screen |= src
- return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:-6"
+ 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)]
-/datum/hud/proc/SetButtonCoords(atom/movable/screen/button,number)
- var/row = round((number-1)/AB_MAX_COLUMNS)
- var/col = ((number - 1)%(AB_MAX_COLUMNS)) + 1
- var/x_offset = 32*(col-1) + 4 + 2*col
- var/y_offset = -32*(row+1) + 26
+ icon_state = "[ui_name]_palette"
- var/matrix/M = matrix()
- M.Translate(x_offset,y_offset)
- button.transform = M
+/atom/movable/screen/button_palette/MouseEntered(location, control, params)
+ . = ..()
+ if(QDELETED(src))
+ return
+ show_tooltip(params)
+
+/atom/movable/screen/button_palette/MouseExited()
+ closeToolTip(usr)
+ return ..()
+
+/atom/movable/screen/button_palette/proc/show_tooltip(params)
+ openToolTip(usr, src, params, title = name, content = desc)
+
+GLOBAL_LIST_INIT(palette_added_matrix, list(0.4,0.5,0.2,0, 0,1.4,0,0, 0,0.4,0.6,0, 0,0,0,1, 0,0,0,0))
+GLOBAL_LIST_INIT(palette_removed_matrix, list(1.4,0,0,0, 0.7,0.4,0,0, 0.4,0,0.6,0, 0,0,0,1, 0,0,0,0))
+
+/atom/movable/screen/button_palette/proc/play_item_added()
+ color_for_now(GLOB.palette_added_matrix)
+
+/atom/movable/screen/button_palette/proc/play_item_removed()
+ color_for_now(GLOB.palette_removed_matrix)
+
+/atom/movable/screen/button_palette/proc/color_for_now(list/color)
+ if(color_timer_id)
+ return
+ add_atom_colour(color, TEMPORARY_COLOUR_PRIORITY) //We unfortunately cannot animate matrix colors. Curse you lummy it would be ~~non~~trivial to interpolate between the two valuessssssssss
+ color_timer_id = addtimer(CALLBACK(src, .proc/remove_color, color), 2 SECONDS)
+
+/atom/movable/screen/button_palette/proc/remove_color(list/to_remove)
+ color_timer_id = null
+ remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, to_remove)
+
+/atom/movable/screen/button_palette/proc/can_use(mob/user)
+ if (isobserver(user))
+ var/mob/dead/observer/O = user
+ return !O.observetarget
+ return TRUE
+
+/atom/movable/screen/button_palette/Click(location, control, params)
+ if(!can_use(usr))
+ return
+
+ var/list/modifiers = params2list(params)
+
+ if(LAZYACCESS(modifiers, ALT_CLICK))
+ for(var/datum/action/action as anything in usr.actions) // Reset action positions to default
+ for(var/datum/hud/hud as anything in action.viewers)
+ var/atom/movable/screen/movable/action_button/button = action.viewers[hud]
+ hud.position_action(button, SCRN_OBJ_DEFAULT)
+ to_chat(usr, span_notice("Action button positions have been reset."))
+ return TRUE
+
+ set_expanded(!expanded)
+
+/atom/movable/screen/button_palette/proc/clicked_while_open(datum/source, atom/target, atom/location, control, params, mob/user)
+ if(istype(target, /atom/movable/screen/movable/action_button) || istype(target, /atom/movable/screen/palette_scroll) || target == src) // If you're clicking on an action button, or us, you can live
+ return
+ set_expanded(FALSE)
+ if(source)
+ UnregisterSignal(source, COMSIG_CLIENT_CLICK)
+
+/atom/movable/screen/button_palette/proc/set_expanded(new_expanded)
+ var/datum/action_group/our_group = our_hud.palette_actions
+ if(!length(our_group.actions)) //Looks dumb, trust me lad
+ new_expanded = FALSE
+ if(expanded == new_expanded)
+ return
+
+ expanded = new_expanded
+ our_group.refresh_actions()
+ update_appearance()
+
+ if(!usr.client)
+ return
+
+ if(expanded)
+ RegisterSignal(usr.client, COMSIG_CLIENT_CLICK, .proc/clicked_while_open)
+ else
+ UnregisterSignal(usr.client, COMSIG_CLIENT_CLICK)
+
+ closeToolTip(usr) //Our tooltips are now invalid, can't seem to update them in one frame, so here, just close them
+
+/atom/movable/screen/palette_scroll
+ icon = 'icons/hud/screen_gen.dmi'
+ screen_loc = ui_palette_scroll
+ /// How should we move the palette's actions?
+ /// Positive scrolls down the list, negative scrolls back
+ var/scroll_direction = 0
+ var/datum/hud/our_hud
+
+/atom/movable/screen/palette_scroll/proc/can_use(mob/user)
+ if (isobserver(user))
+ var/mob/dead/observer/O = user
+ return !O.observetarget
+ return TRUE
+
+/atom/movable/screen/palette_scroll/proc/set_hud(datum/hud/our_hud)
+ src.our_hud = our_hud
+ refresh_owner()
+
+/atom/movable/screen/palette_scroll/proc/refresh_owner()
+ var/mob/viewer = our_hud.mymob
+ if(viewer.client)
+ viewer.client.screen |= src
+
+ var/list/settings = our_hud.get_action_buttons_icons()
+ icon = settings["bg_icon"]
+
+/atom/movable/screen/palette_scroll/Click(location, control, params)
+ if(!can_use(usr))
+ return
+ our_hud.palette_actions.scroll(scroll_direction)
+
+/atom/movable/screen/palette_scroll/MouseEntered(location, control, params)
+ . = ..()
+ if(QDELETED(src))
+ return
+ openToolTip(usr, src, params, title = name, content = desc)
+
+/atom/movable/screen/palette_scroll/MouseExited()
+ closeToolTip(usr)
+ return ..()
+
+/atom/movable/screen/palette_scroll/down
+ name = "Scroll Down"
+ desc = "Click on this to scroll the actions above down"
+ icon_state = "scroll_down"
+ scroll_direction = 1
+
+/atom/movable/screen/palette_scroll/down/Destroy()
+ if(our_hud)
+ our_hud.mymob?.client?.screen -= src
+ our_hud.palette_down = null
+ our_hud = null
+ return ..()
+
+/atom/movable/screen/palette_scroll/up
+ name = "Scroll Up"
+ desc = "Click on this to scroll the actions above up"
+ icon_state = "scroll_up"
+ scroll_direction = -1
+
+/atom/movable/screen/palette_scroll/up/Destroy()
+ if(our_hud)
+ our_hud.mymob?.client?.screen -= src
+ our_hud.palette_up = null
+ our_hud = null
+ return ..()
+
+/// Exists so you have a place to put your buttons when you move them around
+/atom/movable/screen/action_landing
+ name = "Button Space"
+ desc = "Drag and drop a button into this spot
to add it to the group"
+ icon = 'icons/hud/screen_gen.dmi'
+ icon_state = "reserved"
+ // We want our whole 32x32 space to be clickable, so dropping's forgiving
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
+ var/datum/action_group/owner
+
+/atom/movable/screen/action_landing/Destroy()
+ if(owner)
+ owner.landing = null
+ owner?.owner?.mymob?.client?.screen -= src
+ owner.refresh_actions()
+ owner = null
+ return ..()
+
+/atom/movable/screen/action_landing/proc/set_owner(datum/action_group/owner)
+ src.owner = owner
+ refresh_owner()
+
+/atom/movable/screen/action_landing/proc/refresh_owner()
+ var/datum/hud/our_hud = owner.owner
+ var/mob/viewer = our_hud.mymob
+ if(viewer.client)
+ viewer.client.screen |= src
+
+ var/list/settings = our_hud.get_action_buttons_icons()
+ icon = settings["bg_icon"]
+
+/// Reacts to having a button dropped on it
+/atom/movable/screen/action_landing/proc/hit_by(atom/movable/screen/movable/action_button/button)
+ var/datum/hud/our_hud = owner.owner
+ our_hud.position_action(button, owner.location)
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index b18b7ee12f6..8a323820aac 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -93,8 +93,13 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
/// game (MouseEntered).
var/screentip_color
- var/atom/movable/screen/movable/action_button/hide_toggle/hide_actions_toggle
- var/action_buttons_hidden = FALSE
+ var/atom/movable/screen/button_palette/toggle_palette
+ var/atom/movable/screen/palette_scroll/down/palette_down
+ var/atom/movable/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/atom/movable/screen/healths
var/atom/movable/screen/stamina
@@ -114,10 +119,12 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
ui_style = ui_style2icon(owner.client?.prefs?.read_preference(/datum/preference/choiced/ui_style))
erp_ui_style = erp_ui_style2icon(owner.client?.prefs?.read_preference(/datum/preference/choiced/ui_style)) //SKYRAT EDIT - ADDITION - ERP ICONS FIX
- hide_actions_toggle = new
- hide_actions_toggle.InitialiseIcon(src)
- if(mymob.client?.prefs)
- hide_actions_toggle.locked = mymob.client.prefs.read_preference(/datum/preference/toggle/buttons_locked)
+ toggle_palette = new()
+ toggle_palette.set_hud(src)
+ palette_down = new()
+ palette_down.set_hud(src)
+ palette_up = new()
+ palette_up.set_hud(src)
hand_slots = list()
@@ -142,7 +149,13 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
if(mymob.hud_used == src)
mymob.hud_used = null
- QDEL_NULL(hide_actions_toggle)
+ QDEL_NULL(toggle_palette)
+ QDEL_NULL(palette_down)
+ QDEL_NULL(palette_up)
+ QDEL_NULL(palette_actions)
+ QDEL_NULL(listed_actions)
+ QDEL_LIST(floating_actions)
+
QDEL_NULL(module_store_icon)
QDEL_LIST(static_inventory)
@@ -184,10 +197,14 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
/mob/proc/create_mob_hud()
if(!client || hud_used)
return
- hud_used = new hud_type(src)
+ set_hud_used(new hud_type(src))
update_sight()
SEND_SIGNAL(src, COMSIG_MOB_HUD_CREATED)
+/mob/proc/set_hud_used(datum/hud/new_hud)
+ hud_used = new_hud
+ new_hud.build_action_groups()
+
//Version denotes which style should be displayed. blank or 0 means "next version"
/datum/hud/proc/show_hud(version = 0, mob/viewmob)
if(!ismob(mymob))
@@ -196,6 +213,8 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
if(!screenmob.client)
return FALSE
+ // This code is the absolute fucking worst, I want it to go die in a fire
+ // Seriously, why
screenmob.client.screen = list()
screenmob.client.apply_clickcatcher()
@@ -221,7 +240,7 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
if(infodisplay.len)
screenmob.client.screen += infodisplay
- screenmob.client.screen += hide_actions_toggle
+ screenmob.client.screen += toggle_palette
if(action_intent)
action_intent.screen_loc = initial(action_intent.screen_loc) //Restore intent selection to the original position
@@ -320,7 +339,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
ui_style = new_ui_style
build_hand_slots()
- hide_actions_toggle.InitialiseIcon(src)
//SKYRAT EDIT - ADDITION - ERP ICONS FIX
@@ -334,7 +352,6 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
item.icon = new_erp_ui_style
erp_ui_style = new_erp_ui_style
- hide_actions_toggle.InitialiseIcon(src)
//SKYRAT EDIT - ADDITION - ERP ICONS FIX - END
@@ -384,3 +401,306 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
/datum/hud/proc/update_locked_slots()
return
+
+/datum/hud/proc/position_action(atom/movable/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(atom/movable/screen/movable/action_button/button, atom/movable/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_size.getView()) // 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(atom/movable/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
+
+/// Generates visual landings for all groups that the button is not a memeber of
+/datum/hud/proc/generate_landings(atom/movable/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/atom/movable/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/atom/movable/screen/movable/action_button/button = action.viewers[src]
+ if(!button)
+ action.ShowTo(mymob)
+ button = action.viewers[src]
+ position_action(button, button.location)
+
+/datum/action_group
+ /// The hud we're owned by
+ var/datum/hud/owner
+ /// The actions we're managing
+ var/list/atom/movable/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/atom/movable/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(atom/movable/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(atom/movable/screen/action)
+ actions -= action
+ 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/atom/movable/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(atom/movable/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(atom/movable/screen/action, index)
+ . = ..()
+ var/atom/movable/screen/button_palette/palette = owner.toggle_palette
+ palette.play_item_added()
+
+/datum/action_group/palette/remove_action(atom/movable/screen/action)
+ . = ..()
+ var/atom/movable/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/atom/movable/screen/button_palette/palette = owner.toggle_palette
+ var/atom/movable/screen/palette_scroll/scroll_down = owner.palette_down
+ var/atom/movable/screen/palette_scroll/scroll_up = owner.palette_up
+
+ 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/atom/movable/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
diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm
index c3372d338f3..b1f40f76294 100644
--- a/code/_onclick/hud/movable_screen_objects.dm
+++ b/code/_onclick/hud/movable_screen_objects.dm
@@ -9,9 +9,8 @@
//Not tied to the grid, places it's center where the cursor is
/atom/movable/screen/movable
+ 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
@@ -21,35 +20,29 @@
/atom/movable/screen/movable/snap
snap2grid = TRUE
-
/atom/movable/screen/movable/MouseDrop(over_object, src_location, over_location, src_control, over_control, params)
- if(locked) //no! I am locked! begone!
+ var/position = mouse_params_to_position(params)
+ if(!position)
return
+
+ screen_loc = position
+
+/// Takes mouse parmas as input, returns a string representing the appropriate mouse position
+/atom/movable/screen/movable/proc/mouse_params_to_position(params)
var/list/modifiers = params2list(params)
//No screen-loc information? abort.
if(!LAZYACCESS(modifiers, SCREEN_LOC))
return
-
- //Split screen-loc up into X+Pixel_X and Y+Pixel_Y
- var/list/screen_loc_params = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",")
-
- //Split X+Pixel_X up into list(X, Pixel_X)
- var/list/screen_loc_X = splittext(screen_loc_params[1],":")
-
- //Split Y+Pixel_Y up into list(Y, Pixel_Y)
- var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
-
+ var/client/our_client = usr.client
+ var/list/offset = screen_loc_to_offset(LAZYACCESS(modifiers, SCREEN_LOC))
if(snap2grid) //Discard Pixel Values
- screen_loc = "[screen_loc_X[1]],[screen_loc_Y[1]]"
-
+ offset[1] = FLOOR(offset[1], world.icon_size) // drops any pixel offset
+ offset[2] = FLOOR(offset[2], world.icon_size) // drops any pixel offset
else //Normalise Pixel Values (So the object drops at the center of the mouse, not 16 pixels off)
- var/pix_X = text2num(screen_loc_X[2]) + x_off
- var/pix_Y = text2num(screen_loc_Y[2]) + y_off
- screen_loc = "[screen_loc_X[1]]:[pix_X],[screen_loc_Y[1]]:[pix_Y]"
-
- moved = screen_loc
-
+ offset[1] += x_off
+ offset[2] += y_off
+ return offset_to_screen_loc(offset[1], offset[2], our_client?.view)
//Debug procs
/client/proc/test_movable_UI()
diff --git a/code/_onclick/hud/screentip.dm b/code/_onclick/hud/screentip.dm
index 36159ec0086..198e70326ab 100644
--- a/code/_onclick/hud/screentip.dm
+++ b/code/_onclick/hud/screentip.dm
@@ -16,6 +16,6 @@
SIGNAL_HANDLER
if(!hud || !hud.mymob.client.view_size) //Might not have been initialized by now
return
- maptext_width = getviewsize(hud.mymob.client.view_size.getView())[1] * world.icon_size
+ maptext_width = view_to_pixels(hud.mymob.client.view_size.getView())[1]
diff --git a/code/controllers/subsystem/augury.dm b/code/controllers/subsystem/augury.dm
index 922e65e0e74..cb8f62df461 100644
--- a/code/controllers/subsystem/augury.dm
+++ b/code/controllers/subsystem/augury.dm
@@ -72,15 +72,15 @@ SUBSYSTEM_DEF(augury)
SSaugury.watchers += owner
to_chat(owner, span_notice("You are now auto-following debris."))
active = TRUE
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/innate/augury/Deactivate()
SSaugury.watchers -= owner
to_chat(owner, span_notice("You are no longer auto-following debris."))
active = FALSE
- UpdateButtonIcon()
+ UpdateButtons()
-/datum/action/innate/augury/UpdateButtonIcon(status_only = FALSE, force)
+/datum/action/innate/augury/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force)
..()
if(active)
button.icon_state = "template_active"
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 7e152b1131e..4366534e734 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -4,9 +4,10 @@
var/datum/target
var/check_flags = NONE
var/processing = FALSE
- var/atom/movable/screen/movable/action_button/button = null
var/buttontooltipstyle = ""
var/transparent_when_unavailable = TRUE
+ /// Where any buttons we create should be by default. Accepts screen_loc and location defines
+ var/default_button_position = SCRN_OBJ_IN_LIST
var/button_icon = 'icons/mob/actions/backgrounds.dmi' //This is the file for the BACKGROUND icon
var/background_icon_state = ACTION_BUTTON_DEFAULT_BACKGROUND //And this is the state for the background icon
@@ -14,17 +15,11 @@
var/icon_icon = 'icons/hud/actions.dmi' //This is the file for the ACTION icon
var/button_icon_state = "default" //And this is the state for the action icon
var/mob/owner
- ///All mobs that are sharing our action button.
- var/list/sharers = list()
+ ///List of all mobs that are viewing our action button -> A unique movable for them to view.
+ var/list/viewers = list()
/datum/action/New(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
@@ -35,41 +30,21 @@
if(owner)
Remove(owner)
target = null
- QDEL_NULL(button)
+ QDEL_LIST_ASSOC_VAL(viewers) // Qdel the buttons in the viewers list **NOT THE HUDS**
return ..()
/datum/action/proc/Grant(mob/M)
- if(M)
- if(owner)
- if(owner == M)
- return
- Remove(owner)
- owner = M
- RegisterSignal(owner, COMSIG_PARENT_QDELETING, .proc/clear_ref, override = TRUE)
-
- //button id generation
- var/counter = 0
- var/bitfield = 0
- for(var/datum/action/A 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.read_preference(/datum/preference/toggle/buttons_locked) || button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE //even if it's not defaultly locked we should remember we locked it before
- button.moved = button.id ? M.client.prefs.action_buttons_screen_locs["[name]_[button.id]"] : FALSE
- M.update_action_buttons()
- else
+ if(!M)
Remove(owner)
+ return
+ if(owner)
+ if(owner == M)
+ return
+ Remove(owner)
+ owner = M
+ RegisterSignal(owner, COMSIG_PARENT_QDELETING, .proc/clear_ref, override = TRUE)
+
+ GiveAction(M)
/datum/action/proc/clear_ref(datum/ref)
SIGNAL_HANDLER
@@ -79,26 +54,18 @@
qdel(src)
/datum/action/proc/Remove(mob/M)
- for(var/datum/weakref/reference as anything in sharers)
- var/mob/freeloader = reference.resolve()
- if(!freeloader)
+ for(var/datum/hud/hud in viewers)
+ if(!hud.mymob)
continue
- Unshare(freeloader)
- sharers = null
- if(M)
- if(M.client)
- M.client.screen -= button
- LAZYREMOVE(M.actions, src)
- M.update_action_buttons()
+ HideFrom(hud.mymob)
+ LAZYREMOVE(M.actions, src) // We aren't always properly inserted into the viewers list, gotta make sure that action's cleared
+ viewers = list()
+
if(owner)
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
if(target == owner)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/clear_ref)
owner = null
- if(button)
- 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(trigger_flags)
if(!IsAvailable())
@@ -123,31 +90,36 @@
return FALSE
return TRUE
+/datum/action/proc/UpdateButtons(status_only, force)
+ for(var/datum/hud/hud in viewers)
+ var/atom/movable/screen/movable/button = viewers[hud]
+ UpdateButton(button, status_only, force)
-/datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE)
- if(button)
- if(!status_only)
- button.name = name
- button.desc = desc
- if(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 = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0)
+/datum/action/proc/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force = FALSE)
+ if(!button)
+ return
+ if(!status_only)
+ button.name = name
+ button.desc = desc
+ if(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
- button.color = rgb(255,255,255,255)
- return TRUE
+ 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 = 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(atom/movable/screen/movable/action_button/current_button, force = FALSE)
if(icon_icon && button_icon_state && ((current_button.button_icon_state != button_icon_state) || force))
@@ -157,28 +129,67 @@
/datum/action/proc/OnUpdatedIcon()
SIGNAL_HANDLER
- UpdateButtonIcon()
+ UpdateButtons()
-//Adds our action button to the screen of another player
-/datum/action/proc/Share(mob/freeloader)
- if(!freeloader.client)
+//Give our action button to the player
+/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
- sharers += WEAKREF(freeloader)
- freeloader.client.screen += button
- freeloader.actions += src
- freeloader.update_action_buttons()
-//Removes our action button from the screen of another player
-/datum/action/proc/Unshare(mob/freeloader)
- if(!freeloader.client)
+ LAZYOR(viewer.actions, src) // Move this in
+ ShowTo(viewer)
+
+//Adds our action button to the screen of a player
+/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
- for(var/freeloader_reference in sharers)
- if(IS_WEAKREF_OF(freeloader, freeloader_reference))
- sharers -= freeloader_reference
- break
- freeloader.client.screen -= button
- freeloader.actions -= src
- freeloader.update_action_buttons()
+
+ var/atom/movable/screen/movable/action_button/button = CreateButton()
+ 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 button from the screen of a player
+/datum/action/proc/HideFrom(mob/viewer)
+ var/datum/hud/our_hud = viewer.hud_used
+ var/atom/movable/screen/movable/action_button/button = viewers[our_hud]
+ LAZYREMOVE(viewer.actions, src)
+ if(button)
+ qdel(button)
+
+/datum/action/proc/CreateButton()
+ var/atom/movable/screen/movable/action_button/button = new()
+ button.linked_action = src
+ button.name = name
+ button.actiontooltipstyle = buttontooltipstyle
+ if(desc)
+ button.desc = desc
+ return button
+
+/datum/action/proc/SetId(atom/movable/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/atom/movable/screen/movable/action_button/button = action.viewers[owner.hud_used]
+ if(action.name == name && button.id)
+ bitfield |= button.id
+
+ 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
//Presets for item actions
/datum/action/item_action
@@ -274,12 +285,14 @@
/datum/action/item_action/set_internals
name = "Set Internals"
-/datum/action/item_action/set_internals/UpdateButtonIcon(status_only = FALSE, force)
- if(..()) //button available
- if(iscarbon(owner))
- var/mob/living/carbon/C = owner
- if(target == C.internal)
- button.icon_state = "template_active"
+/datum/action/item_action/set_internals/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force)
+ if(!..()) // no button available
+ return
+ if(!iscarbon(owner))
+ return
+ var/mob/living/carbon/C = owner
+ if(target == C.internal)
+ button.icon_state = "template_active"
/datum/action/item_action/pick_color
name = "Choose A Color"
@@ -333,7 +346,7 @@
SIGNAL_HANDLER
button_icon_state = "thermal_[suit.thermal_on ? "on" : "off"]"
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/item_action/vortex_recall
name = "Vortex Recall"
@@ -387,7 +400,6 @@
..()
var/obj/item/item_target = target
name = "Toggle [item_target.name]"
- button.name = name
/datum/action/item_action/halt
name = "HALT!"
@@ -413,7 +425,6 @@
..()
var/obj/item/item_target = target
name = "Adjust [item_target.name]"
- button.name = name
/datum/action/item_action/switch_hud
name = "Switch HUD"
@@ -499,13 +510,11 @@
..()
var/obj/item/organ/organ_target = target
name = "Toggle [organ_target.name]"
- button.name = name
/datum/action/item_action/organ_action/use/New(Target)
..()
var/obj/item/organ/organ_target = target
name = "Use [organ_target.name]"
- button.name = name
/datum/action/item_action/cult_dagger
name = "Draw Blood Rune"
@@ -514,15 +523,13 @@
button_icon_state = "draw"
buttontooltipstyle = "cult"
background_icon_state = "bg_demon"
+ default_button_position = "6:157,4:-2"
/datum/action/item_action/cult_dagger/Grant(mob/M)
if(!IS_CULTIST(M))
Remove(owner)
return
-
- . = ..()
- button.screen_loc = "6:157,4:-2"
- button.moved = "6:157,4:-2"
+ return ..()
/datum/action/item_action/cult_dagger/Trigger(trigger_flags)
for(var/obj/item/held_item as anything in owner.held_items) // In case we were already holding a dagger
@@ -617,7 +624,6 @@
icon_icon = S.action_icon
button_icon_state = S.action_icon_state
background_icon_state = S.action_background_icon_state
- button.name = name
/datum/action/spell_action/Destroy()
var/obj/effect/proc_holder/S = target
@@ -695,13 +701,14 @@
// Shares cooldowns with other cooldown abilities of the same value, not active if null
var/shared_cooldown
-/datum/action/cooldown/New(Target)
- ..()
+/datum/action/cooldown/CreateButton()
+ var/atom/movable/screen/movable/action_button/button = ..()
button.maptext = ""
button.maptext_x = 8
button.maptext_y = 0
button.maptext_width = 24
button.maptext_height = 12
+ return button
/datum/action/cooldown/IsAvailable()
return ..() && (next_use_time <= world.time)
@@ -723,7 +730,7 @@
next_use_time = world.time + override_cooldown_time
else
next_use_time = world.time + cooldown_time
- UpdateButtonIcon()
+ UpdateButtons()
START_PROCESSING(SSfastprocess, src)
/datum/action/cooldown/Trigger(trigger_flags, atom/target)
@@ -741,7 +748,7 @@
else
owner.click_intercept = src
for(var/datum/action/cooldown/ability in owner.actions)
- ability.UpdateButtonIcon()
+ ability.UpdateButtons()
return TRUE
return PreActivate(owner)
@@ -766,12 +773,13 @@
/datum/action/cooldown/proc/Activate(atom/target)
return
-/datum/action/cooldown/UpdateButtonIcon(status_only = FALSE, force = FALSE)
+/datum/action/cooldown/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force = FALSE)
. = ..()
+ if(!button)
+ return
var/time_left = max(next_use_time - world.time, 0)
- if(button)
- if(text_cooldown)
- button.maptext = MAPTEXT("[round(time_left/10, 0.1)]")
+ if(text_cooldown)
+ button.maptext = MAPTEXT("[round(time_left/10, 0.1)]")
if(!owner || time_left == 0)
button.maptext = ""
if(IsAvailable() && owner.click_intercept == src)
@@ -781,28 +789,28 @@
var/time_left = max(next_use_time - world.time, 0)
if(!owner || time_left == 0)
STOP_PROCESSING(SSfastprocess, src)
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/cooldown/Grant(mob/M)
..()
- if(owner)
- UpdateButtonIcon()
- if(next_use_time > world.time)
- START_PROCESSING(SSfastprocess, src)
+ if(!owner)
+ return
+ UpdateButtons()
+ if(next_use_time > world.time)
+ START_PROCESSING(SSfastprocess, src)
///Like a cooldown action, but with an associated proc holder.
/datum/action/cooldown/spell_like
/datum/action/cooldown/spell_like/New(Target)
..()
- var/obj/effect/proc_holder/our_proc_holder = target
+ var/obj/effect/proc_holder/our_proc_holder = Target
our_proc_holder.action = src
name = our_proc_holder.name
desc = our_proc_holder.desc
icon_icon = our_proc_holder.action_icon
button_icon_state = our_proc_holder.action_icon_state
background_icon_state = our_proc_holder.action_background_icon_state
- button.name = name
/datum/action/cooldown/spell_like/Activate(atom/activate_target)
if(!target)
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index a5c7710b067..7b5d6760ec8 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -275,7 +275,7 @@
name = "Hide"
desc = "Hide yourself from your owner's sight."
button_icon_state = "hide"
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/innate/imaginary_hide/Activate()
var/mob/camera/imaginary_friend/I = owner
diff --git a/code/datums/components/admin_popup.dm b/code/datums/components/admin_popup.dm
index 0fff57863e3..c552c596c80 100644
--- a/code/datums/components/admin_popup.dm
+++ b/code/datums/components/admin_popup.dm
@@ -49,7 +49,7 @@
admin_popup = new
var/client/parent_client = parent
- admin_popup.maptext_width = getviewsize(parent_client.view_size.getView())[1] * world.icon_size
+ admin_popup.maptext_width = view_to_pixels(parent_client.view_size.getView())[1]
parent_client.screen += admin_popup
/datum/component/admin_popup/proc/delete_self()
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index 9de207a6423..85d144ea857 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -141,7 +141,7 @@
var/obj/effect/proc_holder/proc_holder = ability
if(!proc_holder.action)
return
- proc_holder.action.Share(rider)
+ proc_holder.action.GiveAction(rider)
/// Takes away the riding parent's abilities from the rider
/datum/component/riding/creature/proc/remove_abilities(mob/living/rider)
@@ -156,7 +156,7 @@
return
if(rider == proc_holder.ranged_ability_user)
proc_holder.remove_ranged_ability()
- proc_holder.action.Unshare(rider)
+ proc_holder.action.HideFrom(rider)
/datum/component/riding/creature/riding_can_z_move(atom/movable/movable_parent, direction, turf/start, turf/destination, z_move_flags, mob/living/rider)
if(!(z_move_flags & ZMOVE_CAN_FLY_CHECKS))
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 7f7467f0e0c..e75cfb5d479 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -841,7 +841,7 @@
if(istype(S, type))
continue
S.charge_counter = delay
- S.updateButtonIcon()
+ S.updateButtons()
INVOKE_ASYNC(S, /obj/effect/proc_holder/spell.proc/start_recharge)
/datum/mind/proc/get_ghost(even_if_they_cant_reenter, ghosts_with_clients)
diff --git a/code/datums/view.dm b/code/datums/view.dm
index dffbc90a4f2..e8b6c1cfe82 100644
--- a/code/datums/view.dm
+++ b/code/datums/view.dm
@@ -33,11 +33,13 @@
default = string
apply()
-/datum/view_data/proc/safeApplyFormat()
+/datum/view_data/proc/afterViewChange()
if(isZooming())
assertFormat()
- return
- resetFormat()
+ else
+ resetFormat()
+ var/datum/hud/our_hud = chief?.mob?.hud_used
+ our_hud.view_audit_buttons() // Make sure our hud's buttons are in our new size
/datum/view_data/proc/assertFormat()//T-Pose
winset(chief, "mapwindow.map", "zoom=0")
@@ -99,7 +101,7 @@
/datum/view_data/proc/apply()
chief?.change_view(getView())
- safeApplyFormat()
+ afterViewChange()
/datum/view_data/proc/supress()
is_suppressed = TRUE
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 2131fb4f671..b4aea6d7c81 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1285,7 +1285,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
*/
/obj/item/proc/update_action_buttons(status_only = FALSE, force = FALSE)
for(var/datum/action/current_action as anything in actions)
- current_action.UpdateButtonIcon(status_only, force)
+ current_action.UpdateButtons(status_only, force)
// Update icons if this is being carried by a mob
/obj/item/wash(clean_types)
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index a9b9b78015d..e6d4293a25d 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -105,13 +105,13 @@
to_chat(owner, span_notice("You will now fold origami planes."))
button_icon_state = "origami_on"
active = TRUE
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/innate/origami/Deactivate()
to_chat(owner, span_notice("You will no longer fold origami planes."))
button_icon_state = "origami_off"
active = FALSE
- UpdateButtonIcon()
+ UpdateButtons()
///SPELLS///
diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm
index 92fdc82f23d..4a265d882be 100644
--- a/code/game/objects/structures/deployable_turret.dm
+++ b/code/game/objects/structures/deployable_turret.dm
@@ -114,7 +114,7 @@
var/client/controlling_client = controller.client
if(controlling_client)
var/modifiers = params2list(controlling_client.mouseParams)
- var/atom/target_atom = controlling_client.mouseObject
+ var/atom/target_atom = controlling_client.mouse_object_ref?.resolve()
var/turf/target_turf = get_turf(target_atom)
if(istype(target_turf)) //They're hovering over something in the map.
direction_track(controller, target_turf)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_spells/spell_edit_menu.dm b/code/modules/admin/verbs/SDQL2/SDQL_spells/spell_edit_menu.dm
index 3bffae75958..7c74c38ca70 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_spells/spell_edit_menu.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_spells/spell_edit_menu.dm
@@ -404,7 +404,7 @@ GLOBAL_LIST_INIT_TYPED(sdql_spells, /obj/effect/proc_holder/spell, list())
if("confirm")
if(target_spell)
reassign_vars(target_spell)
- target_spell.action.UpdateButtonIcon()
+ target_spell.action.UpdateButtons()
log_admin("[key_name(user)] edited the SDQL spell \"[target_spell]\" owned by [key_name(target_mob)].")
else
var/new_spell = give_spell()
@@ -895,7 +895,7 @@ GLOBAL_LIST_INIT_TYPED(sdql_spells, /obj/effect/proc_holder/spell, list())
var/obj/effect/proc_holder/spell/new_spell = new path(null, target_mob, user.ckey)
GLOB.sdql_spells += new_spell
reassign_vars(new_spell)
- new_spell.action.UpdateButtonIcon()
+ new_spell.action.UpdateButtons()
if(target_mob.mind)
target_mob.mind.AddSpell(new_spell)
else
diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm
index 0e1e86baa8f..84aae218a10 100644
--- a/code/modules/antagonists/changeling/cellular_emporium.dm
+++ b/code/modules/antagonists/changeling/cellular_emporium.dm
@@ -95,7 +95,6 @@
/datum/action/innate/cellular_emporium/New(our_target)
. = ..()
- button.name = name
if(istype(our_target, /datum/cellular_emporium))
cellular_emporium = our_target
else
diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm
index 32c29c05105..835e13cfb36 100644
--- a/code/modules/antagonists/changeling/powers/fakedeath.dm
+++ b/code/modules/antagonists/changeling/powers/fakedeath.dm
@@ -18,7 +18,7 @@
name = "Reviving Stasis"
desc = "We fall into a stasis, allowing us to regenerate and trick our enemies."
button_icon_state = "fake_death"
- UpdateButtonIcon()
+ UpdateButtons()
chemical_cost = 15
to_chat(user, span_notice("We have revived ourselves."))
else
@@ -58,7 +58,7 @@
name = "Revive"
desc = "We arise once more."
button_icon_state = "revive"
- UpdateButtonIcon()
+ UpdateButtons()
chemical_cost = 0
revive_ready = TRUE
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 87546b499f3..fd334ab9d7b 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -2,15 +2,10 @@
name = "Prepare Blood Magic"
button_icon_state = "carve"
desc = "Prepare blood magic by carving runes into your flesh. This is easier with an empowering rune."
+ default_button_position = DEFAULT_BLOODSPELLS
var/list/spells = list()
var/channeling = FALSE
-/datum/action/innate/cult/blood_magic/Grant()
- ..()
- button.screen_loc = DEFAULT_BLOODSPELLS
- button.moved = DEFAULT_BLOODSPELLS
- button.ordered = FALSE
-
/datum/action/innate/cult/blood_magic/Remove()
for(var/X in spells)
qdel(X)
@@ -22,15 +17,21 @@
return ..()
/datum/action/innate/cult/blood_magic/proc/Positioning()
- var/list/screen_loc_split = splittext(button.screen_loc,",")
- var/list/screen_loc_X = splittext(screen_loc_split[1],":")
- var/list/screen_loc_Y = splittext(screen_loc_split[2],":")
- var/pix_X = text2num(screen_loc_X[2])
- for(var/datum/action/innate/cult/blood_spell/B in spells)
- if(B.button.locked)
- var/order = pix_X+spells.Find(B)*31
- B.button.screen_loc = "[screen_loc_X[1]]:[order],[screen_loc_Y[1]]:[screen_loc_Y[2]]"
- B.button.moved = B.button.screen_loc
+ for(var/datum/hud/hud as anything in viewers)
+ var/our_view = hud.mymob?.client?.view || "15x15"
+ var/atom/movable/screen/movable/action_button/button = viewers[hud]
+ var/position = screen_loc_to_offset(button.screen_loc)
+ var/spells_iterated = 0
+ for(var/datum/action/innate/cult/blood_spell/blood_spell in spells)
+ spells_iterated += 1
+ if(blood_spell.positioned)
+ continue
+ var/atom/movable/screen/movable/action_button/moving_button = blood_spell.viewers[hud]
+ if(!moving_button)
+ continue
+ var/our_x = position[1] + spells_iterated * world.icon_size // Offset any new buttons into our list
+ hud.position_action(moving_button, offset_to_screen_loc(our_x, position[2], our_view))
+ blood_spell.positioned = TRUE
/datum/action/innate/cult/blood_magic/Activate()
var/rune = FALSE
@@ -97,6 +98,8 @@
var/base_desc //To allow for updating tooltips
var/invocation
var/health_cost = 0
+ /// Have we already been positioned into our starting location?
+ var/positioned = FALSE
/datum/action/innate/cult/blood_spell/Grant(mob/living/owner, datum/action/innate/cult/blood_magic/BM)
if(health_cost)
@@ -104,9 +107,7 @@
base_desc = desc
desc += "
Has [charges] use\s remaining."
all_magic = BM
- ..()
- button.locked = TRUE
- button.ordered = FALSE
+ return ..()
/datum/action/innate/cult/blood_spell/Remove()
if(all_magic)
@@ -275,7 +276,7 @@
attached_action.charges--
attached_action.desc = attached_action.base_desc
attached_action.desc += "
Has [attached_action.charges] use\s remaining."
- attached_action.UpdateButtonIcon()
+ attached_action.UpdateButtons()
if(attached_action.charges <= 0)
remove_ranged_ability(span_cult("You have exhausted the spell's power!"))
qdel(src)
@@ -331,7 +332,7 @@
qdel(src)
desc = base_desc
desc += "
Has [charges] use\s remaining."
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/innate/cult/blood_spell/manipulation
name = "Blood Rites"
@@ -380,7 +381,7 @@
source.charges = uses
source.desc = source.base_desc
source.desc += "
Has [uses] use\s remaining."
- source.UpdateButtonIcon()
+ source.UpdateButtons()
return ..()
/obj/item/melee/blood_magic/attack_self(mob/living/user)
@@ -409,7 +410,7 @@
else if(source)
source.desc = source.base_desc
source.desc += "
Has [uses] use\s remaining."
- source.UpdateButtonIcon()
+ source.UpdateButtons()
//Stun
/obj/item/melee/blood_magic/stun
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 3d54936f0fd..c4f7758c0c6 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -805,14 +805,13 @@ Striking a noncultist, however, will tear their flesh."}
desc = "Call the bloody halberd back to your hand!"
background_icon_state = "bg_demon"
button_icon_state = "bloodspear"
+ default_button_position = "6:157,4:-2"
var/obj/item/melee/cultblade/halberd/halberd
var/cooldown = 0
/datum/action/innate/cult/halberd/Grant(mob/user, obj/blood_halberd)
. = ..()
halberd = blood_halberd
- button.screen_loc = "6:157,4:-2"
- button.moved = "6:157,4:-2"
/datum/action/innate/cult/halberd/Activate()
if(owner == halberd.loc || cooldown > world.time)
diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm
index a619e242376..8609a0d96de 100644
--- a/code/modules/antagonists/heretic/heretic_living_heart.dm
+++ b/code/modules/antagonists/heretic/heretic_living_heart.dm
@@ -43,7 +43,7 @@
organ_parent.icon = 'icons/obj/eldritch.dmi'
organ_parent.icon_state = "living_heart"
- action.UpdateButtonIcon()
+ action.UpdateButtons()
/datum/component/living_heart/Destroy(force, silent)
QDEL_NULL(action)
diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm
index 24bc2fcfa56..36dd5c7c52e 100644
--- a/code/modules/antagonists/revenant/revenant_abilities.dm
+++ b/code/modules/antagonists/revenant/revenant_abilities.dm
@@ -189,7 +189,7 @@
user.reveal(reveal)
user.stun(stun)
if(action)
- action.UpdateButtonIcon()
+ action.UpdateButtons()
return TRUE
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index c17da72e1d3..2acb79cb7d2 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -430,7 +430,6 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/action/innate/ai/ranged/override_machine/New()
..()
desc = "[desc] It has [uses] use\s remaining."
- button.desc = desc
/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M)
if(M && !QDELETED(M))
@@ -458,7 +457,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
attached_action.adjust_uses(-1)
if(attached_action?.uses)
attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining."
- attached_action.UpdateButtonIcon()
+ attached_action.UpdateButtons()
target.audible_message(span_userdanger("You hear a loud electrical buzzing sound coming from [target]!"))
addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep!
remove_ranged_ability(span_danger("Sending override signal..."))
@@ -508,7 +507,6 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/action/innate/ai/ranged/overload_machine/New()
..()
desc = "[desc] It has [uses] use\s remaining."
- button.desc = desc
/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M)
if(M && !QDELETED(M))
@@ -541,7 +539,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
attached_action.adjust_uses(-1)
if(attached_action?.uses)
attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining."
- attached_action.UpdateButtonIcon()
+ attached_action.UpdateButtons()
target.audible_message(span_userdanger("You hear a loud electrical buzzing sound coming from [target]!"))
addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom!
remove_ranged_ability(span_danger("Overcharging machine..."))
@@ -566,7 +564,6 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/action/innate/ai/blackout/New()
..()
desc = "[desc] It has [uses] use\s remaining."
- button.desc = desc
/datum/action/innate/ai/blackout/Activate()
for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
@@ -577,9 +574,10 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
to_chat(owner, span_notice("Overcurrent applied to the powernet."))
owner.playsound_local(owner, SFX_SPARKS, 50, 0)
adjust_uses(-1)
- if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
- desc = "[initial(desc)] It has [uses] use\s remaining."
- UpdateButtonIcon()
+ if(QDELETED(src) || uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
+ return
+ desc = "[initial(desc)] It has [uses] use\s remaining."
+ UpdateButtons()
/// HIGH IMPACT HONKING
/datum/ai_module/destructive/megahonk
@@ -785,7 +783,6 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/action/innate/ai/reactivate_cameras/New()
..()
desc = "[desc] It has [uses] use\s remaining."
- button.desc = desc
/datum/action/innate/ai/reactivate_cameras/Activate()
var/fixed_cameras = 0
@@ -801,9 +798,10 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
to_chat(owner, span_notice("Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses]."))
owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0)
adjust_uses(0, TRUE) //Checks the uses remaining
- if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
- desc = "[initial(desc)] It has [uses] use\s remaining."
- UpdateButtonIcon()
+ if(QDELETED(src) || !uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
+ return
+ desc = "[initial(desc)] It has [uses] use\s remaining."
+ UpdateButtons()
/// Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision.
/datum/ai_module/upgrade/upgrade_cameras
diff --git a/code/modules/antagonists/traitor/equipment/module_picker.dm b/code/modules/antagonists/traitor/equipment/module_picker.dm
index c11ace042d3..001f4434652 100644
--- a/code/modules/antagonists/traitor/equipment/module_picker.dm
+++ b/code/modules/antagonists/traitor/equipment/module_picker.dm
@@ -120,6 +120,6 @@
else //Adding uses to an existing module
action.uses += initial(action.uses)
action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining."
- action.UpdateButtonIcon()
+ action.UpdateButtons()
processing_time -= AM.cost
SSblackbox.record_feedback("nested tally", "malfunction_modules_bought", 1, list("[initial(AM.name)]", "[AM.cost]"))
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index fe324b6d665..2d32aa99586 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -166,14 +166,15 @@
var/obj/item/active_mousedown_item = null
///Used in MouseDrag to preserve the original mouse click parameters
var/mouseParams = ""
- ///Used in MouseDrag to preserve the last mouse-entered location.
- var/mouseLocation = null
- ///Used in MouseDrag to preserve the last mouse-entered object.
- var/mouseObject = null
+ ///Used in MouseDrag to preserve the last mouse-entered location. Weakref
+ var/datum/weakref/mouse_location_ref = null
+ ///Used in MouseDrag to preserve the last mouse-entered object. Weakref
+ var/datum/weakref/mouse_object_ref
//Middle-mouse-button click dragtime control for aimbot exploit detection.
var/middragtime = 0
- //Middle-mouse-button clicked object control for aimbot exploit detection.
- var/atom/middragatom
+ //Middle-mouse-button clicked object control for aimbot exploit detection. Weakref
+ var/datum/weakref/middle_drag_atom_ref
+
/// Messages currently seen by this client
var/list/seen_messages
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index b3af514063d..161e8420b0d 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -911,7 +911,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(dragged && !LAZYACCESS(modifiers, dragged)) //I don't know what's going on here, but I don't trust it
return
- if (object && object == middragatom && LAZYACCESS(modifiers, LEFT_CLICK))
+ if (object && IS_WEAKREF_OF(object, middle_drag_atom_ref) && LAZYACCESS(modifiers, LEFT_CLICK))
ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1)
var/mcl = CONFIG_GET(number/minute_click_limit)
@@ -960,6 +960,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else
winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED]")
+ SEND_SIGNAL(src, COMSIG_CLIENT_CLICK, object, location, control, params, usr)
+
..()
/client/proc/add_verbs_from_config()
diff --git a/code/modules/client/preferences/buttons_locked.dm b/code/modules/client/preferences/buttons_locked.dm
deleted file mode 100644
index b4f54ff10f8..00000000000
--- a/code/modules/client/preferences/buttons_locked.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-/datum/preference/toggle/buttons_locked
- category = PREFERENCE_CATEGORY_GAME_PREFERENCES
- savefile_key = "buttons_locked"
- savefile_identifier = PREFERENCE_PLAYER
- default_value = FALSE
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index 316d01015fc..54131e3928b 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -164,8 +164,8 @@
..()
/datum/action/item_action/chameleon/change/proc/initialize_disguises()
- if(button)
- button.name = "Change [chameleon_name] Appearance"
+ name = "Change [chameleon_name] Appearance"
+ UpdateButtons()
chameleon_blacklist |= typecacheof(target.type)
for(var/V in typesof(chameleon_type))
@@ -210,7 +210,7 @@
update_item(picked_item)
var/obj/item/thing = target
thing.update_slot_icon()
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/item_action/chameleon/change/proc/update_item(obj/item/picked_item)
var/atom/atom_target = target
@@ -326,8 +326,8 @@
agent_card.update_icon()
/datum/action/item_action/chameleon/change/id_trim/initialize_disguises()
- if(button)
- button.name = "Change [chameleon_name] Appearance"
+ name = "Change [chameleon_name] Appearance"
+ UpdateButtons()
chameleon_blacklist |= typecacheof(target.type)
for(var/trim_path in typesof(chameleon_type))
@@ -535,9 +535,9 @@
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
chameleon_action.random_look()
var/datum/action/item_action/chameleon/drone/togglehatmask/togglehatmask_action = new(src)
- togglehatmask_action.UpdateButtonIcon()
+ togglehatmask_action.UpdateButtons()
var/datum/action/item_action/chameleon/drone/randomise/randomise_action = new(src)
- randomise_action.UpdateButtonIcon()
+ randomise_action.UpdateButtons()
/obj/item/clothing/mask/chameleon
name = "gas mask"
@@ -592,9 +592,9 @@
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
chameleon_action.random_look()
var/datum/action/item_action/chameleon/drone/togglehatmask/togglehatmask_action = new(src)
- togglehatmask_action.UpdateButtonIcon()
+ togglehatmask_action.UpdateButtons()
var/datum/action/item_action/chameleon/drone/randomise/randomise_action = new(src)
- randomise_action.UpdateButtonIcon()
+ randomise_action.UpdateButtons()
/obj/item/clothing/mask/chameleon/drone/attack_self(mob/user)
to_chat(user, span_notice("[src] does not have a voice changer."))
diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
index 5b484f3522a..4592dec8e1f 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
@@ -210,7 +210,7 @@ Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien/neurotoxin/update_icon()
action.button_icon_state = "alien_neurotoxin_[active]"
- action.UpdateButtonIcon()
+ action.UpdateButtons()
return ..()
/obj/effect/proc_holder/alien/neurotoxin/InterceptClickOn(mob/living/caller, params, atom/target)
@@ -332,7 +332,7 @@ Doesn't work on other aliens/AI.*/
for(var/X in abilities)
var/obj/effect/proc_holder/alien/APH = X
if(APH.has_action)
- APH.action.UpdateButtonIcon()
+ APH.action.UpdateButtons()
return TRUE
/mob/living/carbon/alien/adjustPlasma(amount)
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index 0716b39bcb2..0f4d4629cb5 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -569,9 +569,9 @@
spark_system.start()
do_teleport(H, get_turf(H), 12, asoundin = 'sound/weapons/emitter2.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
last_teleport = world.time
- UpdateButtonIcon() //action icon looks unavailable
+ UpdateButtons() //action icon looks unavailable
//action icon looks available again
- addtimer(CALLBACK(src, .proc/UpdateButtonIcon), cooldown + 5)
+ addtimer(CALLBACK(src, .proc/UpdateButtons), cooldown + 5)
//honk
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 9dc4dbecea4..c178c10e849 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -69,7 +69,7 @@
Cannibalize_Body(H)
if(regenerate_limbs)
- regenerate_limbs.UpdateButtonIcon()
+ regenerate_limbs.UpdateButtons()
/datum/species/jelly/proc/Cannibalize_Body(mob/living/carbon/human/H)
var/list/limbs_to_consume = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) - H.get_missing_limbs()
@@ -475,9 +475,9 @@
/datum/species/jelly/luminescent/proc/update_slime_actions()
integrate_extract.update_name()
- integrate_extract.UpdateButtonIcon()
- extract_minor.UpdateButtonIcon()
- extract_major.UpdateButtonIcon()
+ integrate_extract.UpdateButtons()
+ extract_minor.UpdateButtons()
+ extract_major.UpdateButtons()
/datum/species/jelly/luminescent/proc/update_glow(mob/living/carbon/C, intensity)
if(intensity)
@@ -516,7 +516,7 @@
name = "Eject Extract"
desc = "Eject your current slime extract."
-/datum/action/innate/integrate_extract/UpdateButtonIcon(status_only, force)
+/datum/action/innate/integrate_extract/UpdateButtons(status_only, force)
var/datum/species/jelly/luminescent/species = target
if(!species || !species.current_extract)
button_icon_state = "slimeconsume"
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 594297d7dc0..3edba64ba94 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -797,7 +797,7 @@
if(mind)
for(var/S in mind.spell_list)
var/obj/effect/proc_holder/spell/spell = S
- spell.updateButtonIcon()
+ spell.updateButtons()
if(excess_healing)
INVOKE_ASYNC(src, .proc/emote, "gasp")
log_combat(src, src, "revived")
diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm
index 3a80ecdf5c6..d77dffa053f 100644
--- a/code/modules/mob/living/silicon/robot/robot_model.dm
+++ b/code/modules/mob/living/silicon/robot/robot_model.dm
@@ -500,7 +500,7 @@
if(!wash_audio.is_active())
wash_audio.start()
clean()
- UpdateButtonIcon()
+ UpdateButtons()
/// Start the process of disabling the buffer. Plays some effects, waits a bit, then finishes
/datum/action/toggle_buffer/proc/deactivate_wash()
@@ -530,7 +530,7 @@
var/mob/living/silicon/robot/robot_owner = owner
buffer_on = FALSE
robot_owner.remove_movespeed_modifier(/datum/movespeed_modifier/auto_wash)
- UpdateButtonIcon()
+ UpdateButtons()
/// Should we keep trying to activate our buffer, or did you fuck it up somehow
/datum/action/toggle_buffer/proc/allow_buffer_activate()
@@ -567,7 +567,7 @@
// We use more water doing this then mopping
reagents.remove_any(2) //reaction() doesn't use up the reagents
-/datum/action/toggle_buffer/UpdateButtonIcon(status_only = FALSE, force = FALSE)
+/datum/action/toggle_buffer/UpdateButtons(status_only = FALSE, force = FALSE)
if(buffer_on)
name = "De-Activate Auto-Wash"
button_icon_state = "deactivate_wash"
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 851cbffc910..fbe6f871d12 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -54,21 +54,18 @@
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
var/spellnum = 1
for(var/spell in construct_spells)
- var/the_spell = new spell(null)
- AddSpell(the_spell)
- var/obj/effect/proc_holder/spell/S = mob_spell_list[spellnum]
var/pos = 2+spellnum*31
if(construct_spells.len >= 4)
pos -= 31*(construct_spells.len - 4)
- S.action.button.screen_loc = "6:[pos],4:-2"
- S.action.button.moved = "6:[pos],4:-2"
+ var/obj/effect/proc_holder/spell/the_spell = new spell(null)
+ the_spell?.action.default_button_position ="6:[pos],4:-2"
+ AddSpell(the_spell)
spellnum++
if(runetype)
- our_rune = new runetype(src)
- our_rune.Grant(src)
var/pos = 2+spellnum*31
- our_rune.button.screen_loc = "6:[pos],4:-2"
- our_rune.button.moved = "6:[pos],4:-2"
+ our_rune = new runetype(src)
+ our_rune.default_button_position = "6:[pos],4:-2" // Set the default position to this random position
+ our_rune.Grant(src)
if(icon_state)
add_overlay("glow_[icon_state]_[theme]")
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 883148c4b20..facffb201ce 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -337,7 +337,7 @@
if(ishuman(living_target) && (living_target.stat != DEAD || !consumed_mobs[living_target.tag])) //if they're not dead, you can consume them anyway
consumed_mobs[living_target.tag] = TRUE
fed++
- lay_eggs_enriched.UpdateButtonIcon(TRUE)
+ lay_eggs_enriched.UpdateButtons(TRUE)
visible_message(span_danger("[src] sticks a proboscis into [living_target] and sucks a viscous substance out."),span_notice("You suck the nutriment out of [living_target], feeding you enough to lay a cluster of eggs."))
living_target.death() //you just ate them, they're dead.
else
@@ -403,7 +403,7 @@
/obj/effect/proc_holder/wrap/update_icon()
action.button_icon_state = "wrap_[active]"
- action.UpdateButtonIcon()
+ action.UpdateButtons()
return ..()
/obj/effect/proc_holder/wrap/Click()
@@ -486,7 +486,7 @@
new_eggs.faction = spider.faction
if(enriched)
spider.fed--
- UpdateButtonIcon(TRUE)
+ UpdateButtons(TRUE)
spider.is_busy = FALSE
spider.stop_automated_movement = FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index c8f26feeb26..b29de2f72bc 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -77,24 +77,32 @@ While using this makes the system rely on OnFire, it still gives options for tim
///The internal attack ID for the elite's OpenFire() proc to use
var/chosen_attack_num = 0
-/datum/action/innate/elite_attack/New()
- ..()
+/datum/action/innate/elite_attack/CreateButton()
+ var/atom/movable/screen/movable/action_button/button = ..()
button.maptext = ""
button.maptext_x = 8
button.maptext_y = 0
button.maptext_width = 24
button.maptext_height = 12
+ return button
/datum/action/innate/elite_attack/process()
if(owner == null)
STOP_PROCESSING(SSfastprocess, src)
qdel(src)
return
+ UpdateButtons()
+
+/datum/action/innate/elite_attack/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(status_only)
+ return
var/mob/living/simple_animal/hostile/asteroid/elite/elite_owner = owner
var/timeleft = max(elite_owner.ranged_cooldown - world.time, 0)
if(timeleft == 0)
button.maptext = ""
- UpdateButtonIcon()
else
button.maptext = "[round(timeleft/10, 0.1)]"
diff --git a/code/modules/mod/mod_actions.dm b/code/modules/mod/mod_actions.dm
index c49f43d85d7..b8634f35640 100644
--- a/code/modules/mod/mod_actions.dm
+++ b/code/modules/mod/mod_actions.dm
@@ -72,7 +72,7 @@
button_icon_state = "activate-ready"
if(!ai_action)
background_icon_state = "bg_tech"
- UpdateButtonIcon()
+ UpdateButtons()
addtimer(CALLBACK(src, .proc/reset_ready), 3 SECONDS)
return
var/obj/item/mod/control/mod = target
@@ -85,7 +85,7 @@
button_icon_state = initial(button_icon_state)
if(!ai_action)
background_icon_state = initial(background_icon_state)
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/item_action/mod/activate/ai
ai_action = TRUE
@@ -180,14 +180,14 @@
/datum/action/item_action/mod/pinned_module/proc/on_module_activate(datum/source)
SIGNAL_HANDLER
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/item_action/mod/pinned_module/proc/on_module_deactivate(datum/source)
SIGNAL_HANDLER
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/item_action/mod/pinned_module/proc/on_module_use(datum/source)
SIGNAL_HANDLER
- UpdateButtonIcon()
+ UpdateButtons()
diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm
index bb7d74b8f54..2b405ec95f7 100644
--- a/code/modules/power/singularity/emitter.dm
+++ b/code/modules/power/singularity/emitter.dm
@@ -460,7 +460,7 @@
for(var/obj/item/item in buckled_mob.held_items)
if(istype(item, /obj/item/turret_control))
qdel(item)
- UpdateButtonIcon()
+ UpdateButtons()
return
playsound(proto_emitter,'sound/mecha/mechmove01.ogg', 50, TRUE)
name = "Switch to Automatic Firing"
@@ -477,7 +477,7 @@
else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand
var/obj/item/turret_control/turret_control = new /obj/item/turret_control()
buckled_mob.put_in_hands(turret_control)
- UpdateButtonIcon()
+ UpdateButtons()
/obj/item/turret_control
diff --git a/code/modules/projectiles/guns/energy/beam_rifle.dm b/code/modules/projectiles/guns/energy/beam_rifle.dm
index 1942417a1f2..01a2e87700d 100644
--- a/code/modules/projectiles/guns/energy/beam_rifle.dm
+++ b/code/modules/projectiles/guns/energy/beam_rifle.dm
@@ -196,7 +196,9 @@
else
P.color = rgb(0, 255, 0)
var/turf/curloc = get_turf(src)
- var/turf/targloc = get_turf(current_user.client.mouseObject)
+
+ var/atom/target_atom = current_user.client.mouse_object_ref?.resolve()
+ var/turf/targloc = get_turf(target_atom)
if(!istype(targloc))
if(!istype(curloc))
return
@@ -293,7 +295,9 @@
process_aim()
if(aiming_time_left <= aiming_time_fire_threshold && check_user())
sync_ammo()
- afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
+ var/atom/target = M.client.mouse_object_ref?.resolve()
+ if(target)
+ afterattack(target, M, FALSE, M.client.mouseParams, passthrough = TRUE)
stop_aiming()
QDEL_LIST(current_tracers)
return ..()
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index c2a5f2c13e9..af587e41b02 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -949,12 +949,10 @@
var/ty = (text2num(screen_loc_Y[1]) - 1) * world.icon_size + text2num(screen_loc_Y[2])
//Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average.
- var/list/screenview = getviewsize(user.client.view)
- var/screenviewX = screenview[1] * world.icon_size
- var/screenviewY = screenview[2] * world.icon_size
+ var/list/screenview = view_to_pixels(user.client.view)
- var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x
- var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y
+ var/ox = round(screenview[1] / 2) - user.client.pixel_x //"origin" x
+ var/oy = round(screenview[2] / 2) - user.client.pixel_y //"origin" y
angle = ATAN2(tx - oy, ty - ox)
return list(angle, p_x, p_y)
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index de944eb4ca6..5501eb73d2c 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -231,7 +231,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
if("holdervar")
adjust_var(user, holder_var_type, holder_var_amount)
if(action)
- action.UpdateButtonIcon()
+ action.UpdateButtons()
return TRUE
/obj/effect/proc_holder/spell/proc/charge_check(mob/user, silent = FALSE)
@@ -304,7 +304,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
if(recharging && charge_type == "recharge" && (charge_counter < charge_max))
charge_counter += delta_time * 10
if(charge_counter >= charge_max)
- action.UpdateButtonIcon()
+ action.UpdateButtons()
charge_counter = charge_max
recharging = FALSE
@@ -321,7 +321,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
cast(targets,user=user)
after_cast(targets)
if(action)
- action.UpdateButtonIcon()
+ action.UpdateButtons()
/obj/effect/proc_holder/spell/proc/before_cast(list/targets)
if(overlay)
@@ -382,7 +382,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
if("holdervar")
adjust_var(user, holder_var_type, -holder_var_amount)
if(action)
- action.UpdateButtonIcon()
+ action.UpdateButtons()
/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types
if (!istype(target))
@@ -507,8 +507,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
perform(targets,user=user)
-/obj/effect/proc_holder/spell/proc/updateButtonIcon(status_only, force)
- action.UpdateButtonIcon(status_only, force)
+/obj/effect/proc_holder/spell/proc/updateButtons(status_only, force)
+ action.UpdateButtons(status_only, force)
/obj/effect/proc_holder/spell/proc/can_be_cast_by(mob/caster)
if((human_req || clothes_req) && !ishuman(caster))
diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm
index 8e35be95735..c8432f4bb1d 100644
--- a/code/modules/spells/spell_types/aimed.dm
+++ b/code/modules/spells/spell_types/aimed.dm
@@ -46,7 +46,7 @@
. = ..()
action.button_icon_state = "[base_icon_state][active]"
- action.UpdateButtonIcon()
+ action.UpdateButtons()
/obj/effect/proc_holder/spell/aimed/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
diff --git a/code/modules/spells/spell_types/pointed/pointed.dm b/code/modules/spells/spell_types/pointed/pointed.dm
index bf5d818f2ce..94676ba64ac 100644
--- a/code/modules/spells/spell_types/pointed/pointed.dm
+++ b/code/modules/spells/spell_types/pointed/pointed.dm
@@ -62,7 +62,7 @@
. = ..()
action.button_icon_state = "[action_icon_state][active ? 1 : null]"
- action.UpdateButtonIcon()
+ action.UpdateButtons()
/obj/effect/proc_holder/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm
index d492d0d792b..6267706074f 100644
--- a/code/modules/spells/spell_types/touch_attacks.dm
+++ b/code/modules/spells/spell_types/touch_attacks.dm
@@ -25,7 +25,7 @@
//Start recharging.
attached_hand = null
recharging = TRUE
- action.UpdateButtonIcon()
+ action.UpdateButtons()
/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr)
if(!QDELETED(attached_hand))
diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm
index 1fef114b341..ece7c70b8ed 100644
--- a/code/modules/surgery/dental_implant.dm
+++ b/code/modules/surgery/dental_implant.dm
@@ -23,7 +23,8 @@
user.transferItemToLoc(tool, target, TRUE)
var/datum/action/item_action/hands_free/activate_pill/pill_action = new(tool)
- pill_action.button.name = "Activate [tool.name]"
+ pill_action.name = "Activate [tool.name]"
+ pill_action.UpdateButtons()
pill_action.target = tool
pill_action.Grant(target) //The pill never actually goes in an inventory slot, so the owner doesn't inherit actions from it
diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm
index 11ab9bb803b..83bbe5f2995 100644
--- a/code/modules/tooltip/tooltip.dm
+++ b/code/modules/tooltip/tooltip.dm
@@ -87,13 +87,13 @@ Notes:
/datum/tooltip/proc/hide()
+ queueHide = showing ? TRUE : FALSE
+
if (queueHide)
addtimer(CALLBACK(src, .proc/do_hide), 1)
else
do_hide()
- queueHide = showing ? TRUE : FALSE
-
return TRUE
/datum/tooltip/proc/do_hide()
diff --git a/code/modules/tooltip/tooltip.html b/code/modules/tooltip/tooltip.html
index b4d858da51b..5be29994cf1 100644
--- a/code/modules/tooltip/tooltip.html
+++ b/code/modules/tooltip/tooltip.html
@@ -130,21 +130,29 @@
//alert(realIconSize + ' | ' +tooltip.tileSize + ' | ' + resizeRatio); //DEBUG
- //Parse out the tile and cursor locations from params (e.g. "icon-x=32;icon-y=29;screen-loc=3:10,15:29")
+ const parameters = new Object();
+
+ //Parse out the contents of params (e.g. "icon-x=32;icon-y=29;screen-loc=3:10,15:29")
+ //It is worth noting that params is not always ordered in the same way. We therefore need to write the code
+ //To load their values in independantly of their order
var paramsA = tooltip.params.cursor.split(';');
- if (paramsA.length < 3) {return false;} //Sometimes screen-loc is never sent ahaha fuck you byond
+ for (var i = 0; i < paramsA.length; i++) {
+ var entry = paramsA[i];
+ var nameAndValue = entry.split("=");
+ parameters[nameAndValue[0]] = nameAndValue[1];
+ }
+
+ //Sometimes screen-loc is never sent ahaha fuck you byond
+ if (!parameters["icon-x"] || !parameters["icon-y"] || !parameters["screen-loc"]) {
+ return false;
+ }
//icon-x
- var iconX = paramsA[0];
- iconX = iconX.split('=');
- iconX = parseInt(iconX[1]);
+ var iconX = parseInt(parameters["icon-x"]);
//icon-y
- var iconY = paramsA[1];
- iconY = iconY.split('=');
- iconY = parseInt(iconY[1]);
+ var iconY = parseInt(parameters["icon-y"]);
//screen-loc
- var screenLoc = paramsA[2];
- screenLoc = screenLoc.split('=');
- screenLoc = screenLoc[1].split(',');
+ var screenLoc = parameters["screen-loc"];
+ screenLoc = screenLoc.split(',');
if (screenLoc.length < 2) {return false;}
var left = screenLoc[0];
var top = screenLoc[1];
diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm
index 862f4fcdb8c..fc8f624e320 100644
--- a/code/modules/vehicles/mecha/combat/durand.dm
+++ b/code/modules/vehicles/mecha/combat/durand.dm
@@ -212,7 +212,7 @@ own integrity back to max. Shield is automatically dropped if we run out of powe
for(var/occupant in chassis.occupants)
var/datum/action/button = chassis.occupant_actions[occupant][/datum/action/vehicle/sealed/mecha/mech_defense_mode]
button.button_icon_state = "mech_defense_mode_[chassis.defense_mode ? "on" : "off"]"
- button.UpdateButtonIcon()
+ button.UpdateButtons()
set_light_on(chassis.defense_mode)
diff --git a/code/modules/vehicles/mecha/combat/gygax.dm b/code/modules/vehicles/mecha/combat/gygax.dm
index 43824350ee0..5f23d21b82f 100644
--- a/code/modules/vehicles/mecha/combat/gygax.dm
+++ b/code/modules/vehicles/mecha/combat/gygax.dm
@@ -44,7 +44,7 @@
chassis.movedelay = initial(chassis.movedelay)
chassis.step_energy_drain = chassis.normal_step_energy_drain
chassis.balloon_alert(owner, "you disable the overload")
- UpdateButtonIcon()
+ UpdateButtons()
/obj/vehicle/sealed/mecha/combat/gygax/dark
desc = "A lightweight exosuit, painted in a dark scheme. This model appears to have some modifications."
diff --git a/code/modules/vehicles/mecha/combat/marauder.dm b/code/modules/vehicles/mecha/combat/marauder.dm
index b0cfd022e0c..9e67af31838 100644
--- a/code/modules/vehicles/mecha/combat/marauder.dm
+++ b/code/modules/vehicles/mecha/combat/marauder.dm
@@ -69,7 +69,7 @@
SEND_SOUND(owner, sound('sound/mecha/imag_enh.ogg', volume=50))
else
owner.client.view_size.resetToDefault()
- UpdateButtonIcon()
+ UpdateButtons()
/obj/vehicle/sealed/mecha/combat/marauder/seraph
desc = "Heavy-duty, command-type exosuit. This is a custom model, utilized only by high-ranking military personnel."
diff --git a/code/modules/vehicles/mecha/combat/phazon.dm b/code/modules/vehicles/mecha/combat/phazon.dm
index 16dc0ce3555..643caf402b0 100644
--- a/code/modules/vehicles/mecha/combat/phazon.dm
+++ b/code/modules/vehicles/mecha/combat/phazon.dm
@@ -44,7 +44,7 @@
chassis.damtype = new_damtype
button_icon_state = "mech_damtype_[new_damtype]"
playsound(chassis, 'sound/mecha/mechmove01.ogg', 50, TRUE)
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/vehicle/sealed/mecha/mech_toggle_phasing
name = "Toggle Phasing"
@@ -56,4 +56,4 @@
chassis.phasing = chassis.phasing ? "" : "phasing"
button_icon_state = "mech_phasing_[chassis.phasing ? "on" : "off"]"
chassis.balloon_alert(owner, "[chassis.phasing ? "enabled" : "disabled"] phasing")
- UpdateButtonIcon()
+ UpdateButtons()
diff --git a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
index c50314d7a60..3b28c661952 100644
--- a/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
+++ b/code/modules/vehicles/mecha/combat/savannah_ivanov.dm
@@ -117,8 +117,8 @@
return
S_TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_SKYFALL, skyfall_cooldown_time)
button_icon_state = "mech_savannah_cooldown"
- UpdateButtonIcon()
- addtimer(CALLBACK(src, /datum/action/vehicle/sealed/mecha/skyfall.proc/reset_button_icon), skyfall_cooldown_time)
+ UpdateButtons()
+ addtimer(CALLBACK(src, .proc/reset_button_icon), skyfall_cooldown_time)
for(var/mob/living/shaken in range(7, chassis))
shake_camera(shaken, 3, 3)
@@ -219,7 +219,7 @@
*/
/datum/action/vehicle/sealed/mecha/skyfall/proc/reset_button_icon()
button_icon_state = "mech_savannah"
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/vehicle/sealed/mecha/ivanov_strike
name = "Ivanov Strike"
@@ -254,7 +254,7 @@
*/
/datum/action/vehicle/sealed/mecha/ivanov_strike/proc/reset_button_icon()
button_icon_state = "mech_ivanov"
- UpdateButtonIcon()
+ UpdateButtons()
/**
* ## start_missile_targeting
@@ -322,7 +322,7 @@
"explosionSize" = list(0,0,1,2)
))
button_icon_state = "mech_ivanov_cooldown"
- UpdateButtonIcon()
+ UpdateButtons()
addtimer(CALLBACK(src, /datum/action/vehicle/sealed/mecha/ivanov_strike.proc/reset_button_icon), strike_cooldown_time)
//misc effects
diff --git a/code/modules/vehicles/mecha/mecha_actions.dm b/code/modules/vehicles/mecha/mecha_actions.dm
index 266a84a51f9..5cbd0c8c57a 100644
--- a/code/modules/vehicles/mecha/mecha_actions.dm
+++ b/code/modules/vehicles/mecha/mecha_actions.dm
@@ -38,7 +38,7 @@
button_icon_state = "mech_internals_[chassis.use_internal_tank ? "on" : "off"]"
chassis.balloon_alert(owner, "taking air from [chassis.use_internal_tank ? "internal airtank" : "environment"]")
chassis.log_message("Now taking air from [chassis.use_internal_tank?"internal airtank":"environment"].", LOG_MECHA)
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/vehicle/sealed/mecha/mech_toggle_lights
name = "Toggle Lights"
@@ -60,7 +60,7 @@
chassis.balloon_alert(owner, "toggled lights [chassis.mecha_flags & LIGHTS_ON ? "on":"off"]")
playsound(chassis,'sound/machines/clockcult/brass_skewer.ogg', 40, TRUE)
chassis.log_message("Toggled lights [(chassis.mecha_flags & LIGHTS_ON)?"on":"off"].", LOG_MECHA)
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/vehicle/sealed/mecha/mech_view_stats
name = "View Stats"
@@ -104,7 +104,7 @@
for(var/occupant in occupants)
var/datum/action/action = LAZYACCESSASSOC(occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/strafe)
- action?.UpdateButtonIcon()
+ action?.UpdateButtons()
///swap seats, for two person mecha
/datum/action/vehicle/sealed/mecha/swap_seat
diff --git a/code/modules/vehicles/mecha/working/clarke.dm b/code/modules/vehicles/mecha/working/clarke.dm
index 49454ce7402..7f55038ab17 100644
--- a/code/modules/vehicles/mecha/working/clarke.dm
+++ b/code/modules/vehicles/mecha/working/clarke.dm
@@ -86,10 +86,10 @@
return
var/mob/living/living_owner = owner
button_icon_state = "mech_search_ruins_cooldown"
- UpdateButtonIcon()
+ UpdateButtons()
COOLDOWN_START(src, search_cooldown, SEARCH_COOLDOWN)
addtimer(VARSET_CALLBACK(src, button_icon_state, "mech_search_ruins"), SEARCH_COOLDOWN)
- addtimer(CALLBACK(src, .proc/UpdateButtonIcon), SEARCH_COOLDOWN)
+ addtimer(CALLBACK(src, .proc/UpdateButtons), SEARCH_COOLDOWN)
var/obj/pinpointed_ruin
for(var/obj/effect/landmark/ruin/ruin_landmark as anything in GLOB.ruin_landmarks)
if(ruin_landmark.z != chassis.z)
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index e0244f5b017..2990ebe568e 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -226,12 +226,16 @@
src.circuit_component = circuit_component
- button.maptext_x = 2
- button.maptext_y = 0
- update_maptext()
+ UpdateButtons()
START_PROCESSING(SSobj, src)
+/datum/action/innate/bci_charge_action/CreateButton()
+ var/atom/movable/screen/movable/action_button/button = ..()
+ button.maptext_x = 2
+ button.maptext_y = 0
+ return button
+
/datum/action/innate/bci_charge_action/Destroy()
circuit_component.charge_action = null
circuit_component = null
@@ -250,9 +254,14 @@
to_chat(owner, span_info("You can recharge it by using a cyborg recharging station."))
/datum/action/innate/bci_charge_action/process(delta_time)
- update_maptext()
+ UpdateButtons()
-/datum/action/innate/bci_charge_action/proc/update_maptext()
+/datum/action/innate/bci_charge_action/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force = FALSE)
+ . = ..()
+ if(!.)
+ return
+ if(status_only)
+ return
var/obj/item/stock_parts/cell/cell = circuit_component.parent.cell
button.maptext = cell ? MAPTEXT("[cell.percent()]%") : ""
diff --git a/icons/effects/mouse_pointers/screen_drag.dmi b/icons/effects/mouse_pointers/screen_drag.dmi
new file mode 100644
index 00000000000..48e4fce85f7
Binary files /dev/null and b/icons/effects/mouse_pointers/screen_drag.dmi differ
diff --git a/icons/hud/64x16_actions.dmi b/icons/hud/64x16_actions.dmi
new file mode 100644
index 00000000000..ae81ace4d55
Binary files /dev/null and b/icons/hud/64x16_actions.dmi differ
diff --git a/icons/hud/actions.dmi b/icons/hud/actions.dmi
index 6e242104110..f4026594247 100644
Binary files a/icons/hud/actions.dmi and b/icons/hud/actions.dmi differ
diff --git a/icons/hud/screen_ai.dmi b/icons/hud/screen_ai.dmi
index 8388ea3f806..8f8d30c6d8b 100644
Binary files a/icons/hud/screen_ai.dmi and b/icons/hud/screen_ai.dmi differ
diff --git a/icons/hud/screen_alien.dmi b/icons/hud/screen_alien.dmi
index 307c41075ef..88791da700b 100644
Binary files a/icons/hud/screen_alien.dmi and b/icons/hud/screen_alien.dmi differ
diff --git a/icons/hud/screen_clockwork.dmi b/icons/hud/screen_clockwork.dmi
index 896bf43b662..a0aa0af594e 100644
Binary files a/icons/hud/screen_clockwork.dmi and b/icons/hud/screen_clockwork.dmi differ
diff --git a/icons/hud/screen_cyborg.dmi b/icons/hud/screen_cyborg.dmi
index dd288e6950a..0b17f099607 100644
Binary files a/icons/hud/screen_cyborg.dmi and b/icons/hud/screen_cyborg.dmi differ
diff --git a/icons/hud/screen_gen.dmi b/icons/hud/screen_gen.dmi
index a9281c36698..725b3074b6d 100644
Binary files a/icons/hud/screen_gen.dmi and b/icons/hud/screen_gen.dmi differ
diff --git a/icons/hud/screen_glass.dmi b/icons/hud/screen_glass.dmi
index 2012716ba89..4cbdd189da2 100644
Binary files a/icons/hud/screen_glass.dmi and b/icons/hud/screen_glass.dmi differ
diff --git a/icons/hud/screen_midnight.dmi b/icons/hud/screen_midnight.dmi
index 605b845efc2..e8e92921f92 100644
Binary files a/icons/hud/screen_midnight.dmi and b/icons/hud/screen_midnight.dmi differ
diff --git a/icons/hud/screen_operative.dmi b/icons/hud/screen_operative.dmi
index 88fcb5b98ea..2dd30571372 100644
Binary files a/icons/hud/screen_operative.dmi and b/icons/hud/screen_operative.dmi differ
diff --git a/icons/hud/screen_pai.dmi b/icons/hud/screen_pai.dmi
index a8856712fad..3af44930e0a 100644
Binary files a/icons/hud/screen_pai.dmi and b/icons/hud/screen_pai.dmi differ
diff --git a/icons/hud/screen_plasmafire.dmi b/icons/hud/screen_plasmafire.dmi
index 61a99bab974..2605c71431b 100644
Binary files a/icons/hud/screen_plasmafire.dmi and b/icons/hud/screen_plasmafire.dmi differ
diff --git a/icons/hud/screen_retro.dmi b/icons/hud/screen_retro.dmi
index d3574b8824f..8c974e304b0 100644
Binary files a/icons/hud/screen_retro.dmi and b/icons/hud/screen_retro.dmi differ
diff --git a/icons/hud/screen_slimecore.dmi b/icons/hud/screen_slimecore.dmi
index 96185ed3cf6..d5021ed3586 100644
Binary files a/icons/hud/screen_slimecore.dmi and b/icons/hud/screen_slimecore.dmi differ
diff --git a/modular_skyrat/master_files/code/modules/projectiles/guns/gun.dm b/modular_skyrat/master_files/code/modules/projectiles/guns/gun.dm
index aa61e7df40c..b33416952ce 100644
--- a/modular_skyrat/master_files/code/modules/projectiles/guns/gun.dm
+++ b/modular_skyrat/master_files/code/modules/projectiles/guns/gun.dm
@@ -135,7 +135,7 @@
if(fire_select_modes.len > 1)
firemode_action = new(src)
firemode_action.button_icon_state = "fireselect_[fire_select]"
- firemode_action.UpdateButtonIcon()
+ firemode_action.UpdateButtons()
/obj/item/gun/ComponentInitialize()
. = ..()
@@ -244,7 +244,7 @@
playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
update_appearance()
firemode_action.button_icon_state = "fireselect_[fire_select]"
- firemode_action.UpdateButtonIcon()
+ firemode_action.UpdateButtons()
SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD)
return TRUE
@@ -397,7 +397,7 @@
else
safety = !safety
tsafety.button_icon_state = "safety_[safety ? "on" : "off"]"
- tsafety.UpdateButtonIcon()
+ tsafety.UpdateButtons()
playsound(src, 'sound/weapons/empty.ogg', 100, TRUE)
user.visible_message(span_notice("[user] toggles [src]'s safety [safety ? "ON" : "OFF"]."),
span_notice("You toggle [src]'s safety [safety ? "ON" : "OFF"]."))
diff --git a/modular_skyrat/modules/hev_suit/code/hev_suit.dm b/modular_skyrat/modules/hev_suit/code/hev_suit.dm
index 7c826ffc7ef..a9dcb502b84 100644
--- a/modular_skyrat/modules/hev_suit/code/hev_suit.dm
+++ b/modular_skyrat/modules/hev_suit/code/hev_suit.dm
@@ -231,7 +231,7 @@
playsound(my_suit, 'modular_skyrat/master_files/sound/blackmesa/hev/blip.ogg', 50)
- UpdateButtonIcon()
+ UpdateButtons()
/datum/action/item_action/hev_toggle/Trigger(trigger_flags)
var/obj/item/clothing/suit/space/hev_suit/my_suit = target
@@ -249,7 +249,7 @@
playsound(my_suit, 'modular_skyrat/master_files/sound/blackmesa/hev/blip.ogg', 50)
- UpdateButtonIcon()
+ UpdateButtons()
/obj/item/clothing/suit/space/hev_suit/proc/send_message(message, color = HEV_COLOR_ORANGE)
if(send_notifications != HEV_NOTIFICATION_TEXT_AND_VOICE && send_notifications != HEV_NOTIFICATION_TEXT)
diff --git a/tgstation.dme b/tgstation.dme
index 2d0a9a629ab..aad53e2413b 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -282,6 +282,7 @@
#include "code\__DEFINES\~skyrat_defines\flavor_defines.dm"
#include "code\__DEFINES\~skyrat_defines\game_options.dm"
#include "code\__DEFINES\~skyrat_defines\gun.dm"
+#include "code\__DEFINES\~skyrat_defines\hud.dm"
#include "code\__DEFINES\~skyrat_defines\integrated_electronics.dm"
#include "code\__DEFINES\~skyrat_defines\interactions.dm"
#include "code\__DEFINES\~skyrat_defines\inventory.dm"
@@ -367,6 +368,7 @@
#include "code\__HELPERS\ref.dm"
#include "code\__HELPERS\roundend.dm"
#include "code\__HELPERS\sanitize_values.dm"
+#include "code\__HELPERS\screen_objs.dm"
#include "code\__HELPERS\shell.dm"
#include "code\__HELPERS\spatial_info.dm"
#include "code\__HELPERS\spawns.dm"
@@ -435,7 +437,6 @@
#include "code\_onclick\overmind.dm"
#include "code\_onclick\pai.dm"
#include "code\_onclick\telekinesis.dm"
-#include "code\_onclick\hud\_defines.dm"
#include "code\_onclick\hud\action_button.dm"
#include "code\_onclick\hud\ai.dm"
#include "code\_onclick\hud\alert.dm"
@@ -2490,7 +2491,6 @@
#include "code\modules\client\preferences\auto_fit_viewport.dm"
#include "code\modules\client\preferences\body_type.dm"
#include "code\modules\client\preferences\broadcast_login_logout.dm"
-#include "code\modules\client\preferences\buttons_locked.dm"
#include "code\modules\client\preferences\clothing.dm"
#include "code\modules\client\preferences\darkened_flash.dm"
#include "code\modules\client\preferences\fov_darkness.dm"