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")