diff --git a/citadel.dme b/citadel.dme
index d1f0fff9911..19499b43a5a 100644
--- a/citadel.dme
+++ b/citadel.dme
@@ -580,6 +580,7 @@
#include "code\datums\components\crafting\recipes\recipes_primal.dm"
#include "code\datums\components\crafting\recipes\recipes_robot.dm"
#include "code\datums\components\crafting\recipes\recipes_weapon_and_ammo.dm"
+#include "code\datums\components\items\wielding.dm"
#include "code\datums\components\riding\riding_filter.dm"
#include "code\datums\components\riding\riding_handler.dm"
#include "code\datums\components\riding\mob\_mob.dm"
diff --git a/code/__DEFINES/_flags/item_flags.dm b/code/__DEFINES/_flags/item_flags.dm
index 3b2dd9b4a32..41981374f47 100644
--- a/code/__DEFINES/_flags/item_flags.dm
+++ b/code/__DEFINES/_flags/item_flags.dm
@@ -30,6 +30,8 @@
#define ITEM_THROW_UNCATCHABLE (1<<15)
/// we cannot be used a tool on click, no matter what
#define ITEM_NO_TOOL_ATTACK (1<<16)
+/// we're dual wielded - multi-wielding coming later tm
+#define ITEM_MULTIHAND_WIELDED (1<<17)
DEFINE_BITFIELD(item_flags, list(
BITFIELD(IN_INVENTORY),
@@ -41,6 +43,7 @@ DEFINE_BITFIELD(item_flags, list(
BITFIELD(IN_STORAGE),
BITFIELD(ITEM_THROW_UNCATCHABLE),
BITFIELD(ITEM_NO_TOOL_ATTACK),
+ BITFIELD(ITEM_MULTIHAND_WIELDED),
))
//! Flags for the clothing_flags var on /obj/item
diff --git a/code/__DEFINES/dcs/signals/signals_twohand.dm b/code/__DEFINES/dcs/signals/signals_twohand.dm
deleted file mode 100644
index bc6a148a3c2..00000000000
--- a/code/__DEFINES/dcs/signals/signals_twohand.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- *! ## two_handed Signals. Format:
- * * When the signal is called: (signal arguments)
- * * All signals send the source datum of the signal as the first argument
- */
-
-///from base of datum/component/two_handed/proc/wield(mob/living/carbon/user): (/mob/user)
-////#define COMSIG_TWOHANDED_WIELD "twohanded_wield"
- ////#define COMPONENT_TWOHANDED_BLOCK_WIELD (1<<0)
-///from base of datum/component/two_handed/proc/unwield(mob/living/carbon/user): (/mob/user)
-////#define COMSIG_TWOHANDED_UNWIELD "twohanded_unwield"
diff --git a/code/_globals/lists/keybindings.dm b/code/_globals/lists/keybindings.dm
index 414c396026b..ec1b114b83e 100644
--- a/code/_globals/lists/keybindings.dm
+++ b/code/_globals/lists/keybindings.dm
@@ -1,3 +1,4 @@
+// todo: why not just all keybindings by tyyypeohrightpreferences
GLOBAL_LIST_EMPTY(classic_keybinding_list_by_key)
GLOBAL_LIST_EMPTY(hotkey_keybinding_list_by_key)
GLOBAL_LIST_EMPTY(keybindings_by_name)
diff --git a/code/datums/components/items/wielding.dm b/code/datums/components/items/wielding.dm
new file mode 100644
index 00000000000..991b1c2c35f
--- /dev/null
+++ b/code/datums/components/items/wielding.dm
@@ -0,0 +1,109 @@
+// todo: can element this by usign 3 signals instead of 2, one to receive a keybind signal.
+/datum/component/wielding
+ /// hands needed
+ var/hands
+ /// lazylist
+ var/list/obj/item/offhand/wielding/offhands
+ /// wielded user
+ var/mob/wielder
+ /// callback on wield
+ var/datum/callback/on_wield
+ /// callback on unwield
+ var/datum/callback/on_unwield
+
+/datum/component/wielding/Initialize(hands = 2, datum/callback/on_wield, datum/callback/on_unwield)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+ if((. = ..()) & COMPONENT_INCOMPATIBLE)
+ return
+ src.hands = hands
+ src.on_wield = on_wield
+ src.on_unwield = on_unwield
+
+/datum/component/wielding/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/signal_examine)
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/signal_dropped)
+
+/datum/component/wielding/UnregisterFromParent()
+ unwield()
+ . = ..()
+ UnregisterSignal(parent, list(
+ COMSIG_PARENT_EXAMINE,
+ COMSIG_ITEM_DROPPED
+ ))
+
+/datum/component/wielding/proc/signal_examine(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+ examine_list += SPAN_NOTICE("[parent] seems to be able to be used with [hands] hands. Press your \"Wield Item\" keybind to toggle wielding.")
+
+/datum/component/wielding/proc/signal_dropped(datum/source, mob/user, flags, atom/newloc)
+ unwield()
+
+/datum/component/wielding/proc/wield(mob/wielder)
+ if(src.wielder)
+ return
+ var/possible = wielder.get_number_of_hands()
+ var/wanted = hands - 1
+ if(possible < wanted)
+ return
+ var/list/obj/item/offhand/wielding/made = list()
+ for(var/i in 1 to wanted)
+ var/obj/item/offhand/wielding/creating = wielder.allocate_offhand(/obj/item/offhand/wielding)
+ if(!creating)
+ QDEL_LIST(made)
+ return
+ creating.host = src
+ made += creating
+ offhands = made
+ src.wielder = wielder
+ to_chat(src.wielder, SPAN_WARNING("You start wielding [parent] with [hands == 2? "both" : "[hands]"] hands."))
+ post_wield(src.wielder, hands)
+
+/datum/component/wielding/proc/post_wield(mob/user, hands)
+ if(on_wield)
+ on_wield.Invoke(user, hands)
+ var/obj/item/I = parent
+ I.on_wield(user, hands)
+
+/datum/component/wielding/proc/unwield(gcing)
+ if(!wielder)
+ return
+ if(offhands)
+ var/list/old = offhands
+ offhands = null
+ QDEL_LIST(old)
+ var/obj/item/I = parent
+ var/mob/unwielding = wielder
+ I.item_flags &= ~ITEM_MULTIHAND_WIELDED
+ if(wielder && !gcing)
+ to_chat(wielder, SPAN_WARNING("You stop wielding [parent] with [hands == 2? "both" : "[hands]"] hands."))
+ wielder = null
+ post_unwield(unwielding, hands)
+
+/datum/component/wielding/proc/post_unwield(mob/user, hands)
+ if(on_unwield)
+ on_unwield.Invoke(user, hands)
+ var/obj/item/I = parent
+ I.on_unwield(user, hands)
+
+/datum/component/wielding/proc/offhand_destroyed(obj/item/offhand/wielding/I)
+ unwield()
+/obj/item/offhand/wielding
+ name = "wielding offhand"
+ desc = "You shouldn't be able to see this."
+ /// host
+ var/datum/component/wielding/host
+
+/obj/item/offhand/wielding/Destroy()
+ if(host)
+ host.offhand_destroyed(src)
+ host = null
+ return ..()
+
+// item procs
+/obj/item/proc/on_wield(mob/user, hands)
+ return
+
+/obj/item/proc/on_unwield(mob/user, hands)
+ return
diff --git a/code/datums/components/riding/riding_filter.dm b/code/datums/components/riding/riding_filter.dm
index ec5c0614d2b..ff1af33ee6c 100644
--- a/code/datums/components/riding/riding_filter.dm
+++ b/code/datums/components/riding/riding_filter.dm
@@ -239,7 +239,7 @@
/**
* grab all necessary offhands
- * qdel the entire list on failure.
+ * qdel the entire list on failure.T
*/
/obj/item/offhand/riding
diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm
index 50a4098d3c7..e1ffc1cfa60 100644
--- a/code/modules/admin/view_variables/topic_basic.dm
+++ b/code/modules/admin/view_variables/topic_basic.dm
@@ -59,11 +59,9 @@
return
var/list/names = list()
var/list/componentsubtypes = subtypesof(/datum/component)
- names += "---Components---"
names += componentsubtypes
- names += "---Elements---"
names += subtypesof(/datum/element)
- var/result = input(usr, "Choose a component/element to add","better know what ur fuckin doin pal") as null|anything in names
+ var/result = tgui_input_list(usr, "Choose a component/element to add", "better know what ur fuckin doin pal", names)
if(!usr || !result || result == "---Components---" || result == "---Elements---")
return
if(QDELETED(src))
@@ -81,6 +79,6 @@
datumname = "element"
target._AddElement(lst)
log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].")
- message_admins("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(src)].")
+ message_admins("[key_name_admin(usr)] has added [result] [datumname] to [target] ([ADMIN_VV(target)]).")
if(href_list[VV_HK_CALLPROC])
usr.client.callproc_datum(target)
diff --git a/code/modules/keybindings/keybind/mob.dm b/code/modules/keybindings/keybind/mob.dm
index a9503400247..7738229ed3e 100644
--- a/code/modules/keybindings/keybind/mob.dm
+++ b/code/modules/keybindings/keybind/mob.dm
@@ -104,6 +104,30 @@
M.mode()
return TRUE
+/datum/keybinding/mob/multihand_wield
+ hotkey_keys = list("ShiftX")
+ classic_keys = list("X")
+ name = "multihand_wield"
+ full_name = "Wield Item"
+ description = "Wield an item with two, or more hands (if it's supported)."
+
+/datum/keybinding/mob/multihand_wield/down(client/user)
+ // yes, get component is asinine sometimes
+ // i don't care though, this is such a small feature
+ var/obj/item/I = user.mob.get_active_held_item()
+ if(!I)
+ to_chat(user, SPAN_WARNING("You are not holding anything to wield."))
+ return FALSE
+ var/datum/component/wielding/comp = I.GetComponent(/datum/component/wielding)
+ if(!comp)
+ to_chat(user, SPAN_WARNING("That can't be wielded."))
+ return FALSE
+ if(comp.wielder)
+ comp.unwield()
+ else
+ comp.wield(user.mob)
+ return TRUE
+
/datum/keybinding/mob/say
hotkey_keys = list("T", "F3")
name = "say"
diff --git a/code/modules/mining/kinetic_crusher.dm b/code/modules/mining/kinetic_crusher.dm
index e68145870c8..2bc2b04bc7d 100644
--- a/code/modules/mining/kinetic_crusher.dm
+++ b/code/modules/mining/kinetic_crusher.dm
@@ -85,14 +85,6 @@
return TRUE
return !ishuman(victim) && !issilicon(victim)
-/// triggered on wield of two handed item
-/obj/item/kinetic_crusher/proc/on_wield(obj/item/source, mob/user)
- wielded = TRUE
-
-/// triggered on unwield of two handed item
-/obj/item/kinetic_crusher/proc/on_unwield(obj/item/source, mob/user)
- wielded = FALSE
-
/obj/item/kinetic_crusher/examine(mob/living/user)
. = ..()
. += "Mark a large creature with the destabilizing force, then hit them in melee to do [force + detonation_damage] damage."
diff --git a/code/modules/preferences/preference_setup/keybindings/keybindings.dm b/code/modules/preferences/preference_setup/keybindings/keybindings.dm
index ab2962da0fe..57e92944ee4 100644
--- a/code/modules/preferences/preference_setup/keybindings/keybindings.dm
+++ b/code/modules/preferences/preference_setup/keybindings/keybindings.dm
@@ -1,3 +1,4 @@
+// todo: convert and make looking up keys BY BIND possible
/datum/preferences
/// Custom Keybindings
var/list/key_bindings = list()