diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm
index ea9dfcc983..5bc5d8bd30 100644
--- a/code/datums/components/crafting/recipes.dm
+++ b/code/datums/components/crafting/recipes.dm
@@ -69,10 +69,10 @@
reqs = list(list(/obj/item/handcuffs/cable = 1),
list(/obj/item/stack/rods = 1),
list(/obj/item/material/shard = 1,
- /obj/item/material/butterflyblade = 1)
+ /obj/item/material/butterflyblade = 1)
)
parts = list(/obj/item/material/shard = 1,
- /obj/item/material/butterflyblade = 1)
+ /obj/item/material/butterflyblade = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
@@ -94,40 +94,6 @@
S.set_material(M.material.name)
qdel(M)
-/datum/crafting_recipe/shortbow
- name = "Shortbow"
- result = /obj/item/gun/launcher/crossbow/bow
- reqs = list(list(/obj/item/stack/material/wood = 10),
- list(/obj/item/stack/material/cloth = 5))
- time = 120
- category = CAT_WEAPONRY
- subcategory = CAT_WEAPON
-
-/datum/crafting_recipe/arrow_sandstone
- name = "Wood arrow (sandstone tip)"
- result = /obj/item/arrow/wood
- reqs = list(list(/obj/item/stack/material/wood = 2),
- list(/obj/item/stack/material/sandstone = 2))
- time = 20
- category = CAT_WEAPONRY
- subcategory = CAT_AMMO
-
-/datum/crafting_recipe/arrow_marble
- name = "Wood arrow (marble tip)"
- result = /obj/item/arrow/wood
- reqs = list(list(/obj/item/stack/material/wood = 2),
- list(/obj/item/stack/material/marble = 2))
- time = 20
- category = CAT_WEAPONRY
- subcategory = CAT_AMMO
-
-/datum/crafting_recipe/quiver
- name = "Arrow quiver"
- result = /obj/item/storage/bag/quiver
- reqs = list(list(/obj/item/stack/material/leather = 8))
- time = 60
- category = CAT_STORAGE
-
/datum/crafting_recipe/material_armor
name = "Material Armor Plate"
result = /obj/item/clothing/accessory/material/advanced
diff --git a/code/datums/components/crafting/recipes/archery.dm b/code/datums/components/crafting/recipes/archery.dm
new file mode 100644
index 0000000000..fa5368a433
--- /dev/null
+++ b/code/datums/components/crafting/recipes/archery.dm
@@ -0,0 +1,107 @@
+/*
+ * Bows, Arrows, and Accessories.
+ */
+
+/datum/crafting_recipe/shortbow
+ name = "Shortbow"
+ result = /obj/item/gun/launcher/crossbow/bow
+ reqs = list(list(/obj/item/stack/material/wood = 10),
+ list(/obj/item/stack/material/cloth = 5))
+ time = 120
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/arrow_sandstone
+ name = "Wood Arrow (Sandstone Tip)"
+ result = /obj/item/arrow/wood
+ reqs = list(list(/obj/item/stack/material/wood = 2),
+ list(/obj/item/stack/material/sandstone = 2))
+ time = 2 SECONDS
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/arrow_marble
+ name = "Wood Arrow (Marble Tip)"
+ result = /obj/item/arrow/wood
+ reqs = list(list(/obj/item/stack/material/wood = 2),
+ list(/obj/item/stack/material/marble = 2))
+ time = 2 SECONDS
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/arrow_chitin
+ name = "Wood Arrow (Chitin Tip)"
+ result = /obj/item/arrow/wood/chitin
+ reqs = list(list(/obj/item/stack/material/wood = 2),
+ list(/obj/item/stack/material/chitin = 2),
+ list(/obj/item/stack/material/cloth = 1))
+ time = 3 SECONDS
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/arrow_metal_material
+ name = "Metal Arrow (Custom Tip)"
+ result = /obj/item/material/arrow
+ reqs = list(list(/obj/item/handcuffs/cable = 1),
+ list(/obj/item/stack/material = 3))
+ parts = list(
+ /obj/item/stack/material = 3
+ )
+ tool_paths = list(
+ /obj/item/material/sharpeningkit
+ )
+ time = 6 SECONDS
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/arrow_metal_material/on_craft_completion(mob/user, atom/result)
+ var/obj/item/stack/material/M
+ for(var/path in parts)
+ var/obj/item/material/N = locate(path) in result
+ if(istype(N, path))
+ if(!istype(M))
+ M = N
+ else
+ N.forceMove(get_turf(result))
+ if(!istype(M))
+ return
+
+ var/obj/item/material/arrow/A = result
+ A.set_material(M.material.name)
+ qdel(M)
+
+/datum/crafting_recipe/arrow_wood_material
+ name = "Wood Arrow (Crude Tip)"
+ result = /obj/item/material/arrow/crude
+ reqs = list(list(/obj/item/stack/material/wood = 2),
+ list(/obj/item/material/knife/machete/hatchet/stone = 1),
+ list(/obj/item/stack/material/cloth = 1))
+ parts = list(
+ /obj/item/material/knife/machete/hatchet/stone = 1
+ )
+ time = 3 SECONDS
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/arrow_wood_material/on_craft_completion(mob/user, atom/result)
+ var/obj/item/material/M
+ for(var/path in parts)
+ var/obj/item/material/N = locate(path) in result
+ if(istype(N, path))
+ if(!istype(M))
+ M = N
+ else
+ N.forceMove(get_turf(result))
+ if(!istype(M))
+ return
+
+ var/obj/item/material/arrow/A = result
+ A.set_material(M.material.name)
+ qdel(M)
+
+/datum/crafting_recipe/quiver
+ name = "Arrow quiver"
+ result = /obj/item/storage/bag/quiver
+ reqs = list(list(/obj/item/stack/material/leather = 8))
+ time = 60
+ category = CAT_STORAGE
diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm
index 1f249c8e7a..c7b84cc5ef 100644
--- a/code/game/objects/items/weapons/material/misc.dm
+++ b/code/game/objects/items/weapons/material/misc.dm
@@ -31,11 +31,14 @@
icon_state = "rock"
item_state = "rock"
attack_verb = list("chopped", "torn", "cut")
+ applies_material_colour = TRUE
+ default_material = MAT_SANDSTONE // Rocks from the earth are not typically high carbon steel.
/obj/item/material/knife/machete/hatchet/stone/set_material(var/new_material)
var/old_name = name
. = ..()
name = old_name
+
/obj/item/material/knife/machete/hatchet/unathiknife
name = "duelling knife"
desc = "A length of leather-bound wood studded with razor-sharp teeth. How crude."
diff --git a/code/game/objects/items/weapons/material/thrown.dm b/code/game/objects/items/weapons/material/thrown.dm
index d9e1bc722c..134e55015d 100644
--- a/code/game/objects/items/weapons/material/thrown.dm
+++ b/code/game/objects/items/weapons/material/thrown.dm
@@ -21,4 +21,28 @@
M.adjustToxLoss(rand(20,40))
/obj/item/material/star/ninja
- default_material = "uranium"
\ No newline at end of file
+ default_material = "uranium"
+
+// Hilariously, arrows are just darts, but bigger! ..The same can be said of spears. Thrown weapon.
+/obj/item/material/arrow
+ name = "arrow"
+ desc = "A sharp, triangular head on a slender body. Yep, it's an arrow."
+ icon = 'icons/obj/weapons.dmi'
+ icon_state = "arrowmetal"
+ item_state = "bolt"
+ force_divisor = 0.1 // 6 with hardness 60 (steel)
+ thrown_force_divisor = 0.4 // 8 with weight 20 (steel)
+ throw_speed = 10
+ throw_range = 4
+ sharp = 1
+ edge = 1
+
+ var/list/knock_point = list(7,7)
+
+/obj/item/material/arrow/crude
+ name = "crude arrow"
+ desc = "An ancient device for stabbing someone at range."
+ icon_state = "arrow"
+ thrown_force_divisor = 0.3
+ throw_range = 3
+ edge = 0
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index a6e65ee2b7..fbcf49471b 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -410,4 +410,4 @@
max_w_class = ITEMSIZE_NORMAL
w_class = ITEMSIZE_LARGE
slot_flags = SLOT_BACK
- can_hold = list(/obj/item/arrow/wood)
\ No newline at end of file
+ can_hold = list(/obj/item/arrow, /obj/item/material/arrow)
diff --git a/code/modules/materials/sheets/_sheets.dm b/code/modules/materials/sheets/_sheets.dm
index 7572d0e654..933b792789 100644
--- a/code/modules/materials/sheets/_sheets.dm
+++ b/code/modules/materials/sheets/_sheets.dm
@@ -1,6 +1,7 @@
// Stacked resources. They use a material datum for a lot of inherited values.
// If you're adding something here, make sure to add it to fifty_spawner_mats.dm as well
/obj/item/stack/material
+ name = "material sheet"
force = 5.0
throwforce = 5
w_class = ITEMSIZE_NORMAL
@@ -86,4 +87,4 @@
else if(istype(W, /obj/item/stack/rods))
material.build_rod_product(user, W, src)
return
- return ..()
\ No newline at end of file
+ return ..()
diff --git a/code/modules/projectiles/guns/launcher/bows.dm b/code/modules/projectiles/guns/launcher/bows.dm
index 0859aec2f2..e2f45a5fc1 100644
--- a/code/modules/projectiles/guns/launcher/bows.dm
+++ b/code/modules/projectiles/guns/launcher/bows.dm
@@ -11,6 +11,12 @@
drop_sound = 'sound/items/drop/woodweapon.ogg'
pickup_sound = 'sound/items/pickup/woodweapon.ogg'
+ knock_point = list(9,8)
+
+/obj/item/arrow/wood/chitin
+ name = "chitin arrow"
+ desc = "A wooden arrow with a hard chitin tip. Simple, but gets the job done."
+ color = "#a66008"
/obj/item/arrow/energy
name = "hardlight arrow"
@@ -48,10 +54,12 @@
release_speed = 15
release_force = 20
tension = 3
- var/drawn = FALSE
drop_sound = 'sound/items/drop/woodweapon.ogg'
pickup_sound = 'sound/items/pickup/woodweapon.ogg'
+ bolt_rotation_transform = 0 // Bows do not rotate their arrows like crossbows, as they face north-east on the ground.
+ drawn_knock = list(8,7)
+ ready_knock = list(15,14)
/obj/item/gun/launcher/crossbow/bow/update_release_force(obj/item/projectile)
return 0
@@ -59,34 +67,34 @@
/obj/item/gun/launcher/crossbow/bow/proc/unload(mob/user)
var/obj/item/arrow/A = bolt
bolt = null
- drawn = FALSE
+ tension = FALSE
A.forceMove(get_turf(user))
user.put_in_hands(A)
update_icon()
/obj/item/gun/launcher/crossbow/bow/consume_next_projectile(mob/user)
- if(!drawn)
+ if(!tension)
to_chat(user, "\The [src] is not drawn back!")
return null
return bolt
/obj/item/gun/launcher/crossbow/bow/handle_post_fire(mob/user, atom/target)
bolt = null
- drawn = FALSE
+ tension = FALSE
update_icon()
..()
/obj/item/gun/launcher/crossbow/bow/attack_hand(mob/living/user)
- if(loc == user && bolt && !drawn)
+ if(loc == user && bolt && !tension)
user.visible_message("[user] removes [bolt] from [src].","You remove [bolt] from [src].")
unload(user)
else
return ..()
/obj/item/gun/launcher/crossbow/bow/attack_self(mob/living/user)
- if(drawn)
+ if(tension)
user.visible_message("[user] relaxes the tension on [src]'s string.","You relax the tension on [src]'s string.")
- drawn = FALSE
+ tension = FALSE
update_icon()
else
draw(user)
@@ -102,7 +110,7 @@
current_user = user
user.visible_message("[user] begins to draw back the string of [src].","You begin to draw back the string of [src].")
if(do_after(user, 25))
- drawn = TRUE
+ tension = TRUE
user.visible_message("[user] draws the string on [src] back fully!", "You draw the string on [src] back fully!")
update_icon()
@@ -110,24 +118,27 @@
return
/obj/item/gun/launcher/crossbow/bow/attackby(obj/item/W as obj, mob/user)
- if(!bolt && istype(W,/obj/item/arrow/wood))
+ if(!bolt && (istype(W,/obj/item/arrow) || istype(W,/obj/item/material/arrow)))
user.drop_from_inventory(W, src)
bolt = W
user.visible_message("[user] nocks [bolt] in [src].","You nock [bolt] in [src].")
update_icon()
/obj/item/gun/launcher/crossbow/bow/update_icon()
- if(drawn)
+ cut_overlays()
+ if(tension)
icon_state = "[initial(icon_state)]_firing"
else if(bolt)
icon_state = "[initial(icon_state)]_loaded"
else
icon_state = "[initial(icon_state)]"
+ add_overlay(update_bolt_transform())
+
/obj/item/gun/launcher/crossbow/bow/dropped(mob/user)
- if(drawn)
+ if(tension)
to_chat(user, "\The [src]'s tension is relaxed as you let go of it!")
- drawn = FALSE
+ tension = FALSE
update_icon()
@@ -139,18 +150,23 @@
drop_sound = 'sound/items/drop/gun.ogg'
pickup_sound = 'sound/items/pickup/gun.ogg'
+ var/arrow_type = /obj/item/arrow/energy
+
/obj/item/gun/launcher/crossbow/bow/hardlight/unload(mob/user)
- qdel_null(bolt)
- update_icon()
+ if(istype(bolt, /obj/item/arrow/energy)) // Let's not delete a Real Arrow^tm
+ qdel_null(bolt)
+ update_icon()
+ else
+ . = ..()
/obj/item/gun/launcher/crossbow/bow/hardlight/attack_self(mob/living/user)
- if(drawn)
+ if(tension)
user.visible_message("[user] relaxes the tension on [src]'s string.","You relax the tension on [src]'s string.")
- drawn = FALSE
+ tension = FALSE
update_icon()
else if(!bolt)
user.visible_message("[user] fabricates a new hardlight projectile with [src].","You fabricate a new hardlight projectile with [src].")
- bolt = new /obj/item/arrow/energy(src)
+ bolt = new arrow_type(src)
update_icon()
else
- draw(user)
\ No newline at end of file
+ draw(user)
diff --git a/code/modules/projectiles/guns/launcher/crossbow.dm b/code/modules/projectiles/guns/launcher/crossbow.dm
index 2eac970aa8..ac0f051579 100644
--- a/code/modules/projectiles/guns/launcher/crossbow.dm
+++ b/code/modules/projectiles/guns/launcher/crossbow.dm
@@ -13,6 +13,8 @@
sharp = 1
edge = 0
+ var/list/knock_point = list(6,6)
+
/obj/item/arrow/proc/removed() //Helper for metal rods falling apart.
return
@@ -37,6 +39,8 @@
item_state = "quill"
throwforce = 5
+ knock_point = list(10,11)
+
/obj/item/arrow/rod
name = "metal rod"
desc = "Don't cry for me, Orithena."
@@ -60,6 +64,11 @@
fire_delay = 25
slot_flags = SLOT_BACK
+ // Pixel location the arrow should rest on.
+ var/list/drawn_knock = list(10,9)
+ var/list/ready_knock = list(4,3)
+ var/bolt_rotation_transform = 180 // Crossbows point 'southwest' in their ground icon, so the arrows have to be flipped around.
+
var/obj/item/bolt
var/tension = 0 // Current draw on the bow.
var/max_tension = 5 // Highest possible tension.
@@ -139,7 +148,7 @@
/obj/item/gun/launcher/crossbow/attackby(obj/item/W as obj, mob/user as mob)
if(!bolt)
- if (istype(W,/obj/item/arrow))
+ if (istype(W,/obj/item/arrow) || istype(W, /obj/item/material/arrow))
user.drop_from_inventory(W, src)
bolt = W
user.visible_message("[user] slides [bolt] into [src].","You slide [bolt] into [src].")
@@ -179,6 +188,14 @@
else
..()
+/obj/item/gun/launcher/crossbow/attack_hand(mob/living/user)
+ . = ..()
+ update_icon()
+
+/obj/item/gun/launcher/crossbow/dropped(mob/user)
+ . = ..()
+ update_icon()
+
/obj/item/gun/launcher/crossbow/proc/superheat_rod(var/mob/user)
if(!user || !cell || !bolt) return
if(cell.charge < 500) return
@@ -190,7 +207,29 @@
bolt.icon_state = "metal-rod-superheated"
cell.use(500)
+/obj/item/gun/launcher/crossbow/proc/update_bolt_transform()
+ if(!(istype(bolt,/obj/item/arrow) || istype(bolt, /obj/item/material/arrow)))
+ return
+
+ var/obj/item/arrow/dart = bolt
+
+ if(LAZYLEN(drawn_knock) && LAZYLEN(ready_knock) && LAZYLEN(dart.knock_point))
+ dart.SetTransform(rotation = bolt_rotation_transform)
+
+ if(!tension)
+ dart.SetTransform(offset_x = ready_knock[1] - dart?.knock_point[1], offset_y = ready_knock[2] - dart?.knock_point[2])
+
+ else
+ dart.SetTransform(offset_x = drawn_knock[1] - dart?.knock_point[1], offset_y = drawn_knock[2] - dart?.knock_point[2])
+
+ var/image/dart_image = image(dart)
+ dart_image.plane = plane // Make sure it's the same plane as the parent, otherwise we get phantom bolts.
+ dart.ClearTransform()
+
+ return dart_image
+
/obj/item/gun/launcher/crossbow/update_icon()
+ cut_overlays()
if(tension > 1)
icon_state = "crossbow-drawn"
else if(bolt)
@@ -198,6 +237,8 @@
else
icon_state = "crossbow"
+ if((istype(bolt,/obj/item/arrow) || istype(bolt, /obj/item/material/arrow)))
+ add_overlay(update_bolt_transform())
// Crossbow construction.
/obj/item/crossbowframe
diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi
index e294b1c59c..aefff673f6 100644
Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ
diff --git a/polaris.dme b/polaris.dme
index ce53f0551c..93f8b74434 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -301,6 +301,7 @@
#include "code\datums\components\crafting\crafting_external.dm"
#include "code\datums\components\crafting\recipes.dm"
#include "code\datums\components\crafting\tool_quality.dm"
+#include "code\datums\components\crafting\recipes\archery.dm"
#include "code\datums\elements\_element.dm"
#include "code\datums\game_masters\_common.dm"
#include "code\datums\game_masters\default.dm"