diff --git a/code/__DEFINES/_click.dm b/code/__DEFINES/_click.dm new file mode 100644 index 0000000000..4118fb873d --- /dev/null +++ b/code/__DEFINES/_click.dm @@ -0,0 +1,2 @@ +//The button used for dragging (only sent for unrelated mouse up/down messages during a drag) +#define DRAG "drag" diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 4477c8aee8..6b9fc0d6ba 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -196,18 +196,6 @@ #define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area) #define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area) -#define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user) -#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob), return flags also used by other signals. - #define COMPONENT_ALLOW_EXAMINATE 1 - #define COMPONENT_DENY_EXAMINATE 2 //Higher priority compared to the above one - -#define COMSIG_CLICK_CTRL "ctrl_click" //from base of atom/CtrlClickOn(): (/mob) -#define COMSIG_CLICK_ALT "alt_click" //from base of atom/AltClick(): (/mob) -#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" //from base of atom/CtrlShiftClick(/mob) -#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" //from base of atom/MouseDrop(): (/atom/over, /mob/user) - #define COMPONENT_NO_MOUSEDROP 1 -#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" //from base of atom/MouseDrop_T: (/atom/from, /mob/user) - // /area signals #define COMSIG_AREA_ENTERED "area_entered" //from base of area/Entered(): (atom/movable/M) #define COMSIG_AREA_EXITED "area_exited" //from base of area/Exited(): (atom/movable/M) diff --git a/code/__DEFINES/dcs/signals/signals_action.dm b/code/__DEFINES/dcs/signals/signals_action.dm new file mode 100644 index 0000000000..62eccf2823 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_action.dm @@ -0,0 +1,6 @@ +/// From base of /datum/action/cooldown/proc/PreActivate(), sent to the action owner: (datum/action/cooldown/activated) +#define COMSIG_MOB_ABILITY_STARTED "mob_ability_base_started" + /// Return to block the ability from starting / activating + #define COMPONENT_BLOCK_ABILITY_START (1<<0) +/// From base of /datum/action/cooldown/proc/PreActivate(), sent to the action owner: (datum/action/cooldown/finished) +#define COMSIG_MOB_ABILITY_FINISHED "mob_ability_base_finished" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm new file mode 100644 index 0000000000..2b0c9c297e --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm @@ -0,0 +1,25 @@ +// 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 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" + /// Allows the user to examinate regardless of client.eye. + #define COMPONENT_ALLOW_EXAMINATE (1<<0) + /// Higher priority compared to the above one + #define COMPONENT_DENY_EXAMINATE (1<<1) +///from base of atom/CtrlClickOn(): (/mob) +#define COMSIG_CLICK_CTRL "ctrl_click" +///from base of atom/AltClick(): (/mob) +#define COMSIG_CLICK_ALT "alt_click" +///from base of atom/CtrlShiftClick(/mob) +#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" +///from base of atom/MouseDrop(): (/atom/over, /mob/user) +#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" + #define COMPONENT_NO_MOUSEDROP (1<<0) +///from base of atom/MouseDrop_T: (/atom/from, /mob/user) +#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" //from base of atom/MouseDrop_T: (/atom/from, /mob/user) diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 88cba1c449..94755e3bd2 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -1,9 +1,232 @@ //HUD styles. Index order defines how they are cycled in F12. -#define HUD_STYLE_STANDARD 1 //Standard hud -#define HUD_STYLE_REDUCED 2 //Reduced hud (just hands and intent switcher) -#define HUD_STYLE_NOHUD 3 //No hud (for screenshots) +/// Standard hud +#define HUD_STYLE_STANDARD 1 +/// Reduced hud (just hands and intent switcher) +#define HUD_STYLE_REDUCED 2 +/// No hud (for screenshots) +#define HUD_STYLE_NOHUD 3 -#define HUD_VERSIONS 3 //Used in show_hud(); Please ensure this is the same as the maximum index. +/// Used in show_hud(); Please ensure this is the same as the maximum index. +#define HUD_VERSIONS 3 +// Consider these images/atoms as part of the UI/HUD (apart of the appearance_flags) +/// Used for progress bars and chat messages +#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)) + 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" + +#define ui_devilsouldisplay "WEST:6,CENTER-1:15" + +//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_pull_resist "EAST-2:26,SOUTH+1:7" +#define ui_movi "EAST-2:26,SOUTH:5" +#define ui_sprintbufferloc "EAST-2:26,SOUTH:18" +#define ui_acti "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-5:20,SOUTH:5"//CIT CHANGE - moves this over one tile to accommodate for combat mode toggle +#define ui_building "EAST-5:20,SOUTH:21"//CIT CHANGE - ditto +#define ui_language_menu "EAST-5:4,SOUTH:21"//CIT CHANGE - ditto +#define ui_voremode "EAST-5:20,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:19"//CIT CHANGE - moves internal icon up a little bit to accommodate for the stamina meter +#define ui_mood "EAST-1:28,CENTER-3:10" +// #define ui_spacesuit "EAST-1:28,CENTER-4: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-2:15" +#define ui_living_health "EAST-1:28,CENTER: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:21,SOUTH+1:5" #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" +#define ui_borg_sensor "CENTER-6:16, SOUTH:5" //LEGACY +#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //LEGACY + +//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-3:26,SOUTH:5" + +//Constructs +#define ui_construct_pull "EAST,CENTER-2:15" +#define ui_construct_health "EAST,CENTER:15" + +//AI +#define ui_ai_core "BOTTOM:6,RIGHT-4" +#define ui_ai_shuttle "BOTTOM:6,RIGHT-3" +#define ui_ai_announcement "BOTTOM:6,RIGHT-2" +#define ui_ai_state_laws "BOTTOM:6,RIGHT-1" +#define ui_ai_pda_log "BOTTOM:6,RIGHT" +#define ui_ai_pda_send "BOTTOM+1:6,RIGHT" +#define ui_ai_language_menu "BOTTOM+1:8,RIGHT-2:30" + +#define ui_ai_crew_monitor "BOTTOM:6,CENTER-1" +#define ui_ai_crew_manifest "BOTTOM:6,CENTER" +#define ui_ai_alerts "BOTTOM:6,CENTER+1" + +#define ui_ai_view_images "BOTTOM:6,LEFT+4" +#define ui_ai_camera_list "BOTTOM:6,LEFT+3" +#define ui_ai_track_with_camera "BOTTOM:6,LEFT+2" +#define ui_ai_camera_light "BOTTOM:6,LEFT+1" +#define ui_ai_sensor "BOTTOM:6,LEFT" +#define ui_ai_multicam "BOTTOM+1:6,LEFT+1" +#define ui_ai_add_multicam "BOTTOM+1:6,LEFT" +#define ui_ai_take_picture "BOTTOM+2:6,LEFT" + + +// 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_take_picture "SOUTH:6,WEST+12" +// #define ui_pai_view_images "SOUTH:6,WEST+13" + +//Ghosts +#define ui_ghost_jumptomob "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_mafia "SOUTH: 6, CENTER+2:24" +#define ui_ghost_spawners "SOUTH: 6, CENTER+1:24" // LEGACY. SAME LOC AS PAI + +//UI position overrides for 1:1 screen layout. (default is 7:5) +#define ui_stamina "EAST-1:28,CENTER:17" // replacing internals button +#define ui_overridden_resist "EAST-3:24,SOUTH+1:7" +#define ui_clickdelay "CENTER,SOUTH+1:-31" +#define ui_resistdelay "EAST-3:24,SOUTH+1:4" +#define ui_combat_toggle "EAST-4:22,SOUTH:5" + +#define ui_boxcraft "EAST-4:22,SOUTH+1:6" +#define ui_boxarea "EAST-4:6,SOUTH+1:6" +#define ui_boxlang "EAST-5:22,SOUTH+1:6" +#define ui_boxvore "EAST-5:22,SOUTH+1:6" + +#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/maths.dm b/code/__DEFINES/maths.dm index 60d02c1e49..01a295634f 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -30,6 +30,8 @@ #define CEILING(x, y) ( -round(-(x) / (y)) * (y) ) +#define ROUND_UP(x) ( -round(-(x))) + // round() acts like floor(x, 1) by default but can't handle other values #define FLOOR(x, y) ( round((x) / (y)) * (y) ) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 4165b1cec3..da01956e64 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -304,10 +304,6 @@ GLOBAL_LIST_INIT(pda_reskins, list( */ -// Consider these images/atoms as part of the UI/HUD -#define APPEARANCE_UI_IGNORE_ALPHA (RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA|PIXEL_SCALE) -#define APPEARANCE_UI (RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|PIXEL_SCALE) - //Just space #define SPACE_ICON_STATE "[((x + y) ^ ~(x * y) + z) % 25]" diff --git a/code/__HELPERS/screen_objs.dm b/code/__HELPERS/screen_objs.dm new file mode 100644 index 0000000000..aa86b6d579 --- /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 1c92971802..6a4210bc53 100644 --- a/code/__HELPERS/view.dm +++ b/code/__HELPERS/view.dm @@ -1,15 +1,19 @@ /proc/getviewsize(view) - var/viewX - var/viewY if(isnum(view)) var/totalviewrange = (view < 0 ? -1 : 1) + 2 * view - viewX = totalviewrange - viewY = totalviewrange + return list(totalviewrange, totalviewrange) else var/list/viewrangelist = splittext(view,"x") - viewX = text2num(viewrangelist[1]) - viewY = text2num(viewrangelist[2]) - return list(viewX, viewY) + 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) diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index a698706f1a..fffbbc059f 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -91,31 +91,31 @@ /atom/movable/screen/click_catcher/IsAutoclickable() . = 1 -//Please don't roast me too hard -/client/MouseMove(object,location,control,params) - mouseParams = params - mouseLocation = location - mouseObject = object - mouseControlObject = control - if(mob) - SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOUSEMOVE, object, location, control, params) - // god forgive me for i have sinned - used for autoparry. currently at 5 objects. - moused_over_objects[object] = world.time - if(moused_over_objects.len > 7) - moused_over_objects.Cut(1, 2) - ..() - /client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params) + var/list/modifiers = params2list(params) + if (LAZYACCESS(modifiers, MIDDLE_CLICK)) + if (src_object && src_location != over_location) + middragtime = world.time + middle_drag_atom_ref = WEAKREF(src_object) + else + middragtime = 0 + middle_drag_atom_ref = null mouseParams = params - mouseLocation = over_location - mouseObject = over_object - mouseControlObject = over_control - if(selected_target[1] && over_object && over_object.IsAutoclickable()) + 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(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 + middle_drag_atom_ref = null + ..() diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm deleted file mode 100644 index 233ae17a4d..0000000000 --- a/code/_onclick/hud/_defines.dm +++ /dev/null @@ -1,195 +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" - -#define ui_devilsouldisplay "WEST:6,CENTER-1:15" - -//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_pull_resist "EAST-2:26,SOUTH+1:7" -#define ui_movi "EAST-2:26,SOUTH:5" -#define ui_sprintbufferloc "EAST-2:26,SOUTH:18" -#define ui_acti "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-5:20,SOUTH:5"//CIT CHANGE - moves this over one tile to accommodate for combat mode toggle -#define ui_building "EAST-5:20,SOUTH:21"//CIT CHANGE - ditto -#define ui_language_menu "EAST-5:4,SOUTH:21"//CIT CHANGE - ditto -#define ui_voremode "EAST-5:20,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:19"//CIT CHANGE - moves internal icon up a little bit to accommodate for the stamina meter -#define ui_mood "EAST-1:28,CENTER-3:10" -// #define ui_spacesuit "EAST-1:28,CENTER-4: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-2:15" -#define ui_living_health "EAST-1:28,CENTER: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:21,SOUTH+1:5" -#define ui_borg_sensor "CENTER-6:16, SOUTH:5" //LEGACY -#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //LEGACY - -//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-3:26,SOUTH:5" - -//Constructs -#define ui_construct_pull "EAST,CENTER-2:15" -#define ui_construct_health "EAST,CENTER:15" - -//AI -#define ui_ai_core "BOTTOM:6,RIGHT-4" -#define ui_ai_shuttle "BOTTOM:6,RIGHT-3" -#define ui_ai_announcement "BOTTOM:6,RIGHT-2" -#define ui_ai_state_laws "BOTTOM:6,RIGHT-1" -#define ui_ai_pda_log "BOTTOM:6,RIGHT" -#define ui_ai_pda_send "BOTTOM+1:6,RIGHT" -#define ui_ai_language_menu "BOTTOM+1:8,RIGHT-2:30" - -#define ui_ai_crew_monitor "BOTTOM:6,CENTER-1" -#define ui_ai_crew_manifest "BOTTOM:6,CENTER" -#define ui_ai_alerts "BOTTOM:6,CENTER+1" - -#define ui_ai_view_images "BOTTOM:6,LEFT+4" -#define ui_ai_camera_list "BOTTOM:6,LEFT+3" -#define ui_ai_track_with_camera "BOTTOM:6,LEFT+2" -#define ui_ai_camera_light "BOTTOM:6,LEFT+1" -#define ui_ai_sensor "BOTTOM:6,LEFT" -#define ui_ai_multicam "BOTTOM+1:6,LEFT+1" -#define ui_ai_add_multicam "BOTTOM+1:6,LEFT" -#define ui_ai_take_picture "BOTTOM+2:6,LEFT" - - -// 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_take_picture "SOUTH:6,WEST+12" -// #define ui_pai_view_images "SOUTH:6,WEST+13" - -//Ghosts -#define ui_ghost_jumptomob "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_mafia "SOUTH: 6, CENTER+2:24" -#define ui_ghost_spawners "SOUTH: 6, CENTER+1:24" // LEGACY. SAME LOC AS PAI - -//UI position overrides for 1:1 screen layout. (default is 7:5) -#define ui_stamina "EAST-1:28,CENTER:17" // replacing internals button -#define ui_overridden_resist "EAST-3:24,SOUTH+1:7" -#define ui_clickdelay "CENTER,SOUTH+1:-31" -#define ui_resistdelay "EAST-3:24,SOUTH+1:4" -#define ui_combat_toggle "EAST-4:22,SOUTH:5" - -#define ui_boxcraft "EAST-4:22,SOUTH+1:6" -#define ui_boxarea "EAST-4:6,SOUTH+1:6" -#define ui_boxlang "EAST-5:22,SOUTH+1:6" -#define ui_boxvore "EAST-5:22,SOUTH+1:6" - -#define ui_wanted_lvl "NORTH,11" diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm index d97e1f5d04..6b70d8cd45 100644 --- a/code/_onclick/hud/action_button.dm +++ b/code/_onclick/hud/action_button.dm @@ -2,186 +2,154 @@ /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) - return linked_action.owner == user + if(linked_action.viewers[user.hud_used]) + return TRUE + return FALSE else if (isobserver(user)) var/mob/dead/observer/O = user return !O.observetarget 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, "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 var/list/modifiers = params2list(params) if(modifiers["shift"]) - if(locked) - to_chat(usr, "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(modifiers["ctrl"]) - locked = !locked - to_chat(usr, "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 linked_action.Trigger() 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/mob/actions.dmi' - icon_state = "bg_default" - var/hidden = 0 - var/hide_icon = 'icons/mob/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() + 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/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) + if(old_object) + old_object.MouseExited(over_location, over_control, params) - 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) + last_hovored_ref = WEAKREF(over_object) + over_object.MouseEntered(over_location, over_control, params) -/atom/movable/screen/movable/action_button/hide_toggle/Click(location,control,params) - if (!can_use(usr)) +/atom/movable/screen/movable/action_button/MouseEntered(location, control, params) + . = ..() + if(!QDELETED(src)) + openToolTip(usr, src, params, title = name, content = desc, theme = actiontooltipstyle) + +/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 - var/list/modifiers = params2list(params) - if(modifiers["shift"]) - if(locked) - to_chat(usr, "Action button \"[name]\" is locked, unlock it first.") - return TRUE - moved = FALSE - usr.update_action_buttons(TRUE) - return TRUE - if(modifiers["ctrl"]) - locked = !locked - to_chat(usr, "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(modifiers["alt"]) - 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 = usr.client.prefs.buttons_locked - locked = usr.client.prefs.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, "Action button positions have been reset.") - return TRUE - usr.hud_used.action_buttons_hidden = !usr.hud_used.action_buttons_hidden - - hidden = usr.hud_used.action_buttons_hidden - if(hidden) - name = "Show Buttons" - else - name = "Hide Buttons" - update_icon() - usr.update_action_buttons() - -/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, "Action button positions have been reset.") - return TRUE - - -/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_icon() - -/atom/movable/screen/movable/action_button/hide_toggle/update_overlays() + 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 . = ..() - if(hidden) - . += show_appearance - else - . += hide_appearance + 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 -/atom/movable/screen/movable/action_button/MouseEntered(location,control,params) - if(!QDELETED(src)) - openToolTip(usr,src,params,title = name,content = desc,theme = actiontooltipstyle) + 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/MouseExited() - closeToolTip(usr) +/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/mob/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) @@ -191,56 +159,243 @@ if(hud_used.hud_shown != HUD_STYLE_STANDARD) return - var/button_number = 0 - var/list/cview = getviewsize(client.view) - var/supportedcolumns = cview[1]-2 + 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, supportedcolumns) - 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, supportedcolumns) - 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() -/datum/hud/proc/ButtonNumberToScreenCoords(number, supportedcolumns) // TODO : Make this zero-indexed for readabilty - var/row = round((number - 1)/supportedcolumns) - var/col = ((number - 1)%(supportedcolumns)) + 1 +/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 - var/coord_col = "+[col-1]" - var/coord_col_offset = 2 + 2 * col +/atom/movable/screen/button_palette/Destroy() + if(our_hud) + our_hud.mymob?.client?.screen -= src + our_hud.toggle_palette = null + our_hud = null + return ..() - var/coord_row = "[row ? -row : "+0"]" +/atom/movable/screen/button_palette/Initialize(mapload) + . = ..() + update_appearance() - return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:-6" +/atom/movable/screen/button_palette/proc/set_hud(datum/hud/our_hud) + src.our_hud = our_hud + refresh_owner() -/datum/hud/proc/SetButtonCoords(atom/movable/screen/button,number, supportedcolumns) - var/row = round((number-1)/supportedcolumns) - var/col = ((number - 1)%(supportedcolumns)) + 1 - var/x_offset = 32*(col-1) + 4 + 2*col - var/y_offset = -32*(row+1) + 26 +/atom/movable/screen/button_palette/update_name(updates) + . = ..() + if(expanded) + name = "Hide Buttons" + else + name = "Show Buttons" - var/matrix/M = matrix() - M.Translate(x_offset,y_offset) - button.transform = M +/atom/movable/screen/button_palette/proc/refresh_owner() + var/mob/viewer = our_hud.mymob + if(viewer.client) + viewer.client.screen |= src + + var/list/settings = our_hud.get_action_buttons_icons() + var/ui_icon = "[settings["bg_icon"]]" + var/list/ui_segments = splittext(ui_icon, ".") + var/list/ui_paths = splittext(ui_segments[1], "/") + var/ui_name = ui_paths[length(ui_paths)] + + icon_state = "[ui_name]_palette" + +/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/mob/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/mob/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 9d762332c6..740174138d 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -73,8 +73,13 @@ GLOBAL_LIST_INIT(available_ui_styles, list( // We don't actually do proccalls really yet, so let's grab at prefs - 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/healthdoll @@ -91,10 +96,12 @@ GLOBAL_LIST_INIT(available_ui_styles, list( // will fall back to the default if any of these are null ui_style = ui_style2icon(owner.client && owner.client.prefs && owner.client.prefs.UI_style) - hide_actions_toggle = new - hide_actions_toggle.InitialiseIcon(src) - if(mymob.client) - hide_actions_toggle.locked = mymob.client.prefs.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() @@ -110,7 +117,13 @@ GLOBAL_LIST_INIT(available_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) @@ -148,10 +161,14 @@ GLOBAL_LIST_INIT(available_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)) @@ -160,6 +177,8 @@ GLOBAL_LIST_INIT(available_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.update_clickcatcher() @@ -181,7 +200,7 @@ GLOBAL_LIST_INIT(available_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 @@ -272,7 +291,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list( ui_style = new_ui_style build_hand_slots() - hide_actions_toggle.InitialiseIcon(src) //Triggered when F12 is pressed (Unless someone changed something in the DMF) /mob/verb/button_pressed_F12() @@ -320,3 +338,306 @@ GLOBAL_LIST_INIT(available_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 a00687e263..6c9f852ef5 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 - var/list/PM = params2list(params) + + 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(!PM || !PM["screen-loc"]) + if(!LAZYACCESS(modifiers, SCREEN_LOC)) return - - //Split screen-loc up into X+Pixel_X and Y+Pixel_Y - var/list/screen_loc_params = splittext(PM["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 5128448561..b9b27072b3 100644 --- a/code/_onclick/hud/screentip.dm +++ b/code/_onclick/hud/screentip.dm @@ -17,4 +17,4 @@ 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 53c86004a6..f7a7c66718 100644 --- a/code/controllers/subsystem/augury.dm +++ b/code/controllers/subsystem/augury.dm @@ -69,15 +69,15 @@ SUBSYSTEM_DEF(augury) SSaugury.watchers += owner to_chat(owner, "You are now auto-following debris.") active = TRUE - UpdateButtonIcon() + UpdateButtons() /datum/action/innate/augury/Deactivate() SSaugury.watchers -= owner to_chat(owner, "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 2d6af6c2de..29c4fa90da 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -11,9 +11,11 @@ var/check_flags = 0 var/required_mobility_flags = MOBILITY_USE 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/use_target_appearance = FALSE var/list/target_appearance_matrix //if set, will be used to transform the target button appearance as an arglist. @@ -28,12 +30,6 @@ /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 @@ -43,51 +39,42 @@ 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 - - //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 - - M.actions += src - if(M.client) - M.client.screen += button - button.locked = M.client.prefs.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 + if(ref == owner) + Remove(owner) + if(ref == target) + qdel(src) /datum/action/proc/Remove(mob/M) - if(M) - if(M.client) - M.client.screen -= button - M.actions -= src - M.update_action_buttons() - owner = null - button.moved = FALSE //so the button appears in its normal position when given to another owner. - button.locked = FALSE - button.id = null + for(var/datum/hud/hud in viewers) + if(!hud.mymob) + continue + 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 /datum/action/proc/Trigger() if(!IsAvailable()) @@ -125,15 +112,15 @@ /datum/action/proc/UpdateButtons(status_only, force) for(var/datum/hud/hud in viewers) var/atom/movable/screen/movable/button = viewers[hud] - UpdateButtonIcon(button, status_only, force) + UpdateButton(button, status_only, force) -/datum/action/proc/UpdateButtonIcon(status_only = FALSE, force = FALSE) +/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 && owner.hud_used && background_icon_state == ACTION_BUTTON_DEFAULT_BACKGROUND) + 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"] @@ -145,25 +132,13 @@ if(button.icon_state != background_icon_state) button.icon_state = background_icon_state - if(!use_target_appearance) - ApplyIcon(button, force) + ApplyIcon(button, force) - else if(target && button.appearance_cache != target.appearance) //replace with /ref comparison if this is not valid. - var/mutable_appearance/M = new(target) - M.layer = FLOAT_LAYER - M.plane = FLOAT_PLANE - if(target_appearance_matrix) - var/list/L = target_appearance_matrix - M.transform = matrix(L[1], L[2], L[3], L[4], L[5], L[6]) - button.cut_overlays() - button.add_overlay(M) - button.appearance_cache = target.appearance - - if(!IsAvailable(TRUE)) + 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 1 + 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)) @@ -184,7 +159,67 @@ M.ghostize(can_reenter_corpse = TRUE, voluntary = TRUE) /datum/action/proc/OnUpdatedIcon() - addtimer(CALLBACK(src, .proc/UpdateButtonIcon), 1) //Hopefully runs after new icon overlays have been compiled. + SIGNAL_HANDLER + UpdateButtons() + +//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 + + 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 + + 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.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 @@ -258,12 +293,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" @@ -309,9 +346,9 @@ /datum/action/item_action/toggle_unfriendly_fire/Trigger() if(..()) - UpdateButtonIcon() + UpdateButtons() -/datum/action/item_action/toggle_unfriendly_fire/UpdateButtonIcon(status_only = FALSE, force) +/datum/action/item_action/toggle_unfriendly_fire/UpdateButton(atom/movable/screen/movable/action_button/button, status_only = FALSE, force) if(istype(target, /obj/item/hierophant_club)) var/obj/item/hierophant_club/H = target if(H.friendly_fire_check) @@ -380,7 +417,6 @@ /datum/action/item_action/toggle/New(Target) ..() name = "Toggle [target.name]" - button.name = name /datum/action/item_action/halt name = "HALT!" @@ -409,7 +445,6 @@ /datum/action/item_action/adjust/New(Target) ..() name = "Adjust [target.name]" - button.name = name /datum/action/item_action/switch_hud name = "Switch HUD" @@ -495,12 +530,10 @@ /datum/action/item_action/organ_action/toggle/New(Target) ..() name = "Toggle [target.name]" - button.name = name /datum/action/item_action/organ_action/use/New(Target) ..() name = "Use [target.name]" - button.name = name /datum/action/item_action/cult_dagger name = "Draw Blood Rune" @@ -509,14 +542,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(iscultist(M)) - ..() - button.screen_loc = "6:157,4:-2" - button.moved = "6:157,4:-2" - else + if(!iscultist(M)) Remove(owner) + return + return ..() /datum/action/item_action/cult_dagger/Trigger() for(var/obj/item/H in owner.held_items) //In case we were already holding another dagger @@ -643,7 +675,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 @@ -711,44 +742,114 @@ /datum/action/cooldown check_flags = 0 transparent_when_unavailable = FALSE + // The default cooldown applied when StartCooldown() is called var/cooldown_time = 0 + // The actual next time this ability can be used var/next_use_time = 0 + // Whether or not you want the cooldown for the ability to display in text form + var/text_cooldown = TRUE + // Setting for intercepting clicks before activating the ability + var/click_to_activate = FALSE + // Shares cooldowns with other cooldown abilities of the same value, not active if null + var/shared_cooldown -/datum/action/cooldown/New() - ..() +/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(silent = FALSE) - return next_use_time <= world.time +/datum/action/cooldown/IsAvailable() + return ..() && (next_use_time <= world.time) -/datum/action/cooldown/proc/StartCooldown() - next_use_time = world.time + cooldown_time - button.maptext = MAPTEXT_TINY_UNICODE("[round(cooldown_time/10, 0.1)]") - UpdateButtonIcon() +/// Starts a cooldown time to be shared with similar abilities, will use default cooldown time if an override is not specified +/datum/action/cooldown/proc/StartCooldown(override_cooldown_time) + if(shared_cooldown) + for(var/datum/action/cooldown/shared_ability in owner.actions - src) + if(shared_cooldown == shared_ability.shared_cooldown) + if(isnum(override_cooldown_time)) + shared_ability.StartCooldownSelf(override_cooldown_time) + else + shared_ability.StartCooldownSelf(cooldown_time) + StartCooldownSelf(override_cooldown_time) + +/// Starts a cooldown time for this ability only, will use default cooldown time if an override is not specified +/datum/action/cooldown/proc/StartCooldownSelf(override_cooldown_time) + if(isnum(override_cooldown_time)) + next_use_time = world.time + override_cooldown_time + else + next_use_time = world.time + cooldown_time + UpdateButtons() START_PROCESSING(SSfastprocess, src) -/datum/action/cooldown/process() +/datum/action/cooldown/Trigger(trigger_flags, atom/target) + . = ..() + if(!.) + return if(!owner) + return FALSE + if(click_to_activate) + if(target) + // For automatic / mob handling + return InterceptClickOn(owner, null, target) + if(owner.click_intercept == src) + owner.click_intercept = null + else + owner.click_intercept = src + for(var/datum/action/cooldown/ability in owner.actions) + ability.UpdateButtons() + return TRUE + return PreActivate(owner) + +/// Intercepts client owner clicks to activate the ability +/datum/action/cooldown/proc/InterceptClickOn(mob/living/caller, params, atom/target) + if(!IsAvailable()) + return FALSE + if(!target) + return FALSE + PreActivate(target) + caller.click_intercept = null + return TRUE + +/// For signal calling +/datum/action/cooldown/proc/PreActivate(atom/target) + if(SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_STARTED, src) & COMPONENT_BLOCK_ABILITY_START) + return + . = Activate(target) + SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_FINISHED, src) + +/// To be implemented by subtypes +/datum/action/cooldown/proc/Activate(atom/target) + return + +/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(text_cooldown) + button.maptext = MAPTEXT("[round(time_left/10, 0.1)]") + if(!owner || time_left == 0) button.maptext = "" + if(IsAvailable() && owner.click_intercept == src) + button.color = COLOR_GREEN + +/datum/action/cooldown/process() + var/time_left = max(next_use_time - world.time, 0) + if(!owner || time_left == 0) STOP_PROCESSING(SSfastprocess, src) - var/timeleft = max(next_use_time - world.time, 0) - if(timeleft == 0) - button.maptext = "" - UpdateButtonIcon() - STOP_PROCESSING(SSfastprocess, src) - else - button.maptext = MAPTEXT_TINY_UNICODE("[round(cooldown_time/10, 0.1)]") + 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) //surf_ss13 /datum/action/item_action/bhop diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm index f35389f171..c7e0522128 100644 --- a/code/datums/brain_damage/imaginary_friend.dm +++ b/code/datums/brain_damage/imaginary_friend.dm @@ -225,7 +225,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/mind.dm b/code/datums/mind.dm index 00fbfc4f24..9b144e74bf 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1661,7 +1661,7 @@ GLOBAL_LIST(objective_choices) if(istype(S, type)) continue S.charge_counter = delay - S.updateButtonIcon() + S.UpdateButton() INVOKE_ASYNC(S, /obj/effect/proc_holder/spell.proc/start_recharge) /datum/mind/proc/get_ghost(even_if_they_cant_reenter) diff --git a/code/datums/view.dm b/code/datums/view.dm index 5610fb040e..7313cc4c18 100644 --- a/code/datums/view.dm +++ b/code/datums/view.dm @@ -1,108 +1,110 @@ //This is intended to be a full wrapper. DO NOT directly modify its values ///Container for client viewsize -/datum/viewData +/datum/view_data var/width = 0 var/height = 0 var/default = "" var/is_suppressed = FALSE var/client/chief = null -/datum/viewData/New(client/owner, view_string) +/datum/view_data/New(client/owner, view_string) default = view_string chief = owner apply() -/datum/viewData/proc/setDefault(string) +/datum/view_data/proc/setDefault(string) default = string apply() -/datum/viewData/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/viewData/proc/assertFormat()//T-Pose +/datum/view_data/proc/assertFormat()//T-Pose // winset(chief, "mapwindow.map", "zoom=0") // Citadel Edit - We're using icon dropdown instead -/datum/viewData/proc/resetFormat()//Cuck +/datum/view_data/proc/resetFormat()//Cuck // winset(chief, "mapwindow.map", "zoom=[chief.prefs.pixel_size]") // Citadel Edit - We're using icon dropdown instead -/datum/viewData/proc/setZoomMode() +/datum/view_data/proc/setZoomMode() // winset(chief, "mapwindow.map", "zoom-mode=[chief.prefs.scaling_method]") // Citadel Edit - We're using icon dropdown instead -/datum/viewData/proc/isZooming() +/datum/view_data/proc/isZooming() return (width || height) -/datum/viewData/proc/resetToDefault() +/datum/view_data/proc/resetToDefault() width = 0 height = 0 apply() -/datum/viewData/proc/add(toAdd) +/datum/view_data/proc/add(toAdd) width += toAdd height += toAdd apply() -/datum/viewData/proc/addTo(toAdd) +/datum/view_data/proc/addTo(toAdd) var/list/shitcode = getviewsize(toAdd) width += shitcode[1] height += shitcode[2] apply() -/datum/viewData/proc/setTo(toAdd) +/datum/view_data/proc/setTo(toAdd) var/list/shitcode = getviewsize(toAdd) //Backward compatability to account width = shitcode[1] //for a change in how sizes get calculated. we used to include world.view in height = shitcode[2] //this, but it was jank, so I had to move it apply() -/datum/viewData/proc/setBoth(wid, hei) +/datum/view_data/proc/setBoth(wid, hei) width = wid height = hei apply() -/datum/viewData/proc/setWidth(wid) +/datum/view_data/proc/setWidth(wid) width = wid apply() -/datum/viewData/proc/setHeight(hei) +/datum/view_data/proc/setHeight(hei) width = hei apply() -/datum/viewData/proc/addToWidth(toAdd) +/datum/view_data/proc/addToWidth(toAdd) width += toAdd apply() -/datum/viewData/proc/addToHeight(screen, toAdd) +/datum/view_data/proc/addToHeight(screen, toAdd) height += toAdd apply() -/datum/viewData/proc/apply() +/datum/view_data/proc/apply() chief.change_view(getView()) - safeApplyFormat() + afterViewChange() -/datum/viewData/proc/supress() +/datum/view_data/proc/supress() is_suppressed = TRUE apply() -/datum/viewData/proc/unsupress() +/datum/view_data/proc/unsupress() is_suppressed = FALSE apply() -/datum/viewData/proc/getView() +/datum/view_data/proc/getView() var/list/temp = getviewsize(default) if(is_suppressed) return "[temp[1]]x[temp[2]]" return "[width + temp[1]]x[height + temp[2]]" -/datum/viewData/proc/zoomIn() +/datum/view_data/proc/zoomIn() resetToDefault() animate(chief, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW) -/datum/viewData/proc/zoomOut(radius = 0, offset = 0, direction = FALSE) +/datum/view_data/proc/zoomOut(radius = 0, offset = 0, direction = FALSE) if(direction) var/_x = 0 var/_y = 0 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 4440b3f91a..ea3531dcd0 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1283,7 +1283,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb */ /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) /// Special stuff you want to do when an outfit equips this item. /obj/item/proc/on_outfit_equip(mob/living/carbon/human/outfit_wearer, visuals_only, item_slot) diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm index 880358a2c1..6873b59146 100644 --- a/code/game/objects/items/defib.dm +++ b/code/game/objects/items/defib.dm @@ -173,7 +173,7 @@ update_power() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/defibrillator/proc/make_paddles() if(!combat) diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 79489c3c51..3f64be4729 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -40,7 +40,7 @@ playsound(src, on ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, TRUE) for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() return TRUE /obj/item/flashlight/DoRevenantThrowEffects(atom/target) diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index f83d01f4ef..801befc9c6 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -129,12 +129,12 @@ /datum/action/innate/drink_fling/Activate() button_icon_state = "drinkfling_on" active = TRUE - UpdateButtonIcon() + UpdateButtons() /datum/action/innate/drink_fling/Deactivate() button_icon_state = "drinkfling_off" active = FALSE - UpdateButtonIcon() + UpdateButtons() /obj/item/book/granter/action/origami granted_action = /datum/action/innate/origami @@ -155,13 +155,13 @@ to_chat(owner, "You will now fold origami planes.") button_icon_state = "origami_on" active = TRUE - UpdateButtonIcon() + UpdateButtons() /datum/action/innate/origami/Deactivate() to_chat(owner, "You will no longer fold origami planes.") button_icon_state = "origami_off" active = FALSE - UpdateButtonIcon() + UpdateButtons() ///SPELLS/// diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 173b420710..d8730cc95b 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -719,6 +719,6 @@ user.vtec = initial(user.vtec) - maxReduction * 1 action.button_icon_state = "Chevron_State_[currentState]" - action.UpdateButtonIcon() + action.UpdateButtons() return TRUE diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm index 25e2f1a292..92e46836b0 100644 --- a/code/game/objects/items/tanks/jetpack.dm +++ b/code/game/objects/items/tanks/jetpack.dm @@ -50,7 +50,7 @@ to_chat(user, "You turn the jetpack off.") for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/tank/jetpack/proc/turn_on(mob/user) on = TRUE diff --git a/code/game/objects/structures/manned_turret.dm b/code/game/objects/structures/manned_turret.dm index 3f1cb5f4c1..639180356d 100644 --- a/code/game/objects/structures/manned_turret.dm +++ b/code/game/objects/structures/manned_turret.dm @@ -80,7 +80,7 @@ return FALSE var/client/C = controller.client if(C) - var/atom/A = C.mouseObject + var/atom/A = WEAKREF(C.mouse_object_ref) var/turf/T = get_turf(A) if(istype(T)) //They're hovering over something in the map. direction_track(controller, T) diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm index 343faccad6..293b5bac4b 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm @@ -59,7 +59,7 @@ H.update_inv_wear_suit() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user, datum/action/A) if(slot == ITEM_SLOT_OCLOTHING) //we only give the mob the ability to activate the vest if he's actually wearing it. diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm index c5f1cfbb40..a3b7fd4c0f 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm @@ -216,7 +216,7 @@ return // Disable Powers: Masquerade * NOTE * This should happen as a FLAW! //if (stat >= UNCONSCIOUS) - // for (var/datum/action/bloodsucker/masquerade/P in powers) + // for (var/datum/action/cooldown/bloodsucker/masquerade/P in powers) // P.Deactivate() // TEMP DEATH var/total_brute = owner.current.getBruteLoss_nonProsthetic() @@ -252,7 +252,7 @@ owner.current.update_sight() owner.current.reload_fullscreen() // Disable ALL Powers - for(var/datum/action/bloodsucker/power in powers) + for(var/datum/action/cooldown/bloodsucker/power in powers) if(power.active && !power.can_use_in_torpor) power.DeactivatePower() if(owner.current.suiciding) diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm b/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm index 0a799021e9..019bbe6fbd 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm @@ -1,22 +1,26 @@ -/datum/action/bloodsucker +/datum/action/cooldown/bloodsucker name = "Vampiric Gift" desc = "A vampiric gift." button_icon = 'icons/mob/actions/bloodsucker.dmi' //This is the file for the BACKGROUND icon background_icon_state = "vamp_power_off" //And this is the state for the background icon - var/background_icon_state_on = "vamp_power_on" // FULP: Our "ON" icon alternative. - var/background_icon_state_off = "vamp_power_off" // FULP: Our "OFF" icon alternative. icon_icon = 'icons/mob/actions/bloodsucker.dmi' //This is the file for the ACTION icon button_icon_state = "power_feed" //And this is the state for the action icon buttontooltipstyle = "cult" + transparent_when_unavailable = TRUE + + /// Cooldown you'll have to wait between each use, decreases depending on level. + cooldown_time = 2 SECONDS + + ///Background icon when the Power is active. + var/background_icon_state_on = "vamp_power_on" + ///Background icon when the Power is NOT active. + var/background_icon_state_off = "vamp_power_off" // Action-Related - //var/amPassive = FALSE // REMOVED: Just made it its own kind. // Am I just "on" at all times? (aka NO ICON) var/amTargetted = FALSE // Am I asked to choose a target when enabled? (Shows as toggled ON when armed) var/amToggle = FALSE // Can I be actively turned on and off? var/amSingleUse = FALSE // Am I removed after a single use? var/active = FALSE - var/cooldown = 20 // 10 ticks, 1 second. - var/cooldownUntil = 0 // From action.dm: next_use_time = world.time + cooldown_time // Power-Related var/level_current = 0 // Can increase to yield new abilities. Each power goes up in strength each Rank. //var/level_max = 1 // @@ -32,7 +36,7 @@ //var/not_bloodsucker = FALSE // This goes to Vassals or Hunters, but NOT bloodsuckers. var/must_be_concious = TRUE //Can't use this ability while unconcious. -/datum/action/bloodsucker/New() +/datum/action/cooldown/bloodsucker/New() if(bloodcost > 0) desc += "

