diff --git a/citadel.dme b/citadel.dme
index 010038d2f8ea..d4c30f09945e 100644
--- a/citadel.dme
+++ b/citadel.dme
@@ -43,6 +43,7 @@
#include "code\__DEFINES\damage_organs.dm"
#include "code\__DEFINES\directional.dm"
#include "code\__DEFINES\dna.dm"
+#include "code\__DEFINES\event_args.dm"
#include "code\__DEFINES\fonts.dm"
#include "code\__DEFINES\gamemode.dm"
#include "code\__DEFINES\holidays.dm"
@@ -161,12 +162,12 @@
#include "code\__DEFINES\dcs\signals\signals_global.dm"
#include "code\__DEFINES\dcs\signals\signals_object.dm"
#include "code\__DEFINES\dcs\signals\signals_storage.dm"
-#include "code\__DEFINES\dcs\signals\signals_tool_system.dm"
#include "code\__DEFINES\dcs\signals\signals_turf.dm"
#include "code\__DEFINES\dcs\signals\datums\signals_beam.dm"
#include "code\__DEFINES\dcs\signals\datums\signals_perspective.dm"
#include "code\__DEFINES\dcs\signals\elements\conflict.dm"
#include "code\__DEFINES\dcs\signals\items\signals_inducer.dm"
+#include "code\__DEFINES\dcs\signals\signals_atom\context_system.dm"
#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_buckling.dm"
@@ -180,6 +181,7 @@
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_throwing.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_visuals.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm"
+#include "code\__DEFINES\dcs\signals\signals_atom\tool_system.dm"
#include "code\__DEFINES\dcs\signals\signals_item\signals_item_economy.dm"
#include "code\__DEFINES\dcs\signals\signals_item\signals_item_inventory.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_appearance.dm"
@@ -744,6 +746,9 @@
#include "code\datums\elements\clothing\dynamic_recolor.dm"
#include "code\datums\elements\items\darksight_granter.dm"
#include "code\datums\elements\items\hud_granter.dm"
+#include "code\datums\event_args\_event_args.dm"
+#include "code\datums\event_args\actor.dm"
+#include "code\datums\event_args\clickchain.dm"
#include "code\datums\helper_datums\construction_datum.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
@@ -937,6 +942,7 @@
#include "code\game\click\adjacency_legacy.dm"
#include "code\game\click\ai.dm"
#include "code\game\click\click.dm"
+#include "code\game\click\context.dm"
#include "code\game\click\cyborg.dm"
#include "code\game\click\drag_drop.dm"
#include "code\game\click\item_attack.dm"
@@ -1869,6 +1875,8 @@
#include "code\game\objects\structures\stool_bed_chair_nest\chairs_vr.dm"
#include "code\game\objects\structures\stool_bed_chair_nest\stools.dm"
#include "code\game\objects\structures\stool_bed_chair_nest\wheelchair.dm"
+#include "code\game\objects\systems\_system.dm"
+#include "code\game\objects\systems\cell_slot.dm"
#include "code\game\rendering\client.dm"
#include "code\game\rendering\mob.dm"
#include "code\game\rendering\screen.dm"
@@ -4686,6 +4694,7 @@
#include "code\modules\tools\_tool_system.dm"
#include "code\modules\tools\items.dm"
#include "code\modules\tools\multitool.dm"
+#include "code\modules\tools\visuals.dm"
#include "code\modules\tools\wrappers.dm"
#include "code\modules\tools\z_legacy.dm"
#include "code\modules\tooltip\tooltip.dm"
diff --git a/code/__DEFINES/admin/admin.dm b/code/__DEFINES/admin/admin.dm
index 2dc222d92e88..a22547f2273d 100644
--- a/code/__DEFINES/admin/admin.dm
+++ b/code/__DEFINES/admin/admin.dm
@@ -60,6 +60,8 @@
#define ADMIN_LOOKUP(user) ("[key_name_admin(user)][ADMIN_QUE(user)]")
#define ADMIN_LOOKUPFLW(user) ("[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]")
#define COORD(src) ("[src ? src.Admin_Coordinates_Readable() : "nonexistent location"]")
+// todo: this should be made faster/better, and support stuff like inventory / storage awareness
+#define AUDIT_COORD(src) ("[src ? src.Admin_Coordinates_Readable() : "nonexistent location"]")
#define AREACOORD(src) ("[src ? src.Admin_Coordinates_Readable(TRUE) : "nonexistent location"]")
#define ADMIN_COORDJMP(src) ("[src ? src.Admin_Coordinates_Readable(FALSE, TRUE) : "nonexistent location"]")
#define ADMIN_VERBOSEJMP(src) ("[src ? src.Admin_Coordinates_Readable(TRUE, TRUE) : "nonexistent location"]")
diff --git a/code/__DEFINES/dcs/signals/signals_atom/context_system.dm b/code/__DEFINES/dcs/signals/signals_atom/context_system.dm
new file mode 100644
index 000000000000..e765fd72e577
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_atom/context_system.dm
@@ -0,0 +1,20 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
+/// from base of /atom/proc/context_query: (list/options, datum/event_args/actor/e_args)
+/// options list is the same format as /atom/proc/context_query, insert directly to it.
+#define COMSIG_ATOM_CONTEXT_QUERY "atom_context_query"
+/// from base of /atom/proc/context_act: (key, datum/event_args/actor/e_args)
+#define COMSIG_ATOM_CONTEXT_ACT "atom_context_act"
+ #define RAISE_ATOM_CONTEXT_ACT_HANDLED (1<<0)
+
+/// create context
+/// * name: name
+/// * image: context menu image
+/// * distance: distance where this is valid; much be reachable or actable; null = requires adjacency or adjacency-equivalence
+/// * mobility: mobility flags required
+#define ATOM_CONTEXT_TUPLE(name, image, distance, mobility) list(name, image, distance, mobility)
+
+/// when used as distance, telekinetics and other things do not count as adjacency
+// todo: currently not implemented
+#define ATOM_CONTEXT_FORCE_PHYSICAL_ADJACENCY null
diff --git a/code/__DEFINES/dcs/signals/signals_atom/tool_system.dm b/code/__DEFINES/dcs/signals/signals_atom/tool_system.dm
new file mode 100644
index 000000000000..5d9c7ab9c1de
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_atom/tool_system.dm
@@ -0,0 +1,10 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
+/// from base of _tool_act: (I, user, function, flags, hint) where I = item, e_args = clickchain data, function = tool behaviour, flags = tool operation flags, hint = set by dynamic tool system
+/// return CLICKCHAIN_COMPONENT_SIGNAL_HANDLED to abort normal tool_act handling.
+#define COMSIG_ATOM_TOOL_ACT "tool_act"
+/// from base of dynamic_tool_query: (I, datum/event_args/actor/clickchain/e_args, functions) where I = item, e_args = clickchain data.
+/// inject by merging into functions
+/// remember to use merge_double_lazy_assoc_list() to merge function lists!
+#define COMSIG_ATOM_TOOL_QUERY "tool_functions"
diff --git a/code/__DEFINES/dcs/signals/signals_tool_system.dm b/code/__DEFINES/dcs/signals/signals_tool_system.dm
deleted file mode 100644
index 24163dae2d1b..000000000000
--- a/code/__DEFINES/dcs/signals/signals_tool_system.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/// from base of _tool_act: (I, user, function, flags, hint) where I = item, user = user, function = tool behaviour, flags = tool operation flags, hint = set by dynamic tool system
-#define COMSIG_ATOM_TOOL_ACT "tool_act"
diff --git a/code/__DEFINES/event_args.dm b/code/__DEFINES/event_args.dm
new file mode 100644
index 000000000000..cfbbc03c37c3
--- /dev/null
+++ b/code/__DEFINES/event_args.dm
@@ -0,0 +1,7 @@
+//? for /datum/event_args/actor
+
+#define WRAP_MOB_TO_ACTOR_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor(VARNAME) : VARNAME
+
+//? for /datum/event_args/actor/clickchain
+
+#define WRAP_MOB_TO_CLICKCHAIN_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor/clickchain(VARNAME) : VARNAME
diff --git a/code/__DEFINES/procs/clickcode.dm b/code/__DEFINES/procs/clickcode.dm
index f8cc82c644c7..9e38b340ea9c 100644
--- a/code/__DEFINES/procs/clickcode.dm
+++ b/code/__DEFINES/procs/clickcode.dm
@@ -24,6 +24,7 @@
/// person can reach us normally
#define CLICKCHAIN_HAS_PROXIMITY (1<<1)
/// in tool act - used to check if we should do default proximity checks when none are specified
+/// this is added to clickchain flags by tool_attack_chain.
#define CLICKCHAIN_TOOL_ACT (1<<2)
/// redirected by something - like when a switchtool to another item
#define CLICKCHAIN_REDIRECTED (1<<3)
@@ -33,7 +34,17 @@
#define CLICKCHAIN_DID_SOMETHING (1<<5)
/// completely block attacking (notably, attack_mob, attack_obj) from happening by halting standard_melee_attack.
#define CLICKCHAIN_DO_NOT_ATTACK (1<<6)
+/// intercepted by component
+#define CLICKCHAIN_COMPONENT_SIGNAL_HANDLED (1<<7)
//! Reachability Depths - checked from level of DirectAccess and turf adjacency.
/// default reachability depth
#define DEFAULT_REACHABILITY_DEPTH 3 // enough to reach into pill bottles in box in backpack
+
+//! Reachability
+/// can't reach - this *must* be a fals-y value.
+#define REACH_FAILED 0
+/// can physically reach normally
+#define REACH_PHYSICAL 1
+/// can reach with something like telekinesis
+#define REACH_INDIRECT 2
diff --git a/code/__DEFINES/tools/functionality.dm b/code/__DEFINES/tools/functionality.dm
index 1f6a4e3cc896..989989538382 100644
--- a/code/__DEFINES/tools/functionality.dm
+++ b/code/__DEFINES/tools/functionality.dm
@@ -38,16 +38,6 @@ GLOBAL_REAL_VAR(_dyntool_image_states) = list(
//* None yet! Waiting on skill-system design.
-//? Tool hints - make these human readable!
-
-#define TOOL_HINT_UNSCREWING_WINDOW_FRAME "unsecure frame"
-#define TOOL_HINT_SCREWING_WINDOW_FRAME "secure frame"
-#define TOOL_HINT_UNSCREWING_WINDOW_PANE "unfasten pane"
-#define TOOL_HINT_SCREWING_WINDOW_PANE "fasten pane"
-#define TOOL_HINT_CROWBAR_WINDOW_IN "pane in"
-#define TOOL_HINT_CROWBAR_WINDOW_OUT "pane out"
-#define TOOL_HINT_WRENCH_WINDOW_DISASSEMBLY "dismantle"
-
//? tool_locked var
/// unlocked - use dynamic tool system
diff --git a/code/__HELPERS/do_after.dm b/code/__HELPERS/do_after.dm
index 32dc1a7da37a..a0365eeb132a 100644
--- a/code/__HELPERS/do_after.dm
+++ b/code/__HELPERS/do_after.dm
@@ -68,8 +68,9 @@
* * max_distance - if not null, the user is required to be get_dist() <= max_distance from target.
* * additional_checks - a callback that allows for custom checks. this is invoked with our args directly, allowing us to modify delay.
* * progress_anchor - override progressbar anchor location
+ * * progress_instance - override progressbar instance
*/
-/proc/do_after(mob/user, delay, atom/target, flags, mobility_flags = MOBILITY_CAN_USE, max_distance, datum/callback/additional_checks, atom/progress_anchor)
+/proc/do_after(mob/user, delay, atom/target, flags, mobility_flags = MOBILITY_CAN_USE, max_distance, datum/callback/additional_checks, atom/progress_anchor, datum/progressbar/progress_instance)
if(isnull(user))
return FALSE
if(!delay)
@@ -97,10 +98,10 @@
var/obj/item/active_held_item = user.get_active_held_item()
- var/datum/progressbar/progress
+ var/datum/progressbar/progress = progress_instance
var/original_delay = delay
var/delay_factor = 1
- if(!(flags & DO_AFTER_NO_PROGRESS) && (!isnull(progress_anchor || !isnull(target))))
+ if(isnull(progress) && !(flags & DO_AFTER_NO_PROGRESS) && (!isnull(progress_anchor || !isnull(target))))
progress = new(user, delay, progress_anchor || target)
var/start_time = world.time
diff --git a/code/datums/event_args/_event_args.dm b/code/datums/event_args/_event_args.dm
new file mode 100644
index 000000000000..d2569f99fff3
--- /dev/null
+++ b/code/datums/event_args/_event_args.dm
@@ -0,0 +1,5 @@
+/**
+ * datums used to hold data for procs without having to pass too many args around
+ */
+/datum/event_args
+ abstract_type = /datum/event_args
diff --git a/code/datums/event_args/actor.dm b/code/datums/event_args/actor.dm
new file mode 100644
index 000000000000..c5186ef2ed96
--- /dev/null
+++ b/code/datums/event_args/actor.dm
@@ -0,0 +1,57 @@
+/**
+ * used to hold semantic data about an action being done by an actor vs initiator (controller)
+ */
+/datum/event_args/actor
+ /// the mob performing the action
+ var/mob/performer
+ /// the mob actually initiating the action, e.g. a remote controller.
+ var/mob/initiator
+
+/datum/event_args/actor/New(mob/performer, mob/initiator)
+ src.performer = performer
+ src.initiator = isnull(initiator)? performer : initiator
+
+/datum/event_args/actor/proc/chat_feedback(msg, atom/target)
+ performer.action_feedback(msg, target)
+ if(performer != initiator)
+ initiator.action_feedback(msg, target)
+
+/datum/event_args/actor/proc/bubble_feedback(msg, atom/target)
+ performer.bubble_action_feedback(msg, target)
+ if(performer != initiator)
+ initiator.bubble_action_feedback(msg, target)
+
+/**
+ * It is highly recommended to use named parameters with this.
+ */
+/datum/event_args/actor/proc/visible_feedback(atom/target, range, visible, audible, visible_self, otherwise_self, visible_them, otherwise_them)
+ performer.visible_action_feedback(
+ target = target,
+ initiator = initiator,
+ hard_range = range,
+ visible_hard = visible,
+ audible_hard = audible,
+ visible_self = visible_self,
+ otherwise_self = otherwise_self,
+ visible_them = visible_them,
+ otherwise_them = otherwise_them,
+ )
+
+/**
+ * It is highly recommended to use named parameters with this.
+ */
+/datum/event_args/actor/proc/visible_dual_feedback(atom/target, range_hard, range_soft, visible_hard, visible_soft, audible_hard, audible_soft, visible_self, otherwise_self, visible_them, otherwise_them)
+ performer.visible_action_feedback(
+ target = target,
+ initiator = initiator,
+ hard_range = range_hard,
+ soft_range = range_soft,
+ visible_hard = visible_hard,
+ visible_soft = visible_soft,
+ audible_hard = audible_hard,
+ audible_soft = audible_soft,
+ visible_self = visible_self,
+ otherwise_self = otherwise_self,
+ visible_them = visible_them,
+ otherwise_them = otherwise_them,
+ )
diff --git a/code/datums/event_args/clickchain.dm b/code/datums/event_args/clickchain.dm
new file mode 100644
index 000000000000..ce2039376a3f
--- /dev/null
+++ b/code/datums/event_args/clickchain.dm
@@ -0,0 +1,13 @@
+/**
+ * used to hold data about a click action
+ */
+/datum/event_args/actor/clickchain
+ /// a_intent
+ var/intent
+ /// click params
+ var/list/params
+
+/datum/event_args/actor/clickchain/New(mob/performer, mob/initiator, intent, list/params)
+ ..()
+ src.intent = isnull(intent)? performer.a_intent : intent
+ src.params = isnull(params)? list() : params
diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm
index 8813b1be798f..ee4a71eee143 100644
--- a/code/datums/progressbar.dm
+++ b/code/datums/progressbar.dm
@@ -1,3 +1,7 @@
+/proc/create_actor_progress_bar(datum/event_args/actor/e_args, goal_number, atom/target)
+ // todo: also show initiator the progress bar
+ return new /datum/progressbar(e_args.performer, goal_number, target)
+
/datum/progressbar
var/goal = 1
var/image/bar
diff --git a/code/game/atoms/action_feedback.dm b/code/game/atoms/action_feedback.dm
index e3e2d4065a00..096a003c3c15 100644
--- a/code/game/atoms/action_feedback.dm
+++ b/code/game/atoms/action_feedback.dm
@@ -7,6 +7,7 @@
*
* @params
* * target - target atom
+ * * initiator - additional thing to show a message to as self
* * hard_range - how far to display hard message; defaults to MESSAGE_RANGE_COMBAT_LOUD. if doesn't exist we use soft.
* * soft_range - how far to display soft message; defaults to MESSAGE_RANGE_COMBAT_LOUD. overrides hard range if smaller.
* * visible_hard - hard message. if doesn't exist we use soft message.
@@ -14,11 +15,11 @@
* * visible_soft - soft message.
* * audible_soft - what blind people hear when inside soft range (overridden by self and them if specified)
* * visible_self - what we see
- * * audible_self - override if self is blind. if null, defaults to 'self.
+ * * otherwise_self - override if self is blind. if null, defaults to 'self.
* * visible_them - what the target see
- * * audible_them - what the target sees if they are blind. if null, defaults to 'them'.
+ * * otherwise_them - what the target sees if they are blind. if null, defaults to 'them'.
*/
-/atom/proc/visible_action_feedback(atom/target, hard_range = MESSAGE_RANGE_COMBAT_LOUD, soft_range, visible_hard, audible_hard, audible_soft, visible_soft, visible_self, audible_self, visible_them, audible_them)
+/atom/proc/visible_action_feedback(atom/target, atom/initiator, hard_range = MESSAGE_RANGE_COMBAT_LOUD, soft_range, visible_hard, audible_hard, audible_soft, visible_soft, visible_self, otherwise_self, visible_them, otherwise_them)
var/list/viewing
var/viewing_range = max(soft_range, hard_range)
//! LEGACY
@@ -30,6 +31,9 @@
//! end
var/hard_visible = visible_hard || visible_soft
var/hard_audible = audible_hard || audible_soft
+ visible_self = visible_self || otherwise_self
+ visible_them = visible_them || otherwise_them
+ // todo: all of this needs rewritten oh my god
for(var/atom/movable/AM as anything in viewing)
if(get_dist(AM, src) <= hard_range)
if(ismob(AM))
diff --git a/code/game/atoms/atom.dm b/code/game/atoms/atom.dm
index 9c9e630ee57d..3a00472a35d2 100644
--- a/code/game/atoms/atom.dm
+++ b/code/game/atoms/atom.dm
@@ -41,6 +41,10 @@
/// armor datum type
var/armor_type = /datum/armor/none
+ //? Context
+ /// open context menus by mob
+ var/list/context_menus
+
//? Economy
/// intrinsic worth without accounting containing reagents / materials - applies in static and dynamic mode.
var/worth_intrinsic = 0
@@ -873,7 +877,7 @@
return reagents && (reagents.reagents_holder_flags & DRAINABLE)
-/atom/proc/get_cell()
+/atom/proc/get_cell(inducer)
return
//? Radiation
diff --git a/code/game/click/click.dm b/code/game/click/click.dm
index d58539cf6565..fdf533a4fe32 100644
--- a/code/game/click/click.dm
+++ b/code/game/click/click.dm
@@ -289,6 +289,8 @@
/atom/proc/AltClick(var/mob/user)
SEND_SIGNAL(src, COMSIG_CLICK_ALT, user)
+ if(context_menu(new /datum/event_args/actor(user)))
+ return TRUE
return FALSE
// todo: rework
diff --git a/code/game/click/context.dm b/code/game/click/context.dm
new file mode 100644
index 000000000000..81c18774cdc2
--- /dev/null
+++ b/code/game/click/context.dm
@@ -0,0 +1,97 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
+/**
+ * get context options
+ *
+ * key is a text string
+ * value are tuples; use ATOM_CONTEXT_TUPLE to create.
+ *
+ * @return list(key = value)
+ */
+/atom/proc/context_query(datum/event_args/actor/e_args)
+ . = list()
+ SEND_SIGNAL(src, COMSIG_ATOM_CONTEXT_QUERY, ., e_args)
+
+/**
+ * act on a context option
+ *
+ * things in this should re-check validity / sanity!
+ *
+ * @return TRUE / FALSE; TRUE if handled.
+ */
+/atom/proc/context_act(datum/event_args/actor/e_args, key)
+ if(SEND_SIGNAL(src, COMSIG_ATOM_CONTEXT_ACT, key, e_args) & RAISE_ATOM_CONTEXT_ACT_HANDLED)
+ return TRUE
+ return FALSE
+
+/atom/proc/context_menu(datum/event_args/actor/e_args)
+ set waitfor = FALSE
+ // admin proccall support
+ WRAP_MOB_TO_ACTOR_EVENT_ARGS(e_args)
+ // todo: dynamically rebuild menu based on distance?
+ var/client/receiving = e_args.initiator.client
+ if(isnull(receiving))
+ // well what the hell are we doing here?
+ // automated functions should be using context_query and context_act directly
+ return FALSE
+ if(context_menus?[receiving])
+ // close
+ log_click_context(e_args, src, "menu close")
+ qdel(context_menus[receiving])
+ return TRUE
+ var/list/menu_options = context_query(e_args)
+ if(!length(menu_options))
+ return FALSE
+ // open
+ log_click_context(e_args, src, "menu open")
+ . = TRUE
+ blocking_context_menu(e_args, receiving, menu_options, e_args.performer)
+
+/atom/proc/blocking_context_menu(datum/event_args/actor/e_args, client/receiving, list/menu_options, mob/actor)
+ // for now, we just filter without auto-updating/rebuilding when things change
+ var/list/transformed = list()
+ var/list/inverse_lookup = list()
+ for(var/key as anything in menu_options)
+ var/list/data = menu_options[key]
+ if(!CHECK_ALL_MOBILITY(actor, data[4]))
+ continue
+ if(isnull(data[3])? !actor.Adjacent(src) : get_dist(actor, src) > data[3])
+ continue
+ var/image/I = data[2]
+ // todo: why isn't radial menu doing this procesisng?
+ if(I)
+ I.maptext_x = -16
+ I.maptext_y = 32
+ I.maptext_width = 64
+ I.maptext = MAPTEXT_CENTER(data[1])
+ transformed[data[1]] = I
+ inverse_lookup[data[1]] = key
+
+ var/datum/radial_menu/context_menu/menu = new
+ var/id = "context_[REF(e_args.initiator)]"
+ GLOB.radial_menus[id] = menu
+ LAZYSET(context_menus, receiving, menu)
+
+ menu.radius = 32
+ menu.host = src
+ menu.anchor = src
+ menu.check_screen_border(receiving.mob)
+ menu.set_choices(transformed, FALSE)
+ menu.show_to(receiving.mob)
+ menu.wait(receiving.mob, src, TRUE)
+
+ var/chosen_name = menu.selected_choice
+
+ qdel(menu)
+ GLOB.radial_menus -= id
+
+ if(isnull(chosen_name))
+ return
+
+ var/key = inverse_lookup[chosen_name]
+ context_act(e_args, key)
+
+/atom/proc/context_close()
+ for(var/client/C as anything in context_menus)
+ qdel(context_menus[C])
diff --git a/code/game/click/item_attack.dm b/code/game/click/item_attack.dm
index f249a92261b9..f64c8bb68fa4 100644
--- a/code/game/click/item_attack.dm
+++ b/code/game/click/item_attack.dm
@@ -29,10 +29,10 @@ avoid code duplication. This includes items that may sometimes act as a standard
return A.attackby(src, user, params, clickchain_flags, attack_modifier)
// No comment
-/atom/proc/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier)
+/atom/proc/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier)
return I.standard_melee_attack(src, user, clickchain_flags, params, damage_multiplier, user.zone_sel?.selecting, user.a_intent)
-/mob/living/attackby(obj/item/I, mob/living/user, list/params, clickchain_flags, damage_multiplier)
+/mob/living/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier)
if(can_operate(src) && user.a_intent != INTENT_HARM && I.do_surgery(src,user))
return NONE
if(attempt_vr(src,"vore_attackby",args))
diff --git a/code/game/click/items.dm b/code/game/click/items.dm
index c703faac0e45..eb53a2568388 100644
--- a/code/game/click/items.dm
+++ b/code/game/click/items.dm
@@ -54,7 +54,7 @@
// are we on harm intent? if so, lol no
if(user && (user.a_intent == INTENT_HARM))
return NONE
- return target.tool_interaction(src, user, clickchain_flags | CLICKCHAIN_TOOL_ACT)
+ return target.tool_interaction(src, new /datum/event_args/actor/clickchain(user, params = params), clickchain_flags | CLICKCHAIN_TOOL_ACT)
/**
* called at the start of melee attack chains
diff --git a/code/game/click/other_mobs.dm b/code/game/click/other_mobs.dm
index d54b5226b340..0506362b2318 100644
--- a/code/game/click/other_mobs.dm
+++ b/code/game/click/other_mobs.dm
@@ -31,9 +31,23 @@
A.attack_hand(src)
/// Return TRUE to cancel other attack hand effects that respect it.
+// todo: /datum/event_args/actor/clickchain
/atom/proc/attack_hand(mob/user, list/params)
+ if(on_attack_hand(new /datum/event_args/actor/clickchain(user, intent = user.a_intent, params = params)))
+ return TRUE
. = _try_interact(user)
+/**
+ * Override this instead of attack_hand.
+ *
+ * Return TRUE to cancel other attack hand effects that respect it.
+ *
+ * @params
+ * * e_args - click data
+ */
+/atom/proc/on_attack_hand(datum/event_args/actor/clickchain/e_args)
+ return FALSE
+
//Return a non FALSE value to cancel whatever called this from propagating, if it respects it.
/atom/proc/_try_interact(mob/user)
// if(isAdminGhostAI(user)) //admin abuse
diff --git a/code/game/click/reachability.dm b/code/game/click/reachability.dm
index 6627d906b6d4..9623a1cd899f 100644
--- a/code/game/click/reachability.dm
+++ b/code/game/click/reachability.dm
@@ -27,6 +27,10 @@
* - for loop runs 3 times, hits turf, doesn't add turf area
* - TurfAdjacency is checked since it isn't a ranged attack
*
+ * Caveats:
+ * * This is not enough for 'can we physically reach'; that should be Adjacency. This is because telekinesis is a thing, and will be added later.
+ * * For this reason, the cost is higher than just checking 'can telekinesis', because telekinesis is checked after physical, as physical is stronger.
+ *
* @params
* - target - the target
* - depth - max depth - should be at least 1 in most cases.
@@ -36,12 +40,12 @@
/atom/movable/proc/Reachability(atom/target, depth = DEFAULT_REACHABILITY_DEPTH, range = 1, obj/item/tool)
if(!target)
// apologies sir, you may not grasp the void...
- return FALSE
+ return REACH_FAILED
// direct cache - check if we can access something using if dc[atom]
var/list/dc = DirectAccessCache()
// optimization - a lot of the time we're clicking on ourselves/things on ourselves
if(dc[target])
- return TRUE
+ return REACH_PHYSICAL
// turf adjacency enabled? stores if we can try to path to our turf
var/turf/tadj
// loc checking
@@ -105,7 +109,7 @@
continue
if(dc[l])
// found
- return TRUE
+ return REACH_PHYSICAL
if(isturf(l) && !th)
// is turf; turf adjacency enabled
th = l
@@ -117,7 +121,7 @@
++i
if(!(tadj && th))
// didn't hit both, fail
- return FALSE
+ return REACH_FAILED
// at this point, we're on a turf
if(range == 1)
// most common case: reach directly aronud yourself
@@ -136,18 +140,18 @@
if(n.TurfAdjacency(th))
// succeeded
qdel(D)
- return TRUE
+ return REACH_PHYSICAL
// dumb directional pathfinding both for cheapness and for practical purposes
// so you can't snake-arms round a row of windows or something crazy
n = get_step(D, get_dir(D, th))
if(!D.Move(n))
// failed
qdel(D)
- return FALSE
+ return REACH_FAILED
// keep going
// at this point, we failed
qdel(D)
- return FALSE
+ return REACH_FAILED
/**
* quick and dirty reachability check
@@ -160,18 +164,18 @@
if(isturf(curr))
source = get_turf(src)
if(!source)
- return FALSE
+ return REACH_FAILED
return curr.TurfAdjacency(source)
do
if(curr == src)
- return TRUE
+ return REACH_PHYSICAL
if(!curr)
- return FALSE
+ return REACH_FAILED
curr = curr.loc
while(!isturf(curr))
source = get_turf(src)
if(!source)
- return FALSE
+ return REACH_FAILED
return curr.TurfAdjacency(source)
/atom/movable/reachability_delegate
diff --git a/code/game/machinery/_machinery_construction.dm b/code/game/machinery/_machinery_construction.dm
index b2e8dc5afae6..025d3d1b213c 100644
--- a/code/game/machinery/_machinery_construction.dm
+++ b/code/game/machinery/_machinery_construction.dm
@@ -1,103 +1,101 @@
//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2023 Citadel Station developers. *//
-/obj/machinery/dynamic_tool_functions(obj/item/I, mob/user)
+/obj/machinery/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
. = list()
if(tool_deconstruct && !isnull(default_deconstruct) && panel_open)
- LAZYADD(.[tool_panel], "deconstruct")
+ LAZYSET(.[tool_deconstruct], "deconstruct", dyntool_image_backward(tool_deconstruct))
if(tool_unanchor && !isnull(default_unanchor))
- LAZYADD(.[tool_panel], anchored? "unanchor" : "anchor")
+ LAZYSET(.[tool_unanchor], anchored? "unanchor" : "anchor", anchored? dyntool_image_backward(tool_unanchor) : dyntool_image_forward(tool_unanchor))
if(tool_panel && !isnull(default_panel))
- LAZYADD(.[tool_panel], panel_open? "close panel" : "open panel")
+ LAZYSET(.[tool_panel], panel_open? "close panel" : "open panel", panel_open? dyntool_image_forward(tool_panel) : dyntool_image_backward(tool_panel))
return merge_double_lazy_assoc_list(., ..())
-/obj/machinery/dynamic_tool_image(function, hint)
- switch(hint)
- if("anchor", "close panel")
- return dyntool_image_backward(function)
- if("unanchor", "open panel", "deconstruct")
- return dyntool_image_backward(function)
- return ..()
-
-/obj/machinery/tool_act(obj/item/I, mob/user, function, flags, hint)
- if(INTERACTING_WITH_FOR(user, src, INTERACTING_FOR_CONSTRUCTION))
+/obj/machinery/tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint)
+ if(INTERACTING_WITH_FOR(e_args.performer, src, INTERACTING_FOR_CONSTRUCTION))
return CLICKCHAIN_DO_NOT_PROPAGATE
- START_INTERACTING_WITH(user, src, INTERACTING_FOR_CONSTRUCTION)
+ START_INTERACTING_WITH(e_args.performer, src, INTERACTING_FOR_CONSTRUCTION)
if(function == tool_deconstruct && !isnull(default_deconstruct))
- if(default_deconstruction_dismantle(I, user, flags = flags))
+ if(default_deconstruction_dismantle(I, e_args, flags = flags))
. = CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE
. = CLICKCHAIN_DO_NOT_PROPAGATE
else if(function == tool_unanchor && !isnull(default_unanchor))
- if(default_deconstruction_anchor(I, user, flags = flags))
+ if(default_deconstruction_anchor(I, e_args, flags = flags))
. = CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE
. = CLICKCHAIN_DO_NOT_PROPAGATE
else if(function == tool_panel && !isnull(default_panel))
- if(default_deconstruction_panel(I, user, flags = flags))
+ if(default_deconstruction_panel(I, e_args, flags = flags))
. = CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE
. = CLICKCHAIN_DO_NOT_PROPAGATE
- STOP_INTERACTING_WITH(user, src,INTERACTING_FOR_CONSTRUCTION)
+ STOP_INTERACTING_WITH(e_args.performer, src,INTERACTING_FOR_CONSTRUCTION)
if(isnull(.))
return ..()
// todo: better verb/message support
-/obj/machinery/proc/default_deconstruction_panel(obj/item/tool, mob/user, speed_mult = 1, flags)
+/obj/machinery/proc/default_deconstruction_panel(obj/item/tool, datum/event_args/actor/clickchain/e_args, speed_mult = 1, flags)
var/needed_time = default_panel * speed_mult * (isnull(tool)? 1 : tool.tool_speed)
if(needed_time)
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- soft_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_soft = SPAN_WARNING("[user] starts to [panel_open? "close" : "open"] [src]'s maintenance panel."),
- audible_soft = SPAN_WARNING("You hear something being (un)fastened."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_WARNING("[e_args.performer] starts to [panel_open? "close" : "open"] [src]'s maintenance panel."),
+ audible = SPAN_WARNING("You hear something being (un)fastened."),
+ otherwise_self = SPAN_WARNING("You start to [panel_open? "close" : "open"] [src]'s panel."),
)
- if(!use_tool(tool_panel, tool, user, flags, needed_time))
+ if(!use_tool(tool_panel, tool, e_args, flags, needed_time))
return FALSE
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- soft_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_soft = SPAN_WARNING("[user] [panel_open? "closes" : "opens"] [src]'s maintenance panel."),
- audible_soft = SPAN_WARNING("You hear something being (un)fastened."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_WARNING("[e_args.performer] [panel_open? "closes" : "opens"] [src]'s maintenance panel."),
+ audible = SPAN_WARNING("You hear something being (un)fastened."),
+ otherwise_self = SPAN_WARNING("You [panel_open? "close" : "open"] [src]'s panel."),
)
set_panel_open(!panel_open)
return TRUE
// todo: better verb/message support
-/obj/machinery/proc/default_deconstruction_dismantle(obj/item/tool, mob/user, speed_mult = 1, flags)
+/obj/machinery/proc/default_deconstruction_dismantle(obj/item/tool, datum/event_args/actor/clickchain/e_args, speed_mult = 1, flags)
var/needed_time = default_deconstruct * speed_mult * (isnull(tool)? 1 : tool.tool_speed)
if(needed_time)
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- soft_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_soft = SPAN_WARNING("[user] starts to dismantle [src]."),
- audible_soft = SPAN_WARNING("You hear a series of small parts being removed from something."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_WARNING("[e_args.performer] starts to dismantle [src]."),
+ audible = SPAN_WARNING("You hear a series of small parts being removed from something."),
+ otherwise_self = SPAN_WARNING("You start to dismantle [src]."),
)
- if(!use_tool(tool_deconstruct, tool, user, flags, needed_time))
+ if(!use_tool(tool_deconstruct, tool, e_args, flags, needed_time))
return FALSE
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- soft_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_soft = SPAN_WARNING("[user] dismantles [src]."),
- audible_soft = SPAN_WARNING("You hear something getting dismantled."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_WARNING("[e_args.performer] dismantles [src]."),
+ audible = SPAN_WARNING("You hear something getting dismantled."),
+ otherwise_self = SPAN_WARNING("You dismantle [src]."),
)
dismantle()
return TRUE
// todo: better verb/message support
-/obj/machinery/proc/default_deconstruction_anchor(obj/item/tool, mob/user, speed_mult = 1, flags)
+/obj/machinery/proc/default_deconstruction_anchor(obj/item/tool, datum/event_args/actor/clickchain/e_args, speed_mult = 1, flags)
var/needed_time = default_unanchor * speed_mult * (isnull(tool)? 1 : tool.tool_speed)
if(needed_time)
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- soft_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_soft = SPAN_WARNING("[user] starts to [anchored? "unbolt" : "bolt"] [src] [anchored? "from" : "to"] the floor."),
- audible_soft = SPAN_WARNING("You hear something heavy being (un)fastened."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_WARNING("[e_args.performer] starts to [anchored? "unbolt" : "bolt"] [src] [anchored? "from" : "to"] the floor."),
+ audible = SPAN_WARNING("You hear something heavy being (un)fastened."),
+ otherwise_self = SPAN_WARNING("You start to [anchored? "unbolt" : "bolt"] [src] [anchored? "from" : "to"] the floor."),
)
- if(!use_tool(tool_unanchor, tool, user, flags, needed_time))
+ if(!use_tool(tool_unanchor, tool, e_args, flags, needed_time))
return FALSE
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- soft_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_soft = SPAN_WARNING("[user] [anchored? "bolts" : "unbolts"] [src] [anchored? "to" : "from"] from the floor."),
- audible_soft = SPAN_WARNING("You hear something heavy being (un)fastened."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_WARNING("[e_args.performer] [anchored? "bolts" : "unbolts"] [src] [anchored? "to" : "from"] from the floor."),
+ audible = SPAN_WARNING("You hear something heavy being (un)fastened."),
+ otherwise_self = SPAN_WARNING("You [anchored? "unbolt" : "bolt"] [src] [anchored? "from" : "to"] the floor."),
)
set_anchored(!anchored)
return TRUE
diff --git a/code/game/machinery/misc/bioscan_antenna.dm b/code/game/machinery/misc/bioscan_antenna.dm
index e7d0c52c06d8..286bd68c62b0 100644
--- a/code/game/machinery/misc/bioscan_antenna.dm
+++ b/code/game/machinery/misc/bioscan_antenna.dm
@@ -34,17 +34,22 @@ GLOBAL_LIST_EMPTY(bioscan_antenna_list)
change_network(null)
return ..()
-/obj/machinery/bioscan_antenna/multitool_act(obj/item/I, mob/user, flags, hint)
+/obj/machinery/bioscan_antenna/multitool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
if(!network_mutable)
return ..()
. = TRUE
- var/new_network = default_input_text(user, "What do you want to set the network key to?", "Modify Network", network_key)
- if(!user.Reachability(src) || isnull(new_network))
+ var/new_network = default_input_text(e_args.initiator, "What do you want to set the network key to?", "Modify Network", network_key)
+ if(!e_args.performer.Reachability(src) || isnull(new_network))
return
- user.visible_message(SPAN_NOTICE("[user] reprograms the network on [src]."), range = MESSAGE_RANGE_CONFIGURATION)
+ e_args.visible_feedback(
+ target = src,
+ visible = SPAN_NOTICE("[e_args.performer] reprograms the network on [src]."),
+ range = MESSAGE_RANGE_CONFIGURATION,
+ otherwise_self = SPAN_NOTICE("You reprogram the network on [src]."),
+ )
change_network(new_network)
-/obj/machinery/bioscan_antenna/dynamic_tool_functions(obj/item/I, mob/user)
+/obj/machinery/bioscan_antenna/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
. = list()
if(network_mutable)
.[TOOL_MULTITOOL] = "change network"
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index ac6b4eb4122b..d56a9340f005 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -175,28 +175,23 @@ Buildable meters
else
return ..()
-/obj/item/pipe/attackby(var/obj/item/W as obj, var/mob/user as mob)
- if(W.is_wrench())
- return wrench_act(W, user)
- return ..()
-
-/obj/item/pipe/wrench_act(obj/item/I, mob/user, flags, hint)
+/obj/item/pipe/wrench_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
if(!isturf(loc))
return TRUE
- add_fingerprint(user)
+ add_fingerprint(e_args.performer)
fixdir()
var/obj/machinery/atmospherics/fakeA = pipe_type
var/initial_flags = initial(fakeA.pipe_flags)
for(var/obj/machinery/atmospherics/M in loc)
if((M.pipe_flags & initial_flags & PIPING_ONE_PER_TURF)) //Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers.
- to_chat(user, "Something is hogging the tile!")
+ e_args.chat_feedback(SPAN_WARNING("Something is hogging the tile!"), src)
return TRUE
if((M.piping_layer != piping_layer) && !((M.pipe_flags | initial_flags) & PIPING_ALL_LAYER)) // Pipes on different layers can't block each other unless they are ALL_LAYER
continue
if(M.get_init_dirs() & SSmachines.get_init_dirs(pipe_type, dir)) // matches at least one direction on either type of pipe
- to_chat(user, "There is already a pipe at that location!")
+ e_args.chat_feedback(SPAN_WARNING("There is already a pipe at that location!"), src)
return TRUE
// no conflicts found
@@ -205,15 +200,17 @@ Buildable meters
// TODO - Evaluate and remove the "need at least one thing to connect to" thing ~Leshana
// With how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment.
if (QDELETED(A))
- to_chat(user, "There's nothing to connect this pipe section to!")
+ e_args.chat_feedback(SPAN_WARNING("There's nothing to connect this pipe section to!"), src)
return TRUE
transfer_fingerprints_to(A)
playsound(src, I.tool_sound, 50, 1)
- user.visible_message( \
- "[user] fastens \the [src].", \
- "You fasten \the [src].", \
- "You hear ratcheting.")
+ e_args.visible_feedback(
+ target = src,
+ visible = SPAN_NOTICE("[e_args.performer] fastens \the [src]."),
+ audible = SPAN_WARNING("You hear ratcheting."),
+ otherwise_self = SPAN_NOTICE("You fasten \the [src].")
+ )
qdel(src)
@@ -269,18 +266,18 @@ Buildable meters
return wrench_act(W, user)
return ..()
-/obj/item/pipe_meter/wrench_act(obj/item/I, mob/user, flags, hint)
+/obj/item/pipe_meter/wrench_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
var/obj/machinery/atmospherics/pipe/pipe
for(var/obj/machinery/atmospherics/pipe/P in loc)
if(P.piping_layer == piping_layer)
pipe = P
break
if(!pipe)
- to_chat(user, "You need to fasten it to a pipe!")
+ e_args.chat_feedback(SPAN_WARNING("You need to fasten it to a pipe!"), src)
return TRUE
new /obj/machinery/meter(loc, piping_layer)
playsound(src, I.tool_sound, 50, 1)
- to_chat(user, "You fasten the meter to the pipe.")
+ e_args.chat_feedback(SPAN_NOTICE("You fasten the meter to the pipe."), src)
qdel(src)
/obj/item/pipe_meter/dropped(mob/user, flags, atom/newLoc)
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 04dbf514158d..410669e08932 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -401,7 +401,7 @@
return
cell = new /obj/item/cell/high(src)
-/obj/mecha/get_cell()
+/obj/mecha/get_cell(inducer)
return cell
/obj/mecha/proc/add_cabin()
@@ -2853,5 +2853,5 @@
occupant.clear_alert("mech damage")
// Various sideways-defined get_cells
-/obj/mecha/get_cell()
+/obj/mecha/get_cell(inducer)
return cell
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index eab4a93c53a5..ecf0da767c20 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -378,9 +378,9 @@
R.activate_module(src)
R.hud_used.update_robot_modules_display()
-/obj/item/attackby(obj/item/W as obj, mob/user as mob)
- if(istype(W, /obj/item/storage))
- var/obj/item/storage/S = W
+/obj/item/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier)
+ if(istype(I, /obj/item/storage))
+ var/obj/item/storage/S = I
if(S.use_to_pickup)
if(S.collection_mode) //Mode is set to collect all items
if(isturf(src.loc))
@@ -388,7 +388,19 @@
else if(S.can_be_inserted(src))
S.handle_item_insertion(src, user)
- return
+ if(istype(I, /obj/item/cell) && !isnull(obj_cell_slot) && isnull(obj_cell_slot.cell) && obj_cell_slot.interaction_active(user))
+ if(!user.transfer_item_to_loc(I, src))
+ user.action_feedback(SPAN_WARNING("[I] is stuck to your hand!"), src)
+ return CLICKCHAIN_DO_NOT_PROPAGATE
+ user.visible_action_feedback(
+ target = src,
+ hard_range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible_hard = SPAN_NOTICE("[user] inserts [I] into [src]."),
+ audible_hard = SPAN_NOTICE("You hear something being slotted in."),
+ visible_self = SPAN_NOTICE("You insert [I] into [src]."),
+ )
+ obj_cell_slot.insert_cell(I)
+ return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING
/obj/item/proc/talk_into(mob/M as mob, text)
return
@@ -769,17 +781,33 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
// SHOULD_CALL_PARENT(TRUE)
// attack_self isn't really part of the item attack chain.
SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user)
+ if(on_attack_self(new /datum/event_args/actor(user)))
+ return TRUE
if(interaction_flags_item & INTERACT_ITEM_ATTACK_SELF)
interact(user)
- on_attack_self(user)
/**
* Called after we attack self
* Used to allow for attack_self to be interrupted by signals in nearly all cases.
* You should usually override this instead of attack_self.
+ *
+ * You should do . = ..() and check ., if it's TRUE, it means a parent proc requested the call chain to stop.
+ *
+ * @return TRUE to signal to overrides to stop the chain and do nothing.
*/
-/obj/item/proc/on_attack_self(mob/user)
- return
+/obj/item/proc/on_attack_self(datum/event_args/actor/e_args)
+ if(!isnull(obj_cell_slot?.cell) && obj_cell_slot.remove_yank_inhand && obj_cell_slot.interaction_active(src))
+ e_args.visible_feedback(
+ target = src,
+ range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] removes the cell from [src]."),
+ audible = SPAN_NOTICE("You hear fasteners falling out and something being removed."),
+ otherwise_self = SPAN_NOTICE("You remove the cell from [src]."),
+ )
+ log_construction(e_args, src, "removed cell [obj_cell_slot.cell] ([obj_cell_slot.cell.type])")
+ e_args.performer.put_in_hands_or_drop(obj_cell_slot.remove_cell(e_args.performer))
+ return TRUE
+ return FALSE
//? Mob Armor
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 9ece7196e696..605d91a181a3 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -72,7 +72,7 @@
icon_state = "[base_icon]"
return
-/obj/item/flash/get_cell()
+/obj/item/flash/get_cell(inducer)
return power_supply
/obj/item/flash/proc/get_external_power_supply()
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 6fcf18ad20bc..6c497c9b01e3 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -57,7 +57,7 @@
update_appearance()
return PROCESS_KILL
-/obj/item/flashlight/get_cell()
+/obj/item/flashlight/get_cell(inducer)
return cell
/obj/item/flashlight/verb/toggle()
diff --git a/code/game/objects/items/devices/radio/jammer.dm b/code/game/objects/items/devices/radio/jammer.dm
index 24453693e9c5..69ef1cf538f8 100644
--- a/code/game/objects/items/devices/radio/jammer.dm
+++ b/code/game/objects/items/devices/radio/jammer.dm
@@ -39,7 +39,7 @@ var/global/list/active_radio_jammers = list()
QDEL_NULL(power_source)
return ..()
-/obj/item/radio_jammer/get_cell()
+/obj/item/radio_jammer/get_cell(inducer)
return power_source
/obj/item/radio_jammer/proc/turn_off(mob/user)
diff --git a/code/game/objects/items/devices/suit_cooling.dm b/code/game/objects/items/devices/suit_cooling.dm
index d32a4a0219a7..10718aac8cd0 100644
--- a/code/game/objects/items/devices/suit_cooling.dm
+++ b/code/game/objects/items/devices/suit_cooling.dm
@@ -217,7 +217,7 @@
/obj/item/suit_cooling_unit/emergency/updateicon()
return
-/obj/item/suit_cooling_unit/emergency/get_cell()
+/obj/item/suit_cooling_unit/emergency/get_cell(inducer)
if(on)
return null // Don't let recharging happen while we're on
return cell
diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/inducer.dm
index d807d6a8631c..67b653310715 100644
--- a/code/game/objects/items/inducer.dm
+++ b/code/game/objects/items/inducer.dm
@@ -18,8 +18,6 @@
var/transfer_rate = 1000
/// type of cell to spawn
var/cell_type = /obj/item/cell/high
- /// our cell
- var/obj/item/cell/cell
/// panel open?
var/opened = FALSE
/// currently inducing?
@@ -35,17 +33,24 @@
/obj/item/inducer/Initialize(mapload)
. = ..()
- if(!cell && cell_type)
- cell = new cell_type
+ var/datum/object_system/cell_slot/cell_slot = init_cell_slot(cell_type)
+ cell_slot.receive_emp = TRUE
+ cell_slot.receive_inducer = TRUE
+ cell_slot.remove_yank_offhand = TRUE
+ cell_slot.remove_yank_context = TRUE
+ cell_slot.remove_yank_inhand = TRUE
update_appearance()
-/obj/item/inducer/get_cell()
- return cell
-
-/obj/item/inducer/emp_act(severity)
+/obj/item/inducer/examine(mob/user, dist)
. = ..()
- if(cell)
- cell.emp_act(severity)
+ if(!isnull(obj_cell_slot.cell))
+ . += "
Its display shows: [round(obj_cell_slot.cell.charge)] / [obj_cell_slot.cell.maxcharge]."
+ else
+ . += "
Its display is dark."
+ if(opened)
+ . += SPAN_NOTICE("Its battery compartment is open, and looks like it can be closed with a screwdriver")
+ else
+ . += SPAN_NOTICE("Its battery compartment is closed, and looks like it can be opened with a screwdriver")
/obj/item/inducer/afterattack(atom/target, mob/user, clickchain_flags, list/params)
if(user.a_intent == INTENT_HARM)
@@ -59,16 +64,15 @@
to_chat(user, "You don't have the dexterity to use [src]!")
return TRUE
- if(!cell)
+ if(!obj_cell_slot.cell)
to_chat(user, "[src] doesn't have a power cell installed!")
return TRUE
- if(!cell.charge)
+ if(!obj_cell_slot.cell.charge)
to_chat(user, "[src]'s battery is dead!")
return TRUE
return FALSE
-
/obj/item/inducer/attackby(obj/item/W, mob/user)
if(W.is_screwdriver())
playsound(src, W.tool_sound, 50, 1)
@@ -82,24 +86,6 @@
opened = FALSE
update_icon()
return
- if(istype(W, /obj/item/cell))
- if(opened)
- if(!cell)
- if(!user.attempt_insert_item_for_installation(W, src))
- return
- to_chat(user, "You insert [W] into [src].")
- cell = W
- update_icon()
- return
- else
- to_chat(user, "[src] already has \a [cell] installed!")
- return
-
- if(cantbeused(user))
- return
-
- if(recharge(W, user))
- return
return ..()
@@ -142,8 +128,8 @@
var/datum/current = targets[1]
targets.Cut(1, 2)
- while(!QDELETED(A) && do_after(user, 2 SECONDS, A, DO_AFTER_IGNORE_MOVEMENT, max_distance = recharge_dist) && !QDELETED(cell))
- var/amount = min(cell.charge, transfer_rate * 2) // transfer rate is per second, we do this every 2 seconds
+ while(!QDELETED(A) && do_after(user, 2 SECONDS, A, DO_AFTER_IGNORE_MOVEMENT, max_distance = recharge_dist) && !QDELETED(obj_cell_slot.cell))
+ var/amount = min(obj_cell_slot.cell.charge, transfer_rate * 2) // transfer rate is per second, we do this every 2 seconds
var/charged = current.inducer_act(src, amount, inducer_flags)
spark_system.start()
if(charged == INDUCER_ACT_CONTINUE)
@@ -152,7 +138,7 @@
break
if(charged <= 0)
break
- cell.use(charged)
+ obj_cell_slot.cell.use(charged)
used += charged
qdel(spark_system)
@@ -161,31 +147,22 @@
inducing = FALSE
user.visible_message(SPAN_NOTICE("[user] recharged [A]."), SPAN_NOTICE("Rechraged [A] with [used] units of power."))
-/obj/item/inducer/attack_self(mob/user)
+/obj/item/inducer/object_cell_slot_removed(obj/item/cell/cell, datum/object_system/cell_slot/slot)
. = ..()
- if(.)
- return
- if(opened && cell)
- user.visible_message("[user] removes [cell] from [src]!", "You remove [cell].")
- cell.update_icon()
- user.put_in_hands_or_drop(cell)
- cell = null
- update_icon()
+ update_icon()
-/obj/item/inducer/examine(mob/living/M)
+/obj/item/inducer/object_cell_slot_inserted(obj/item/cell/cell, datum/object_system/cell_slot/slot)
. = ..()
- if(cell)
- . += "
Its display shows: [round(cell.charge)] / [cell.maxcharge]."
- else
- . += "
Its display is dark."
- if(opened)
- . += "
Its battery compartment is open."
+ update_icon()
+
+/obj/item/inducer/object_cell_slot_mutable(mob/user, datum/object_system/cell_slot/slot)
+ return opened && ..()
/obj/item/inducer/update_icon()
..()
cut_overlays()
if(opened)
- if(!cell)
+ if(isnull(obj_cell_slot.cell))
add_overlay("inducer-nobat")
else
add_overlay("inducer-bat")
@@ -237,7 +214,7 @@
* even if full, always add things, or the inducer might think we don't support induction when we do!
*/
/atom/proc/inducer_scan(obj/item/inducer/I, list/things_to_induce = list(), inducer_flags)
- var/obj/item/cell/C = get_cell()
+ var/obj/item/cell/C = get_cell(TRUE)
if(C)
things_to_induce += C
if(C.charge >= C.maxcharge)
diff --git a/code/game/objects/items/tools/switchtool.dm b/code/game/objects/items/tools/switchtool.dm
index 64a57623544d..e1420f552939 100644
--- a/code/game/objects/items/tools/switchtool.dm
+++ b/code/game/objects/items/tools/switchtool.dm
@@ -256,11 +256,11 @@
return "shield"
//? tool redirection
-/obj/item/switchtool/tool_check(function, mob/user, atom/target, flags, usage)
+/obj/item/switchtool/tool_check(function, datum/event_args/actor/clickchain/e_args, atom/target, flags, usage)
return (function in tool_functions)? tool_quality : null
//? tool redirection
-/obj/item/switchtool/tool_query(mob/user, atom/target, flags, usage)
+/obj/item/switchtool/tool_query(datum/event_args/actor/clickchain/e_args, atom/target, flags, usage)
. = list()
for(var/i in tool_functions)
.[i] = tool_quality
diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm
index a080a6322f25..56273803514e 100644
--- a/code/game/objects/items/tools/weldingtool.dm
+++ b/code/game/objects/items/tools/weldingtool.dm
@@ -162,20 +162,20 @@
/obj/item/weldingtool/proc/get_max_fuel()
return max_fuel
-/obj/item/weldingtool/using_as_tool(function, flags, mob/user, atom/target, time, cost, usage)
+/obj/item/weldingtool/using_as_tool(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage)
. = ..()
if(!. || function != TOOL_WELDER)
return
if(!isOn())
- user.action_feedback(SPAN_WARNING("[src] must be on to be used to weld!"), target)
+ e_args.chat_feedback(SPAN_WARNING("[src] must be on to be used to weld!"), target)
return FALSE
// floor
var/computed = round(cost * time * TOOL_WELDING_FUEL_PER_DS)
if(get_fuel() < computed)
- user.action_feedback(SPAN_WARNING("[src] doesn't have enough fuel left to do that!"), target)
+ e_args.chat_feedback(SPAN_WARNING("[src] doesn't have enough fuel left to do that!"), target)
return FALSE
-/obj/item/weldingtool/used_as_tool(function, flags, mob/user, atom/target, time, cost, usage, success)
+/obj/item/weldingtool/used_as_tool(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage, success)
. = ..()
if(!.)
return
@@ -570,7 +570,7 @@
power_supply = new /obj/item/cell/device(src)
update_icon()
-/obj/item/weldingtool/electric/get_cell()
+/obj/item/weldingtool/electric/get_cell(inducer)
return power_supply
/obj/item/weldingtool/electric/examine(mob/user, dist)
diff --git a/code/game/objects/items/uav.dm b/code/game/objects/items/uav.dm
index 47b10a81b77f..43cca4dbc4b8 100644
--- a/code/game/objects/items/uav.dm
+++ b/code/game/objects/items/uav.dm
@@ -250,7 +250,7 @@
visible_message(SPAN_NOTICE("[nickname] gracefully settles onto the ground."))
//////////////// Helpers
-/obj/item/uav/get_cell()
+/obj/item/uav/get_cell(inducer)
return cell
/obj/item/uav/relaymove(var/mob/user, direction, signal = 1)
diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm
index 7a0569b4c20e..da304f685f5e 100644
--- a/code/game/objects/items/weapons/RCD.dm
+++ b/code/game/objects/items/weapons/RCD.dm
@@ -291,7 +291,7 @@
QDEL_NULL(cell)
return ..()
-/obj/item/rcd/electric/get_cell()
+/obj/item/rcd/electric/get_cell(inducer)
return cell
/obj/item/rcd/electric/can_afford(amount) // This makes it so borgs won't drain their last sliver of charge by mistake, as a bonus.
@@ -323,7 +323,7 @@
desc = "A device used to rapidly build and deconstruct. It runs directly off of electricity from an external power source."
make_cell = FALSE
-/obj/item/rcd/electric/mounted/get_cell()
+/obj/item/rcd/electric/mounted/get_cell(inducer)
return get_external_power_supply()
/obj/item/rcd/electric/mounted/proc/get_external_power_supply()
diff --git a/code/game/objects/items/weapons/RPD.dm b/code/game/objects/items/weapons/RPD.dm
index 0aa4275b216f..c8fc53f016af 100644
--- a/code/game/objects/items/weapons/RPD.dm
+++ b/code/game/objects/items/weapons/RPD.dm
@@ -352,9 +352,7 @@
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
/obj/item/pipe_dispenser/proc/do_wrench(var/atom/target, mob/user)
- var/resolved = target.attackby(tool,user)
- if(!resolved && tool && target)
- tool.afterattack(target,user,1)
+ tool.melee_attack_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/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 30eb2ca05c58..d2b96d3c40c0 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -145,7 +145,7 @@
return
return ..()
-/obj/item/melee/energy/get_cell()
+/obj/item/melee/energy/get_cell(inducer)
return bcell
/obj/item/melee/energy/update_icon()
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 97b65063c557..a2426135c332 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -30,7 +30,7 @@
. = ..()
update_icon()
-/obj/item/melee/baton/get_cell()
+/obj/item/melee/baton/get_cell(inducer)
return bcell
/obj/item/melee/baton/suicide_act(mob/user)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 954db393c9b8..5019578d42aa 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -59,6 +59,10 @@
/// volume when breaking out using resist process
var/breakout_volume = 100
+ //? Systems - naming convention is 'object_[system]'
+ /// cell slot system
+ var/datum/object_system/cell_slot/obj_cell_slot
+
//? misc / legacy
/// Set when a player renames a renamable object.
var/renamed_by_player = FALSE
@@ -105,6 +109,7 @@
unregister_dangerous_to_step()
SStgui.close_uis(src)
SSnanoui.close_uis(src)
+ QDEL_NULL(obj_cell_slot)
return ..()
/obj/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
@@ -238,6 +243,59 @@
add_fingerprint(user)
..()
+//? Attacks
+
+/obj/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(istype(I, /obj/item/cell) && !isnull(obj_cell_slot) && isnull(obj_cell_slot.cell) && obj_cell_slot.interaction_active(user))
+ if(!user.transfer_item_to_loc(I, src))
+ user.action_feedback(SPAN_WARNING("[I] is stuck to your hand!"), src)
+ return CLICKCHAIN_DO_NOT_PROPAGATE
+ user.visible_action_feedback(
+ target = src,
+ hard_range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible_hard = SPAN_NOTICE("[user] inserts [I] into [src]."),
+ audible_hard = SPAN_NOTICE("You hear something being slotted in."),
+ visible_self = SPAN_NOTICE("You insert [I] into [src]."),
+ )
+ obj_cell_slot.insert_cell(I)
+ return CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_DID_SOMETHING
+ return ..()
+
+/obj/on_attack_hand(datum/event_args/actor/clickchain/e_args)
+ . = ..()
+ if(.)
+ return
+ if(!isnull(obj_cell_slot?.cell) && obj_cell_slot.remove_yank_offhand && e_args.performer.is_holding_inactive(src) && obj_cell_slot.interaction_active(e_args.performer))
+ e_args.performer.visible_action_feedback(
+ target = src,
+ hard_range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible_hard = SPAN_NOTICE("[e_args.performer] removes the cell from [src]."),
+ audible_hard = SPAN_NOTICE("You hear fasteners falling out and something being removed."),
+ visible_self = SPAN_NOTICE("You remove the cell from [src]."),
+ )
+ log_construction(e_args, src, "removed cell [obj_cell_slot.cell] ([obj_cell_slot.cell.type])")
+ e_args.performer.put_in_hands_or_drop(obj_cell_slot.remove_cell(e_args.performer))
+ return TRUE
+
+//? Cells / Inducers
+
+/**
+ * get cell slot
+ */
+/obj/get_cell(inducer)
+ . = ..()
+ if(.)
+ return
+ if(obj_cell_slot?.primary && !isnull(obj_cell_slot.cell) && (!inducer || obj_cell_slot.receive_inducer))
+ return obj_cell_slot.cell
+
+/obj/inducer_scan(obj/item/inducer/I, list/things_to_induce, inducer_flags)
+ . = ..()
+ if(!isnull(obj_cell_slot?.cell) && !obj_cell_slot.primary && obj_cell_slot.receive_inducer)
+ things_to_induce += obj_cell_slot.cell
+
//? Climbing
/obj/MouseDroppedOn(atom/dropping, mob/user, proximity, params)
@@ -368,7 +426,51 @@
H.update_health()
*/
-//* Hiding / Underfloor
+//? Context
+
+/obj/context_query(datum/event_args/actor/e_args)
+ . = ..()
+ if(!isnull(obj_cell_slot?.cell) && obj_cell_slot.remove_yank_context && obj_cell_slot.interaction_active(e_args.performer))
+ var/image/rendered = image(obj_cell_slot.cell)
+ .["obj_cell_slot"] = ATOM_CONTEXT_TUPLE("remove cell", rendered, null, MOBILITY_CAN_USE)
+
+/obj/context_act(datum/event_args/actor/e_args, key)
+ if(key == "obj_cell_slot")
+ var/reachability = e_args.performer.Reachability(src)
+ if(!reachability)
+ return TRUE
+ if(!CHECK_MOBILITY(e_args.performer, MOBILITY_CAN_USE))
+ e_args.initiator.action_feedback(SPAN_WARNING("You can't do that right now!"), src)
+ return TRUE
+ if(isnull(obj_cell_slot.cell))
+ e_args.initiator.action_feedback(SPAN_WARNING("[src] doesn't have a cell installed."))
+ return TRUE
+ if(!obj_cell_slot.interaction_active(e_args.performer))
+ return TRUE
+ e_args.visible_feedback(
+ target = src,
+ range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] removes the cell from [src]."),
+ audible = SPAN_NOTICE("You hear fasteners falling out and something being removed."),
+ otherwise_self = SPAN_NOTICE("You remove the cell from [src]."),
+ )
+ log_construction(e_args, src, "removed cell [obj_cell_slot.cell] ([obj_cell_slot.cell.type])")
+ var/obj/item/cell/removed = obj_cell_slot.remove_cell(src)
+ if(reachability == REACH_PHYSICAL)
+ e_args.performer.put_in_hands_or_drop(removed)
+ else
+ removed.forceMove(drop_location())
+ return TRUE
+ return ..()
+
+//? EMP
+
+/obj/emp_act(severity)
+ . = ..()
+ if(obj_cell_slot?.receive_emp)
+ obj_cell_slot?.cell?.emp_act(severity)
+
+//? Hiding / Underfloor
/obj/proc/is_hidden_underfloor()
return FALSE
@@ -472,3 +574,38 @@
var/shake_dir = pick(-1, 1)
animate(src, transform=turn(matrix(), 8*shake_dir), pixel_x=init_px + 2*shake_dir, time=1)
animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
+
+//? Tool System
+
+/obj/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
+ if(isnull(obj_cell_slot) || !obj_cell_slot.remove_tool_behavior || !obj_cell_slot.interaction_active(e_args.performer))
+ return ..()
+ . = list()
+ LAZYSET(.[obj_cell_slot.remove_tool_behavior], "remove cell", dyntool_image_backward(obj_cell_slot.remove_tool_behavior))
+ return merge_double_lazy_assoc_list(..(), .)
+
+/obj/tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint)
+ if(isnull(obj_cell_slot) || (obj_cell_slot.remove_tool_behavior != function) || !obj_cell_slot.interaction_active(e_args.performer))
+ return ..()
+ if(isnull(obj_cell_slot.cell))
+ e_args.chat_feedback(SPAN_WARNING("[src] has no cell in it."))
+ return CLICKCHAIN_DO_NOT_PROPAGATE
+ log_construction(e_args, src, "removing cell")
+ e_args.visible_feedback(
+ target = src,
+ range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] starts removing the cell from [src]."),
+ audible = SPAN_NOTICE("You hear fasteners being undone."),
+ otherwise_self = SPAN_NOTICE("You start removing the cell from [src]."),
+ )
+ if(!use_tool(function, I, e_args, flags, obj_cell_slot.remove_tool_time, 1))
+ return CLICKCHAIN_DO_NOT_PROPAGATE
+ log_construction(e_args, src, "removed cell")
+ e_args.visible_feedback(
+ target = src,
+ range = obj_cell_slot.remove_is_discrete? 0 : MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] removes the cell from [src]."),
+ audible = SPAN_NOTICE("You hear fasteners falling out and something being removed."),
+ otherwise_self = SPAN_NOTICE("You remove the cell from [src]."),
+ )
+ return CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index f1d095eedbab..1e8b1d22cd9b 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -453,15 +453,15 @@
new shardtype(drop_location())
-/obj/structure/window/screwdriver_act(obj/item/I, mob/user, flags, hint)
+/obj/structure/window/screwdriver_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = TRUE
if (construction_state == WINDOW_STATE_UNSECURED || construction_state == WINDOW_STATE_SCREWED_TO_FLOOR || !considered_reinforced)
- if (!use_screwdriver(I, user, flags))
+ if (!use_screwdriver(I, e_args, flags))
return
var/unsecuring = construction_state != WINDOW_STATE_UNSECURED
- user.action_feedback(SPAN_NOTICE("You [unsecuring? "unfasten" : "fasten"] the frame [unsecuring? "from" : "to"] the floor."), src)
+ e_args.chat_feedback(SPAN_NOTICE("You [unsecuring? "unfasten" : "fasten"] the frame [unsecuring? "from" : "to"] the floor."), src)
if (unsecuring)
construction_state = WINDOW_STATE_UNSECURED
set_anchored(FALSE)
@@ -475,84 +475,82 @@
if (construction_state != WINDOW_STATE_CROWBRARED_IN && construction_state != WINDOW_STATE_SECURED_TO_FRAME)
return
- if (!use_screwdriver(I, user, flags))
+ if (!use_screwdriver(I, e_args, flags))
return
var/unsecuring = construction_state == WINDOW_STATE_SECURED_TO_FRAME
- user.action_feedback(SPAN_NOTICE("You [unsecuring? "unfasten" : "fasten"] the window [unsecuring? "from" : "to"] the frame."), src)
+ e_args.chat_feedback(SPAN_NOTICE("You [unsecuring? "unfasten" : "fasten"] the window [unsecuring? "from" : "to"] the frame."), src)
construction_state = unsecuring ? WINDOW_STATE_CROWBRARED_IN : WINDOW_STATE_SECURED_TO_FRAME
-/obj/structure/window/crowbar_act(obj/item/I, mob/user, flags, hint)
+/obj/structure/window/crowbar_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = TRUE
if (!considered_reinforced)
return
if (construction_state != WINDOW_STATE_CROWBRARED_IN && construction_state != WINDOW_STATE_SCREWED_TO_FLOOR)
return
- if (!use_crowbar(I, user, flags))
+ if (!use_crowbar(I, e_args, flags))
return
var/unsecuring = construction_state == WINDOW_STATE_CROWBRARED_IN
- user.action_feedback(SPAN_NOTICE("You pry [src] [unsecuring ? "out of" : "into"] the frame."), src)
+ e_args.chat_feedback(SPAN_NOTICE("You pry [src] [unsecuring ? "out of" : "into"] the frame."), src)
construction_state = unsecuring ? WINDOW_STATE_SCREWED_TO_FLOOR : WINDOW_STATE_CROWBRARED_IN
-/obj/structure/window/wrench_act(obj/item/I, mob/user, flags, hint)
+/obj/structure/window/wrench_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = TRUE
if (construction_state != WINDOW_STATE_UNSECURED)
- user.action_feedback(SPAN_WARNING("[src] has to be entirely unfastened from the floor before you can disasemble it!"))
+ e_args.chat_feedback(SPAN_WARNING("[src] has to be entirely unfastened from the floor before you can disasemble it!"))
return
- if (!use_wrench(I, user, flags))
+ if (!use_wrench(I, e_args, flags))
return
- user.action_feedback(SPAN_NOTICE("You disassemble [src]."), src)
+ e_args.chat_feedback(SPAN_NOTICE("You disassemble [src]."), src)
deconstruct(ATOM_DECONSTRUCT_DISASSEMBLED)
-/obj/structure/window/dynamic_tool_functions(obj/item/I, mob/user)
+/obj/structure/window/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
if (construction_state == WINDOW_STATE_UNSECURED)
. = list(
- TOOL_SCREWDRIVER = TOOL_HINT_SCREWING_WINDOW_FRAME,
- TOOL_WRENCH
+ TOOL_SCREWDRIVER = list(
+ "fasten frame" = dyntool_image_forward(TOOL_SCREWDRIVER),
+ ),
+ TOOL_WRENCH = list(
+ "deconstruct" = dyntool_image_backward(TOOL_WRENCH),
+ ),
)
else if (!considered_reinforced)
. = list(
- TOOL_SCREWDRIVER = TOOL_HINT_UNSCREWING_WINDOW_FRAME
+ TOOL_SCREWDRIVER = list(
+ "unfasten frame" = dyntool_image_backward(TOOL_SCREWDRIVER),
+ ),
)
else
switch (construction_state)
if (WINDOW_STATE_SCREWED_TO_FLOOR)
. = list(
- TOOL_SCREWDRIVER = TOOL_HINT_UNSCREWING_WINDOW_FRAME,
- TOOL_CROWBAR = TOOL_HINT_CROWBAR_WINDOW_IN
+ TOOL_SCREWDRIVER = list(
+ "unfasten frame" = dyntool_image_backward(TOOL_SCREWDRIVER),
+ ),
+ TOOL_CROWBAR = list(
+ "seat pane" = dyntool_image_forward(TOOL_CROWBAR),
+ ),
)
if (WINDOW_STATE_CROWBRARED_IN)
. = list(
- TOOL_SCREWDRIVER = TOOL_HINT_SCREWING_WINDOW_PANE,
- TOOL_CROWBAR = TOOL_HINT_CROWBAR_WINDOW_OUT
+ TOOL_SCREWDRIVER = list(
+ "fasten pane" = dyntool_image_forward(TOOL_SCREWDRIVER),
+ ),
+ TOOL_CROWBAR = list(
+ "unseat pane" = dyntool_image_backward(TOOL_CROWBAR),
+ ),
)
if (WINDOW_STATE_SECURED_TO_FRAME)
. = list(
- TOOL_SCREWDRIVER = TOOL_HINT_UNSCREWING_WINDOW_PANE
+ TOOL_SCREWDRIVER = list(
+ "unfasten pane" = dyntool_image_backward(TOOL_SCREWDRIVER),
+ ),
)
return merge_double_lazy_assoc_list(., ..())
-
-/obj/structure/window/dynamic_tool_image(function, hint)
- switch (hint)
- if (TOOL_HINT_CROWBAR_WINDOW_IN)
- return dyntool_image_forward(TOOL_CROWBAR)
- if (TOOL_HINT_CROWBAR_WINDOW_OUT)
- return dyntool_image_backward(TOOL_CROWBAR)
- if (TOOL_HINT_SCREWING_WINDOW_FRAME)
- return dyntool_image_forward(TOOL_SCREWDRIVER)
- if (TOOL_HINT_UNSCREWING_WINDOW_FRAME)
- return dyntool_image_backward(TOOL_SCREWDRIVER)
- if (TOOL_HINT_SCREWING_WINDOW_PANE)
- return dyntool_image_forward(TOOL_SCREWDRIVER)
- if (TOOL_HINT_UNSCREWING_WINDOW_PANE)
- return dyntool_image_backward(TOOL_SCREWDRIVER)
- return ..()
-
-
//This proc is used to update the icons of nearby windows.
/obj/structure/window/proc/update_nearby_icons()
update_appearance()
diff --git a/code/game/objects/systems/_system.dm b/code/game/objects/systems/_system.dm
new file mode 100644
index 000000000000..c47ad87d80ce
--- /dev/null
+++ b/code/game/objects/systems/_system.dm
@@ -0,0 +1,16 @@
+/**
+ * just the base type of object systems
+ *
+ * components are just terrible API, inefficient, and obnoxious sometimes
+ * /obj systems are the replacement for stuff like storage, cell slots, etc
+ *
+ * they are singletons on /obj level.
+ */
+/datum/object_system
+ abstract_type = /datum/object_system
+
+ /// owning object
+ var/obj/parent
+
+/datum/object_system/New(obj/parent)
+ src.parent = parent
diff --git a/code/game/objects/systems/cell_slot.dm b/code/game/objects/systems/cell_slot.dm
new file mode 100644
index 000000000000..bf4e4e138807
--- /dev/null
+++ b/code/game/objects/systems/cell_slot.dm
@@ -0,0 +1,100 @@
+/**
+ * cell slot
+ */
+/datum/object_system/cell_slot
+ /// held cell
+ var/obj/item/cell/cell
+ /// reserved - cell type accepted enum, for when we do large/medium/small/etc cells later.
+ var/cell_type
+ /// considered primary? if so, we get returned on get_cell()
+ var/primary = TRUE
+ /// allow inducer?
+ var/receive_inducer = FALSE
+ /// allow EMPs to hit?
+ var/receive_emp = FALSE
+ /// allow explosions to hit cell?
+ // todo: currently unused
+ var/recieve_explosion = FALSE
+ /// allow quick removal by clicking with hand?
+ var/remove_yank_offhand = FALSE
+ /// allow context menu removal?
+ var/remove_yank_context = FALSE
+ /// allow quick removal by using in hand?
+ var/remove_yank_inhand = FALSE
+ /// no-tool time for removal, if any
+ var/remove_yank_time = 0
+ /// tool behavior for removal, if any
+ var/remove_tool_behavior = null
+ /// tool time for removal, if any
+ var/remove_tool_time = 0
+ /// removal / insertion is discrete or loud
+ var/remove_is_discrete = TRUE
+ /// legacy
+ // todo: kill this
+ var/legacy_use_device_cells = FALSE
+
+/datum/object_system/cell_slot/proc/accepts_cell(obj/item/cell/cell)
+ return legacy_use_device_cells? istype(cell, /obj/item/cell/device) : TRUE
+
+/datum/object_system/cell_slot/proc/remove_cell(atom/new_loc)
+ if(isnull(cell))
+ return
+ . = cell
+ if(cell.loc != new_loc)
+ cell.forceMove(new_loc)
+ cell = null
+ parent.object_cell_slot_removed(., src)
+
+/datum/object_system/cell_slot/proc/insert_cell(obj/item/cell/cell)
+ if(!isnull(cell))
+ . = remove_cell(parent.drop_location())
+ src.cell = cell
+ if(cell.loc != parent)
+ cell.forceMove(parent)
+ parent.object_cell_slot_inserted(cell, src)
+
+/datum/object_system/cell_slot/proc/interaction_active(mob/user)
+ return parent.object_cell_slot_mutable(user, src)
+
+//? Hooks
+
+/**
+ * hook called on cell slot removal
+ */
+/obj/proc/object_cell_slot_removed(obj/item/cell/cell, datum/object_system/cell_slot/slot)
+ return
+
+/**
+ * hook called on cell slot insertion
+ */
+/obj/proc/object_cell_slot_inserted(obj/item/cell/cell, datum/object_system/cell_slot/slot)
+ return
+
+/**
+ * hook called to check if cell slot removal behavior is active
+ */
+/obj/proc/object_cell_slot_mutable(mob/user, datum/object_system/cell_slot/slot)
+ return TRUE
+
+//? Lazy wrappers for init
+
+/obj/proc/init_cell_slot(initial_cell_path)
+ RETURN_TYPE(/datum/object_system/cell_slot)
+ ASSERT(isnull(obj_cell_slot))
+ obj_cell_slot = new(src)
+ if(initial_cell_path)
+ obj_cell_slot.cell = new initial_cell_path
+ return obj_cell_slot
+
+/obj/proc/init_cell_slot_easy_tool(initial_cell_path, offhand_removal = TRUE, inhand_removal = FALSE)
+ RETURN_TYPE(/datum/object_system/cell_slot)
+ if(isnull(init_cell_slot(initial_cell_path)))
+ return
+ if(offhand_removal)
+ obj_cell_slot.remove_yank_offhand = TRUE
+ if(inhand_removal)
+ obj_cell_slot.remove_yank_inhand = FALSE
+ obj_cell_slot.remove_yank_context = TRUE
+ obj_cell_slot.remove_yank_time = 0
+ obj_cell_slot.legacy_use_device_cells = TRUE
+ return obj_cell_slot
diff --git a/code/game/rendering/legacy/radial.dm b/code/game/rendering/legacy/radial.dm
index a75870ad3a52..e470eeaba274 100644
--- a/code/game/rendering/legacy/radial.dm
+++ b/code/game/rendering/legacy/radial.dm
@@ -234,8 +234,12 @@ GLOBAL_LIST_EMPTY(radial_menus)
choices += id
choices_values[id] = E
if(new_choices[E])
- var/I = extract_image(new_choices[E])
+ var/image/I = extract_image(new_choices[E])
if(I)
+ //! perform fixup
+ I.plane = FLOAT_PLANE
+ I.layer = FLOAT_LAYER
+ //! end
choices_icons[id] = I
setup_menu(use_tooltips)
@@ -314,3 +318,13 @@ GLOBAL_LIST_EMPTY(radial_menus)
QDEL_NULL(menu)
GLOB.radial_menus -= uniqueid
return answer
+
+/datum/radial_menu/context_menu
+ /// host atom
+ var/atom/host
+ // todo: this needs such a drastic fucking refactor along with the rest of this file i'm going to scream
+
+/datum/radial_menu/context_menu/Destroy()
+ LAZYREMOVE(host.context_menus, current_user)
+ host = null
+ return ..()
diff --git a/code/game/vehicles/vehicle.dm b/code/game/vehicles/vehicle.dm
index 16c57f5a5618..14d16ccbd38f 100644
--- a/code/game/vehicles/vehicle.dm
+++ b/code/game/vehicles/vehicle.dm
@@ -187,5 +187,5 @@
mecha.cell.maxcharge -= min(20,mecha.cell.maxcharge)
return
-/obj/vehicle/get_cell()
+/obj/vehicle/get_cell(inducer)
return cell
diff --git a/code/modules/atmospherics/machinery/components/component.dm b/code/modules/atmospherics/machinery/components/component.dm
index e2ffac8c1685..91d8305d24dc 100644
--- a/code/modules/atmospherics/machinery/components/component.dm
+++ b/code/modules/atmospherics/machinery/components/component.dm
@@ -125,21 +125,23 @@
ui = new(user, src, tgui_interface)
ui.open()
-/obj/machinery/atmospherics/component/multitool_act(obj/item/I, mob/user, flags, hint)
+/obj/machinery/atmospherics/component/multitool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = ..()
if(.)
return
if(isnull(default_multitool_hijack))
return FALSE
if(hijack_require_exposed && is_hidden_underfloor())
- user.action_feedback(SPAN_WARNING("You can't reach the controls of [src] while it's covered by flooring."), src)
+ e_args.chat_feedback(SPAN_WARNING("You can't reach the controls of [src] while it's covered by flooring."), src)
return TRUE
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- hard_range = MESSAGE_RANGE_CONFIGURATION,
- visible_hard = SPAN_WARNING("[user] starts tinkering with [src] using their [I]!"),
+ range = MESSAGE_RANGE_CONFIGURATION,
+ visible = SPAN_WARNING("[e_args.performer] starts tinkering with [src] using their [I]!"),
+ otherwise_self = SPAN_WARNING("You start tinkering with [src] using your [I]..."),
)
- if(!do_after(user, default_multitool_hijack, src, mobility_flags = MOBILITY_CAN_USE))
+ if(!do_after(e_args.performer, default_multitool_hijack, src, mobility_flags = MOBILITY_CAN_USE, progress_instance = create_actor_progress_bar(e_args)))
return TRUE
- ui_interact(user)
+ // todo: uh, this obviously needs a wrapper
+ ui_interact(e_args.initiator)
return TRUE
diff --git a/code/modules/client/client.dm b/code/modules/client/client.dm
index 915bb8a0680d..57b2b0908a09 100644
--- a/code/modules/client/client.dm
+++ b/code/modules/client/client.dm
@@ -52,6 +52,10 @@
/// panic bunker is still resolving
var/panic_bunker_pending = FALSE
+ //? Context Menus
+ /// open context menu
+ var/datum/radial_menu/context_menu/context_menu
+
//? Rendering
/// Click catcher
var/atom/movable/screen/click_catcher/click_catcher
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 376641d2b80e..23dc06cc8aeb 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -359,6 +359,8 @@
return ..()
/client/Destroy()
+ // get rid of context menus
+ QDEL_NULL(context_menu)
// Unregister globals
GLOB.clients -= src
GLOB.directory -= ckey
diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm
index 156c2f2fefc5..51ff4fd5641e 100644
--- a/code/modules/fishing/aquarium/aquarium.dm
+++ b/code/modules/fishing/aquarium/aquarium.dm
@@ -118,22 +118,24 @@
update_appearance()
return TRUE
-/obj/structure/aquarium/dynamic_tool_functions(obj/item/I, mob/user)
+/obj/structure/aquarium/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
. = ..()
if(allow_unanchor)
- .[TOOL_WRENCH] = anchored? "anchor" : "unanchor"
+ LAZYSET(.[TOOL_WRENCH], anchored? "unanchor" : "anchor", anchored? dyntool_image_backward(TOOL_WRENCH) : dyntool_image_forward(TOOL_WRENCH))
-/obj/structure/aquarium/dynamic_tool_image(function, hint)
- switch(function)
- if(TOOL_WRENCH)
- return anchored? dyntool_image_backward(TOOL_WRENCH) : dyntool_image_forward(TOOL_WRENCH)
- return ..()
-
-/obj/structure/aquarium/wrench_act(obj/item/I, mob/user, flags, hint)
+/obj/structure/aquarium/wrench_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
if(!allow_unanchor)
return ..()
- if(use_wrench(I, user, delay = 4 SECONDS))
- user.visible_message(SPAN_NOTICE("[user] [anchored? "fastens [src] to the ground" : "unfastens [src] from the ground"]."), range = MESSAGE_RANGE_CONSTRUCTION)
+ if(use_wrench(I, e_args, delay = 4 SECONDS))
+ log_construction(e_args.performer, src, "fastened")
+ set_anchored(!anchored)
+ e_args.visible_feedback(
+ target = src,
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] [anchored? "fastens [src] to the ground" : "unfastens [src] from the ground"]."),
+ audible = SPAN_WARNING("You hear bolts being [anchored? "fastened" : "unfastened"]"),
+ otherwise_self = SPAN_NOTICE("You [anchored? "fasten" : "unfasten"] [src]."),
+ )
return TRUE
return ..()
diff --git a/code/modules/fishing/equipment/rod.dm b/code/modules/fishing/equipment/rod.dm
index d2e3ee710661..4af1ad639709 100644
--- a/code/modules/fishing/equipment/rod.dm
+++ b/code/modules/fishing/equipment/rod.dm
@@ -80,8 +80,11 @@
SStgui.update_uis(src)
update_icon()
-/obj/item/fishing_rod/on_attack_self(mob/user)
- reel(user)
+/obj/item/fishing_rod/on_attack_self(datum/event_args/actor/e_args)
+ . = ..()
+ if(.)
+ return
+ reel(e_args.performer)
/obj/item/fishing_rod/proc/reel(mob/user, atom/target)
// signal first for fishing minigame
diff --git a/code/modules/hardsuits/_rig.dm b/code/modules/hardsuits/_rig.dm
index e744deb0b9af..e7535c476e5f 100644
--- a/code/modules/hardsuits/_rig.dm
+++ b/code/modules/hardsuits/_rig.dm
@@ -117,7 +117,7 @@
var/sprint_slowdown_modifier = 0 // Sprinter module modifier.
-/obj/item/hardsuit/get_cell()
+/obj/item/hardsuit/get_cell(inducer)
return cell
/obj/item/hardsuit/examine(mob/user, dist)
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
index 82a7de071c85..2f4f13d4d817 100644
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -149,7 +149,7 @@
/obj/item/electronic_assembly/proc/check_interactivity(mob/user)
return ui_status(user, GLOB.physical_state) == UI_INTERACTIVE
-/obj/item/electronic_assembly/get_cell()
+/obj/item/electronic_assembly/get_cell(inducer)
return battery
// TGUI
diff --git a/code/modules/logging/logging.dm b/code/modules/logging/logging.dm
index 53ef337d6fec..dd3ca429729d 100644
--- a/code/modules/logging/logging.dm
+++ b/code/modules/logging/logging.dm
@@ -22,12 +22,22 @@
/**
* Log construction action
+ *
+ * todo: log initiator
*/
-/proc/log_construction(mob/user, atom/target, message)
- log_game("CONSTRUCTION: [key_name(user)] [COORD(user)] -> [target] [COORD(target)]: [message]")
+/proc/log_construction(datum/event_args/actor/e_args, atom/target, message)
+ log_game("CONSTRUCTION: [key_name(e_args.performer)] [COORD(e_args.performer)] -> [target] [COORD(target)]: [message]")
+
+/**
+ * log click - context menu
+ */
+/proc/log_click_context(datum/event_args/actor/e_args, atom/target, message)
+ log_click("CONTEXT: [key_name(e_args.initiator)][e_args.performer != e_args.initiator? " via [key_name(e_args.performer)]" : ""] -> [target] [AUDIT_COORD(target)]: [message]")
/**
* Log stack crafting
+ *
+ * todo: log initiator
*/
/proc/log_stackcrafting(mob/user, obj/item/stack/stack, name, amount, used, turf/where = get_turf(user))
log_game("STACKCRAFT: [key_name(user)] crafted [amount] of [name] with [used] of [stack] at [where]")
diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm
index 59b97d13a2f6..2ef6a7d542f5 100644
--- a/code/modules/mining/drilling/drill.dm
+++ b/code/modules/mining/drilling/drill.dm
@@ -54,7 +54,7 @@
)
RefreshParts()
-/obj/machinery/mining/drill/get_cell()
+/obj/machinery/mining/drill/get_cell(inducer)
return cell
/obj/machinery/mining/drill/process(delta_time)
diff --git a/code/modules/mob/inventory/items.dm b/code/modules/mob/inventory/items.dm
index aa38b32e5929..2abb30e06e34 100644
--- a/code/modules/mob/inventory/items.dm
+++ b/code/modules/mob/inventory/items.dm
@@ -101,6 +101,9 @@
if(zoom)
zoom() //binoculars, scope, etc
+ // close context menus
+ context_close()
+
return ((. & COMPONENT_ITEM_DROPPED_RELOCATE)? ITEM_RELOCATED_BY_DROPPED : NONE)
/**
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 80259f1ef5b4..740c7c9122a9 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -1071,7 +1071,7 @@
to_chat(src, "Module isn't activated")
installed_modules()
return 1
-
+
if(href_list["character_profile"])
if(!profile)
profile = new(src)
@@ -1403,7 +1403,7 @@
/mob/living/silicon/robot/is_sentient()
return braintype != BORG_BRAINTYPE_DRONE
-/mob/living/silicon/robot/get_cell()
+/mob/living/silicon/robot/get_cell(inducer)
return cell
/mob/living/silicon/robot/verb/robot_nom(var/mob/living/T in living_mobs(1))
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 5d0c48e5e963..1820d5514afe 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -27,6 +27,9 @@
update_Login_details()
world.update_status()
+ // get rid of old context menus
+ QDEL_NULL(client.context_menu)
+
client.images = list() //remove the images such as AIs being unable to see runes
client.screen = list() //remove hud items just in case
if(hud_used)
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 2e33634e47b5..4c9448b1322d 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -400,7 +400,7 @@ GLOBAL_LIST_EMPTY(apcs)
return ..()
-/obj/machinery/power/apc/get_cell()
+/obj/machinery/power/apc/get_cell(inducer)
return cell
// APCs are pixel-shifted, so they need to be updated.
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index f357acf48c95..27014e8c924c 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -48,7 +48,7 @@
/obj/item/cell/get_rating()
return rating
-/obj/item/cell/get_cell()
+/obj/item/cell/get_cell(inducer)
return src
/obj/item/cell/process(delta_time)
diff --git a/code/modules/power/lighting/lighting.dm b/code/modules/power/lighting/lighting.dm
index 2487b2f2cecf..dd33a01955c4 100644
--- a/code/modules/power/lighting/lighting.dm
+++ b/code/modules/power/lighting/lighting.dm
@@ -632,7 +632,7 @@ var/global/list/light_type_cache = list()
on = (s && status == LIGHT_OK)
update()
-/obj/machinery/light/get_cell()
+/obj/machinery/light/get_cell(inducer)
return cell
// examine verb
diff --git a/code/modules/projectiles/ammunition/ammo_casing.dm b/code/modules/projectiles/ammunition/ammo_casing.dm
index af3afd358323..cb3a53a228dc 100644
--- a/code/modules/projectiles/ammunition/ammo_casing.dm
+++ b/code/modules/projectiles/ammunition/ammo_casing.dm
@@ -48,21 +48,23 @@
setDir(pick(GLOB.cardinal)) //spin spent casings
update_icon()
-/obj/item/ammo_casing/screwdriver_act(obj/item/I, mob/user, flags, hint)
+/obj/item/ammo_casing/screwdriver_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = TRUE
if(!stored)
- user.action_feedback(SPAN_WARNING("There is no bullet in [src] to inscribe."), src)
+ e_args.chat_feedback(SPAN_WARNING("There is no bullet in [src] to inscribe."), src)
+ return
+ var/label_text = input(e_args.initiator, "Inscribe some text into [initial(stored.name)]", "Inscription", stored.name)
+ if(!e_args.performer.Adjacent(src))
return
- var/label_text = input(user, "Inscribe some text into [initial(stored.name)]", "Inscription", stored.name)
label_text = sanitize(label_text, MAX_NAME_LEN, extra = FALSE)
if(!label_text)
- user.action_feedback(SPAN_NOTICE("You scratch the inscription off of [initial(stored.name)]."), src)
+ e_args.chat_feedback(SPAN_NOTICE("You scratch the inscription off of [initial(stored.name)]."), src)
stored.name = initial(stored.name)
return
- user.action_feedback(SPAN_NOTICE("You inscribe [label_text] into \the [initial(stored.name)]."), src)
+ e_args.chat_feedback(SPAN_NOTICE("You inscribe [label_text] into \the [initial(stored.name)]."), src)
stored.name = "[initial(stored.name)] (\"[label_text]\")"
-/obj/item/ammo_casing/dynamic_tool_functions(obj/item/I, mob/user)
+/obj/item/ammo_casing/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
. = list(
TOOL_SCREWDRIVER = list(
"etch"
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 34a4fa476934..5d3d7e0718bf 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -46,7 +46,7 @@
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/gun/energy/get_cell()
+/obj/item/gun/energy/get_cell(inducer)
return power_supply
/obj/item/gun/energy/process(delta_time)
diff --git a/code/modules/projectiles/guns/magnetic/magnetic.dm b/code/modules/projectiles/guns/magnetic/magnetic.dm
index fd78f7aee3d1..ec96d1d51869 100644
--- a/code/modules/projectiles/guns/magnetic/magnetic.dm
+++ b/code/modules/projectiles/guns/magnetic/magnetic.dm
@@ -35,7 +35,7 @@
QDEL_NULL(capacitor)
. = ..()
-/obj/item/gun/magnetic/get_cell()
+/obj/item/gun/magnetic/get_cell(inducer)
return cell
/obj/item/gun/magnetic/process(delta_time)
diff --git a/code/modules/projectiles/magazines/smartmag.dm b/code/modules/projectiles/magazines/smartmag.dm
index 91abc9552a02..36f372c37b1f 100644
--- a/code/modules/projectiles/magazines/smartmag.dm
+++ b/code/modules/projectiles/magazines/smartmag.dm
@@ -129,7 +129,7 @@
attached_cell.emp_act(severity)
// Finds the cell for the magazine, used by rechargers
-/obj/item/ammo_magazine/smart/get_cell()
+/obj/item/ammo_magazine/smart/get_cell(inducer)
return attached_cell
// Removes energy from the attached cell when creating new bullets
diff --git a/code/modules/sculpting/sculpting_block.dm b/code/modules/sculpting/sculpting_block.dm
index 899e34465699..c3cf53eb47ee 100644
--- a/code/modules/sculpting/sculpting_block.dm
+++ b/code/modules/sculpting/sculpting_block.dm
@@ -128,53 +128,53 @@
initiate_sculpting(user, tool = I)
return CLICKCHAIN_DO_NOT_PROPAGATE
-/obj/structure/sculpting_block/wrench_act(obj/item/I, mob/user, flags, hint)
+/obj/structure/sculpting_block/wrench_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = ..()
if(.)
return
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- hard_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_hard = SPAN_NOTICE("[user] starts [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
- visible_self = SPAN_NOTICE("You start [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
- audible_hard = SPAN_WARNING("You hear bolts being [anchored? "unfastened" : "fastened"]."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] starts [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
+ audible = SPAN_WARNING("You hear bolts being [anchored? "unfastened" : "fastened"]."),
+ otherwise_self = SPAN_NOTICE("You start [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
)
- log_construction(user, src, "started [anchored? "unanchoring" : "anchoring"]")
- if(!use_wrench(I, user, flags, 3 SECONDS))
+ log_construction(e_args, src, "started [anchored? "unanchoring" : "anchoring"]")
+ if(!use_wrench(I, e_args, flags, 3 SECONDS))
return TRUE
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- hard_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_hard = SPAN_NOTICE("[user] finishes [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
- visible_self = SPAN_NOTICE("You finish [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
- audible_hard = SPAN_WARNING("You hear bolts [anchored? "falling out" : "clicking into place"]."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] finishes [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
+ audible = SPAN_WARNING("You hear bolts [anchored? "falling out" : "clicking into place"]."),
+ otherwise_self = SPAN_NOTICE("You finish [anchored? "unbolting [src] from the floor" : "bolting [src] to the floor"]."),
)
- log_construction(user, src, "[anchored? "unanchored" : "anchored"]")
+ log_construction(e_args, src, "[anchored? "unanchored" : "anchored"]")
set_anchored(!anchored)
return TRUE
-/obj/structure/sculpting_block/welder_act(obj/item/I, mob/user, flags, hint)
+/obj/structure/sculpting_block/welder_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
. = ..()
if(.)
return
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- hard_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_hard = SPAN_NOTICE("[user] starts slicing [src] apart."),
- visible_self = SPAN_NOTICE("You start slicing [src] apart."),
- audible_hard = SPAN_WARNING("You hear the sound of a welding torch being used on something metallic."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] starts slicing [src] apart."),
+ audible = SPAN_WARNING("You hear the sound of a welding torch being used on something metallic."),
+ otherwise_self = SPAN_NOTICE("You start slicing [src] apart."),
)
- log_construction(user, src, "started deconstructing")
- if(!use_welder(I, user, flags, 7 SECONDS, 3))
+ log_construction(e_args, src, "started deconstructing")
+ if(!use_welder(I, e_args, flags, 7 SECONDS, 3))
return TRUE
- user.visible_action_feedback(
+ e_args.visible_feedback(
target = src,
- hard_range = MESSAGE_RANGE_CONSTRUCTION,
- visible_hard = SPAN_NOTICE("[user] slices [src] apart."),
- visible_self = SPAN_NOTICE("You slice [src] apart."),
- audible_hard = SPAN_WARNING("You hear the sound of a welding torch moving back into open air, and a few pieces of metal falling apart."),
+ range = MESSAGE_RANGE_CONSTRUCTION,
+ visible = SPAN_NOTICE("[e_args.performer] slices [src] apart."),
+ audible = SPAN_WARNING("You hear the sound of a welding torch moving back into open air, and a few pieces of metal falling apart."),
+ otherwise_self = SPAN_NOTICE("You slice [src] apart."),
)
- log_construction(user, src, "deconstructed")
+ log_construction(e_args, src, "deconstructed")
set_anchored(!anchored)
deconstruct(ATOM_DECONSTRUCT_DISASSEMBLED)
return TRUE
@@ -183,24 +183,12 @@
. = ..()
material.place_sheet(drop_location(), 10)
-/obj/structure/sculpting_block/dynamic_tool_functions(obj/item/I, mob/user)
+/obj/structure/sculpting_block/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args, list/hint_images = list())
. = list()
- .[TOOL_WRENCH] = anchored? "unanchor" : "anchor"
- .[TOOL_WELDER] = "deconstruct"
+ LAZYSET(.[TOOL_WRENCH], anchored? "unanchor" : "anchor", anchored? dyntool_image_backward(TOOL_WRENCH) : dyntool_image_forward(TOOL_WRENCH))
+ LAZYSET(.[TOOL_WELDER], "deconstruct", dyntool_image_backward(TOOL_WELDER))
return merge_double_lazy_assoc_list(., ..())
-/obj/structure/sculpting_block/dynamic_tool_image(function, hint)
- . = ..()
- if(.)
- return
- switch(hint)
- if("unanchor")
- return dyntool_image_backward(TOOL_WRENCH)
- if("anchor")
- return dyntool_image_forward(TOOL_WRENCH)
- if("deconstruct")
- return dyntool_image_backward(TOOL_WELDER)
-
/**
* returns speed multiplier, or null if not tool
*/
diff --git a/code/modules/shieldgen/handheld_defuser.dm b/code/modules/shieldgen/handheld_defuser.dm
index edf509c4bda7..2c0d19c2b871 100644
--- a/code/modules/shieldgen/handheld_defuser.dm
+++ b/code/modules/shieldgen/handheld_defuser.dm
@@ -21,7 +21,7 @@
STOP_PROCESSING(SSobj, src)
. = ..()
-/obj/item/shield_diffuser/get_cell()
+/obj/item/shield_diffuser/get_cell(inducer)
return cell
/obj/item/shield_diffuser/process(delta_time)
diff --git a/code/modules/tools/_tool_system.dm b/code/modules/tools/_tool_system.dm
index b9e2030c2d5d..e2d49b770d6a 100644
--- a/code/modules/tools/_tool_system.dm
+++ b/code/modules/tools/_tool_system.dm
@@ -1,3 +1,6 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
/**
* ? Atom Tool API
*
@@ -12,7 +15,7 @@
*
* intended api for static tool usage:
*
- * - override necessary _act for that tool type
+ * * override necessary _act for that tool type
* .../function_act(...)
* if(use_(...))
* # success code
@@ -21,11 +24,11 @@
* return TRUE // halt attack chain
*
* intended api for dynamic tool usage:
- * - override dynamic_tool_functions() to return the functions and minimal qualities for a user
- * - override dynamic_tool_act() if needed, otherwise it will simply go into tool_act
- * - override dynamic_tool_image() to return the image to render for a specific tool function for radials
- * - realistically, you just need to override dynamic_tool_functions.
- * - if you don't override dynamic_tool_image you are a lemming and it'll probabl be ugly.
+ * * override dynamic_tool_query() to return the functions and minimal qualities for a user
+ * * override dynamic_tool_act() if needed, otherwise it will simply go into tool_act
+ * * override dynamic_tool_image() to return the image to render for a specific tool function for radials
+ * * realistically, you just need to override dynamic_tool_query.
+ * * if you don't override dynamic_tool_image you are a lemming and it'll probabl be ugly.
*
* It's That Simple (tm)!
*
@@ -41,47 +44,53 @@
* warning: this proc is not necessarily called only within clickcode.
*
* @params
- * - I - the item
- * - user - the user
- * - clickchain_flags - the clickchain flags given
- * - function - forced function - used in automation
- * - hint - forced hint - used in automation
- * - reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise.
+ * * I - the item
+ * * user - the user
+ * * clickchain_flags - the clickchain flags given
+ * * function - forced function - used in automation
+ * * hint - forced hint - used in automation
+ * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise.
*/
-/atom/proc/tool_interaction(obj/item/I, mob/user, clickchain_flags, function, hint, datum/callback/reachability_check)
+/atom/proc/tool_interaction(obj/item/I, datum/event_args/actor/clickchain/e_args, clickchain_flags, function, hint, datum/callback/reachability_check)
SHOULD_NOT_OVERRIDE(TRUE)
- return _tool_interaction_entrypoint(I, user, clickchain_flags, function, hint, reachability_check)
+ return _tool_interaction_entrypoint(I, e_args, clickchain_flags, function, hint, reachability_check)
-/atom/proc/_tool_interaction_entrypoint(obj/item/provided_item, mob/user, clickchain_flags, function, hint, datum/callback/reachability_check)
+/atom/proc/_tool_interaction_entrypoint(obj/item/provided_item, datum/event_args/actor/clickchain/e_args, clickchain_flags, function, hint, datum/callback/reachability_check)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
if(isnull(reachability_check))
if(clickchain_flags & CLICKCHAIN_TOOL_ACT)
// provided_item should never be null
- reachability_check = CALLBACK(user, TYPE_PROC_REF(/atom/movable, Reachability), src, null, provided_item.reach, provided_item)
+ // we inject our default reachability check if tool act is set by tool attack chain
+ // otherwise we just ignore it if it wasn't specified
+ reachability_check = CALLBACK(e_args.performer, TYPE_PROC_REF(/atom/movable, Reachability), src, null, provided_item.reach, provided_item)
if(reachability_check && !reachability_check.Invoke())
return NONE
// from click chain
if(provided_item)
if(function)
// automation, just go
- return _dynamic_tool_act(provided_item, user, function, TOOL_OP_AUTOPILOT | TOOL_OP_REAL, hint)
+ return _dynamic_tool_act(provided_item, e_args, function, TOOL_OP_AUTOPILOT | TOOL_OP_REAL, hint)
// used in clickchain
- var/list/possibilities = dynamic_tool_functions(provided_item, user)
+ // as of now, format is:
+ // function = hint OR list(hint, ...)
+ // hint images is passed in so it can be prebuilt by components
+ // more info is in comment of dynamic_tool_query
+ var/list/possibilities = dynamic_tool_query(provided_item, e_args)
if(!length(possibilities) || (provided_item.tool_locked == TOOL_LOCKING_STATIC))
// no dynamic tool functionality, or dynamic functionality disabled, route normally.
function = provided_item.tool_behaviour()
if(!function)
return NONE
- return _tool_act(provided_item, user, function, TOOL_OP_REAL)
+ return _tool_act(provided_item, e_args, function, TOOL_OP_REAL)
+ var/list/functions = provided_item.tool_query(e_args, src)
// enumerate
- var/list/functions = provided_item.tool_query(user, src)
if((provided_item.tool_locked == TOOL_LOCKING_AUTO) && (length(functions) == 1))
// use first function
function = functions[1]
- if(!(function in possibilities))
+ if(isnull(possibilities[function]))
// not found, route normally
- return _tool_act(provided_item, user, function, TOOL_OP_REAL)
+ return _tool_act(provided_item, e_args, function, TOOL_OP_REAL)
else
for(var/i in possibilities)
if(functions[i])
@@ -92,60 +101,66 @@
function = provided_item.tool_behaviour()
if(!function)
return NONE
- return _tool_act(provided_item, user, function, TOOL_OP_REAL)
+ return _tool_act(provided_item, e_args, function, TOOL_OP_REAL)
+ // possibilities is now filtered
// everything in possibilities is valid for the tool
var/list/transformed = list()
+ // check if we have function locked already
if(!function)
// we're about to sleep; if we're already breaking from this, maybe like, don't
- if(INTERACTING_WITH_FOR(user, src, INTERACTING_FOR_DYNAMIC_TOOL))
+ if(INTERACTING_WITH_FOR(e_args.initiator, src, INTERACTING_FOR_DYNAMIC_TOOL))
return CLICKCHAIN_DO_NOT_PROPAGATE
// if we didn't pick function already
- for(var/i in possibilities)
- // is there only one hint?
- var/list/associated = possibilities[i]
- if(associated && (!islist(associated) || (length(associated) == 1)))
- // yes there is!
- associated = islist(associated)? associated[1] : associated
+ for(var/potential_function in possibilities)
+ var/list/potential_hints = possibilities[potential_function]
+ var/image/radial_render
+ var/radial_text
+ if(length(potential_hints) == 1)
+ radial_text = potential_hints[1]
+ radial_render = potential_hints[radial_text] || dyntool_image_neutral(potential_function)
else
- associated = null
- var/image/I = dynamic_tool_image(i, associated)
- I.maptext = MAPTEXT_CENTER(associated || i)
- I.maptext_x = -16
- I.maptext_y = 32
- I.maptext_width = 64
- transformed[i] = I
+ radial_text = potential_function
+ radial_render = dyntool_image_neutral(potential_function)
+ radial_render.maptext = MAPTEXT_CENTER(radial_text)
+ radial_render.maptext_x = -16
+ radial_render.maptext_y = 32
+ radial_render.maptext_width = 64
+ transformed[potential_function] = radial_render
// todo: radial menu at some point should be made to automatically close when they click something else.
- START_INTERACTING_WITH(user, src, INTERACTING_FOR_DYNAMIC_TOOL)
- function = show_radial_menu(user, src, transformed, custom_check = reachability_check)
- STOP_INTERACTING_WITH(user, src, INTERACTING_FOR_DYNAMIC_TOOL)
+ // todo: the initiator/performer pattern doesn't work well with radial menu and interacting with
+ // todo: because there's no semantics for mutexing the performer vs the initator
+ // todo: in the future, we are going to want to get a proper mutex up for tool interactions
+ START_INTERACTING_WITH(e_args.initiator, src, INTERACTING_FOR_DYNAMIC_TOOL)
+ function = show_radial_menu(e_args.initiator, src, transformed, custom_check = reachability_check)
+ STOP_INTERACTING_WITH(e_args.initiator, src, INTERACTING_FOR_DYNAMIC_TOOL)
if(!function || (reachability_check && !reachability_check.Invoke()))
return CLICKCHAIN_DO_NOT_PROPAGATE
// determine hint
var/list/hints = possibilities[function]
if(!islist(hints))
// is a direct hint or null
- return _dynamic_tool_act(provided_item, user, function, TOOL_OP_REAL, hints)
+ return _dynamic_tool_act(provided_item, e_args, function, TOOL_OP_REAL, hints)
else if(length(hints) <= 1)
// no hint, or only one hint
- return _dynamic_tool_act(provided_item, user, function, TOOL_OP_REAL, length(hints)? hints[1] : null)
+ return _dynamic_tool_act(provided_item, e_args, function, TOOL_OP_REAL, length(hints)? hints[1] : null)
// we're about to sleep; if we're already breaking from this, maybe like, don't
- if(INTERACTING_WITH_FOR(user, src, INTERACTING_FOR_DYNAMIC_TOOL))
+ if(INTERACTING_WITH_FOR(e_args.initiator, src, INTERACTING_FOR_DYNAMIC_TOOL))
return CLICKCHAIN_DO_NOT_PROPAGATE
transformed.len = 0
for(var/i in hints)
- var/image/I = dynamic_tool_image(function, i)
- I.maptext = MAPTEXT_CENTER(i)
- I.maptext_x = -16
- I.maptext_y = 32
- I.maptext_width = 64
- transformed[i] = I
- START_INTERACTING_WITH(user, src, INTERACTING_FOR_DYNAMIC_TOOL)
- hint = show_radial_menu(user, src, transformed, custom_check = reachability_check)
- STOP_INTERACTING_WITH(user, src, INTERACTING_FOR_DYNAMIC_TOOL)
+ var/image/radial_render = hints[i] || dyntool_image_neutral(function)
+ radial_render.maptext = MAPTEXT_CENTER(i)
+ radial_render.maptext_x = -16
+ radial_render.maptext_y = 32
+ radial_render.maptext_width = 64
+ transformed[i] = radial_render
+ START_INTERACTING_WITH(e_args.initiator, src, INTERACTING_FOR_DYNAMIC_TOOL)
+ hint = show_radial_menu(e_args.initiator, src, transformed, custom_check = reachability_check)
+ STOP_INTERACTING_WITH(e_args.initiator, src, INTERACTING_FOR_DYNAMIC_TOOL)
if(!hint || (reachability_check && !reachability_check.Invoke()))
return CLICKCHAIN_DO_NOT_PROPAGATE
// use hint
- return _dynamic_tool_act(provided_item, user, function, TOOL_OP_REAL, hint) | CLICKCHAIN_DO_NOT_PROPAGATE
+ return _dynamic_tool_act(provided_item, e_args, function, TOOL_OP_REAL, hint) | CLICKCHAIN_DO_NOT_PROPAGATE
else
// in the future, we might have situations where clicking something with an empty hand
// yet having organs that server as built-in tools can do something with
@@ -153,98 +168,99 @@
return NONE
//! Primary Tool API
-/atom/proc/_tool_act(obj/item/I, mob/user, function, flags, hint)
+/atom/proc/_tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint)
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
- SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT, I, user, function, flags, hint)
- return tool_act(I, user, function, flags, hint)
+ if((. = SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT, I, e_args, function, flags, hint)) & CLICKCHAIN_COMPONENT_SIGNAL_HANDLED)
+ return . & ~(CLICKCHAIN_COMPONENT_SIGNAL_HANDLED)
+ return tool_act(I, e_args, function, flags, hint)
/**
* primary proc to be used when calling an interaction with a tool with an atom
*
* everything in this proc and procs it calls:
- * - should not verify that the item is on the user
- * - can, but doesn't need to verify that the item has the function in question (assumed it does)
- * - should not require a user to run (do not runtime without user)
- * - should handle functions with helpers in tihs file whenever possibl
+ * * should not verify that the item is on the user
+ * * can, but doesn't need to verify that the item has the function in question (assumed it does)
+ * * should not require a user to run (do not runtime without user)
+ * * should handle functions with helpers in tihs file whenever possibl
*
* default behaviour: route the call to _act, which interrupts the melee attack chain if it returns TRUE.
*
* @params
- * - I - the tool in question
- * - user - the user in question, if they exist
- * - function - the tool function used
- * - flags - tool operation flags
- * - hint - the operation hint, if the calling system is the dynamic tool system.
+ * * I - the tool in question
+ * * user - the user in question, if they exist
+ * * function - the tool function used
+ * * flags - tool operation flags
+ * * hint - the operation hint, if the calling system is the dynamic tool system.
*/
-/atom/proc/tool_act(obj/item/I, mob/user, function, flags, hint)
+/atom/proc/tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint)
switch(function)
if(TOOL_CROWBAR)
- return crowbar_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return crowbar_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
if(TOOL_MULTITOOL)
- return multitool_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return multitool_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
if(TOOL_SCREWDRIVER)
- return screwdriver_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return screwdriver_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
if(TOOL_WRENCH)
- return wrench_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return wrench_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
if(TOOL_WELDER)
- return welder_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return welder_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
if(TOOL_WIRECUTTER)
- return wirecutter_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return wirecutter_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
if(TOOL_ANALYZER)
- return analyzer_act(I, user, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
+ return analyzer_act(I, e_args, flags, hint)? CLICKCHAIN_DO_NOT_PROPAGATE : NONE
//? Add more tool_acts as necessary.
/**
* standard use tool
*
* @params
- * - function - tool function
- * - I - the tool
- * - user - the person using it
- * - flags - tool operation flags
- * - delay - how long it'll take to use the tool
- * - cost - optional; cost multiplier to the default cost of 1 per second.
- * - usage - optional; usage flags for tool speed/quality checks.
+ * * function - tool function
+ * * I - the tool
+ * * user - the person using it
+ * * flags - tool operation flags
+ * * delay - how long it'll take to use the tool
+ * * cost - optional; cost multiplier to the default cost of 1 per second.
+ * * usage - optional; usage flags for tool speed/quality checks.
*/
-/atom/proc/use_tool_standard(function, obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool(function, I, user, flags, delay, cost, usage)
+/atom/proc/use_tool_standard(function, obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool(function, I, e_args, flags, delay, cost, usage)
/**
* primary proc called by wrappers to use a tool on us
*
* @params
- * - function - tool function
- * - I - the tool
- * - user - the person using it
- * - flags - tool operation flags
- * - delay - how long it'll take to use the tool
- * - cost - optional; cost multiplier to the default cost of 1 per second.
- * - usage - optional; usage flags for tool speed/quality checks.
- * - volume - optional; volume override
+ * * function - tool function
+ * * I - the tool
+ * * user - the person using it
+ * * flags - tool operation flags
+ * * delay - how long it'll take to use the tool
+ * * cost - optional; cost multiplier to the default cost of 1 per second.
+ * * usage - optional; usage flags for tool speed/quality checks.
+ * * volume - optional; volume override
*/
-/atom/proc/use_tool(function, obj/item/I, mob/user, flags, delay, cost = 1, usage, volume)
+/atom/proc/use_tool(function, obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost = 1, usage, volume)
SHOULD_NOT_OVERRIDE(TRUE)
- var/quality = I.tool_check(function, user, src, flags, usage)
+ var/quality = I.tool_check(function, e_args, src, flags, usage)
if(!quality)
return FALSE
- var/speed = I.tool_speed(function, user, src, flags, usage)
+ var/speed = I.tool_speed(function, e_args, src, flags, usage)
//! this currently makes tools more efficient if you have more toolspeed.
//! this is probably a bad thing.
// todo: automatically adjust cost to rectify this.
// todo: tool_cost()? potentially invert cost multiplier automagically, or invert it in this proc.
delay = delay * speed
- if(!I.using_as_tool(function, flags, user, src, delay, cost, usage))
+ if(!I.using_as_tool(function, flags, e_args, src, delay, cost, usage))
return FALSE
- I.tool_feedback_start(function, flags, user, src, delay, cost, usage, volume)
- if(!do_after(user, delay, src))
- I.used_as_tool(function, flags, user, src, delay, cost, usage, FALSE)
- I.tool_feedback_end(function, flags, user, src, delay, cost, usage, FALSE, volume)
+ I.tool_feedback_start(function, flags, e_args, src, delay, cost, usage, volume)
+ if(!do_after(e_args.performer, delay, src, progress_instance = create_actor_progress_bar(e_args, delay)))
+ I.used_as_tool(function, flags, e_args, src, delay, cost, usage, FALSE)
+ I.tool_feedback_end(function, flags, e_args, src, delay, cost, usage, FALSE, volume)
return FALSE
- if(!I.used_as_tool(function, flags, user, src, delay, cost, usage, TRUE))
- I.tool_feedback_end(function, flags, user, src, delay, cost, usage, FALSE, volume)
+ if(!I.used_as_tool(function, flags, e_args, src, delay, cost, usage, TRUE))
+ I.tool_feedback_end(function, flags, e_args, src, delay, cost, usage, FALSE, volume)
return FALSE
- I.tool_feedback_end(function, flags, user, src, delay, cost, usage, TRUE, volume)
+ I.tool_feedback_end(function, flags, e_args, src, delay, cost, usage, TRUE, volume)
return TRUE
//! Dynamic Tool API
@@ -252,25 +268,25 @@
* returns a list of behaviours that can be used on us in our current state
* the behaviour may be associated to a list of "hints" for multiple possible actions per behaviour.
* the hint should be human readable.
- * associating directly to a single hint is allowed.
+ * hints can / should be associated to images for graphics, otherwise defaults to dyntool neutral images
*
* **warning**: by default, the provided list is mutable
* if you're caching your own list, make sure to return cache.Copy()!
*
* @params
- * - I - the tool used, if any
- * - user - the user, if any
+ * * I - the tool used, if any
+ * * user - the user, if any
*/
-/atom/proc/dynamic_tool_functions(obj/item/I, mob/user)
- // todo: signal
- return list()
+/atom/proc/dynamic_tool_query(obj/item/I, datum/event_args/actor/clickchain/e_args)
+ . = list()
+ SEND_SIGNAL(src, COMSIG_ATOM_TOOL_QUERY, I, e_args, .)
-/atom/proc/_dynamic_tool_act(obj/item/I, mob/user, function, flags, hint)
+/atom/proc/_dynamic_tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint)
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
flags |= TOOL_OP_DYNAMIC
- SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT, I, user, function, flags, hint)
- return dynamic_tool_act(I, user, function, flags, hint)
+ SEND_SIGNAL(src, COMSIG_ATOM_TOOL_ACT, I, e_args, function, flags, hint)
+ return dynamic_tool_act(I, e_args, function, flags, hint)
/**
* called when we are acted on by the dynamic tool system
@@ -279,33 +295,11 @@
* this must return a set of clickchain flags!
*
* @params
- * - I - the tool used
- * - user - the user, if any
- * - function - the tool behaviour used
- * - flags - tool operation flags
- * - hint - the hint of what operation to do
+ * * I - the tool used
+ * * user - the user, if any
+ * * function - the tool behaviour used
+ * * flags - tool operation flags
+ * * hint - the hint of what operation to do
*/
-/atom/proc/dynamic_tool_act(obj/item/I, mob/user, function, flags, hint)
- return tool_act(I, user, function, flags, hint)
-
-/**
- * builds the image used for the radial icon
- *
- * WARNING: If you use tool **and** hint, you need to implement a hintless, or return to base to use the default.
- *
- * @params
- * - function - the tool behaviour
- * - hint - the context provided when you want to implement multiple actions for a tool
- */
-/atom/proc/dynamic_tool_image(function, hint)
- return dyntool_image_neutral(function)
-
-//! Dynamic Tools - default images
-/proc/dyntool_image_neutral(function)
- return image('icons/screen/radial/tools/generic.dmi', icon_state = _dyntool_image_states[function] || "unknown")
-
-/proc/dyntool_image_forward(function)
- return image('icons/screen/radial/tools/generic.dmi', icon_state = "[_dyntool_image_states[function] || "unknown"]_up")
-
-/proc/dyntool_image_backward(function)
- return image('icons/screen/radial/tools/generic.dmi', icon_state = "[_dyntool_image_states[function] || "unknown"]_down")
+/atom/proc/dynamic_tool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, function, flags, hint)
+ return tool_act(I, e_args, function, flags, hint)
diff --git a/code/modules/tools/items.dm b/code/modules/tools/items.dm
index f35c57adfd94..a0c0bd467781 100644
--- a/code/modules/tools/items.dm
+++ b/code/modules/tools/items.dm
@@ -1,3 +1,6 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
/**
* item tool API: allows items to be one or more types of generic tool-functionalities
* with arbitrary tool speeds and qualities, while allowing the item to hook usages.
@@ -28,12 +31,12 @@
* that's where things like skill checks are done.
*
* @params
- * - user - person using tool, can be null
+ * - e_args - person using tool, can be null
* - target - atom being used on, can be null
* - flags - tool operation flags
* - usage - what we're being used for, bitfield
*/
-/obj/item/proc/tool_query(mob/user, atom/target, flags, usage)
+/obj/item/proc/tool_query(datum/event_args/actor/clickchain/e_args, atom/target, flags, usage)
RETURN_TYPE(/list)
. = list()
// if normal tool behavior
@@ -43,7 +46,7 @@
if(tool_override)
. |= tool_override
for(var/i in .)
- .[i] = tool_quality_transform(.[i], user, target, flags, usage)
+ .[i] = tool_quality_transform(.[i], e_args, target, flags, usage)
/**
* checks for a tool function
@@ -54,16 +57,16 @@
*
* @params
* - function - tool function enum
- * - user - person using tool, if any
+ * - e_args - person using tool, if any
* - target - atom tool being used on, if any
* - flags - tool operation flags
* - usage - what we're being used for, bitfield
*/
-/obj/item/proc/tool_check(function, mob/user, atom/target, flags, usage)
+/obj/item/proc/tool_check(function, datum/event_args/actor/clickchain/e_args, atom/target, flags, usage)
ASSERT(function)
if(tool_override && tool_override[function])
- return tool_quality_transform(tool_override[function], user, target, flags, usage)
- return (function == tool_behaviour)? tool_quality_transform(tool_quality, user, target, flags, usage) : null
+ return tool_quality_transform(tool_override[function], e_args, target, flags, usage)
+ return (function == tool_behaviour)? tool_quality_transform(tool_quality, e_args, target, flags, usage) : null
/**
* transforms tool quality according to a user's skill
@@ -71,12 +74,12 @@
* @params
* - original - original quality
*
- * - user - user, if any
+ * - e_args - actor data, if any
* - target - target, if any
* - flags - tool operation flags
* - usage - what we're being used for, bitfield
*/
-/obj/item/proc/tool_quality_transform(original, user, target, flags, usage)
+/obj/item/proc/tool_quality_transform(original, datum/event_args/actor/clickchain/e_args, target, flags, usage)
return original
/**
@@ -88,12 +91,12 @@
*
* @params
* - function - tool function enum
- * - user - person using tool, if any
+ * - e_args - actor data for person using tool, if any
* - target - atom tool being used on, if any
* - flags - tool operation flags
* - usage - what we're being used for, bitfield
*/
-/obj/item/proc/tool_speed(function, mob/user, atom/target, flags, usage)
+/obj/item/proc/tool_speed(function, datum/event_args/actor/clickchain/e_args, atom/target, flags, usage)
SHOULD_CALL_PARENT(TRUE)
return (flags & TOOL_OP_INSTANT)? 0 : tool_speed
@@ -113,14 +116,14 @@
*
* @params
* - function - tool function enum; if null, defaults to static tool behaviour.
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - flags - tool operation flags
* - usage - what we're being used for
*/
-/obj/item/proc/tool_quality(function = tool_behaviour(), mob/user, atom/target, flags, usage)
+/obj/item/proc/tool_quality(function = tool_behaviour(), datum/event_args/actor/clickchain/e_args, atom/target, flags, usage)
// this is just a wrapper, the only difference is function is automatically provided.
- return tool_check(function, user, target, flags, usage)
+ return tool_check(function, e_args, target, flags, usage)
/**
* called when we start being used as a tool
@@ -129,13 +132,13 @@
* @params
* - function - tool function enum
* - flags - tool operation flags
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - time - approximated duration of the action in deciseconds
* - cost - cost multiplier
* - usage - usage flags, if any
*/
-/obj/item/proc/using_as_tool(function, flags, mob/user, atom/target, time, cost, usage)
+/obj/item/proc/using_as_tool(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage)
SHOULD_CALL_PARENT(TRUE)
return TRUE
@@ -146,14 +149,14 @@
* @params
* - function - tool function enum
* - flags - tool operation flags
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - time - duration of the action in deciseconds
* - cost - cost multiplier
* - usage - usage flags, if any
* - success - was it successful?
*/
-/obj/item/proc/used_as_tool(function, flags, mob/user, atom/target, time, cost, usage, success)
+/obj/item/proc/used_as_tool(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage, success)
SHOULD_CALL_PARENT(TRUE)
return TRUE
@@ -163,17 +166,17 @@
* @params
* - function - tool function enum
* - flags - tool operation flags
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - time - duration of the action in deciseconds
* - cost - cost multiplier
* - usage - usage flags, if any
* - volume - volume for sounds
*/
-/obj/item/proc/tool_feedback_start(function, flags, mob/user, atom/target, time, cost, usage, volume)
+/obj/item/proc/tool_feedback_start(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage, volume)
SHOULD_CALL_PARENT(TRUE)
if(!(flags & TOOL_OP_NO_STANDARD_AUDIO))
- standard_tool_feedback_sound(function, flags, user, target, time, cost, usage, volume)
+ standard_tool_feedback_sound(function, flags, e_args, target, time, cost, usage, volume)
/**
* standard feedback for ending a tool usage
@@ -181,7 +184,7 @@
* @params
* - function - tool function enum
* - flags - tool operation flags
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - time - duration of the action in deciseconds
* - cost - cost multiplier
@@ -189,10 +192,10 @@
* - success - was it successful?
* - volume - volume for sounds
*/
-/obj/item/proc/tool_feedback_end(function, flags, mob/user, atom/target, time, cost, usage, success, volume)
+/obj/item/proc/tool_feedback_end(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage, success, volume)
SHOULD_CALL_PARENT(TRUE)
if(!(flags & TOOL_OP_NO_STANDARD_AUDIO))
- standard_tool_feedback_sound(function, flags, user, target, time, cost, usage, success)
+ standard_tool_feedback_sound(function, flags, e_args, target, time, cost, usage, success)
/**
* plays tool sound
@@ -200,7 +203,7 @@
* @params
* - function - tool function enum
* - flags - tool operation flags
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - time - duration of the action in deciseconds
* - cost - cost multiplier
@@ -208,14 +211,14 @@
* - success - was it successful? null if we're just starting
* - volume - volume for sounds
*/
-/obj/item/proc/standard_tool_feedback_sound(function, flags, mob/user, atom/target, time, cost, usage, success, volume = 50)
+/obj/item/proc/standard_tool_feedback_sound(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage, success, volume = 50)
if(isnull(success))
// starting
- playsound(src, tool_sound(function, flags, user, target, time, cost, usage, success), volume, TRUE)
+ playsound(src, tool_sound(function, flags, e_args, target, time, cost, usage, success), volume, TRUE)
else
// finishing
if(time >= MIN_TOOL_SOUND_DELAY)
- playsound(src, tool_sound(function, flags, user, target, time, cost, usage, success), volume, TRUE)
+ playsound(src, tool_sound(function, flags, e_args, target, time, cost, usage, success), volume, TRUE)
/**
* gets sound to play on tool usage
@@ -223,14 +226,14 @@
* @params
* - function - tool function enum
* - flags - tool operation flags
- * - user - person using tool, if any
+ * - e_args - actor data of who's using the tool
* - target - atom tool being used on, if any
* - time - duration of the action in deciseconds
* - cost - cost multiplier
* - usage - usage flags, if any
* - success - was it successful? null if we're just starting
*/
-/obj/item/proc/tool_sound(function, flags, mob/user, atom/target, time, cost, usage, success)
+/obj/item/proc/tool_sound(function, flags, datum/event_args/actor/clickchain/e_args, atom/target, time, cost, usage, success)
if(tool_sound)
return tool_sound
// return default
diff --git a/code/modules/tools/visuals.dm b/code/modules/tools/visuals.dm
new file mode 100644
index 000000000000..90516413ec42
--- /dev/null
+++ b/code/modules/tools/visuals.dm
@@ -0,0 +1,8 @@
+/proc/dyntool_image_neutral(function)
+ return image('icons/screen/radial/tools/generic.dmi', icon_state = _dyntool_image_states[function] || "unknown")
+
+/proc/dyntool_image_forward(function)
+ return image('icons/screen/radial/tools/generic.dmi', icon_state = "[_dyntool_image_states[function] || "unknown"]_up")
+
+/proc/dyntool_image_backward(function)
+ return image('icons/screen/radial/tools/generic.dmi', icon_state = "[_dyntool_image_states[function] || "unknown"]_down")
diff --git a/code/modules/tools/wrappers.dm b/code/modules/tools/wrappers.dm
index 233664c9b3d8..0d8535103e99 100644
--- a/code/modules/tools/wrappers.dm
+++ b/code/modules/tools/wrappers.dm
@@ -1,3 +1,6 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
//! ON GOD, READ tool_system.dm's use_tool_standard to learn how to use these!
/**
@@ -9,7 +12,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/crowbar_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/crowbar_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -21,14 +24,14 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_crowbar(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_CROWBAR, I, user, flags, delay, cost, usage)
+/atom/proc/use_crowbar(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_CROWBAR, I, e_args, flags, delay, cost, usage)
/**
* Called when a wrench is used on us.
@@ -39,7 +42,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/wrench_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/wrench_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -51,14 +54,14 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_wrench(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_WRENCH, I, user, flags, delay, cost, usage)
+/atom/proc/use_wrench(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_WRENCH, I, e_args, flags, delay, cost, usage)
/**
* Called when a welder is used on us.
@@ -69,7 +72,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/welder_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/welder_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -81,14 +84,14 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_welder(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_WELDER, I, user, flags, delay, cost, usage)
+/atom/proc/use_welder(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_WELDER, I, e_args, flags, delay, cost, usage)
/**
* Called when a pair of wirecutters is used on us.
@@ -99,7 +102,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/wirecutter_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/wirecutter_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -111,14 +114,14 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_wirecutter(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_WIRECUTTER, I, user, flags, delay, cost, usage)
+/atom/proc/use_wirecutter(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_WIRECUTTER, I, e_args, flags, delay, cost, usage)
/**
* Called when a screwdriver is used on us.
@@ -129,7 +132,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/screwdriver_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/screwdriver_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -141,14 +144,14 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_screwdriver(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_SCREWDRIVER, I, user, flags, delay, cost, usage)
+/atom/proc/use_screwdriver(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_SCREWDRIVER, I, e_args, flags, delay, cost, usage)
/**
* Called when a analyzer is used on us.
@@ -159,7 +162,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/analyzer_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/analyzer_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -171,14 +174,14 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_analyzer(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_ANALYZER, I, user, flags, delay, cost, usage)
+/atom/proc/use_analyzer(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_ANALYZER, I, e_args, flags, delay, cost, usage)
/**
@@ -190,7 +193,7 @@
* - flags - tool operation flags
* - hint - operation hint, if using dynamic tool system
*/
-/atom/proc/multitool_act(obj/item/I, mob/user, flags, hint)
+/atom/proc/multitool_act(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, hint)
return FALSE
/**
@@ -202,12 +205,12 @@
*
* @params
* - I - the item
- * - user - the user, if any
+ * - e_args - clickchain data, if any
* - flags - tool operation flags
* - delay - how long this should take
* - cost - multiplier for cost, standard tool "cost" is 1 per second of usage.
* - usage - usage flags for skill system checks.
*/
-/atom/proc/use_multitool(obj/item/I, mob/user, flags, delay, cost, usage)
- return use_tool_standard(TOOL_MULTITOOL, I, user, flags, delay, cost, usage)
+/atom/proc/use_multitool(obj/item/I, datum/event_args/actor/clickchain/e_args, flags, delay, cost, usage)
+ return use_tool_standard(TOOL_MULTITOOL, I, e_args, flags, delay, cost, usage)
diff --git a/code/modules/tools/z_legacy.dm b/code/modules/tools/z_legacy.dm
index 4f67004a445d..37ef1820cb2d 100644
--- a/code/modules/tools/z_legacy.dm
+++ b/code/modules/tools/z_legacy.dm
@@ -1,3 +1,6 @@
+//* This file is explicitly licensed under the MIT license. *//
+//* Copyright (c) 2023 Citadel Station developers. *//
+
//! these wrappers are used for things that still check tools on attackby()
//! new usages are prohibited.