diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index c428e1e7dd53..5c6f14c887eb 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -3,7 +3,6 @@
#define COLOR_INPUT_DISABLED "#F0F0F0"
#define COLOR_INPUT_ENABLED "#D3B5B5"
-//BeginWS
#define COLOR_DARKMODE_DARKBACKGROUND "#383838"
#define COLOR_DARKMODE_BACKGROUND "#272727"
#define COLOR_DARKMODE_HEADER "#ffffff"
@@ -23,7 +22,6 @@
#define WOOD_COLOR_BLACK "#332521"
#define WOOD_COLOR_CHOCOLATE "#543C30"
#define WOOD_COLOR_YELLOW "#E3994E"
-//EndWS
#define COLOR_WHITE "#FFFFFF"
#define COLOR_OFF_WHITE "#FFF5ED"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 6c6849724bde..8299d389fb18 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -160,6 +160,7 @@
var/hitsound_type = PROJECTILE_HITSOUND_NON_LIVING
///volume wanted for being hit
var/hitsound_volume = 50
+
/**
* Called when an atom is created in byond (built in engine proc)
*
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index f3802e381c55..9ae7034366dc 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1057,6 +1057,12 @@
else if(can_be_firemanned(target))
fireman_carry(target)
+/mob/living/carbon/human/limb_attack_self()
+ var/obj/item/bodypart/arm = hand_bodyparts[active_hand_index]
+ if(arm)
+ arm.attack_self(src)
+ return ..()
+
/mob/living/carbon/human/MouseDrop(mob/over)
. = ..()
if(ishuman(over))
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d475891fc28f..7e246dd2d7ac 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -565,6 +565,15 @@
var/msg = "[src] makes eye contact with you."
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), examined_mob, msg), 3)
+/**
+ * Called by using Activate Held Object with an empty hand/limb
+ *
+ * Does nothing by default. The intended use is to allow limbs to call their
+ * own attack_self procs. It is up to the individual mob to override this
+ * parent and actually use it.
+ */
+/mob/proc/limb_attack_self()
+ return
///Can this mob resist (default FALSE)
/mob/proc/can_resist()
@@ -622,6 +631,8 @@
if(I)
I.attack_self(src)
update_inv_hands()
+ return
+ limb_attack_self()
/mob/verb/do_unique_action()
set name = "Do Unique Action"
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index d9d3d6b0b717..5d6fac852d51 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -13,6 +13,10 @@
var/list/items_list = list()// I would use contents, but they shuffle on every activation/deactivation leading to interface inconsistencies.
/// You can use this var for item path, it would be converted into an item on New().
var/obj/item/active_item
+ /// Sound played when extending
+ var/extend_sound = 'sound/mecha/mechmove03.ogg'
+ /// Sound played when retracting
+ var/retract_sound = 'sound/mecha/mechmove03.ogg'
/obj/item/organ/cyberimp/arm/Initialize()
. = ..()
@@ -68,18 +72,34 @@
to_chat(user, "You modify [src] to be installed on the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.")
update_appearance()
+/obj/item/organ/cyberimp/arm/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+ . = ..()
+ var/side = zone == BODY_ZONE_R_ARM? RIGHT_HANDS : LEFT_HANDS
+ hand = owner.hand_bodyparts[side]
+ if(hand)
+ RegisterSignal(hand, COMSIG_ITEM_ATTACK_SELF, PROC_REF(ui_action_click)) //If the limb gets an attack-self, open the menu. Only happens when hand is empty
+ RegisterSignal(M, COMSIG_KB_MOB_DROPITEM_DOWN, PROC_REF(dropkey)) //We're nodrop, but we'll watch for the drop hotkey anyway and then stow if possible.
+
/obj/item/organ/cyberimp/arm/Remove(mob/living/carbon/M, special = 0)
Retract()
+ if(hand)
+ UnregisterSignal(hand, COMSIG_ITEM_ATTACK_SELF)
+ UnregisterSignal(M, COMSIG_KB_MOB_DROPITEM_DOWN)
..()
-/obj/item/organ/cyberimp/arm/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- if(prob(15/severity) && owner)
- to_chat(owner, "[src] is hit by EMP!")
- // give the owner an idea about why his implant is glitching
- Retract()
+/**
+ * Called when the mob uses the "drop item" hotkey
+ *
+ * Items inside toolset implants have TRAIT_NODROP, but we can still use the drop item hotkey as a
+ * quick way to store implant items. In this case, we check to make sure the user has the correct arm
+ * selected, and that the item is actually owned by us, and then we'll hand off the rest to Retract()
+**/
+/obj/item/organ/cyberimp/arm/proc/dropkey(mob/living/carbon/host)
+ if(!host)
+ return //How did we even get here
+ if(hand != host.hand_bodyparts[host.active_hand_index])
+ return //wrong hand
+ Retract()
/obj/item/organ/cyberimp/arm/proc/Retract()
if(!active_item || (active_item in src))
@@ -89,13 +109,9 @@
"[active_item] snaps back into your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.",
"You hear a short mechanical noise.")
- if(istype(active_item, /obj/item/assembly/flash/armimplant))
- var/obj/item/assembly/flash/F = active_item
- F.set_light(0)
-
owner.transferItemToLoc(active_item, src, TRUE)
+ playsound(get_turf(owner), retract_sound, 50, TRUE)
active_item = null
- playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, TRUE)
/obj/item/organ/cyberimp/arm/proc/Extend(obj/item/item)
if(!(item in src))
@@ -135,7 +151,7 @@
owner.visible_message("[owner] extends [active_item] from [owner.p_their()] [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.",
"You extend [active_item] from your [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm.",
"You hear a short mechanical noise.")
- playsound(get_turf(owner), 'sound/mecha/mechmove03.ogg', 50, TRUE)
+ playsound(get_turf(owner), extend_sound, 50, TRUE)
/obj/item/organ/cyberimp/arm/ui_action_click()
if((organ_flags & ORGAN_FAILING) || (!active_item && !contents.len))
@@ -161,6 +177,14 @@
else
Retract()
+/obj/item/organ/cyberimp/arm/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ if(prob(15/severity) && owner)
+ to_chat(owner, "[src] is hit by EMP!")
+ // give the owner an idea about why his implant is glitching
+ Retract()
/obj/item/organ/cyberimp/arm/gun/emp_act(severity)
. = ..()
@@ -225,7 +249,6 @@
desc = "A cybernetic implant that allows the user to project a healing beam from their hand."
items_to_create = list(/obj/item/gun/medbeam)
-
/obj/item/organ/cyberimp/arm/flash
name = "integrated high-intensity photon projector" //Why not
desc = "An integrated projector mounted onto a user's arm that is able to be used as a powerful flash."
@@ -277,4 +300,4 @@
name = "power cord implant"
desc = "An internal power cord hooked up to a battery. Useful if you run on volts."
items_to_create = list(/obj/item/apc_powercord)
- zone = "l_arm"
+ zone = BODY_ZONE_L_ARM
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index ae6f1cf43d39..f623bb67606b 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -2,6 +2,7 @@
/obj/item/organ/cyberimp
name = "cybernetic implant"
desc = "A state-of-the-art implant that improves a baseline's functionality."
+ icon = 'icons/obj/implants/implant.dmi'
status = ORGAN_ROBOTIC
organ_flags = ORGAN_SYNTHETIC
var/implant_color = "#FFFFFF"
@@ -17,8 +18,6 @@
add_overlay(overlay)
return ..()
-
-
//[[[[BRAIN]]]]
/obj/item/organ/cyberimp/brain
diff --git a/icons/mob/inhands/weapons/axes_lefthand.dmi b/icons/mob/inhands/weapons/axes_lefthand.dmi
index 810455a611bd..3b95d17cdc78 100644
Binary files a/icons/mob/inhands/weapons/axes_lefthand.dmi and b/icons/mob/inhands/weapons/axes_lefthand.dmi differ
diff --git a/icons/obj/implants/implant.dmi b/icons/obj/implants/implant.dmi
new file mode 100644
index 000000000000..31bd68db0caf
Binary files /dev/null and b/icons/obj/implants/implant.dmi differ
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index d94097ddf2e5..8f2566a98f8a 100644
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