diff --git a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
index 87ec5cdaab..63972a98aa 100644
--- a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
+++ b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
@@ -1,19 +1,3 @@
-/datum/crafting_recipe/pin_removal
- name = "Pin Removal"
- result = /obj/item/gun
- reqs = list(/obj/item/gun = 1)
- parts = list(/obj/item/gun = 1)
- tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
- time = 50
- category = CAT_WEAPONRY
- subcategory = CAT_OTHER
-
-/datum/crafting_recipe/pin_removal/check_requirements(mob/user, list/collected_requirements)
- var/obj/item/gun/G = collected_requirements[/obj/item/gun][1]
- if (G.no_pin_required || !G.pin)
- return FALSE
- return TRUE
-
/datum/crafting_recipe/strobeshield
name = "Strobe Shield"
result = /obj/item/shield/riot/flash
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 06bf298454..2759a8f8d6 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -1,5 +1,6 @@
#define DUALWIELD_PENALTY_EXTRA_MULTIPLIER 1.4
+#define FIRING_PIN_REMOVAL_DELAY 50
/obj/item/gun
name = "gun"
@@ -115,23 +116,28 @@
QDEL_NULL(chambered)
return ..()
-/obj/item/gun/CheckParts(list/parts_list)
- ..()
- var/obj/item/gun/G = locate(/obj/item/gun) in contents
- if(G)
- G.forceMove(loc)
- QDEL_NULL(G.pin)
- visible_message("[G] can now fit a new pin, but the old one was destroyed in the process.", null, null, 3)
- qdel(src)
-
/obj/item/gun/examine(mob/user)
. = ..()
- if(no_pin_required)
- return
- if(pin)
- . += "It has \a [pin] installed."
- else
- . += "It doesn't have a firing pin installed, and won't fire."
+ if(!no_pin_required)
+ if(pin)
+ . += "It has \a [pin] installed."
+ . += "[pin] looks like it could be removed with some tools."
+ else
+ . += "It doesn't have a firing pin installed, and won't fire."
+
+ if(gun_light)
+ . += "It has \a [gun_light] [can_flashlight ? "" : "permanently "]mounted on it."
+ if(can_flashlight) //if it has a light and this is false, the light is permanent.
+ . += "[gun_light] looks like it can be unscrewed from [src]."
+ else if(can_flashlight)
+ . += "It has a mounting point for a seclite."
+
+ if(bayonet)
+ . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it."
+ if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent.
+ . += "[bayonet] looks like it can be unscrewed from [src]."
+ else if(can_bayonet)
+ . += "It has a bayonet lug on it."
/obj/item/gun/equipped(mob/living/user, slot)
. = ..()
@@ -239,7 +245,7 @@
return
if(weapon_weight == WEAPON_HEAVY && user.get_inactive_held_item())
- to_chat(user, "You need both hands free to fire \the [src]!")
+ to_chat(user, "You need both hands free to fire [src]!")
return
user.DelayNextAction()
@@ -417,12 +423,12 @@
if(!gun_light)
if(!user.transferItemToLoc(I, src))
return
- to_chat(user, "You click \the [S] into place on \the [src].")
+ to_chat(user, "You click [S] into place on [src].")
if(S.on)
set_light(0)
- gun_light = S
+ set_gun_light(S)
update_gunlight(user)
- alight = new /datum/action/item_action/toggle_gunlight(src)
+ alight = new(src)
if(loc == user)
alight.Grant(user)
else if(istype(I, /obj/item/kitchen/knife))
@@ -431,27 +437,133 @@
return ..()
if(!user.transferItemToLoc(I, src))
return
- to_chat(user, "You attach \the [K] to the front of \the [src].")
+ to_chat(user, "You attach [K] to [src]'s bayonet lug.")
bayonet = K
update_icon()
- else if(I.tool_behaviour == TOOL_SCREWDRIVER)
- if(gun_light)
- var/obj/item/flashlight/seclite/S = gun_light
- to_chat(user, "You unscrew the seclite from \the [src].")
- gun_light = null
- S.forceMove(get_turf(user))
- update_gunlight(user)
- S.update_brightness(user)
- QDEL_NULL(alight)
- if(bayonet)
- to_chat(user, "You unscrew the bayonet from \the [src].")
- var/obj/item/kitchen/knife/K = bayonet
- K.forceMove(get_turf(user))
- bayonet = null
- update_icon()
else
return ..()
+/obj/item/gun/screwdriver_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(.)
+ return
+ if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ if((can_flashlight && gun_light) && (can_bayonet && bayonet)) //give them a choice instead of removing both
+ var/list/possible_items = list(gun_light, bayonet)
+ var/obj/item/item_to_remove = input(user, "Select an attachment to remove", "Attachment Removal") as null|obj in possible_items
+ if(!item_to_remove || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ return remove_gun_attachment(user, I, item_to_remove)
+
+ else if(gun_light && can_flashlight) //if it has a gun_light and can_flashlight is false, the flashlight is permanently attached.
+ return remove_gun_attachment(user, I, gun_light, "unscrewed")
+
+ else if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed
+ return remove_gun_attachment(user, I, bayonet, "unfix")
+
+ else if(pin && user.is_holding(src))
+ user.visible_message(span_warning("[user] attempts to remove [pin] from [src] with [I]."),
+ span_notice("You attempt to remove [pin] from [src]. (It will take [DisplayTimeText(FIRING_PIN_REMOVAL_DELAY)].)"), null, 3)
+ if(I.use_tool(src, user, FIRING_PIN_REMOVAL_DELAY, volume = 50))
+ if(!pin) //check to see if the pin is still there, or we can spam messages by clicking multiple times during the tool delay
+ return
+ user.visible_message(span_notice("[pin] is pried out of [src] by [user], destroying the pin in the process."),
+ span_warning("You pry [pin] out with [I], destroying the pin in the process."), null, 3)
+ QDEL_NULL(pin)
+ return TRUE
+
+/obj/item/gun/welder_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(.)
+ return
+ if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ if(pin && user.is_holding(src))
+ user.visible_message(span_warning("[user] attempts to remove [pin] from [src] with [I]."),
+ span_notice("You attempt to remove [pin] from [src]. (It will take [DisplayTimeText(FIRING_PIN_REMOVAL_DELAY)].)"), null, 3)
+ if(I.use_tool(src, user, FIRING_PIN_REMOVAL_DELAY, 5, volume = 50))
+ if(!pin) //check to see if the pin is still there, or we can spam messages by clicking multiple times during the tool delay
+ return
+ user.visible_message(span_notice("[pin] is spliced out of [src] by [user], melting part of the pin in the process."),
+ span_warning("You splice [pin] out of [src] with [I], melting part of the pin in the process."), null, 3)
+ QDEL_NULL(pin)
+ return TRUE
+
+/obj/item/gun/wirecutter_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(.)
+ return
+ if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ if(pin && user.is_holding(src))
+ user.visible_message(span_warning("[user] attempts to remove [pin] from [src] with [I]."),
+ span_notice("You attempt to remove [pin] from [src]. (It will take [DisplayTimeText(FIRING_PIN_REMOVAL_DELAY)].)"), null, 3)
+ if(I.use_tool(src, user, FIRING_PIN_REMOVAL_DELAY, volume = 50))
+ if(!pin) //check to see if the pin is still there, or we can spam messages by clicking multiple times during the tool delay
+ return
+ user.visible_message(span_notice("[pin] is ripped out of [src] by [user], mangling the pin in the process."),
+ span_warning("You rip [pin] out of [src] with [I], mangling the pin in the process."), null, 3)
+ QDEL_NULL(pin)
+ return TRUE
+
+/obj/item/gun/proc/remove_gun_attachment(mob/living/user, obj/item/tool_item, obj/item/item_to_remove, removal_verb)
+ if(tool_item)
+ tool_item.play_tool_sound(src)
+ to_chat(user, span_notice("You [removal_verb ? removal_verb : "remove"] [item_to_remove] from [src]."))
+ item_to_remove.forceMove(drop_location())
+
+ if(Adjacent(user) && !issilicon(user))
+ user.put_in_hands(item_to_remove)
+
+ if(item_to_remove == bayonet)
+ return clear_bayonet()
+ else if(item_to_remove == gun_light)
+ return clear_gunlight()
+
+/obj/item/gun/proc/clear_bayonet()
+ if(!bayonet)
+ return
+ bayonet = null
+ update_appearance()
+ return TRUE
+
+/obj/item/gun/proc/clear_gunlight()
+ if(!gun_light)
+ return
+ var/obj/item/flashlight/seclite/removed_light = gun_light
+ set_gun_light(null)
+ update_gunlight()
+ removed_light.update_brightness()
+ QDEL_NULL(alight)
+ return TRUE
+
+/**
+ * Swaps the gun's seclight, dropping the old seclight if it has not been qdel'd.
+ *
+ * Returns the former gun_light that has now been replaced by this proc.
+ * Arguments:
+ * * new_light - The new light to attach to the weapon. Can be null, which will mean the old light is removed with no replacement.
+ */
+/obj/item/gun/proc/set_gun_light(obj/item/flashlight/seclite/new_light)
+ // Doesn't look like this should ever happen? We're replacing our old light with our old light?
+ if(gun_light == new_light)
+ CRASH("Tried to set a new gun light when the old gun light was also the new gun light.")
+
+ . = gun_light
+
+ // If there's an old gun light that isn't being QDELETED, detatch and drop it to the floor.
+ if(!QDELETED(gun_light))
+ if(gun_light.loc == src)
+ gun_light.forceMove(get_turf(src))
+
+ // If there's a new gun light to be added, attach and move it to the gun.
+ if(new_light)
+ if(new_light.loc != src)
+ new_light.forceMove(src)
+
+ gun_light = new_light
+
/obj/item/gun/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_scope_zoom))
zoom(user, user.dir)
@@ -606,9 +718,16 @@
user.client.view_size.zoomIn()
/obj/item/gun/handle_atom_del(atom/A)
+ if(A == pin)
+ pin = null
if(A == chambered)
chambered = null
update_icon()
+ if(A == bayonet)
+ clear_bayonet()
+ if(A == gun_light)
+ clear_gunlight()
+ return ..()
/obj/item/gun/proc/getinaccuracy(mob/living/user, bonus_spread, stamloss)
return 0 // Replacement TBD: Exponential curved aim instability system.
@@ -642,3 +761,6 @@
. = recoil
if(user && !user.has_gravity())
. = recoil*5
+
+#undef FIRING_PIN_REMOVAL_DELAY
+#undef DUALWIELD_PENALTY_EXTRA_MULTIPLIER