COST: [bloodcost] Blood" // Modify description to add cost. if(warn_constant_cost) @@ -46,7 +50,7 @@ // click.dm <--- Where we can take over mouse clicks // spells.dm /add_ranged_ability() <--- How we take over the mouse click to use a power on a target. -/datum/action/bloodsucker/Trigger() +/datum/action/cooldown/bloodsucker/Trigger() // Active? DEACTIVATE AND END! if(active && CheckCanDeactivate(TRUE)) DeactivatePower() @@ -56,7 +60,7 @@ PayCost() if(amToggle) active = !active - UpdateButtonIcon() + UpdateButtons() if(!amToggle || !active) StartCooldown() // Must come AFTER UpdateButton(), otherwise icon will revert. ActivatePower() // NOTE: ActivatePower() freezes this power in place until it ends. @@ -65,13 +69,13 @@ if(amSingleUse) RemoveAfterUse() -/datum/action/bloodsucker/proc/CheckCanPayCost(display_error) +/datum/action/cooldown/bloodsucker/proc/CheckCanPayCost(display_error) if(!owner || !owner.mind) return FALSE // Cooldown? - if(cooldownUntil > world.time) + if(next_use_time > world.time) if(display_error) - to_chat(owner, "[src] is unavailable. Wait [(cooldownUntil - world.time) / 10] seconds.") + to_chat(owner, "[src] is unavailable. Wait [(next_use_time - world.time) / 10] seconds.") return FALSE // Have enough blood? var/mob/living/L = owner @@ -81,7 +85,7 @@ return FALSE return TRUE -/datum/action/bloodsucker/proc/CheckCanUse(display_error) // These checks can be scanned every frame while a ranged power is on. +/datum/action/cooldown/bloodsucker/proc/CheckCanUse(display_error) // These checks can be scanned every frame while a ranged power is on. if(!owner || !owner.mind) return FALSE // Torpor? @@ -123,79 +127,43 @@ return FALSE return TRUE -/datum/action/bloodsucker/proc/StartCooldown() - set waitfor = FALSE - // Alpha Out - button.color = rgb(128,0,0,128) - button.alpha = 100 - // Calculate Cooldown (by power's level) - var/this_cooldown = (cooldown_static || amSingleUse) ? cooldown : max(cooldown / 2, cooldown - (cooldown / 16 * (level_current-1))) - // NOTE: With this formula, you'll hit half cooldown at level 8 for that power. - - // Wait for cooldown - cooldownUntil = world.time + this_cooldown - spawn(this_cooldown) - // Alpha In - button.color = rgb(255,255,255,255) - button.alpha = 255 - -/datum/action/bloodsucker/proc/CheckCanDeactivate(display_error) +/datum/action/cooldown/bloodsucker/proc/CheckCanDeactivate(display_error) return TRUE -/datum/action/bloodsucker/UpdateButtonIcon(force = FALSE) +/datum/action/cooldown/bloodsucker/UpdateButton(atom/movable/screen/movable/action_button/button, force = FALSE) background_icon_state = active? background_icon_state_on : background_icon_state_off - ..()//UpdateButtonIcon() + ..()//UpdateButton() - -/datum/action/bloodsucker/proc/PayCost() +/datum/action/cooldown/bloodsucker/proc/PayCost() // owner for actions is the mob, not mind. var/mob/living/L = owner L.blood_volume -= bloodcost -/datum/action/bloodsucker/proc/ActivatePower() +/datum/action/cooldown/bloodsucker/proc/ActivatePower() -/datum/action/bloodsucker/proc/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/proc/DeactivatePower(mob/living/user = owner, mob/living/target) active = FALSE - UpdateButtonIcon() + UpdateButtons() StartCooldown() -/datum/action/bloodsucker/proc/ContinueActive(mob/living/user, mob/living/target) // Used by loops to make sure this power can stay active. +/datum/action/cooldown/bloodsucker/proc/ContinueActive(mob/living/user, mob/living/target) // Used by loops to make sure this power can stay active. return active && user && (!warn_constant_cost || user.blood_volume > 0) -/datum/action/bloodsucker/proc/RemoveAfterUse() +/datum/action/cooldown/bloodsucker/proc/RemoveAfterUse() // Un-Learn Me! (GO HOME var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) if (istype(bloodsuckerdatum)) bloodsuckerdatum.powers -= src Remove(owner) -/datum/action/bloodsucker/proc/Upgrade() +/datum/action/cooldown/bloodsucker/proc/Upgrade() level_current ++ -/////////////////////////////////// PASSIVE POWERS /////////////////////////////////// - -// New Type: Passive (Always on, no button) -/datum/action/bloodsucker/passive - -/datum/action/bloodsucker/passive/New() - // REMOVED: DO NOTHBING! - ..() - // Don't Display Button! (it doesn't do anything anyhow) - button.screen_loc = DEFAULT_BLOODSPELLS - button.moved = DEFAULT_BLOODSPELLS - button.ordered = FALSE - -/datum/action/bloodsucker/passive/Destroy() - if(owner) - Remove(owner) - target = null - return ..() - /////////////////////////////////// TARGETTED POWERS /////////////////////////////////// -/datum/action/bloodsucker/targeted +/datum/action/cooldown/bloodsucker/targeted // NOTE: All Targeted spells are Toggles! We just don't bother checking here. var/target_range = 99 var/message_Trigger = "Select a target." @@ -204,7 +172,7 @@ var/power_in_use = FALSE // Is this power LOCKED due to being used? -/datum/action/bloodsucker/targeted/New(Target) +/datum/action/cooldown/bloodsucker/targeted/New(Target) desc += "
\[Targeted Power\]" // Modify description to add notice that this is aimed. ..() // Create Proc Holder for intercepting clicks @@ -212,7 +180,7 @@ bs_proc_holder.linked_power = src // Click power: Begin Aim -/datum/action/bloodsucker/targeted/Trigger() +/datum/action/cooldown/bloodsucker/targeted/Trigger() if(active && CheckCanDeactivate(TRUE)) DeactivateRangedAbility() DeactivatePower() @@ -220,7 +188,7 @@ if(!CheckCanPayCost(TRUE) || !CheckCanUse(TRUE)) return active = !active - UpdateButtonIcon() + UpdateButtons() // Create & Link Targeting Proc var/mob/living/L = owner if(L.ranged_ability) @@ -230,7 +198,7 @@ if(message_Trigger != "") to_chat(owner, "[message_Trigger]") -/datum/action/bloodsucker/targeted/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/targeted/CheckCanUse(display_error) . = ..() if(!.) return @@ -238,21 +206,21 @@ return FALSE // doesn't let you remove powers if you're not there. So, let's just cancel the power entirely. return TRUE -/datum/action/bloodsucker/targeted/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/targeted/DeactivatePower(mob/living/user = owner, mob/living/target) // Don't run ..(), we don't want to engage the cooldown until we USE this power! active = FALSE - UpdateButtonIcon() + UpdateButtons() -/datum/action/bloodsucker/targeted/proc/DeactivateRangedAbility() +/datum/action/cooldown/bloodsucker/targeted/proc/DeactivateRangedAbility() // Only Turned off when CLICK is disabled...aka, when you successfully clicked (or bs_proc_holder.remove_ranged_ability() // Check if target is VALID (wall, turf, or character?) -/datum/action/bloodsucker/targeted/proc/CheckValidTarget(atom/A) +/datum/action/cooldown/bloodsucker/targeted/proc/CheckValidTarget(atom/A) return FALSE // FALSE targets nothing. // Check if valid target meets conditions -/datum/action/bloodsucker/targeted/proc/CheckCanTarget(atom/A, display_error) +/datum/action/cooldown/bloodsucker/targeted/proc/CheckCanTarget(atom/A, display_error) // Out of Range if(!(A in view(target_range, owner))) if(display_error && target_range > 1) // Only warn for range if it's greater than 1. Brawn doesn't need to announce itself. @@ -261,7 +229,7 @@ return istype(A) // Click Target -/datum/action/bloodsucker/targeted/proc/ClickWithPower(atom/A) +/datum/action/cooldown/bloodsucker/targeted/proc/ClickWithPower(atom/A) // CANCEL RANGED TARGET check if(power_in_use || !CheckValidTarget(A)) return FALSE @@ -276,21 +244,21 @@ power_in_use = FALSE return TRUE -/datum/action/bloodsucker/targeted/proc/FireTargetedPower(atom/A) +/datum/action/cooldown/bloodsucker/targeted/proc/FireTargetedPower(atom/A) // Like ActivatePower, but specific to Targeted (and takes an atom input). We don't use ActivatePower for targeted. -/datum/action/bloodsucker/targeted/proc/PowerActivatedSuccessfully() +/datum/action/cooldown/bloodsucker/targeted/proc/PowerActivatedSuccessfully() // The power went off! We now pay the cost of the power. PayCost() DeactivateRangedAbility() DeactivatePower() StartCooldown() // Do AFTER UpdateIcon() inside of DeactivatePower. Otherwise icon just gets wiped. -/datum/action/bloodsucker/targeted/ContinueActive(mob/living/user, mob/living/target) // Used by loops to make sure this power can stay active. +/datum/action/cooldown/bloodsucker/targeted/ContinueActive(mob/living/user, mob/living/target) // Used by loops to make sure this power can stay active. return ..() // Target Proc Holder /obj/effect/proc_holder/bloodsucker - var/datum/action/bloodsucker/targeted/linked_power + var/datum/action/cooldown/bloodsucker/targeted/linked_power /obj/effect/proc_holder/bloodsucker/remove_ranged_ability(msg) ..() diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm index a86cb374d2..88a4b6bb32 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm @@ -55,8 +55,8 @@ bloodsuckerdatum.warn_sun_locker = FALSE bloodsuckerdatum.warn_sun_burn = FALSE // Remove Dawn Powers - for(var/datum/action/bloodsucker/P in bloodsuckerdatum.powers) - if(istype(P, /datum/action/bloodsucker/gohome)) + for(var/datum/action/cooldown/bloodsucker/P in bloodsuckerdatum.powers) + if(istype(P, /datum/action/cooldown/bloodsucker/gohome)) bloodsuckerdatum.powers -= P P.Remove(M.current) nighttime_duration += 100 //Each day makes the night a minute longer. @@ -149,5 +149,5 @@ if(!istype(M) || !istype(M.current)) continue var/datum/antagonist/bloodsucker/bloodsuckerdatum = M.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) - if(istype(bloodsuckerdatum) && bloodsuckerdatum.lair && !(locate(/datum/action/bloodsucker/gohome) in bloodsuckerdatum.powers)) - bloodsuckerdatum.BuyPower(new /datum/action/bloodsucker/gohome) + if(istype(bloodsuckerdatum) && bloodsuckerdatum.lair && !(locate(/datum/action/cooldown/bloodsucker/gohome) in bloodsuckerdatum.powers)) + bloodsuckerdatum.BuyPower(new /datum/action/cooldown/bloodsucker/gohome) diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_ui.dm b/code/modules/antagonists/bloodsucker/bloodsucker_ui.dm index 486ae51117..3edffe9c31 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_ui.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_ui.dm @@ -14,7 +14,7 @@ dat += "
" // Step One: Decide powers you CAN buy. - for(var/pickedpower in typesof(/datum/action/bloodsucker)) + for(var/pickedpower in typesof(/datum/action/cooldown/bloodsucker)) var/obj/effect/proc_holder/spell/bloodsucker/power = pickedpower // NAME dat += "[power.name]" diff --git a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm index 73625c894d..4044ac9616 100644 --- a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm +++ b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm @@ -168,7 +168,7 @@ return fullname -/datum/antagonist/bloodsucker/proc/BuyPower(datum/action/bloodsucker/power)//(obj/effect/proc_holder/spell/power) +/datum/antagonist/bloodsucker/proc/BuyPower(datum/action/cooldown/bloodsucker/power)//(obj/effect/proc_holder/spell/power) powers += power power.Grant(owner.current)// owner.AddSpell(power) @@ -177,9 +177,9 @@ add_hud() update_hud(TRUE) // Set blood value, current rank // Powers - BuyPower(new /datum/action/bloodsucker/feed) - BuyPower(new /datum/action/bloodsucker/masquerade) - BuyPower(new /datum/action/bloodsucker/veil) + BuyPower(new /datum/action/cooldown/bloodsucker/feed) + BuyPower(new /datum/action/cooldown/bloodsucker/masquerade) + BuyPower(new /datum/action/cooldown/bloodsucker/veil) // Traits for(var/T in defaultTraits) ADD_TRAIT(owner.current, T, BLOODSUCKER_TRAIT) @@ -221,7 +221,7 @@ remove_hud() // Powers while(powers.len) - var/datum/action/bloodsucker/power = pick(powers) + var/datum/action/cooldown/bloodsucker/power = pick(powers) powers -= power power.Remove(owner.current) // owner.RemoveSpell(power) @@ -267,7 +267,7 @@ to_chat(owner, "Bloodsucker Tip: If you cannot find or steal a coffin to use, you can build one from wooden planks.
") /datum/antagonist/bloodsucker/proc/LevelUpPowers() - for(var/datum/action/bloodsucker/power in powers) + for(var/datum/action/cooldown/bloodsucker/power in powers) power.level_current ++ /datum/antagonist/bloodsucker/proc/SpendRank() @@ -281,8 +281,8 @@ //TODO: Make this into a radial, or perhaps a tgui next UI // Purchase Power Prompt var/list/options = list() - for(var/pickedpower in typesof(/datum/action/bloodsucker)) - var/datum/action/bloodsucker/power = pickedpower + for(var/pickedpower in typesof(/datum/action/cooldown/bloodsucker)) + var/datum/action/cooldown/bloodsucker/power = pickedpower // If I don't own it, and I'm allowed to buy it. if(!(locate(power) in powers) && initial(power.bloodsucker_can_buy)) options[initial(power.name)] = power // TESTING: After working with TGUI, it seems you can use initial() to view the variables inside a path? @@ -303,7 +303,7 @@ to_chat(owner.current, "You dont have enough blood to thicken your blood, you need [level_bloodcost - L.blood_volume] units more!") return // Buy New Powers - var/datum/action/bloodsucker/P = options[choice] + var/datum/action/cooldown/bloodsucker/P = options[choice] AddBloodVolume(-level_bloodcost) BuyPower(new P) to_chat(owner.current, "You have used [level_bloodcost] units of blood and learned [initial(P.name)]!") diff --git a/code/modules/antagonists/bloodsucker/datum_hunter.dm b/code/modules/antagonists/bloodsucker/datum_hunter.dm index b221a83a60..9c862fe42a 100644 --- a/code/modules/antagonists/bloodsucker/datum_hunter.dm +++ b/code/modules/antagonists/bloodsucker/datum_hunter.dm @@ -26,7 +26,7 @@ //owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer/hunter_edition) // Give Hunter Power - var/datum/action/P = new /datum/action/bloodsucker/trackvamp + var/datum/action/P = new /datum/action/cooldown/bloodsucker/trackvamp P.Grant(owner.current) // Give Hunter Martial Arts @@ -69,7 +69,7 @@ // Take Hunter Power if (owner.current) - for (var/datum/action/bloodsucker/P in owner.current.actions) + for (var/datum/action/cooldown/bloodsucker/P in owner.current.actions) P.Remove(owner.current) // Take Hunter Martial Arts @@ -166,7 +166,7 @@ /* -/datum/action/bloodsucker/trackvamp/ +/datum/action/cooldown/bloodsucker/trackvamp/ name = "Track Monster"//"Cellular Emporium" desc = "Take a moment to look for clues of any nearby monsters.
These creatures are slippery, and often look like the crew." button_icon = 'icons/mob/actions/bloodsucker.dmi' //This is the file for the BACKGROUND icon @@ -177,7 +177,7 @@ cooldown = 300 // 10 ticks, 1 second. bloodcost = 0 -/datum/action/bloodsucker/trackvamp/ActivatePower() +/datum/action/cooldown/bloodsucker/trackvamp/ActivatePower() var/mob/living/user = owner to_chat(user, "You look around, scanning your environment and discerning signs of any filthy, wretched affronts to the natural order.") @@ -192,7 +192,7 @@ // NOTE: DON'T DEACTIVATE! //DeactivatePower() -/datum/action/bloodsucker/trackvamp/proc/display_proximity() +/datum/action/cooldown/bloodsucker/trackvamp/proc/display_proximity() // Pick target var/turf/my_loc = get_turf(owner) //var/list/mob/living/carbon/vamps = list() diff --git a/code/modules/antagonists/bloodsucker/datum_vassal.dm b/code/modules/antagonists/bloodsucker/datum_vassal.dm index 5162897d60..9a2e3a1ae6 100644 --- a/code/modules/antagonists/bloodsucker/datum_vassal.dm +++ b/code/modules/antagonists/bloodsucker/datum_vassal.dm @@ -38,7 +38,7 @@ // Master Pinpointer owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer/vassal_edition) // Powers - var/datum/action/bloodsucker/vassal/recuperate/new_Recuperate = new () + var/datum/action/cooldown/bloodsucker/vassal/recuperate/new_Recuperate = new () new_Recuperate.Grant(owner.current) powers += new_Recuperate // Give Vassal Objective diff --git a/code/modules/antagonists/bloodsucker/powers/brawn.dm b/code/modules/antagonists/bloodsucker/powers/brawn.dm index db3ce92ec1..25a04d16cc 100644 --- a/code/modules/antagonists/bloodsucker/powers/brawn.dm +++ b/code/modules/antagonists/bloodsucker/powers/brawn.dm @@ -1,11 +1,11 @@ -/datum/action/bloodsucker/targeted/brawn +/datum/action/cooldown/bloodsucker/targeted/brawn name = "Brawn" desc = "Snap restraints with ease, or deal terrible damage with your bare hands." button_icon_state = "power_strength" bloodcost = 10 - cooldown = 90 + cooldown_time = 90 target_range = 1 power_activates_immediately = TRUE message_Trigger = ""//"Whom will you subvert to your will?" @@ -16,7 +16,7 @@ var/upgrade_canLocker = FALSE var/upgrade_canDoor = FALSE -/datum/action/bloodsucker/targeted/brawn/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/targeted/brawn/CheckCanUse(display_error) . = ..() if(!.) return @@ -38,10 +38,10 @@ // NOTE: We use . = FALSE so that we can break cuffs AND throw off our attacker in one use! //return TRUE -/datum/action/bloodsucker/targeted/brawn/CheckValidTarget(atom/A) +/datum/action/cooldown/bloodsucker/targeted/brawn/CheckValidTarget(atom/A) return isliving(A) || istype(A, /obj/machinery/door) -/datum/action/bloodsucker/targeted/brawn/CheckCanTarget(atom/A, display_error) +/datum/action/cooldown/bloodsucker/targeted/brawn/CheckCanTarget(atom/A, display_error) // DEFAULT CHECKS (Distance) if(!..()) // Disable range notice for Brawn. return FALSE @@ -59,7 +59,7 @@ return TRUE return ..() // yes, FALSE! You failed if you got here! BAD TARGET -/datum/action/bloodsucker/targeted/brawn/FireTargetedPower(atom/A) +/datum/action/cooldown/bloodsucker/targeted/brawn/FireTargetedPower(atom/A) // set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done. var/mob/living/carbon/target = A var/mob/living/user = owner @@ -97,7 +97,7 @@ D.open(2) // open(2) is like a crowbar or jaws of life. // Target Type: Closet -/datum/action/bloodsucker/targeted/brawn/proc/CheckBreakRestraints() +/datum/action/cooldown/bloodsucker/targeted/brawn/proc/CheckBreakRestraints() if(!iscarbon(owner)) // || !owner.restrained() return FALSE // (NOTE: Just like biodegrade.dm, we only remove one thing per use // @@ -131,7 +131,7 @@ return TRUE return FALSE -/datum/action/bloodsucker/targeted/brawn/proc/CheckEscapePuller() +/datum/action/cooldown/bloodsucker/targeted/brawn/proc/CheckEscapePuller() if(!owner.pulledby)// || owner.pulledby.grab_state <= GRAB_PASSIVE) return FALSE var/mob/M = owner.pulledby @@ -153,7 +153,7 @@ owner.pulledby = null // It's already done, but JUST IN CASE. return TRUE /* Doesnt work -/datum/action/bloodsucker/targeted/brawn/proc/CheckBreakLocker() +/datum/action/cooldown/bloodsucker/targeted/brawn/proc/CheckBreakLocker() if(!istype(owner.loc, /obj/structure/closet)) return FALSE playsound(get_turf(owner), 'sound/machines/airlock_alien_prying.ogg', 40, 1, -1) diff --git a/code/modules/antagonists/bloodsucker/powers/cloak.dm b/code/modules/antagonists/bloodsucker/powers/cloak.dm index 5383e8c1ac..cb3a1e9fcf 100644 --- a/code/modules/antagonists/bloodsucker/powers/cloak.dm +++ b/code/modules/antagonists/bloodsucker/powers/cloak.dm @@ -1,11 +1,11 @@ -/datum/action/bloodsucker/cloak +/datum/action/cooldown/bloodsucker/cloak name = "Cloak of Darkness" desc = "Blend into the shadows and become invisible to the untrained eye. Movement is slowed in brightly lit areas, and you cannot dissapear while mortals watch you." button_icon_state = "power_cloak" bloodcost = 5 - cooldown = 50 + cooldown_time = 50 bloodsucker_can_buy = TRUE amToggle = TRUE warn_constant_cost = TRUE @@ -14,7 +14,7 @@ var/walk_threshold = 0.4 // arbitrary number, to be changed. edit in last commit: this is fine after testing on box station for a bit var/lum -/datum/action/bloodsucker/cloak/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/cloak/CheckCanUse(display_error) . = ..() if(!.) return @@ -24,7 +24,7 @@ return FALSE return TRUE -/datum/action/bloodsucker/cloak/ActivatePower() +/datum/action/cooldown/bloodsucker/cloak/ActivatePower() var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) var/mob/living/user = owner @@ -52,7 +52,7 @@ sleep(5) // Check every few ticks -/datum/action/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target) +/datum/action/cooldown/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target) if (!..()) return FALSE if(user.stat == !CONSCIOUS) // Must be CONSCIOUS @@ -60,7 +60,7 @@ return FALSE return TRUE -/datum/action/bloodsucker/cloak/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/cloak/DeactivatePower(mob/living/user = owner, mob/living/target) ..() REMOVE_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness") user.alpha = 255 diff --git a/code/modules/antagonists/bloodsucker/powers/feed.dm b/code/modules/antagonists/bloodsucker/powers/feed.dm index a4bdcfee54..b6b3c6be3e 100644 --- a/code/modules/antagonists/bloodsucker/powers/feed.dm +++ b/code/modules/antagonists/bloodsucker/powers/feed.dm @@ -1,12 +1,12 @@ -/datum/action/bloodsucker/feed +/datum/action/cooldown/bloodsucker/feed name = "Feed" desc = "Draw the heartsblood of living victims in your grasp.
None/Passive: Feed silently and unnoticed by your victim.
Aggressive: Subdue your target quickly." button_icon_state = "power_feed" bloodcost = 0 - cooldown = 30 + cooldown_time = 30 amToggle = TRUE bloodsucker_can_buy = TRUE can_be_staked = TRUE @@ -16,7 +16,7 @@ var/mob/living/feed_target // So we can validate more than just the guy we're grappling. var/target_grappled = FALSE // If you started grappled, then ending it will end your Feed. -/datum/action/bloodsucker/feed/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/feed/CheckCanUse(display_error) . = ..() if(!.) return @@ -33,7 +33,7 @@ // DONE! return TRUE -/datum/action/bloodsucker/feed/proc/ValidateTarget(mob/living/target, display_error) // Called twice: validating a subtle victim, or validating your grapple victim. +/datum/action/cooldown/bloodsucker/feed/proc/ValidateTarget(mob/living/target, display_error) // Called twice: validating a subtle victim, or validating your grapple victim. // Bloodsuckers + Animals MUST be grabbed aggressively! if(!owner.pulling || target == owner.pulling && owner.grab_state < GRAB_AGGRESSIVE) // NOTE: It's OKAY that we are checking if(!target) below, AFTER animals here. We want passive check vs animal to warn you first, THEN the standard warning. @@ -76,7 +76,7 @@ return TRUE // If I'm not grabbing someone, find me someone nearby. -/datum/action/bloodsucker/feed/proc/FindMyTarget(display_error) +/datum/action/cooldown/bloodsucker/feed/proc/FindMyTarget(display_error) // Default feed_target = null target_grappled = FALSE @@ -132,7 +132,7 @@ feed_target = pick(targets_valid)//targets[1] return TRUE -/datum/action/bloodsucker/feed/ActivatePower() +/datum/action/cooldown/bloodsucker/feed/ActivatePower() // set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up Activate(), so Deactivate() can happen after. var/mob/living/target = feed_target // Stored during CheckCanUse(). Can be a grabbed OR adjecent character. var/mob/living/user = owner @@ -302,16 +302,16 @@ CheckKilledTarget(user,target) -/datum/action/bloodsucker/feed/proc/CheckKilledTarget(mob/living/user, mob/living/target) +/datum/action/cooldown/bloodsucker/feed/proc/CheckKilledTarget(mob/living/user, mob/living/target) // Bad Bloodsucker. You shouldn't do that. if(target && target.stat >= DEAD && ishuman(target)) SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankkilled", /datum/mood_event/drankkilled) // BAD // in bloodsucker_life.dm -/datum/action/bloodsucker/feed/ContinueActive(mob/living/user, mob/living/target) +/datum/action/cooldown/bloodsucker/feed/ContinueActive(mob/living/user, mob/living/target) return ..() && target && (!target_grappled || user.pulling == target) && blood_sucking_checks(target, TRUE, TRUE) // Active, and still antag, // NOTE: We only care about pulling if target started off that way. Mostly only important for Aggressive feed. -/datum/action/bloodsucker/feed/proc/ApplyVictimEffects(mob/living/target) +/datum/action/cooldown/bloodsucker/feed/proc/ApplyVictimEffects(mob/living/target) // Bloodsuckers not affected by "the Kiss" of another vampire if(!target.mind || !target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) target.Unconscious(50,0) @@ -320,7 +320,7 @@ if(ishuman(target)) target.adjustStaminaLoss(5, forced = TRUE)// Base Stamina Damage -/datum/action/bloodsucker/feed/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/feed/DeactivatePower(mob/living/user = owner, mob/living/target) ..() // activate = FALSE var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) // No longer Feeding diff --git a/code/modules/antagonists/bloodsucker/powers/fortitude.dm b/code/modules/antagonists/bloodsucker/powers/fortitude.dm index 740ec81782..c343270910 100644 --- a/code/modules/antagonists/bloodsucker/powers/fortitude.dm +++ b/code/modules/antagonists/bloodsucker/powers/fortitude.dm @@ -2,12 +2,12 @@ -/datum/action/bloodsucker/fortitude +/datum/action/cooldown/bloodsucker/fortitude name = "Fortitude" desc = "Withstand egregious physical wounds and walk away from attacks that would stun, pierce, and dismember lesser beings. You cannot run while active." button_icon_state = "power_fortitude" bloodcost = 30 - cooldown = 80 + cooldown_time = 80 bloodsucker_can_buy = TRUE amToggle = TRUE warn_constant_cost = TRUE @@ -15,7 +15,7 @@ var/fortitude_resist // So we can raise and lower your brute resist based on what your level_current WAS. -/datum/action/bloodsucker/fortitude/ActivatePower() +/datum/action/cooldown/bloodsucker/fortitude/ActivatePower() var/datum/antagonist/bloodsucker/B = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) var/mob/living/user = owner to_chat(user, "Your flesh, skin, and muscles become as steel.") @@ -47,8 +47,8 @@ B.AddBloodVolume(-0.5) sleep(20) // Check every few ticks that we haven't disabled this power // Return to Running (if you were before) - -/datum/action/bloodsucker/fortitude/DeactivatePower(mob/living/user = owner, mob/living/target) + +/datum/action/cooldown/bloodsucker/fortitude/DeactivatePower(mob/living/user = owner, mob/living/target) ..() // Restore Traits & Effects REMOVE_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude") diff --git a/code/modules/antagonists/bloodsucker/powers/go_home.dm b/code/modules/antagonists/bloodsucker/powers/go_home.dm index a8060ad07e..e78b9089fb 100644 --- a/code/modules/antagonists/bloodsucker/powers/go_home.dm +++ b/code/modules/antagonists/bloodsucker/powers/go_home.dm @@ -1,6 +1,6 @@ -/datum/action/bloodsucker/gohome +/datum/action/cooldown/bloodsucker/gohome name = "Vanishing Act" desc = "As dawn aproaches, disperse into mist and return directly to your Lair.
WARNING: You will drop ALL of your possessions if observed by mortals." button_icon_state = "power_gohome" @@ -8,7 +8,7 @@ background_icon_state_off = "vamp_power_off_oneshot" bloodcost = 100 - cooldown = 99999 // It'll never come back. + cooldown_time = 99999 // It'll never come back. amToggle = FALSE amSingleUse = TRUE @@ -18,7 +18,7 @@ can_be_immobilized = TRUE must_be_concious = FALSE -/datum/action/bloodsucker/gohome/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/gohome/CheckCanUse(display_error) . = ..() if(!.) return @@ -30,12 +30,12 @@ return FALSE return TRUE -/datum/action/bloodsucker/gohome/proc/flicker_lights(var/flicker_range, var/beat_volume) +/datum/action/cooldown/bloodsucker/gohome/proc/flicker_lights(var/flicker_range, var/beat_volume) for(var/obj/machinery/light/L in view(flicker_range, get_turf(owner))) playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', beat_volume, 1) -/datum/action/bloodsucker/gohome/ActivatePower() +/datum/action/cooldown/bloodsucker/gohome/ActivatePower() var/mob/living/carbon/user = owner var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) // IMPORTANT: Check for lair at every step! It might get destroyed. diff --git a/code/modules/antagonists/bloodsucker/powers/haste.dm b/code/modules/antagonists/bloodsucker/powers/haste.dm index 05e2cd0330..6f7ff54749 100644 --- a/code/modules/antagonists/bloodsucker/powers/haste.dm +++ b/code/modules/antagonists/bloodsucker/powers/haste.dm @@ -3,12 +3,12 @@ // Level 2: Dodge Bullets // Level 3: Stun People Passed -/datum/action/bloodsucker/targeted/haste +/datum/action/cooldown/bloodsucker/targeted/haste name = "Immortal Haste" desc = "Dash somewhere with supernatural speed. Those nearby may be knocked away, stunned, or left empty-handed." button_icon_state = "power_speed" bloodcost = 6 - cooldown = 120 + cooldown_time = 120 target_range = 15 power_activates_immediately = TRUE message_Trigger = ""//"Whom will you subvert to your will?" @@ -18,7 +18,7 @@ /// If set, uses this speed in deciseconds instead of world.tick_lag var/speed_override -/datum/action/bloodsucker/targeted/haste/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/targeted/haste/CheckCanUse(display_error) . = ..() if(!.) return @@ -33,10 +33,10 @@ return FALSE return TRUE -/datum/action/bloodsucker/targeted/haste/CheckValidTarget(atom/A) +/datum/action/cooldown/bloodsucker/targeted/haste/CheckValidTarget(atom/A) return isturf(A) || A.loc != owner.loc // Anything will do, if it's not me or my square -/datum/action/bloodsucker/targeted/haste/CheckCanTarget(atom/A, display_error) +/datum/action/cooldown/bloodsucker/targeted/haste/CheckCanTarget(atom/A, display_error) // DEFAULT CHECKS (Distance) if (!..()) return FALSE @@ -45,7 +45,7 @@ // return FALSE return TRUE -/datum/action/bloodsucker/targeted/haste/FireTargetedPower(atom/A) +/datum/action/cooldown/bloodsucker/targeted/haste/FireTargetedPower(atom/A) // This is a non-async proc to make sure the power is "locked" until this finishes. hit = list() RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/on_move) @@ -78,11 +78,11 @@ hit = null user.update_mobility() -/datum/action/bloodsucker/targeted/haste/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/targeted/haste/DeactivatePower(mob/living/user = owner, mob/living/target) ..() // activate = FALSE user.update_mobility() -/datum/action/bloodsucker/targeted/haste/proc/on_move() +/datum/action/cooldown/bloodsucker/targeted/haste/proc/on_move() for(var/mob/living/L in dview(1, get_turf(owner))) if(!hit[L] && (L != owner)) hit[L] = TRUE diff --git a/code/modules/antagonists/bloodsucker/powers/lunge.dm b/code/modules/antagonists/bloodsucker/powers/lunge.dm index db7868fa8a..5cd1a52e39 100644 --- a/code/modules/antagonists/bloodsucker/powers/lunge.dm +++ b/code/modules/antagonists/bloodsucker/powers/lunge.dm @@ -1,25 +1,25 @@ -/datum/action/bloodsucker/lunge +/datum/action/cooldown/bloodsucker/lunge name = "Predatory Lunge" desc = "Prepare the strenght to grapple your prey." button_icon_state = "power_lunge" bloodcost = 10 - cooldown = 30 + cooldown_time = 30 bloodsucker_can_buy = TRUE warn_constant_cost = TRUE amToggle = TRUE var/leap_skill_mod = 5 -/datum/action/bloodsucker/lunge/New() +/datum/action/cooldown/bloodsucker/lunge/New() . = ..() - -/datum/action/bloodsucker/lunge/Destroy() + +/datum/action/cooldown/bloodsucker/lunge/Destroy() . = ..() UnregisterSignal(owner, COMSIG_CARBON_TACKLED) -/datum/action/bloodsucker/lunge/ActivatePower() +/datum/action/cooldown/bloodsucker/lunge/ActivatePower() var/mob/living/carbon/user = owner var/datum/antagonist/bloodsucker/B = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) var/datum/component/tackler/T = user.LoadComponent(/datum/component/tackler) @@ -37,10 +37,10 @@ sleep(5) //Without this, the leap component would get removed too early, causing the normal crash into effects. -/datum/action/bloodsucker/lunge/proc/DelayedDeactivatePower() +/datum/action/cooldown/bloodsucker/lunge/proc/DelayedDeactivatePower() addtimer(CALLBACK(src, .proc/DeactivatePower), 1 SECONDS, TIMER_UNIQUE) -/datum/action/bloodsucker/lunge/DeactivatePower(mob/living/user = owner) +/datum/action/cooldown/bloodsucker/lunge/DeactivatePower(mob/living/user = owner) . = ..() qdel(user.GetComponent(/datum/component/tackler)) UnregisterSignal(user, COMSIG_CARBON_TACKLED) diff --git a/code/modules/antagonists/bloodsucker/powers/masquerade.dm b/code/modules/antagonists/bloodsucker/powers/masquerade.dm index f4ce531ef0..78748a3c80 100644 --- a/code/modules/antagonists/bloodsucker/powers/masquerade.dm +++ b/code/modules/antagonists/bloodsucker/powers/masquerade.dm @@ -16,12 +16,12 @@ // - -/datum/action/bloodsucker/masquerade +/datum/action/cooldown/bloodsucker/masquerade name = "Masquerade" desc = "Feign the vital signs of a mortal, and escape both casual and medical notice as the monster you truly are." button_icon_state = "power_human" bloodcost = 10 - cooldown = 50 + cooldown_time = 50 amToggle = TRUE bloodsucker_can_buy = TRUE warn_constant_cost = TRUE @@ -31,7 +31,7 @@ // NOTE: Firing off vulgar powers disables your Masquerade! -/*/datum/action/bloodsucker/masquerade/CheckCanUse(display_error) +/*/datum/action/cooldown/bloodsucker/masquerade/CheckCanUse(display_error) if(!..(display_error))// DEFAULT CHECKS return FALSE // DONE! @@ -39,7 +39,7 @@ */ -/datum/action/bloodsucker/masquerade/ActivatePower() +/datum/action/cooldown/bloodsucker/masquerade/ActivatePower() var/mob/living/user = owner var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) @@ -78,14 +78,14 @@ sleep(20) // Check every few ticks that we haven't disabled this power -/datum/action/bloodsucker/masquerade/ContinueActive(mob/living/user) +/datum/action/cooldown/bloodsucker/masquerade/ContinueActive(mob/living/user) // Disable if unable to use power anymore. //if (user.stat == DEAD || user.blood_volume <= 0) // not conscious or soft critor uncon, just dead // return FALSE return ..() // Active, and still Antag -/datum/action/bloodsucker/masquerade/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/masquerade/DeactivatePower(mob/living/user = owner, mob/living/target) ..() // activate = FALSE var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) diff --git a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm index 25f50d7af4..ba6c4634f5 100644 --- a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm +++ b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm @@ -5,12 +5,12 @@ // FOLLOW: Target follows you, spouting random phrases from their history (or maybe Polly's or NPC's vocab?) // ATTACK: Target finds a nearby non-Bloodsucker victim to attack. -/datum/action/bloodsucker/targeted/mesmerize +/datum/action/cooldown/bloodsucker/targeted/mesmerize name = "Mesmerize" desc = "Dominate the mind of a mortal who can see your eyes." button_icon_state = "power_mez" bloodcost = 30 - cooldown = 300 + cooldown_time = 300 target_range = 2 power_activates_immediately = TRUE message_Trigger = "Whom will you subvert to your will?" @@ -18,7 +18,7 @@ bloodsucker_can_buy = TRUE var/success -/datum/action/bloodsucker/targeted/mesmerize/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/CheckCanUse(display_error) . = ..() if(!.) return @@ -34,10 +34,10 @@ return FALSE return TRUE -/datum/action/bloodsucker/targeted/mesmerize/CheckValidTarget(atom/A) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/CheckValidTarget(atom/A) return iscarbon(A) -/datum/action/bloodsucker/targeted/mesmerize/CheckCanTarget(atom/A,display_error) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/CheckCanTarget(atom/A,display_error) // Check: Self if(A == owner) return FALSE @@ -88,7 +88,7 @@ return FALSE return TRUE -/datum/action/bloodsucker/targeted/mesmerize/proc/ContinueTarget(atom/A) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/proc/ContinueTarget(atom/A) var/mob/living/carbon/target = A var/mob/living/L = owner @@ -103,7 +103,7 @@ to_chat(L, "[target] has escaped your gaze!") UnregisterSignal(target, COMSIG_MOVABLE_MOVED) -/datum/action/bloodsucker/targeted/mesmerize/FireTargetedPower(atom/A) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/FireTargetedPower(atom/A) // set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done. var/mob/living/carbon/target = A var/mob/living/L = owner @@ -118,7 +118,7 @@ // 5 second windup addtimer(CALLBACK(src, .proc/apply_effects, L, target, power_time), 6 SECONDS) -/datum/action/bloodsucker/targeted/mesmerize/proc/apply_effects(aggressor, victim, power_time) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/proc/apply_effects(aggressor, victim, power_time) var/mob/living/carbon/target = victim var/mob/living/L = aggressor if(!success) @@ -135,5 +135,5 @@ to_chat(L, "[target] has snapped out of their trance.") -/datum/action/bloodsucker/targeted/mesmerize/ContinueActive(mob/living/user, mob/living/target) +/datum/action/cooldown/bloodsucker/targeted/mesmerize/ContinueActive(mob/living/user, mob/living/target) return ..() && CheckCanUse() && CheckCanTarget(target) diff --git a/code/modules/antagonists/bloodsucker/powers/recuperate.dm b/code/modules/antagonists/bloodsucker/powers/recuperate.dm index 90a2e3ff38..6f6b204299 100644 --- a/code/modules/antagonists/bloodsucker/powers/recuperate.dm +++ b/code/modules/antagonists/bloodsucker/powers/recuperate.dm @@ -1,12 +1,12 @@ -/datum/action/bloodsucker/vassal/recuperate +/datum/action/cooldown/bloodsucker/vassal/recuperate name = "Sanguine Recuperation" desc = "Slowly heal brute damage while active. This process is exhausting, and requires some of your tainted blood." button_icon_state = "power_recup" amToggle = TRUE bloodcost = 5 - cooldown = 100 + cooldown_time = 100 -/datum/action/bloodsucker/vassal/recuperate/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/vassal/recuperate/CheckCanUse(display_error) . = ..() if(!.) return @@ -14,7 +14,7 @@ return FALSE return TRUE -/datum/action/bloodsucker/vassal/recuperate/ActivatePower() +/datum/action/cooldown/bloodsucker/vassal/recuperate/ActivatePower() to_chat(owner, "Your muscles clench and your skin crawls as your master's immortal blood knits your wounds and gives you stamina.") var/mob/living/carbon/C = owner var/mob/living/carbon/human/H @@ -35,5 +35,5 @@ // DONE! //DeactivatePower(owner) -/datum/action/bloodsucker/vassal/recuperate/ContinueActive(mob/living/user, mob/living/target) +/datum/action/cooldown/bloodsucker/vassal/recuperate/ContinueActive(mob/living/user, mob/living/target) return ..() && user.stat <= DEAD && user.blood_volume > 500 diff --git a/code/modules/antagonists/bloodsucker/powers/trespass.dm b/code/modules/antagonists/bloodsucker/powers/trespass.dm index 56b72a562e..9a64fe8cb3 100644 --- a/code/modules/antagonists/bloodsucker/powers/trespass.dm +++ b/code/modules/antagonists/bloodsucker/powers/trespass.dm @@ -1,12 +1,12 @@ -/datum/action/bloodsucker/targeted/trespass +/datum/action/cooldown/bloodsucker/targeted/trespass name = "Trespass" desc = "Become mist and advance two tiles in one direction, ignoring all obstacles except for walls. Useful for skipping past doors and barricades." button_icon_state = "power_tres" bloodcost = 10 - cooldown = 80 + cooldown_time = 80 amToggle = FALSE //target_range = 2 @@ -16,7 +16,7 @@ var/turf/target_turf // We need to decide where we're going based on where we clicked. It's not actually the tile we clicked. -/datum/action/bloodsucker/targeted/trespass/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/targeted/trespass/CheckCanUse(display_error) . = ..() if(!.) return @@ -26,7 +26,7 @@ return TRUE -/datum/action/bloodsucker/targeted/trespass/CheckValidTarget(atom/A) +/datum/action/cooldown/bloodsucker/targeted/trespass/CheckValidTarget(atom/A) // Can't target my tile if(A == get_turf(owner) || get_turf(A) == get_turf(owner)) return FALSE @@ -34,7 +34,7 @@ return TRUE // All we care about is destination. Anything you click is fine. -/datum/action/bloodsucker/targeted/trespass/CheckCanTarget(atom/A, display_error) +/datum/action/cooldown/bloodsucker/targeted/trespass/CheckCanTarget(atom/A, display_error) // NOTE: Do NOT use ..()! We don't want to check distance or anything. // Get clicked tile @@ -60,7 +60,7 @@ return TRUE -/datum/action/bloodsucker/targeted/trespass/FireTargetedPower(atom/A) +/datum/action/cooldown/bloodsucker/targeted/trespass/FireTargetedPower(atom/A) // set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done. // Find target turf, at or below Atom @@ -87,7 +87,7 @@ user.invisibility = INVISIBILITY_MAXIMUM // LOSE CUFFS - + // Wait... sleep(mist_delay / 2) diff --git a/code/modules/antagonists/bloodsucker/powers/veil.dm b/code/modules/antagonists/bloodsucker/powers/veil.dm index b170b9d442..9b04a6f774 100644 --- a/code/modules/antagonists/bloodsucker/powers/veil.dm +++ b/code/modules/antagonists/bloodsucker/powers/veil.dm @@ -1,10 +1,10 @@ -/datum/action/bloodsucker/veil +/datum/action/cooldown/bloodsucker/veil name = "Veil of Many Faces" desc = "Disguise yourself in the illusion of another identity." button_icon_state = "power_veil" bloodcost = 15 - cooldown = 100 + cooldown_time = 100 amToggle = TRUE bloodsucker_can_buy = TRUE warn_constant_cost = TRUE @@ -29,7 +29,7 @@ var/list/prev_features // For lizards and such -/datum/action/bloodsucker/veil/CheckCanUse(display_error) +/datum/action/cooldown/bloodsucker/veil/CheckCanUse(display_error) . = ..() if(!.) return @@ -37,7 +37,7 @@ return TRUE -/datum/action/bloodsucker/veil/ActivatePower() +/datum/action/cooldown/bloodsucker/veil/ActivatePower() cast_effect() // POOF @@ -47,14 +47,14 @@ Disguise_FaceName() -/datum/action/bloodsucker/veil/proc/Disguise_Outfit() +/datum/action/cooldown/bloodsucker/veil/proc/Disguise_Outfit() // Step One: Back up original items -/datum/action/bloodsucker/veil/proc/Disguise_FaceName() +/datum/action/cooldown/bloodsucker/veil/proc/Disguise_FaceName() // Change Name/Voice var/mob/living/carbon/human/H = owner @@ -112,7 +112,7 @@ sleep(50) -/datum/action/bloodsucker/veil/DeactivatePower(mob/living/user = owner, mob/living/target) +/datum/action/cooldown/bloodsucker/veil/DeactivatePower(mob/living/user = owner, mob/living/target) ..() if (ishuman(user)) var/mob/living/carbon/human/H = user @@ -149,7 +149,7 @@ cast_effect() // POOF // CAST EFFECT // // General effect (poof, splat, etc) when you cast. Doesn't happen automatically! -/datum/action/bloodsucker/veil/proc/cast_effect() +/datum/action/cooldown/bloodsucker/veil/proc/cast_effect() // Effect playsound(get_turf(owner), 'sound/magic/smoke.ogg', 20, 1) var/datum/effect_system/steam_spread/puff = new /datum/effect_system/steam_spread/() diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm index d825fa048e..3e48fca317 100644 --- a/code/modules/antagonists/changeling/cellular_emporium.dm +++ b/code/modules/antagonists/changeling/cellular_emporium.dm @@ -80,7 +80,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 7ce9da633e..714150fc93 100644 --- a/code/modules/antagonists/changeling/powers/fakedeath.dm +++ b/code/modules/antagonists/changeling/powers/fakedeath.dm @@ -44,7 +44,7 @@ name = "Reviving Stasis" desc = "We fall into a stasis, allowing us to regenerate and trick our enemies. Costs 15 chemicals." button_icon_state = "fake_death" - UpdateButtonIcon() + UpdateButtons() chemical_cost = 15 to_chat(user, "We have revived ourselves.") @@ -56,7 +56,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/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm index 3298196de7..c4528b6ebc 100644 --- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm +++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm @@ -411,6 +411,6 @@ Q.name = "[quickbind_slot.name] ([Q.scripture_index])" Q.desc = quickbind_slot.quickbind_desc Q.button_icon_state = quickbind_slot.name - Q.UpdateButtonIcon() + Q.UpdateButtons() if(isliving(loc)) Q.Grant(loc) diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index 0efde2d3db..93280c4222 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -2,16 +2,11 @@ name = "Prepare Blood Magic" button_icon_state = "carve" desc = "Prepare blood magic by carving runes into your flesh. This rite is most effective with an empowering rune" + default_button_position = DEFAULT_BLOODSPELLS var/list/spells = list() var/channeling = FALSE var/holy_dispel = 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) @@ -23,15 +18,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() if(holy_dispel) @@ -99,6 +100,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) @@ -106,9 +109,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) @@ -273,7 +274,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("You have exhausted the spell's power!") qdel(src) @@ -325,7 +326,7 @@ qdel(src) desc = base_desc desc += "
Has [charges] use\s remaining." - UpdateButtonIcon() + UpdateButtons() /datum/action/innate/cult/blood_spell/manipulation name = "Blood Rites" @@ -373,7 +374,7 @@ source.charges = uses source.desc = source.base_desc source.desc += "
Has [uses] use\s remaining." - source.UpdateButtonIcon() + source.UpdateButtons() ..() /obj/item/melee/blood_magic/attack_self(mob/living/user) @@ -400,7 +401,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 a522285210..4d15654411 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -795,14 +795,13 @@ desc = "Call the blood spear back to your hand!" background_icon_state = "bg_demon" button_icon_state = "bloodspear" + default_button_position = "6:157,4:-2" var/obj/item/cult_spear/spear var/cooldown = 0 /datum/action/innate/cult/spear/Grant(mob/user, obj/blood_spear) . = ..() spear = blood_spear - button.screen_loc = "6:157,4:-2" - button.moved = "6:157,4:-2" /datum/action/innate/cult/spear/Activate() if(owner == spear.loc || cooldown > world.time) diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm index 885921a944..4eee03303d 100644 --- a/code/modules/antagonists/revenant/revenant_abilities.dm +++ b/code/modules/antagonists/revenant/revenant_abilities.dm @@ -176,7 +176,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 e5fa38a415..a7ed574c87 100644 --- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm +++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm @@ -33,9 +33,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( /datum/action/innate/ai/New() ..() - if(uses > 1) - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc + desc = "[desc] It has [uses] use\s remaining." /datum/action/innate/ai/Grant(mob/living/L) . = ..() @@ -63,7 +61,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( if(!silent) to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() + UpdateButtons() return if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! to_chat(owner, "[name] has run out of uses!") @@ -92,7 +90,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( if(!silent) to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() + UpdateButtons() return if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! to_chat(owner, "[name] has run out of uses!") @@ -203,7 +201,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( 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() temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" processing_time -= AM.cost @@ -759,6 +757,10 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( uses = 3 auto_use_uses = FALSE +/datum/action/innate/ai/blackout/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + /datum/action/innate/ai/blackout/Activate() for(var/obj/machinery/power/apc/apc in GLOB.apcs_list) if(prob(30 * apc.overload)) @@ -768,7 +770,10 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( to_chat(owner, "Overcurrent applied to the powernet.") owner.playsound_local(owner, "sparks", 50, 0) adjust_uses(-1) - + 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() //Disable Emergency Lights /datum/AI_Module/small/emergency_lights @@ -816,6 +821,10 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( auto_use_uses = FALSE cooldown_period = 30 +/datum/action/innate/ai/reactivate_cameras/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + /datum/action/innate/ai/reactivate_cameras/Activate() var/fixed_cameras = 0 for(var/V in GLOB.cameranet.cameras) @@ -830,7 +839,10 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( to_chat(owner, "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(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/large/upgrade_cameras diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index c1399ce646..940e8bf622 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -134,16 +134,19 @@ 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 - var/mouseControlObject = 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. Weakref + var/datum/weakref/middle_drag_atom_ref /// Messages currently seen by this client var/list/seen_messages /// viewsize datum for holding our view size - var/datum/viewData/view_size + var/datum/view_data/view_size /// our current tab var/stat_tab diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 6ca963df73..6036ab6ff1 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -881,11 +881,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( click_intercept_time = 0 //Reset and return. Next click should work, but not this one. return click_intercept_time = 0 //Just reset. Let's not keep re-checking forever. - var/list/L = params2list(params) + var/ab = FALSE + var/list/modifiers = params2list(params) - if(L["drag"]) + var/dragged = LAZYACCESS(modifiers, DRAG) + if(dragged && !LAZYACCESS(modifiers, dragged)) //I don't know what's going on here, but I don't trust it return + 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) if (!holder && !ignore_spam && mcl) var/minute = round(world.time, 600) @@ -901,6 +906,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( clicklimiter[ADMINSWARNED_AT] = minute msg += " Administrators have been informed." + if (ab) + log_game("[key_name(src)] is using the middle click aimbot exploit") + message_admins("[ADMIN_LOOKUPFLW(usr)] [ADMIN_KICK(usr)] is using the middle click aimbot exploit") log_click(object, location, control, params, src, "lockout (spam - minute)", TRUE) log_game("[key_name(src)] Has hit the per-minute click limit of [mcl] clicks in a given game minute") message_admins("[ADMIN_LOOKUPFLW(src)] [ADMIN_KICK(usr)] Has hit the per-minute click limit of [mcl] clicks in a given game minute") @@ -929,6 +937,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( // so that the visual focus indicator matches reality. winset(src, null, "input.background-color=[COLOR_INPUT_DISABLED]") + 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.dm b/code/modules/client/preferences.dm index e50539f91d..d6d50c9dbc 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -65,7 +65,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/screentip_pref = SCREENTIP_PREFERENCE_ENABLED var/screentip_color = "#ffd391" var/screentip_images = TRUE - var/buttons_locked = FALSE var/hotkeys = FALSE ///Runechat preference. If true, certain messages will be displayed on the map, not ust on the chat area. Boolean. @@ -1070,8 +1069,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Runechat message char limit: [max_chat_length]
" dat += "See Runechat for non-mobs: [see_chat_non_mob ? "Enabled" : "Disabled"]
" dat += "
" - dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" - dat += "
" dat += "PDA Color: [pda_color] Change
" dat += "PDA Style: [pda_style]
" dat += "PDA Reskin: [pda_skin]
" @@ -3001,8 +2998,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) chat_on_map = !chat_on_map if("see_chat_non_mob") see_chat_non_mob = !see_chat_non_mob - if("action_buttons") - buttons_locked = !buttons_locked if("tgui_fancy") tgui_fancy = !tgui_fancy if("outline_enabled") diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 6bb704f8a5..afbaba72cd 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -435,7 +435,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["see_chat_non_mob"] >> see_chat_non_mob S["tgui_fancy"] >> tgui_fancy S["tgui_lock"] >> tgui_lock - S["buttons_locked"] >> buttons_locked S["windowflash"] >> windowflashing S["be_special"] >> be_special @@ -513,7 +512,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob)) tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy)) tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock)) - buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked)) windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing)) default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) toggles = sanitize_integer(toggles, 0, 16777215, initial(toggles)) @@ -626,7 +624,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["see_chat_non_mob"], see_chat_non_mob) WRITE_FILE(S["tgui_fancy"], tgui_fancy) WRITE_FILE(S["tgui_lock"], tgui_lock) - WRITE_FILE(S["buttons_locked"], buttons_locked) WRITE_FILE(S["windowflash"], windowflashing) WRITE_FILE(S["be_special"], be_special) WRITE_FILE(S["default_slot"], default_slot) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 05ef6f18e0..6d569fb4e0 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -162,8 +162,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)) @@ -209,7 +209,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) target.name = initial(picked_item.name) @@ -435,9 +435,9 @@ CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/head/chameleon) 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() CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/mask/chameleon) name = "gas mask" @@ -489,9 +489,9 @@ CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/mask/chameleon) 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, "[src] does not have a voice changer.") diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 878b54de38..52bf7c4ccb 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -402,7 +402,7 @@ BLIND // can't see anything C.head_update(src, forced = 1) for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() return TRUE /obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm index 013084e38f..395eeaeaa6 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -62,7 +62,7 @@ update_icon() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/glasses/meson/engine/attack_self(mob/user) toggle_mode(user, TRUE) diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index ff78b51909..4f370866b3 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -389,7 +389,7 @@ set_light(0) for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/head/helmet/durathread name = "makeshift helmet" diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index a2d13822f8..1c9b15b398 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -115,7 +115,7 @@ user.update_inv_wear_mask() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() to_chat(user, "Your Clown Mask has now morphed into [choice], all praise the Honkmother!") return TRUE @@ -181,7 +181,7 @@ user.update_inv_wear_mask() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() to_chat(user, "Your Mime Mask has now morphed into [choice]!") return TRUE @@ -274,7 +274,7 @@ user.update_inv_wear_mask() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() to_chat(M, "The Tiki Mask has now changed into the [choice] Mask!") return TRUE diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index f96fe2ac2a..8aead31d2f 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -109,7 +109,7 @@ user.update_inv_wear_mask() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() to_chat(user, "Your Joy mask now has a [choice] Emotion!") return 1 @@ -475,6 +475,6 @@ user.update_inv_wear_mask() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() to_chat(user, "Your paper mask now has a [choice] symbol!") return 1 diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 726b284632..00da8bf9d8 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -36,7 +36,7 @@ user.update_gravity(user.has_gravity()) for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/shoes/magboots/negates_gravity() return clothing_flags & NOSLIP diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm index 5ec0fad2e6..d3b51cd444 100644 --- a/code/modules/clothing/spacesuits/chronosuit.dm +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -91,7 +91,7 @@ if(camera) camera.remove_target_ui() camera.forceMove(user) - teleport_now.UpdateButtonIcon() + teleport_now.UpdateButtons() /obj/item/clothing/suit/space/chronos/proc/chronowalk(atom/location) var/mob/living/carbon/human/user = src.loc @@ -105,7 +105,7 @@ if(camera) camera.remove_target_ui() - teleport_now.UpdateButtonIcon() + teleport_now.UpdateButtons() var/list/nonsafe_slots = list(ITEM_SLOT_BELT, ITEM_SLOT_BACK) var/list/exposed = list() diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index c946002fa2..a1fa2bf42b 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -345,7 +345,7 @@ C.head_update(src, forced = 1) for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode if(linkedsuit) @@ -937,7 +937,7 @@ set_light(0) for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/head/helmet/space/hardsuit/lavaknight/update_overlays() . = ..() diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 25cbf27cf6..bb2b1bde5a 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -139,7 +139,7 @@ usr.update_inv_wear_suit() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/clothing/suit/toggle/examine(mob/user) . = ..() diff --git a/code/modules/keybindings/keybind/mob.dm b/code/modules/keybindings/keybind/mob.dm index 15911d831e..d5b8eb1a38 100644 --- a/code/modules/keybindings/keybind/mob.dm +++ b/code/modules/keybindings/keybind/mob.dm @@ -85,6 +85,6 @@ description = "Immediately examine anything you're hovering your mouse over." /datum/keybinding/mob/examine_immediate/down(client/user) - var/atom/A = user.mouseObject + var/atom/A = WEAKREF(user.mouse_object_ref) if(A) A.attempt_examinate(user.mob) 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 a5ea6e1c89..181850d541 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm @@ -200,7 +200,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() /obj/effect/proc_holder/alien/neurotoxin/InterceptClickOn(mob/living/caller, params, atom/target) if(..()) @@ -333,7 +333,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 1 /mob/living/carbon/alien/adjustPlasma(amount) diff --git a/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm b/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm index 482268f8f5..177444503b 100644 --- a/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm +++ b/code/modules/mob/living/carbon/human/innate_abilities/blobform.dm @@ -37,7 +37,7 @@ to_chat(owner, "There's something stuck to your hand, stopping you from transforming!") return if(IsAvailable()) - UpdateButtonIcon() + UpdateButtons() var/mutcolor = owner.get_ability_property(INNATE_ABILITY_SLIME_BLOBFORM, PROPERTY_BLOBFORM_COLOR) || ("#" + H.dna.features["mcolor"]) if(!is_puddle) if(CHECK_MOBILITY(H, MOBILITY_USE)) //if we can use items, we can turn into a puddle @@ -78,7 +78,7 @@ owner.update_antag_overlays() transforming = FALSE - UpdateButtonIcon() + UpdateButtons() else detransform() else @@ -109,4 +109,4 @@ squeak.RemoveComponent() H.regenerate_icons() transforming = FALSE - UpdateButtonIcon() + UpdateButtons() 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 c4be0d3948..60a7e945a3 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -480,9 +480,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 - sleep(cooldown + 5) - UpdateButtonIcon() //action icon looks available again + UpdateButtons() //action icon looks unavailable + //action icon looks available again + 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 e8042dfac9..f88de43a3b 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -82,7 +82,7 @@ var/datum/action/innate/ability/regrowth = H.ability_actions[INNATE_ABILITY_LIMB_REGROWTH] if(regrowth) - regrowth.UpdateButtonIcon() + regrowth.UpdateButtons() return FALSE // to let living/handle_blood know that the species is handling blood instead @@ -472,9 +472,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) @@ -514,7 +514,7 @@ name = "Eject Extract" desc = "Eject your current slime extract." -/datum/action/innate/integrate_extract/UpdateButtonIcon(status_only, force) +/datum/action/innate/integrate_extract/UpdateButton(atom/movable/screen/movable/action_button/button, status_only, force) if(!species || !species.current_extract) button_icon_state = "slimeconsume" else diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f6a2f1cb38..d8a8d5c471 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -636,7 +636,7 @@ if(mind) for(var/S in mind.spell_list) var/obj/effect/proc_holder/spell/spell = S - spell.updateButtonIcon() + spell.UpdateButton() //proc used to remove all immobilisation effects + reset stamina /mob/living/proc/remove_CC(should_update_mobility = TRUE) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 13240b2734..dacfe3a863 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -241,7 +241,7 @@ // before doing anything, check if the user moused over them properly if(!client) return BLOCK_NONE - var/found = attacker == client.mouseObject + var/found = attacker == WEAKREF(client.mouse_object_ref) if(!found) for(var/i in client.moused_over_objects) if(i == object) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 7d37039fa1..a65e7a71da 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -44,6 +44,9 @@ var/can_repair_constructs = FALSE var/can_repair_self = FALSE var/runetype + var/datum/action/innate/cult/create_rune/our_rune + /// Theme controls color. THEME_CULT is red THEME_WIZARD is purple and THEME_HOLY is blue + var/theme = "cult" var/datum/mind/original_mind /mob/living/simple_animal/hostile/construct/Initialize(mapload) @@ -51,24 +54,24 @@ update_health_hud() 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) - var/datum/action/innate/cult/create_rune/CR = new runetype(src) - CR.Grant(src) var/pos = 2+spellnum*31 - CR.button.screen_loc = "6:[pos],4:-2" - CR.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]") /mob/living/simple_animal/hostile/construct/Destroy() original_mind = null + QDEL_NULL(our_rune) . = ..() /mob/living/simple_animal/hostile/construct/death() diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm index 1dedf8cd29..88f25eb5ce 100644 --- a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm +++ b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm @@ -143,7 +143,7 @@ var/datum/action/boss/AB = ab if(!boss.client && (!AB.req_statuses || (boss.AIStatus in AB.req_statuses)) && prob(AB.usage_probability) && AB.Trigger()) break - AB.UpdateButtonIcon(TRUE) + AB.UpdateButtons(TRUE) /datum/boss_active_timed_battle/Destroy() 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 23a0d44d56..385d37f5a4 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -336,7 +336,7 @@ if(L.blood_volume && (L.stat != DEAD || !consumed_mobs[L.tag])) //if they're not dead, you can consume them anyway consumed_mobs[L.tag] = TRUE fed++ - lay_eggs.UpdateButtonIcon(TRUE) + lay_eggs.UpdateButtons(TRUE) visible_message("[src] sticks a proboscis into [L] and sucks a viscous substance out.","You suck the nutriment out of [L], feeding you enough to lay a cluster of eggs.") L.death() //you just ate them, they're dead. else @@ -402,7 +402,7 @@ /obj/effect/proc_holder/wrap/update_icon() action.button_icon_state = "wrap_[active]" - action.UpdateButtonIcon() + action.UpdateButtons() /obj/effect/proc_holder/wrap/Trigger(mob/living/simple_animal/hostile/poison/giant_spider/nurse/user) if(!istype(user)) @@ -482,7 +482,7 @@ C.poison_per_bite = S.poison_per_bite C.faction = S.faction.Copy() S.fed-- - UpdateButtonIcon(TRUE) + UpdateButtons(TRUE) S.busy = SPIDER_IDLE S.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 1f5cace824..3db1d1892b 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 @@ -72,19 +72,48 @@ While using this makes the system rely on OnFire, it still gives options for tim icon_icon = 'icons/mob/actions/actions_elites.dmi' button_icon_state = "" background_icon_state = "bg_default" - var/mob/living/simple_animal/hostile/asteroid/elite/M var/chosen_message var/chosen_attack_num = 0 +/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 = "" + else + button.maptext = "[round(timeleft/10, 0.1)]" + /datum/action/innate/elite_attack/Grant(mob/living/L) if(istype(L, /mob/living/simple_animal/hostile/asteroid/elite)) - M = L + START_PROCESSING(SSfastprocess, src) return ..() return FALSE /datum/action/innate/elite_attack/Activate() - M.chosen_attack = chosen_attack_num - to_chat(M, chosen_message) + var/mob/living/simple_animal/hostile/asteroid/elite/elite_owner = owner + elite_owner.chosen_attack = chosen_attack_num + to_chat(elite_owner, chosen_message) /mob/living/simple_animal/hostile/asteroid/elite/updatehealth() . = ..() diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index be03e8a9c9..0f6bd1aee7 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -426,7 +426,7 @@ for(var/obj/item/I in U.held_items) if(istype(I, /obj/item/turret_control)) qdel(I) - UpdateButtonIcon() + UpdateButtons() return else playsound(PE,'sound/mecha/mechmove01.ogg', 50, TRUE) @@ -443,7 +443,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/TC = new /obj/item/turret_control() U.put_in_hands(TC) - UpdateButtonIcon() + UpdateButtons() /obj/item/turret_control diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index a0f98f4046..9efbe1cffb 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -142,7 +142,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() . = ..() @@ -219,7 +219,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() return TRUE /obj/item/gun/equipped(mob/living/user, slot) @@ -670,7 +670,7 @@ update_icon() for(var/X in actions) var/datum/action/A = X - A.UpdateButtonIcon() + A.UpdateButtons() /obj/item/gun/update_overlays() . = ..() diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index 6f70998834..98de59b00e 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -200,7 +200,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(fire_check() && can_trigger_gun(M)) sync_ammo() - do_fire(M.client.mouseObject, M, FALSE, M.client.mouseParams, M.zone_selected) + var/atom/target = M.client.mouse_object_ref?.resolve() + if(target) + afterattack(target, M, FALSE, M.client.mouseParams) stop_aiming() QDEL_LIST(current_tracers) return ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 0aee51bb84..aa90ea470a 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -939,12 +939,10 @@ var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 //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 = arctan(y - oy, x - ox) return list(angle, p_x, p_y) diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 4be592b919..68207491a5 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -165,7 +165,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) @@ -243,7 +243,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 += 2 //processes 5 times per second instead of 10. if(charge_counter >= charge_max) - action.UpdateButtonIcon() + action.UpdateButtons() charge_counter = charge_max recharging = FALSE @@ -259,7 +259,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) @@ -321,7 +321,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)) @@ -438,8 +438,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/UpdateButton(atom/movable/screen/movable/action_button/button, status_only, force) + action.UpdateButtons(status_only, force) /obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) //Checks for obstacles from A to B diff --git a/code/modules/spells/spell_types/aimed.dm b/code/modules/spells/spell_types/aimed.dm index aa2a2e4083..62f65688d9 100644 --- a/code/modules/spells/spell_types/aimed.dm +++ b/code/modules/spells/spell_types/aimed.dm @@ -43,7 +43,7 @@ if(!action) return 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 7b942dee27..1c32f04710 100644 --- a/code/modules/spells/spell_types/pointed/pointed.dm +++ b/code/modules/spells/spell_types/pointed/pointed.dm @@ -63,7 +63,7 @@ action.button_icon_state = "[action_icon_state]1" else action.button_icon_state = "[action_icon_state]" - 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/taeclowndo.dm b/code/modules/spells/spell_types/taeclowndo.dm index 9d6d71a89f..3566a36a25 100644 --- a/code/modules/spells/spell_types/taeclowndo.dm +++ b/code/modules/spells/spell_types/taeclowndo.dm @@ -54,7 +54,7 @@ else action.button_icon_state = action_icon_state - action.UpdateButtonIcon() + action.UpdateButtons() return ////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm index a23e16cf88..4f0ac2964f 100644 --- a/code/modules/spells/spell_types/touch_attacks.dm +++ b/code/modules/spells/spell_types/touch_attacks.dm @@ -18,7 +18,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 f41f299439..6f55cb0c46 100644 --- a/code/modules/surgery/dental_implant.dm +++ b/code/modules/surgery/dental_implant.dm @@ -18,10 +18,11 @@ user.transferItemToLoc(tool, target, TRUE) - var/datum/action/item_action/hands_free/activate_pill/P = new(tool) - P.button.name = "Activate [tool.name]" - P.target = tool - P.Grant(target) //The pill never actually goes in an inventory slot, so the owner doesn't inherit actions from it + var/datum/action/item_action/hands_free/activate_pill/pill_action = new(tool) + 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 user.visible_message("[user] wedges \the [tool] into [target]'s [parse_zone(target_zone)]!", "You wedge [tool] into [target]'s [parse_zone(target_zone)].") return 1 diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm index 01c74122bd..3ca7fd590d 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 2743bc03d5..b66d48d6c6 100644 --- a/code/modules/tooltip/tooltip.html +++ b/code/modules/tooltip/tooltip.html @@ -136,21 +136,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 36c5f75984..8567e31b07 100644 --- a/code/modules/vehicles/mecha/combat/durand.dm +++ b/code/modules/vehicles/mecha/combat/durand.dm @@ -194,7 +194,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(light_range, light_power, light_color) diff --git a/code/modules/vehicles/mecha/mecha_actions.dm b/code/modules/vehicles/mecha/mecha_actions.dm index 09ccaad2c2..976ffea40a 100644 --- a/code/modules/vehicles/mecha/mecha_actions.dm +++ b/code/modules/vehicles/mecha/mecha_actions.dm @@ -37,7 +37,7 @@ button_icon_state = "mech_internals_[chassis.use_internal_tank ? "on" : "off"]" to_chat(chassis.occupants, "[icon2html(chassis, owner)]Now 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_cycle_equip name = "Cycle Equipment" @@ -61,7 +61,7 @@ to_chat(owner, "[icon2html(chassis, owner)]You select [chassis.selected].") send_byjax(chassis.occupants,"exosuit.browser","eq_list",chassis.get_equipment_list()) button_icon_state = "mech_cycle_equip_on" - UpdateButtonIcon() + UpdateButtons() return var/number = 0 for(var/equipment in available_equipment) @@ -77,7 +77,7 @@ to_chat(owner, "[icon2html(chassis, owner)]You switch to [chassis.selected].") button_icon_state = "mech_cycle_equip_on" send_byjax(chassis.occupants,"exosuit.browser","eq_list",chassis.get_equipment_list()) - UpdateButtonIcon() + UpdateButtons() return @@ -100,7 +100,7 @@ chassis.set_light(0) to_chat(owner, "[icon2html(chassis, owner)]Toggled lights [(chassis.mecha_flags & LIGHTS_ON)?"on":"off"].") 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" @@ -140,7 +140,7 @@ for(var/occupant in occupants) var/datum/action/action = LAZYACCESSASSOC(occupant_actions, occupant, /datum/action/vehicle/sealed/mecha/strafe) - action?.UpdateButtonIcon() + action?.UpdateButtons() //////////////////////////////////////// Specific Ability Actions /////////////////////////////////////////////// //Need to be granted by the mech type, Not default abilities. @@ -176,7 +176,7 @@ chassis.movedelay = initial(chassis.movedelay) chassis.step_energy_drain = chassis.normal_step_energy_drain to_chat(owner, "[icon2html(chassis, owner)]You disable leg actuators overload.") - UpdateButtonIcon() + UpdateButtons() /datum/action/vehicle/sealed/mecha/mech_smoke name = "Smoke" @@ -208,7 +208,7 @@ SEND_SOUND(owner, sound('sound/mecha/imag_enh.ogg',volume=50)) else owner.client.view_size.resetToDefault() //Let's not let this stack shall we? - UpdateButtonIcon() + UpdateButtons() /datum/action/vehicle/sealed/mecha/mech_switch_damtype name = "Reconfigure arm microtool arrays" @@ -231,7 +231,7 @@ chassis.damtype = new_damtype button_icon_state = "mech_damtype_[new_damtype]" playsound(chassis, 'sound/mecha/mechmove01.ogg', 50, TRUE) - UpdateButtonIcon() + UpdateButtons() ///swap seats, for two person mecha /datum/action/vehicle/sealed/mecha/swap_seat @@ -272,4 +272,4 @@ chassis.phasing = !chassis.phasing button_icon_state = "mech_phasing_[chassis.phasing ? "on" : "off"]" to_chat(owner, "[icon2html(chassis, owner)]En":"#f00\">Dis"]abled phasing.") - UpdateButtonIcon() + UpdateButtons() diff --git a/icons/effects/mouse_pointers/screen_drag.dmi b/icons/effects/mouse_pointers/screen_drag.dmi new file mode 100644 index 0000000000..48e4fce85f 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 0000000000..ae81ace4d5 Binary files /dev/null and b/icons/hud/64x16_actions.dmi differ diff --git a/icons/mob/screen_ai.dmi b/icons/mob/screen_ai.dmi index 8388ea3f80..e9360926a4 100644 Binary files a/icons/mob/screen_ai.dmi and b/icons/mob/screen_ai.dmi differ diff --git a/icons/mob/screen_alien.dmi b/icons/mob/screen_alien.dmi index fe23a59dfd..b20bfaa405 100644 Binary files a/icons/mob/screen_alien.dmi and b/icons/mob/screen_alien.dmi differ diff --git a/icons/mob/screen_clockwork.dmi b/icons/mob/screen_clockwork.dmi index 268fed4094..0cd15937ed 100644 Binary files a/icons/mob/screen_clockwork.dmi and b/icons/mob/screen_clockwork.dmi differ diff --git a/icons/mob/screen_cyborg.dmi b/icons/mob/screen_cyborg.dmi index 13aa792944..2f976f9d3a 100644 Binary files a/icons/mob/screen_cyborg.dmi and b/icons/mob/screen_cyborg.dmi differ diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index 39e8eb4b45..0b286f9625 100644 Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ diff --git a/icons/mob/screen_midnight.dmi b/icons/mob/screen_midnight.dmi index 3d89504272..ec583eb485 100644 Binary files a/icons/mob/screen_midnight.dmi and b/icons/mob/screen_midnight.dmi differ diff --git a/icons/mob/screen_operative.dmi b/icons/mob/screen_operative.dmi index d51022b98a..fd3879e428 100644 Binary files a/icons/mob/screen_operative.dmi and b/icons/mob/screen_operative.dmi differ diff --git a/icons/mob/screen_plasmafire.dmi b/icons/mob/screen_plasmafire.dmi index f251b8b167..7e1fb8308f 100644 Binary files a/icons/mob/screen_plasmafire.dmi and b/icons/mob/screen_plasmafire.dmi differ diff --git a/icons/mob/screen_retro.dmi b/icons/mob/screen_retro.dmi index e57cefa83e..e7c40db5d3 100644 Binary files a/icons/mob/screen_retro.dmi and b/icons/mob/screen_retro.dmi differ diff --git a/icons/mob/screen_slimecore.dmi b/icons/mob/screen_slimecore.dmi index 01831250b8..3d335a846f 100644 Binary files a/icons/mob/screen_slimecore.dmi and b/icons/mob/screen_slimecore.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 50ff9eba35..2b8e694dd3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -20,6 +20,7 @@ #include "code\_compile_options.dm" #include "code\world.dm" #include "code\__DEFINES\_auxtools.dm" +#include "code\__DEFINES\_click.dm" #include "code\__DEFINES\_globals.dm" #include "code\__DEFINES\_protect.dm" #include "code\__DEFINES\_tick.dm" @@ -160,6 +161,7 @@ #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\dcs\signals\signals_action.dm" #include "code\__DEFINES\dcs\signals\signals_global.dm" #include "code\__DEFINES\dcs\signals\signals_hud.dm" #include "code\__DEFINES\dcs\signals\signals_medical.dm" @@ -169,6 +171,7 @@ #include "code\__DEFINES\dcs\signals\signals_screentips.dm" #include "code\__DEFINES\dcs\signals\signals_subsystem.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_main.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_mouse.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_main.dm" @@ -223,6 +226,7 @@ #include "code\__HELPERS\reagents.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\stat_tracking.dm" #include "code\__HELPERS\text.dm" @@ -281,7 +285,6 @@ #include "code\_onclick\right_item_attack.dm" #include "code\_onclick\right_other_mobs.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"