diff --git a/citadel.dme b/citadel.dme index 93a21ff6356..15ac09304d7 100644 --- a/citadel.dme +++ b/citadel.dme @@ -203,6 +203,7 @@ #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-clickchain.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-context_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-defense.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-mouse.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-reachability.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-throwing.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-tool_system.dm" @@ -210,7 +211,6 @@ #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.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_movable.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_radiation.dm" @@ -1134,9 +1134,18 @@ #include "code\game\backend\world\world_location.dm" #include "code\game\click\adjacency.dm" #include "code\game\click\adjacency_legacy.dm" -#include "code\game\click\ai.dm" +#include "code\game\click\click-human-legacy.dm" +#include "code\game\click\click-legacy.dm" +#include "code\game\click\click-living.dm" +#include "code\game\click\click-new_player.dm" +#include "code\game\click\click-observer-dead-legacy.dm" +#include "code\game\click\click-observer-dead.dm" +#include "code\game\click\click-observer.dm" +#include "code\game\click\click-silicon-ai.dm" +#include "code\game\click\click-silicon-legacy.dm" +#include "code\game\click\click-silicon-robot.dm" +#include "code\game\click\click-silicon.dm" #include "code\game\click\click.dm" -#include "code\game\click\cyborg.dm" #include "code\game\click\drag_drop.dm" #include "code\game\click\item_attack.dm" #include "code\game\click\items-item_attack_chain.dm" @@ -1145,10 +1154,11 @@ #include "code\game\click\items.dm" #include "code\game\click\mobs-melee_attack_chain.dm" #include "code\game\click\mobs.dm" -#include "code\game\click\observer.dm" #include "code\game\click\other_mobs.dm" #include "code\game\click\reachability.dm" -#include "code\game\click\rig.dm" +#include "code\game\click\route-rig-legacy.dm" +#include "code\game\click\route-rig.dm" +#include "code\game\click\route-vehicle.dm" #include "code\game\click\telekinesis.dm" #include "code\game\click\melee\melee_attack.dm" #include "code\game\click\melee\melee_combo.dm" @@ -1531,6 +1541,7 @@ #include "code\game\machinery\doors\multi_tile_vr.dm" #include "code\game\machinery\doors\unpowered.dm" #include "code\game\machinery\doors\windowdoor.dm" +#include "code\game\machinery\doors\airlock\airlock-ai.dm" #include "code\game\machinery\doors\airlock\airlock.dm" #include "code\game\machinery\doors\airlock\airlock_control.dm" #include "code\game\machinery\doors\airlock\airlock_electronics.dm" @@ -1587,6 +1598,7 @@ #include "code\game\machinery\teleporter\projector.dm" #include "code\game\machinery\turrets\turret-ai_holder.dm" #include "code\game\machinery\turrets\turret.dm" +#include "code\game\machinery\turrets\turret_control-ai.dm" #include "code\game\machinery\turrets\turret_control.dm" #include "code\game\machinery\turrets\turret_frame.dm" #include "code\game\machinery\turrets\subtypes\lasertag.dm" @@ -3131,6 +3143,7 @@ #include "code\modules\guidebook\sections\reagents.dm" #include "code\modules\hardsuits\_rig.dm" #include "code\modules\hardsuits\activation.dm" +#include "code\modules\hardsuits\rig-input.dm" #include "code\modules\hardsuits\rig_attackby.dm" #include "code\modules\hardsuits\rig_pieces.dm" #include "code\modules\hardsuits\rig_verbs.dm" @@ -3459,6 +3472,7 @@ #include "code\modules\logging\event_logger.dm" #include "code\modules\logging\logging.dm" #include "code\modules\logging\api\attack.dm" +#include "code\modules\logging\api\clickchain.dm" #include "code\modules\logging\api\construction.dm" #include "code\modules\logging\api\projectile.dm" #include "code\modules\logging\api\reagents.dm" @@ -3763,6 +3777,7 @@ #include "code\modules\mob\mob-client.dm" #include "code\modules\mob\mob-damage.dm" #include "code\modules\mob\mob-defense.dm" +#include "code\modules\mob\mob-examine.dm" #include "code\modules\mob\mob-hands.dm" #include "code\modules\mob\mob-iff.dm" #include "code\modules\mob\mob-interaction.dm" @@ -4011,6 +4026,7 @@ #include "code\modules\mob\living\silicon\silicon.dm" #include "code\modules\mob\living\silicon\subystems.dm" #include "code\modules\mob\living\silicon\translation.dm" +#include "code\modules\mob\living\silicon\ai\ai-examine.dm" #include "code\modules\mob\living\silicon\ai\ai.dm" #include "code\modules\mob\living\silicon\ai\ai_movement.dm" #include "code\modules\mob\living\silicon\ai\ai_remote_control.dm" @@ -4578,7 +4594,6 @@ #include "code\modules\planet\planet.dm" #include "code\modules\planet\time.dm" #include "code\modules\planet\weather.dm" -#include "code\modules\power\apc.dm" #include "code\modules\power\atom.dm" #include "code\modules\power\batteryrack.dm" #include "code\modules\power\breaker_box.dm" @@ -4602,6 +4617,8 @@ #include "code\modules\power\antimatter\containment_jar.dm" #include "code\modules\power\antimatter\control.dm" #include "code\modules\power\antimatter\shielding.dm" +#include "code\modules\power\apc\apc-ai.dm" +#include "code\modules\power\apc\apc.dm" #include "code\modules\power\cells\device_cells.dm" #include "code\modules\power\cells\power_cells.dm" #include "code\modules\power\engines\rust\_setup.dm" @@ -4622,6 +4639,7 @@ #include "code\modules\power\fission\computer.dm" #include "code\modules\power\fission\engine.dm" #include "code\modules\power\fission\rods.dm" +#include "code\modules\power\lighting\light-ai.dm" #include "code\modules\power\lighting\lighting.dm" #include "code\modules\power\lighting\lights.dm" #include "code\modules\power\lighting\lightswitch.dm" @@ -5408,6 +5426,7 @@ #include "code\modules\vehicles\actions.dm" #include "code\modules\vehicles\ridden.dm" #include "code\modules\vehicles\sealed.dm" +#include "code\modules\vehicles\vehicle-input.dm" #include "code\modules\vehicles\vehicle-physics.dm" #include "code\modules\vehicles\vehicle.dm" #include "code\modules\vehicles\ridden\bike.dm" @@ -5422,6 +5441,7 @@ #include "code\modules\vehicles\sealed\mecha\mech_fabricator.dm" #include "code\modules\vehicles\sealed\mecha\mech_prosthetics.dm" #include "code\modules\vehicles\sealed\mecha\mech_sensor.dm" +#include "code\modules\vehicles\sealed\mecha\mecha-input.dm" #include "code\modules\vehicles\sealed\mecha\mecha.dm" #include "code\modules\vehicles\sealed\mecha\mecha_actions.dm" #include "code\modules\vehicles\sealed\mecha\mecha_appearance.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 new file mode 100644 index 00000000000..761ce09967c --- /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 + * + * Mouse signals are different from clickchain; these are generally earlier in the chain and are only sent by clients + * using their, well, mouse. Clickchain is more of a citadel RP concept and is more complicated. + */ + +/// Sent with (mob/user, location, control, params). +/// * Do not listen to this signal unless you really know what you're doing. This isn't necessary most of the time. +#define COMSIG_ATOM_CLICK "atom-click" + /// drops the click. this will stop standard logging too. + #define RAISE_ATOM_CLICK_DROP (1<<0) + +// TODO: audit below + +/// From base of atom/OnMouseDrop(): (/atom/over, /mob/user, proximity, params) +#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" +/// From base of atom/MouseDroppedOn(): (/atom/from, /mob/user, proximity, params) +#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" + /// blocks standard mousedrop procs from running, works on both, will terminate remainder of mousedrop proc chain + #define COMPONENT_NO_MOUSEDROP (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_atom/signals_atom_mouse.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm deleted file mode 100644 index 3144e0bf47e..00000000000 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm +++ /dev/null @@ -1,29 +0,0 @@ -/** - *! ## 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) -/// From base of atom/CtrlClickOn(): (/mob) -#define COMSIG_CLICK_CTRL "ctrl_click" -/// From base of atom/AltClick(): (/mob) -#define COMSIG_CLICK_ALT "alt_click" - #define COMPONENT_CANCEL_CLICK_ALT (1<<0) -/// From base of atom/CtrlShiftClick(/mob) -////#define COMSIG_CLICK_CTRL_SHIFT "ctrl_shift_click" -/// From base of atom/OnMouseDrop(): (/atom/over, /mob/user, proximity, params) -#define COMSIG_MOUSEDROP_ONTO "mousedrop_onto" -/// From base of atom/MouseDroppedOn(): (/atom/from, /mob/user, proximity, params) -#define COMSIG_MOUSEDROPPED_ONTO "mousedropped_onto" - /// blocks standard mousedrop procs from running, works on both, will terminate remainder of mousedrop proc chain - #define COMPONENT_NO_MOUSEDROP (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/event_args/actor.dm b/code/__DEFINES/event_args/actor.dm index 2b0de2f781b..5f884efcdd6 100644 --- a/code/__DEFINES/event_args/actor.dm +++ b/code/__DEFINES/event_args/actor.dm @@ -3,6 +3,10 @@ //* Actor Data Keys *// +/// logging only +#define ACTOR_DATA_RIG_CLICK_LOG "rig-click-log" +/// logging only +#define ACTOR_DATA_SILICON_CONTROL_LOG "silicon-control-log" /// logging only #define ACTOR_DATA_UNARMED_LOG "unarmed-log" /// logging only diff --git a/code/__DEFINES/procs/clickcode.dm b/code/__DEFINES/procs/clickcode.dm index 7d54bf5ce76..3970564f5fa 100644 --- a/code/__DEFINES/procs/clickcode.dm +++ b/code/__DEFINES/procs/clickcode.dm @@ -75,6 +75,8 @@ #define CLICKCHAIN_REFLEX_COUNTER (1<<9) /// put this in if we should entirely abort the attack #define CLICKCHAIN_FULL_BLOCKED (1<<10) +/// always log +#define CLICKCHAIN_ALWAYS_LOG (1<<11) /// check these for 'unconditional abort' #define CLICKCHAIN_FLAGS_UNCONDITIONAL_ABORT (CLICKCHAIN_DO_NOT_PROPAGATE) @@ -92,6 +94,22 @@ #define CLICKCHAIN_MELEE_ATTACK_ARG_CLICKCHAIN_FLAGS 2 #define CLICKCHAIN_MELEE_ATTACK_ARG_STYLE 3 +//* Click param - Action. This is injected by CitRP code. *// + +#define CLICK_PARAM_ACTION "action" + +#define CLICK_ACTION_LMB "left" +#define CLICK_ACTION_CTRL_LMB "ctrl-left" +#define CLICK_ACTION_CTRL_SHIFT_LMB "ctrl-shift-left" +#define CLICK_ACTION_SHIFT_LMB "shift-left" +#define CLICK_ACTION_ALT_LMB "alt-left" + +#define CLICK_ACTION_MMB "middle" +#define CLICK_ACTION_SHIFT_MMB "shift-middle" + +#define CLICK_ACTION_RMB "right" +#define CLICK_ACTION_SHIFT_RMB "shift-rmb" + //* Reachability Depths *// /// default reachability depth diff --git a/code/__HELPERS/animations/attack.dm b/code/__HELPERS/animations/attack.dm index eba15eae459..a3992dad5ca 100644 --- a/code/__HELPERS/animations/attack.dm +++ b/code/__HELPERS/animations/attack.dm @@ -44,11 +44,11 @@ // todo: overlays/vis contents? var/image/rendering = image(weapon, loc = src) - flick_overlay(rendering, GLOB.clients, time) // yeah we don't care about the mob's state, we're an animation rendering.appearance_flags = KEEP_APART | RESET_ALPHA | RESET_TRANSFORM | RESET_COLOR rendering.plane = MOB_PLANE + rendering.layer = layer + 0.01 // size down rendering.transform = matrix() * 0.4 @@ -65,6 +65,8 @@ rendering.pixel_y = d_y #undef ATTACK_ITEM_OFFSET + flick_overlay(rendering, GLOB.clients, time) + animate(rendering, alpha = 175, transform = matrix() * 0.7, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = time * (1 / 2), easing = BACK_EASING | EASE_OUT) animate(alpha = 0, time = time * (1 / 2), easing = SINE_EASING | EASE_OUT) diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index 4ba0dfbaa20..718db9da235 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -12,7 +12,7 @@ C.alpha = H.ui_alpha LAZYADD(H.other_important, C) CL.screen += C - RegisterSignal(C, COMSIG_CLICK, PROC_REF(component_ui_interact)) + RegisterSignal(C, COMSIG_ATOM_CLICK, PROC_REF(component_ui_interact)) /datum/component/personal_crafting registered_type = /datum/component/personal_crafting @@ -300,7 +300,7 @@ Deletion.Cut(Deletion.len) qdel(DL) -/datum/component/personal_crafting/proc/component_ui_interact(source, location, control, params, user) +/datum/component/personal_crafting/proc/component_ui_interact(datum/source, mob/user, location, control, params) // SIGNAL_HANDLER if(user == parent) diff --git a/code/datums/components/mobs/parry_frame.dm b/code/datums/components/mobs/parry_frame.dm index 75fdf4021b0..d8b79de0596 100644 --- a/code/datums/components/mobs/parry_frame.dm +++ b/code/datums/components/mobs/parry_frame.dm @@ -515,6 +515,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/render/parry_frame) bind_to.vis_contents += src cycle(frame, single_deflect) QDEL_IN(src, qdel_time) + atom_flags |= ATOM_INITIALIZED return INITIALIZE_HINT_NORMAL /atom/movable/render/parry_frame/Destroy() diff --git a/code/datums/event_args/actor.dm b/code/datums/event_args/actor.dm index 0225259951b..95d07409c11 100644 --- a/code/datums/event_args/actor.dm +++ b/code/datums/event_args/actor.dm @@ -38,8 +38,7 @@ /datum/event_args/actor/proc/actor_log_string() return performer == initiator ? key_name(performer) : "[key_name(performer)] (via [key_name(initiator)])" -//* Feedback *(// - +//* Feedback *// /datum/event_args/actor/proc/chat_feedback(msg, atom/target) performer.action_feedback(msg, target) @@ -51,7 +50,7 @@ if(performer != initiator) initiator.bubble_action_feedback(msg, target) -// todo: rework these awful ass feedback/message procs wtf +// todo: rework these awful ass feedback/message procs below wtf // proposal: // visible_feedback(visible, audible, self, range, target) diff --git a/code/game/atoms/atom-context_menu.dm b/code/game/atoms/atom-context_menu.dm index 6810e9a6508..54b6529f26c 100644 --- a/code/game/atoms/atom-context_menu.dm +++ b/code/game/atoms/atom-context_menu.dm @@ -94,6 +94,9 @@ transformed[data[1]] = I inverse_lookup[data[1]] = key + if(!length(transformed)) + return null + var/datum/radial_menu/context_menu/menu = new var/id = "context_[REF(e_args.initiator)]" GLOB.radial_menus[id] = menu diff --git a/code/game/atoms/atom-defense.dm b/code/game/atoms/atom-defense.dm index cd0d436c059..0d802400c20 100644 --- a/code/game/atoms/atom-defense.dm +++ b/code/game/atoms/atom-defense.dm @@ -342,17 +342,25 @@ return return P.resolve_impact_sfx(get_combat_fx_classifier(ATTACK_TYPE_PROJECTILE, P, bullet_act_args[BULLET_ACT_ARG_ZONE]), src) -/atom/proc/hitsound_throwhit(obj/item/I) - . = I.attacksound_override(src, ATTACK_TYPE_THROWN) +/atom/proc/hitsound_throwhit(atom/movable/impacting) + var/resolved_damage_type = DAMAGE_TYPE_BRUTE + var/resolved_damage_mode = NONE + var/resolved_impacting_attack_sound + if(isitem(impacting)) + var/obj/item/casted_item = impacting + resolved_damage_type = casted_item.damage_type + resolved_damage_mode = casted_item.damage_mode + resolved_impacting_attack_sound = casted_item.attack_sound + . = casted_item.attacksound_override(src, ATTACK_TYPE_THROWN) if(!isnull(.)) return - . = hitsound_override(I.damage_type, I.damage_mode, ATTACK_TYPE_THROWN, I) + . = hitsound_override(resolved_damage_type, resolved_damage_mode, ATTACK_TYPE_THROWN, impacting) if(.) return - . = (I.damage_type == DAMAGE_TYPE_BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound + . = (resolved_damage_type == DAMAGE_TYPE_BURN? hit_sound_burn : hit_sound_brute) || resolved_impacting_attack_sound if(.) return - switch(I.damage_type) + switch(resolved_damage_type) if(DAMAGE_TYPE_BRUTE) return "swing_hit" if(DAMAGE_TYPE_BURN) diff --git a/code/game/atoms/atom-examine.dm b/code/game/atoms/atom-examine.dm index 2a80e31a604..8ba068ab7e0 100644 --- a/code/game/atoms/atom-examine.dm +++ b/code/game/atoms/atom-examine.dm @@ -64,6 +64,9 @@ . += "
[get_examine_desc(user, dist)]" if(get_description_info() || get_description_fluff() || length(get_description_interaction(user))) . += SPAN_TINYNOTICE("For more information, click here.") //This feels VERY HACKY but eh its PROBABLY fine + // todo: i'm crying i hate this better software context menus whennnnnnnnnn + if(length(context_menu_query(new /datum/event_args/actor(user)))) + . += SPAN_NOTICE("Ctrl-Shift click on this entity to show additional options.") if(integrity_flags & INTEGRITY_INDESTRUCTIBLE) . += SPAN_NOTICE("It doesn't look like it can be damaged through common means.") /* diff --git a/code/game/click/ai.dm b/code/game/click/ai.dm deleted file mode 100644 index c47fedda6ce..00000000000 --- a/code/game/click/ai.dm +++ /dev/null @@ -1,180 +0,0 @@ -/* - AI ClickOn() - - Note currently ai restrained() returns 0 in all cases, - therefore restrained code has been removed - - The AI can double click to move the camera (this was already true but is cleaner), - or double click a mob to track them. - - 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(client.buildmode) // comes after object.Click to allow buildmode gui objects to be clicked - build_click(src, client.buildmode, params, A) - return - - if(control_disabled || stat) return - - if(ismob(A)) - ai_actual_track(A) - else - A.move_camera_by_click() - - -/mob/living/silicon/ai/ClickOn(var/atom/A, params) - if(world.time <= next_click) - return - next_click = world.time + 1 - - if(client.buildmode) // comes after object.Click to allow buildmode gui objects to be clicked - build_click(src, client.buildmode, params, A) - return - - if(stat) - return - - var/list/modifiers = params2list(params) - if(modifiers["shift"] && modifiers["ctrl"]) - CtrlShiftClickOn(A) - return - if(modifiers["middle"]) - MiddleClickOn(A) - return - if(modifiers["shift"]) - ShiftClickOn(A) - return - if(modifiers["alt"]) // alt and alt-gr (rightalt) - AltClickOn(A) - return - if(modifiers["ctrl"]) - CtrlClickOn(A) - return - - if(control_disabled || !canClick()) - return - - if(aiCamera.in_camera_mode) - aiCamera.camera_mode_off() - aiCamera.captureimage(A, usr) - return - - /* - AI restrained() currently does nothing - if(restrained()) - RestrainedClickOn(A) - else - */ - A.add_hiddenprint(src) - A.attack_ai(src) - -/* - AI has no need for the UnarmedAttack() and RangedAttack() procs, - because the AI code is not generic; attack_ai() is used instead. - The below is only really for safety, or you can alter the way - it functions and re-insert it above. -*/ -/mob/living/silicon/ai/UnarmedAttack(atom/A) - A.attack_ai(src) -/mob/living/silicon/ai/RangedAttack(atom/A) - A.attack_ai(src) - -/atom/proc/attack_ai(mob/user as mob) - return - -/* - Since the AI handles shift, ctrl, and alt-click differently - than anything else in the game, atoms have separate procs - for AI shift, ctrl, and alt clicking. -*/ - -/mob/living/silicon/ai/ShiftClickOn(var/atom/A) - if(!control_disabled && A.AIShiftClick(src)) - return - ..() - -/mob/living/silicon/ai/CtrlClickOn(var/atom/A) - if(!control_disabled && A.AICtrlClick(src)) - return - ..() - -/mob/living/silicon/ai/AltClickOn(var/atom/A) - if(!control_disabled && A.AIAltClick(src)) - return - ..() - -/mob/living/silicon/ai/MiddleClickOn(var/atom/A) - if(!control_disabled && A.AIMiddleClick(src)) - return - ..() - -/* - The following criminally helpful code is just the previous code cleaned up; - I have no idea why it was in atoms.dm instead of respective files. -*/ - -/atom/proc/AICtrlShiftClick(mob/user) - return - -/atom/proc/AIShiftClick(mob/user) - return - -/obj/machinery/door/airlock/AIShiftClick(mob/user) // Opens and closes doors! - add_hiddenprint(user) - toggle_open(user)//instead of topic() procs - return TRUE - -/atom/proc/AICtrlClick(mob/user) - return - -/obj/machinery/door/airlock/AICtrlClick(mob/user) // Bolts doors - add_hiddenprint(user) - toggle_bolt(user)//apparently this is better than the topic function - return TRUE - -/obj/machinery/power/apc/AICtrlClick(mob/user) // turns off/on APCs. - add_hiddenprint(user) - toggle_breaker(user) - return TRUE - -/obj/machinery/turretid/AICtrlClick(mob/user) //turns off/on Turrets - add_hiddenprint(user) - enabled = !enabled //toggles the turret on/off - return TRUE - -/atom/proc/AIAltClick(var/atom/A) - return AltClick(A) - -/obj/machinery/door/airlock/AIAltClick(mob/user) // Electrifies doors. - if(electrified_until) - electrify(0, 1) - else - electrify(-1,1) - return TRUE - -/obj/machinery/turretid/AIAltClick(mob/user) //toggles lethal on turrets - add_hiddenprint(user) - lethal = !lethal - return TRUE - -/atom/proc/AIMiddleClick(var/mob/living/silicon/user) - return FALSE - -/obj/machinery/door/airlock/AIMiddleClick(mob/user) // Toggles door bolt lights. - if(..()) - return - add_hiddenprint(user) - if(wires.is_cut(WIRE_BOLT_LIGHT)) - to_chat(user, "The lights are not responsive to your command.") - return - lights = !lights - to_chat(user, SPAN_NOTICE("Lights are now [lights ? "enabled." : "disabled."]")) - update_icon() - return TRUE - -// -// Override AdjacentQuick for AltClicking -// - -/mob/living/silicon/ai/TurfAdjacent(var/turf/T) - return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) diff --git a/code/game/click/click-human-legacy.dm b/code/game/click/click-human-legacy.dm new file mode 100644 index 00000000000..38e81c8f77b --- /dev/null +++ b/code/game/click/click-human-legacy.dm @@ -0,0 +1,9 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/living/carbon/human/click_on(atom/target, location, control, raw_params, inject_clickchain_flags) + // this does imply abilities are not put in clickchain logs, + // but hey what can i do about old code i'm not refactoring this shit right now lmao + if(ab_handler?.process_click(src, target)) + return + ..() diff --git a/code/game/click/click-legacy.dm b/code/game/click/click-legacy.dm new file mode 100644 index 00000000000..e9aa7e63b47 --- /dev/null +++ b/code/game/click/click-legacy.dm @@ -0,0 +1,131 @@ +// todo: this is legacy because majority of calls to it are unaudited and unnecessary; new system +// handles melee cooldown at clickcode level. +/mob/proc/setClickCooldownLegacy(var/timeout) + next_move = max(world.time + timeout, next_move) + +/mob/proc/canClick() + if(next_move <= world.time) + return 1 + return 0 + +/* + Translates into attack_hand, 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. +*/ +/mob/proc/UnarmedAttack(var/atom/A, var/proximity_flag) + return + +/mob/living/UnarmedAttack(var/atom/A, var/proximity_flag) + if(is_incorporeal()) + return 0 + if(stat) + return 0 + return 1 + +/* + Ranged unarmed attack: + + This currently is just a default for all mobs, involving + laser eyes and telekinesis. You could easily add exceptions + for things like ranged glove touches, spitting alien acid/neurotoxin, + animals lunging, etc. +*/ +/mob/proc/RangedAttack(var/atom/A, var/params) + if(!mutations.len) return + if((MUTATION_LASER in mutations) && a_intent == INTENT_HARM) + LaserEyes(A) // moved into a proc below + else if(MUTATION_TELEKINESIS in mutations) + if(get_dist(src, A) > tk_maxrange) + return + A.attack_tk(src) + +// LEGACY PROC, STOP USING THIS +/atom/proc/CtrlClick(var/mob/user) + . = "keep-going" + +/atom/movable/CtrlClick(var/mob/user) + if(Adjacent(user)) + user.start_pulling(src) + else + return ..() + +/mob/proc/altclick_listed_turf(atom/A) + var/turf/T = get_turf(A) + if(!T) + return FALSE + if(!snowflake_ai_vision_adjacency(T)) + return FALSE + if(!client) + return FALSE + if(T == client.tgui_stat?.byond_stat_turf) + client.unlist_turf() + return TRUE + client.list_turf(T) + return TRUE + +/atom/proc/should_list_turf_on_alt_click(mob/user) + return isturf(src) || isturf(loc) + +// LEGACY PROC, STOP USING THIS +/atom/proc/AltClick(var/mob/user) + if(isAI(user) && !isitem(src) && !isturf(src)) + return "keep-going" + if(should_list_turf_on_alt_click(user) && user.altclick_listed_turf(src)) + return TRUE + return "keep-going" + +// todo: rework +/** + * If you see me on git blame just know that it's a weird situation. This was TurfAdjacent() + * but it was only used for AI cameras and statpanel + * So, this is an IOU refactor and in the legacy file. + */ +/mob/proc/snowflake_ai_vision_adjacency(var/turf/T) + return T.AdjacentQuick(src) + +// LEGACY PROC, STOP USING THIS +/atom/proc/CtrlShiftClick(var/mob/user) + return "keep-going" + +/mob/proc/LaserEyes(atom/A, params) + return + +/mob/living/LaserEyes(atom/A, params) + setClickCooldownLegacy(4) + var/turf/T = get_turf(src) + + var/obj/projectile/beam/LE = new (T) + LE.icon = 'icons/effects/genetics.dmi' + LE.icon_state = "eyelasers" + playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1) + LE.firer = src + LE.preparePixelProjectile(A, src, params) + LE.fire() + +/mob/living/carbon/human/LaserEyes(atom/A, params) + if(nutrition>0) + ..() + nutrition = max(nutrition - rand(1,5),0) + handle_regular_hud_updates() + else + to_chat(src, "You're out of energy! You need food!") + +/// MouseWheelOn +/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params) + SEND_SIGNAL(src, COMSIG_MOUSE_SCROLL_ON, A, delta_x, delta_y, params) + +//* Click Cooldown *// + +/** + * Prevents a mob from acting again until time has passed. + * This is in legacy because at some point click cooldowns will be .. weird, with remote control + * being a thing. But for now, let's not overcomplicate it. + */ +/mob/proc/apply_click_cooldown(time) + next_move = max(world.time + time, next_move) diff --git a/code/game/click/click-living.dm b/code/game/click/click-living.dm new file mode 100644 index 00000000000..d88c8c758ba --- /dev/null +++ b/code/game/click/click-living.dm @@ -0,0 +1,35 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/living/click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + if(!canClick()) + return clickchain_flags + //! legacy-ish; is there a better way to do this? + if(istype(loc, /obj/vehicle)) + var/obj/vehicle/in_vehicle = loc + if(in_vehicle.is_driver(src)) + return in_vehicle.handle_vehicle_click(clickchain, clickchain_flags) + //! end + if(!IS_CONSCIOUS(src)) + // only warn if they're trying to click Something + if(active_item || clickchain.target) + clickchain.chat_feedback( + SPAN_WARNING("You can't do that while unconscious."), + ) + return clickchain_flags | CLICKCHAIN_DO_NOT_PROPAGATE + if(!CHECK_MOBILITY(src, MOBILITY_CAN_USE)) + // only warn if they're trying to click Something + if(active_item || clickchain.target) + clickchain.chat_feedback( + SPAN_WARNING("You can't do that right now."), + ) + return clickchain_flags | CLICKCHAIN_DO_NOT_PROPAGATE + // by popular demand this is restored + // but given this is a low priority action and rigsuits are technically part of click interaction chain right now, + // this has to be in click interaction chain despite being what's supposed to be a 'ui click' and not a 'living click' + // TODO: just inject our own 'fullButton' param for left/middle/right with modifiers because this && chain is insane lol + if(clickchain.click_params["button"] == "middle" && !clickchain.click_params["shift"] && !clickchain.click_params["ctrl"] && !clickchain.click_params["alt"]) + // change hand on initiator, not ourselves, incase they're different + clickchain.initiator?.swap_hand() + return clickchain_flags | CLICKCHAIN_DO_NOT_PROPAGATE + return ..() diff --git a/code/game/click/click-new_player.dm b/code/game/click/click-new_player.dm new file mode 100644 index 00000000000..18a32a4607c --- /dev/null +++ b/code/game/click/click-new_player.dm @@ -0,0 +1,8 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** +* New players shouldn't click on anything at all. +*/ +/mob/new_player/click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + return CLICKCHAIN_DO_NOT_PROPAGATE diff --git a/code/game/click/click-observer-dead-legacy.dm b/code/game/click/click-observer-dead-legacy.dm new file mode 100644 index 00000000000..9267eb22697 --- /dev/null +++ b/code/game/click/click-observer-dead-legacy.dm @@ -0,0 +1,26 @@ +/client/var/inquisitive_ghost = 1 +/mob/observer/dead/verb/toggle_inquisition() // warning: unexpected inquisition + set name = "Toggle Inquisitiveness" + set desc = "Sets whether your ghost examines everything on click by default" + set category = "Ghost" + if(!client) return + client.inquisitive_ghost = !client.inquisitive_ghost + if(client.inquisitive_ghost) + to_chat(src, "You will now examine everything you click on.") + else + to_chat(src, "You will no longer examine things you click on.") + +// Oh by the way this didn't work with old click code which is why clicking shit didn't spam you +/atom/proc/attack_ghost(mob/observer/dead/user) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_GHOST, user) + // TODO: main ai interact bay code fucking disgusts me wtf + if(IsAdminGhost(user)) // admin AI interact + AdminAIInteract(user) + return + if(user.client && user.client.inquisitive_ghost) + user.examinate(src) + +// defaults to just attack_ai +/atom/proc/AdminAIInteract(mob/user) + return attack_ai(user) diff --git a/code/game/click/click-observer-dead.dm b/code/game/click/click-observer-dead.dm new file mode 100644 index 00000000000..5759a6b0df1 --- /dev/null +++ b/code/game/click/click-observer-dead.dm @@ -0,0 +1,16 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/observer/dead/double_click_on_special(atom/target, location, control, list/params) + var/mob/maybe_current = mind?.current + if(maybe_current && (maybe_current == target || (maybe_current in target))) + if(can_reenter_corpse) + reenter_corpse() + return TRUE + if(ismovable(target)) + ManualFollow(target) + return TRUE + if(isturf(target.loc) || isturf(target)) + forceMove(get_turf(target)) + return TRUE + return ..() diff --git a/code/game/click/click-observer.dm b/code/game/click/click-observer.dm new file mode 100644 index 00000000000..1b3a4a1ea87 --- /dev/null +++ b/code/game/click/click-observer.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/observer/click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + // yeaaah let's not remote control a ghost, yea? + // in the future this should probably only be for 'dead' because technically we can have + // other observers, but for now, this holds true + ASSERT(clickchain.initiator == src) + clickchain.target.attack_ghost(src) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING diff --git a/code/game/click/click-silicon-ai.dm b/code/game/click/click-silicon-ai.dm new file mode 100644 index 00000000000..87e41dadde8 --- /dev/null +++ b/code/game/click/click-silicon-ai.dm @@ -0,0 +1,47 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/living/silicon/ai/double_click_on_special(atom/target, location, control, list/params) + // TODO: refactor this, kinda funny how I wrote 'silicon control interaction allowed' should be + // made generic / not special and then just copypasted it lol + if(IS_CONSCIOUS(src) && !control_disabled) + // TODO: refactor both of these; latter uses usr, former idk + if(ismob(target)) + ai_actual_track(target) + return TRUE + else if(isturf(target) || isturf(target.loc)) + target.move_camera_by_click() + return TRUE + return ..() + +/mob/living/silicon/ai/silicon_control_interaction_allowed(atom/target, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + if(!IS_CONSCIOUS(src)) + actor.chat_feedback( + SPAN_WARNING("You are unconscious."), + target = src, + ) + return FALSE + if(control_disabled) + actor.chat_feedback( + SPAN_WARNING("Your wireless control is disabled."), + target = src, + ) + return FALSE + return ..() + +// this will probably need to be rewritten at some point but like. +// it works, right. +// plus what's the chances we need to implement AIs literally punching people +// haaaah.. + +/mob/living/silicon/ai/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + clickchain.target.attack_ai(src) + // TODO: better hiddenprint/tracing system that's actor-aware + clickchain.target.add_hiddenprint(usr) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/mob/living/silicon/ai/ranged_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + clickchain.target.attack_ai(src) + // TODO: better hiddenprint/tracing system that's actor-aware + clickchain.target.add_hiddenprint(usr) + return CLICKCHAIN_DO_NOT_PROPAGATE diff --git a/code/game/click/click-silicon-legacy.dm b/code/game/click/click-silicon-legacy.dm new file mode 100644 index 00000000000..aca2009b72f --- /dev/null +++ b/code/game/click/click-silicon-legacy.dm @@ -0,0 +1,9 @@ +// TODO: IOU a better silicon control system. + +/atom/proc/attack_ai(mob/user as mob) + +/atom/proc/attack_robot(mob/user as mob) + attack_ai(user) + +/mob/living/silicon/ai/snowflake_ai_vision_adjacency(var/turf/T) + return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) diff --git a/code/game/click/click-silicon-robot.dm b/code/game/click/click-silicon-robot.dm new file mode 100644 index 00000000000..e293af8ea3c --- /dev/null +++ b/code/game/click/click-silicon-robot.dm @@ -0,0 +1,24 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/living/silicon/robot/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + clickchain.target.attack_robot(src) + // TODO: better hiddenprint/tracing system that's actor-aware + clickchain.target.add_hiddenprint(usr) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/mob/living/silicon/robot/ranged_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + clickchain.target.attack_robot(src) + // TODO: better hiddenprint/tracing system that's actor-aware + clickchain.target.add_hiddenprint(usr) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/mob/living/silicon/robot/silicon_control_interaction_allowed(atom/target, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + // sigh. rework bolts when? + if(bolt && !bolt.malfunction) + actor?.chat_feedback( + SPAN_WARNING("Your restraining bolt prevents you from doing that."), + target = src, + ) + return FALSE + return TRUE diff --git a/code/game/click/click-silicon.dm b/code/game/click/click-silicon.dm new file mode 100644 index 00000000000..d321060e3d4 --- /dev/null +++ b/code/game/click/click-silicon.dm @@ -0,0 +1,97 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/living/silicon/click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + // first, the camera hook + if(aiCamera?.in_camera_mode) + aiCamera.camera_mode_off() + aiCamera.captureimage(clickchain.target, src, clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + // The hooks to perform AI control are before 'ranged_interaction_chain' + // as I don't know if we will at some point add, oh, I don't know, modules that let AIs + // fire lasers out of their core or something. + if(!active_item) + . = silicon_control_interaction_chain(clickchain.target, clickchain, clickchain, clickchain_flags) + if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) + return + return . | ..() + +/** + * Notice how weird the proc args are? That's because this can be called with a variety of manners, + * and clickchain data isn't always available. + * + * If you have a function that should require clickchain, like a lot of the default functions, + * simply do nothing. The default function for this should be to open an UI anyways. + * + * Unfortunately, this entire system does not support admin AI interact; this was deemed + * acceptable because admin AI interact should probably be a sideband from actual AI interact + * in the first place, with all the AI reworks planned for this codebase. + */ +/mob/living/silicon/proc/silicon_control_interaction_chain(atom/target, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + SHOULD_NOT_SLEEP(TRUE) + if(!silicon_control_interaction_allowed(target, actor, clickchain, clickchain_flags)) + return clickchain_flags | CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + var/list/click_params = clickchain.click_params + var/executed_something + if(click_params["shift"]) + if(click_params["ctrl"]) + executed_something = target.on_silicon_control_ctrl_shift_click(src, actor, clickchain, clickchain_flags) && "ctrl-shift" + else + executed_something = target.on_silicon_control_shift_click(src, actor, clickchain, clickchain_flags) && "shift" + else if(click_params["ctrl"]) + executed_something = target.on_silicon_control_ctrl_click(src, actor, clickchain, clickchain_flags) && "ctrl" + else if(click_params["alt"]) + executed_something = target.on_silicon_control_alt_click(src, actor, clickchain, clickchain_flags) && "alt" + else if(click_params["button"] == "middle") + executed_something = target.on_silicon_control_middle_click(src, actor, clickchain, clickchain_flags) && "middle" + if(executed_something) + clickchain.data[ACTOR_DATA_SILICON_CONTROL_LOG] ||= executed_something + // TODO: better hiddenprint/tracing system that's actor-aware + target?.add_hiddenprint(usr) + return clickchain_flags | CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING + return clickchain_flags + +/** + * This proc is never silent and should always tell the user what's wrong. This should not be called + * outside of user invocations. Do not overload this proc with snowflake behavior if you can help it. + * * Entities can still do their own checks down the line; this checks if we're allowed to interact at all. + */ +/mob/living/silicon/proc/silicon_control_interaction_allowed(atom/target, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + SHOULD_NOT_SLEEP(TRUE) + return TRUE + +/** + * * User argument provided for easy handling. Please emit feedback via actor.bubble_feedback() and similar procs. + * @return TRUE to prevent propagation of the click normally. + */ +/atom/proc/on_silicon_control_ctrl_shift_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + return FALSE + +/** + * * User argument provided for easy handling. Please emit feedback via actor.bubble_feedback() and similar procs. + * @return TRUE to prevent propagation of the click normally. + */ +/atom/proc/on_silicon_control_shift_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + return FALSE + +/** + * * User argument provided for easy handling. Please emit feedback via actor.bubble_feedback() and similar procs. + * @return TRUE to prevent propagation of the click normally. + */ +/atom/proc/on_silicon_control_ctrl_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + return FALSE + +/** + * * User argument provided for easy handling. Please emit feedback via actor.bubble_feedback() and similar procs. + * @return TRUE to prevent propagation of the click normally. + */ +/atom/proc/on_silicon_control_alt_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + return FALSE + +/** + * * Please do not put important functions on this. For accessibility reasons, we cannot assume the middle button exists for all users. + * * User argument provided for easy handling. Please emit feedback via actor.bubble_feedback() and similar procs. + * @return TRUE to prevent propagation of the click normally. + */ +/atom/proc/on_silicon_control_middle_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + return FALSE diff --git a/code/game/click/click.dm b/code/game/click/click.dm index af7d65e3495..93bb792d1b2 100644 --- a/code/game/click/click.dm +++ b/code/game/click/click.dm @@ -1,334 +1,456 @@ -/** - * Backend clickcode. - * - * * Things in this file are **not** part of the clickchain, instead being the machinery that initiates the clickchain. - */ - -/* - 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. - - Note that this proc can be overridden, and is in the case of screen objects. -*/ - -/atom/Click(var/location, var/control, var/params) // This is their reaction to being clicked on (standard proc) - if(!(atom_flags & ATOM_INITIALIZED)) - to_chat(usr, SPAN_WARNING("[type] initialization failure. Click dropped. Contact a coder or admin.")) - return - SEND_SIGNAL(src, COMSIG_CLICK, location, control, params, usr) - usr.ClickOn(src, params) - -/atom/DblClick(var/location, var/control, var/params) - if(!(atom_flags & ATOM_INITIALIZED)) - to_chat(usr, SPAN_WARNING("[type] initialization failure. Click dropped. Contact a coder or admin.")) - return - usr.DblClickOn(src, params) - -/atom/MouseWheel(delta_x,delta_y,location,control,params) - usr.MouseWheelOn(src, delta_x, delta_y, params) +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// /** - * click handling entrypoint + * Master click handling entrypoint file for both actual clicks and + * remote control (direct click interaction chain invocation) clicks. * - * handles some intercepts (many of which will be potentially moved into other procs later) - * handles root level intercepts like admin buildmode / panel + * A 'natural' clickchain is approximately as follows; + * * Click/DblClick/MouseWheel/... (native procs) called on the client. Security handling and misc hooks happen here. + * * Click/DblClick/MouseWheel/... (native procs) called on the clicked atom. Overrides are allowed but discouraged here. + * * click_on/double_click_on/... called on the mob. The majority of sanity checks, bindings, etc, are handled here. + * * on_x_click called on the atom if it's a special modifier click. Various behaviors are here. + * --- Above cannot be touched by remote control --- + * * If not, the click is passsed off to `click_interaction_chain()` for standard handling of what you'd + * usually consider 'interacting' with something. * - * ! Warning: Any custom calls to this must ensure 'params' argument adheres to BYOND specifications. ! - * - * todo: better description - * todo: accept params as string from BYOND or pre-built list - * - * @params - * * A - /atom clicked on - * * params - byond params list - * * clickchain_flags - allows additional flags to be passed down from, say, the statpanel if this is a routed call. + * How remote control of mobs (will) work is that clicks are sent to click_interaction_chain(). */ -/mob/proc/ClickOn(atom/A, params, clickchain_flags) - if(world.time < next_click) // Hard check, before anything else, to avoid crashing - return - next_click = world.time + 1 - if(client.buildmode) - build_click(src, client.buildmode, params, A) - return +//* Native /atom Procs *// - // params are sent as a list directly to item procs - // because WHY - // would you do the work of list-allocing and unpacking and then send a - // packed version things have to unpack a second or even third time - // because they can't check the test version??? - var/list/unpacked_params = params2list(params) - // todo: this is shitcode, entire click params system needs an overhaul to support stuff better. - // notably we should stop relying on old button=1 params, as opposed to button=left/right/middle param. - if(unpacked_params["shift"] && unpacked_params["ctrl"]) - CtrlShiftClickOn(A) - return 1 - if(unpacked_params["shift"] && unpacked_params["middle"]) - ShiftMiddleClickOn(A) - return 1 - if(unpacked_params["middle"]) - MiddleClickOn(A) - return 1 - if(unpacked_params["shift"]) - ShiftClickOn(A) - return 0 - if(unpacked_params["alt"]) // alt and alt-gr (rightalt) - AltClickOn(A) - return 1 - if(unpacked_params["ctrl"]) - CtrlClickOn(A) - return 1 - switch(unpacked_params["button"]) - if("right") +/atom/Click(location, control, params) + if(!(atom_flags & ATOM_INITIALIZED)) + to_chat(usr, SPAN_WARNING("[src] ([type]) not yet initialized; please contact a coder with this message.")) + return + var/signal_returns = SEND_SIGNAL(src, COMSIG_ATOM_CLICK, usr, location, control, params) + if(signal_returns & RAISE_ATOM_CLICK_DROP) + return + usr.click_on(src, location, control, params) + +/atom/DblClick(location, control, params) + if(!(atom_flags & ATOM_INITIALIZED)) + to_chat(usr, SPAN_WARNING("[src] ([type]) not yet initialized; please contact a coder with this message.")) + return + usr.double_click_on(src, location, control, params) + +/atom/MouseWheel(delta_x, delta_y, location, control, params) + if(!(atom_flags & ATOM_INITIALIZED)) + return + usr.mouse_wheel_on(src, delta_x, delta_y, location, control, params) + +// TODO: other click procs should be here too + +//* Single Click *// + +/** + * Entrypoint of clickchain processing. + * * This should never be called other than as a **verb** executed by our own client. + */ +/mob/proc/click_on(atom/target, location, control, raw_params, inject_clickchain_flags) + // make sure no one's doing something insane + if(usr != src) + CRASH("non-src usr click_on in mob. someone is abusing the proc and likely incorrectly so.") + // handle legacy; in the future, things like buildmode clicks (pre-throttle) happen here + if(legacy_click_on(target, location, control, raw_params)) + return TRUE + // Throttle self outbound clicks to once per tick. + if(world.time < next_click) + return FALSE + next_click = world.time + world.tick_lag + + var/list/params = params2list(raw_params) + + /** + * If you've noticed we are missing a few of the usual modifiers, that's correct. + * /mob level only defines the 'standard' keys that are considered to be so + * ubiquitous across ss13 that we are choosing not to change them. + * + * Special behaviors like ctrl shift middle click whatnots should go to subtypes of mobs. + * + * In the future, we'll need a dynamic rebind system for click behaviors. + * + * For laptop compatibility, middle click is not considered a standard bind. While we can + * and will use it, please opt to not make it too standardized. + */ + + /** + * Furthermore, note that 'button' is the mouse button being pressed or released. + * 'left' 'right' 'middle' being set in params just means it's still being held. + * This means that detecting specific clicks is actually somewhat complicated. + * It'll be a project for another time to determine what is the ultimately 'correct' behavior + * for this. + * Generally, we consider the button being pressed/depressed as the active cilck button. + */ + var/resolved_action + if(click_on_override(target, location, control, params)) + return TRUE + + switch(params["button"]) if("left") + if(params["ctrl"]) + if(params["shift"]) + resolved_action = CLICK_ACTION_CTRL_SHIFT_LMB + else + resolved_action = CLICK_ACTION_CTRL_LMB + else if(params["shift"]) + resolved_action = CLICK_ACTION_SHIFT_LMB + else if(params["alt"]) + resolved_action = CLICK_ACTION_ALT_LMB + else + resolved_action = CLICK_ACTION_LMB if("middle") - MiddleClickOn(A) - return 1 + if(params["shift"]) + resolved_action = CLICK_ACTION_SHIFT_MMB + else + resolved_action = CLICK_ACTION_MMB + if("right") + if(params["shift"]) + resolved_action = CLICK_ACTION_SHIFT_RMB + else + resolved_action = CLICK_ACTION_RMB - if(!IS_CONSCIOUS(src)) + switch(resolved_action) + if(CLICK_ACTION_CTRL_LMB) + if(ctrl_click_on(target, location, control, params)) + return TRUE + if(CLICK_ACTION_CTRL_SHIFT_LMB) + if(ctrl_shift_click_on(target, location, control, params)) + return TRUE + if(CLICK_ACTION_SHIFT_LMB) + if(shift_click_on(target, location, control, params)) + return TRUE + if(CLICK_ACTION_ALT_LMB) + if(alt_click_on(target, location, control, params)) + return TRUE + if(CLICK_ACTION_MMB) + if(middle_click_on(target, location, control, params)) + return TRUE + if(CLICK_ACTION_SHIFT_MMB) + if(shift_middle_click_on(target, location, control, params)) + return TRUE + + if(click_on_special(target, location, control, params)) + return TRUE + + var/datum/event_args/actor/clickchain/clickchain = new + clickchain.initiator = src + clickchain.performer = src + clickchain.target = target + clickchain.target_zone = zone_sel?.selecting || BP_TORSO + clickchain.using_intent = a_intent + clickchain.using_hand_index = active_hand + params["action"] = resolved_action + clickchain.click_params = params + + var/clickchain_flags = inject_clickchain_flags || NONE + var/ret_clickchain_flags = click_interaction(clickchain, clickchain_flags) + + if(ret_clickchain_flags & (CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_ALWAYS_LOG)) + log_clickchain(clickchain, clickchain_flags) + return !!(ret_clickchain_flags & CLICKCHAIN_DID_SOMETHING) + +/** + * Standard world-click interaction chain. + * * Routes as needed to item/melee/tool interaction chains. + * * This can be called by remote control handling directly. + * @return clickchain flags + */ +/mob/proc/click_interaction(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + var/obj/item/active_item = get_active_held_item() + if(clickchain.target && Reachability(clickchain.target, null, active_item?.reach, active_item)) + clickchain_flags |= CLICKCHAIN_HAS_PROXIMITY + return click_interaction_chain(clickchain, clickchain_flags, active_item) + +/** + * The proc that actually does something with a held item. Override this to do something else. + * * Also handles standard unarmed behavior. + * @return clickchain flags + */ +/mob/proc/click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + face_atom(clickchain.target) + + . = click_interaction_chain_override(clickchain, clickchain_flags, active_item) + if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) return - face_atom(A) // change direction to face what you clicked on - - if(!canClick()) // in the year 2000... + switch(clickchain.click_params[CLICK_PARAM_ACTION]) + if(CLICK_ACTION_CTRL_LMB) + . = ctrl_click_interaction_chain(clickchain, clickchain_flags, active_item) + if(CLICK_ACTION_CTRL_SHIFT_LMB) + . = ctrl_shift_click_interaction_chain(clickchain, clickchain_flags, active_item) + if(CLICK_ACTION_SHIFT_LMB) + . = shift_click_interaction_chain(clickchain, clickchain_flags, active_item) + if(CLICK_ACTION_ALT_LMB) + . = alt_click_interaction_chain(clickchain, clickchain_flags, active_item) + if(CLICK_ACTION_MMB) + . = middle_click_interaction_chain(clickchain, clickchain_flags, active_item) + if(CLICK_ACTION_SHIFT_MMB) + . = shift_middle_click_interaction_chain(clickchain, clickchain_flags, active_item) + if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) return - if(istype(loc, /obj/vehicle/sealed/mecha)) - if(!locate(/turf) in list(A, A.loc)) // Prevents inventory from being drilled - return - var/obj/vehicle/sealed/mecha/M = loc - return M.click_action(A, src, params) + . = click_interaction_chain_special(clickchain, clickchain_flags, active_item) + if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) + return + + if(active_item == clickchain.target) + active_item.attack_self(src, clickchain) + //! legacy + trigger_aiming(TARGET_CAN_CLICK) + //! end + return clickchain_flags | CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE if(restrained()) - setClickCooldownLegacy(10) - RestrainedClickOn(A) - return 1 - - if(!CHECK_MOBILITY(src, MOBILITY_CAN_USE)) - to_chat(src, SPAN_WARNING("You can't do that right now.")) - return + // TODO: should we warn them that they're restrained? + setClickCooldownLegacy(1 SECONDS) + return clickchain_flags | CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE if(throw_mode_check()) - if(isturf(A) || isturf(A.loc)) - throw_active_held_item(A) - // todo: pass in overhand arg so we aren't stuck using throw mode off AFTER the call + if(isturf(clickchain.target) || isturf(clickchain.target.loc)) + throw_active_held_item(clickchain.target) + // TODO: pass in overhand arg so we can turn throw mode off immediately, not after the call throw_mode_off() - return 1 + return clickchain_flags | CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE throw_mode_off() - //? Grab click semantics - var/obj/item/I = get_active_held_item() + // check if we can click 'out' from our current location + // TODO: refactor this, maybe? + var/ranged_generics_allowed = loc?.AllowClick(src, clickchain.target, active_item) - //? Handle special cases - if(I == A) - // attack_self - I.attack_self(src) - // todo: refactor - trigger_aiming(TARGET_CAN_CLICK) - return - - //? check if we can click from our current location - var/ranged_generics_allowed = loc?.AllowClick(src, A, I) - - if(Reachability(A, null, I?.reach, I)) - //? attempt melee attack chain - if(I) - I.melee_interaction_chain(A, src, clickchain_flags | CLICKCHAIN_HAS_PROXIMITY, unpacked_params) + // check if we should route to melee or ranged interaction chains + if(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) + if(active_item) + . = active_item.melee_interaction_chain(clickchain, clickchain_flags) else - melee_interaction_chain(A, clickchain_flags | CLICKCHAIN_HAS_PROXIMITY, unpacked_params) - // todo: refactor aiming + . = melee_interaction_chain(clickchain, clickchain_flags) + //! legacy trigger_aiming(TARGET_CAN_CLICK) + //! end return else if(ranged_generics_allowed) - //? attempt ranged attack chain - if(I) - I.ranged_interaction_chain(A, src, clickchain_flags, unpacked_params) + if(active_item) + . = active_item.ranged_interaction_chain(clickchain, clickchain_flags) else - ranged_interaction_chain(A, clickchain_flags, unpacked_params) - // todo: refactor aiming + . = ranged_interaction_chain(clickchain, clickchain_flags) + //! legacy trigger_aiming(TARGET_CAN_CLICK) + //! end return -// todo: this is legacy because majority of calls to it are unaudited and unnecessary; new system -// handles melee cooldown at clickcode level. -/mob/proc/setClickCooldownLegacy(var/timeout) - next_move = max(world.time + timeout, next_move) - -/mob/proc/canClick() - if(next_move <= world.time) - 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) - return - -/* - Translates into attack_hand, 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. -*/ -/mob/proc/UnarmedAttack(var/atom/A, var/proximity_flag) - return - -/mob/living/UnarmedAttack(var/atom/A, var/proximity_flag) - if(is_incorporeal()) - return 0 - if(stat) - return 0 - return 1 - -/* - Ranged unarmed attack: - - This currently is just a default for all mobs, involving - laser eyes and telekinesis. You could easily add exceptions - for things like ranged glove touches, spitting alien acid/neurotoxin, - animals lunging, etc. -*/ -/mob/proc/RangedAttack(var/atom/A, var/params) - if(!mutations.len) return - if((MUTATION_LASER in mutations) && a_intent == INTENT_HARM) - LaserEyes(A) // moved into a proc below - else if(MUTATION_TELEKINESIS in mutations) - if(get_dist(src, A) > tk_maxrange) - return - A.attack_tk(src) -/* - Restrained ClickOn - - Used when you are handcuffed and click things. - Not currently used by anything but could easily be. -*/ -/mob/proc/RestrainedClickOn(var/atom/A) - return - -/mob/proc/MiddleClickOn(var/atom/A) - swap_hand() - -/mob/proc/ShiftMiddleClickOn(atom/A) - pointed(A) - -/mob/proc/ShiftClickOn(var/atom/A) - A.ShiftClick(src) - -/atom/proc/ShiftClick(var/mob/user) - if(user.client && user.allow_examine(src)) - user.examinate(src) - -/mob/proc/CtrlClickOn(var/atom/A) - A.CtrlClick(src) - -/atom/proc/CtrlClick(var/mob/user) - -/atom/movable/CtrlClick(var/mob/user) - if(Adjacent(user)) - user.start_pulling(src) - -/mob/proc/AltClickOn(atom/A) - if(!A.AltClick(src)) - altclick_listed_turf(A) - -/mob/proc/altclick_listed_turf(atom/A) - var/turf/T = get_turf(A) - if(!T) - return - if(!TurfAdjacent(T)) - return - if(!client) - return - if(T == client.tgui_stat?.byond_stat_turf) - client.unlist_turf() - return - client.list_turf(T) - -/atom/proc/AltClick(var/mob/user) - SEND_SIGNAL(src, COMSIG_CLICK_ALT, user) - if(open_context_menu(new /datum/event_args/actor(user))) +/** + * pretty much just for hooks that happen before standard handling and I'm too lazy to rewrite. + */ +/mob/proc/legacy_click_on(atom/target, location, control, params) + if(client.buildmode) + build_click(src, client.buildmode, params, target) return TRUE return FALSE -// todo: rework -/mob/proc/TurfAdjacent(var/turf/T) - return T.AdjacentQuick(src) - -/mob/proc/CtrlShiftClickOn(var/atom/A) - A.CtrlShiftClick(src) - return - -/atom/proc/CtrlShiftClick(var/mob/user) - return - -/mob/proc/LaserEyes(atom/A, params) - return - -/mob/living/LaserEyes(atom/A, params) - setClickCooldownLegacy(4) - var/turf/T = get_turf(src) - - var/obj/projectile/beam/LE = new (T) - LE.icon = 'icons/effects/genetics.dmi' - LE.icon_state = "eyelasers" - playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1) - LE.firer = src - LE.preparePixelProjectile(A, src, params) - LE.fire() - -/mob/living/carbon/human/LaserEyes(atom/A, params) - if(nutrition>0) - ..() - nutrition = max(nutrition - rand(1,5),0) - handle_regular_hud_updates() - else - to_chat(src, "You're out of energy! You need food!") - -/// Simple helper to face what you clicked on, in case it should be needed in more than one place. -/mob/proc/face_atom(var/atom/atom_to_face) - if(buckled || stat != CONSCIOUS || !atom_to_face || !x || !y || !atom_to_face.x || !atom_to_face.y) - return - if(!CHECK_MOBILITY(src, MOBILITY_CAN_MOVE)) - return - - var/dx = atom_to_face.x - x - var/dy = atom_to_face.y - y - if(!dx && !dy) // Wall items are graphically shifted but on the floor - if(atom_to_face.pixel_y > 16) - setDir(NORTH) - else if(atom_to_face.pixel_y < -16) - setDir(SOUTH) - else if(atom_to_face.pixel_x > 16) - setDir(EAST) - else if(atom_to_face.pixel_x < -16) - setDir(WEST) - return - - if(abs(dx) < abs(dy)) - if(dy > 0) - setDir(NORTH) - else - setDir(SOUTH) - else - if(dx > 0) - setDir(EAST) - else - setDir(WEST) - -/// MouseWheelOn -/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params) - SEND_SIGNAL(src, COMSIG_MOUSE_SCROLL_ON, A, delta_x, delta_y, params) - -//* Click Cooldown *// +//* Special modifiers; these are often routed per-mob. *// +//* This is done before clickchain, which means remote control doens't route here. *// /** - * Prevents a mob from acting again until time has passed. + * * Handled before atom procs. + * @return TRUE to break regular click handling logic. */ -/mob/proc/apply_click_cooldown(time) - next_move = max(world.time + time, next_move) +/mob/proc/click_on_override(atom/target, location, control, list/params) + return FALSE + +/** + * * Handled after atom procs. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/click_on_special(atom/target, location, control, list/params) + return FALSE + +/** + * * Standard binding; usually used for soft context menu. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/ctrl_shift_click_on(atom/target, location, control, list/params) + return target.ctrl_shift_clicked_on(src, location, control, params) + +/** + * * Will only be called from input of a client's mob. Remote control procs won't call this. + * @return TRUE to stop click propagation. + */ +/atom/proc/ctrl_shift_clicked_on(mob/user, location, control, list/params) + . = CtrlShiftClick(user) != "keep-going" + if(!.) + open_context_menu(new /datum/event_args/actor(user)) + . = TRUE + +/** + * * Nonstandard binding. + * * Usually 'point-at'. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/shift_middle_click_on(atom/target, location, control, list/params) + pointed(target) + return TRUE + +/** + * * Nonstandard binding. + * * Usually rigsuit activation, which isn't actually on this proc, so we generally just do nothing. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/middle_click_on(atom/target, location, control, list/params) + return target.middle_clicked_on(src, location, control, params) + +/atom/proc/middle_clicked_on(mob/user, location, control, list/params) + return FALSE + +/** + * * Standard binding; usually 'examine'. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/shift_click_on(atom/target, location, control, list/params) + return target.shift_clicked_on(src, location, control, params) + +/** + * * Will only be called from input of a client's mob. Remote control procs won't call this. + * @return TRUE to stop click propagation. + */ +/atom/proc/shift_clicked_on(mob/user, location, control, list/params) + return FALSE + +/** + * * Standard binding; usually 'pull'. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/ctrl_click_on(atom/target, location, control, list/params) + return target.ctrl_clicked_on(src, location, control, params) + +/** + * * Will only be called from input of a client's mob. Remote control procs won't call this. + * @return TRUE to stop click propagation. + */ +/atom/proc/ctrl_clicked_on(mob/user, location, control, list/params) + return CtrlClick(user) != "keep-going" + +/** + * * Standard binding; usually 'list turf'. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/alt_click_on(atom/target, location, control, list/params) + return target.alt_clicked_on(src, location, control, params) + +/** + * * Will only be called from input of a client's mob. Remote control procs won't call this. + * @return TRUE to stop click propagation. + */ +/atom/proc/alt_clicked_on(mob/user, location, control, list/params) + return AltClick(user) != "keep-going" + +//* Clickchain special modifier handling; remote control can route here, *// + +/mob/proc/click_interaction_chain_override(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_click_interaction_chain_override(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/ctrl_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_ctrl_click_interaction_chain(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/shift_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_shift_click_interaction_chain(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/ctrl_shift_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_ctrl_shift_click_interaction_chain(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/middle_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_middle_click_interaction_chain(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/shift_middle_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_shift_middle_click_interaction_chain(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/alt_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_alt_click_interaction_chain(clickchain, clickchain_flags, active_item) : NONE + +/mob/proc/click_interaction_chain_special(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain.target ? clickchain.target.on_click_interaction_chain_special(clickchain, clickchain_flags, active_item) : NONE + +/atom/proc/on_click_interaction_chain_override(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +/atom/proc/on_ctrl_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +/atom/proc/on_shift_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + // TODO: AI still can't examine. + if(clickchain.performer.should_client_shift_click_examine(src) && clickchain.performer.allow_examine(src)) + clickchain.performer.examinate(src) + return clickchain_flags | CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE + return clickchain_flags + +/atom/proc/on_ctrl_shift_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +/atom/proc/on_middle_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +/atom/proc/on_shift_middle_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +/atom/proc/on_alt_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +/atom/proc/on_click_interaction_chain_special(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + return clickchain_flags + +//* Double Click *// + +/** + * A click entrypoint proc. + * * This should never be called other than as a **verb** executed by our own client. + */ +/mob/proc/double_click_on(atom/target, location, control, raw_params) + // make sure no one's doing something insane + if(usr != src) + CRASH("non-src usr double_click_on in mob. someone is abusing the proc and likely incorrectly so.") + // Throttle self outbound clicks to once per tick. + if(world.time < next_click) + return TRUE + next_click = world.time + world.tick_lag + + var/list/params = params2list(raw_params) + + if(double_click_on_override(target, location, control, params)) + return TRUE + if(target.double_clicked_on(src, location, control, params)) + return TRUE + if(double_click_on_special(target, location, control, params)) + return TRUE + return TRUE + +/** + * * Handled before atom procs. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/double_click_on_override(atom/target, location, control, list/params) + return FALSE + +/** + * * Handled after atom procs. + * @return TRUE to break regular click handling logic. + */ +/mob/proc/double_click_on_special(atom/target, location, control, list/params) + return FALSE + +/atom/proc/double_clicked_on(mob/user, location, control, list/params) + return FALSE + +//* Mouse Wheel *// + +/** + * A click entrypoint proc. + * * This should never be called other than as a **verb** executed by our own client. + */ +/mob/proc/mouse_wheel_on(atom/target, delta_x, delta_y, location, control, raw_params) + SHOULD_NOT_OVERRIDE(TRUE) + // make sure no one's doing something insane + if(usr != src) + CRASH("non-src usr mouse_wheel_on in mob. someone is abusing the proc and likely incorrectly so.") + return FALSE + +/atom/proc/mouse_wheeled_on(mob/user, delta_x, delta_y, location, control, raw_params) + return user.MouseWheelOn(src, delta_x, delta_y, raw_params) diff --git a/code/game/click/cyborg.dm b/code/game/click/cyborg.dm deleted file mode 100644 index 5cdaf198213..00000000000 --- a/code/game/click/cyborg.dm +++ /dev/null @@ -1,187 +0,0 @@ -/* - Cyborg ClickOn() - - Cyborgs have no range restriction on attack_robot(), because it is basically an AI click. - However, they do have a range restriction on item use, so they cannot do without the - adjacency code. -*/ - -// todo: unify with normal click procs -/mob/living/silicon/robot/ClickOn(var/atom/A, var/params) - if(world.time <= next_click) - return - next_click = world.time + 1 - - if(client.buildmode) // comes after object.Click to allow buildmode gui objects to be clicked - build_click(src, client.buildmode, params, A) - return - - var/list/unpacked_params = params2list(params) - if(unpacked_params["shift"] && unpacked_params["ctrl"]) - CtrlShiftClickOn(A) - return - if(unpacked_params["shift"] && unpacked_params["middle"]) - ShiftMiddleClickOn(A) - return - if(unpacked_params["middle"]) - MiddleClickOn(A) - return - if(unpacked_params["shift"]) - ShiftClickOn(A) - return - if(unpacked_params["alt"]) // alt and alt-gr (rightalt) - AltClickOn(A) - return - if(unpacked_params["ctrl"]) - CtrlClickOn(A) - return - - if(!IS_CONSCIOUS(src) || !CHECK_MOBILITY(src, MOBILITY_CAN_USE)) - return - - if(!canClick()) - return - - face_atom(A) // change direction to face what you clicked on - - if(aiCamera.in_camera_mode) - aiCamera.camera_mode_off() - if(is_component_functioning("camera")) - aiCamera.captureimage(A, usr) - else - to_chat(src, "Your camera isn't functional.") - return - - /* - cyborg restrained() currently does nothing - if(restrained()) - RestrainedClickOn(A) - return - */ - - //? Grab click semantics - var/obj/item/I = get_active_held_item() - - //? Core cyborg code - // Cyborgs have no range-checking unless there is item use - if(!I) - if(bolt && !bolt.malfunction && A.loc != module) - return - A.add_hiddenprint(src) - A.attack_robot(src) - return - - //? Handle special cases - if(I == A) - // attack_self - I.attack_self(src) - // todo: refactor - trigger_aiming(TARGET_CAN_CLICK) - return - - //? check if we can click from our current location - var/ranged_generics_allowed = loc?.AllowClick(src, A, I) - - if(Reachability(A, null, I?.reach, I)) - //? attempt melee attack chain - if(I) - I.melee_interaction_chain(A, src, CLICKCHAIN_HAS_PROXIMITY, unpacked_params) - else - melee_interaction_chain(A, CLICKCHAIN_HAS_PROXIMITY, unpacked_params) - // todo: refactor aiming - trigger_aiming(TARGET_CAN_CLICK) - return - else if(ranged_generics_allowed) - //? attempt ranged attack chain - if(I) - I.ranged_interaction_chain(A, src, NONE, unpacked_params) - else - ranged_interaction_chain(A, NONE, unpacked_params) - // todo: refactor aiming - trigger_aiming(TARGET_CAN_CLICK) - return - -//Middle click cycles through selected modules. -/mob/living/silicon/robot/MiddleClickOn(var/atom/A) - cycle_modules() - return - -//Give cyborgs hotkey clicks without breaking existing uses of hotkey clicks -// for non-doors/apcs -/mob/living/silicon/robot/CtrlShiftClickOn(var/atom/A) - A.BorgCtrlShiftClick(src) - -/mob/living/silicon/robot/ShiftClickOn(var/atom/A) - A.BorgShiftClick(src) - -/mob/living/silicon/robot/CtrlClickOn(var/atom/A) - A.BorgCtrlClick(src) - -/mob/living/silicon/robot/AltClickOn(var/atom/A) - if(!A.AltClick(src)) - altclick_listed_turf(A) - A.BorgAltClick(src) - -/atom/proc/BorgCtrlShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden - CtrlShiftClick(user) - -/obj/machinery/door/airlock/BorgCtrlShiftClick(mob/living/silicon/robot/user) - if(user.bolt && !user.bolt.malfunction) - return - AICtrlShiftClick(user) - -/atom/proc/BorgShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden - ShiftClick(user) - -/obj/machinery/door/airlock/BorgShiftClick(mob/living/silicon/robot/user) // Opens and closes doors! Forwards to AI code. - if(user.bolt && !user.bolt.malfunction) - return - AIShiftClick(user) - -/atom/proc/BorgCtrlClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden - CtrlClick(user) - -/obj/machinery/door/airlock/BorgCtrlClick(mob/living/silicon/robot/user) // Bolts doors. Forwards to AI code. - if(user.bolt && !user.bolt.malfunction) - return - AICtrlClick(user) - -/obj/machinery/power/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. - if(user.bolt && !user.bolt.malfunction) - return - AICtrlClick(user) - -/obj/machinery/turretid/BorgCtrlClick(mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. - if(user.bolt && !user.bolt.malfunction) - return - AICtrlClick(user) - -/atom/proc/BorgAltClick(var/mob/living/silicon/robot/user) - return - -/obj/machinery/door/airlock/BorgAltClick(mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. - if(user.bolt && !user.bolt.malfunction) - return - AIAltClick(user) - -/obj/machinery/turretid/BorgAltClick(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. - if(user.bolt && !user.bolt.malfunction) - return - AIAltClick(user) - -/* - As with AI, these are not used in click code, - because the code for robots is specific, not generic. - - If you would like to add advanced features to robot - clicks, you can do so here, but you will have to - change attack_robot() above to the proper function -*/ -/mob/living/silicon/robot/UnarmedAttack(atom/A) - A.attack_robot(src) -/mob/living/silicon/robot/RangedAttack(atom/A) - A.attack_robot(src) - -/atom/proc/attack_robot(mob/user as mob) - attack_ai(user) - return diff --git a/code/game/click/items-item_attack_chain.dm b/code/game/click/items-item_attack_chain.dm index bf89cdaae25..baf002529d1 100644 --- a/code/game/click/items-item_attack_chain.dm +++ b/code/game/click/items-item_attack_chain.dm @@ -8,27 +8,27 @@ * * Called before melee_attack_chain() * * @params - * * e_args - the clickchain data, including who's doing the interaction + * * clickchain - the clickchain data, including who's doing the interaction * * clickchain_flags - the clickchain flags given * * @return CLICKCHAIN_* flags. These are added / interpreted by the caller. */ -/obj/item/proc/item_attack_chain(datum/event_args/actor/clickchain/e_args, clickchain_flags) +/obj/item/proc/item_attack_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) SHOULD_NOT_OVERRIDE(TRUE) . = clickchain_flags - . |= SEND_SIGNAL(src, COMSIG_ITEM_USING_AS_ITEM, e_args, clickchain_flags) + . |= SEND_SIGNAL(src, COMSIG_ITEM_USING_AS_ITEM, clickchain, clickchain_flags) if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return - . |= using_as_item(e_args.target, e_args, clickchain_flags) + . |= using_as_item(clickchain.target, clickchain, clickchain_flags) if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return - . |= SEND_SIGNAL(e_args.target, COMSIG_ATOM_USING_ITEM_ON, src, e_args, clickchain_flags) + . |= SEND_SIGNAL(clickchain.target, COMSIG_ATOM_USING_ITEM_ON, src, clickchain, clickchain_flags) if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return - . |= e_args.target.using_item_on(src, e_args, clickchain_flags) + . |= clickchain.target.using_item_on(src, clickchain, clickchain_flags) if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return diff --git a/code/game/click/items-tool_attack_chain.dm b/code/game/click/items-tool_attack_chain.dm index 323170b24d5..2a9c9e7401d 100644 --- a/code/game/click/items-tool_attack_chain.dm +++ b/code/game/click/items-tool_attack_chain.dm @@ -4,12 +4,10 @@ /** * Called when trying to click something that the user can Reachability() to, * to allow for the tool system to intercept the attack as a tool action. - * - * todo: should only have e_args and clickchain_flags as params. */ -/obj/item/proc/tool_attack_chain(atom/target, mob/user, clickchain_flags, list/params) +/obj/item/proc/tool_attack_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) SHOULD_NOT_OVERRIDE(TRUE) // are we on harm intent? if so, lol no - if(user && (user.a_intent == INTENT_HARM)) + if(clickchain.initiator?.a_intent == INTENT_HARM) return NONE - return target.tool_interaction(src, new /datum/event_args/actor/clickchain(user, target = target, params = params), clickchain_flags | CLICKCHAIN_TOOL_ACT) + return clickchain.target.tool_interaction(src, clickchain, clickchain_flags | CLICKCHAIN_TOOL_ACT) diff --git a/code/game/click/items.dm b/code/game/click/items.dm index 32f2ddc3690..096f4664ad0 100644 --- a/code/game/click/items.dm +++ b/code/game/click/items.dm @@ -5,44 +5,35 @@ //? Click-Chain system - using an item in hand to "attack", whether in melee or ranged. -// todo: refactor attack object/mob to just melee_attack_chain and a single melee attack system or something -// todo: yeah most of this file needs re-evaluated again, especially for event_args/actor/clickchain support & right clicks +/obj/item/proc/lazy_melee_interaction_chain(atom/target, mob/user, clickchain_flags, list/click_params) + var/datum/event_args/actor/clickchain/clickchain = new + clickchain.performer = clickchain.initiator = user + clickchain.using_intent = user.a_intent + clickchain.click_params = click_params || list() + clickchain.target = target + clickchain.target_zone = user.zone_sel?.selecting || BP_TORSO + return melee_interaction_chain(clickchain, clickchain_flags) /** * Called when trying to click something that the user can Reachability() to. * - * todo: this should allow passing in a clickchain datum instead. - * todo: lazy_melee_attack() for when you don't want to. - * * @params - * - target - thing hitting - * - user - user using us - * - clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] - * - params - params as list. + * * clickchain - clickchain data + * * clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] */ -/obj/item/proc/melee_interaction_chain(atom/target, mob/user, clickchain_flags, list/params) - // todo: this should be not overridable but it's not right now for indirection handling. we should just have - // an alternative indirection proc. - // SHOULD_NOT_OVERRIDE(TRUE) - +/obj/item/proc/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) // wow we have a lot of params // if only this was ss14 so we could have the EntityEventArgs :pleading: . = clickchain_flags - - var/datum/event_args/actor/clickchain/e_args = new(user) - e_args.target = target - e_args.click_params = params - - if(!(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) && ((. |= item_attack_chain(e_args, .)) & CLICKCHAIN_DO_NOT_PROPAGATE)) + if(!(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) && ((. |= item_attack_chain(clickchain, .)) & CLICKCHAIN_DO_NOT_PROPAGATE)) return - // todo: should only have e_args and clickchain_flags as params. - if(!(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) && ((. |= tool_attack_chain(target, user, ., params)) & CLICKCHAIN_DO_NOT_PROPAGATE)) + if(!(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) && ((. |= tool_attack_chain(clickchain, .)) & CLICKCHAIN_DO_NOT_PROPAGATE)) return // todo: is pre_attack really needed/justified? - if(!(. & (CLICKCHAIN_FLAGS_INTERACT_ABORT | CLICKCHAIN_FLAGS_ATTACK_ABORT)) && ((. |= pre_attack(target, user, ., params)) & CLICKCHAIN_DO_NOT_PROPAGATE)) + if(!(. & (CLICKCHAIN_FLAGS_INTERACT_ABORT | CLICKCHAIN_FLAGS_ATTACK_ABORT)) && ((. |= pre_attack(clickchain.target, clickchain.performer, ., clickchain.click_params)) & CLICKCHAIN_DO_NOT_PROPAGATE)) return // todo: refactor @@ -52,27 +43,24 @@ // - melee attack & receive melee attack (melee_interaction() on /atom? not item_melee_act directly?) // - melee attack shouldn't require attackby() to allow it to, it should be automatic on harm intent (?) // - the item should have final say but we need a way to allow click redirections so.. - if(!(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) && ((. |= resolve_attackby(target, user, params, null, ., e_args)) & CLICKCHAIN_DO_NOT_PROPAGATE)) + if(!(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) && ((. |= resolve_attackby(clickchain.target, clickchain.performer, clickchain.click_params, null, ., clickchain)) & CLICKCHAIN_DO_NOT_PROPAGATE)) return // todo: signal for afterattack here - return . | afterattack(target, user, clickchain_flags, params) + return . | afterattack(clickchain.target, clickchain.performer, clickchain_flags, clickchain.click_params) + +// TODO: lazy_ranged_interaction_chain /** * Called when trying to click something that the user can't Reachability() to. * - * todo: this should allow passing in a clickchain datum instead. - * todo: lazy_ranged_attack() for when you don't want to. - * * @params - * - target - thing hitting - * - user - user using us - * - clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] - * - params - params as list. + * * clickchain - clickchain data + * * clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] */ -/obj/item/proc/ranged_interaction_chain(atom/target, mob/user, clickchain_flags, list/params) +/obj/item/proc/ranged_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) // todo: signal for afterattack here - return clickchain_flags | afterattack(target, user, clickchain_flags, params) + return clickchain_flags | afterattack(clickchain.target, clickchain.performer, clickchain_flags, clickchain.click_params) /** * called at the start of melee attack chains diff --git a/code/game/click/mobs-melee_attack_chain.dm b/code/game/click/mobs-melee_attack_chain.dm index 4596aa5a977..4033612724e 100644 --- a/code/game/click/mobs-melee_attack_chain.dm +++ b/code/game/click/mobs-melee_attack_chain.dm @@ -11,7 +11,7 @@ * todo: how to inject style? * * @params - * * e_args - the clickchain data, including who's doing the interaction + * * clickchain - the clickchain data, including who's doing the interaction * * clickchain_flags - the clickchain flags given * * @return CLICKCHAIN_* flags. These are added / interpreted by the caller. @@ -23,7 +23,7 @@ if(clickchain_flags & CLICKCHAIN_DO_NOT_ATTACK) return clickchain_flags - if(!clickchain.target.is_melee_targetable(clickchain, clickchain_flags)) + if(!clickchain.target?.is_melee_targetable(clickchain, clickchain_flags)) return clickchain_flags var/datum/melee_attack/unarmed/using_style = default_unarmed_attack_style() diff --git a/code/game/click/mobs.dm b/code/game/click/mobs.dm index 48974308a33..c9518ef015d 100644 --- a/code/game/click/mobs.dm +++ b/code/game/click/mobs.dm @@ -1,35 +1,34 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 Citadel Station Developers *// +// TODO: lazy_melee_interaction_chain + /** * Called when trying to click on someone we can Reachability() to without an item in hand. * - * todo: this should allow passing in a clickchain datum instead. - * todo: lazy_melee_attack() for when you don't want to. - * * @params - * - target - thing we're clicking - * - clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] - * - params - parameters of click, as list + * * clickchain - clickchain data + * * clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] */ -/mob/proc/melee_interaction_chain(atom/target, clickchain_flags, list/params) +/mob/proc/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) // todo: refactor cooldown handling - if(ismob(target)) + if(ismob(clickchain.target)) setClickCooldownLegacy(get_attack_speed_legacy()) - UnarmedAttack(target, clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) + UnarmedAttack(clickchain.target, clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) + +// TODO: lazy_ranged_interaction_chain /** * Called when trying to click on someone we can't Reachability() to without an item in hand. * * @params - * - target - thing we're clicking - * - clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] - * - params - parameters of click, as list + * * clickchain - clickchain data + * * clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] */ -/mob/proc/ranged_interaction_chain(atom/target, clickchain_flags, list/params) +/mob/proc/ranged_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) // todo: NO. MORE. TEXT. PARAMS. WHY. ARE. WE. UNPACKING. THE. LIST. MULTIPLE. TIMES? - var/stupid_fucking_shim = list2params(params) - RangedAttack(target, stupid_fucking_shim) + var/stupid_fucking_shim = list2params(clickchain.click_params) + RangedAttack(clickchain.target, stupid_fucking_shim) /** * construct default event args for what we're doing to a target diff --git a/code/game/click/observer.dm b/code/game/click/observer.dm deleted file mode 100644 index bcc6bd43cf8..00000000000 --- a/code/game/click/observer.dm +++ /dev/null @@ -1,101 +0,0 @@ -/mob/observer/dead/DblClickOn(atom/A, params) - if(client.buildmode) - build_click(src, client.buildmode, params, A) - return - if(can_reenter_corpse && mind && mind.current) - if(A == mind.current || (mind.current in A)) // double click your corpse or whatever holds it - reenter_corpse() // (cloning scanner, body bag, closet, mech, etc) - return - - // Things you might plausibly want to follow - if(ismovable(A)) - ManualFollow(A) - - // Otherwise jump - else if(A.loc) - forceMove(get_turf(A)) -// update_parallax_contents() - -/client/var/inquisitive_ghost = 1 -/mob/observer/dead/verb/toggle_inquisition() // warning: unexpected inquisition - set name = "Toggle Inquisitiveness" - set desc = "Sets whether your ghost examines everything on click by default" - set category = "Ghost" - if(!client) return - client.inquisitive_ghost = !client.inquisitive_ghost - if(client.inquisitive_ghost) - to_chat(src, "You will now examine everything you click on.") - else - to_chat(src, "You will no longer examine things you click on.") - -/mob/observer/dead/ClickOn(var/atom/A, var/params) - if(client.buildmode) - build_click(src, client.buildmode, params, A) - return - var/list/modifiers = params2list(params) - if(modifiers["shift"] && modifiers["middle"]) - ShiftMiddleClickOn(A) - return - if(!canClick()) return - setClickCooldownLegacy(4) - // You are responsible for checking config_legacy.ghost_interaction when you override this function - // Not all of them require checking, see below - A.attack_ghost(src) - -// Oh by the way this didn't work with old click code which is why clicking shit didn't spam you -/atom/proc/attack_ghost(mob/observer/dead/user) - SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_GHOST, user) - // TODO: main ai interact bay code fucking disgusts me wtf - if(IsAdminGhost(user)) // admin AI interact - AdminAIInteract(user) - return - if(user.client && user.client.inquisitive_ghost) - user.examinate(src) - -// defaults to just attack_ai -/atom/proc/AdminAIInteract(mob/user) - return attack_ai(user) - -// --------------------------------------- -// And here are some good things for free: -// Now you can click through portals, wormholes, gateways, and teleporters while observing. -Sayu -/* -/obj/machinery/tele_pad/attack_ghost(mob/user as mob) - var/atom/l = loc - var/obj/machinery/computer/teleporter/com = locate(/obj/machinery/computer/teleporter, locate(l.x - 2, l.y, l.z)) - if(com.locked) - user.loc = get_turf(com.locked) -*/ -/obj/effect/portal/attack_ghost(mob/user) - . = ..() - if(target) - user.forceMove(get_turf(target)) - -/obj/machinery/gateway/centerstation/attack_ghost(mob/user) - . = ..() - if(awaygate) - user.forceMove(awaygate.loc) - else - to_chat(user, "[src] has no destination.") - -/obj/machinery/gateway/centeraway/attack_ghost(mob/user) - . = ..() - if(stationgate) - user.forceMove(stationgate.loc) - else - to_chat(user, "[src] has no destination.") - -// ------------------------------------------- -// This was supposed to be used by adminghosts -// I think it is a *terrible* idea -// but I'm leaving it here anyway -// commented out, of course. -/* -/atom/proc/attack_admin(mob/user as mob) - if(!user || !user.client || !user.client.holder) - return - attack_hand(user) - -*/ - diff --git a/code/game/click/other_mobs.dm b/code/game/click/other_mobs.dm index 9f18de76296..0c581836750 100644 --- a/code/game/click/other_mobs.dm +++ b/code/game/click/other_mobs.dm @@ -92,9 +92,6 @@ return (ui_interact(user) || nano_ui_interact(user)) // return FALSE -/mob/living/carbon/human/RestrainedClickOn(var/atom/A) - return - /mob/living/carbon/human/RangedAttack(atom/A) . = ..() if(.) @@ -117,35 +114,13 @@ else if(spitting) //Only used by xenos right now, can be expanded. Spit(A) -/mob/living/RestrainedClickOn(var/atom/A) - return - -/* - Animals & All Unspecified -*/ -// /mob/living/UnarmedAttack(atom/A) -// A.attack_animal(src) - -// /atom/proc/attack_animal(mob/user) -// SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_ANIMAL, user) - /* Aliens */ -/mob/living/carbon/alien/RestrainedClickOn(var/atom/A) - return - /mob/living/carbon/alien/UnarmedAttack(atom/A) if(!..()) return FALSE setClickCooldownLegacy(get_attack_speed_legacy()) A.attack_generic(src,rand(5,6),"bitten") - -/* - New Players: - Have no reason to click on anything at all. -*/ -/mob/new_player/ClickOn() - return diff --git a/code/game/click/reachability.dm b/code/game/click/reachability.dm index 4fec1fe4b95..75d02bc7433 100644 --- a/code/game/click/reachability.dm +++ b/code/game/click/reachability.dm @@ -137,7 +137,7 @@ var/turf/n = D.loc for(i in 1 to range) ASSERT(isturf(n)) - if(n.TurfAdjacency(th)) + if(n.TurfAdjacency(th, target, src)) // succeeded qdel(D) return REACH_PHYSICAL diff --git a/code/game/click/rig.dm b/code/game/click/route-rig-legacy.dm similarity index 56% rename from code/game/click/rig.dm rename to code/game/click/route-rig-legacy.dm index 5abb61642b2..ece150fbf57 100644 --- a/code/game/click/rig.dm +++ b/code/game/click/route-rig-legacy.dm @@ -29,23 +29,27 @@ to_chat(src, "Somehow you bugged the system. Setting your hardsuit mode to middle-click.") hardsuit_click_mode = MIDDLE_CLICK -/mob/living/MiddleClickOn(atom/A) - if(client && client.hardsuit_click_mode == MIDDLE_CLICK) - if(HardsuitClickOn(A)) +/mob/living/click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + if(!active_item) + var/route_to_rig = FALSE + // this is shitcode but at some point we'll need to refactor how this works so it works for now + switch(client?.hardsuit_click_mode) + if(MIDDLE_CLICK) + if(clickchain.click_params["button"] == "middle" && !clickchain.click_params["ctrl"] && !clickchain.click_params["shift"] && !clickchain.click_params["alt"]) + route_to_rig = TRUE + if(ALT_CLICK) + if(clickchain.click_params["alt"] && !clickchain.click_params["ctrl"] && !clickchain.click_params["shift"] ) + route_to_rig = TRUE + if(CTRL_CLICK) + if(clickchain.click_params["ctrl"] && !clickchain.click_params["alt"] && !clickchain.click_params["shift"] ) + route_to_rig = TRUE + // this is definitely shitcode. + var/obj/item/hardsuit/maybe_hardsuit = get_hardsuit(TRUE) + if(route_to_rig && maybe_hardsuit) + . = attempt_rigsuit_click(clickchain, clickchain_flags, maybe_hardsuit) + clickchain.data[ACTOR_DATA_RIG_CLICK_LOG] ||= "[maybe_hardsuit]" return - ..() - -/mob/living/AltClickOn(atom/A) - if(client && client.hardsuit_click_mode == ALT_CLICK) - if(HardsuitClickOn(A)) - return - ..() - -/mob/living/CtrlClickOn(atom/A) - if(client && client.hardsuit_click_mode == CTRL_CLICK) - if(HardsuitClickOn(A)) - return - ..() + return ..() /mob/living/proc/can_use_hardsuit() return 0 @@ -65,22 +69,6 @@ /mob/living/silicon/pai/can_use_hardsuit() return loc == card -/mob/living/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0) - if(!can_use_hardsuit() || !canClick()) - return 0 - var/obj/item/hardsuit/hardsuit = get_hardsuit(TRUE) - if(hardsuit?.selected_module) - if(src != hardsuit.wearer) - if(hardsuit.ai_can_move_suit(src, check_user_module = 1)) - message_admins("[key_name_admin(src, include_name = 1)] is trying to force \the [key_name_admin(hardsuit.wearer, include_name = 1)] to use a hardsuit module.") - else - return 0 - hardsuit.selected_module.engage(A, alert_ai) - if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns - setClickCooldownLegacy(get_attack_speed_legacy()) - return 1 - return 0 - #undef MIDDLE_CLICK #undef ALT_CLICK #undef CTRL_CLICK diff --git a/code/game/click/route-rig.dm b/code/game/click/route-rig.dm new file mode 100644 index 00000000000..7a935f47c84 --- /dev/null +++ b/code/game/click/route-rig.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * This should **not** be called by remote control. This is a direct route from click handling. + * Remote control of hardsuits should route to the hardsuit's clickchain receiver directly. + * @return clickchain flags + */ +/mob/proc/attempt_rigsuit_click(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/hardsuit/use_suit) + if(!use_suit) + use_suit = get_hardsuit(TRUE) + if(!use_suit) + return NONE + return use_suit.handle_hardsuit_module_click(clickchain, clickchain_flags) diff --git a/code/game/click/route-vehicle.dm b/code/game/click/route-vehicle.dm new file mode 100644 index 00000000000..807048c3569 --- /dev/null +++ b/code/game/click/route-vehicle.dm @@ -0,0 +1,12 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * This should **not** be called by remote control. This is a direct route from click handling. + * Remote control of vehicle should route to the vehicle's clickchain receiver directly. + * @return clickchain flags + */ +/mob/proc/attempt_mecha_click(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/vehicle/use_vehicle) + if(!use_vehicle) + return NONE + return use_vehicle.handle_vehicle_click(clickchain, clickchain_flags) diff --git a/code/game/gamemodes/technomancer/spells/flame_tongue.dm b/code/game/gamemodes/technomancer/spells/flame_tongue.dm index 98ad44291c9..5c695ec8f93 100644 --- a/code/game/gamemodes/technomancer/spells/flame_tongue.dm +++ b/code/game/gamemodes/technomancer/spells/flame_tongue.dm @@ -54,7 +54,7 @@ var/resolved = hit_atom.attackby(welder, user) if(!resolved && welder && hit_atom) if(pay_energy(500)) - welder.melee_interaction_chain(hit_atom, user, def_zone) + welder.lazy_melee_interaction_chain(hit_atom, user, def_zone) adjust_instability(4) if(welder && user && (welder.loc == user)) welder.loc = src diff --git a/code/game/gamemodes/technomancer/spells/reflect.dm b/code/game/gamemodes/technomancer/spells/reflect.dm index e837d14c190..434049bf64a 100644 --- a/code/game/gamemodes/technomancer/spells/reflect.dm +++ b/code/game/gamemodes/technomancer/spells/reflect.dm @@ -91,7 +91,7 @@ var/obj/item/W = clickchain.using_melee_weapon attacker = clickchain.performer if(attacker) - W.melee_interaction_chain(attacker, attacker) + W.lazy_melee_interaction_chain(attacker, attacker) to_chat(attacker, "Your [damage_source] goes through \the [src] in one location, comes out \ on the same side, and hits you!") diff --git a/code/game/gamemodes/technomancer/spells/targeting_matrix.dm b/code/game/gamemodes/technomancer/spells/targeting_matrix.dm index ea600152198..01f70f70254 100644 --- a/code/game/gamemodes/technomancer/spells/targeting_matrix.dm +++ b/code/game/gamemodes/technomancer/spells/targeting_matrix.dm @@ -26,7 +26,7 @@ if(I && pay_energy(200)) var/prox = user.Adjacent(chosen_target) if(prox) // Needed or else they can attack with melee from afar. - I.melee_interaction_chain(chosen_target,owner) + I.lazy_melee_interaction_chain(chosen_target,owner) I.afterattack(chosen_target,owner, prox) adjust_instability(2) diff --git a/code/game/gamemodes/technomancer/spells/warp_strike.dm b/code/game/gamemodes/technomancer/spells/warp_strike.dm index ec9ddc8e986..e6fbc286784 100644 --- a/code/game/gamemodes/technomancer/spells/warp_strike.dm +++ b/code/game/gamemodes/technomancer/spells/warp_strike.dm @@ -69,10 +69,10 @@ if(istype(I, /obj/item)) var/obj/item/W = I - W.melee_interaction_chain(chosen_target, user) + W.lazy_melee_interaction_chain(chosen_target, user) W.afterattack(chosen_target, user) else - I.melee_interaction_chain(chosen_target, user) + I.lazy_melee_interaction_chain(chosen_target, user) I.afterattack(chosen_target, user) else chosen_target.attack_hand(user) diff --git a/code/game/machinery/doors/airlock/airlock-ai.dm b/code/game/machinery/doors/airlock/airlock-ai.dm new file mode 100644 index 00000000000..150dbeea497 --- /dev/null +++ b/code/game/machinery/doors/airlock/airlock-ai.dm @@ -0,0 +1,54 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/obj/machinery/door/airlock/on_silicon_control_shift_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + var/log_str + // TODO: we only have 'attempted' because the procs don't have real ret vals + if(!density) + close() + log_str = "close (attempted)" + actor.chat_feedback( + SPAN_NOTICE("You send [src] a command to close."), + target = src, + ) + else + open() + log_str = "open (attempted)" + actor.chat_feedback( + SPAN_NOTICE("You send [src] a command to open."), + target = src, + ) + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "airlock [COORD(src)] [src] [log_str]" + return TRUE + +/obj/machinery/door/airlock/on_silicon_control_alt_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + var/log_str + // TODO: we only have 'attempted' because the procs don't have real ret vals + if(electrified_until) + electrify(0, TRUE) + log_str = "electrify off (attempted)" + else + electrify(-1, TRUE) + log_str = "electrify on (attempted)" + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "airlock [COORD(src)] [src] [log_str]" + return TRUE + +/obj/machinery/door/airlock/on_silicon_control_ctrl_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + var/log_str + // TODO: we only have 'attempted' because the procs don't have real ret vals + if(locked) + toggle_bolt(user) + log_str = "unbolt (attempted)" + else + toggle_bolt(user) + log_str = "bolt (attempted)" + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "airlock [COORD(src)] [src] [log_str]" + return TRUE + +/obj/machinery/door/airlock/on_silicon_control_ctrl_shift_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + // TODO: emergency access + return FALSE + +/obj/machinery/door/airlock/on_silicon_control_middle_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + // TODO: flash the lights or something else? + return FALSE diff --git a/code/game/machinery/turrets/turret_control-ai.dm b/code/game/machinery/turrets/turret_control-ai.dm new file mode 100644 index 00000000000..301a9134be9 --- /dev/null +++ b/code/game/machinery/turrets/turret_control-ai.dm @@ -0,0 +1,22 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/obj/machinery/turretid/on_silicon_control_alt_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + lethal = !lethal + actor.chat_feedback( + SPAN_WARNING("You set [src] to [lethal ? "lethal" : "stun"]."), + target = src, + ) + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "turret-control [COORD(src)] [REF(src)] lethal [lethal ? "on" : "off"]" + updateTurrets() + return TRUE + +/obj/machinery/turretid/on_silicon_control_ctrl_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + enabled = !enabled + actor.chat_feedback( + SPAN_WARNING("You turn [src] [enabled ? "on" : "off"]."), + target = src, + ) + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "turret-control [COORD(src)] [REF(src)] power [enabled ? "on" : "off"]" + updateTurrets() + return TRUE diff --git a/code/game/machinery/virtual_reality/ar_console.dm b/code/game/machinery/virtual_reality/ar_console.dm index 50724b83125..ed647378db4 100644 --- a/code/game/machinery/virtual_reality/ar_console.dm +++ b/code/game/machinery/virtual_reality/ar_console.dm @@ -35,7 +35,7 @@ add_fingerprint(user) if(occupant && (istype(I, /obj/item/healthanalyzer) || istype(I, /obj/item/robotanalyzer))) - I.melee_interaction_chain(occupant, user) + I.lazy_melee_interaction_chain(occupant, user) return /obj/machinery/vr_sleeper/alien/eject() diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm index 3e8e094da69..06eb5c606cb 100644 --- a/code/game/machinery/virtual_reality/vr_console.dm +++ b/code/game/machinery/virtual_reality/vr_console.dm @@ -73,7 +73,7 @@ add_fingerprint(user) if(occupant && (istype(I, /obj/item/healthanalyzer) || istype(I, /obj/item/robotanalyzer))) - I.melee_interaction_chain(occupant, user) + I.lazy_melee_interaction_chain(occupant, user) return if(default_deconstruction_screwdriver(user, I)) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 4216510e7dd..0103286a5fb 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -45,3 +45,7 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) if (istype(M, /atom/movable)) do_teleport(M, target, 1) ///You will appear adjacent to the beacon +/obj/effect/portal/attack_ghost(mob/user) + . = ..() + if(target) + user.forceMove(get_turf(target)) diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index 115c23d1fa3..5651d16a89f 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -223,7 +223,7 @@ if(istype(W,/obj/item/healthanalyzer)) var/obj/item/healthanalyzer/analyzer = W for(var/mob/living/L in contents) - analyzer.melee_interaction_chain(L,user) + analyzer.lazy_melee_interaction_chain(L,user) else if(istype(W,/obj/item/reagent_containers/syringe)) if(syringe) diff --git a/code/game/objects/items/robobag.dm b/code/game/objects/items/robobag.dm index 8971556ecb3..be0efe574dc 100644 --- a/code/game/objects/items/robobag.dm +++ b/code/game/objects/items/robobag.dm @@ -75,7 +75,7 @@ if(istype(W,/obj/item/robotanalyzer)) var/obj/item/robotanalyzer/analyzer = W for(var/mob/living/L in contents) - analyzer.melee_interaction_chain(L,user) + analyzer.lazy_melee_interaction_chain(L,user) else if(istype(W, /obj/item/clothing/accessory/badge)) if(corptag) diff --git a/code/game/objects/items/robot/gripper.dm b/code/game/objects/items/robot/gripper.dm index 94544c9cca9..8af962db4d3 100644 --- a/code/game/objects/items/robot/gripper.dm +++ b/code/game/objects/items/robot/gripper.dm @@ -92,9 +92,9 @@ return clickchain_flags | I.afterattack(src, user, clickchain_flags, params) return ..() -/obj/item/gripper/melee_interaction_chain(atom/target, mob/user, clickchain_flags, list/params) +/obj/item/gripper/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) if(!isnull(wrapped)) - return wrapped.melee_interaction_chain(target, user, clickchain_flags | CLICKCHAIN_DO_NOT_ATTACK, params) + return wrapped.melee_interaction_chain(clickchain, clickchain_flags | CLICKCHAIN_REDIRECTED) return ..() /obj/item/gripper/verb/drop_item() diff --git a/code/game/objects/items/shield/types/shields_legacy.dm b/code/game/objects/items/shield/types/shields_legacy.dm index 26a77a63b3a..fc4855dcab0 100644 --- a/code/game/objects/items/shield/types/shields_legacy.dm +++ b/code/game/objects/items/shield/types/shields_legacy.dm @@ -53,7 +53,7 @@ /obj/item/shield/riot/flash/legacy_mob_melee_hook(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) if(user.a_intent == INTENT_HARM) return ..() - embedded_flash.melee_interaction_chain(target, user, clickchain_flags, params) + embedded_flash.lazy_melee_interaction_chain(target, user, clickchain_flags, params) /obj/item/shield/riot/flash/attack_self(mob/user, datum/event_args/actor/actor) . = ..() @@ -74,7 +74,7 @@ // var/mob/attacker = clickchain?.performer // if(attacker) // log_attack(key_name(attacker), key_name(defending), "flash shield auto-invoke") - // embedded_flash.melee_interaction_chain(attacker, defending) + // embedded_flash.lazy_melee_interaction_chain(attacker, defending) // else log_attack(key_name(defending), "none (AoE)", "flash shield auto-invoke") embedded_flash.attack_self(defending) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 242950b271a..758168477ff 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -369,11 +369,13 @@ return return ..() -/obj/item/stack/AltClick(mob/living/user) +/obj/item/stack/alt_clicked_on(mob/user, location, control, list/params) . = ..() - if(!istype(user) || !in_range(user, src) || !CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP)) + if(.) return - attempt_split_stack(user) + if(user.Reachability(src) && CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP)) + attempt_split_stack(user) + return TRUE /obj/item/stack/proc/attempt_split_stack(mob/living/user) if(uses_charge) diff --git a/code/game/objects/items/throwing.dm b/code/game/objects/items/throwing.dm index 46081eb5426..5ed399c0550 100644 --- a/code/game/objects/items/throwing.dm +++ b/code/game/objects/items/throwing.dm @@ -1,3 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + /** * get what we actually throw, so we can throw holders/grabs */ diff --git a/code/game/objects/items/tools/switchtool.dm b/code/game/objects/items/tools/switchtool.dm index 9b7499f0845..9c675ad33e5 100644 --- a/code/game/objects/items/tools/switchtool.dm +++ b/code/game/objects/items/tools/switchtool.dm @@ -277,19 +277,19 @@ update_icon() //? click redirection -/obj/item/switchtool/melee_interaction_chain(atom/target, mob/user, clickchain_flags, params) +/obj/item/switchtool/melee_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) if(!deployed) return ..() - . = deployed.melee_interaction_chain(target, user, clickchain_flags | CLICKCHAIN_REDIRECTED, params) + . = deployed.melee_interaction_chain(clickchain, clickchain_flags | CLICKCHAIN_REDIRECTED) if(deployed && deployed.loc != src) deployed.forceMove(src) undeploy() //? click redirection -/obj/item/switchtool/ranged_interaction_chain(atom/target, mob/user, clickchain_flags, params) +/obj/item/switchtool/ranged_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) if(!deployed) return ..() - . = deployed.ranged_interaction_chain(target, user, clickchain_flags | CLICKCHAIN_REDIRECTED, params) + . = deployed.ranged_interaction_chain(clickchain, clickchain_flags | CLICKCHAIN_REDIRECTED) if(deployed.loc != src) deployed.forceMove(src) undeploy() diff --git a/code/game/objects/items/weapons/RPD.dm b/code/game/objects/items/weapons/RPD.dm index 15dfa207739..42494001023 100644 --- a/code/game/objects/items/weapons/RPD.dm +++ b/code/game/objects/items/weapons/RPD.dm @@ -343,7 +343,7 @@ playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) /obj/item/pipe_dispenser/proc/do_wrench(var/atom/target, mob/user) - tool.melee_interaction_chain(target, user, CLICKCHAIN_HAS_PROXIMITY) + tool.lazy_melee_interaction_chain(target, user, CLICKCHAIN_HAS_PROXIMITY) /obj/item/pipe_dispenser/proc/mouse_wheeled(mob/user, atom/A, delta_x, delta_y, params) SIGNAL_HANDLER diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index 549fab40588..c697f5f8ac5 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -63,7 +63,7 @@ var/obj/item/I = user.get_inactive_held_item() if(istype(I, /obj/item/material/knife/machete/hatchet/unathiknife)) hits ++ - I.melee_interaction_chain(target, user, CLICKCHAIN_REDIRECTED, params) + I.lazy_melee_interaction_chain(target, user, CLICKCHAIN_REDIRECTED, params) ..() /obj/item/material/knife/machete/hatchet/unathiknife/afterattack(atom/target, mob/user, clickchain_flags, list/params) diff --git a/code/game/objects/items/weapons/nullrod.dm b/code/game/objects/items/weapons/nullrod.dm index 515cd27cb20..a2b7ede8081 100644 --- a/code/game/objects/items/weapons/nullrod.dm +++ b/code/game/objects/items/weapons/nullrod.dm @@ -69,7 +69,7 @@ nullrod_icons = sortList(nullrod_icons) - var/choice = show_radial_menu(L, src , nullrod_icons, custom_check = CALLBACK(src, PROC_REF(check_menu), L), radius = 42, require_near = TRUE) + var/choice = show_radial_menu(L, inv_inside ? get_turf(L) : src, nullrod_icons, custom_check = CALLBACK(src, PROC_REF(check_menu), L), radius = 42, require_near = TRUE) if(!choice || !check_menu(L)) return diff --git a/code/game/objects/obj-defense.dm b/code/game/objects/obj-defense.dm index c369a6d1604..7755571c33f 100644 --- a/code/game/objects/obj-defense.dm +++ b/code/game/objects/obj-defense.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 silicons *// +//* Copyright (c) 2025 Citadel Station Developers *// /obj/is_melee_targetable(datum/event_args/actor/clickchain/clickchain, clickchain_flags) return obj_flags & OBJ_MELEE_TARGETABLE @@ -23,11 +23,14 @@ // todo: /atom/movable/proc/throw_impact_attack(atom/target) if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) return + var/inflicted_damage if(isitem(AM)) var/obj/item/I = AM - inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) + inflicted_damage = inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) else - inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) + inflicted_damage = inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) + if(inflicted_damage) + playsound(src, hitsound_throwhit(AM), 75) // if we got destroyed if(QDELETED(src) && (obj_flags & OBJ_ALLOW_THROW_THROUGH)) . |= COMPONENT_THROW_HIT_PIERCE @@ -44,10 +47,18 @@ return return ..() -/obj/hitsound_throwhit(obj/item/I) +/obj/hitsound_throwhit(atom/movable/impacting) if(!isnull(material_primary)) var/datum/prototype/material/primary = get_primary_material() - . = I.damage_type == DAMAGE_TYPE_BURN? primary.sound_melee_burn : primary.sound_melee_brute + + var/resolved_damage_type + if(isitem(impacting)) + var/obj/item/casted_item = impacting + resolved_damage_type = casted_item.damage_type + else + resolved_damage_type = DAMAGE_TYPE_BRUTE + + . = resolved_damage_type == DAMAGE_TYPE_BURN? primary.sound_melee_burn : primary.sound_melee_brute if(!isnull(.)) return return ..() diff --git a/code/game/objects/obj.dm b/code/game/objects/obj.dm index 7b987d013c4..1a01782061a 100644 --- a/code/game/objects/obj.dm +++ b/code/game/objects/obj.dm @@ -247,7 +247,7 @@ material_constraints = get_typelist(material_constraints) else material_constraints = typelist(NAMEOF(src, material_constraints), material_constraints) - + // initialize material parts system if(material_parts != MATERIAL_DEFAULT_DISABLED) // process material parts only if it wasn't set already @@ -453,6 +453,12 @@ //* Climbing *// +/obj/alt_clicked_on(mob/user, location, control, list/params) + if(obj_storage?.allow_open_via_alt_click && user.Reachability(src)) + obj_storage.auto_handle_interacted_open(new /datum/event_args/actor(user)) + return TRUE + return ..() + /obj/MouseDroppedOn(atom/dropping, mob/user, proximity, params) if(drag_drop_climb_interaction(user, dropping)) return CLICKCHAIN_DO_NOT_PROPAGATE diff --git a/code/game/objects/structures/crates_lockers/__closet.dm b/code/game/objects/structures/crates_lockers/__closet.dm index f55b87ed0bc..6f632e39092 100644 --- a/code/game/objects/structures/crates_lockers/__closet.dm +++ b/code/game/objects/structures/crates_lockers/__closet.dm @@ -424,10 +424,16 @@ else toggle(user) -/obj/structure/closet/AltClick() - ..() +/obj/structure/closet/on_alt_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) + . = ..() + if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) + return if(secure) verb_togglelock() + return . | CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE + +/obj/structure/closet/should_list_turf_on_alt_click(mob/user) + return FALSE /obj/structure/closet/verb/verb_togglelock() set src in oview(1) // One square distance diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 95222e64e01..5a00fe85d13 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -144,12 +144,13 @@ desc = "A secure crate." name = "Secure crate" icon_state = "securecrate" + broken = FALSE + locked = TRUE + secure = TRUE var/redlight = "securecrater" var/greenlight = "securecrateg" var/sparks = "securecratesparks" var/emag = "securecrateemag" - broken = 0 - locked = 1 //closet_appearance = /singleton/closet_appearance/crate/secure var/tamper_proof = 0 diff --git a/code/game/objects/systems/storage/storage.dm b/code/game/objects/systems/storage/storage.dm index 50b9311f2dc..c8e8bcad4b5 100644 --- a/code/game/objects/systems/storage/storage.dm +++ b/code/game/objects/systems/storage/storage.dm @@ -105,6 +105,8 @@ /// allow opening when clicking from other hand var/allow_open_via_offhand_click = TRUE /// allow opening via alt click + var/allow_open_via_alt_click = TRUE + /// allow opening via context menu click var/allow_open_via_context_click = TRUE /// allow opening when clicking from hand if this is equipped var/allow_open_via_equipped_click = TRUE diff --git a/code/game/rendering/actor_huds/huds/inventory-screen_object.dm b/code/game/rendering/actor_huds/huds/inventory-screen_object.dm index 27a190e59c5..24474c4c565 100644 --- a/code/game/rendering/actor_huds/huds/inventory-screen_object.dm +++ b/code/game/rendering/actor_huds/huds/inventory-screen_object.dm @@ -111,7 +111,7 @@ var/obj/item/in_slot = user.item_by_slot_id(inventory_slot_id) if(with_item) if(in_slot) - with_item.melee_interaction_chain(in_slot, user, NONE, list()) + with_item.lazy_melee_interaction_chain(in_slot, user, NONE, list()) else user.equip_to_slot_if_possible(with_item, inventory_slot_id, NONE, user) else @@ -192,7 +192,7 @@ /atom/movable/screen/actor_hud/inventory/swap_hand name = "swap active hand" icon_state = "hand-swap" - + /atom/movable/screen/actor_hud/inventory/swap_hand/Initialize(mapload, datum/inventory/host, hand_count) . = ..() screen_loc = SCREEN_LOC_MOB_HUD_INVENTORY_HAND_SWAP(hand_count) diff --git a/code/game/rendering/legacy/hud_object.dm b/code/game/rendering/legacy/hud_object.dm index 121a8e11266..391bd2d71a0 100644 --- a/code/game/rendering/legacy/hud_object.dm +++ b/code/game/rendering/legacy/hud_object.dm @@ -1,3 +1,4 @@ +// TODO: reconcile this with screen/actor_hud type? /atom/movable/screen/hud /atom/movable/screen/hud/Initialize(mapload, datum/hud/master) @@ -13,20 +14,20 @@ alpha = hud_legacy.ui_alpha /atom/movable/screen/hud/Click(location, control, params) - SEND_SIGNAL(src, COMSIG_CLICK, location, control, params) + // TODO: add signals to the click abstraction procs on this. + var/signal_returns = SEND_SIGNAL(src, COMSIG_ATOM_CLICK, usr, location, control, params) + if(signal_returns & RAISE_ATOM_CLICK_DROP) + return clicked(usr) -/atom/movable/screen/hud/ShiftClick(mob/user) - SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) - shift_clicked(usr) +/atom/movable/screen/hud/shift_clicked_on(mob/user, location, control, list/params) + return shift_clicked(usr) -/atom/movable/screen/hud/AltClick(mob/user) - SEND_SIGNAL(src, COMSIG_CLICK_ALT, user) - alt_clicked(usr) +/atom/movable/screen/hud/alt_clicked_on(mob/user, location, control, list/params) + return alt_clicked(usr) -/atom/movable/screen/hud/CtrlClick(mob/user) - SEND_SIGNAL(src, COMSIG_CLICK_CTRL, user) - ctrl_clicked(usr) +/atom/movable/screen/hud/ctrl_clicked_on(mob/user, location, control, list/params) + return ctrl_clicked(usr) //! Abstracts interface actions to these 4. Middle mouse/Double click not included purposefully for they are awful. diff --git a/code/game/turfs/simulated/floor/floor-construction.dm b/code/game/turfs/simulated/floor/floor-construction.dm index d0a37d37eac..dd3cae57d72 100644 --- a/code/game/turfs/simulated/floor/floor-construction.dm +++ b/code/game/turfs/simulated/floor/floor-construction.dm @@ -18,12 +18,12 @@ if(istype(with_item, /obj/item/stack/material)) var/obj/item/stack/material/with_mat_stack = with_item possible = RSflooring.build_material_lookup[with_mat_stack.material.id] - has_amount = with_mat_stack.amount + has_amount = with_mat_stack.get_amount() using_stack = with_mat_stack else var/obj/item/stack/with_stack = with_item possible = RSflooring.build_item_lookup[with_stack.get_use_as_type()] - has_amount = with_stack.amount + has_amount = with_stack.get_amount() using_stack = with_stack if(!possible) return FALSE diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index f9ed822051c..1d3a96a0992 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -264,6 +264,8 @@ /turf/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() + if(.) + return //QOL feature, clicking on turf can toggle doors, unless pulling something if(!user.pulling) var/obj/machinery/door/airlock/AL = locate(/obj/machinery/door/airlock) in src.contents @@ -276,11 +278,11 @@ return TRUE if(!CHECK_MOBILITY(user, MOBILITY_CAN_MOVE) || user.restrained() || !(user.pulling)) - return 0 + return FALSE if(user.pulling.anchored || !isturf(user.pulling.loc)) - return 0 + return FALSE if(user.pulling.loc != user.loc && get_dist(user, user.pulling) > 1) - return 0 + return FALSE if(ismob(user.pulling)) var/mob/M = user.pulling var/atom/movable/t = M.pulling @@ -289,12 +291,13 @@ M.start_pulling(t, suppress_message = TRUE) else step(user.pulling, get_dir(user.pulling.loc, src)) - return 1 + return TRUE /turf/attack_ai(mob/user as mob) //this feels like a bad idea ultimately but this is the cheapest way to let cyborgs nudge things they're pulling around . = ..() if(Adjacent(user)) - attack_hand(user, new /datum/event_args/actor/clickchain(user)) + var/datum/event_args/actor/clickchain/clickchain = user.default_clickchain_event_args(src) + attack_hand(user, clickchain) /turf/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier) if(I.obj_storage?.allow_mass_gather && I.obj_storage.allow_mass_gather_via_click) diff --git a/code/modules/client/client_statpanel.dm b/code/modules/client/client_statpanel.dm index 98f178c3394..40a29dff1c2 100644 --- a/code/modules/client/client_statpanel.dm +++ b/code/modules/client/client_statpanel.dm @@ -249,7 +249,7 @@ tgui_stat.byond_stat_turf = null /client/proc/list_turf_check(turf/T) - return mob.TurfAdjacent(T) + return mob.snowflake_ai_vision_adjacency(T) /** * must return list(name, icon, ref). @@ -329,7 +329,7 @@ return // todo: admin token implementation var/clickparams = params["params"] - mob.ClickOn(A, clickparams, CLICKCHAIN_FROM_HREF) + mob.click_on(A, null, null, clickparams, CLICKCHAIN_FROM_HREF) return // todo: mousedrag event diff --git a/code/modules/detectivework/tools/scanner.dm b/code/modules/detectivework/tools/scanner.dm index 96f2fe133fa..95fe0ca9030 100644 --- a/code/modules/detectivework/tools/scanner.dm +++ b/code/modules/detectivework/tools/scanner.dm @@ -29,7 +29,7 @@ return else if(user.zone_sel.selecting == "r_hand" || user.zone_sel.selecting == "l_hand") var/obj/item/sample/print/P = new /obj/item/sample/print(user.loc) - P.melee_interaction_chain(target, user) + P.lazy_melee_interaction_chain(target, user) to_chat(user,"Done printing.") // to_chat(user, "[target]'s Fingerprints: [md5(target.dna.uni_identity)]") diff --git a/code/modules/examine/descriptions/engineering.dm b/code/modules/examine/descriptions/engineering.dm index 66c5dd9bfb3..b4670be26fb 100644 --- a/code/modules/examine/descriptions/engineering.dm +++ b/code/modules/examine/descriptions/engineering.dm @@ -14,16 +14,6 @@ cause a period of lag as the explosion is processed by the server, as well as irradiating the entire station and causing hallucinations to happen. \ Wearing radiation equipment will protect you from most of the delamination effects sans explosion." -/obj/machinery/power/apc - description_info = "An APC (Area Power Controller) regulates and supplies backup power for the area they are in. Their power channels are divided \ - out into 'environmental' (Items that manipulate airflow and temperature), 'lighting' (the lights), and 'equipment' (Everything else that consumes power). \ - Power consumption and backup power cell charge can be seen from the interface, further controls (turning a specific channel on, off or automatic, \ - toggling the APC's ability to charge the backup cell, or toggling power for the entire area via master breaker) first requires the interface to be unlocked \ - with an ID with Engineering access or by one of the station's robots or the artificial intelligence." - - description_antag = "This can be emagged to unlock it. It will cause the APC to have a blue error screen. \ - Wires can be pulsed remotely with a signaler attached to it. A powersink will also drain any APCs connected to the same wire the powersink is on." - /obj/item/inflatable description_info = "Inflate by using it in your hand. The inflatable barrier will inflate on your tile. To deflate it, use the 'deflate' verb. \ You can also inflate this on an adjacent tile by clicking the tile." diff --git a/code/modules/food/machinery/appliance/_appliance.dm b/code/modules/food/machinery/appliance/_appliance.dm index c723c8f36aa..e308719eb41 100644 --- a/code/modules/food/machinery/appliance/_appliance.dm +++ b/code/modules/food/machinery/appliance/_appliance.dm @@ -161,8 +161,9 @@ playsound(src, 'sound/machines/click.ogg', 40, 1) update_icon() -/obj/machinery/appliance/AICtrlClick(mob/user) - attempt_toggle_power(user) +// TODO: probably add this ..? and more control for ais? +// /obj/machinery/appliance/AICtrlClick(mob/user) +// attempt_toggle_power(user) /obj/machinery/appliance/proc/choose_output() set src in view() diff --git a/code/modules/gateway/stargate/stargate-away.dm b/code/modules/gateway/stargate/stargate-away.dm index 9a1e694cf28..0f1243db9e9 100644 --- a/code/modules/gateway/stargate/stargate-away.dm +++ b/code/modules/gateway/stargate/stargate-away.dm @@ -105,3 +105,10 @@ to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") calibrated = 1 return + +/obj/machinery/gateway/centeraway/attack_ghost(mob/user) + . = ..() + if(stationgate) + user.forceMove(stationgate.loc) + else + to_chat(user, "[src] has no destination.") diff --git a/code/modules/gateway/stargate/stargate-station.dm b/code/modules/gateway/stargate/stargate-station.dm index 08a036de1c3..942e2214d33 100644 --- a/code/modules/gateway/stargate/stargate-station.dm +++ b/code/modules/gateway/stargate/stargate-station.dm @@ -150,3 +150,10 @@ if(href_list[VV_HK_SETUP_GATEWAY] && check_rights(R_FUN)) admin_setup(usr) . = ..() + +/obj/machinery/gateway/centerstation/attack_ghost(mob/user) + . = ..() + if(awaygate) + user.forceMove(awaygate.loc) + else + to_chat(user, "[src] has no destination.") diff --git a/code/modules/hardsuits/modules/utility.dm b/code/modules/hardsuits/modules/utility.dm index 56c4876bed5..6c6c7f36425 100644 --- a/code/modules/hardsuits/modules/utility.dm +++ b/code/modules/hardsuits/modules/utility.dm @@ -121,7 +121,7 @@ if(istype(T) && !T.Adjacent(get_turf(src))) return 0 - device.melee_interaction_chain(target, holder.wearer, CLICKCHAIN_HAS_PROXIMITY) + device.lazy_melee_interaction_chain(target, holder.wearer, CLICKCHAIN_HAS_PROXIMITY) return 1 /obj/item/hardsuit_module/chem_dispenser diff --git a/code/modules/hardsuits/rig-input.dm b/code/modules/hardsuits/rig-input.dm new file mode 100644 index 00000000000..877374db0ce --- /dev/null +++ b/code/modules/hardsuits/rig-input.dm @@ -0,0 +1,31 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * Routed proc from either wearer or remote click. + * @return clickchain flags + */ +/obj/item/hardsuit/proc/handle_hardsuit_module_click(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/hardsuit_module/using_module) + if(!using_module) + using_module = selected_module + if(!using_module) + clickchain.chat_feedback( + SPAN_WARNING("None of your hardsuit's modules are currently the active module."), + target = src, + ) + return NONE + + if(clickchain.performer != wearer) + if(!ai_can_move_suit(clickchain.performer, check_user_module = TRUE)) + clickchain.chat_feedback( + SPAN_WARNING("The hardsuit rejects your input."), + target = src, + ) + return NONE + + //! legacy + clickchain.data[ACTOR_DATA_RIG_CLICK_LOG] = "[src]: [selected_module]" + selected_module.engage(clickchain.target) + clickchain.performer.setClickCooldownLegacy(wearer?.get_attack_speed_legacy() || 0.8 SECONDS) + //! end + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING diff --git a/code/modules/logging/api/clickchain.dm b/code/modules/logging/api/clickchain.dm new file mode 100644 index 00000000000..5e581805eab --- /dev/null +++ b/code/modules/logging/api/clickchain.dm @@ -0,0 +1,18 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/proc/log_clickchain(datum/event_args/actor/clickchain/clickchain, clickchain_flags) + var/list/data = list( + "performer" = key_name(clickchain.performer), + "target" = ismob(clickchain.target) ? key_name(clickchain.target) : "[clickchain.target || "null"]", + "intent" = clickchain.using_intent, + "hand" = clickchain.using_hand_index, + "zone" = clickchain.target_zone, + "data" = clickchain.data, + "params" = clickchain.click_params, + "flags" = clickchain_flags, + ) + if(clickchain.performer != clickchain.initiator) + data["initiator"] = key_name(clickchain.initiator) + WRITE_LOG(GLOB.click_log , json_encode(data)) + // global.event_logger.log__clickchain(clickchain, clickchain_flags) diff --git a/code/modules/materials/dynamics.dm b/code/modules/materials/dynamics.dm index b8a741d4a6e..b273d43dba6 100644 --- a/code/modules/materials/dynamics.dm +++ b/code/modules/materials/dynamics.dm @@ -4,6 +4,9 @@ //? Page has all balancing parameters + algorithms for dynamic attribute computations for things like armor ?// //? Prefix subsystem procs with 'dynamic_', please! ?// +// TODO: redo pretty much this entire file, all the calculations are horrible +// lohikar was right i should've used a straight line man + //* Armor *// /** @@ -282,7 +285,7 @@ * * nullification_weight : weight of material nulli (0-1), should sum with all other weights to 1 * * initial_toolspeed : speed of the tool to begin with * * significance - a modifier that determines how well materials scale; less significant tools scale less. we assume baseline for most tools. - * + * * https://www.desmos.com/calculator/m4gtk3aabl * @return new_toolspeed */ diff --git a/code/modules/mob/grab.dm b/code/modules/mob/grab.dm index 172e3bf3ccc..c758bab9225 100644 --- a/code/modules/mob/grab.dm +++ b/code/modules/mob/grab.dm @@ -99,7 +99,7 @@ name = "grab" icon = 'icons/mob/screen1.dmi' icon_state = "reinforce" - item_flags = ITEM_ABSTRACT | ITEM_DROPDEL | ITEM_ENCUMBERS_WHILE_HELD + item_flags = ITEM_ABSTRACT | ITEM_DROPDEL | ITEM_ENCUMBERS_WHILE_HELD | ITEM_NO_BLUDGEON atom_flags = ATOM_ABSTRACT drop_sound = null pickup_sound = null @@ -285,7 +285,7 @@ H.apply_pressure(assailant, hit_zone) else inspect_organ(affecting, assailant, hit_zone) - + if(INTENT_DISARM) pin_down(affecting, assailant) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 342fe4a2e95..27480d2384e 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -101,7 +101,7 @@ to_chat(user, "Access denied.") return if(brainmob) - O.melee_interaction_chain(brainmob, user)//Oh noooeeeee + O.lazy_melee_interaction_chain(brainmob, user)//Oh noooeeeee return ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1110d9951ff..d5e2550bdfd 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -580,8 +580,7 @@ var/obj/item/I = locate(href_list["clickitem"]) if(get_dist(src, get_turf(I)) > 7) return - if(src.client) - src.ClickOn(I) + click_on(I, inject_clickchain_flags = CLICKCHAIN_FROM_HREF) if (href_list["flavor_change"]) if(usr != src) @@ -1639,8 +1638,3 @@ if(!isTaurTail(tail_style) || !(dir & (EAST|WEST))) return . += (size_multiplier * icon_scale_x) * ((dir & EAST)? 8 : -8) - -/mob/living/carbon/human/ClickOn(var/atom/A) - if(ab_handler?.process_click(src, A)) - return - ..() diff --git a/code/modules/mob/living/silicon/ai/ai-examine.dm b/code/modules/mob/living/silicon/ai/ai-examine.dm new file mode 100644 index 00000000000..6dfb8a40b31 --- /dev/null +++ b/code/modules/mob/living/silicon/ai/ai-examine.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/mob/living/silicon/ai/should_client_shift_click_examine(atom/entity) + if(!ismob(entity) && !(entity in view(usr?.client?.view || world.view, src))) + return FALSE + return ..() + +/mob/living/silicon/ai/allow_examine(atom/A) + return snowflake_ai_vision_adjacency(get_turf(A)) diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 0af8dacd731..446940565ee 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -337,7 +337,8 @@ if(initial(path.slot_flags) & SLOT_OCLOTHING) return /obj/item/clothing/suit -/mob/living/silicon/pai/AltClickOn(var/atom/A) +/mob/living/silicon/pai/alt_click_on(atom/target, location, control, list/params) + var/atom/A = target if((isobj(A) || ismob(A)) && in_range_of(src, A) && !istype(A, /obj/item/paicard) && !istype(A, /obj/effect/pai_hologram)) if(world.time > last_scanned_time + 600) last_scanned_time = world.time @@ -345,6 +346,8 @@ to_chat(src, "You scan the [A.name]") else to_chat(src, "You need to wait [((last_scanned_time+600) - world.time)/10] seconds to scan another object.") + return TRUE + return ..() /mob/living/silicon/pai/proc/scan_object(var/atom/A) var/icon/hologram_icon = render_hologram_icon(A, 210, TRUE, TRUE, "_pai") diff --git a/code/modules/mob/living/simple_animal/animals/spiderbot.dm b/code/modules/mob/living/simple_animal/animals/spiderbot.dm index 3b88c3ed183..074d586c5d3 100644 --- a/code/modules/mob/living/simple_animal/animals/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/animals/spiderbot.dm @@ -150,7 +150,7 @@ return 0 else - O.melee_interaction_chain(src, user, user.zone_sel.selecting) + O.lazy_melee_interaction_chain(src, user, user.zone_sel.selecting) /mob/living/simple_mob/spiderbot/emag_act(var/remaining_charges, var/mob/user) if (emagged) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm index b43b139a955..dc299594b2b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -71,7 +71,8 @@ . += "It doesn't look quite right..." return -/mob/living/simple_mob/vore/hostile/morph/ShiftClickOn(atom/movable/A) +/mob/living/simple_mob/vore/hostile/morph/shift_click_on(atom/target, location, control, list/params) + var/atom/A = target if(Adjacent(A)) if(morph_time <= world.time && !stat) if(A == src) @@ -81,8 +82,9 @@ assume(A) else to_chat(src, "Your chameleon skin is still repairing itself!") + return TRUE else - ..() + return ..() /mob/living/simple_mob/vore/hostile/morph/proc/assume(atom/movable/target) if(morphed) diff --git a/code/modules/mob/living/throwing.dm b/code/modules/mob/living/throwing.dm index ee46a2f797c..e7dd35a33a6 100644 --- a/code/modules/mob/living/throwing.dm +++ b/code/modules/mob/living/throwing.dm @@ -28,7 +28,7 @@ if(!I.throw_resolve_override(throwing, src)) // drop item if(is_in_inventory(I)) - if(!drop_item_to_ground(I)) + if(!drop_item_to_ground(I, INV_OP_SUPPRESS_SOUND)) to_chat(src, SPAN_WARNING("You fail to throw [I] at [target].")) return FALSE else diff --git a/code/modules/mob/mob-examine.dm b/code/modules/mob/mob-examine.dm new file mode 100644 index 00000000000..e9a7e4fc5d3 --- /dev/null +++ b/code/modules/mob/mob-examine.dm @@ -0,0 +1,11 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * Checks if we should examine on shift click + * * This is from ourselves, not from remote control. + * * This doesn't prevent examines, this just is a control / input thing. + * Context menus usually still allow examining. + */ +/mob/proc/should_client_shift_click_examine(atom/entity) + return TRUE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 8d25ec09e5c..e782803b54f 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1141,7 +1141,42 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) /mob/z_pass_out(atom/movable/AM, dir, turf/new_loc) return TRUE -//? Pixel Offsets +//* Dir *// + +/** + * Tries to turn to face an atom. + */ +/mob/proc/face_atom(var/atom/atom_to_face) + if(buckled || stat != CONSCIOUS || !atom_to_face || !x || !y || !atom_to_face.x || !atom_to_face.y) + return + if(!CHECK_MOBILITY(src, MOBILITY_CAN_MOVE)) + return + + var/dx = atom_to_face.x - x + var/dy = atom_to_face.y - y + if(!dx && !dy) // Wall items are graphically shifted but on the floor + if(atom_to_face.pixel_y > 16) + setDir(NORTH) + else if(atom_to_face.pixel_y < -16) + setDir(SOUTH) + else if(atom_to_face.pixel_x > 16) + setDir(EAST) + else if(atom_to_face.pixel_x < -16) + setDir(WEST) + return + + if(abs(dx) < abs(dy)) + if(dy > 0) + setDir(NORTH) + else + setDir(SOUTH) + else + if(dx > 0) + setDir(EAST) + else + setDir(WEST) + +//* Pixel Offsets *// /mob/proc/get_buckled_pixel_x_offset() if(!buckled) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 14b89cf9613..beaa65d1c52 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -40,6 +40,12 @@ /// How are we intending to act? Help / harm / etc. var/a_intent = INTENT_HELP + //* Input*// + /// next time we should allow a click being ingested into the click-chain handling sequence. + /// * This is effectively only from our client. Remote control should directly call clickchain + /// handlers, instead of 'click_on'. + var/next_click + //* Perspective & Vision *// /// using perspective - if none, it'll be self - when client logs out, if using_perspective has reset_on_logout, this'll be unset. var/datum/perspective/using_perspective @@ -410,10 +416,6 @@ /// a singular thing that can intercept keyboard inputs var/datum/key_intercept - //Moved from code\game\click\click.dm - // 1 decisecond click delay (above and beyond mob/next_move) - var/next_click = 0 - //Moved from code\game\rendering\legacy\alert.dm var/list/alerts = list() // contains /atom/movable/screen/alert only // On /mob so clientless mobs will throw alerts properly diff --git a/code/modules/nifsoft/nif_tgui.dm b/code/modules/nifsoft/nif_tgui.dm index 7fbb9231c3f..6b31bed9546 100644 --- a/code/modules/nifsoft/nif_tgui.dm +++ b/code/modules/nifsoft/nif_tgui.dm @@ -43,17 +43,16 @@ var/mob/owner = parent if(screen_icon) owner?.client?.screen -= screen_icon - UnregisterSignal(screen_icon, COMSIG_CLICK) + UnregisterSignal(screen_icon, COMSIG_ATOM_CLICK) QDEL_NULL(screen_icon) if(ishuman(parent)) remove_verb(owner, /mob/living/carbon/human/proc/nif_menu) - /datum/component/nif_menu/proc/create_mob_button(mob/user) var/datum/hud/HUD = user.hud_used if(!screen_icon) screen_icon = new() - RegisterSignal(screen_icon, COMSIG_CLICK, PROC_REF(nif_menu_click)) + RegisterSignal(screen_icon, COMSIG_ATOM_CLICK, PROC_REF(nif_menu_click)) screen_icon.icon = HUD.ui_style screen_icon.color = HUD.ui_color screen_icon.alpha = HUD.ui_alpha @@ -62,7 +61,7 @@ add_verb(user, /mob/living/carbon/human/proc/nif_menu) -/datum/component/nif_menu/proc/nif_menu_click(mob/user) +/datum/component/nif_menu/proc/nif_menu_click(datum/source, mob/user, location, control, params) var/mob/living/carbon/human/H = user if(istype(H) && H.nif) INVOKE_ASYNC(H.nif, PROC_REF(ui_interact), user) diff --git a/code/modules/photography/legacy_silicon.dm b/code/modules/photography/legacy_silicon.dm index 7d841387f07..b2da72c2789 100644 --- a/code/modules/photography/legacy_silicon.dm +++ b/code/modules/photography/legacy_silicon.dm @@ -70,7 +70,7 @@ /obj/item/camera/siliconcam/ai_camera/can_capture_turf(turf/T, mob/user) var/mob/living/silicon/ai = user - return ai.TurfAdjacent(T) + return ai.snowflake_ai_vision_adjacency(T) /obj/item/camera/siliconcam/proc/toggle_camera_mode() if(in_camera_mode) diff --git a/code/modules/power/apc/apc-ai.dm b/code/modules/power/apc/apc-ai.dm new file mode 100644 index 00000000000..3c1a1cb45c5 --- /dev/null +++ b/code/modules/power/apc/apc-ai.dm @@ -0,0 +1,11 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/obj/machinery/power/apc/on_silicon_control_ctrl_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + toggle_breaker() + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "apc [COORD(src)] [REF(src)] breaker [operating ? "on" : "off"]" + actor.chat_feedback( + SPAN_WARNING("You toggle [src]'s breaker [operating ? "on" : "off"]"), + target = src, + ) + return TRUE diff --git a/code/modules/power/apc.dm b/code/modules/power/apc/apc.dm similarity index 98% rename from code/modules/power/apc.dm rename to code/modules/power/apc/apc.dm index b9ee10e3cba..0b0b4b48c59 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc/apc.dm @@ -119,6 +119,15 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/power/apc, 22) use_power = USE_POWER_OFF req_access = list(ACCESS_ENGINEERING_ENGINE) armor_type = /datum/armor/object/medium + description_info = "An APC (Area Power Controller) regulates and supplies backup power for the area they are in. Their power channels are divided \ + out into 'environmental' (Items that manipulate airflow and temperature), 'lighting' (the lights), and 'equipment' (Everything else that consumes power). \ + Power consumption and backup power cell charge can be seen from the interface, further controls (turning a specific channel on, off or automatic, \ + toggling the APC's ability to charge the backup cell, or toggling power for the entire area via master breaker) first requires the interface to be unlocked \ + with an ID with Engineering access or by one of the station's robots or the artificial intelligence." + + description_antag = "This can be emagged to unlock it. It will cause the APC to have a blue error screen. \ + Wires can be pulsed remotely with a signaler attached to it. A powersink will also drain any APCs connected to the same wire the powersink is on." + var/area/area var/areastring = null var/obj/item/cell/cell diff --git a/code/modules/power/lighting/light-ai.dm b/code/modules/power/lighting/light-ai.dm new file mode 100644 index 00000000000..95f782392aa --- /dev/null +++ b/code/modules/power/lighting/light-ai.dm @@ -0,0 +1,11 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/obj/machinery/light/on_silicon_control_alt_click(mob/living/silicon/user, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags) + flicker(1) + actor.data[ACTOR_DATA_SILICON_CONTROL_LOG] = "light [COORD(src)] [src] flickered" + actor.chat_feedback( + SPAN_NOTICE("You flicker the light."), + target = src, + ) + return TRUE diff --git a/code/modules/power/lighting/lighting.dm b/code/modules/power/lighting/lighting.dm index 4faafaefade..51e409eb8b1 100644 --- a/code/modules/power/lighting/lighting.dm +++ b/code/modules/power/lighting/lighting.dm @@ -862,10 +862,6 @@ var/global/list/light_type_cache = list() to_chat(user, "Emergency lights for this fixture have been [no_emergency ? "disabled" : "enabled"].") update(FALSE) -// ai alt click - Make light flicker. Very important for atmosphere. -/obj/machinery/light/AIAltClick(mob/user) - flicker(1) - /obj/machinery/light/flamp/attack_ai(mob/user) attack_hand() return diff --git a/code/modules/projectiles/guns/gun.dm b/code/modules/projectiles/guns/gun.dm index 18c49ad5786..b3eb3543c77 100644 --- a/code/modules/projectiles/guns/gun.dm +++ b/code/modules/projectiles/guns/gun.dm @@ -6,6 +6,8 @@ * todo: /obj/item/gun/projectile vs /obj/item/gun/launcher, * instead of have projectile be on /obj/item/gun * + * TODO: screentips - alt click is safety + * * ## Hotkey Priority * * The usable semantic hotkeys for guns are: Z, Spacebar, F, G. @@ -430,7 +432,7 @@ if(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) return - if(!user?.client?.get_preference_toggle(/datum/game_preference_toggle/game/help_intent_firing) && user.a_intent == INTENT_HELP) + if(user?.client?.get_preference_toggle(/datum/game_preference_toggle/game/help_intent_firing) && user.a_intent == INTENT_HELP) to_chat(user, SPAN_WARNING("You refrain from firing [src] because your intent is set to help!")) return @@ -739,10 +741,13 @@ //* Interaction *// -/obj/item/gun/CtrlClick(mob/user) +/obj/item/gun/on_alt_click_interaction_chain(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/active_item) . = ..() - if(user.is_holding(src)) - toggle_safety(user) + if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT) + return + if(clickchain.performer.is_holding(src)) + toggle_safety(clickchain.performer) + return . | CLICKCHAIN_DID_SOMETHING //* Rendering *// diff --git a/code/modules/vehicles/sealed/mecha/mecha-input.dm b/code/modules/vehicles/sealed/mecha/mecha-input.dm new file mode 100644 index 00000000000..8b9a09056bc --- /dev/null +++ b/code/modules/vehicles/sealed/mecha/mecha-input.dm @@ -0,0 +1,12 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * Routed proc from either wearer or remote click. + * @return clickchain flags + */ +/obj/vehicle/sealed/mecha/handle_vehicle_click(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/vehicle_module/using_module) + // TODO: eventually we'll want to start re-implementing click_action to be more modular, + // but for now we just route over there to the old code + click_action(clickchain.target, clickchain.performer, clickchain.click_params) + return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING diff --git a/code/modules/vehicles/sealed/mecha/modules/tools/powertool.dm b/code/modules/vehicles/sealed/mecha/modules/tools/powertool.dm index d4ab6874390..7a3549d7bbc 100644 --- a/code/modules/vehicles/sealed/mecha/modules/tools/powertool.dm +++ b/code/modules/vehicles/sealed/mecha/modules/tools/powertool.dm @@ -29,7 +29,7 @@ return FALSE if(isliving(target)) - my_tool.melee_interaction_chain(target, chassis.occupant_legacy, BP_TORSO) + my_tool.lazy_melee_interaction_chain(target, chassis.occupant_legacy, BP_TORSO) target.attackby(my_tool,chassis.occupant_legacy) diff --git a/code/modules/vehicles/vehicle-input.dm b/code/modules/vehicles/vehicle-input.dm new file mode 100644 index 00000000000..a006286a355 --- /dev/null +++ b/code/modules/vehicles/vehicle-input.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2025 Citadel Station Developers *// + +/** + * Routed proc from either wearer or remote click. + * @return clickchain flags + */ +/obj/vehicle/proc/handle_vehicle_click(datum/event_args/actor/clickchain/clickchain, clickchain_flags, obj/item/vehicle_module/using_module) + // TODO: for now, only mechas can handle this. + return clickchain_flags diff --git a/code/modules/ventcrawl/ventcrawl.dm b/code/modules/ventcrawl/ventcrawl.dm index b4e5a22b77a..162c4c9e9af 100644 --- a/code/modules/ventcrawl/ventcrawl.dm +++ b/code/modules/ventcrawl/ventcrawl.dm @@ -77,10 +77,11 @@ var/list/ventcrawl_machinery = list( return FALSE return TRUE -/mob/living/AltClickOn(var/atom/A) +/mob/living/alt_click_on(atom/target, location, control, list/params) + var/atom/A = target if(is_type_in_list(A,ventcrawl_machinery)) handle_ventcrawl(A) - return 1 + return TRUE return ..() /mob/proc/start_ventcrawl() diff --git a/code/modules/vore/eating/silicon_vr.dm b/code/modules/vore/eating/silicon_vr.dm index 4f3f5fdc131..f3409e398bd 100644 --- a/code/modules/vore/eating/silicon_vr.dm +++ b/code/modules/vore/eating/silicon_vr.dm @@ -33,8 +33,3 @@ return hologram.vore_someone(prey) - -/mob/living/AIShiftClick(mob/user) //Shift-click as AI overridden on mobs to examine. - if(user.client) - var/list/result = examine(user) - to_chat(user, "
[result.Join("\n")]
") diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index 83764adbfa1..0f0e00711f1 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -467,7 +467,8 @@ to_chat(user,"You can't do that in your state!") return TRUE - user.ClickOn(T) + // wow this is shit, refactor when + user.click_on(T, null, null, "", CLICKCHAIN_FROM_HREF) sleep(5) //Seems to exit too fast for the panel to update if(href_list["insidepick"]) diff --git a/code/unit_tests/combat.dm b/code/unit_tests/combat.dm index 570eb0fd1e0..96085c65f9e 100644 --- a/code/unit_tests/combat.dm +++ b/code/unit_tests/combat.dm @@ -63,7 +63,7 @@ attacker.put_in_active_hand(toolbox, forced = TRUE) attacker.set_combat_mode(TRUE) - toolbox.melee_interaction_chain(attacker, victim) + toolbox.lazy_melee_interaction_chain(attacker, victim) TEST_ASSERT(pre_attack_hit, "Pre-attack signal was not fired") TEST_ASSERT(attack_hit, "Attack signal was not fired") diff --git a/code/unit_tests/metabolizing.dm b/code/unit_tests/metabolizing.dm index 1096cb5f4fa..9b4968dfea5 100644 --- a/code/unit_tests/metabolizing.dm +++ b/code/unit_tests/metabolizing.dm @@ -25,7 +25,7 @@ // Give them enough meth to be consumed in 2 metabolizations pill.reagents.add_reagent(meth, initial(meth.metabolization_rate) * 1.9) - pill.melee_interaction_chain(user, user) + pill.lazy_melee_interaction_chain(user, user) user.Life() diff --git a/code/unit_tests/pills.dm b/code/unit_tests/pills.dm index 0bd82dc81d0..581f7ad116f 100644 --- a/code/unit_tests/pills.dm +++ b/code/unit_tests/pills.dm @@ -4,7 +4,7 @@ TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/iron), FALSE, "Human somehow has iron before taking pill") - pill.melee_interaction_chain(human, human) + pill.lazy_melee_interaction_chain(human, human) human.Life() TEST_ASSERT(human.has_reagent(/datum/reagent/iron), "Human doesn't have iron after taking pill") diff --git a/code/unit_tests/serving_tray.dm b/code/unit_tests/serving_tray.dm index 92a70dfcf25..c9945849978 100644 --- a/code/unit_tests/serving_tray.dm +++ b/code/unit_tests/serving_tray.dm @@ -41,7 +41,7 @@ human.equip_to_slot(test_tray, ITEM_SLOT_RPOCKET) TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_RPOCKET), "Serving tray failed to fit in the Right Pocket") - test_tray.melee_interaction_chain(human, human) + test_tray.lazy_melee_interaction_chain(human, human) TEST_ASSERT_EQUAL(test_tray.contents.len, 0, "The serving tray did not drop all items on hitting a human") diff --git a/code/unit_tests/stomach.dm b/code/unit_tests/stomach.dm index 6dcccac92ef..2d77b8f1da1 100644 --- a/code/unit_tests/stomach.dm +++ b/code/unit_tests/stomach.dm @@ -11,14 +11,14 @@ TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human somehow has ketchup before eating") - fooditem.melee_interaction_chain(human, human) + fooditem.lazy_melee_interaction_chain(human, human) TEST_ASSERT(belly.reagents.has_reagent(/datum/reagent/consumable/ketchup), "Stomach doesn't have ketchup after eating") TEST_ASSERT_EQUAL(human.reagents.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human body has ketchup after eating it should only be in the stomach") //Give them meth and let it kick in pill.reagents.add_reagent(meth, initial(meth.metabolization_rate) * 1.9) - pill.melee_interaction_chain(human, human) + pill.lazy_melee_interaction_chain(human, human) human.Life() TEST_ASSERT(human.reagents.has_reagent(meth), "Human body does not have meth after life tick") @@ -29,7 +29,7 @@ TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human has reagents after clearing") - fooditem.melee_interaction_chain(human, human) + fooditem.lazy_melee_interaction_chain(human, human) TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human has ketchup without a stomach")