diff --git a/code/datums/components/crafting/craft.dm b/code/datums/components/crafting/craft.dm
index 5c21801c14..b5c7f34c27 100644
--- a/code/datums/components/crafting/craft.dm
+++ b/code/datums/components/crafting/craft.dm
@@ -64,29 +64,48 @@
-
-/datum/component/personal_crafting/proc/check_contents(datum/crafting_recipe/R, list/contents)
+/**
+ * Check that the contents of the recipe meet the requirements.
+ *
+ * user: The /mob that initated the crafting.
+ * R: The /datum/crafting_recipe being attempted.
+ * contents: List of items to search for R's reqs.
+ */
+/datum/component/personal_crafting/proc/check_contents(mob/user, datum/crafting_recipe/R, list/contents)
+ var/list/item_instances = contents["instances"]
contents = contents["other"]
- main_loop:
- for(var/A in R.reqs)
- var/needed_amount = R.reqs[A]
- for(var/B in contents)
- if(ispath(B, A))
- if (R.blacklist.Find(B))
- continue
- if(contents[B] >= R.reqs[A])
- continue main_loop
- else
- needed_amount -= contents[B]
- if(needed_amount <= 0)
- continue main_loop
- else
- continue
+
+ var/list/requirements_list = list()
+
+ // Process all requirements
+ for(var/requirement_path in R.reqs)
+ // Check we have the appropriate amount available in the contents list
+ var/needed_amount = R.reqs[requirement_path]
+ for(var/content_item_path in contents)
+ // Right path and not blacklisted
+ if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(requirement_path))
+ continue
+
+ needed_amount -= contents[content_item_path]
+ if(needed_amount <= 0)
+ break
+
+ if(needed_amount > 0)
return FALSE
- for(var/A in R.chem_catalysts)
- if(contents[A] < R.chem_catalysts[A])
+
+ // Store the instances of what we will use for R.check_requirements() for requirement_path
+ var/list/instances_list = list()
+ for(var/instance_path in item_instances)
+ if(ispath(instance_path, requirement_path))
+ instances_list += item_instances[instance_path]
+
+ requirements_list[requirement_path] = instances_list
+
+ for(var/requirement_path in R.chem_catalysts)
+ if(contents[requirement_path] < R.chem_catalysts[requirement_path])
return FALSE
- return TRUE
+
+ return R.check_requirements(user, requirements_list)
/datum/component/personal_crafting/proc/get_environment(mob/user)
. = list()
@@ -110,9 +129,14 @@
. = list()
.["tool_behaviour"] = list()
.["other"] = list()
+ .["instances"] = list()
for(var/obj/item/I in get_environment(user))
if(I.flags_1 & HOLOGRAM_1)
continue
+ if(.["instances"][I.type])
+ .["instances"][I.type] += I
+ else
+ .["instances"][I.type] = list(I)
if(istype(I, /obj/item/stack))
var/obj/item/stack/S = I
.["other"][I.type] += S.amount
@@ -161,11 +185,11 @@
/datum/component/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R)
var/list/contents = get_surroundings(user)
var/send_feedback = 1
- if(check_contents(R, contents))
+ if(check_contents(user, R, contents))
if(check_tools(user, R, contents))
if(do_after(user, R.time, target = user))
contents = get_surroundings(user)
- if(!check_contents(R, contents))
+ if(!check_contents(user, R, contents))
return ", missing component."
if(!check_tools(user, R, contents))
return ", missing tool."
@@ -341,7 +365,7 @@
if((R.category != cur_category) || (R.subcategory != cur_subcategory))
continue
- craftability["[REF(R)]"] = check_contents(R, surroundings)
+ craftability["[REF(R)]"] = check_contents(user, R, surroundings)
data["craftability"] = craftability
return data
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 06f79396b8..104b58ca28 100644
--- a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
+++ b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
@@ -8,6 +8,12 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
+/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/assembly/flash/shield
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 082cbe245d..1be9df5c30 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -612,6 +612,21 @@
/obj/item/ammo_casing
))
+/obj/item/storage/belt/quiver
+ name = "leather quiver"
+ desc = "A quiver made from the hide of some animal. Used to hold arrows."
+ icon_state = "quiver"
+ item_state = "quiver"
+
+/obj/item/storage/belt/quiver/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 15
+ STR.display_numerical_stacking = TRUE
+ STR.can_hold = typecacheof(list(
+ /obj/item/ammo_casing/caseless/arrow
+ ))
+
/obj/item/storage/belt/medolier
name = "medolier"
desc = "A medical bandolier for holding smartdarts."
diff --git a/code/modules/projectiles/ammunition/caseless/arrow.dm b/code/modules/projectiles/ammunition/caseless/arrow.dm
new file mode 100644
index 0000000000..e0ca637a8b
--- /dev/null
+++ b/code/modules/projectiles/ammunition/caseless/arrow.dm
@@ -0,0 +1,8 @@
+/obj/item/ammo_casing/caseless/arrow
+ name = "wooden arrow"
+ desc = "An arrow made of wood, typically fired from a bow."
+ projectile_type = /obj/item/projectile/bullet/reusable/arrow
+ caliber = "arrow"
+ icon_state = "arrow"
+ throwforce = 3 //good luck hitting someone with the pointy end of the arrow
+ throw_speed = 3
\ No newline at end of file
diff --git a/code/modules/projectiles/boxes_magazines/internal/bow.dm b/code/modules/projectiles/boxes_magazines/internal/bow.dm
new file mode 100644
index 0000000000..9ce1565606
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/bow.dm
@@ -0,0 +1,6 @@
+/obj/item/ammo_box/magazine/internal/bow
+ name = "bow... magazine?" //shouldn't be seeing this
+ ammo_type = /obj/item/ammo_casing/caseless/arrow
+ caliber = "arrow"
+ max_ammo = 1
+ start_empty = 1
\ No newline at end of file
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 9740b4be04..29d31b28fd 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -51,6 +51,7 @@
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
var/obj/item/firing_pin/pin = /obj/item/firing_pin //standard firing pin for most guns
+ var/no_pin_required = FALSE //whether the gun can be fired without a pin
var/obj/item/flashlight/gun_light
var/can_flashlight = 0
@@ -79,7 +80,10 @@
/obj/item/gun/Initialize()
. = ..()
if(pin)
- pin = new pin(src)
+ if(no_pin_required)
+ pin = null
+ else
+ pin = new pin(src)
if(gun_light)
alight = new (src)
if(zoomable)
@@ -107,6 +111,8 @@
/obj/item/gun/examine(mob/user)
. = ..()
+ if(no_pin_required)
+ return
if(pin)
. += "It has \a [pin] installed."
else
@@ -226,6 +232,8 @@
return FALSE
/obj/item/gun/proc/handle_pins(mob/living/user)
+ if(no_pin_required)
+ return TRUE
if(pin)
if(pin.pin_auth(user) || (pin.obj_flags & EMAGGED))
return TRUE
diff --git a/code/modules/projectiles/guns/ballistic/bow.dm b/code/modules/projectiles/guns/ballistic/bow.dm
new file mode 100644
index 0000000000..448e2915d9
--- /dev/null
+++ b/code/modules/projectiles/guns/ballistic/bow.dm
@@ -0,0 +1,53 @@
+/obj/item/gun/ballistic/bow
+ name = "wooden bow"
+ desc = "Some sort of primitive projectile weapon. Used to fire arrows."
+ icon_state = "bow"
+ item_state = "bow"
+ w_class = WEIGHT_CLASS_BULKY
+ weapon_weight = WEAPON_HEAVY //need both hands to fire
+ force = 5
+ mag_type = /obj/item/ammo_box/magazine/internal/bow
+ fire_sound = 'sound/weapons/bowfire.wav'
+ slot_flags = ITEM_SLOT_BACK
+ item_flags = NONE
+ casing_ejector = FALSE
+ inaccuracy_modifier = 0.33 //to counteract the innaccuracy from WEAPON_HEAVY, bows are supposed to be accurate but only able to be fired with both hands
+ pin = null
+ no_pin_required = TRUE
+ trigger_guard = TRIGGER_GUARD_NONE //so ashwalkers can use it
+
+/obj/item/gun/ballistic/bow/shoot_with_empty_chamber()
+ return
+
+/obj/item/gun/ballistic/bow/chamber_round()
+ chambered = magazine.get_round(1)
+
+/obj/item/gun/ballistic/bow/afterattack()
+ . = ..()
+ if (chambered)
+ chambered = null
+ magazine.get_round(0)
+ update_icon()
+
+/obj/item/gun/ballistic/bow/attack_self(mob/living/user)
+ if (chambered)
+ var/obj/item/ammo_casing/AC = magazine.get_round(0)
+ user.put_in_hands(AC)
+ chambered = null
+ to_chat(user, "You gently release the bowstring, removing the arrow.")
+ else if (get_ammo())
+ to_chat(user, "You draw back the bowstring.")
+ playsound(src, 'sound/weapons/bowdraw.wav', 75, 0) //gets way too high pitched if the freq varies
+ chamber_round()
+ update_icon()
+
+/obj/item/gun/ballistic/bow/attackby(obj/item/I, mob/user, params)
+ if (magazine.attackby(I, user, params, 1))
+ to_chat(user, "You notch the arrow.")
+ update_icon()
+
+/obj/item/gun/ballistic/bow/update_icon()
+ icon_state = "bow_[get_ammo() ? (chambered ? "firing" : "loaded") : "unloaded"]"
+
+/obj/item/gun/ballistic/bow/can_shoot()
+ return chambered
\ No newline at end of file
diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm
index 70551d07b6..e754a9c070 100644
--- a/code/modules/projectiles/pins.dm
+++ b/code/modules/projectiles/pins.dm
@@ -23,6 +23,8 @@
if(proximity_flag)
if(istype(target, /obj/item/gun))
var/obj/item/gun/G = target
+ if(G.no_pin_required)
+ return
if(G.pin && (force_replace || G.pin.pin_removeable))
G.pin.forceMove(get_turf(G))
G.pin.gun_remove(user)
diff --git a/code/modules/projectiles/projectile/reusable/arrow.dm b/code/modules/projectiles/projectile/reusable/arrow.dm
new file mode 100644
index 0000000000..f1c9638fd9
--- /dev/null
+++ b/code/modules/projectiles/projectile/reusable/arrow.dm
@@ -0,0 +1,6 @@
+/obj/item/projectile/bullet/reusable/arrow
+ name = "wooden arrow"
+ desc = "Woosh!"
+ damage = 15
+ icon_state = "arrow"
+ ammo_type = /obj/item/ammo_casing/caseless/arrow
\ No newline at end of file
diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi
index 26f81b70df..7a28827903 100644
Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ
diff --git a/icons/mob/belt.dmi b/icons/mob/belt.dmi
index 6843bac6bf..070034d778 100644
Binary files a/icons/mob/belt.dmi and b/icons/mob/belt.dmi differ
diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi
index e329720cf7..1ff87b26e4 100644
Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ
diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi
index 4aa54a2308..a92af16e96 100644
Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ
diff --git a/sound/weapons/bowdraw.wav b/sound/weapons/bowdraw.wav
new file mode 100644
index 0000000000..bba8a87e06
Binary files /dev/null and b/sound/weapons/bowdraw.wav differ
diff --git a/sound/weapons/bowfire.wav b/sound/weapons/bowfire.wav
new file mode 100644
index 0000000000..c079d43475
Binary files /dev/null and b/sound/weapons/bowfire.wav differ
diff --git a/tgstation.dme b/tgstation.dme
index 1614c12e87..6774d3ce80 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2584,6 +2584,7 @@
#include "code\modules\projectiles\ammunition\ballistic\smg.dm"
#include "code\modules\projectiles\ammunition\ballistic\sniper.dm"
#include "code\modules\projectiles\ammunition\caseless\_caseless.dm"
+#include "code\modules\projectiles\ammunition\caseless\arrow.dm"
#include "code\modules\projectiles\ammunition\caseless\ferromagnetic.dm"
#include "code\modules\projectiles\ammunition\caseless\foam.dm"
#include "code\modules\projectiles\ammunition\caseless\misc.dm"
@@ -2614,6 +2615,7 @@
#include "code\modules\projectiles\boxes_magazines\external\toy.dm"
#include "code\modules\projectiles\boxes_magazines\internal\_cylinder.dm"
#include "code\modules\projectiles\boxes_magazines\internal\_internal.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\bow.dm"
#include "code\modules\projectiles\boxes_magazines\internal\grenade.dm"
#include "code\modules\projectiles\boxes_magazines\internal\misc.dm"
#include "code\modules\projectiles\boxes_magazines\internal\revolver.dm"
@@ -2624,6 +2626,7 @@
#include "code\modules\projectiles\guns\energy.dm"
#include "code\modules\projectiles\guns\magic.dm"
#include "code\modules\projectiles\guns\ballistic\automatic.dm"
+#include "code\modules\projectiles\guns\ballistic\bow.dm"
#include "code\modules\projectiles\guns\ballistic\laser_gatling.dm"
#include "code\modules\projectiles\guns\ballistic\launchers.dm"
#include "code\modules\projectiles\guns\ballistic\magweapon.dm"
@@ -2676,6 +2679,7 @@
#include "code\modules\projectiles\projectile\energy\tesla.dm"
#include "code\modules\projectiles\projectile\magic\spellcard.dm"
#include "code\modules\projectiles\projectile\reusable\_reusable.dm"
+#include "code\modules\projectiles\projectile\reusable\arrow.dm"
#include "code\modules\projectiles\projectile\reusable\foam_dart.dm"
#include "code\modules\projectiles\projectile\reusable\magspear.dm"
#include "code\modules\projectiles\projectile\special\curse.dm"