From 457a091d9afbcc3441fa9b2355ced009ea141fe3 Mon Sep 17 00:00:00 2001 From: Fluffy <65877598+FluffyGhoster@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:04:48 +0100 Subject: [PATCH] Clicks refactor (#20321) Added various events to the dcs system in regards to click handling. Refactored various mouse related procs. Fixed MUI mask. Fixed AI jump on double click. Fixed some runtimes with the click handler system. Updated the click handler system. Fixed fireman carry. --- aurorastation.dme | 2 + .../signals_atom/signals_atom_mouse.dm | 15 + .../signals/signals_mob/signals_mob_main.dm | 13 + .../signals/signals_object/signals_object.dm | 4 + code/__HELPERS/lists.dm | 8 + code/__HELPERS/turfs.dm | 30 +- code/_onclick/ai.dm | 36 +- code/_onclick/click.dm | 410 ++++++++---------- code/_onclick/click_ctrl.dm | 29 ++ code/_onclick/click_handling.dm | 226 ++++++++++ code/_onclick/cyborg.dm | 4 +- code/_onclick/item_attack.dm | 9 +- code/datums/components/_component.dm | 48 +- code/datums/components/eye/_eye.dm | 2 +- code/datums/components/eye/freelook.dm | 14 +- code/modules/admin/admin_verbs.dm | 2 +- code/modules/admin/buildmode/buttons.dm | 2 +- code/modules/admin/buildmode/click_handler.dm | 2 +- code/modules/client/client_procs.dm | 4 + code/modules/clothing/masks/miscellaneous.dm | 2 +- .../modules/heavy_vehicle/mech_interaction.dm | 2 +- code/modules/mob/abstract/freelook/ai/eye.dm | 23 +- code/modules/mob/abstract/freelook/eye.dm | 2 +- code/modules/mob/abstract/ghost/ghost.dm | 2 +- .../ghost/storyteller/storyteller_verbs.dm | 2 +- .../mob/living/carbon/human/update_icons.dm | 4 +- code/modules/mob/mob.dm | 3 +- code/modules/mob/mob_grab.dm | 2 +- code/modules/mob/update_icons.dm | 4 +- code/modules/paperwork/photography.dm | 1 + .../changelogs/fluffyghost-clicksrefactor.yml | 64 +++ 31 files changed, 661 insertions(+), 310 deletions(-) create mode 100644 code/_onclick/click_ctrl.dm create mode 100644 code/_onclick/click_handling.dm create mode 100644 html/changelogs/fluffyghost-clicksrefactor.yml diff --git a/aurorastation.dme b/aurorastation.dme index f2ae630614f..7f37578211e 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -275,6 +275,8 @@ #include "code\_onclick\adjacent.dm" #include "code\_onclick\ai.dm" #include "code\_onclick\click.dm" +#include "code\_onclick\click_ctrl.dm" +#include "code\_onclick\click_handling.dm" #include "code\_onclick\cyborg.dm" #include "code\_onclick\drag_drop.dm" #include "code\_onclick\item_attack.dm" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm index 267af1377e5..b336d67c343 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm @@ -2,6 +2,19 @@ // 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" + #define COMPONENT_ALLOW_EXAMINATE (1<<0) //! Allows the user to examinate regardless of client.eye. +///from base of atom/ShiftClick() +#define COMSIG_SHIFT_CLICKED_ON "shift_clicked_on" + +///from base of atom/base_click_alt_secondary(): (/mob) +#define COMSIG_CLICK_ALT_SECONDARY "click_alt_secondary" + #define COMPONENT_CANCEL_CLICK_ALT_SECONDARY (1<<0) ///from base of atom/MouseDrop(): (/atom/over, /mob/user) #define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" @@ -9,3 +22,5 @@ ///from base of atom/handle_mouse_drop_receive: (/atom/from, /mob/user) #define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" #define COMPONENT_CANCEL_MOUSEDROPPED_ONTO (1<<0) +///from base of mob/MouseWheelOn(): (/atom, delta_x, delta_y, params) +#define COMSIG_MOUSE_SCROLL_ON "mousescroll_on" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index bc24ffed6d0..8ca2026f6d4 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -1,3 +1,14 @@ + +///from base of mob/clickon(): (atom/A, params) +#define COMSIG_MOB_CLICKON "mob_clickon" +///from base of mob/MiddleClickOn(): (atom/A) +#define COMSIG_MOB_MIDDLECLICKON "mob_middleclickon" +///from base of mob/AltClickOn(): (atom/A) +#define COMSIG_MOB_ALTCLICKON "mob_altclickon" + #define COMSIG_MOB_CANCEL_CLICKON (1<<0) +///from base of mob/alt_click_on_secodary(): (atom/A) +#define COMSIG_MOB_ALTCLICKON_SECONDARY "mob_altclickon_secondary" + /// From base of /client/Move(): (list/move_args) #define COMSIG_MOB_CLIENT_PRE_LIVING_MOVE "mob_client_pre_living_move" /// Should we stop the current living movement attempt @@ -20,6 +31,8 @@ ///from base of /obj/item/attack(): (mob/M, mob/user) #define COMSIG_MOB_ITEM_ATTACK "mob_item_attack" +///from base of mob/RangedAttack(): (atom/A, modifiers) +#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" ///From base of mob/update_movespeed():area #define COMSIG_MOB_MOVESPEED_UPDATED "mob_update_movespeed" diff --git a/code/__DEFINES/dcs/signals/signals_object/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object/signals_object.dm index 16f54e6d985..4386c17a90a 100644 --- a/code/__DEFINES/dcs/signals/signals_object/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object/signals_object.dm @@ -20,6 +20,10 @@ ///from base of /obj/item/attack(): (mob/living, mob/living, params) #define COMSIG_ITEM_ATTACK "item_attack" +///from base of obj/item/attack_self(): (/mob) +#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" +//from base of obj/item/attack_self_secondary(): (/mob) +#define COMSIG_ITEM_ATTACK_SELF_SECONDARY "item_attack_self_secondary" ///from base of [obj/item/attack()]: (atom/target, mob/user, proximity_flag, click_parameters) #define COMSIG_ITEM_AFTERATTACK "item_afterattack" diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 8cf9c6714cc..46f9ca38cc0 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -881,3 +881,11 @@ else used_key_list[input_key] = 1 return input_key + +// Gets the first instance that is of the given type (strictly) +/proc/get_instance_of_strict_type(list/L, T) + RETURN_TYPE(/atom) + for(var/key in L) + var/atom/A = key + if(A.type == T) + return A diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm index 7ee4cb962db..c1adc3bba16 100644 --- a/code/__HELPERS/turfs.dm +++ b/code/__HELPERS/turfs.dm @@ -51,18 +51,26 @@ if(turfs.len) return pick(turfs) -/proc/screen_loc2turf(text, turf/origin) - if(!origin) +///Returns a turf based on text inputs, original turf and viewing client +/proc/parse_caught_click_modifiers(list/modifiers, turf/origin, client/viewing_client) + if(!modifiers) return null - var/tZ = splittext(text, ",") - var/tX = splittext(tZ[1], "-") - var/tY = text2num(tX[2]) - tX = splittext(tZ[2], "-") - tX = text2num(tX[2]) - tZ = origin.z - tX = max(1, min(origin.x + 7 - tX, world.maxx)) - tY = max(1, min(origin.y + 7 - tY, world.maxy)) - return locate(tX, tY, tZ) + + var/screen_loc = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",") + var/list/actual_view = getviewsize(viewing_client ? viewing_client.view : world.view) + var/click_turf_x = splittext(screen_loc[1], ":") + var/click_turf_y = splittext(screen_loc[2], ":") + var/click_turf_z = origin.z + + var/click_turf_px = text2num(click_turf_x[2]) + var/click_turf_py = text2num(click_turf_y[2]) + click_turf_x = origin.x + text2num(click_turf_x[1]) - round(actual_view[1] / 2) - 1 + click_turf_y = origin.y + text2num(click_turf_y[1]) - round(actual_view[2] / 2) - 1 + + var/turf/click_turf = locate(clamp(click_turf_x, 1, world.maxx), clamp(click_turf_y, 1, world.maxy), click_turf_z) + LAZYSET(modifiers, ICON_X, "[(click_turf_px - click_turf.pixel_x) + ((click_turf_x - click_turf.x) * ICON_SIZE_X)]") + LAZYSET(modifiers, ICON_Y, "[(click_turf_py - click_turf.pixel_y) + ((click_turf_y - click_turf.y) * ICON_SIZE_Y)]") + return click_turf // This proc will check if a neighboring tile in the stated direction "dir" is dense or not // Will return 1 if it is dense and zero if not diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index e523618ddcb..a77be0dd0e6 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -9,8 +9,9 @@ Note that AI have no need for the adjacency proc, and so this proc is a lot cleaner. */ -/mob/living/silicon/ai/DblClickOn(var/atom/A, params) - if(control_disabled || stat) return +/mob/living/silicon/ai/DblClickOn(atom/A, params) + if(control_disabled || stat) + return if(ismob(A)) ai_actual_track(A) @@ -18,7 +19,7 @@ A.move_camera_by_click() -/mob/living/silicon/ai/ClickOn(var/atom/A, params) +/mob/living/silicon/ai/ClickOn(atom/A, params) if(world.time <= next_click) return next_click = world.time + 1 @@ -27,19 +28,28 @@ return var/list/modifiers = params2list(params) - if(modifiers["shift"] && modifiers["ctrl"]) - CtrlShiftClickOn(A) - return - if(modifiers["middle"]) - MiddleClickOn(A) - return - if(modifiers["shift"]) + if(LAZYACCESS(modifiers, SHIFT_CLICK)) + if(LAZYACCESS(modifiers, MIDDLE_CLICK)) + ShiftMiddleClickOn(A) + return + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlShiftClickOn(A) + return ShiftClickOn(A) return - if(modifiers["alt"]) // alt and alt-gr (rightalt) - AltClickOn(A) + if(LAZYACCESS(modifiers, MIDDLE_CLICK)) + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlMiddleClickOn(A) + else + MiddleClickOn(A, params) return - if(modifiers["ctrl"]) + if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + AltClickSecondaryOn(A) + else + AltClickOn(A) + return + if(LAZYACCESS(modifiers, CTRL_CLICK)) CtrlClickOn(A) return diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 7518ba94274..0edd81b8951 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -6,49 +6,38 @@ // 1 decisecond click delay (above and beyond mob/next_move) /mob/var/next_click = 0 -/* - Before anything else, defer these calls to a per-mobtype handler. This allows us to - remove istype() spaghetti code, but requires the addition of other handler procs to simplify it. - Alternately, you could hardcode every mob's variation in a flat ClickOn() proc; however, - that's a lot of code duplication and is hard to maintain. +/** + * Before anything else, defer these calls to a per-mobtype handler. This allows us to + * remove istype() spaghetti code, but requires the addition of other handler procs to simplify it. + * + * Alternately, you could hardcode every mob's variation in a flat [/mob/proc/ClickOn] proc; however, + * that's a lot of code duplication and is hard to maintain. + * + * Note that this proc can be overridden, and is in the case of screen objects. + */ +/atom/Click(location, control, params) + if(flags_1 & INITIALIZED_1) + SEND_SIGNAL(src, COMSIG_CLICK, location, control, params, usr) - Note that this proc can be overridden, and is in the case of screen objects. -*/ + //Aurora snowflake click handler system + var/datum/click_handler/click_handler = usr.GetClickHandler() + click_handler.OnClick(src, params) -/atom/Click(location,control,params) - var/datum/click_handler/click_handler = usr.GetClickHandler() - click_handler.OnClick(src, params) +/atom/DblClick(location,control,params) + if(flags_1 & INITIALIZED_1) + //Aurora snowflake click handler system + var/datum/click_handler/click_handler = usr.GetClickHandler() + click_handler.OnDblClick(src, params) -/atom/DblClick(var/location, var/control, var/params) - var/datum/click_handler/click_handler = usr.GetClickHandler() - click_handler.OnDblClick(src, params) + //Why god why + if(istype(usr.machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = usr.machine + console.jump_on_click(usr,src) - if(istype(usr.machine,/obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = usr.machine - console.jump_on_click(usr,src) - -/atom/proc/allow_click_through(var/atom/A, var/params, var/mob/user) - return FALSE - -/turf/allow_click_through(var/atom/A, var/params, var/mob/user) - return TRUE - -/atom/proc/RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, var/mob/user) - return FALSE - -/mob/proc/OnMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) - if(istype(loc, /atom)) - var/atom/A = loc - if(A.RelayMouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params, src)) - return - - if(over_object) - if(!incapacitated()) - var/obj/item/gun/gun = get_active_hand() - if(istype(gun)) - set_dir(get_dir(src, over_object)) - gun.Fire(get_turf(over_object), src, params, (get_dist(over_object, src) <= 1), FALSE) +/atom/MouseWheel(delta_x,delta_y,location,control,params) + if(flags_1 & INITIALIZED_1) + usr.MouseWheelOn(src, delta_x, delta_y, params) /** * Standard mob ClickOn() @@ -63,13 +52,18 @@ * * [obj/item/proc/afterattack] (atom,user,adjacent,params) - used both ranged and adjacent * * [mob/proc/RangedAttack] (atom,modifiers) - used only ranged, only used for tk and laser eyes but could be changed */ -/mob/proc/ClickOn(var/atom/A, var/params) - +/mob/proc/ClickOn(atom/A, params) if(world.time <= next_click) // Hard check, before anything else, to avoid crashing return - next_click = world.time + 1 + var/list/modifiers = params2list(params) + + if(SEND_SIGNAL(src, COMSIG_MOB_CLICKON, A, modifiers) & COMSIG_MOB_CANCEL_CLICKON) + return + + /* START Aurora snowflake */ + if(istype(loc, /mob/living/heavy_vehicle) && !(A in src.contents)) var/mob/living/heavy_vehicle/M = loc return M.ClickOn(A, params, src) @@ -82,34 +76,44 @@ return return B.ClickOn(A, params) - var/list/modifiers = params2list(params) - if(modifiers["right"]) - RightClickOn(A) - return TRUE - if(modifiers["shift"] && modifiers["ctrl"]) - CtrlShiftClickOn(A) - return TRUE - if(modifiers["ctrl"] && modifiers["middle"]) - pointed(A) - return TRUE - if(modifiers["middle"]) - MiddleClickOn(A) - return TRUE - if(modifiers["shift"]) - ShiftClickOn(A) - return FALSE - if(modifiers["alt"]) // alt and alt-gr (rightalt) - AltClickOn(A) - return - if(modifiers["ctrl"]) - CtrlClickOn(A) - return TRUE + /* END Aurora snowflake */ - if(stat || paralysis || stunned || weakened) + if(LAZYACCESS(modifiers, SHIFT_CLICK)) + if(LAZYACCESS(modifiers, MIDDLE_CLICK)) + ShiftMiddleClickOn(A) + return + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlShiftClickOn(A) + return + ShiftClickOn(A) + return + if(LAZYACCESS(modifiers, MIDDLE_CLICK)) + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlMiddleClickOn(A) + else + MiddleClickOn(A, params) + return + if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + AltClickSecondaryOn(A) + else + AltClickOn(A) + return + if(LAZYACCESS(modifiers, CTRL_CLICK)) + CtrlClickOn(A) + return + + if(stat || paralysis || stunned || weakened) // In TG it's if(INCAPACITATED_IGNORING(src, INCAPABLE_RESTRAINTS|INCAPABLE_STASIS)) return face_atom(A) // change direction to face what you clicked on + /* START Aurora snowflake */ + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + RightClickOn(A) + return TRUE + /* END Aurora snowflake */ + if(!canClick()) // in the year 2000... return @@ -125,14 +129,30 @@ var/obj/item/W = get_active_hand() - if(W == A) // Handle attack_self - W.attack_self(src) - trigger_aiming(TARGET_CAN_CLICK) - if(hand) - update_inv_l_hand(0) + if(W == A) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + W.attack_self_secondary(src, modifiers) + /* START Aurora snowflake */ + trigger_aiming(TARGET_CAN_CLICK) + if(hand) + update_inv_l_hand(FALSE) + else + update_inv_r_hand(FALSE) + /* END Aurora snowflake */ + return else - update_inv_r_hand(0) - return 1 + W.attack_self(src, modifiers) + /* START Aurora snowflake */ + trigger_aiming(TARGET_CAN_CLICK) + if(hand) + update_inv_l_hand(FALSE) + else + update_inv_r_hand(FALSE) + /* END Aurora snowflake */ + return + + + /* START AURORA SNOWFLAKE CODE */ //Atoms on your person // A is your location but is not a turf; or is on you (backpack); or is on something on you (box in backpack); sdepth is needed here because contents depth does not equate inventory storage depth. @@ -149,7 +169,7 @@ else if(ismob(A)) // No instant mob attacking setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - UnarmedAttack(A, 1) + UnarmedAttack(A, TRUE, modifiers) trigger_aiming(TARGET_CAN_CLICK) return 1 @@ -170,7 +190,7 @@ else if(ismob(A)) // No instant mob attacking setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - UnarmedAttack(A, 1) + UnarmedAttack(A, TRUE, modifiers) trigger_aiming(TARGET_CAN_CLICK) return @@ -183,6 +203,8 @@ trigger_aiming(TARGET_CAN_CLICK) return 1 + /* END AURORA SNOWFLAKE CODE */ + /mob/proc/setClickCooldown(var/timeout) next_move = max(world.time + timeout, next_move) @@ -191,21 +213,28 @@ return 1 return 0 -// Default behavior: ignore double clicks, the second click that makes the doubleclick call already calls for a normal click -/mob/proc/DblClickOn(var/atom/A, var/params) +/// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click) +/mob/proc/DblClickOn(atom/A, params) return -/* - Translates into attack_hand, etc. +/** + * UnarmedAttack: The higest level of mob click chain discounting click itself. + * + * This handles, just "clicking on something" without an item. It translates + * into [atom/proc/attack_hand], [atom/proc/attack_animal] etc. + * + * Note: proximity_flag here is used to distinguish between normal usage (flag=1), + * and usage when clicking on things telekinetically (flag=0). This proc will + * not be called at ranged except with telekinesis. + * + * proximity_flag is not currently passed to attack_hand, and is instead used + * in human click code to allow glove touches only at melee range. + * + * modifiers is a lazy list of click modifiers this attack had, + * used for figuring out different properties of the click, mostly right vs left and such. + */ - Note: proximity_flag here is used to distinguish between normal usage (flag=1), - and usage when clicking on things telekinetically (flag=0). This proc will - not be called at ranged except with telekinesis. - - proximity_flag is not currently passed to attack_hand, and is instead used - in human click code to allow glove touches only at melee range. -*/ -/mob/proc/UnarmedAttack(var/atom/A, var/proximity_flag) +/mob/proc/UnarmedAttack(atom/A, proximity_flag, list/modifiers) return /mob/living/UnarmedAttack(var/atom/A, var/proximity_flag) @@ -229,13 +258,14 @@ * for things like ranged glove touches, spitting alien acid/neurotoxin, * animals lunging, etc. */ -/mob/proc/RangedAttack(atom/A, params) - SHOULD_NOT_SLEEP(TRUE) +/mob/proc/RangedAttack(atom/A, modifiers) + if(SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE if((mutations & LASER_EYES) && a_intent == I_HURT) - LaserEyes(A, params) // moved into a proc below + LaserEyes(A, modifiers) // moved into a proc below return - A.attack_ranged(src, params) + A.attack_ranged(src, modifiers) /* Restrained ClickOn @@ -246,53 +276,44 @@ /mob/proc/RestrainedClickOn(var/atom/A) return -/* - Middle click -*/ -/mob/proc/MiddleClickOn(var/atom/A) +/** + * Middle click + * Mainly used for swapping hands + */ +/mob/proc/MiddleClickOn(atom/A, params) + . = SEND_SIGNAL(src, COMSIG_MOB_MIDDLECLICKON, A, params) + if(. & COMSIG_MOB_CANCEL_CLICKON) + return if(A.handle_middle_mouse_click(src)) return swap_hand() -/mob/living/carbon/human/MiddleClickOn(var/atom/A) +/mob/living/carbon/human/MiddleClickOn(atom/A, params) if(species.handle_middle_mouse_click(src, A)) return return ..() -// In case of use break glass -/* -/atom/proc/MiddleClick(var/mob/M as mob) - return -*/ - -/* - Shift click - For most mobs, examine. - This is overridden in ai.dm -*/ -/mob/proc/ShiftClickOn(var/atom/A) +/** + * Shift click + * For most mobs, examine. + * This is overridden in ai.dm + */ +/mob/proc/ShiftClickOn(atom/A) A.ShiftClick(src) return -/atom/proc/ShiftClick(var/mob/user) - if(user.can_examine()) +/atom/proc/ShiftClick(mob/user) + SEND_SIGNAL(src, COMSIG_SHIFT_CLICKED_ON, user) + var/flags = SEND_SIGNAL(user, COMSIG_CLICK_SHIFT, src) + if(flags & COMSIG_MOB_CANCEL_CLICKON) + return + if(user.can_examine() || flags & COMPONENT_ALLOW_EXAMINATE) examinate(user, src) -/* - Ctrl click - For most objects, pull -*/ -/mob/proc/CtrlClickOn(var/atom/A) - A.CtrlClick(src) +/mob/proc/ShiftMiddleClickOn(atom/A) + src.pointed(A) return -/atom/proc/CtrlClick(var/mob/user) - return - -/atom/movable/CtrlClick(var/mob/user) - if(Adjacent(user)) - user.start_pulling(src) - /* Alt click Unused except for AI @@ -317,16 +338,40 @@ /atom/proc/RightClick(mob/user) return -/* - Control+Shift click - Unused except for AI -*/ -/mob/proc/CtrlShiftClickOn(var/atom/A) - A.CtrlShiftClick(src) - return +///Main proc for secondary alt click +/mob/proc/AltClickSecondaryOn(atom/target) + base_click_alt_secondary(target) -/atom/proc/CtrlShiftClick(var/mob/user) - return +/** + * ### Base proc for alt click interaction right click. + * + * If you wish to add custom `click_alt_secondary` behavior for a single type, use that proc. + */ +/mob/proc/base_click_alt_secondary(atom/target) + SHOULD_NOT_OVERRIDE(TRUE) + + //Hook on the mob to intercept the click + if(SEND_SIGNAL(src, COMSIG_MOB_ALTCLICKON_SECONDARY, target) & COMSIG_MOB_CANCEL_CLICKON) + return + + //Hook on the atom to intercept the click + if(SEND_SIGNAL(target, COMSIG_CLICK_ALT_SECONDARY, src) & COMPONENT_CANCEL_CLICK_ALT_SECONDARY) + return + + // If it has a custom click_alt_secondary then do that + //if(can_perform_action(target, target.interaction_flags_click | SILENT_ADJACENCY)) + target.click_alt_secondary(src) + +/** + * ## Custom alt click secondary interaction + * Override this to change default alt right click behavior. + * + * ### Guard clauses + * Consider adding `interaction_flags_click` before adding unique guard clauses. + **/ +/atom/proc/click_alt_secondary(mob/user) + SHOULD_CALL_PARENT(FALSE) + return NONE /* Misc helpers @@ -399,15 +444,20 @@ var/global/list/click_catchers /atom/movable/screen/click_catcher/Click(location, control, params) var/list/modifiers = params2list(params) - if(modifiers["middle"] && istype(usr, /mob/living/carbon)) + if(LAZYACCESS(modifiers, MIDDLE_CLICK) && iscarbon(usr)) var/mob/living/carbon/C = usr C.swap_hand() else - var/turf/T = screen_loc2turf(screen_loc, get_turf(usr)) - if(T) - T.Click(location, control, params) + var/turf/click_turf = parse_caught_click_modifiers(modifiers, get_turf(usr.client ? usr.client.eye : usr), usr.client) + if (click_turf) + modifiers["catcher"] = TRUE + click_turf.Click(click_turf, control, list2params(modifiers)) . = 1 +/// MouseWheelOn +/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params) + SEND_SIGNAL(src, COMSIG_MOUSE_SCROLL_ON, A, delta_x, delta_y, params) + // Suppress the mouse macros /client/var/has_mouse_macro_warning /mob/proc/LogMouseMacro(verbused, params) @@ -427,95 +477,3 @@ var/global/list/click_catchers set hidden = 1 set name = ".mouse" LogMouseMacro(".mouse", params) - -/* - Custom Click Handlinig -*/ - -/mob - var/datum/stack/click_handlers - - -var/const/CLICK_HANDLER_NONE = 0 -var/const/CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT = 1 -var/const/CLICK_HANDLER_ALL = (~0) - -/datum/click_handler - var/mob/user - var/handler_flags = CLICK_HANDLER_NONE - -/datum/click_handler/New(mob/user) - ..() - src.user = user - if(handler_flags & CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT) - RegisterSignal(user, COMSIG_MOB_LOGOUT, /datum/click_handler/proc/OnMobLogout, TRUE) - -/datum/click_handler/Destroy() - if(handler_flags & CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT) - UnregisterSignal(user, COMSIG_MOB_LOGOUT) - user = null - . = ..() - -/datum/click_handler/proc/Enter() - return - -/datum/click_handler/proc/Exit() - return - - -/datum/click_handler/proc/OnMobLogout() - user.RemoveClickHandler(src) - -/datum/click_handler/proc/OnClick(var/atom/A, var/params) - return - -/datum/click_handler/proc/OnDblClick(var/atom/A, var/params) - return - -/datum/click_handler/default/OnClick(var/atom/A, var/params) - user.ClickOn(A, params) - -/datum/click_handler/default/OnDblClick(var/atom/A, var/params) - user.DblClickOn(A, params) - -/mob/proc/GetClickHandler(var/datum/click_handler/popped_handler) - if(!click_handlers) - click_handlers = new() - if(click_handlers.is_empty()) - PushClickHandler(/datum/click_handler/default) - return click_handlers.Top() - -/mob/proc/RemoveClickHandler(var/datum/click_handler/click_handler) - if(!click_handlers) - return - - var/was_top = click_handlers.Top() == click_handler - - if(was_top) - click_handler.Exit() - click_handlers.Remove(click_handler) - qdel(click_handler) - - if(!was_top) - return - click_handler = click_handlers.Top() - if(click_handler) - click_handler.Enter() - -/mob/proc/PopClickHandler() - if(!click_handlers) - return - RemoveClickHandler(click_handlers.Top()) - -/mob/proc/PushClickHandler(var/datum/click_handler/new_click_handler_type) - if((initial(new_click_handler_type.handler_flags) & CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT) && !client) - return FALSE - if(!click_handlers) - click_handlers = new() - var/datum/click_handler/click_handler = click_handlers.Top() - if(click_handler) - click_handler.Exit() - - click_handler = new new_click_handler_type(src) - click_handler.Enter() - click_handlers.Push(click_handler) diff --git a/code/_onclick/click_ctrl.dm b/code/_onclick/click_ctrl.dm new file mode 100644 index 00000000000..17901036b42 --- /dev/null +++ b/code/_onclick/click_ctrl.dm @@ -0,0 +1,29 @@ +/** + * Ctrl click + */ +/mob/proc/CtrlClickOn(atom/A) + A.CtrlClick(src) + +/mob/proc/CtrlShiftClickOn(var/atom/A) + A.CtrlShiftClick(src) + return + +/atom/proc/CtrlShiftClick(var/mob/user) + return + +/atom/proc/CtrlClick(var/mob/user) + return + +/atom/movable/CtrlClick(var/mob/user) + if(Adjacent(user)) + user.start_pulling(src) + +/** + * Ctrl mouse wheel click + * Except for tagging datumns same as control click (<-- not implemented in Aurora yet) + */ +/mob/proc/CtrlMiddleClickOn(atom/A) + // if(check_rights_for(client, R_ADMIN)) + // client.toggle_tag_datum(A) + // return + CtrlClickOn(A) diff --git a/code/_onclick/click_handling.dm b/code/_onclick/click_handling.dm new file mode 100644 index 00000000000..286905a12e9 --- /dev/null +++ b/code/_onclick/click_handling.dm @@ -0,0 +1,226 @@ +/* + Custom click handling + + All of this shit should be made redundant by signals and burned to the ground at some point, in the meantime I have updated it +*/ + +/// Populates `click_handlers` with the default handler if not already defined. +#define SETUP_CLICK_HANDLERS \ +if(!click_handlers) { \ + click_handlers = new(); \ + click_handlers += new/datum/click_handler/default(src) \ +} + +/** + * LAZYLIST (Instances of `/datum/click_handler`). Click handlers for this mob that should intercept and handle click + * calls. + * + * The 'topmost'/'active' click handler for the mob is the handler currently at index `1`. By default, this will be + * `/datum/click_handler/default`. + */ +/mob/var/list/click_handlers + +// In the mob code, because otherwise it's a redefinition +// /mob/Destroy() +// QDEL_LIST(click_handlers) +// return ..() + +/// Removes this click handler on `/mob/Logout()`. +var/global/const/CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT = FLAG(0) +/// Removes and prevents creation of the click handler if it is not the active handler for the mob. +var/global/const/CLICK_HANDLER_REMOVE_IF_NOT_TOP = FLAG(1) + +/datum/click_handler + /// The mob this click handler is attached to. + var/mob/user + + /** + * Bitfield (Any of `CLICK_HANDLER_*`). Relevant flags to control the logic flow of this click handler. + * + * See `code\_onclick\click_handling.dm` for valid options. + */ + var/flags = EMPTY_BITFIELD + +/datum/click_handler/New(mob/user) + ..() + src.user = user + if (flags & CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT) + RegisterSignal(user, COMSIG_MOB_LOGOUT, PROC_REF(OnMobLogout)) + +/datum/click_handler/Destroy() + if (flags & CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT) + UnregisterSignal(user, COMSIG_MOB_LOGOUT) + user = null + . = ..() + +/** + * Called when the click handler becomes the active entry in the mob's click handlers list. Called by + * `PushClickHandler()` and `RemoveClickHandler()`. + * + * Has no return value. + */ +/datum/click_handler/proc/Enter() + return + +/** + * Called when the click handler was active in the mob's click handler list and has been replaced by another handler or + * otherwise removed. Called by `PushClickHandler()` and `RemoveClickHandler()`. + * + * Has no return value. + */ +/datum/click_handler/proc/Exit() + return + +/** + * Event proc for `logged_out_event`. Called when the mob logs out and this click handler has the + * `CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT` flag set. + * + * By default, this removes the click handler from the assigned mob. + * + * Has no return value. + */ +/datum/click_handler/proc/OnMobLogout() + user.RemoveClickHandler(src) + +/** + * Called on click by `/atom/Click()` when this is the mob's currently active click handler. + * + * **Parameters**: + * - `A` - The atom clicked on. + * - `params` (list of strings) - List of click parameters. See BYOND's `CLick()` documentation. + * + * Has no return value. + */ +/datum/click_handler/proc/OnClick(atom/A, params) + return + +/** + * Called on doubleclick by `/atom/DblClick()` when this is the mob's currently active click handler. + * + * **Parameters**: + * - `A` - The atom double clicked on. + * - `params` (list of strings) - List of click parameters. See BYOND's `CLick()` documentation. + * + * Has no return value. + */ +/datum/click_handler/proc/OnDblClick(atom/A, params) + return + +/** + * Called on MouseDown by `/client/MouseDown()` when this is the mob's currently active click handler. + * + * **Parameters**: + * - `object` - The atom mouse is underneath. + * - `location` - the turf, stat panel, grid cell, etc. containing the object where it was clicked + * - `params` (list of strings) - List of click parameters. See BYOND's `CLick()` documentation. + * + * Has no return value. + */ +// /datum/click_handler/proc/OnMouseDown(object, location, params) //NOT IMPLEMENTED +// return + +/** + * Called on MouseUp by `/client/MouseUp()` when this is the mob's currently active click handler. + * + * **Parameters**: + * - `object` - The atom underneath mouse. + * - `location` - the turf, stat panel, grid cell, etc. containing the object where it was clicked + * - `params` (list of strings) - List of click parameters. See BYOND's `CLick()` documentation. + * + * Has no return value. + */ +// /datum/click_handler/proc/OnMouseUp(object, location, params) //NOT IMPLEMENTED +// return + +/datum/click_handler/default/OnClick(atom/A, params) + user.ClickOn(A, params) + +/datum/click_handler/default/OnDblClick(atom/A, params) + user.DblClickOn(A, params) + +/** + * Returns the mob's currently active click handler. + * + * **Parameters**: + * - `popped_handler`. Not used, should probably be removed? + */ +/mob/proc/GetClickHandler(datum/click_handler/popped_handler) + SETUP_CLICK_HANDLERS + return click_handlers[1] + +/** + * Removes the given click handler from `click_handlers`. + * + * This will not remove the protected default handler. + * + * **Parameters**: + * - `click_handler` (Path or instance of `/datum/click_handler`) - The handler to remove. If a path, finds the first + * matching instance of that type. + * + * Returns boolean. Whether or not the click handler was found and removed. + */ +/mob/proc/RemoveClickHandler(datum/click_handler/click_handler) + if(!click_handlers) + return FALSE + if(ispath(click_handler)) // If we were given a path instead of an instance, find the first matching instance by type + // No removing of the default click handler + if(click_handler == /datum/click_handler/default) + return FALSE + click_handler = get_instance_of_strict_type(click_handlers, click_handler) + if(!click_handler) + return FALSE + + . = (click_handler in click_handlers) + if(!.) + return + + var/was_top = click_handlers[1] == click_handler + + if(was_top) + click_handler.Exit() + click_handlers.Remove(click_handler) + qdel(click_handler) + + if(!was_top) + return + click_handler = click_handlers[1] + if(click_handler) + click_handler.Enter() + +/** + * Pushes the given click handler to the top of the `click_handlers` list, making it the active handler. + * + * **Parameters**: + * - `new_click_handler_type` (Type path - Type of `/datum/click_handler`). The click handler to make active. + * + * Returns boolean. If `TRUE`, the click handler was successfully pushed to the top. Returns `FALSE` if the handler was + * already active. + */ +/mob/proc/PushClickHandler(datum/click_handler/new_click_handler_type) + // No manipulation of the default click handler + if(new_click_handler_type == /datum/click_handler/default) + return FALSE + if ((initial(new_click_handler_type.flags) & CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT) && !client) + return FALSE + SETUP_CLICK_HANDLERS + + var/datum/click_handler/click_handler = click_handlers[1] + if(click_handler.type == new_click_handler_type) + return FALSE // If the top click handler is already the same as the desired one, bow out + + click_handler.Exit() + if (click_handler.flags & CLICK_HANDLER_REMOVE_IF_NOT_TOP) + click_handlers.Remove(click_handler) + qdel(click_handler) + + click_handler = get_instance_of_strict_type(click_handlers, click_handler) + if(click_handler) + click_handlers.Remove(click_handler) + else + click_handler = new new_click_handler_type(src) + + click_handlers.Insert(1, click_handler) // Insert new handlers first + click_handler.Enter() + return TRUE + +#undef SETUP_CLICK_HANDLERS diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 40ecc88f1cf..dae5de8c3b7 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -6,7 +6,7 @@ adjacency code. */ -/mob/living/silicon/robot/ClickOn(var/atom/A, var/params) +/mob/living/silicon/robot/ClickOn(atom/A, params) if(world.time <= next_click) return next_click = world.time + 1 @@ -16,7 +16,7 @@ CtrlShiftClickOn(A) return if(modifiers["middle"]) - MiddleClickOn(A) + MiddleClickOn(A, params) return if(modifiers["shift"]) ShiftClickOn(A) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index d904f24a1a7..f0b0931e91e 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -14,9 +14,16 @@ avoid code duplication. This includes items that may sometimes act as a standard */ // Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown. -/obj/item/proc/attack_self(mob/user) +/obj/item/proc/attack_self(mob/user, modifiers) + if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE return +/// Called when the item is in the active hand, and right-clicked. Intended for alternate or opposite functions, such as lowering reagent transfer amount. At the moment, there is no verb or hotkey. +/obj/item/proc/attack_self_secondary(mob/user, modifiers) + if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF_SECONDARY, user) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE + // Called at the start of resolve_attackby(), before the actual attack. /obj/item/proc/pre_attack(atom/a, mob/user) return diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index ac06c3c3075..de0ae7226ea 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -4,7 +4,7 @@ * The component datum * * A component should be a single standalone unit - * of functionality, that works by receiving signals from it's parent + * of functionality, that works by receiving signals from its parent * object to provide some single functionality (i.e a slippery component) * that makes the object it's attached to cause people to slip over. * Useful when you want shared behaviour independent of type inheritance @@ -17,15 +17,6 @@ */ var/dupe_mode = COMPONENT_DUPE_HIGHLANDER - /** - * The type to check for duplication - * - * `null` means exact match on `type` (default) - * - * Any other type means that and all subtypes - */ - var/dupe_type - /// The datum this components belongs to var/datum/parent @@ -57,7 +48,7 @@ qdel(src, TRUE, TRUE) return - _JoinParent(parent) + _JoinParent() /** * Called during component creation with the same arguments as in new excluding parent. @@ -217,7 +208,7 @@ * * Use this to do any special cleanup you might need to do before being deregged from an object */ -/datum/component/proc/PreTransfer() +/datum/component/proc/PreTransfer(datum/new_parent) return /** @@ -227,7 +218,7 @@ * * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead */ -/datum/component/proc/PostTransfer() +/datum/component/proc/PostTransfer(datum/new_parent) return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it /** @@ -328,7 +319,6 @@ CRASH("[component_type] attempted instantiation!") var/dupe_mode = initial(component_type.dupe_mode) - var/dupe_type = initial(component_type.dupe_type) var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES) if(uses_sources && !source) CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!") @@ -339,10 +329,7 @@ raw_args[1] = src if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES) - if(!dupe_type) - old_component = GetExactComponent(component_type) - else - old_component = GetComponent(dupe_type) + old_component = GetComponent(component_type) if(old_component) switch(dupe_mode) @@ -434,11 +421,11 @@ * Removes the component from parent, ends up with a null parent * Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does */ -/datum/component/proc/ClearFromParent() +/datum/component/proc/ClearFromParent(datum/new_parent) if(!parent) return var/datum/old_parent = parent - PreTransfer() + PreTransfer(new_parent) _RemoveFromParent() parent = null SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src) @@ -455,16 +442,17 @@ if(!target || target.parent == src) return if(target.parent) - target.ClearFromParent() - target.parent = src - var/result = target.PostTransfer() + target.ClearFromParent(src) + var/result = target.PostTransfer(src) switch(result) if(COMPONENT_INCOMPATIBLE) var/c_type = target.type qdel(target) CRASH("Incompatible [c_type] transfer attempt to a [type]!") - if(target == AddComponent(target)) + AddComponent(target) + if(!QDELETED(target)) + target.parent = src target._JoinParent() /** @@ -482,13 +470,13 @@ for(var/component_key in dc) var/component_or_list = dc[component_key] if(islist(component_or_list)) - for(var/datum/component/I in component_or_list) - if(I.can_transfer) - target.TakeComponent(I) + for(var/datum/component/component in component_or_list) + if(component.can_transfer) + target.TakeComponent(component) else - var/datum/component/C = component_or_list - if(C.can_transfer) - target.TakeComponent(C) + var/datum/component/component = component_or_list + if(!QDELETED(component) && component.can_transfer) + target.TakeComponent(component) /** * Return the object that is the host of any UI's that this component has diff --git a/code/datums/components/eye/_eye.dm b/code/datums/components/eye/_eye.dm index a2b36e207a2..1847caf1c7b 100644 --- a/code/datums/components/eye/_eye.dm +++ b/code/datums/components/eye/_eye.dm @@ -17,7 +17,7 @@ QDEL_LIST(actions) . = ..() -/datum/component/eye/proc/look(var/mob/new_looker, var/list/eye_args) +/datum/component/eye/proc/look(mob/new_looker, list/eye_args) if(new_looker.eyeobj || current_looker) return FALSE LAZYINSERT(eye_args, get_turf(current_looker), 1) //Make sure that a loc is provided to the eye diff --git a/code/datums/components/eye/freelook.dm b/code/datums/components/eye/freelook.dm index 38d6f91ecca..24713f01619 100644 --- a/code/datums/components/eye/freelook.dm +++ b/code/datums/components/eye/freelook.dm @@ -1,12 +1,14 @@ -//For mobs connecting to the station's cameranet without needing AI procs +/** + * Freelook eye component + * + * For mobs connecting to the station's cameranet without needing AI procs + * + * For mobs connecting to other visualnets, call set_visualnet() with the visualnet to use + */ /datum/component/eye/freelook eye_type = /mob/abstract/eye/freelook -//For mobs connecting to other visualnets. Pass visualnet as eye_args in look() -/datum/component/eye/freelook - eye_type = /mob/abstract/eye - -/datum/component/eye/freelook/proc/set_visualnet(var/datum/visualnet/net) +/datum/component/eye/freelook/proc/set_visualnet(datum/visualnet/net = GLOB.cameranet) if(component_eye) var/mob/abstract/eye/eye = component_eye if(istype(eye)) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 149c2d17233..ec1da3db540 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -759,7 +759,7 @@ var/list/admin_verbs_cciaa = list( return var/datum/click_handler/handler = mob.GetClickHandler() if(handler.type == /datum/click_handler/build_mode) - usr.PopClickHandler() + usr.RemoveClickHandler(/datum/click_handler/build_mode) else usr.PushClickHandler(/datum/click_handler/build_mode) feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/buildmode/buttons.dm b/code/modules/admin/buildmode/buttons.dm index 35349f134c6..a99d55eb816 100644 --- a/code/modules/admin/buildmode/buttons.dm +++ b/code/modules/admin/buildmode/buttons.dm @@ -76,4 +76,4 @@ /obj/effect/bmode/quit/OnClick() var/datum/click_handler/handler = usr.GetClickHandler() if(handler.type == /datum/click_handler/build_mode) - usr.PopClickHandler() + usr.RemoveClickHandler(/datum/click_handler/build_mode) diff --git a/code/modules/admin/buildmode/click_handler.dm b/code/modules/admin/buildmode/click_handler.dm index 700eabadb35..4a4b44cdbb6 100644 --- a/code/modules/admin/buildmode/click_handler.dm +++ b/code/modules/admin/buildmode/click_handler.dm @@ -1,5 +1,5 @@ /datum/click_handler/build_mode - handler_flags = CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT + flags = CLICK_HANDLER_REMOVE_ON_MOB_LOGOUT var/dir var/list/build_modes diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 3d1648d0b7b..4c7d8f54d1c 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -813,6 +813,10 @@ var/list/localhost_addresses = list( else CRASH("Age check regex failed for [src.ckey]") +/client/Click(atom/object, atom/location, control, params) + SEND_SIGNAL(src, COMSIG_CLIENT_CLICK, object, location, control, params, usr) + ..() + /client/MouseDrag(src_object, over_object, src_location, over_location, src_control, over_control, params) var/list/modifiers = params2list(params) diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 8b04f31433b..334a650cc4c 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -155,7 +155,7 @@ if(user.get_equipped_item(slot_wear_mask) != src) to_chat(user, SPAN_WARNING("You must be wearing \the [src] to activate it!")) return - var/datum/component/eye/freelook/CN = GetComponent(/datum/component/eye) + var/datum/component/eye/freelook/CN = GetComponent(/datum/component/eye/freelook) if(!CN) to_chat(user, SPAN_WARNING("\The [src] doesn't respond!")) return diff --git a/code/modules/heavy_vehicle/mech_interaction.dm b/code/modules/heavy_vehicle/mech_interaction.dm index 1cf77f56de0..d9b5ce18d3c 100644 --- a/code/modules/heavy_vehicle/mech_interaction.dm +++ b/code/modules/heavy_vehicle/mech_interaction.dm @@ -16,7 +16,7 @@ body.mouse_drop_receive(arglist(args)) else . = ..() -/mob/living/heavy_vehicle/ClickOn(var/atom/A, params, var/mob/user) +/mob/living/heavy_vehicle/ClickOn(atom/A, params, mob/user) if(!user || incapacitated() || user.incapacitated() || lockdown) return diff --git a/code/modules/mob/abstract/freelook/ai/eye.dm b/code/modules/mob/abstract/freelook/ai/eye.dm index 595a3a6bf5b..5bb99282f4c 100644 --- a/code/modules/mob/abstract/freelook/ai/eye.dm +++ b/code/modules/mob/abstract/freelook/ai/eye.dm @@ -1,23 +1,33 @@ -// AI EYE -// -// A mob that the AI controls to look around the station with. -// It streams chunks as it moves around, which will show it what the AI can and cannot see. +/// Generic version of the AI eye without the AI-specific handling, for things like the Camera MIU mask. /mob/abstract/eye/freelook - // Generic version of the AI eye without the AI-specific handling, for things like the Camera MIU mask. name = "Inactive Camera Eye" name_suffix = "Camera Eye" living_eye = FALSE + click_handler_type = /datum/click_handler/eye/freelook /mob/abstract/eye/freelook/Initialize() . = ..() visualnet = GLOB.cameranet +/datum/click_handler/eye/freelook/OnDblClick(atom/A, params) + var/mob/abstract/eye = user.eyeobj + if(!eye) //Something has broken, ensure the click handler doesn't stick around + user.RemoveClickHandler(src) + return + eye.DblClickOn(A, params) + + +/// AI EYE +/// +/// A mob that the AI controls to look around the station with. +/// It streams chunks as it moves around, which will show it what the AI can and cannot see. /mob/abstract/eye/freelook/aiEye name = "Inactive AI Eye" name_suffix = "AI Eye" icon_state = "AI-eye" living_eye = FALSE + click_handler_type = /datum/click_handler/eye/freelook/aiEye /mob/abstract/eye/freelook/aiEye/Initialize() . = ..() @@ -35,6 +45,9 @@ ai.holo.move_hologram(ai) return 1 +/datum/click_handler/eye/freelook/aiEye/OnDblClick(atom/A, params) + user.DblClickOn(A, params) + // AI MOVEMENT // The AI's "eye". Described on the top of the page. diff --git a/code/modules/mob/abstract/freelook/eye.dm b/code/modules/mob/abstract/freelook/eye.dm index 705b9f5295a..ca15b4a7887 100644 --- a/code/modules/mob/abstract/freelook/eye.dm +++ b/code/modules/mob/abstract/freelook/eye.dm @@ -99,7 +99,7 @@ return remove_visual(owner) LAZYREMOVE(owner.additional_vision_handlers, src) - visualnet.remove_eye(src) + visualnet?.remove_eye(src) owner.eyeobj = null if(owner.client) owner.client.eye = owner diff --git a/code/modules/mob/abstract/ghost/ghost.dm b/code/modules/mob/abstract/ghost/ghost.dm index 813efb48839..409481c0fca 100644 --- a/code/modules/mob/abstract/ghost/ghost.dm +++ b/code/modules/mob/abstract/ghost/ghost.dm @@ -38,7 +38,7 @@ if(istype(target)) ManualFollow(target) -/mob/abstract/ghost/ClickOn(var/atom/A, var/params) +/mob/abstract/ghost/ClickOn(atom/A, params) if(!canClick()) return setClickCooldown(4) diff --git a/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm b/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm index 61f5fbf968a..0eabee55ebc 100644 --- a/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm +++ b/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm @@ -175,7 +175,7 @@ var/datum/click_handler/handler = GetClickHandler() if(handler.type == /datum/click_handler/build_mode) - usr.PopClickHandler() + usr.RemoveClickHandler(/datum/click_handler/build_mode) else usr.PushClickHandler(/datum/click_handler/build_mode) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 6d880cd85b8..d8a2d2313ac 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -1205,7 +1205,7 @@ There are several things that need to be remembered: if(update_icons) update_icon() -/mob/living/carbon/human/update_inv_l_hand(var/update_icons=1) +/mob/living/carbon/human/update_inv_l_hand(update_icons = TRUE) if (QDELETED(src)) return @@ -1245,7 +1245,7 @@ There are several things that need to be remembered: if(update_icons) update_icon(forceDirUpdate = TRUE) -/mob/living/carbon/human/update_inv_r_hand(var/update_icons=1) +/mob/living/carbon/human/update_inv_r_hand(update_icons = TRUE) if (QDELETED(src)) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 14b02a4014d..219718d8f0b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -52,8 +52,7 @@ QDEL_NULL(ability_master) if(click_handlers) - click_handlers.QdelClear() - QDEL_NULL(click_handlers) + QDEL_LIST(click_handlers) return ..() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 80148f0cefd..31e278911dd 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -432,7 +432,7 @@ return ..() /obj/item/grab/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params) - var/mob/living/carbon/human/H = affecting + var/mob/living/carbon/human/H = over if(wielded || affecting.buckled_to || !istype(H) || assailant != H || H.get_active_hand() != src) return if(!ishuman(affecting)) diff --git a/code/modules/mob/update_icons.dm b/code/modules/mob/update_icons.dm index 65fe194ee40..097e7a1b4f4 100644 --- a/code/modules/mob/update_icons.dm +++ b/code/modules/mob/update_icons.dm @@ -16,10 +16,10 @@ /mob/proc/update_inv_back() return -/mob/proc/update_inv_l_hand() +/mob/proc/update_inv_l_hand(update_icons = TRUE) return -/mob/proc/update_inv_r_hand() +/mob/proc/update_inv_r_hand(update_icons = TRUE) return /mob/proc/update_inv_wear_mask() diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index f7dc3bb2a58..f6472d1f269 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -46,6 +46,7 @@ var/global/photo_count = 0 pickup_sound = 'sound/items/pickup/paper.ogg' /obj/item/photo/New() + . = ..() id = photo_count++ /obj/item/photo/attack_self(mob/user as mob) diff --git a/html/changelogs/fluffyghost-clicksrefactor.yml b/html/changelogs/fluffyghost-clicksrefactor.yml new file mode 100644 index 00000000000..4a817664470 --- /dev/null +++ b/html/changelogs/fluffyghost-clicksrefactor.yml @@ -0,0 +1,64 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# - (fixes bugs) +# wip +# - (work in progress) +# qol +# - (quality of life) +# soundadd +# - (adds a sound) +# sounddel +# - (removes a sound) +# rscadd +# - (adds a feature) +# rscdel +# - (removes a feature) +# imageadd +# - (adds an image or sprite) +# imagedel +# - (removes an image or sprite) +# spellcheck +# - (fixes spelling or grammar) +# experiment +# - (experimental change) +# balance +# - (balance changes) +# code_imp +# - (misc internal code change) +# refactor +# - (refactors code) +# config +# - (makes a change to the config files) +# admin +# - (makes changes to administrator tools) +# server +# - (miscellaneous changes to server) +################################# + +# Your name. +author: FluffyGhost + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - refactor: "Added various events to the dcs system in regards to click handling." + - refactor: "Refactored various mouse related procs." + - bugfix: "Fixed MUI mask." + - bugfix: "Fixed AI jump on double click." + - bugfix: "Fixed some runtimes with the click handler system." + - code_imp: "Updated the click handler system." + - bugfix: "Fixed fireman carry."