diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
similarity index 100%
rename from code/modules/projectiles/ammunition.dm
rename to code/modules/projectiles/ammunition/_ammunition.dm
diff --git a/code/modules/projectiles/firing.dm b/code/modules/projectiles/ammunition/_firing.dm
similarity index 100%
rename from code/modules/projectiles/firing.dm
rename to code/modules/projectiles/ammunition/_firing.dm
diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm
deleted file mode 100644
index df0fd7278e..0000000000
--- a/code/modules/projectiles/ammunition/ammo_casings.dm
+++ /dev/null
@@ -1,314 +0,0 @@
-// .357 (Syndie Revolver)
-
-/obj/item/ammo_casing/a357
- name = ".357 bullet casing"
- desc = "A .357 bullet casing."
- caliber = "357"
- projectile_type = /obj/item/projectile/bullet/a357
-
-// 7.62 (Nagant Rifle)
-
-/obj/item/ammo_casing/a762
- name = "7.62 bullet casing"
- desc = "A 7.62 bullet casing."
- icon_state = "762-casing"
- caliber = "a762"
- projectile_type = /obj/item/projectile/bullet/a762
-
-/obj/item/ammo_casing/a762/enchanted
- projectile_type = /obj/item/projectile/bullet/a762_enchanted
-
-// 7.62x38mmR (Nagant Revolver)
-
-/obj/item/ammo_casing/n762
- name = "7.62x38mmR bullet casing"
- desc = "A 7.62x38mmR bullet casing."
- caliber = "n762"
- projectile_type = /obj/item/projectile/bullet/n762
-
-// .50AE (Desert Eagle)
-
-/obj/item/ammo_casing/a50AE
- name = ".50AE bullet casing"
- desc = "A .50AE bullet casing."
- caliber = ".50"
- projectile_type = /obj/item/projectile/bullet/a50AE
-
-// .38 (Detective's Gun)
-
-/obj/item/ammo_casing/c38
- name = ".38 bullet casing"
- desc = "A .38 bullet casing."
- caliber = "38"
- projectile_type = /obj/item/projectile/bullet/c38
-
-// 10mm (Stechkin)
-
-/obj/item/ammo_casing/c10mm
- name = ".10mm bullet casing"
- desc = "A 10mm bullet casing."
- caliber = "10mm"
- projectile_type = /obj/item/projectile/bullet/c10mm
-
-/obj/item/ammo_casing/c10mm/ap
- name = ".10mm armor-piercing bullet casing"
- desc = "A 10mm armor-piercing bullet casing."
- projectile_type = /obj/item/projectile/bullet/c10mm_ap
-
-/obj/item/ammo_casing/c10mm/hp
- name = ".10mm hollow-point bullet casing"
- desc = "A 10mm hollow-point bullet casing."
- projectile_type = /obj/item/projectile/bullet/c10mm_hp
-
-/obj/item/ammo_casing/c10mm/fire
- name = ".10mm incendiary bullet casing"
- desc = "A 10mm incendiary bullet casing."
- projectile_type = /obj/item/projectile/bullet/incendiary/c10mm
-
-// 9mm (Stechkin APS)
-
-/obj/item/ammo_casing/c9mm
- name = "9mm bullet casing"
- desc = "A 9mm bullet casing."
- caliber = "9mm"
- projectile_type = /obj/item/projectile/bullet/c9mm
-
-/obj/item/ammo_casing/c9mm/ap
- name = "9mm armor-piercing bullet casing"
- desc = "A 9mm armor-piercing bullet casing."
- projectile_type =/obj/item/projectile/bullet/c9mm_ap
-
-/obj/item/ammo_casing/c9mm/inc
- name = "9mm incendiary bullet casing"
- desc = "A 9mm incendiary bullet casing."
- projectile_type = /obj/item/projectile/bullet/incendiary/c9mm
-
-// 4.6x30mm (Autorifles)
-
-/obj/item/ammo_casing/c46x30mm
- name = "4.6x30mm bullet casing"
- desc = "A 4.6x30mm bullet casing."
- caliber = "4.6x30mm"
- projectile_type = /obj/item/projectile/bullet/c46x30mm
-
-/obj/item/ammo_casing/c46x30mm/ap
- name = "4.6x30mm armor-piercing bullet casing"
- desc = "A 4.6x30mm armor-piercing bullet casing."
- projectile_type = /obj/item/projectile/bullet/c46x30mm_ap
-
-/obj/item/ammo_casing/c46x30mm/inc
- name = "4.6x30mm incendiary bullet casing"
- desc = "A 4.6x30mm incendiary bullet casing."
- projectile_type = /obj/item/projectile/bullet/incendiary/c46x30mm
-
-// .45 (M1911 + C20r)
-
-/obj/item/ammo_casing/c45
- name = ".45 bullet casing"
- desc = "A .45 bullet casing."
- caliber = ".45"
- projectile_type = /obj/item/projectile/bullet/c45
-
-/obj/item/ammo_casing/c45/nostamina
- projectile_type = /obj/item/projectile/bullet/c45_nostamina
-
-// 5.56mm (M-90gl Carbine)
-
-/obj/item/ammo_casing/a556
- name = "5.56mm bullet casing"
- desc = "A 5.56mm bullet casing."
- caliber = "a556"
- projectile_type = /obj/item/projectile/bullet/a556
-
-// 40mm (Grenade Launcher)
-
-/obj/item/ammo_casing/a40mm
- name = "40mm HE shell"
- desc = "A cased high explosive grenade that can only be activated once fired out of a grenade launcher."
- caliber = "40mm"
- icon_state = "40mmHE"
- projectile_type = /obj/item/projectile/bullet/a40mm
-
-// .50 (Sniper)
-
-/obj/item/ammo_casing/p50
- name = ".50 bullet casing"
- desc = "A .50 bullet casing."
- caliber = ".50"
- projectile_type = /obj/item/projectile/bullet/p50
- icon_state = ".50"
-
-/obj/item/ammo_casing/p50/soporific
- name = ".50 soporific bullet casing"
- desc = "A .50 bullet casing, specialised in sending the target to sleep, instead of hell."
- projectile_type = /obj/item/projectile/bullet/p50/soporific
- icon_state = "sleeper"
-
-/obj/item/ammo_casing/p50/penetrator
- name = ".50 penetrator round bullet casing"
- desc = "A .50 caliber penetrator round casing."
- projectile_type = /obj/item/projectile/bullet/p50/penetrator
-
-// 1.95x129mm (SAW)
-
-/obj/item/ammo_casing/mm195x129
- name = "1.95x129mm bullet casing"
- desc = "A 1.95x129mm bullet casing."
- icon_state = "762-casing"
- caliber = "mm195129"
- projectile_type = /obj/item/projectile/bullet/mm195x129
-
-/obj/item/ammo_casing/mm195x129/ap
- name = "1.95x129mm armor-piercing bullet casing"
- desc = "A 1.95x129mm bullet casing designed with a hardened-tipped core to help penetrate armored targets."
- projectile_type = /obj/item/projectile/bullet/mm195x129_ap
-
-/obj/item/ammo_casing/mm195x129/hollow
- name = "1.95x129mm hollow-point bullet casing"
- desc = "A 1.95x129mm bullet casing designed to cause more damage to unarmored targets."
- projectile_type = /obj/item/projectile/bullet/mm195x129_hp
-
-/obj/item/ammo_casing/mm195x129/incen
- name = "1.95x129mm incendiary bullet casing"
- desc = "A 1.95x129mm bullet casing designed with a chemical-filled capsule on the tip that when bursted, reacts with the atmosphere to produce a fireball, engulfing the target in flames."
- projectile_type = /obj/item/projectile/bullet/incendiary/mm195x129
-
-// Shotgun
-
-/obj/item/ammo_casing/shotgun
- name = "shotgun slug"
- desc = "A 12 gauge lead slug."
- icon_state = "blshell"
- caliber = "shotgun"
- projectile_type = /obj/item/projectile/bullet/shotgun_slug
- materials = list(MAT_METAL=4000)
-
-/obj/item/ammo_casing/shotgun/beanbag
- name = "beanbag slug"
- desc = "A weak beanbag slug for riot control."
- icon_state = "bshell"
- projectile_type = /obj/item/projectile/bullet/shotgun_beanbag
- materials = list(MAT_METAL=250)
-
-/obj/item/ammo_casing/shotgun/incendiary
- name = "incendiary slug"
- desc = "An incendiary-coated shotgun slug."
- icon_state = "ishell"
- projectile_type = /obj/item/projectile/bullet/incendiary/shotgun
-
-/obj/item/ammo_casing/shotgun/dragonsbreath
- name = "dragonsbreath shell"
- desc = "A shotgun shell which fires a spread of incendiary pellets."
- icon_state = "ishell2"
- projectile_type = /obj/item/projectile/bullet/incendiary/shotgun/dragonsbreath
- pellets = 4
- variance = 35
-
-/obj/item/ammo_casing/shotgun/stunslug
- name = "taser slug"
- desc = "A stunning taser slug."
- icon_state = "stunshell"
- projectile_type = /obj/item/projectile/bullet/shotgun_stunslug
- materials = list(MAT_METAL=250)
-
-/obj/item/ammo_casing/shotgun/meteorslug
- name = "meteorslug shell"
- desc = "A shotgun shell rigged with CMC technology, which launches a massive slug when fired."
- icon_state = "mshell"
- projectile_type = /obj/item/projectile/bullet/shotgun_meteorslug
-
-/obj/item/ammo_casing/shotgun/pulseslug
- name = "pulse slug"
- desc = "A delicate device which can be loaded into a shotgun. The primer acts as a button which triggers the gain medium and fires a powerful \
- energy blast. While the heat and power drain limit it to one use, it can still allow an operator to engage targets that ballistic ammunition \
- would have difficulty with."
- icon_state = "pshell"
- projectile_type = /obj/item/projectile/beam/pulse/shotgun
-
-/obj/item/ammo_casing/shotgun/frag12
- name = "FRAG-12 slug"
- desc = "A high explosive breaching round for a 12 gauge shotgun."
- icon_state = "heshell"
- projectile_type = /obj/item/projectile/bullet/shotgun_frag12
-
-/obj/item/ammo_casing/shotgun/buckshot
- name = "buckshot shell"
- desc = "A 12 gauge buckshot shell."
- icon_state = "gshell"
- projectile_type = /obj/item/projectile/bullet/pellet/shotgun_buckshot
- pellets = 6
- variance = 25
-
-/obj/item/ammo_casing/shotgun/rubbershot
- name = "rubber shot"
- desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance."
- icon_state = "bshell"
- projectile_type = /obj/item/projectile/bullet/pellet/shotgun_rubbershot
- pellets = 6
- variance = 25
- materials = list(MAT_METAL=4000)
-
-/obj/item/ammo_casing/shotgun/improvised
- name = "improvised shell"
- desc = "An extremely weak shotgun shell with multiple small pellets made out of metal shards."
- icon_state = "improvshell"
- projectile_type = /obj/item/projectile/bullet/pellet/shotgun_improvised
- materials = list(MAT_METAL=250)
- pellets = 10
- variance = 25
-
-/obj/item/ammo_casing/shotgun/ion
- name = "ion shell"
- desc = "An advanced shotgun shell which uses a subspace ansible crystal to produce an effect similar to a standard ion rifle. \
- The unique properties of the crystal split the pulse into a spread of individually weaker bolts."
- icon_state = "ionshell"
- projectile_type = /obj/item/projectile/ion/weak
- pellets = 4
- variance = 35
-
-/obj/item/ammo_casing/shotgun/laserslug
- name = "laser slug"
- desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a laser weapon in a ballistic package."
- icon_state = "lshell"
- projectile_type = /obj/item/projectile/beam/laser
-
-/obj/item/ammo_casing/shotgun/techshell
- name = "unloaded technological shell"
- desc = "A high-tech shotgun shell which can be loaded with materials to produce unique effects."
- icon_state = "cshell"
- projectile_type = null
-
-/obj/item/ammo_casing/shotgun/dart
- name = "shotgun dart"
- desc = "A dart for use in shotguns. Can be injected with up to 30 units of any chemical."
- icon_state = "cshell"
- projectile_type = /obj/item/projectile/bullet/dart
- var/reagent_amount = 30
- var/reagent_react = TRUE
-
-/obj/item/ammo_casing/shotgun/dart/noreact
- name = "cryostasis shotgun dart"
- desc = "A dart for use in shotguns, using similar technolgoy as cryostatis beakers to keep internal reagents from reacting. Can be injected with up to 10 units of any chemical."
- icon_state = "cnrshell"
- reagent_amount = 10
- reagent_react = FALSE
-
-/obj/item/ammo_casing/shotgun/dart/Initialize()
- . = ..()
- container_type |= OPENCONTAINER
- create_reagents(reagent_amount)
- reagents.set_reacting(reagent_react)
-
-/obj/item/ammo_casing/shotgun/dart/attackby()
- return
-
-/obj/item/ammo_casing/shotgun/dart/bioterror
- desc = "A shotgun dart filled with deadly toxins."
-
-/obj/item/ammo_casing/shotgun/dart/bioterror/Initialize()
- . = ..()
- reagents.add_reagent("neurotoxin", 6)
- reagents.add_reagent("spore", 6)
- reagents.add_reagent("mutetoxin", 6) //;HELP OPS IN MAINT
- reagents.add_reagent("coniine", 6)
- reagents.add_reagent("sodium_thiopental", 6)
diff --git a/code/modules/projectiles/ammunition/ballistic/lmg.dm b/code/modules/projectiles/ammunition/ballistic/lmg.dm
new file mode 100644
index 0000000000..1ce2e0065e
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/lmg.dm
@@ -0,0 +1,23 @@
+// 1.95x129mm (SAW)
+
+/obj/item/ammo_casing/mm195x129
+ name = "1.95x129mm bullet casing"
+ desc = "A 1.95x129mm bullet casing."
+ icon_state = "762-casing"
+ caliber = "mm195129"
+ projectile_type = /obj/item/projectile/bullet/mm195x129
+
+/obj/item/ammo_casing/mm195x129/ap
+ name = "1.95x129mm armor-piercing bullet casing"
+ desc = "A 1.95x129mm bullet casing designed with a hardened-tipped core to help penetrate armored targets."
+ projectile_type = /obj/item/projectile/bullet/mm195x129_ap
+
+/obj/item/ammo_casing/mm195x129/hollow
+ name = "1.95x129mm hollow-point bullet casing"
+ desc = "A 1.95x129mm bullet casing designed to cause more damage to unarmored targets."
+ projectile_type = /obj/item/projectile/bullet/mm195x129_hp
+
+/obj/item/ammo_casing/mm195x129/incen
+ name = "1.95x129mm incendiary bullet casing"
+ desc = "A 1.95x129mm bullet casing designed with a chemical-filled capsule on the tip that when bursted, reacts with the atmosphere to produce a fireball, engulfing the target in flames."
+ projectile_type = /obj/item/projectile/bullet/incendiary/mm195x129
diff --git a/code/modules/projectiles/ammunition/ballistic/pistol.dm b/code/modules/projectiles/ammunition/ballistic/pistol.dm
new file mode 100644
index 0000000000..02134e95e1
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/pistol.dm
@@ -0,0 +1,50 @@
+// 10mm (Stechkin)
+
+/obj/item/ammo_casing/c10mm
+ name = ".10mm bullet casing"
+ desc = "A 10mm bullet casing."
+ caliber = "10mm"
+ projectile_type = /obj/item/projectile/bullet/c10mm
+
+/obj/item/ammo_casing/c10mm/ap
+ name = ".10mm armor-piercing bullet casing"
+ desc = "A 10mm armor-piercing bullet casing."
+ projectile_type = /obj/item/projectile/bullet/c10mm_ap
+
+/obj/item/ammo_casing/c10mm/hp
+ name = ".10mm hollow-point bullet casing"
+ desc = "A 10mm hollow-point bullet casing."
+ projectile_type = /obj/item/projectile/bullet/c10mm_hp
+
+/obj/item/ammo_casing/c10mm/fire
+ name = ".10mm incendiary bullet casing"
+ desc = "A 10mm incendiary bullet casing."
+ projectile_type = /obj/item/projectile/bullet/incendiary/c10mm
+
+// 9mm (Stechkin APS)
+
+/obj/item/ammo_casing/c9mm
+ name = "9mm bullet casing"
+ desc = "A 9mm bullet casing."
+ caliber = "9mm"
+ projectile_type = /obj/item/projectile/bullet/c9mm
+
+/obj/item/ammo_casing/c9mm/ap
+ name = "9mm armor-piercing bullet casing"
+ desc = "A 9mm armor-piercing bullet casing."
+ projectile_type =/obj/item/projectile/bullet/c9mm_ap
+
+/obj/item/ammo_casing/c9mm/inc
+ name = "9mm incendiary bullet casing"
+ desc = "A 9mm incendiary bullet casing."
+ projectile_type = /obj/item/projectile/bullet/incendiary/c9mm
+
+
+// .50AE (Desert Eagle)
+
+/obj/item/ammo_casing/a50AE
+ name = ".50AE bullet casing"
+ desc = "A .50AE bullet casing."
+ caliber = ".50"
+ projectile_type = /obj/item/projectile/bullet/a50AE
+
diff --git a/code/modules/projectiles/ammunition/ballistic/revolver.dm b/code/modules/projectiles/ammunition/ballistic/revolver.dm
new file mode 100644
index 0000000000..52a72e0ff7
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/revolver.dm
@@ -0,0 +1,23 @@
+// .357 (Syndie Revolver)
+
+/obj/item/ammo_casing/a357
+ name = ".357 bullet casing"
+ desc = "A .357 bullet casing."
+ caliber = "357"
+ projectile_type = /obj/item/projectile/bullet/a357
+
+// 7.62x38mmR (Nagant Revolver)
+
+/obj/item/ammo_casing/n762
+ name = "7.62x38mmR bullet casing"
+ desc = "A 7.62x38mmR bullet casing."
+ caliber = "n762"
+ projectile_type = /obj/item/projectile/bullet/n762
+
+// .38 (Detective's Gun)
+
+/obj/item/ammo_casing/c38
+ name = ".38 bullet casing"
+ desc = "A .38 bullet casing."
+ caliber = "38"
+ projectile_type = /obj/item/projectile/bullet/c38
diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm
new file mode 100644
index 0000000000..a35cfcba1c
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm
@@ -0,0 +1,28 @@
+// 7.62 (Nagant Rifle)
+
+/obj/item/ammo_casing/a762
+ name = "7.62 bullet casing"
+ desc = "A 7.62 bullet casing."
+ icon_state = "762-casing"
+ caliber = "a762"
+ projectile_type = /obj/item/projectile/bullet/a762
+
+/obj/item/ammo_casing/a762/enchanted
+ projectile_type = /obj/item/projectile/bullet/a762_enchanted
+
+// 5.56mm (M-90gl Carbine)
+
+/obj/item/ammo_casing/a556
+ name = "5.56mm bullet casing"
+ desc = "A 5.56mm bullet casing."
+ caliber = "a556"
+ projectile_type = /obj/item/projectile/bullet/a556
+
+// 40mm (Grenade Launcher)
+
+/obj/item/ammo_casing/a40mm
+ name = "40mm HE shell"
+ desc = "A cased high explosive grenade that can only be activated once fired out of a grenade launcher."
+ caliber = "40mm"
+ icon_state = "40mmHE"
+ projectile_type = /obj/item/projectile/bullet/a40mm
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
new file mode 100644
index 0000000000..b700d092d7
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -0,0 +1,139 @@
+// Shotgun
+
+/obj/item/ammo_casing/shotgun
+ name = "shotgun slug"
+ desc = "A 12 gauge lead slug."
+ icon_state = "blshell"
+ caliber = "shotgun"
+ projectile_type = /obj/item/projectile/bullet/shotgun_slug
+ materials = list(MAT_METAL=4000)
+
+/obj/item/ammo_casing/shotgun/beanbag
+ name = "beanbag slug"
+ desc = "A weak beanbag slug for riot control."
+ icon_state = "bshell"
+ projectile_type = /obj/item/projectile/bullet/shotgun_beanbag
+ materials = list(MAT_METAL=250)
+
+/obj/item/ammo_casing/shotgun/incendiary
+ name = "incendiary slug"
+ desc = "An incendiary-coated shotgun slug."
+ icon_state = "ishell"
+ projectile_type = /obj/item/projectile/bullet/incendiary/shotgun
+
+/obj/item/ammo_casing/shotgun/dragonsbreath
+ name = "dragonsbreath shell"
+ desc = "A shotgun shell which fires a spread of incendiary pellets."
+ icon_state = "ishell2"
+ projectile_type = /obj/item/projectile/bullet/incendiary/shotgun/dragonsbreath
+ pellets = 4
+ variance = 35
+
+/obj/item/ammo_casing/shotgun/stunslug
+ name = "taser slug"
+ desc = "A stunning taser slug."
+ icon_state = "stunshell"
+ projectile_type = /obj/item/projectile/bullet/shotgun_stunslug
+ materials = list(MAT_METAL=250)
+
+/obj/item/ammo_casing/shotgun/meteorslug
+ name = "meteorslug shell"
+ desc = "A shotgun shell rigged with CMC technology, which launches a massive slug when fired."
+ icon_state = "mshell"
+ projectile_type = /obj/item/projectile/bullet/shotgun_meteorslug
+
+/obj/item/ammo_casing/shotgun/pulseslug
+ name = "pulse slug"
+ desc = "A delicate device which can be loaded into a shotgun. The primer acts as a button which triggers the gain medium and fires a powerful \
+ energy blast. While the heat and power drain limit it to one use, it can still allow an operator to engage targets that ballistic ammunition \
+ would have difficulty with."
+ icon_state = "pshell"
+ projectile_type = /obj/item/projectile/beam/pulse/shotgun
+
+/obj/item/ammo_casing/shotgun/frag12
+ name = "FRAG-12 slug"
+ desc = "A high explosive breaching round for a 12 gauge shotgun."
+ icon_state = "heshell"
+ projectile_type = /obj/item/projectile/bullet/shotgun_frag12
+
+/obj/item/ammo_casing/shotgun/buckshot
+ name = "buckshot shell"
+ desc = "A 12 gauge buckshot shell."
+ icon_state = "gshell"
+ projectile_type = /obj/item/projectile/bullet/pellet/shotgun_buckshot
+ pellets = 6
+ variance = 25
+
+/obj/item/ammo_casing/shotgun/rubbershot
+ name = "rubber shot"
+ desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance."
+ icon_state = "bshell"
+ projectile_type = /obj/item/projectile/bullet/pellet/shotgun_rubbershot
+ pellets = 6
+ variance = 25
+ materials = list(MAT_METAL=4000)
+
+/obj/item/ammo_casing/shotgun/improvised
+ name = "improvised shell"
+ desc = "An extremely weak shotgun shell with multiple small pellets made out of metal shards."
+ icon_state = "improvshell"
+ projectile_type = /obj/item/projectile/bullet/pellet/shotgun_improvised
+ materials = list(MAT_METAL=250)
+ pellets = 10
+ variance = 25
+
+/obj/item/ammo_casing/shotgun/ion
+ name = "ion shell"
+ desc = "An advanced shotgun shell which uses a subspace ansible crystal to produce an effect similar to a standard ion rifle. \
+ The unique properties of the crystal split the pulse into a spread of individually weaker bolts."
+ icon_state = "ionshell"
+ projectile_type = /obj/item/projectile/ion/weak
+ pellets = 4
+ variance = 35
+
+/obj/item/ammo_casing/shotgun/laserslug
+ name = "laser slug"
+ desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a laser weapon in a ballistic package."
+ icon_state = "lshell"
+ projectile_type = /obj/item/projectile/beam/laser
+
+/obj/item/ammo_casing/shotgun/techshell
+ name = "unloaded technological shell"
+ desc = "A high-tech shotgun shell which can be loaded with materials to produce unique effects."
+ icon_state = "cshell"
+ projectile_type = null
+
+/obj/item/ammo_casing/shotgun/dart
+ name = "shotgun dart"
+ desc = "A dart for use in shotguns. Can be injected with up to 30 units of any chemical."
+ icon_state = "cshell"
+ projectile_type = /obj/item/projectile/bullet/dart
+ var/reagent_amount = 30
+ var/reagent_react = TRUE
+
+/obj/item/ammo_casing/shotgun/dart/noreact
+ name = "cryostasis shotgun dart"
+ desc = "A dart for use in shotguns, using similar technolgoy as cryostatis beakers to keep internal reagents from reacting. Can be injected with up to 10 units of any chemical."
+ icon_state = "cnrshell"
+ reagent_amount = 10
+ reagent_react = FALSE
+
+/obj/item/ammo_casing/shotgun/dart/Initialize()
+ . = ..()
+ container_type |= OPENCONTAINER
+ create_reagents(reagent_amount)
+ reagents.set_reacting(reagent_react)
+
+/obj/item/ammo_casing/shotgun/dart/attackby()
+ return
+
+/obj/item/ammo_casing/shotgun/dart/bioterror
+ desc = "A shotgun dart filled with deadly toxins."
+
+/obj/item/ammo_casing/shotgun/dart/bioterror/Initialize()
+ . = ..()
+ reagents.add_reagent("neurotoxin", 6)
+ reagents.add_reagent("spore", 6)
+ reagents.add_reagent("mutetoxin", 6) //;HELP OPS IN MAINT
+ reagents.add_reagent("coniine", 6)
+ reagents.add_reagent("sodium_thiopental", 6)
diff --git a/code/modules/projectiles/ammunition/ballistic/smg.dm b/code/modules/projectiles/ammunition/ballistic/smg.dm
new file mode 100644
index 0000000000..3be419c933
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/smg.dm
@@ -0,0 +1,28 @@
+// 4.6x30mm (Autorifles)
+
+/obj/item/ammo_casing/c46x30mm
+ name = "4.6x30mm bullet casing"
+ desc = "A 4.6x30mm bullet casing."
+ caliber = "4.6x30mm"
+ projectile_type = /obj/item/projectile/bullet/c46x30mm
+
+/obj/item/ammo_casing/c46x30mm/ap
+ name = "4.6x30mm armor-piercing bullet casing"
+ desc = "A 4.6x30mm armor-piercing bullet casing."
+ projectile_type = /obj/item/projectile/bullet/c46x30mm_ap
+
+/obj/item/ammo_casing/c46x30mm/inc
+ name = "4.6x30mm incendiary bullet casing"
+ desc = "A 4.6x30mm incendiary bullet casing."
+ projectile_type = /obj/item/projectile/bullet/incendiary/c46x30mm
+
+// .45 (M1911 + C20r)
+
+/obj/item/ammo_casing/c45
+ name = ".45 bullet casing"
+ desc = "A .45 bullet casing."
+ caliber = ".45"
+ projectile_type = /obj/item/projectile/bullet/c45
+
+/obj/item/ammo_casing/c45/nostamina
+ projectile_type = /obj/item/projectile/bullet/c45_nostamina
diff --git a/code/modules/projectiles/ammunition/ballistic/sniper.dm b/code/modules/projectiles/ammunition/ballistic/sniper.dm
new file mode 100644
index 0000000000..5906fcfaba
--- /dev/null
+++ b/code/modules/projectiles/ammunition/ballistic/sniper.dm
@@ -0,0 +1,19 @@
+// .50 (Sniper)
+
+/obj/item/ammo_casing/p50
+ name = ".50 bullet casing"
+ desc = "A .50 bullet casing."
+ caliber = ".50"
+ projectile_type = /obj/item/projectile/bullet/p50
+ icon_state = ".50"
+
+/obj/item/ammo_casing/p50/soporific
+ name = ".50 soporific bullet casing"
+ desc = "A .50 bullet casing, specialised in sending the target to sleep, instead of hell."
+ projectile_type = /obj/item/projectile/bullet/p50/soporific
+ icon_state = "sleeper"
+
+/obj/item/ammo_casing/p50/penetrator
+ name = ".50 penetrator round bullet casing"
+ desc = "A .50 caliber penetrator round casing."
+ projectile_type = /obj/item/projectile/bullet/p50/penetrator
diff --git a/code/modules/projectiles/ammunition/caseless/_caseless.dm b/code/modules/projectiles/ammunition/caseless/_caseless.dm
new file mode 100644
index 0000000000..154d269cd9
--- /dev/null
+++ b/code/modules/projectiles/ammunition/caseless/_caseless.dm
@@ -0,0 +1,15 @@
+/obj/item/ammo_casing/caseless
+ desc = "A caseless bullet casing."
+ firing_effect_type = null
+ heavy_metal = FALSE
+
+/obj/item/ammo_casing/caseless/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread)
+ if (..()) //successfully firing
+ moveToNullspace()
+ return 1
+ else
+ return 0
+
+/obj/item/ammo_casing/caseless/update_icon()
+ ..()
+ icon_state = "[initial(icon_state)]"
diff --git a/code/modules/projectiles/ammunition/caseless.dm b/code/modules/projectiles/ammunition/caseless/foam.dm
similarity index 56%
rename from code/modules/projectiles/ammunition/caseless.dm
rename to code/modules/projectiles/ammunition/caseless/foam.dm
index b3439c86b2..fdf685e001 100644
--- a/code/modules/projectiles/ammunition/caseless.dm
+++ b/code/modules/projectiles/ammunition/caseless/foam.dm
@@ -1,117 +1,61 @@
-
-// Caseless Ammunition
-
-/obj/item/ammo_casing/caseless
- desc = "A caseless bullet casing."
- firing_effect_type = null
- heavy_metal = FALSE
-
-/obj/item/ammo_casing/caseless/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread)
- if (..()) //successfully firing
- moveToNullspace()
- return 1
- else
- return 0
-
-/obj/item/ammo_casing/caseless/update_icon()
- ..()
- icon_state = "[initial(icon_state)]"
-
-/obj/item/ammo_casing/caseless/a75
- desc = "A .75 bullet casing."
- caliber = "75"
- icon_state = "s-casing-live"
- projectile_type = /obj/item/projectile/bullet/gyro
-
-/obj/item/ammo_casing/caseless/a84mm
- desc = "An 84mm anti-armour rocket."
- caliber = "84mm"
- icon_state = "s-casing-live"
- projectile_type = /obj/item/projectile/bullet/a84mm
-
-/obj/item/ammo_casing/caseless/magspear
- name = "magnetic spear"
- desc = "A reusable spear that is typically loaded into kinetic spearguns."
- projectile_type = /obj/item/projectile/bullet/reusable/magspear
- caliber = "speargun"
- icon_state = "magspear"
- throwforce = 15 //still deadly when thrown
- throw_speed = 3
-
-
-/obj/item/ammo_casing/caseless/laser
- name = "laser casing"
- desc = "You shouldn't be seeing this."
- caliber = "laser"
- icon_state = "s-casing-live"
- projectile_type = /obj/item/projectile/beam
- fire_sound = 'sound/weapons/laser.ogg'
- firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
-
-/obj/item/ammo_casing/caseless/laser/gatling
- projectile_type = /obj/item/projectile/beam/weak
- variance = 0.8
- click_cooldown_override = 1
-
-
-/obj/item/ammo_casing/caseless/foam_dart
- name = "foam dart"
- desc = "It's nerf or nothing! Ages 8 and up."
- projectile_type = /obj/item/projectile/bullet/reusable/foam_dart
- caliber = "foam_force"
- icon = 'icons/obj/guns/toy.dmi'
- icon_state = "foamdart"
- var/modified = 0
-
-/obj/item/ammo_casing/caseless/foam_dart/update_icon()
- ..()
- if (modified)
- icon_state = "foamdart_empty"
- desc = "It's nerf or nothing! ... Although, this one doesn't look too safe."
- if(BB)
- BB.icon_state = "foamdart_empty"
- else
- icon_state = initial(icon_state)
- desc = "It's nerf or nothing! Ages 8 and up."
- if(BB)
- BB.icon_state = initial(BB.icon_state)
-
-
-/obj/item/ammo_casing/caseless/foam_dart/attackby(obj/item/A, mob/user, params)
- var/obj/item/projectile/bullet/reusable/foam_dart/FD = BB
- if (istype(A, /obj/item/screwdriver) && !modified)
- modified = 1
- FD.modified = 1
- FD.damage_type = BRUTE
- to_chat(user, "You pop the safety cap off [src].")
- update_icon()
- else if (istype(A, /obj/item/pen))
- if(modified)
- if(!FD.pen)
- if(!user.transferItemToLoc(A, FD))
- return
- FD.pen = A
- FD.damage = 5
- FD.nodamage = 0
- to_chat(user, "You insert [A] into [src].")
- else
- to_chat(user, "There's already something in [src].")
- else
- to_chat(user, "The safety cap prevents you from inserting [A] into [src].")
- else
- return ..()
-
-/obj/item/ammo_casing/caseless/foam_dart/attack_self(mob/living/user)
- var/obj/item/projectile/bullet/reusable/foam_dart/FD = BB
- if(FD.pen)
- FD.damage = initial(FD.damage)
- FD.nodamage = initial(FD.nodamage)
- user.put_in_hands(FD.pen)
- to_chat(user, "You remove [FD.pen] from [src].")
- FD.pen = null
-
-/obj/item/ammo_casing/caseless/foam_dart/riot
- name = "riot foam dart"
- desc = "Whose smart idea was it to use toys as crowd control? Ages 18 and up."
- projectile_type = /obj/item/projectile/bullet/reusable/foam_dart/riot
- icon_state = "foamdart_riot"
+/obj/item/ammo_casing/caseless/foam_dart
+ name = "foam dart"
+ desc = "It's nerf or nothing! Ages 8 and up."
+ projectile_type = /obj/item/projectile/bullet/reusable/foam_dart
+ caliber = "foam_force"
+ icon = 'icons/obj/guns/toy.dmi'
+ icon_state = "foamdart"
+ var/modified = 0
+
+/obj/item/ammo_casing/caseless/foam_dart/update_icon()
+ ..()
+ if (modified)
+ icon_state = "foamdart_empty"
+ desc = "It's nerf or nothing! ... Although, this one doesn't look too safe."
+ if(BB)
+ BB.icon_state = "foamdart_empty"
+ else
+ icon_state = initial(icon_state)
+ desc = "It's nerf or nothing! Ages 8 and up."
+ if(BB)
+ BB.icon_state = initial(BB.icon_state)
+
+
+/obj/item/ammo_casing/caseless/foam_dart/attackby(obj/item/A, mob/user, params)
+ var/obj/item/projectile/bullet/reusable/foam_dart/FD = BB
+ if (istype(A, /obj/item/screwdriver) && !modified)
+ modified = 1
+ FD.modified = 1
+ FD.damage_type = BRUTE
+ to_chat(user, "You pop the safety cap off [src].")
+ update_icon()
+ else if (istype(A, /obj/item/pen))
+ if(modified)
+ if(!FD.pen)
+ if(!user.transferItemToLoc(A, FD))
+ return
+ FD.pen = A
+ FD.damage = 5
+ FD.nodamage = 0
+ to_chat(user, "You insert [A] into [src].")
+ else
+ to_chat(user, "There's already something in [src].")
+ else
+ to_chat(user, "The safety cap prevents you from inserting [A] into [src].")
+ else
+ return ..()
+
+/obj/item/ammo_casing/caseless/foam_dart/attack_self(mob/living/user)
+ var/obj/item/projectile/bullet/reusable/foam_dart/FD = BB
+ if(FD.pen)
+ FD.damage = initial(FD.damage)
+ FD.nodamage = initial(FD.nodamage)
+ user.put_in_hands(FD.pen)
+ to_chat(user, "You remove [FD.pen] from [src].")
+ FD.pen = null
+
+/obj/item/ammo_casing/caseless/foam_dart/riot
+ name = "riot foam dart"
+ desc = "Whose smart idea was it to use toys as crowd control? Ages 18 and up."
+ projectile_type = /obj/item/projectile/bullet/reusable/foam_dart/riot
+ icon_state = "foamdart_riot"
diff --git a/code/modules/projectiles/ammunition/caseless/misc.dm b/code/modules/projectiles/ammunition/caseless/misc.dm
new file mode 100644
index 0000000000..fcb491f071
--- /dev/null
+++ b/code/modules/projectiles/ammunition/caseless/misc.dm
@@ -0,0 +1,22 @@
+/obj/item/ammo_casing/caseless/magspear
+ name = "magnetic spear"
+ desc = "A reusable spear that is typically loaded into kinetic spearguns."
+ projectile_type = /obj/item/projectile/bullet/reusable/magspear
+ caliber = "speargun"
+ icon_state = "magspear"
+ throwforce = 15 //still deadly when thrown
+ throw_speed = 3
+
+/obj/item/ammo_casing/caseless/laser
+ name = "laser casing"
+ desc = "You shouldn't be seeing this."
+ caliber = "laser"
+ icon_state = "s-casing-live"
+ projectile_type = /obj/item/projectile/beam
+ fire_sound = 'sound/weapons/laser.ogg'
+ firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
+
+/obj/item/ammo_casing/caseless/laser/gatling
+ projectile_type = /obj/item/projectile/beam/weak
+ variance = 0.8
+ click_cooldown_override = 1
diff --git a/code/modules/projectiles/ammunition/caseless/rocket.dm b/code/modules/projectiles/ammunition/caseless/rocket.dm
new file mode 100644
index 0000000000..0b74f6ff8c
--- /dev/null
+++ b/code/modules/projectiles/ammunition/caseless/rocket.dm
@@ -0,0 +1,11 @@
+/obj/item/ammo_casing/caseless/a84mm
+ desc = "An 84mm anti-armour rocket."
+ caliber = "84mm"
+ icon_state = "s-casing-live"
+ projectile_type = /obj/item/projectile/bullet/a84mm
+
+/obj/item/ammo_casing/caseless/a75
+ desc = "A .75 bullet casing."
+ caliber = "75"
+ icon_state = "s-casing-live"
+ projectile_type = /obj/item/projectile/bullet/gyro
diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm
deleted file mode 100644
index 96d0fd2e29..0000000000
--- a/code/modules/projectiles/ammunition/energy.dm
+++ /dev/null
@@ -1,258 +0,0 @@
-/obj/item/ammo_casing/energy
- name = "energy weapon lens"
- desc = "The part of the gun that makes the laser go pew."
- caliber = "energy"
- projectile_type = /obj/item/projectile/energy
- var/e_cost = 100 //The amount of energy a cell needs to expend to create this shot.
- var/select_name = "energy"
- fire_sound = 'sound/weapons/laser.ogg'
- firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
- heavy_metal = FALSE
-
-/obj/item/ammo_casing/energy/chameleon
- projectile_type = /obj/item/projectile/energy/chameleon
- e_cost = 0
- var/hitscan_mode = FALSE
- var/list/projectile_vars = list()
-
-/obj/item/ammo_casing/energy/chameleon/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
- . = ..()
- if(!BB)
- newshot()
- for(var/V in projectile_vars)
- if(BB.vars.Find(V))
- BB.vars[V] = projectile_vars[V]
- if(hitscan_mode)
- BB.hitscan = TRUE
-
-/obj/item/ammo_casing/energy/laser
- projectile_type = /obj/item/projectile/beam/laser
- select_name = "kill"
-
-/obj/item/ammo_casing/energy/lasergun
- projectile_type = /obj/item/projectile/beam/laser
- e_cost = 83
- select_name = "kill"
-
-/obj/item/ammo_casing/energy/lasergun/old
- projectile_type = /obj/item/projectile/beam/laser
- e_cost = 200
- select_name = "kill"
-
-/obj/item/ammo_casing/energy/laser/hos
- e_cost = 100
-
-/obj/item/ammo_casing/energy/laser/practice
- projectile_type = /obj/item/projectile/beam/practice
- select_name = "practice"
-
-/obj/item/ammo_casing/energy/laser/scatter
- projectile_type = /obj/item/projectile/beam/scatter
- pellets = 5
- variance = 25
- select_name = "scatter"
-
-/obj/item/ammo_casing/energy/laser/scatter/disabler
- projectile_type = /obj/item/projectile/beam/disabler
- pellets = 3
- variance = 15
-
-/obj/item/ammo_casing/energy/laser/heavy
- projectile_type = /obj/item/projectile/beam/laser/heavylaser
- select_name = "anti-vehicle"
- fire_sound = 'sound/weapons/lasercannonfire.ogg'
-
-/obj/item/ammo_casing/energy/laser/pulse
- projectile_type = /obj/item/projectile/beam/pulse
- e_cost = 200
- select_name = "DESTROY"
- fire_sound = 'sound/weapons/pulse.ogg'
-
-/obj/item/ammo_casing/energy/laser/bluetag
- projectile_type = /obj/item/projectile/beam/lasertag/bluetag
- select_name = "bluetag"
-
-/obj/item/ammo_casing/energy/laser/bluetag/hitscan
- projectile_type = /obj/item/projectile/beam/lasertag/bluetag/hitscan
-
-/obj/item/ammo_casing/energy/laser/redtag
- projectile_type = /obj/item/projectile/beam/lasertag/redtag
- select_name = "redtag"
-
-/obj/item/ammo_casing/energy/laser/redtag/hitscan
- projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan
-
-/obj/item/ammo_casing/energy/xray
- projectile_type = /obj/item/projectile/beam/xray
- e_cost = 50
- fire_sound = 'sound/weapons/laser3.ogg'
-
-/obj/item/ammo_casing/energy/electrode
- projectile_type = /obj/item/projectile/energy/electrode
- select_name = "stun"
- fire_sound = 'sound/weapons/taser.ogg'
- e_cost = 200
-
-/obj/item/ammo_casing/energy/electrode/spec
- e_cost = 100
-
-/obj/item/ammo_casing/energy/electrode/gun
- fire_sound = 'sound/weapons/gunshot.ogg'
- e_cost = 100
-
-/obj/item/ammo_casing/energy/electrode/hos
- e_cost = 200
-
-/obj/item/ammo_casing/energy/electrode/old
- e_cost = 1000
-
-/obj/item/ammo_casing/energy/ion
- projectile_type = /obj/item/projectile/ion
- select_name = "ion"
- fire_sound = 'sound/weapons/ionrifle.ogg'
-
-/obj/item/ammo_casing/energy/declone
- projectile_type = /obj/item/projectile/energy/declone
- select_name = "declone"
- fire_sound = 'sound/weapons/pulse3.ogg'
-
-/obj/item/ammo_casing/energy/mindflayer
- projectile_type = /obj/item/projectile/beam/mindflayer
- select_name = "MINDFUCK"
- fire_sound = 'sound/weapons/laser.ogg'
-
-/obj/item/ammo_casing/energy/flora
- fire_sound = 'sound/effects/stealthoff.ogg'
-
-/obj/item/ammo_casing/energy/flora/yield
- projectile_type = /obj/item/projectile/energy/florayield
- select_name = "yield"
-
-/obj/item/ammo_casing/energy/flora/mut
- projectile_type = /obj/item/projectile/energy/floramut
- select_name = "mutation"
-
-/obj/item/ammo_casing/energy/temp
- projectile_type = /obj/item/projectile/temp
- select_name = "freeze"
- e_cost = 250
- fire_sound = 'sound/weapons/pulse3.ogg'
-
-/obj/item/ammo_casing/energy/temp/hot
- projectile_type = /obj/item/projectile/temp/hot
- select_name = "bake"
-
-/obj/item/ammo_casing/energy/meteor
- projectile_type = /obj/item/projectile/meteor
- select_name = "goddamn meteor"
-
-/obj/item/ammo_casing/energy/disabler
- projectile_type = /obj/item/projectile/beam/disabler
- select_name = "disable"
- e_cost = 50
- fire_sound = 'sound/weapons/taser2.ogg'
-
-/obj/item/ammo_casing/energy/plasma
- projectile_type = /obj/item/projectile/plasma
- select_name = "plasma burst"
- fire_sound = 'sound/weapons/plasma_cutter.ogg'
- delay = 15
- e_cost = 25
-
-/obj/item/ammo_casing/energy/plasma/adv
- projectile_type = /obj/item/projectile/plasma/adv
- delay = 10
- e_cost = 10
-
-/obj/item/ammo_casing/energy/wormhole
- projectile_type = /obj/item/projectile/beam/wormhole
- e_cost = 0
- fire_sound = 'sound/weapons/pulse3.ogg'
- var/obj/item/gun/energy/wormhole_projector/gun = null
- select_name = "blue"
-
-/obj/item/ammo_casing/energy/wormhole/orange
- projectile_type = /obj/item/projectile/beam/wormhole/orange
- select_name = "orange"
-
-/obj/item/ammo_casing/energy/bolt
- projectile_type = /obj/item/projectile/energy/bolt
- select_name = "bolt"
- e_cost = 500
- fire_sound = 'sound/weapons/genhit.ogg'
-
-/obj/item/ammo_casing/energy/bolt/halloween
- projectile_type = /obj/item/projectile/energy/bolt/halloween
-
-/obj/item/ammo_casing/energy/bolt/large
- projectile_type = /obj/item/projectile/energy/bolt/large
- select_name = "heavy bolt"
-
-/obj/item/ammo_casing/energy/net
- projectile_type = /obj/item/projectile/energy/net
- select_name = "netting"
- pellets = 6
- variance = 40
-
-/obj/item/ammo_casing/energy/trap
- projectile_type = /obj/item/projectile/energy/trap
- select_name = "snare"
-
-/obj/item/ammo_casing/energy/instakill
- projectile_type = /obj/item/projectile/beam/instakill
- e_cost = 0
- select_name = "DESTROY"
-
-/obj/item/ammo_casing/energy/instakill/blue
- projectile_type = /obj/item/projectile/beam/instakill/blue
-
-/obj/item/ammo_casing/energy/instakill/red
- projectile_type = /obj/item/projectile/beam/instakill/red
-
-/obj/item/ammo_casing/energy/tesla_revolver
- fire_sound = 'sound/magic/lightningbolt.ogg'
- e_cost = 200
- select_name = "stun"
- projectile_type = /obj/item/projectile/energy/tesla/revolver
-
-/obj/item/ammo_casing/energy/gravityrepulse
- projectile_type = /obj/item/projectile/gravityrepulse
- e_cost = 0
- fire_sound = 'sound/weapons/wave.ogg'
- select_name = "repulse"
- delay = 50
- var/obj/item/gun/energy/gravity_gun/gun = null
-
-/obj/item/ammo_casing/energy/gravityrepulse/New(var/obj/item/gun/energy/gravity_gun/G)
- gun = G
-
-/obj/item/ammo_casing/energy/gravityattract
- projectile_type = /obj/item/projectile/gravityattract
- e_cost = 0
- fire_sound = 'sound/weapons/wave.ogg'
- select_name = "attract"
- delay = 50
- var/obj/item/gun/energy/gravity_gun/gun = null
-
-
-/obj/item/ammo_casing/energy/gravityattract/New(var/obj/item/gun/energy/gravity_gun/G)
- gun = G
-
-/obj/item/ammo_casing/energy/gravitychaos
- projectile_type = /obj/item/projectile/gravitychaos
- e_cost = 0
- fire_sound = 'sound/weapons/wave.ogg'
- select_name = "chaos"
- delay = 50
- var/obj/item/gun/energy/gravity_gun/gun = null
-
-/obj/item/ammo_casing/energy/gravitychaos/New(var/obj/item/gun/energy/gravity_gun/G)
- gun = G
-
-/obj/item/ammo_casing/energy/plasma
- projectile_type = /obj/item/projectile/plasma
- select_name = "plasma burst"
- fire_sound = 'sound/weapons/pulse.ogg'
-
-/obj/item/ammo_casing/energy/plasma/adv
- projectile_type = /obj/item/projectile/plasma/adv
diff --git a/code/modules/projectiles/ammunition/energy/_energy.dm b/code/modules/projectiles/ammunition/energy/_energy.dm
new file mode 100644
index 0000000000..3a4e457c3d
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/_energy.dm
@@ -0,0 +1,10 @@
+/obj/item/ammo_casing/energy
+ name = "energy weapon lens"
+ desc = "The part of the gun that makes the laser go pew."
+ caliber = "energy"
+ projectile_type = /obj/item/projectile/energy
+ var/e_cost = 100 //The amount of energy a cell needs to expend to create this shot.
+ var/select_name = "energy"
+ fire_sound = 'sound/weapons/laser.ogg'
+ firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
+ heavy_metal = FALSE
diff --git a/code/modules/projectiles/ammunition/energy/chameleon.dm b/code/modules/projectiles/ammunition/energy/chameleon.dm
new file mode 100644
index 0000000000..4688518d46
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/chameleon.dm
@@ -0,0 +1,15 @@
+/obj/item/ammo_casing/energy/chameleon
+ projectile_type = /obj/item/projectile/energy/chameleon
+ e_cost = 0
+ var/hitscan_mode = FALSE
+ var/list/projectile_vars = list()
+
+/obj/item/ammo_casing/energy/chameleon/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+ . = ..()
+ if(!BB)
+ newshot()
+ for(var/V in projectile_vars)
+ if(BB.vars.Find(V))
+ BB.vars[V] = projectile_vars[V]
+ if(hitscan_mode)
+ BB.hitscan = TRUE
diff --git a/code/modules/projectiles/ammunition/energy/ebow.dm b/code/modules/projectiles/ammunition/energy/ebow.dm
new file mode 100644
index 0000000000..8d9c72d1ba
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/ebow.dm
@@ -0,0 +1,12 @@
+/obj/item/ammo_casing/energy/bolt
+ projectile_type = /obj/item/projectile/energy/bolt
+ select_name = "bolt"
+ e_cost = 500
+ fire_sound = 'sound/weapons/genhit.ogg'
+
+/obj/item/ammo_casing/energy/bolt/halloween
+ projectile_type = /obj/item/projectile/energy/bolt/halloween
+
+/obj/item/ammo_casing/energy/bolt/large
+ projectile_type = /obj/item/projectile/energy/bolt/large
+ select_name = "heavy bolt"
diff --git a/code/modules/projectiles/ammunition/energy/gravity.dm b/code/modules/projectiles/ammunition/energy/gravity.dm
new file mode 100644
index 0000000000..f549a5b5e4
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/gravity.dm
@@ -0,0 +1,36 @@
+/obj/item/ammo_casing/energy/gravityrepulse
+ projectile_type = /obj/item/projectile/gravityrepulse
+ e_cost = 0
+ fire_sound = 'sound/weapons/wave.ogg'
+ select_name = "repulse"
+ delay = 50
+ var/obj/item/gun/energy/gravity_gun/gun
+
+/obj/item/ammo_casing/energy/gravityrepulse/Initialize(mapload, obj/item/gun/energy/gravity_gun/G)
+ . = ..()
+ gun = G
+
+/obj/item/ammo_casing/energy/gravityattract
+ projectile_type = /obj/item/projectile/gravityattract
+ e_cost = 0
+ fire_sound = 'sound/weapons/wave.ogg'
+ select_name = "attract"
+ delay = 50
+ var/obj/item/gun/energy/gravity_gun/gun
+
+
+/obj/item/ammo_casing/energy/gravityattract/Initialize(mapload, obj/item/gun/energy/gravity_gun/G)
+ . = ..()
+ gun = G
+
+/obj/item/ammo_casing/energy/gravitychaos
+ projectile_type = /obj/item/projectile/gravitychaos
+ e_cost = 0
+ fire_sound = 'sound/weapons/wave.ogg'
+ select_name = "chaos"
+ delay = 50
+ var/obj/item/gun/energy/gravity_gun/gun
+
+/obj/item/ammo_casing/energy/gravitychaos/Initialize(mapload, obj/item/gun/energy/gravity_gun/G)
+ . = ..()
+ gun = G
diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm
new file mode 100644
index 0000000000..c87ea2ffbd
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/laser.dm
@@ -0,0 +1,66 @@
+/obj/item/ammo_casing/energy/laser
+ projectile_type = /obj/item/projectile/beam/laser
+ select_name = "kill"
+
+/obj/item/ammo_casing/energy/lasergun
+ projectile_type = /obj/item/projectile/beam/laser
+ e_cost = 83
+ select_name = "kill"
+
+/obj/item/ammo_casing/energy/lasergun/old
+ projectile_type = /obj/item/projectile/beam/laser
+ e_cost = 200
+ select_name = "kill"
+
+/obj/item/ammo_casing/energy/laser/hos
+ e_cost = 100
+
+/obj/item/ammo_casing/energy/laser/practice
+ projectile_type = /obj/item/projectile/beam/practice
+ select_name = "practice"
+
+/obj/item/ammo_casing/energy/laser/scatter
+ projectile_type = /obj/item/projectile/beam/scatter
+ pellets = 5
+ variance = 25
+ select_name = "scatter"
+
+/obj/item/ammo_casing/energy/laser/scatter/disabler
+ projectile_type = /obj/item/projectile/beam/disabler
+ pellets = 3
+ variance = 15
+
+/obj/item/ammo_casing/energy/laser/heavy
+ projectile_type = /obj/item/projectile/beam/laser/heavylaser
+ select_name = "anti-vehicle"
+ fire_sound = 'sound/weapons/lasercannonfire.ogg'
+
+/obj/item/ammo_casing/energy/laser/pulse
+ projectile_type = /obj/item/projectile/beam/pulse
+ e_cost = 200
+ select_name = "DESTROY"
+ fire_sound = 'sound/weapons/pulse.ogg'
+
+/obj/item/ammo_casing/energy/laser/bluetag
+ projectile_type = /obj/item/projectile/beam/lasertag/bluetag
+ select_name = "bluetag"
+
+/obj/item/ammo_casing/energy/laser/bluetag/hitscan
+ projectile_type = /obj/item/projectile/beam/lasertag/bluetag/hitscan
+
+/obj/item/ammo_casing/energy/laser/redtag
+ projectile_type = /obj/item/projectile/beam/lasertag/redtag
+ select_name = "redtag"
+
+/obj/item/ammo_casing/energy/laser/redtag/hitscan
+ projectile_type = /obj/item/projectile/beam/lasertag/redtag/hitscan
+
+/obj/item/ammo_casing/energy/xray
+ projectile_type = /obj/item/projectile/beam/xray
+ e_cost = 50
+ fire_sound = 'sound/weapons/laser3.ogg'
+
+/obj/item/ammo_casing/energy/mindflayer
+ projectile_type = /obj/item/projectile/beam/mindflayer
+ select_name = "MINDFUCK"
+ fire_sound = 'sound/weapons/laser.ogg'
diff --git a/code/modules/projectiles/ammunition/energy/lmg.dm b/code/modules/projectiles/ammunition/energy/lmg.dm
new file mode 100644
index 0000000000..5ebe83f792
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/lmg.dm
@@ -0,0 +1,6 @@
+/obj/item/ammo_casing/energy/c3dbullet
+ projectile_type = /obj/item/projectile/bullet/c3d
+ select_name = "spraydown"
+ fire_sound = 'sound/weapons/gunshot_smg.ogg'
+ e_cost = 20
+ firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect
diff --git a/code/modules/projectiles/ammunition/energy/plasma.dm b/code/modules/projectiles/ammunition/energy/plasma.dm
new file mode 100644
index 0000000000..d02abf9c88
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/plasma.dm
@@ -0,0 +1,11 @@
+/obj/item/ammo_casing/energy/plasma
+ projectile_type = /obj/item/projectile/plasma
+ select_name = "plasma burst"
+ fire_sound = 'sound/weapons/plasma_cutter.ogg'
+ delay = 15
+ e_cost = 25
+
+/obj/item/ammo_casing/energy/plasma/adv
+ projectile_type = /obj/item/projectile/plasma/adv
+ delay = 10
+ e_cost = 10
diff --git a/code/modules/projectiles/ammunition/plasma.dm b/code/modules/projectiles/ammunition/energy/plasma_cit.dm
similarity index 100%
rename from code/modules/projectiles/ammunition/plasma.dm
rename to code/modules/projectiles/ammunition/energy/plasma_cit.dm
diff --git a/code/modules/projectiles/ammunition/energy/portal.dm b/code/modules/projectiles/ammunition/energy/portal.dm
new file mode 100644
index 0000000000..3a6300a2f5
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/portal.dm
@@ -0,0 +1,14 @@
+/obj/item/ammo_casing/energy/wormhole
+ projectile_type = /obj/item/projectile/beam/wormhole
+ e_cost = 0
+ fire_sound = 'sound/weapons/pulse3.ogg'
+ var/obj/item/gun/energy/wormhole_projector/gun = null
+ select_name = "blue"
+
+/obj/item/ammo_casing/energy/wormhole/orange
+ projectile_type = /obj/item/projectile/beam/wormhole/orange
+ select_name = "orange"
+
+/obj/item/ammo_casing/energy/wormhole/Initialize(mapload, obj/item/gun/energy/wormhole_projector/wh)
+ . = ..()
+ gun = wh
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
new file mode 100644
index 0000000000..0438baf490
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -0,0 +1,61 @@
+/obj/item/ammo_casing/energy/ion
+ projectile_type = /obj/item/projectile/ion
+ select_name = "ion"
+ fire_sound = 'sound/weapons/ionrifle.ogg'
+
+/obj/item/ammo_casing/energy/declone
+ projectile_type = /obj/item/projectile/energy/declone
+ select_name = "declone"
+ fire_sound = 'sound/weapons/pulse3.ogg'
+
+/obj/item/ammo_casing/energy/flora
+ fire_sound = 'sound/effects/stealthoff.ogg'
+
+/obj/item/ammo_casing/energy/flora/yield
+ projectile_type = /obj/item/projectile/energy/florayield
+ select_name = "yield"
+
+/obj/item/ammo_casing/energy/flora/mut
+ projectile_type = /obj/item/projectile/energy/floramut
+ select_name = "mutation"
+
+/obj/item/ammo_casing/energy/temp
+ projectile_type = /obj/item/projectile/temp
+ select_name = "freeze"
+ e_cost = 250
+ fire_sound = 'sound/weapons/pulse3.ogg'
+
+/obj/item/ammo_casing/energy/temp/hot
+ projectile_type = /obj/item/projectile/temp/hot
+ select_name = "bake"
+
+/obj/item/ammo_casing/energy/meteor
+ projectile_type = /obj/item/projectile/meteor
+ select_name = "goddamn meteor"
+
+/obj/item/ammo_casing/energy/net
+ projectile_type = /obj/item/projectile/energy/net
+ select_name = "netting"
+ pellets = 6
+ variance = 40
+
+/obj/item/ammo_casing/energy/trap
+ projectile_type = /obj/item/projectile/energy/trap
+ select_name = "snare"
+
+/obj/item/ammo_casing/energy/instakill
+ projectile_type = /obj/item/projectile/beam/instakill
+ e_cost = 0
+ select_name = "DESTROY"
+
+/obj/item/ammo_casing/energy/instakill/blue
+ projectile_type = /obj/item/projectile/beam/instakill/blue
+
+/obj/item/ammo_casing/energy/instakill/red
+ projectile_type = /obj/item/projectile/beam/instakill/red
+
+/obj/item/ammo_casing/energy/tesla_revolver
+ fire_sound = 'sound/magic/lightningbolt.ogg'
+ e_cost = 200
+ select_name = "stun"
+ projectile_type = /obj/item/projectile/energy/tesla/revolver
diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm
new file mode 100644
index 0000000000..5a88a97b08
--- /dev/null
+++ b/code/modules/projectiles/ammunition/energy/stun.dm
@@ -0,0 +1,24 @@
+/obj/item/ammo_casing/energy/electrode
+ projectile_type = /obj/item/projectile/energy/electrode
+ select_name = "stun"
+ fire_sound = 'sound/weapons/taser.ogg'
+ e_cost = 200
+
+/obj/item/ammo_casing/energy/electrode/spec
+ e_cost = 100
+
+/obj/item/ammo_casing/energy/electrode/gun
+ fire_sound = 'sound/weapons/gunshot.ogg'
+ e_cost = 100
+
+/obj/item/ammo_casing/energy/electrode/hos
+ e_cost = 200
+
+/obj/item/ammo_casing/energy/electrode/old
+ e_cost = 1000
+
+/obj/item/ammo_casing/energy/disabler
+ projectile_type = /obj/item/projectile/beam/disabler
+ select_name = "disable"
+ e_cost = 50
+ fire_sound = 'sound/weapons/taser2.ogg'
diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm
new file mode 100644
index 0000000000..6ebf5739a9
--- /dev/null
+++ b/code/modules/projectiles/ammunition/special/magic.dm
@@ -0,0 +1,42 @@
+/obj/item/ammo_casing/magic
+ name = "magic casing"
+ desc = "I didn't even know magic needed ammo..."
+ projectile_type = /obj/item/projectile/magic
+ firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/magic
+ heavy_metal = FALSE
+
+/obj/item/ammo_casing/magic/change
+ projectile_type = /obj/item/projectile/magic/change
+
+/obj/item/ammo_casing/magic/animate
+ projectile_type = /obj/item/projectile/magic/animate
+
+/obj/item/ammo_casing/magic/heal
+ projectile_type = /obj/item/projectile/magic/resurrection
+
+/obj/item/ammo_casing/magic/death
+ projectile_type = /obj/item/projectile/magic/death
+
+/obj/item/ammo_casing/magic/teleport
+ projectile_type = /obj/item/projectile/magic/teleport
+
+/obj/item/ammo_casing/magic/door
+ projectile_type = /obj/item/projectile/magic/door
+
+/obj/item/ammo_casing/magic/fireball
+ projectile_type = /obj/item/projectile/magic/aoe/fireball
+
+/obj/item/ammo_casing/magic/chaos
+ projectile_type = /obj/item/projectile/magic
+
+/obj/item/ammo_casing/magic/spellblade
+ projectile_type = /obj/item/projectile/magic/spellblade
+
+/obj/item/ammo_casing/magic/arcane_barrage
+ projectile_type = /obj/item/projectile/magic/arcane_barrage
+
+/obj/item/ammo_casing/magic/chaos/newshot()
+ ..()
+
+/obj/item/ammo_casing/magic/honk
+ projectile_type = /obj/item/projectile/bullet/honker
diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special/syringe.dm
similarity index 53%
rename from code/modules/projectiles/ammunition/special.dm
rename to code/modules/projectiles/ammunition/special/syringe.dm
index b378d7fa6c..4a2a354ca6 100644
--- a/code/modules/projectiles/ammunition/special.dm
+++ b/code/modules/projectiles/ammunition/special/syringe.dm
@@ -1,111 +1,61 @@
-/obj/item/ammo_casing/magic
- name = "magic casing"
- desc = "I didn't even know magic needed ammo..."
- projectile_type = /obj/item/projectile/magic
- firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/magic
- heavy_metal = FALSE
-
-/obj/item/ammo_casing/magic/change
- projectile_type = /obj/item/projectile/magic/change
-
-/obj/item/ammo_casing/magic/animate
- projectile_type = /obj/item/projectile/magic/animate
-
-/obj/item/ammo_casing/magic/heal
- projectile_type = /obj/item/projectile/magic/resurrection
-
-/obj/item/ammo_casing/magic/death
- projectile_type = /obj/item/projectile/magic/death
-
-/obj/item/ammo_casing/magic/teleport
- projectile_type = /obj/item/projectile/magic/teleport
-
-/obj/item/ammo_casing/magic/door
- projectile_type = /obj/item/projectile/magic/door
-
-/obj/item/ammo_casing/magic/fireball
- projectile_type = /obj/item/projectile/magic/aoe/fireball
-
-/obj/item/ammo_casing/magic/chaos
- projectile_type = /obj/item/projectile/magic
-
-/obj/item/ammo_casing/magic/spellblade
- projectile_type = /obj/item/projectile/magic/spellblade
-
-/obj/item/ammo_casing/magic/arcane_barrage
- projectile_type = /obj/item/projectile/magic/arcane_barrage
-
-/obj/item/ammo_casing/magic/chaos/newshot()
- ..()
-
-/obj/item/ammo_casing/magic/honk
- projectile_type = /obj/item/projectile/bullet/honker
-
-/obj/item/ammo_casing/syringegun
- name = "syringe gun spring"
- desc = "A high-power spring that throws syringes."
- projectile_type = /obj/item/projectile/bullet/dart/syringe
- firing_effect_type = null
-
-/obj/item/ammo_casing/syringegun/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
- if(!BB)
- return
- if(istype(loc, /obj/item/gun/syringe))
- var/obj/item/gun/syringe/SG = loc
- if(!SG.syringes.len)
- return
-
- var/obj/item/reagent_containers/syringe/S = SG.syringes[1]
-
- S.reagents.trans_to(BB, S.reagents.total_volume)
- BB.name = S.name
- var/obj/item/projectile/bullet/dart/D = BB
- D.piercing = S.proj_piercing
- SG.syringes.Remove(S)
- qdel(S)
- ..()
-
-/obj/item/ammo_casing/chemgun
- name = "dart synthesiser"
- desc = "A high-power spring, linked to an energy-based dart synthesiser."
- projectile_type = /obj/item/projectile/bullet/dart
- firing_effect_type = null
-
-/obj/item/ammo_casing/chemgun/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
- if(!BB)
- return
- if(istype(loc, /obj/item/gun/chem))
- var/obj/item/gun/chem/CG = loc
- if(CG.syringes_left <= 0)
- return
- CG.reagents.trans_to(BB, 15)
- BB.name = "chemical dart"
- CG.syringes_left--
- ..()
-
-/obj/item/ammo_casing/dnainjector
- name = "rigged syringe gun spring"
- desc = "A high-power spring that throws DNA injectors."
- projectile_type = /obj/item/projectile/bullet/dnainjector
- firing_effect_type = null
-
-/obj/item/ammo_casing/dnainjector/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
- if(!BB)
- return
- if(istype(loc, /obj/item/gun/syringe/dna))
- var/obj/item/gun/syringe/dna/SG = loc
- if(!SG.syringes.len)
- return
-
- var/obj/item/dnainjector/S = popleft(SG.syringes)
- var/obj/item/projectile/bullet/dnainjector/D = BB
- S.forceMove(D)
- D.injector = S
- ..()
-
-/obj/item/ammo_casing/energy/c3dbullet
- projectile_type = /obj/item/projectile/bullet/c3d
- select_name = "spraydown"
- fire_sound = 'sound/weapons/gunshot_smg.ogg'
- e_cost = 20
- firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect
+/obj/item/ammo_casing/syringegun
+ name = "syringe gun spring"
+ desc = "A high-power spring that throws syringes."
+ projectile_type = /obj/item/projectile/bullet/dart/syringe
+ firing_effect_type = null
+
+/obj/item/ammo_casing/syringegun/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+ if(!BB)
+ return
+ if(istype(loc, /obj/item/gun/syringe))
+ var/obj/item/gun/syringe/SG = loc
+ if(!SG.syringes.len)
+ return
+
+ var/obj/item/reagent_containers/syringe/S = SG.syringes[1]
+
+ S.reagents.trans_to(BB, S.reagents.total_volume)
+ BB.name = S.name
+ var/obj/item/projectile/bullet/dart/D = BB
+ D.piercing = S.proj_piercing
+ SG.syringes.Remove(S)
+ qdel(S)
+ ..()
+
+/obj/item/ammo_casing/chemgun
+ name = "dart synthesiser"
+ desc = "A high-power spring, linked to an energy-based dart synthesiser."
+ projectile_type = /obj/item/projectile/bullet/dart
+ firing_effect_type = null
+
+/obj/item/ammo_casing/chemgun/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+ if(!BB)
+ return
+ if(istype(loc, /obj/item/gun/chem))
+ var/obj/item/gun/chem/CG = loc
+ if(CG.syringes_left <= 0)
+ return
+ CG.reagents.trans_to(BB, 15)
+ BB.name = "chemical dart"
+ CG.syringes_left--
+ ..()
+
+/obj/item/ammo_casing/dnainjector
+ name = "rigged syringe gun spring"
+ desc = "A high-power spring that throws DNA injectors."
+ projectile_type = /obj/item/projectile/bullet/dnainjector
+ firing_effect_type = null
+
+/obj/item/ammo_casing/dnainjector/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+ if(!BB)
+ return
+ if(istype(loc, /obj/item/gun/syringe/dna))
+ var/obj/item/gun/syringe/dna/SG = loc
+ if(!SG.syringes.len)
+ return
+
+ var/obj/item/dnainjector/S = popleft(SG.syringes)
+ var/obj/item/projectile/bullet/dnainjector/D = BB
+ S.forceMove(D)
+ D.injector = S
+ ..()
diff --git a/code/modules/projectiles/box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
similarity index 96%
rename from code/modules/projectiles/box_magazine.dm
rename to code/modules/projectiles/boxes_magazines/_box_magazine.dm
index 57860e7910..1c5a2b1199 100644
--- a/code/modules/projectiles/box_magazine.dm
+++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
@@ -1,121 +1,121 @@
-//Boxes of ammo
-/obj/item/ammo_box
- name = "ammo box (null_reference_exception)"
- desc = "A box of ammo."
- icon_state = "357"
- icon = 'icons/obj/ammo.dmi'
- flags_1 = CONDUCT_1
- slot_flags = SLOT_BELT
- item_state = "syringe_kit"
- lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- materials = list(MAT_METAL=30000)
- throwforce = 2
- w_class = WEIGHT_CLASS_TINY
- throw_speed = 3
- throw_range = 7
- var/list/stored_ammo = list()
- var/ammo_type = /obj/item/ammo_casing
- var/max_ammo = 7
- var/multiple_sprites = 0
- var/caliber
- var/multiload = 1
- var/start_empty = 0
-
-/obj/item/ammo_box/Initialize()
- . = ..()
- if(!start_empty)
- for(var/i = 1, i <= max_ammo, i++)
- stored_ammo += new ammo_type(src)
- update_icon()
-
-/obj/item/ammo_box/proc/get_round(keep = 0)
- if (!stored_ammo.len)
- return null
- else
- var/b = stored_ammo[stored_ammo.len]
- stored_ammo -= b
- if (keep)
- stored_ammo.Insert(1,b)
- return b
-
-/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0)
- // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type.
- if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type))
- return 0
-
- if (stored_ammo.len < max_ammo)
- stored_ammo += R
- R.forceMove(src)
- return 1
-
- //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo
- else if(replace_spent)
- for(var/obj/item/ammo_casing/AC in stored_ammo)
- if(!AC.BB)//found a spent ammo
- stored_ammo -= AC
- AC.forceMove(get_turf(src.loc))
-
- stored_ammo += R
- R.forceMove(src)
- return 1
-
- return 0
-
-/obj/item/ammo_box/proc/can_load(mob/user)
- return 1
-
-/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = FALSE, replace_spent = 0)
- var/num_loaded = 0
- if(!can_load(user))
- return
- if(istype(A, /obj/item/ammo_box))
- var/obj/item/ammo_box/AM = A
- for(var/obj/item/ammo_casing/AC in AM.stored_ammo)
- var/did_load = give_round(AC, replace_spent)
- if(did_load)
- AM.stored_ammo -= AC
- num_loaded++
- if(!did_load || !multiload)
- break
- if(istype(A, /obj/item/ammo_casing))
- var/obj/item/ammo_casing/AC = A
- if(give_round(AC, replace_spent))
- user.transferItemToLoc(AC, src, TRUE)
- num_loaded++
-
- if(num_loaded)
- if(!silent)
- to_chat(user, "You load [num_loaded] shell\s into \the [src]!")
- playsound(src, 'sound/weapons/bulletinsert.ogg', 60, 1)
- A.update_icon()
- update_icon()
-
- return num_loaded
-
-/obj/item/ammo_box/attack_self(mob/user)
- var/obj/item/ammo_casing/A = get_round()
- if(A)
- if(!user.put_in_hands(A))
- A.bounce_away(FALSE, NONE)
- playsound(src, 'sound/weapons/bulletinsert.ogg', 60, 1)
- to_chat(user, "You remove a round from \the [src]!")
- update_icon()
-
-/obj/item/ammo_box/update_icon()
- switch(multiple_sprites)
- if(1)
- icon_state = "[initial(icon_state)]-[stored_ammo.len]"
- if(2)
- icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]"
- desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!"
-
-//Behavior for magazines
-/obj/item/ammo_box/magazine/proc/ammo_count()
- return stored_ammo.len
-
-/obj/item/ammo_box/magazine/proc/empty_magazine()
- var/turf_mag = get_turf(src)
- for(var/obj/item/ammo in stored_ammo)
- ammo.forceMove(turf_mag)
+//Boxes of ammo
+/obj/item/ammo_box
+ name = "ammo box (null_reference_exception)"
+ desc = "A box of ammo."
+ icon_state = "357"
+ icon = 'icons/obj/ammo.dmi'
+ flags_1 = CONDUCT_1
+ slot_flags = SLOT_BELT
+ item_state = "syringe_kit"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ materials = list(MAT_METAL=30000)
+ throwforce = 2
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ var/list/stored_ammo = list()
+ var/ammo_type = /obj/item/ammo_casing
+ var/max_ammo = 7
+ var/multiple_sprites = 0
+ var/caliber
+ var/multiload = 1
+ var/start_empty = 0
+
+/obj/item/ammo_box/Initialize()
+ . = ..()
+ if(!start_empty)
+ for(var/i = 1, i <= max_ammo, i++)
+ stored_ammo += new ammo_type(src)
+ update_icon()
+
+/obj/item/ammo_box/proc/get_round(keep = 0)
+ if (!stored_ammo.len)
+ return null
+ else
+ var/b = stored_ammo[stored_ammo.len]
+ stored_ammo -= b
+ if (keep)
+ stored_ammo.Insert(1,b)
+ return b
+
+/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0)
+ // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type.
+ if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type))
+ return 0
+
+ if (stored_ammo.len < max_ammo)
+ stored_ammo += R
+ R.forceMove(src)
+ return 1
+
+ //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo
+ else if(replace_spent)
+ for(var/obj/item/ammo_casing/AC in stored_ammo)
+ if(!AC.BB)//found a spent ammo
+ stored_ammo -= AC
+ AC.forceMove(get_turf(src.loc))
+
+ stored_ammo += R
+ R.forceMove(src)
+ return 1
+
+ return 0
+
+/obj/item/ammo_box/proc/can_load(mob/user)
+ return 1
+
+/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = FALSE, replace_spent = 0)
+ var/num_loaded = 0
+ if(!can_load(user))
+ return
+ if(istype(A, /obj/item/ammo_box))
+ var/obj/item/ammo_box/AM = A
+ for(var/obj/item/ammo_casing/AC in AM.stored_ammo)
+ var/did_load = give_round(AC, replace_spent)
+ if(did_load)
+ AM.stored_ammo -= AC
+ num_loaded++
+ if(!did_load || !multiload)
+ break
+ if(istype(A, /obj/item/ammo_casing))
+ var/obj/item/ammo_casing/AC = A
+ if(give_round(AC, replace_spent))
+ user.transferItemToLoc(AC, src, TRUE)
+ num_loaded++
+
+ if(num_loaded)
+ if(!silent)
+ to_chat(user, "You load [num_loaded] shell\s into \the [src]!")
+ playsound(src, 'sound/weapons/bulletinsert.ogg', 60, 1)
+ A.update_icon()
+ update_icon()
+
+ return num_loaded
+
+/obj/item/ammo_box/attack_self(mob/user)
+ var/obj/item/ammo_casing/A = get_round()
+ if(A)
+ if(!user.put_in_hands(A))
+ A.bounce_away(FALSE, NONE)
+ playsound(src, 'sound/weapons/bulletinsert.ogg', 60, 1)
+ to_chat(user, "You remove a round from \the [src]!")
+ update_icon()
+
+/obj/item/ammo_box/update_icon()
+ switch(multiple_sprites)
+ if(1)
+ icon_state = "[initial(icon_state)]-[stored_ammo.len]"
+ if(2)
+ icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]"
+ desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!"
+
+//Behavior for magazines
+/obj/item/ammo_box/magazine/proc/ammo_count()
+ return stored_ammo.len
+
+/obj/item/ammo_box/magazine/proc/empty_magazine()
+ var/turf_mag = get_turf(src)
+ for(var/obj/item/ammo in stored_ammo)
+ ammo.forceMove(turf_mag)
stored_ammo -= ammo
\ No newline at end of file
diff --git a/code/modules/projectiles/boxes_magazines/external/grenade.dm b/code/modules/projectiles/boxes_magazines/external/grenade.dm
new file mode 100644
index 0000000000..2b3c31f81c
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/grenade.dm
@@ -0,0 +1,8 @@
+/obj/item/ammo_box/magazine/m75
+ name = "specialized magazine (.75)"
+ icon_state = "75"
+ ammo_type = /obj/item/ammo_casing/caseless/a75
+ caliber = "75"
+ multiple_sprites = 2
+ max_ammo = 8
+
diff --git a/code/modules/projectiles/boxes_magazines/external/lmg.dm b/code/modules/projectiles/boxes_magazines/external/lmg.dm
new file mode 100644
index 0000000000..cb42989022
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/lmg.dm
@@ -0,0 +1,22 @@
+/obj/item/ammo_box/magazine/mm195x129
+ name = "box magazine (1.95x129mm)"
+ icon_state = "a762-50"
+ ammo_type = /obj/item/ammo_casing/mm195x129
+ caliber = "mm195129"
+ max_ammo = 50
+
+/obj/item/ammo_box/magazine/mm195x129/hollow
+ name = "box magazine (Hollow-Point 1.95x129mm)"
+ ammo_type = /obj/item/ammo_casing/mm195x129/hollow
+
+/obj/item/ammo_box/magazine/mm195x129/ap
+ name = "box magazine (Armor Penetrating 1.95x129mm)"
+ ammo_type = /obj/item/ammo_casing/mm195x129/ap
+
+/obj/item/ammo_box/magazine/mm195x129/incen
+ name = "box magazine (Incendiary 1.95x129mm)"
+ ammo_type = /obj/item/ammo_casing/mm195x129/incen
+
+/obj/item/ammo_box/magazine/mm195x129/update_icon()
+ ..()
+ icon_state = "a762-[round(ammo_count(),10)]"
diff --git a/code/modules/projectiles/boxes_magazines/external/pistol.dm b/code/modules/projectiles/boxes_magazines/external/pistol.dm
new file mode 100644
index 0000000000..d70b20c65c
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/pistol.dm
@@ -0,0 +1,56 @@
+/obj/item/ammo_box/magazine/m10mm
+ name = "pistol magazine (10mm)"
+ desc = "A gun magazine."
+ icon_state = "9x19p"
+ ammo_type = /obj/item/ammo_casing/c10mm
+ caliber = "10mm"
+ max_ammo = 8
+ multiple_sprites = 2
+
+/obj/item/ammo_box/magazine/m10mm/fire
+ name = "pistol magazine (10mm incendiary)"
+ icon_state = "9x19pI"
+ desc = "A gun magazine. Loaded with rounds which ignite the target."
+ ammo_type = /obj/item/ammo_casing/c10mm/fire
+
+/obj/item/ammo_box/magazine/m10mm/hp
+ name = "pistol magazine (10mm HP)"
+ icon_state = "9x19pH"
+ desc= "A gun magazine. Loaded with hollow-point rounds, extremely effective against unarmored targets, but nearly useless against protective clothing."
+ ammo_type = /obj/item/ammo_casing/c10mm/hp
+
+/obj/item/ammo_box/magazine/m10mm/ap
+ name = "pistol magazine (10mm AP)"
+ icon_state = "9x19pA"
+ desc= "A gun magazine. Loaded with rounds which penetrate armour, but are less effective against normal targets."
+ ammo_type = /obj/item/ammo_casing/c10mm/ap
+
+/obj/item/ammo_box/magazine/m45
+ name = "handgun magazine (.45)"
+ icon_state = "45-8"
+ ammo_type = /obj/item/ammo_casing/c45
+ caliber = ".45"
+ max_ammo = 8
+
+/obj/item/ammo_box/magazine/m45/update_icon()
+ ..()
+ icon_state = "45-[ammo_count() ? "8" : "0"]"
+
+/obj/item/ammo_box/magazine/pistolm9mm
+ name = "pistol magazine (9mm)"
+ icon_state = "9x19p-8"
+ ammo_type = /obj/item/ammo_casing/c9mm
+ caliber = "9mm"
+ max_ammo = 15
+
+/obj/item/ammo_box/magazine/pistolm9mm/update_icon()
+ ..()
+ icon_state = "9x19p-[ammo_count() ? "8" : "0"]"
+
+/obj/item/ammo_box/magazine/m50
+ name = "handgun magazine (.50ae)"
+ icon_state = "50ae"
+ ammo_type = /obj/item/ammo_casing/a50AE
+ caliber = ".50"
+ max_ammo = 7
+ multiple_sprites = 1
diff --git a/code/modules/projectiles/boxes_magazines/external/rechargable.dm b/code/modules/projectiles/boxes_magazines/external/rechargable.dm
new file mode 100644
index 0000000000..c4fb00aa22
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/rechargable.dm
@@ -0,0 +1,14 @@
+/obj/item/ammo_box/magazine/recharge
+ name = "power pack"
+ desc = "A rechargeable, detachable battery that serves as a magazine for laser rifles."
+ icon_state = "oldrifle-20"
+ ammo_type = /obj/item/ammo_casing/caseless/laser
+ caliber = "laser"
+ max_ammo = 20
+
+/obj/item/ammo_box/magazine/recharge/update_icon()
+ desc = "[initial(desc)] It has [stored_ammo.len] shot\s left."
+ icon_state = "oldrifle-[round(ammo_count(),4)]"
+
+/obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets"
+ return
diff --git a/code/modules/projectiles/boxes_magazines/external/rifle.dm b/code/modules/projectiles/boxes_magazines/external/rifle.dm
new file mode 100644
index 0000000000..96e7d377ea
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/rifle.dm
@@ -0,0 +1,21 @@
+/obj/item/ammo_box/magazine/m10mm/rifle
+ name = "rifle magazine (10mm)"
+ desc = "A well-worn magazine fitted for the surplus rifle."
+ icon_state = "75-8"
+ ammo_type = /obj/item/ammo_casing/c10mm
+ caliber = "10mm"
+ max_ammo = 10
+
+/obj/item/ammo_box/magazine/m10mm/rifle/update_icon()
+ if(ammo_count())
+ icon_state = "75-8"
+ else
+ icon_state = "75-0"
+
+/obj/item/ammo_box/magazine/m556
+ name = "toploader magazine (5.56mm)"
+ icon_state = "5.56m"
+ ammo_type = /obj/item/ammo_casing/a556
+ caliber = "a556"
+ max_ammo = 30
+ multiple_sprites = 2
diff --git a/code/modules/projectiles/boxes_magazines/external/shotgun.dm b/code/modules/projectiles/boxes_magazines/external/shotgun.dm
new file mode 100644
index 0000000000..dc8d0175ba
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/shotgun.dm
@@ -0,0 +1,36 @@
+/obj/item/ammo_box/magazine/m12g
+ name = "shotgun magazine (12g taser slugs)"
+ desc = "A drum magazine."
+ icon_state = "m12gs"
+ ammo_type = /obj/item/ammo_casing/shotgun/stunslug
+ caliber = "shotgun"
+ max_ammo = 8
+
+/obj/item/ammo_box/magazine/m12g/update_icon()
+ ..()
+ icon_state = "[initial(icon_state)]-[CEILING(ammo_count(0)/8, 1)*8]"
+
+/obj/item/ammo_box/magazine/m12g/buckshot
+ name = "shotgun magazine (12g buckshot slugs)"
+ icon_state = "m12gb"
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+
+/obj/item/ammo_box/magazine/m12g/slug
+ name = "shotgun magazine (12g slugs)"
+ icon_state = "m12gb"
+ ammo_type = /obj/item/ammo_casing/shotgun
+
+/obj/item/ammo_box/magazine/m12g/dragon
+ name = "shotgun magazine (12g dragon's breath)"
+ icon_state = "m12gf"
+ ammo_type = /obj/item/ammo_casing/shotgun/dragonsbreath
+
+/obj/item/ammo_box/magazine/m12g/bioterror
+ name = "shotgun magazine (12g bioterror)"
+ icon_state = "m12gt"
+ ammo_type = /obj/item/ammo_casing/shotgun/dart/bioterror
+
+/obj/item/ammo_box/magazine/m12g/meteor
+ name = "shotgun magazine (12g meteor slugs)"
+ icon_state = "m12gbc"
+ ammo_type = /obj/item/ammo_casing/shotgun/meteorslug
diff --git a/code/modules/projectiles/boxes_magazines/external/smg.dm b/code/modules/projectiles/boxes_magazines/external/smg.dm
new file mode 100644
index 0000000000..c6dc004879
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/smg.dm
@@ -0,0 +1,76 @@
+/obj/item/ammo_box/magazine/wt550m9
+ name = "wt550 magazine (4.6x30mm)"
+ icon_state = "46x30mmt-20"
+ ammo_type = /obj/item/ammo_casing/c46x30mm
+ caliber = "4.6x30mm"
+ max_ammo = 20
+
+/obj/item/ammo_box/magazine/wt550m9/update_icon()
+ ..()
+ icon_state = "46x30mmt-[round(ammo_count(),4)]"
+
+/obj/item/ammo_box/magazine/wt550m9/wtap
+ name = "wt550 magazine (Armour Piercing 4.6x30mm)"
+ icon_state = "46x30mmtA-20"
+ ammo_type = /obj/item/ammo_casing/c46x30mm/ap
+
+/obj/item/ammo_box/magazine/wt550m9/wtap/update_icon()
+ ..()
+ icon_state = "46x30mmtA-[round(ammo_count(),4)]"
+
+/obj/item/ammo_box/magazine/wt550m9/wtic
+ name = "wt550 magazine (Incindiary 4.6x30mm)"
+ icon_state = "46x30mmtI-20"
+ ammo_type = /obj/item/ammo_casing/c46x30mm/inc
+
+/obj/item/ammo_box/magazine/wt550m9/wtic/update_icon()
+ ..()
+ icon_state = "46x30mmtI-[round(ammo_count(),4)]"
+
+/obj/item/ammo_box/magazine/uzim9mm
+ name = "uzi magazine (9mm)"
+ icon_state = "uzi9mm-32"
+ ammo_type = /obj/item/ammo_casing/c9mm
+ caliber = "9mm"
+ max_ammo = 32
+
+/obj/item/ammo_box/magazine/uzim9mm/update_icon()
+ ..()
+ icon_state = "uzi9mm-[round(ammo_count(),4)]"
+
+/obj/item/ammo_box/magazine/smgm9mm
+ name = "SMG magazine (9mm)"
+ icon_state = "smg9mm-42"
+ ammo_type = /obj/item/ammo_casing/c9mm
+ caliber = "9mm"
+ max_ammo = 21
+
+/obj/item/ammo_box/magazine/smgm9mm/update_icon()
+ ..()
+ icon_state = "smg9mm-[ammo_count() ? "42" : "0"]"
+
+/obj/item/ammo_box/magazine/smgm9mm/ap
+ name = "SMG magazine (Armour Piercing 9mm)"
+ ammo_type = /obj/item/ammo_casing/c9mm/ap
+
+/obj/item/ammo_box/magazine/smgm9mm/fire
+ name = "SMG Magazine (Incindiary 9mm)"
+ ammo_type = /obj/item/ammo_casing/c9mm/inc
+
+/obj/item/ammo_box/magazine/smgm45
+ name = "SMG magazine (.45)"
+ icon_state = "c20r45-24"
+ ammo_type = /obj/item/ammo_casing/c45/nostamina
+ caliber = ".45"
+ max_ammo = 24
+
+/obj/item/ammo_box/magazine/smgm45/update_icon()
+ ..()
+ icon_state = "c20r45-[round(ammo_count(),2)]"
+
+/obj/item/ammo_box/magazine/tommygunm45
+ name = "drum magazine (.45)"
+ icon_state = "drum45"
+ ammo_type = /obj/item/ammo_casing/c45
+ caliber = ".45"
+ max_ammo = 50
diff --git a/code/modules/projectiles/boxes_magazines/external/sniper.dm b/code/modules/projectiles/boxes_magazines/external/sniper.dm
new file mode 100644
index 0000000000..67c6257bac
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/sniper.dm
@@ -0,0 +1,26 @@
+/obj/item/ammo_box/magazine/sniper_rounds
+ name = "sniper rounds (.50)"
+ icon_state = ".50mag"
+ ammo_type = /obj/item/ammo_casing/p50
+ max_ammo = 6
+ caliber = ".50"
+
+/obj/item/ammo_box/magazine/sniper_rounds/update_icon()
+ if(ammo_count())
+ icon_state = "[initial(icon_state)]-ammo"
+ else
+ icon_state = "[initial(icon_state)]"
+
+/obj/item/ammo_box/magazine/sniper_rounds/soporific
+ name = "sniper rounds (Zzzzz)"
+ desc = "Soporific sniper rounds, designed for happy days and dead quiet nights..."
+ icon_state = "soporific"
+ ammo_type = /obj/item/ammo_casing/p50/soporific
+ max_ammo = 3
+ caliber = ".50"
+
+/obj/item/ammo_box/magazine/sniper_rounds/penetrator
+ name = "sniper rounds (penetrator)"
+ desc = "An extremely powerful round capable of passing straight through cover and anyone unfortunate enough to be behind it."
+ ammo_type = /obj/item/ammo_casing/p50/penetrator
+ max_ammo = 5
diff --git a/code/modules/projectiles/boxes_magazines/external/toy.dm b/code/modules/projectiles/boxes_magazines/external/toy.dm
new file mode 100644
index 0000000000..cb66391c02
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/external/toy.dm
@@ -0,0 +1,55 @@
+/obj/item/ammo_box/magazine/toy
+ name = "foam force META magazine"
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart
+ caliber = "foam_force"
+
+/obj/item/ammo_box/magazine/toy/smg
+ name = "foam force SMG magazine"
+ icon_state = "smg9mm-42"
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart
+ max_ammo = 20
+
+/obj/item/ammo_box/magazine/toy/smg/update_icon()
+ ..()
+ if(ammo_count())
+ icon_state = "smg9mm-42"
+ else
+ icon_state = "smg9mm-0"
+
+/obj/item/ammo_box/magazine/toy/smg/riot
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
+
+/obj/item/ammo_box/magazine/toy/pistol
+ name = "foam force pistol magazine"
+ icon_state = "9x19p"
+ max_ammo = 8
+ multiple_sprites = 2
+
+/obj/item/ammo_box/magazine/toy/pistol/riot
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
+
+/obj/item/ammo_box/magazine/toy/smgm45
+ name = "donksoft SMG magazine"
+ caliber = "foam_force"
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart
+ max_ammo = 20
+
+/obj/item/ammo_box/magazine/toy/smgm45/update_icon()
+ ..()
+ icon_state = "c20r45-[round(ammo_count(),2)]"
+
+/obj/item/ammo_box/magazine/toy/smgm45/riot
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
+
+/obj/item/ammo_box/magazine/toy/m762
+ name = "donksoft box magazine"
+ caliber = "foam_force"
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart
+ max_ammo = 50
+
+/obj/item/ammo_box/magazine/toy/m762/update_icon()
+ ..()
+ icon_state = "a762-[round(ammo_count(),10)]"
+
+/obj/item/ammo_box/magazine/toy/m762/riot
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
diff --git a/code/modules/projectiles/boxes_magazines/external_mag.dm b/code/modules/projectiles/boxes_magazines/external_mag.dm
deleted file mode 100644
index b7b3a3e286..0000000000
--- a/code/modules/projectiles/boxes_magazines/external_mag.dm
+++ /dev/null
@@ -1,340 +0,0 @@
-
-///////////EXTERNAL MAGAZINES////////////////
-
-/obj/item/ammo_box/magazine/m10mm
- name = "pistol magazine (10mm)"
- desc = "A gun magazine."
- icon_state = "9x19p"
- ammo_type = /obj/item/ammo_casing/c10mm
- caliber = "10mm"
- max_ammo = 8
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/m10mm/rifle
- name = "rifle magazine (10mm)"
- desc = "A well-worn magazine fitted for the surplus rifle."
- icon_state = "75-8"
- ammo_type = /obj/item/ammo_casing/c10mm
- caliber = "10mm"
- max_ammo = 10
-
-/obj/item/ammo_box/magazine/m10mm/rifle/update_icon()
- if(ammo_count())
- icon_state = "75-8"
- else
- icon_state = "75-0"
-
-
-/obj/item/ammo_box/magazine/m10mm/fire
- name = "pistol magazine (10mm incendiary)"
- icon_state = "9x19pI"
- desc = "A gun magazine. Loaded with rounds which ignite the target."
- ammo_type = /obj/item/ammo_casing/c10mm/fire
-
-/obj/item/ammo_box/magazine/m10mm/hp
- name = "pistol magazine (10mm HP)"
- icon_state = "9x19pH"
- desc= "A gun magazine. Loaded with hollow-point rounds, extremely effective against unarmored targets, but nearly useless against protective clothing."
- ammo_type = /obj/item/ammo_casing/c10mm/hp
-
-/obj/item/ammo_box/magazine/m10mm/ap
- name = "pistol magazine (10mm AP)"
- icon_state = "9x19pA"
- desc= "A gun magazine. Loaded with rounds which penetrate armour, but are less effective against normal targets."
- ammo_type = /obj/item/ammo_casing/c10mm/ap
-
-/obj/item/ammo_box/magazine/m45
- name = "handgun magazine (.45)"
- icon_state = "45-8"
- ammo_type = /obj/item/ammo_casing/c45
- caliber = ".45"
- max_ammo = 8
-
-/obj/item/ammo_box/magazine/m45/update_icon()
- ..()
- icon_state = "45-[ammo_count() ? "8" : "0"]"
-
-/obj/item/ammo_box/magazine/wt550m9
- name = "wt550 magazine (4.6x30mm)"
- icon_state = "46x30mmt-20"
- ammo_type = /obj/item/ammo_casing/c46x30mm
- caliber = "4.6x30mm"
- max_ammo = 20
-
-/obj/item/ammo_box/magazine/wt550m9/update_icon()
- ..()
- icon_state = "46x30mmt-[round(ammo_count(),4)]"
-
-/obj/item/ammo_box/magazine/wt550m9/wtap
- name = "wt550 magazine (Armour Piercing 4.6x30mm)"
- icon_state = "46x30mmtA-20"
- ammo_type = /obj/item/ammo_casing/c46x30mm/ap
-
-/obj/item/ammo_box/magazine/wt550m9/wtap/update_icon()
- ..()
- icon_state = "46x30mmtA-[round(ammo_count(),4)]"
-
-/obj/item/ammo_box/magazine/wt550m9/wtic
- name = "wt550 magazine (Incindiary 4.6x30mm)"
- icon_state = "46x30mmtI-20"
- ammo_type = /obj/item/ammo_casing/c46x30mm/inc
-
-/obj/item/ammo_box/magazine/wt550m9/wtic/update_icon()
- ..()
- icon_state = "46x30mmtI-[round(ammo_count(),4)]"
-
-/obj/item/ammo_box/magazine/uzim9mm
- name = "uzi magazine (9mm)"
- icon_state = "uzi9mm-32"
- ammo_type = /obj/item/ammo_casing/c9mm
- caliber = "9mm"
- max_ammo = 32
-
-/obj/item/ammo_box/magazine/uzim9mm/update_icon()
- ..()
- icon_state = "uzi9mm-[round(ammo_count(),4)]"
-
-/obj/item/ammo_box/magazine/smgm9mm
- name = "SMG magazine (9mm)"
- icon_state = "smg9mm-42"
- ammo_type = /obj/item/ammo_casing/c9mm
- caliber = "9mm"
- max_ammo = 21
-
-/obj/item/ammo_box/magazine/smgm9mm/update_icon()
- ..()
- icon_state = "smg9mm-[ammo_count() ? "42" : "0"]"
-
-/obj/item/ammo_box/magazine/smgm9mm/ap
- name = "SMG magazine (Armour Piercing 9mm)"
- ammo_type = /obj/item/ammo_casing/c9mm/ap
-
-/obj/item/ammo_box/magazine/smgm9mm/fire
- name = "SMG Magazine (Incindiary 9mm)"
- ammo_type = /obj/item/ammo_casing/c9mm/inc
-
-/obj/item/ammo_box/magazine/pistolm9mm
- name = "pistol magazine (9mm)"
- icon_state = "9x19p-8"
- ammo_type = /obj/item/ammo_casing/c9mm
- caliber = "9mm"
- max_ammo = 15
-
-/obj/item/ammo_box/magazine/pistolm9mm/update_icon()
- ..()
- icon_state = "9x19p-[ammo_count() ? "8" : "0"]"
-
-/obj/item/ammo_box/magazine/smgm45
- name = "SMG magazine (.45)"
- icon_state = "c20r45-24"
- ammo_type = /obj/item/ammo_casing/c45/nostamina
- caliber = ".45"
- max_ammo = 24
-
-/obj/item/ammo_box/magazine/smgm45/update_icon()
- ..()
- icon_state = "c20r45-[round(ammo_count(),2)]"
-
-/obj/item/ammo_box/magazine/tommygunm45
- name = "drum magazine (.45)"
- icon_state = "drum45"
- ammo_type = /obj/item/ammo_casing/c45
- caliber = ".45"
- max_ammo = 50
-
-/obj/item/ammo_box/magazine/m50
- name = "handgun magazine (.50ae)"
- icon_state = "50ae"
- ammo_type = /obj/item/ammo_casing/a50AE
- caliber = ".50"
- max_ammo = 7
- multiple_sprites = 1
-
-/obj/item/ammo_box/magazine/m75
- name = "specialized magazine (.75)"
- icon_state = "75"
- ammo_type = /obj/item/ammo_casing/caseless/a75
- caliber = "75"
- multiple_sprites = 2
- max_ammo = 8
-
-/obj/item/ammo_box/magazine/m556
- name = "toploader magazine (5.56mm)"
- icon_state = "5.56m"
- ammo_type = /obj/item/ammo_casing/a556
- caliber = "a556"
- max_ammo = 30
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/m12g
- name = "shotgun magazine (12g taser slugs)"
- desc = "A drum magazine."
- icon_state = "m12gs"
- ammo_type = /obj/item/ammo_casing/shotgun/stunslug
- caliber = "shotgun"
- max_ammo = 8
-
-/obj/item/ammo_box/magazine/m12g/update_icon()
- ..()
- icon_state = "[initial(icon_state)]-[CEILING(ammo_count(0)/8, 1)*8]"
-
-/obj/item/ammo_box/magazine/m12g/buckshot
- name = "shotgun magazine (12g buckshot slugs)"
- icon_state = "m12gb"
- ammo_type = /obj/item/ammo_casing/shotgun/buckshot
-
-/obj/item/ammo_box/magazine/m12g/slug
- name = "shotgun magazine (12g slugs)"
- icon_state = "m12gb"
- ammo_type = /obj/item/ammo_casing/shotgun
-
-/obj/item/ammo_box/magazine/m12g/dragon
- name = "shotgun magazine (12g dragon's breath)"
- icon_state = "m12gf"
- ammo_type = /obj/item/ammo_casing/shotgun/dragonsbreath
-
-/obj/item/ammo_box/magazine/m12g/bioterror
- name = "shotgun magazine (12g bioterror)"
- icon_state = "m12gt"
- ammo_type = /obj/item/ammo_casing/shotgun/dart/bioterror
-
-/obj/item/ammo_box/magazine/m12g/meteor
- name = "shotgun magazine (12g meteor slugs)"
- icon_state = "m12gbc"
- ammo_type = /obj/item/ammo_casing/shotgun/meteorslug
-
-
-//// SNIPER MAGAZINES
-
-/obj/item/ammo_box/magazine/sniper_rounds
- name = "sniper rounds (.50)"
- icon_state = ".50mag"
- ammo_type = /obj/item/ammo_casing/p50
- max_ammo = 6
- caliber = ".50"
-
-/obj/item/ammo_box/magazine/sniper_rounds/update_icon()
- if(ammo_count())
- icon_state = "[initial(icon_state)]-ammo"
- else
- icon_state = "[initial(icon_state)]"
-
-/obj/item/ammo_box/magazine/sniper_rounds/soporific
- name = "sniper rounds (Zzzzz)"
- desc = "Soporific sniper rounds, designed for happy days and dead quiet nights..."
- icon_state = "soporific"
- ammo_type = /obj/item/ammo_casing/p50/soporific
- max_ammo = 3
- caliber = ".50"
-
-/obj/item/ammo_box/magazine/sniper_rounds/penetrator
- name = "sniper rounds (penetrator)"
- desc = "An extremely powerful round capable of passing straight through cover and anyone unfortunate enough to be behind it."
- ammo_type = /obj/item/ammo_casing/p50/penetrator
- max_ammo = 5
-
-//// SAW MAGAZINES
-
-/obj/item/ammo_box/magazine/mm195x129
- name = "box magazine (1.95x129mm)"
- icon_state = "a762-50"
- ammo_type = /obj/item/ammo_casing/mm195x129
- caliber = "mm195129"
- max_ammo = 50
-
-/obj/item/ammo_box/magazine/mm195x129/hollow
- name = "box magazine (Hollow-Point 1.95x129mm)"
- ammo_type = /obj/item/ammo_casing/mm195x129/hollow
-
-/obj/item/ammo_box/magazine/mm195x129/ap
- name = "box magazine (Armor Penetrating 1.95x129mm)"
- ammo_type = /obj/item/ammo_casing/mm195x129/ap
-
-/obj/item/ammo_box/magazine/mm195x129/incen
- name = "box magazine (Incendiary 1.95x129mm)"
- ammo_type = /obj/item/ammo_casing/mm195x129/incen
-
-/obj/item/ammo_box/magazine/mm195x129/update_icon()
- ..()
- icon_state = "a762-[round(ammo_count(),10)]"
-
-
-
-
-////TOY GUN MAGAZINES
-
-/obj/item/ammo_box/magazine/toy
- name = "foam force META magazine"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart
- caliber = "foam_force"
-
-/obj/item/ammo_box/magazine/toy/smg
- name = "foam force SMG magazine"
- icon_state = "smg9mm-42"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart
- max_ammo = 20
-
-/obj/item/ammo_box/magazine/toy/smg/update_icon()
- ..()
- if(ammo_count())
- icon_state = "smg9mm-42"
- else
- icon_state = "smg9mm-0"
-
-/obj/item/ammo_box/magazine/toy/smg/riot
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
-
-/obj/item/ammo_box/magazine/toy/pistol
- name = "foam force pistol magazine"
- icon_state = "9x19p"
- max_ammo = 8
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/toy/pistol/riot
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
-
-/obj/item/ammo_box/magazine/toy/smgm45
- name = "donksoft SMG magazine"
- caliber = "foam_force"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart
- max_ammo = 20
-
-/obj/item/ammo_box/magazine/toy/smgm45/update_icon()
- ..()
- icon_state = "c20r45-[round(ammo_count(),2)]"
-
-/obj/item/ammo_box/magazine/toy/smgm45/riot
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
-
-/obj/item/ammo_box/magazine/toy/m762
- name = "donksoft box magazine"
- caliber = "foam_force"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart
- max_ammo = 50
-
-/obj/item/ammo_box/magazine/toy/m762/update_icon()
- ..()
- icon_state = "a762-[round(ammo_count(),10)]"
-
-/obj/item/ammo_box/magazine/toy/m762/riot
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
-
-
-
-
-//// RECHARGEABLE MAGAZINES
-
-/obj/item/ammo_box/magazine/recharge
- name = "power pack"
- desc = "A rechargeable, detachable battery that serves as a magazine for laser rifles."
- icon_state = "oldrifle-20"
- ammo_type = /obj/item/ammo_casing/caseless/laser
- caliber = "laser"
- max_ammo = 20
-
-/obj/item/ammo_box/magazine/recharge/update_icon()
- desc = "[initial(desc)] It has [stored_ammo.len] shot\s left."
- icon_state = "oldrifle-[round(ammo_count(),4)]"
-
-/obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets"
- return
diff --git a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm
new file mode 100644
index 0000000000..bbfc79471c
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm
@@ -0,0 +1,47 @@
+/obj/item/ammo_box/magazine/internal/cylinder
+ name = "revolver cylinder"
+ ammo_type = /obj/item/ammo_casing/a357
+ caliber = "357"
+ max_ammo = 7
+
+/obj/item/ammo_box/magazine/internal/cylinder/ammo_count(countempties = 1)
+ var/boolets = 0
+ for(var/obj/item/ammo_casing/bullet in stored_ammo)
+ if(bullet && (bullet.BB || countempties))
+ boolets++
+
+ return boolets
+
+/obj/item/ammo_box/magazine/internal/cylinder/get_round(keep = 0)
+ rotate()
+
+ var/b = stored_ammo[1]
+ if(!keep)
+ stored_ammo[1] = null
+
+ return b
+
+/obj/item/ammo_box/magazine/internal/cylinder/proc/rotate()
+ var/b = stored_ammo[1]
+ stored_ammo.Cut(1,2)
+ stored_ammo.Insert(0, b)
+
+/obj/item/ammo_box/magazine/internal/cylinder/proc/spin()
+ for(var/i in 1 to rand(0, max_ammo*2))
+ rotate()
+
+/obj/item/ammo_box/magazine/internal/cylinder/give_round(obj/item/ammo_casing/R, replace_spent = 0)
+ if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type))
+ return FALSE
+
+ for(var/i in 1 to stored_ammo.len)
+ var/obj/item/ammo_casing/bullet = stored_ammo[i]
+ if(!bullet || !bullet.BB) // found a spent ammo
+ stored_ammo[i] = R
+ R.forceMove(src)
+
+ if(bullet)
+ bullet.forceMove(drop_location())
+ return TRUE
+
+ return FALSE
diff --git a/code/modules/projectiles/boxes_magazines/internal/_internal.dm b/code/modules/projectiles/boxes_magazines/internal/_internal.dm
new file mode 100644
index 0000000000..e21cb5ce61
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/_internal.dm
@@ -0,0 +1,7 @@
+/obj/item/ammo_box/magazine/internal
+ desc = "Oh god, this shouldn't be here"
+ flags_1 = CONDUCT_1|ABSTRACT_1
+
+//internals magazines are accessible, so replace spent ammo if full when trying to put a live one in
+/obj/item/ammo_box/magazine/internal/give_round(obj/item/ammo_casing/R)
+ return ..(R,1)
diff --git a/code/modules/projectiles/boxes_magazines/internal/grenade.dm b/code/modules/projectiles/boxes_magazines/internal/grenade.dm
new file mode 100644
index 0000000000..12325a0299
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/grenade.dm
@@ -0,0 +1,17 @@
+/obj/item/ammo_box/magazine/internal/cylinder/grenademulti
+ name = "grenade launcher internal magazine"
+ ammo_type = /obj/item/ammo_casing/a40mm
+ caliber = "40mm"
+ max_ammo = 6
+
+/obj/item/ammo_box/magazine/internal/grenadelauncher
+ name = "grenade launcher internal magazine"
+ ammo_type = /obj/item/ammo_casing/a40mm
+ caliber = "40mm"
+ max_ammo = 1
+
+/obj/item/ammo_box/magazine/internal/rocketlauncher
+ name = "grenade launcher internal magazine"
+ ammo_type = /obj/item/ammo_casing/caseless/a84mm
+ caliber = "84mm"
+ max_ammo = 1
diff --git a/code/modules/projectiles/boxes_magazines/internal/misc.dm b/code/modules/projectiles/boxes_magazines/internal/misc.dm
new file mode 100644
index 0000000000..aab0643cbc
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/misc.dm
@@ -0,0 +1,11 @@
+/obj/item/ammo_box/magazine/internal/speargun
+ name = "speargun internal magazine"
+ ammo_type = /obj/item/ammo_casing/caseless/magspear
+ caliber = "speargun"
+ max_ammo = 1
+
+/obj/item/ammo_box/magazine/internal/minigun
+ name = "gatling gun fusion core"
+ ammo_type = /obj/item/ammo_casing/caseless/laser/gatling
+ caliber = "gatling"
+ max_ammo = 5000
diff --git a/code/modules/projectiles/boxes_magazines/internal/revolver.dm b/code/modules/projectiles/boxes_magazines/internal/revolver.dm
new file mode 100644
index 0000000000..976f80f437
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/revolver.dm
@@ -0,0 +1,22 @@
+/obj/item/ammo_box/magazine/internal/cylinder/rev38
+ name = "detective revolver cylinder"
+ ammo_type = /obj/item/ammo_casing/c38
+ caliber = "38"
+ max_ammo = 6
+
+/obj/item/ammo_box/magazine/internal/cylinder/rev762
+ name = "nagant revolver cylinder"
+ ammo_type = /obj/item/ammo_casing/n762
+ caliber = "n762"
+ max_ammo = 7
+
+/obj/item/ammo_box/magazine/internal/cylinder/rus357
+ name = "russian revolver cylinder"
+ ammo_type = /obj/item/ammo_casing/a357
+ caliber = "357"
+ max_ammo = 6
+ multiload = 0
+
+/obj/item/ammo_box/magazine/internal/rus357/Initialize()
+ stored_ammo += new ammo_type(src)
+ . = ..()
diff --git a/code/modules/projectiles/boxes_magazines/internal/rifle.dm b/code/modules/projectiles/boxes_magazines/internal/rifle.dm
new file mode 100644
index 0000000000..ef83e96b1c
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/rifle.dm
@@ -0,0 +1,15 @@
+/obj/item/ammo_box/magazine/internal/boltaction
+ name = "bolt action rifle internal magazine"
+ desc = "Oh god, this shouldn't be here"
+ ammo_type = /obj/item/ammo_casing/a762
+ caliber = "a762"
+ max_ammo = 5
+ multiload = 1
+
+/obj/item/ammo_box/magazine/internal/boltaction/enchanted
+ max_ammo = 1
+ ammo_type = /obj/item/ammo_casing/a762/enchanted
+
+/obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage
+ ammo_type = /obj/item/ammo_casing/magic/arcane_barrage
+
diff --git a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
new file mode 100644
index 0000000000..3bd277da31
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm
@@ -0,0 +1,48 @@
+/obj/item/ammo_box/magazine/internal/shot
+ name = "shotgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/beanbag
+ caliber = "shotgun"
+ max_ammo = 4
+ multiload = 0
+
+/obj/item/ammo_box/magazine/internal/shot/ammo_count(countempties = 1)
+ if (!countempties)
+ var/boolets = 0
+ for(var/obj/item/ammo_casing/bullet in stored_ammo)
+ if(bullet.BB)
+ boolets++
+ return boolets
+ else
+ return ..()
+
+/obj/item/ammo_box/magazine/internal/shot/tube
+ name = "dual feed shotgun internal tube"
+ ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
+ max_ammo = 4
+
+/obj/item/ammo_box/magazine/internal/shot/lethal
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+
+/obj/item/ammo_box/magazine/internal/shot/com
+ name = "combat shotgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+ max_ammo = 6
+
+/obj/item/ammo_box/magazine/internal/shot/com/compact
+ name = "compact combat shotgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/buckshot
+ max_ammo = 4
+
+/obj/item/ammo_box/magazine/internal/shot/dual
+ name = "double-barrel shotgun internal magazine"
+ max_ammo = 2
+
+/obj/item/ammo_box/magazine/internal/shot/improvised
+ name = "improvised shotgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/improvised
+ max_ammo = 1
+
+/obj/item/ammo_box/magazine/internal/shot/riot
+ name = "riot shotgun internal magazine"
+ ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
+ max_ammo = 6
diff --git a/code/modules/projectiles/boxes_magazines/internal/toy.dm b/code/modules/projectiles/boxes_magazines/internal/toy.dm
new file mode 100644
index 0000000000..f2bb0dbf08
--- /dev/null
+++ b/code/modules/projectiles/boxes_magazines/internal/toy.dm
@@ -0,0 +1,7 @@
+/obj/item/ammo_box/magazine/internal/shot/toy
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart
+ caliber = "foam_force"
+ max_ammo = 4
+
+/obj/item/ammo_box/magazine/internal/shot/toy/crossbow
+ max_ammo = 5
diff --git a/code/modules/projectiles/boxes_magazines/internal_mag.dm b/code/modules/projectiles/boxes_magazines/internal_mag.dm
deleted file mode 100644
index b26d30c389..0000000000
--- a/code/modules/projectiles/boxes_magazines/internal_mag.dm
+++ /dev/null
@@ -1,191 +0,0 @@
-////////////////INTERNAL MAGAZINES//////////////////////
-
-/obj/item/ammo_box/magazine/internal
- desc = "Oh god, this shouldn't be here"
- flags_1 = CONDUCT_1|ABSTRACT_1
-
-//internals magazines are accessible, so replace spent ammo if full when trying to put a live one in
-/obj/item/ammo_box/magazine/internal/give_round(obj/item/ammo_casing/R)
- return ..(R,1)
-
-
-
-// Revolver internal mags
-/obj/item/ammo_box/magazine/internal/cylinder
- name = "revolver cylinder"
- ammo_type = /obj/item/ammo_casing/a357
- caliber = "357"
- max_ammo = 7
-
-/obj/item/ammo_box/magazine/internal/cylinder/ammo_count(countempties = 1)
- var/boolets = 0
- for(var/obj/item/ammo_casing/bullet in stored_ammo)
- if(bullet && (bullet.BB || countempties))
- boolets++
-
- return boolets
-
-/obj/item/ammo_box/magazine/internal/cylinder/get_round(keep = 0)
- rotate()
-
- var/b = stored_ammo[1]
- if(!keep)
- stored_ammo[1] = null
-
- return b
-
-/obj/item/ammo_box/magazine/internal/cylinder/proc/rotate()
- var/b = stored_ammo[1]
- stored_ammo.Cut(1,2)
- stored_ammo.Insert(0, b)
-
-/obj/item/ammo_box/magazine/internal/cylinder/proc/spin()
- for(var/i in 1 to rand(0, max_ammo*2))
- rotate()
-
-
-/obj/item/ammo_box/magazine/internal/cylinder/give_round(obj/item/ammo_casing/R, replace_spent = 0)
- if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type))
- return 0
-
- for(var/i in 1 to stored_ammo.len)
- var/obj/item/ammo_casing/bullet = stored_ammo[i]
- if(!bullet || !bullet.BB) // found a spent ammo
- stored_ammo[i] = R
- R.forceMove(src)
-
- if(bullet)
- bullet.forceMove(drop_location())
- return 1
-
- return 0
-
-/obj/item/ammo_box/magazine/internal/cylinder/rev38
- name = "detective revolver cylinder"
- ammo_type = /obj/item/ammo_casing/c38
- caliber = "38"
- max_ammo = 6
-
-/obj/item/ammo_box/magazine/internal/cylinder/grenademulti
- name = "grenade launcher internal magazine"
- ammo_type = /obj/item/ammo_casing/a40mm
- caliber = "40mm"
- max_ammo = 6
-
-/obj/item/ammo_box/magazine/internal/cylinder/rev762
- name = "nagant revolver cylinder"
- ammo_type = /obj/item/ammo_casing/n762
- caliber = "n762"
- max_ammo = 7
-
-// Shotgun internal mags
-/obj/item/ammo_box/magazine/internal/shot
- name = "shotgun internal magazine"
- ammo_type = /obj/item/ammo_casing/shotgun/beanbag
- caliber = "shotgun"
- max_ammo = 4
- multiload = 0
-
-/obj/item/ammo_box/magazine/internal/shot/ammo_count(countempties = 1)
- if (!countempties)
- var/boolets = 0
- for(var/obj/item/ammo_casing/bullet in stored_ammo)
- if(bullet.BB)
- boolets++
- return boolets
- else
- return ..()
-
-
-/obj/item/ammo_box/magazine/internal/shot/tube
- name = "dual feed shotgun internal tube"
- ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
- max_ammo = 4
-
-/obj/item/ammo_box/magazine/internal/shot/lethal
- ammo_type = /obj/item/ammo_casing/shotgun/buckshot
-
-/obj/item/ammo_box/magazine/internal/shot/com
- name = "combat shotgun internal magazine"
- ammo_type = /obj/item/ammo_casing/shotgun/buckshot
- max_ammo = 6
-
-/obj/item/ammo_box/magazine/internal/shot/com/compact
- name = "compact combat shotgun internal magazine"
- ammo_type = /obj/item/ammo_casing/shotgun/buckshot
- max_ammo = 4
-
-/obj/item/ammo_box/magazine/internal/shot/dual
- name = "double-barrel shotgun internal magazine"
- max_ammo = 2
-
-/obj/item/ammo_box/magazine/internal/shot/improvised
- name = "improvised shotgun internal magazine"
- ammo_type = /obj/item/ammo_casing/shotgun/improvised
- max_ammo = 1
-
-/obj/item/ammo_box/magazine/internal/shot/riot
- name = "riot shotgun internal magazine"
- ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
- max_ammo = 6
-
-
-
-
-/obj/item/ammo_box/magazine/internal/grenadelauncher
- name = "grenade launcher internal magazine"
- ammo_type = /obj/item/ammo_casing/a40mm
- caliber = "40mm"
- max_ammo = 1
-
-/obj/item/ammo_box/magazine/internal/rocketlauncher
- name = "grenade launcher internal magazine"
- ammo_type = /obj/item/ammo_casing/caseless/a84mm
- caliber = "84mm"
- max_ammo = 1
-
-/obj/item/ammo_box/magazine/internal/speargun
- name = "speargun internal magazine"
- ammo_type = /obj/item/ammo_casing/caseless/magspear
- caliber = "speargun"
- max_ammo = 1
-
-/obj/item/ammo_box/magazine/internal/cylinder/rus357
- name = "russian revolver cylinder"
- ammo_type = /obj/item/ammo_casing/a357
- caliber = "357"
- max_ammo = 6
- multiload = 0
-
-/obj/item/ammo_box/magazine/internal/rus357/Initialize()
- stored_ammo += new ammo_type(src)
- . = ..()
-
-/obj/item/ammo_box/magazine/internal/boltaction
- name = "bolt action rifle internal magazine"
- desc = "Oh god, this shouldn't be here"
- ammo_type = /obj/item/ammo_casing/a762
- caliber = "a762"
- max_ammo = 5
- multiload = 1
-
-/obj/item/ammo_box/magazine/internal/boltaction/enchanted
- max_ammo = 1
- ammo_type = /obj/item/ammo_casing/a762/enchanted
-
-/obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage
- ammo_type = /obj/item/ammo_casing/magic/arcane_barrage
-
-/obj/item/ammo_box/magazine/internal/shot/toy
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart
- caliber = "foam_force"
- max_ammo = 4
-
-/obj/item/ammo_box/magazine/internal/shot/toy/crossbow
- max_ammo = 5
-
-/obj/item/ammo_box/magazine/internal/minigun
- name = "gatling gun fusion core"
- ammo_type = /obj/item/ammo_casing/caseless/laser/gatling
- caliber = "gatling"
- max_ammo = 5000
diff --git a/code/modules/projectiles/guns/mounted.dm b/code/modules/projectiles/guns/energy/mounted.dm
similarity index 96%
rename from code/modules/projectiles/guns/mounted.dm
rename to code/modules/projectiles/guns/energy/mounted.dm
index 5893c2a107..79226689de 100644
--- a/code/modules/projectiles/guns/mounted.dm
+++ b/code/modules/projectiles/guns/energy/mounted.dm
@@ -1,26 +1,26 @@
-/obj/item/gun/energy/e_gun/advtaser/mounted
- name = "mounted taser"
- desc = "An arm mounted dual-mode weapon that fires electrodes and disabler shots."
- icon = 'icons/obj/items_cyborg.dmi'
- icon_state = "taser"
- item_state = "armcannonstun4"
- force = 5
- selfcharge = 1
- can_flashlight = 0
- trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses neural signals instead
-
-/obj/item/gun/energy/e_gun/advtaser/mounted/dropped()//if somebody manages to drop this somehow...
- ..()
-
-/obj/item/gun/energy/laser/mounted
- name = "mounted laser"
- desc = "An arm mounted cannon that fires lethal lasers."
- icon = 'icons/obj/items_cyborg.dmi'
- icon_state = "laser"
- item_state = "armcannonlase"
- force = 5
- selfcharge = 1
- trigger_guard = TRIGGER_GUARD_ALLOW_ALL
-
-/obj/item/gun/energy/laser/mounted/dropped()
- ..()
+/obj/item/gun/energy/e_gun/advtaser/mounted
+ name = "mounted taser"
+ desc = "An arm mounted dual-mode weapon that fires electrodes and disabler shots."
+ icon = 'icons/obj/items_cyborg.dmi'
+ icon_state = "taser"
+ item_state = "armcannonstun4"
+ force = 5
+ selfcharge = 1
+ can_flashlight = 0
+ trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses neural signals instead
+
+/obj/item/gun/energy/e_gun/advtaser/mounted/dropped()//if somebody manages to drop this somehow...
+ ..()
+
+/obj/item/gun/energy/laser/mounted
+ name = "mounted laser"
+ desc = "An arm mounted cannon that fires lethal lasers."
+ icon = 'icons/obj/items_cyborg.dmi'
+ icon_state = "laser"
+ item_state = "armcannonlase"
+ force = 5
+ selfcharge = 1
+ trigger_guard = TRIGGER_GUARD_ALLOW_ALL
+
+/obj/item/gun/energy/laser/mounted/dropped()
+ ..()
diff --git a/code/modules/projectiles/guns/energy/plasma.dm b/code/modules/projectiles/guns/energy/plasma_cit.dm
similarity index 100%
rename from code/modules/projectiles/guns/energy/plasma.dm
rename to code/modules/projectiles/guns/energy/plasma_cit.dm
diff --git a/code/modules/projectiles/guns/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm
similarity index 97%
rename from code/modules/projectiles/guns/beam_rifle.dm
rename to code/modules/projectiles/guns/misc/beam_rifle.dm
index a2365dfc13..59465eb989 100644
--- a/code/modules/projectiles/guns/beam_rifle.dm
+++ b/code/modules/projectiles/guns/misc/beam_rifle.dm
@@ -1,590 +1,590 @@
-
-#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0
-#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1
-#define ZOOM_LOCK_CENTER_VIEW 2
-#define ZOOM_LOCK_OFF 3
-
-#define AUTOZOOM_PIXEL_STEP_FACTOR 48
-
-#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1
-
-/obj/item/gun/energy/beam_rifle
- name = "particle acceleration rifle"
- desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targetted by one. \
- Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or \
- changing where you're pointing at while aiming will delay the aiming process depending on how much you changed."
- icon = 'icons/obj/guns/energy.dmi'
- icon_state = "esniper"
- item_state = "esniper"
- fire_sound = 'sound/weapons/beam_sniper.ogg'
- slot_flags = SLOT_BACK
- force = 15
- materials = list()
- recoil = 4
- ammo_x_offset = 3
- ammo_y_offset = 3
- modifystate = FALSE
- weapon_weight = WEAPON_HEAVY
- w_class = WEIGHT_CLASS_BULKY
- ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan)
- cell_type = /obj/item/stock_parts/cell/beam_rifle
- canMouseDown = TRUE
- pin = null
- var/aiming = FALSE
- var/aiming_time = 12
- var/aiming_time_fire_threshold = 5
- var/aiming_time_left = 12
- var/aiming_time_increase_user_movement = 3
- var/scoped_slow = 1
- var/aiming_time_increase_angle_multiplier = 0.3
- var/last_process = 0
-
- var/lastangle = 0
- var/aiming_lastangle = 0
- var/mob/current_user = null
- var/list/obj/effect/projectile/tracer/current_tracers
-
- var/structure_piercing = 2 //Amount * 2. For some reason structures aren't respecting this unless you have it doubled. Probably with the objects in question's Bump() code instead of this but I'll deal with this later.
- var/structure_bleed_coeff = 0.7
- var/wall_pierce_amount = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 1
- var/aoe_structure_damage = 50
- var/aoe_fire_range = 2
- var/aoe_fire_chance = 40
- var/aoe_mob_range = 1
- var/aoe_mob_damage = 30
- var/impact_structure_damage = 60
- var/projectile_damage = 30
- var/projectile_stun = 0
- var/projectile_setting_pierce = TRUE
- var/delay = 65
- var/lastfire = 0
-
- //ZOOMING
- var/zoom_current_view_increase = 0
- var/zoom_target_view_increase = 10
- var/zooming = FALSE
- var/zoom_lock = ZOOM_LOCK_OFF
- var/zooming_angle
- var/current_zoom_x = 0
- var/current_zoom_y = 0
- var/zoom_animating = 0
-
- var/static/image/charged_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_charged")
- var/static/image/drained_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_empty")
-
- var/datum/action/item_action/zoom_lock_action/zoom_lock_action
- var/datum/component/mobhook
-
-/obj/item/gun/energy/beam_rifle/debug
- delay = 0
- cell_type = /obj/item/stock_parts/cell/infinite
- aiming_time = 0
- recoil = 0
- pin = /obj/item/device/firing_pin
-
-/obj/item/gun/energy/beam_rifle/equipped(mob/user)
- set_user(user)
- . = ..()
-
-/obj/item/gun/energy/beam_rifle/pickup(mob/user)
- set_user(user)
- . = ..()
-
-/obj/item/gun/energy/beam_rifle/dropped(mob/user)
- set_user()
- . = ..()
-
-/obj/item/gun/energy/beam_rifle/ui_action_click(owner, action)
- if(istype(action, /datum/action/item_action/zoom_lock_action))
- zoom_lock++
- if(zoom_lock > 3)
- zoom_lock = 0
- switch(zoom_lock)
- if(ZOOM_LOCK_AUTOZOOM_FREEMOVE)
- to_chat(owner, "You switch [src]'s zooming processor to free directional.")
- if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK)
- to_chat(owner, "You switch [src]'s zooming processor to locked directional.")
- if(ZOOM_LOCK_CENTER_VIEW)
- to_chat(owner, "You switch [src]'s zooming processor to center mode.")
- if(ZOOM_LOCK_OFF)
- to_chat(owner, "You disable [src]'s zooming system.")
- reset_zooming()
-
-/obj/item/gun/energy/beam_rifle/proc/smooth_zooming(delay_override = null)
- if(!check_user() || !zooming || zoom_lock == ZOOM_LOCK_OFF || zoom_lock == ZOOM_LOCK_CENTER_VIEW)
- return
- if(zoom_animating && delay_override != 0)
- return smooth_zooming(zoom_animating + delay_override) //Automatically compensate for ongoing zooming actions.
- var/total_time = SSfastprocess.wait
- if(delay_override)
- total_time = delay_override
- zoom_animating = total_time
- animate(current_user.client, pixel_x = current_zoom_x, pixel_y = current_zoom_y , total_time, SINE_EASING, ANIMATION_PARALLEL)
- zoom_animating = 0
-
-/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle)
- if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF)
- return
- current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
- current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
-
-/obj/item/gun/energy/beam_rifle/proc/handle_zooming()
- if(!zooming || !check_user())
- return
- current_user.client.change_view(world.view + zoom_target_view_increase)
- zoom_current_view_increase = zoom_target_view_increase
- set_autozoom_pixel_offsets_immediate(zooming_angle)
- smooth_zooming()
-
-/obj/item/gun/energy/beam_rifle/proc/start_zooming()
- if(zoom_lock == ZOOM_LOCK_OFF)
- return
- zooming = TRUE
-
-/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
- if(zooming)
- zooming = FALSE
- reset_zooming(user)
-
-/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user)
- if(!user)
- user = current_user
- if(!user || !user.client)
- return FALSE
- zoom_animating = 0
- animate(user.client, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
- zoom_current_view_increase = 0
- user.client.change_view(CONFIG_GET(string/default_view))
- zooming_angle = 0
- current_zoom_x = 0
- current_zoom_y = 0
-
-/obj/item/gun/energy/beam_rifle/update_icon()
- cut_overlays()
- var/obj/item/ammo_casing/energy/primary_ammo = ammo_type[1]
- if(cell.charge > primary_ammo.e_cost)
- add_overlay(charged_overlay)
- else
- add_overlay(drained_overlay)
-
-/obj/item/gun/energy/beam_rifle/attack_self(mob/user)
- projectile_setting_pierce = !projectile_setting_pierce
- to_chat(user, "You set \the [src] to [projectile_setting_pierce? "pierce":"impact"] mode.")
- aiming_beam()
-
-/obj/item/gun/energy/beam_rifle/proc/update_slowdown()
- if(aiming)
- slowdown = scoped_slow
- else
- slowdown = initial(slowdown)
-
-/obj/item/gun/energy/beam_rifle/Initialize()
- . = ..()
- current_tracers = list()
- START_PROCESSING(SSprojectiles, src)
- zoom_lock_action = new(src)
-
-/obj/item/gun/energy/beam_rifle/Destroy()
- STOP_PROCESSING(SSfastprocess, src)
- set_user(null)
- QDEL_LIST(current_tracers)
- QDEL_NULL(mobhook)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/emp_act(severity)
- chambered = null
- recharge_newshot()
-
-/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
- var/diff = abs(aiming_lastangle - lastangle)
- check_user()
- if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update)
- return
- aiming_lastangle = lastangle
- var/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/P = new
- P.gun = src
- P.wall_pierce_amount = wall_pierce_amount
- P.structure_pierce_amount = structure_piercing
- P.do_pierce = projectile_setting_pierce
- if(aiming_time)
- var/percent = ((100/aiming_time)*aiming_time_left)
- P.color = rgb(255 * percent,255 * ((100 - percent) / 100),0)
- else
- P.color = rgb(0, 255, 0)
- var/turf/curloc = get_turf(src)
- var/turf/targloc = get_turf(current_user.client.mouseObject)
- if(!istype(targloc))
- if(!istype(curloc))
- return
- targloc = get_turf_in_angle(lastangle, curloc, 10)
- P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
- P.fire(lastangle)
-
-/obj/item/gun/energy/beam_rifle/process()
- if(!aiming)
- last_process = world.time
- return
- check_user()
- handle_zooming()
- aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
- aiming_beam(TRUE)
- last_process = world.time
-
-/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
- if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
- if(automatic_cleanup)
- stop_aiming()
- set_user(null)
- return FALSE
- return TRUE
-
-/obj/item/gun/energy/beam_rifle/proc/process_aim()
- if(istype(current_user) && current_user.client && current_user.client.mouseParams)
- var/angle = mouse_angle_from_client(current_user.client)
- switch(angle)
- if(316 to 360)
- current_user.setDir(NORTH)
- if(0 to 45)
- current_user.setDir(NORTH)
- if(46 to 135)
- current_user.setDir(EAST)
- if(136 to 225)
- current_user.setDir(SOUTH)
- if(226 to 315)
- current_user.setDir(WEST)
- var/difference = abs(lastangle - angle)
- if(difference > 350) //Too lazy to properly math, detects 360 --> 0 changes.
- difference = (lastangle > 350? ((360 - lastangle) + angle) : ((360 - angle) + lastangle))
- delay_penalty(difference * aiming_time_increase_angle_multiplier)
- lastangle = angle
-
-/obj/item/gun/energy/beam_rifle/proc/on_mob_move()
- check_user()
- if(aiming)
- delay_penalty(aiming_time_increase_user_movement)
- process_aim()
- aiming_beam(TRUE)
-
-/obj/item/gun/energy/beam_rifle/proc/start_aiming()
- aiming_time_left = aiming_time
- aiming = TRUE
- process_aim()
- aiming_beam(TRUE)
- zooming_angle = lastangle
- start_zooming()
-
-/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user)
- set waitfor = FALSE
- aiming_time_left = aiming_time
- aiming = FALSE
- QDEL_LIST(current_tracers)
- stop_zooming(user)
-
-/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user)
- if(user == current_user)
- return
- stop_aiming(current_user)
- QDEL_NULL(mobhook)
- if(istype(current_user))
- LAZYREMOVE(current_user.mousemove_intercept_objects, src)
- current_user = null
- if(istype(user))
- current_user = user
- LAZYADD(current_user.mousemove_intercept_objects, src)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move))
-
-/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
- if(aiming)
- process_aim()
- aiming_beam()
- if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE)
- zooming_angle = lastangle
- set_autozoom_pixel_offsets_immediate(zooming_angle)
- smooth_zooming(2)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/onMouseDown(object, location, params, mob/mob)
- if(istype(mob))
- set_user(mob)
- if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
- return
- if((object in mob.contents) || (object == mob))
- return
- start_aiming()
- return ..()
-
-/obj/item/gun/energy/beam_rifle/onMouseUp(object, location, params, mob/M)
- if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
- return
- process_aim()
- if(aiming_time_left <= aiming_time_fire_threshold && check_user())
- sync_ammo()
- afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
- stop_aiming()
- QDEL_LIST(current_tracers)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE)
- if(flag) //It's adjacent, is the user, or is on the user's person
- if(target in user.contents) //can't shoot stuff inside us.
- return
- if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack
- return
- if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected)
- return
- if(!passthrough && (aiming_time > aiming_time_fire_threshold))
- return
- if(lastfire > world.time + delay)
- return
- lastfire = world.time
- . = ..()
- stop_aiming()
-
-/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
- for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents)
- AC.sync_stats()
-
-/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount)
- aiming_time_left = CLAMP(aiming_time_left + amount, 0, aiming_time)
-
-/obj/item/ammo_casing/energy/beam_rifle
- name = "particle acceleration lens"
- desc = "Don't look into barrel!"
- var/wall_pierce_amount = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 1
- var/aoe_structure_damage = 30
- var/aoe_fire_range = 2
- var/aoe_fire_chance = 66
- var/aoe_mob_range = 1
- var/aoe_mob_damage = 20
- var/impact_structure_damage = 50
- var/projectile_damage = 40
- var/projectile_stun = 0
- var/structure_piercing = 2
- var/structure_bleed_coeff = 0.7
- var/do_pierce = TRUE
- var/obj/item/gun/energy/beam_rifle/host
-
-/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats()
- var/obj/item/gun/energy/beam_rifle/BR = loc
- if(!istype(BR))
- stack_trace("Beam rifle syncing error")
- host = BR
- do_pierce = BR.projectile_setting_pierce
- wall_pierce_amount = BR.wall_pierce_amount
- wall_devastate = BR.wall_devastate
- aoe_structure_range = BR.aoe_structure_range
- aoe_structure_damage = BR.aoe_structure_damage
- aoe_fire_range = BR.aoe_fire_range
- aoe_fire_chance = BR.aoe_fire_chance
- aoe_mob_range = BR.aoe_mob_range
- aoe_mob_damage = BR.aoe_mob_damage
- impact_structure_damage = BR.impact_structure_damage
- projectile_damage = BR.projectile_damage
- projectile_stun = BR.projectile_stun
- delay = BR.delay
- structure_piercing = BR.structure_piercing
- structure_bleed_coeff = BR.structure_bleed_coeff
-
-/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
- . = ..()
- var/obj/item/projectile/beam/beam_rifle/hitscan/HS_BB = BB
- if(!istype(HS_BB))
- return
- HS_BB.impact_direct_damage = projectile_damage
- HS_BB.stun = projectile_stun
- HS_BB.impact_structure_damage = impact_structure_damage
- HS_BB.aoe_mob_damage = aoe_mob_damage
- HS_BB.aoe_mob_range = CLAMP(aoe_mob_range, 0, 15) //Badmin safety lock
- HS_BB.aoe_fire_chance = aoe_fire_chance
- HS_BB.aoe_fire_range = aoe_fire_range
- HS_BB.aoe_structure_damage = aoe_structure_damage
- HS_BB.aoe_structure_range = CLAMP(aoe_structure_range, 0, 15) //Badmin safety lock
- HS_BB.wall_devastate = wall_devastate
- HS_BB.wall_pierce_amount = wall_pierce_amount
- HS_BB.structure_pierce_amount = structure_piercing
- HS_BB.structure_bleed_coeff = structure_bleed_coeff
- HS_BB.do_pierce = do_pierce
- HS_BB.gun = host
-
-/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread)
- var/turf/curloc = get_turf(user)
- if(!istype(curloc) || !BB)
- return FALSE
- var/obj/item/gun/energy/beam_rifle/gun = loc
- if(!targloc && gun)
- targloc = get_turf_in_angle(gun.lastangle, curloc, 10)
- else if(!targloc)
- return FALSE
- var/firing_dir
- if(BB.firer)
- firing_dir = BB.firer.dir
- if(!BB.suppressed && firing_effect_type)
- new firing_effect_type(get_turf(src), firing_dir)
- BB.preparePixelProjectile(target, user, params, spread)
- BB.fire(gun? gun.lastangle : null, null)
- BB = null
- return TRUE
-
-/obj/item/ammo_casing/energy/beam_rifle/hitscan
- projectile_type = /obj/item/projectile/beam/beam_rifle/hitscan
- select_name = "beam"
- e_cost = 5000
- fire_sound = 'sound/weapons/beam_sniper.ogg'
-
-/obj/item/projectile/beam/beam_rifle
- name = "particle beam"
- icon = ""
- hitsound = 'sound/effects/explosion3.ogg'
- damage = 0 //Handled manually.
- damage_type = BURN
- flag = "energy"
- range = 150
- jitter = 10
- var/obj/item/gun/energy/beam_rifle/gun
- var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing.
- var/structure_bleed_coeff = 0
- var/structure_pierce = 0
- var/do_pierce = TRUE
- var/wall_pierce_amount = 0
- var/wall_pierce = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 0
- var/aoe_structure_damage = 0
- var/aoe_fire_range = 0
- var/aoe_fire_chance = 0
- var/aoe_mob_range = 0
- var/aoe_mob_damage = 0
- var/impact_structure_damage = 0
- var/impact_direct_damage = 0
- var/turf/cached
- var/list/pierced = list()
-
-/obj/item/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
- set waitfor = FALSE
- if(!epicenter)
- return
- new /obj/effect/temp_visual/explosion/fast(epicenter)
- for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage
- L.adjustFireLoss(aoe_mob_damage)
- to_chat(L, "\The [src] sears you!")
- for(var/turf/T in range(aoe_fire_range, epicenter)) //handle aoe fire
- if(prob(aoe_fire_chance))
- new /obj/effect/hotspot(T)
- for(var/obj/O in range(aoe_structure_range, epicenter))
- if(!isitem(O))
- if(O.level == 1) //Please don't break underfloor items!
- continue
- O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, "laser", FALSE)
-
-/obj/item/projectile/beam/beam_rifle/proc/check_pierce(atom/target)
- if(!do_pierce)
- return FALSE
- if(pierced[target]) //we already pierced them go away
- return TRUE
- if(isclosedturf(target))
- if(wall_pierce++ < wall_pierce_amount)
- if(prob(wall_devastate))
- if(iswallturf(target))
- var/turf/closed/wall/W = target
- W.dismantle_wall(TRUE, TRUE)
- else
- target.ex_act(EXPLODE_HEAVY)
- return TRUE
- if(ismovableatom(target))
- var/atom/movable/AM = target
- if(AM.density && !AM.CanPass(src, get_turf(target)) && !ismob(AM))
- if(structure_pierce < structure_pierce_amount)
- if(isobj(AM))
- var/obj/O = AM
- O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(AM), BURN, "energy", FALSE)
- pierced[AM] = TRUE
- structure_pierce++
- return TRUE
- return FALSE
-
-/obj/item/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
- if(istype(target, /obj/machinery/door))
- return 0.4
- if(istype(target, /obj/structure/window))
- return 0.5
- return 1
-
-/obj/item/projectile/beam/beam_rifle/proc/handle_impact(atom/target)
- if(isobj(target))
- var/obj/O = target
- O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, "laser", FALSE)
- if(isliving(target))
- var/mob/living/L = target
- L.adjustFireLoss(impact_direct_damage)
- L.emote("scream")
-
-/obj/item/projectile/beam/beam_rifle/proc/handle_hit(atom/target)
- set waitfor = FALSE
- if(!cached && !QDELETED(target))
- cached = get_turf(target)
- if(nodamage)
- return FALSE
- playsound(cached, 'sound/effects/explosion3.ogg', 100, 1)
- AOE(cached)
- if(!QDELETED(target))
- handle_impact(target)
-
-/obj/item/projectile/beam/beam_rifle/Collide(atom/target)
- if(check_pierce(target))
- permutated += target
- trajectory_ignore_forcemove = TRUE
- forceMove(target)
- trajectory_ignore_forcemove = FALSE
- return FALSE
- if(!QDELETED(target))
- cached = get_turf(target)
- . = ..()
-
-/obj/item/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE)
- if(!QDELETED(target))
- cached = get_turf(target)
- handle_hit(target)
- . = ..()
-
-/obj/item/projectile/beam/beam_rifle/hitscan
- icon_state = ""
- hitscan = TRUE
- tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
- var/constant_tracer = FALSE
-
-/obj/item/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander)
- set waitfor = FALSE
- if(isnull(highlander))
- highlander = constant_tracer
- if(highlander && istype(gun))
- QDEL_LIST(gun.current_tracers)
- for(var/datum/point/p in beam_segments)
- gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0)
- else
- for(var/datum/point/p in beam_segments)
- generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration)
- if(cleanup)
- QDEL_LIST(beam_segments)
- beam_segments = null
- QDEL_NULL(beam_index)
-
-/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam
- tracer_type = /obj/effect/projectile/tracer/tracer/aiming
- name = "aiming beam"
- hitsound = null
- hitsound_wall = null
- nodamage = TRUE
- damage = 0
- constant_tracer = TRUE
-
-/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target)
- qdel(src)
- return FALSE
-
-/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit()
- qdel(src)
- return FALSE
+
+#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0
+#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1
+#define ZOOM_LOCK_CENTER_VIEW 2
+#define ZOOM_LOCK_OFF 3
+
+#define AUTOZOOM_PIXEL_STEP_FACTOR 48
+
+#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1
+
+/obj/item/gun/energy/beam_rifle
+ name = "particle acceleration rifle"
+ desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targetted by one. \
+ Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or \
+ changing where you're pointing at while aiming will delay the aiming process depending on how much you changed."
+ icon = 'icons/obj/guns/energy.dmi'
+ icon_state = "esniper"
+ item_state = "esniper"
+ fire_sound = 'sound/weapons/beam_sniper.ogg'
+ slot_flags = SLOT_BACK
+ force = 15
+ materials = list()
+ recoil = 4
+ ammo_x_offset = 3
+ ammo_y_offset = 3
+ modifystate = FALSE
+ weapon_weight = WEAPON_HEAVY
+ w_class = WEIGHT_CLASS_BULKY
+ ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan)
+ cell_type = /obj/item/stock_parts/cell/beam_rifle
+ canMouseDown = TRUE
+ pin = null
+ var/aiming = FALSE
+ var/aiming_time = 12
+ var/aiming_time_fire_threshold = 5
+ var/aiming_time_left = 12
+ var/aiming_time_increase_user_movement = 3
+ var/scoped_slow = 1
+ var/aiming_time_increase_angle_multiplier = 0.3
+ var/last_process = 0
+
+ var/lastangle = 0
+ var/aiming_lastangle = 0
+ var/mob/current_user = null
+ var/list/obj/effect/projectile/tracer/current_tracers
+
+ var/structure_piercing = 2 //Amount * 2. For some reason structures aren't respecting this unless you have it doubled. Probably with the objects in question's Bump() code instead of this but I'll deal with this later.
+ var/structure_bleed_coeff = 0.7
+ var/wall_pierce_amount = 0
+ var/wall_devastate = 0
+ var/aoe_structure_range = 1
+ var/aoe_structure_damage = 50
+ var/aoe_fire_range = 2
+ var/aoe_fire_chance = 40
+ var/aoe_mob_range = 1
+ var/aoe_mob_damage = 30
+ var/impact_structure_damage = 60
+ var/projectile_damage = 30
+ var/projectile_stun = 0
+ var/projectile_setting_pierce = TRUE
+ var/delay = 65
+ var/lastfire = 0
+
+ //ZOOMING
+ var/zoom_current_view_increase = 0
+ var/zoom_target_view_increase = 10
+ var/zooming = FALSE
+ var/zoom_lock = ZOOM_LOCK_OFF
+ var/zooming_angle
+ var/current_zoom_x = 0
+ var/current_zoom_y = 0
+ var/zoom_animating = 0
+
+ var/static/image/charged_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_charged")
+ var/static/image/drained_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_empty")
+
+ var/datum/action/item_action/zoom_lock_action/zoom_lock_action
+ var/datum/component/mobhook
+
+/obj/item/gun/energy/beam_rifle/debug
+ delay = 0
+ cell_type = /obj/item/stock_parts/cell/infinite
+ aiming_time = 0
+ recoil = 0
+ pin = /obj/item/device/firing_pin
+
+/obj/item/gun/energy/beam_rifle/equipped(mob/user)
+ set_user(user)
+ . = ..()
+
+/obj/item/gun/energy/beam_rifle/pickup(mob/user)
+ set_user(user)
+ . = ..()
+
+/obj/item/gun/energy/beam_rifle/dropped(mob/user)
+ set_user()
+ . = ..()
+
+/obj/item/gun/energy/beam_rifle/ui_action_click(owner, action)
+ if(istype(action, /datum/action/item_action/zoom_lock_action))
+ zoom_lock++
+ if(zoom_lock > 3)
+ zoom_lock = 0
+ switch(zoom_lock)
+ if(ZOOM_LOCK_AUTOZOOM_FREEMOVE)
+ to_chat(owner, "You switch [src]'s zooming processor to free directional.")
+ if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK)
+ to_chat(owner, "You switch [src]'s zooming processor to locked directional.")
+ if(ZOOM_LOCK_CENTER_VIEW)
+ to_chat(owner, "You switch [src]'s zooming processor to center mode.")
+ if(ZOOM_LOCK_OFF)
+ to_chat(owner, "You disable [src]'s zooming system.")
+ reset_zooming()
+
+/obj/item/gun/energy/beam_rifle/proc/smooth_zooming(delay_override = null)
+ if(!check_user() || !zooming || zoom_lock == ZOOM_LOCK_OFF || zoom_lock == ZOOM_LOCK_CENTER_VIEW)
+ return
+ if(zoom_animating && delay_override != 0)
+ return smooth_zooming(zoom_animating + delay_override) //Automatically compensate for ongoing zooming actions.
+ var/total_time = SSfastprocess.wait
+ if(delay_override)
+ total_time = delay_override
+ zoom_animating = total_time
+ animate(current_user.client, pixel_x = current_zoom_x, pixel_y = current_zoom_y , total_time, SINE_EASING, ANIMATION_PARALLEL)
+ zoom_animating = 0
+
+/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle)
+ if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF)
+ return
+ current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
+ current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
+
+/obj/item/gun/energy/beam_rifle/proc/handle_zooming()
+ if(!zooming || !check_user())
+ return
+ current_user.client.change_view(world.view + zoom_target_view_increase)
+ zoom_current_view_increase = zoom_target_view_increase
+ set_autozoom_pixel_offsets_immediate(zooming_angle)
+ smooth_zooming()
+
+/obj/item/gun/energy/beam_rifle/proc/start_zooming()
+ if(zoom_lock == ZOOM_LOCK_OFF)
+ return
+ zooming = TRUE
+
+/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
+ if(zooming)
+ zooming = FALSE
+ reset_zooming(user)
+
+/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user)
+ if(!user)
+ user = current_user
+ if(!user || !user.client)
+ return FALSE
+ zoom_animating = 0
+ animate(user.client, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
+ zoom_current_view_increase = 0
+ user.client.change_view(CONFIG_GET(string/default_view))
+ zooming_angle = 0
+ current_zoom_x = 0
+ current_zoom_y = 0
+
+/obj/item/gun/energy/beam_rifle/update_icon()
+ cut_overlays()
+ var/obj/item/ammo_casing/energy/primary_ammo = ammo_type[1]
+ if(cell.charge > primary_ammo.e_cost)
+ add_overlay(charged_overlay)
+ else
+ add_overlay(drained_overlay)
+
+/obj/item/gun/energy/beam_rifle/attack_self(mob/user)
+ projectile_setting_pierce = !projectile_setting_pierce
+ to_chat(user, "You set \the [src] to [projectile_setting_pierce? "pierce":"impact"] mode.")
+ aiming_beam()
+
+/obj/item/gun/energy/beam_rifle/proc/update_slowdown()
+ if(aiming)
+ slowdown = scoped_slow
+ else
+ slowdown = initial(slowdown)
+
+/obj/item/gun/energy/beam_rifle/Initialize()
+ . = ..()
+ current_tracers = list()
+ START_PROCESSING(SSprojectiles, src)
+ zoom_lock_action = new(src)
+
+/obj/item/gun/energy/beam_rifle/Destroy()
+ STOP_PROCESSING(SSfastprocess, src)
+ set_user(null)
+ QDEL_LIST(current_tracers)
+ QDEL_NULL(mobhook)
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/emp_act(severity)
+ chambered = null
+ recharge_newshot()
+
+/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
+ var/diff = abs(aiming_lastangle - lastangle)
+ check_user()
+ if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update)
+ return
+ aiming_lastangle = lastangle
+ var/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/P = new
+ P.gun = src
+ P.wall_pierce_amount = wall_pierce_amount
+ P.structure_pierce_amount = structure_piercing
+ P.do_pierce = projectile_setting_pierce
+ if(aiming_time)
+ var/percent = ((100/aiming_time)*aiming_time_left)
+ P.color = rgb(255 * percent,255 * ((100 - percent) / 100),0)
+ else
+ P.color = rgb(0, 255, 0)
+ var/turf/curloc = get_turf(src)
+ var/turf/targloc = get_turf(current_user.client.mouseObject)
+ if(!istype(targloc))
+ if(!istype(curloc))
+ return
+ targloc = get_turf_in_angle(lastangle, curloc, 10)
+ P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
+ P.fire(lastangle)
+
+/obj/item/gun/energy/beam_rifle/process()
+ if(!aiming)
+ last_process = world.time
+ return
+ check_user()
+ handle_zooming()
+ aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
+ aiming_beam(TRUE)
+ last_process = world.time
+
+/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
+ if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
+ if(automatic_cleanup)
+ stop_aiming()
+ set_user(null)
+ return FALSE
+ return TRUE
+
+/obj/item/gun/energy/beam_rifle/proc/process_aim()
+ if(istype(current_user) && current_user.client && current_user.client.mouseParams)
+ var/angle = mouse_angle_from_client(current_user.client)
+ switch(angle)
+ if(316 to 360)
+ current_user.setDir(NORTH)
+ if(0 to 45)
+ current_user.setDir(NORTH)
+ if(46 to 135)
+ current_user.setDir(EAST)
+ if(136 to 225)
+ current_user.setDir(SOUTH)
+ if(226 to 315)
+ current_user.setDir(WEST)
+ var/difference = abs(lastangle - angle)
+ if(difference > 350) //Too lazy to properly math, detects 360 --> 0 changes.
+ difference = (lastangle > 350? ((360 - lastangle) + angle) : ((360 - angle) + lastangle))
+ delay_penalty(difference * aiming_time_increase_angle_multiplier)
+ lastangle = angle
+
+/obj/item/gun/energy/beam_rifle/proc/on_mob_move()
+ check_user()
+ if(aiming)
+ delay_penalty(aiming_time_increase_user_movement)
+ process_aim()
+ aiming_beam(TRUE)
+
+/obj/item/gun/energy/beam_rifle/proc/start_aiming()
+ aiming_time_left = aiming_time
+ aiming = TRUE
+ process_aim()
+ aiming_beam(TRUE)
+ zooming_angle = lastangle
+ start_zooming()
+
+/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user)
+ set waitfor = FALSE
+ aiming_time_left = aiming_time
+ aiming = FALSE
+ QDEL_LIST(current_tracers)
+ stop_zooming(user)
+
+/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user)
+ if(user == current_user)
+ return
+ stop_aiming(current_user)
+ QDEL_NULL(mobhook)
+ if(istype(current_user))
+ LAZYREMOVE(current_user.mousemove_intercept_objects, src)
+ current_user = null
+ if(istype(user))
+ current_user = user
+ LAZYADD(current_user.mousemove_intercept_objects, src)
+ mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move))
+
+/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
+ if(aiming)
+ process_aim()
+ aiming_beam()
+ if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE)
+ zooming_angle = lastangle
+ set_autozoom_pixel_offsets_immediate(zooming_angle)
+ smooth_zooming(2)
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/onMouseDown(object, location, params, mob/mob)
+ if(istype(mob))
+ set_user(mob)
+ if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
+ return
+ if((object in mob.contents) || (object == mob))
+ return
+ start_aiming()
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/onMouseUp(object, location, params, mob/M)
+ if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
+ return
+ process_aim()
+ if(aiming_time_left <= aiming_time_fire_threshold && check_user())
+ sync_ammo()
+ afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
+ stop_aiming()
+ QDEL_LIST(current_tracers)
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE)
+ if(flag) //It's adjacent, is the user, or is on the user's person
+ if(target in user.contents) //can't shoot stuff inside us.
+ return
+ if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack
+ return
+ if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected)
+ return
+ if(!passthrough && (aiming_time > aiming_time_fire_threshold))
+ return
+ if(lastfire > world.time + delay)
+ return
+ lastfire = world.time
+ . = ..()
+ stop_aiming()
+
+/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
+ for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents)
+ AC.sync_stats()
+
+/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount)
+ aiming_time_left = CLAMP(aiming_time_left + amount, 0, aiming_time)
+
+/obj/item/ammo_casing/energy/beam_rifle
+ name = "particle acceleration lens"
+ desc = "Don't look into barrel!"
+ var/wall_pierce_amount = 0
+ var/wall_devastate = 0
+ var/aoe_structure_range = 1
+ var/aoe_structure_damage = 30
+ var/aoe_fire_range = 2
+ var/aoe_fire_chance = 66
+ var/aoe_mob_range = 1
+ var/aoe_mob_damage = 20
+ var/impact_structure_damage = 50
+ var/projectile_damage = 40
+ var/projectile_stun = 0
+ var/structure_piercing = 2
+ var/structure_bleed_coeff = 0.7
+ var/do_pierce = TRUE
+ var/obj/item/gun/energy/beam_rifle/host
+
+/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats()
+ var/obj/item/gun/energy/beam_rifle/BR = loc
+ if(!istype(BR))
+ stack_trace("Beam rifle syncing error")
+ host = BR
+ do_pierce = BR.projectile_setting_pierce
+ wall_pierce_amount = BR.wall_pierce_amount
+ wall_devastate = BR.wall_devastate
+ aoe_structure_range = BR.aoe_structure_range
+ aoe_structure_damage = BR.aoe_structure_damage
+ aoe_fire_range = BR.aoe_fire_range
+ aoe_fire_chance = BR.aoe_fire_chance
+ aoe_mob_range = BR.aoe_mob_range
+ aoe_mob_damage = BR.aoe_mob_damage
+ impact_structure_damage = BR.impact_structure_damage
+ projectile_damage = BR.projectile_damage
+ projectile_stun = BR.projectile_stun
+ delay = BR.delay
+ structure_piercing = BR.structure_piercing
+ structure_bleed_coeff = BR.structure_bleed_coeff
+
+/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+ . = ..()
+ var/obj/item/projectile/beam/beam_rifle/hitscan/HS_BB = BB
+ if(!istype(HS_BB))
+ return
+ HS_BB.impact_direct_damage = projectile_damage
+ HS_BB.stun = projectile_stun
+ HS_BB.impact_structure_damage = impact_structure_damage
+ HS_BB.aoe_mob_damage = aoe_mob_damage
+ HS_BB.aoe_mob_range = CLAMP(aoe_mob_range, 0, 15) //Badmin safety lock
+ HS_BB.aoe_fire_chance = aoe_fire_chance
+ HS_BB.aoe_fire_range = aoe_fire_range
+ HS_BB.aoe_structure_damage = aoe_structure_damage
+ HS_BB.aoe_structure_range = CLAMP(aoe_structure_range, 0, 15) //Badmin safety lock
+ HS_BB.wall_devastate = wall_devastate
+ HS_BB.wall_pierce_amount = wall_pierce_amount
+ HS_BB.structure_pierce_amount = structure_piercing
+ HS_BB.structure_bleed_coeff = structure_bleed_coeff
+ HS_BB.do_pierce = do_pierce
+ HS_BB.gun = host
+
+/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread)
+ var/turf/curloc = get_turf(user)
+ if(!istype(curloc) || !BB)
+ return FALSE
+ var/obj/item/gun/energy/beam_rifle/gun = loc
+ if(!targloc && gun)
+ targloc = get_turf_in_angle(gun.lastangle, curloc, 10)
+ else if(!targloc)
+ return FALSE
+ var/firing_dir
+ if(BB.firer)
+ firing_dir = BB.firer.dir
+ if(!BB.suppressed && firing_effect_type)
+ new firing_effect_type(get_turf(src), firing_dir)
+ BB.preparePixelProjectile(target, user, params, spread)
+ BB.fire(gun? gun.lastangle : null, null)
+ BB = null
+ return TRUE
+
+/obj/item/ammo_casing/energy/beam_rifle/hitscan
+ projectile_type = /obj/item/projectile/beam/beam_rifle/hitscan
+ select_name = "beam"
+ e_cost = 5000
+ fire_sound = 'sound/weapons/beam_sniper.ogg'
+
+/obj/item/projectile/beam/beam_rifle
+ name = "particle beam"
+ icon = ""
+ hitsound = 'sound/effects/explosion3.ogg'
+ damage = 0 //Handled manually.
+ damage_type = BURN
+ flag = "energy"
+ range = 150
+ jitter = 10
+ var/obj/item/gun/energy/beam_rifle/gun
+ var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing.
+ var/structure_bleed_coeff = 0
+ var/structure_pierce = 0
+ var/do_pierce = TRUE
+ var/wall_pierce_amount = 0
+ var/wall_pierce = 0
+ var/wall_devastate = 0
+ var/aoe_structure_range = 0
+ var/aoe_structure_damage = 0
+ var/aoe_fire_range = 0
+ var/aoe_fire_chance = 0
+ var/aoe_mob_range = 0
+ var/aoe_mob_damage = 0
+ var/impact_structure_damage = 0
+ var/impact_direct_damage = 0
+ var/turf/cached
+ var/list/pierced = list()
+
+/obj/item/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
+ set waitfor = FALSE
+ if(!epicenter)
+ return
+ new /obj/effect/temp_visual/explosion/fast(epicenter)
+ for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage
+ L.adjustFireLoss(aoe_mob_damage)
+ to_chat(L, "\The [src] sears you!")
+ for(var/turf/T in range(aoe_fire_range, epicenter)) //handle aoe fire
+ if(prob(aoe_fire_chance))
+ new /obj/effect/hotspot(T)
+ for(var/obj/O in range(aoe_structure_range, epicenter))
+ if(!isitem(O))
+ if(O.level == 1) //Please don't break underfloor items!
+ continue
+ O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, "laser", FALSE)
+
+/obj/item/projectile/beam/beam_rifle/proc/check_pierce(atom/target)
+ if(!do_pierce)
+ return FALSE
+ if(pierced[target]) //we already pierced them go away
+ return TRUE
+ if(isclosedturf(target))
+ if(wall_pierce++ < wall_pierce_amount)
+ if(prob(wall_devastate))
+ if(iswallturf(target))
+ var/turf/closed/wall/W = target
+ W.dismantle_wall(TRUE, TRUE)
+ else
+ target.ex_act(EXPLODE_HEAVY)
+ return TRUE
+ if(ismovableatom(target))
+ var/atom/movable/AM = target
+ if(AM.density && !AM.CanPass(src, get_turf(target)) && !ismob(AM))
+ if(structure_pierce < structure_pierce_amount)
+ if(isobj(AM))
+ var/obj/O = AM
+ O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(AM), BURN, "energy", FALSE)
+ pierced[AM] = TRUE
+ structure_pierce++
+ return TRUE
+ return FALSE
+
+/obj/item/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
+ if(istype(target, /obj/machinery/door))
+ return 0.4
+ if(istype(target, /obj/structure/window))
+ return 0.5
+ return 1
+
+/obj/item/projectile/beam/beam_rifle/proc/handle_impact(atom/target)
+ if(isobj(target))
+ var/obj/O = target
+ O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, "laser", FALSE)
+ if(isliving(target))
+ var/mob/living/L = target
+ L.adjustFireLoss(impact_direct_damage)
+ L.emote("scream")
+
+/obj/item/projectile/beam/beam_rifle/proc/handle_hit(atom/target)
+ set waitfor = FALSE
+ if(!cached && !QDELETED(target))
+ cached = get_turf(target)
+ if(nodamage)
+ return FALSE
+ playsound(cached, 'sound/effects/explosion3.ogg', 100, 1)
+ AOE(cached)
+ if(!QDELETED(target))
+ handle_impact(target)
+
+/obj/item/projectile/beam/beam_rifle/Collide(atom/target)
+ if(check_pierce(target))
+ permutated += target
+ trajectory_ignore_forcemove = TRUE
+ forceMove(target)
+ trajectory_ignore_forcemove = FALSE
+ return FALSE
+ if(!QDELETED(target))
+ cached = get_turf(target)
+ . = ..()
+
+/obj/item/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE)
+ if(!QDELETED(target))
+ cached = get_turf(target)
+ handle_hit(target)
+ . = ..()
+
+/obj/item/projectile/beam/beam_rifle/hitscan
+ icon_state = ""
+ hitscan = TRUE
+ tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
+ var/constant_tracer = FALSE
+
+/obj/item/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander)
+ set waitfor = FALSE
+ if(isnull(highlander))
+ highlander = constant_tracer
+ if(highlander && istype(gun))
+ QDEL_LIST(gun.current_tracers)
+ for(var/datum/point/p in beam_segments)
+ gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0)
+ else
+ for(var/datum/point/p in beam_segments)
+ generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration)
+ if(cleanup)
+ QDEL_LIST(beam_segments)
+ beam_segments = null
+ QDEL_NULL(beam_index)
+
+/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam
+ tracer_type = /obj/effect/projectile/tracer/tracer/aiming
+ name = "aiming beam"
+ hitsound = null
+ hitsound_wall = null
+ nodamage = TRUE
+ damage = 0
+ constant_tracer = TRUE
+
+/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target)
+ qdel(src)
+ return FALSE
+
+/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit()
+ qdel(src)
+ return FALSE
diff --git a/code/modules/projectiles/guns/chem_gun.dm b/code/modules/projectiles/guns/misc/chem_gun.dm
similarity index 95%
rename from code/modules/projectiles/guns/chem_gun.dm
rename to code/modules/projectiles/guns/misc/chem_gun.dm
index b928abafef..17e3bd1876 100644
--- a/code/modules/projectiles/guns/chem_gun.dm
+++ b/code/modules/projectiles/guns/misc/chem_gun.dm
@@ -1,47 +1,47 @@
-//his isn't a subtype of the syringe gun because the syringegun subtype is made to hold syringes
-//this is meant to hold reagents/obj/item/gun/syringe
-/obj/item/gun/chem
- name = "reagent gun"
- desc = "A Nanotrasen syringe gun, modified to automatically synthesise chemical darts, and instead hold reagents."
- icon_state = "chemgun"
- item_state = "chemgun"
- w_class = WEIGHT_CLASS_NORMAL
- throw_speed = 3
- throw_range = 7
- force = 4
- materials = list(MAT_METAL=2000)
- clumsy_check = FALSE
- fire_sound = 'sound/items/syringeproj.ogg'
- container_type = OPENCONTAINER
- var/time_per_syringe = 250
- var/syringes_left = 4
- var/max_syringes = 4
- var/last_synth = 0
-
-/obj/item/gun/chem/Initialize()
- . = ..()
- chambered = new /obj/item/ammo_casing/chemgun(src)
- START_PROCESSING(SSobj, src)
- create_reagents(100)
-
-/obj/item/gun/chem/Destroy()
- . = ..()
- STOP_PROCESSING(SSobj, src)
-
-/obj/item/gun/chem/can_shoot()
- return syringes_left
-
-/obj/item/gun/chem/process_chamber()
- if(chambered && !chambered.BB && syringes_left)
- chambered.newshot()
-
-/obj/item/gun/chem/process()
- if(syringes_left >= max_syringes)
- return
- if(world.time < last_synth+time_per_syringe)
- return
- to_chat(loc, "You hear a click as [src] synthesizes a new dart.")
- syringes_left++
- if(chambered && !chambered.BB)
- chambered.newshot()
+//his isn't a subtype of the syringe gun because the syringegun subtype is made to hold syringes
+//this is meant to hold reagents/obj/item/gun/syringe
+/obj/item/gun/chem
+ name = "reagent gun"
+ desc = "A Nanotrasen syringe gun, modified to automatically synthesise chemical darts, and instead hold reagents."
+ icon_state = "chemgun"
+ item_state = "chemgun"
+ w_class = WEIGHT_CLASS_NORMAL
+ throw_speed = 3
+ throw_range = 7
+ force = 4
+ materials = list(MAT_METAL=2000)
+ clumsy_check = FALSE
+ fire_sound = 'sound/items/syringeproj.ogg'
+ container_type = OPENCONTAINER
+ var/time_per_syringe = 250
+ var/syringes_left = 4
+ var/max_syringes = 4
+ var/last_synth = 0
+
+/obj/item/gun/chem/Initialize()
+ . = ..()
+ chambered = new /obj/item/ammo_casing/chemgun(src)
+ START_PROCESSING(SSobj, src)
+ create_reagents(100)
+
+/obj/item/gun/chem/Destroy()
+ . = ..()
+ STOP_PROCESSING(SSobj, src)
+
+/obj/item/gun/chem/can_shoot()
+ return syringes_left
+
+/obj/item/gun/chem/process_chamber()
+ if(chambered && !chambered.BB && syringes_left)
+ chambered.newshot()
+
+/obj/item/gun/chem/process()
+ if(syringes_left >= max_syringes)
+ return
+ if(world.time < last_synth+time_per_syringe)
+ return
+ to_chat(loc, "You hear a click as [src] synthesizes a new dart.")
+ syringes_left++
+ if(chambered && !chambered.BB)
+ chambered.newshot()
last_synth = world.time
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/grenade_launcher.dm b/code/modules/projectiles/guns/misc/grenade_launcher.dm
similarity index 97%
rename from code/modules/projectiles/guns/grenade_launcher.dm
rename to code/modules/projectiles/guns/misc/grenade_launcher.dm
index 771c0091e3..e57d77bdf9 100644
--- a/code/modules/projectiles/guns/grenade_launcher.dm
+++ b/code/modules/projectiles/guns/misc/grenade_launcher.dm
@@ -1,52 +1,52 @@
-/obj/item/gun/grenadelauncher
- name = "grenade launcher"
- desc = "A terrible, terrible thing. It's really awful!"
- icon = 'icons/obj/guns/projectile.dmi'
- icon_state = "riotgun"
- item_state = "riotgun"
- w_class = WEIGHT_CLASS_BULKY
- throw_speed = 2
- throw_range = 7
- force = 5
- var/list/grenades = new/list()
- var/max_grenades = 3
- materials = list(MAT_METAL=2000)
-
-/obj/item/gun/grenadelauncher/examine(mob/user)
- ..()
- to_chat(user, "[grenades.len] / [max_grenades] grenades loaded.")
-
-/obj/item/gun/grenadelauncher/attackby(obj/item/I, mob/user, params)
-
- if((istype(I, /obj/item/grenade)))
- if(grenades.len < max_grenades)
- if(!user.transferItemToLoc(I, src))
- return
- grenades += I
- to_chat(user, "You put the grenade in the grenade launcher.")
- to_chat(user, "[grenades.len] / [max_grenades] Grenades.")
- else
- to_chat(usr, "The grenade launcher cannot hold more grenades.")
-
-/obj/item/gun/grenadelauncher/afterattack(obj/target, mob/user , flag)
- if(target == user)
- return
-
- if(grenades.len)
- fire_grenade(target,user)
- else
- to_chat(user, "The grenade launcher is empty.")
-
-/obj/item/gun/grenadelauncher/proc/fire_grenade(atom/target, mob/user)
- user.visible_message("[user] fired a grenade!", \
- "You fire the grenade launcher!")
- var/obj/item/grenade/F = grenades[1] //Now with less copypasta!
- grenades -= F
- F.forceMove(user.loc)
- F.throw_at(target, 30, 2, user)
- message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).")
- log_game("[key_name(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).")
- F.active = 1
- F.icon_state = initial(F.icon_state) + "_active"
- playsound(user.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3)
- addtimer(CALLBACK(F, /obj/item/grenade.proc/prime), 15)
+/obj/item/gun/grenadelauncher
+ name = "grenade launcher"
+ desc = "A terrible, terrible thing. It's really awful!"
+ icon = 'icons/obj/guns/projectile.dmi'
+ icon_state = "riotgun"
+ item_state = "riotgun"
+ w_class = WEIGHT_CLASS_BULKY
+ throw_speed = 2
+ throw_range = 7
+ force = 5
+ var/list/grenades = new/list()
+ var/max_grenades = 3
+ materials = list(MAT_METAL=2000)
+
+/obj/item/gun/grenadelauncher/examine(mob/user)
+ ..()
+ to_chat(user, "[grenades.len] / [max_grenades] grenades loaded.")
+
+/obj/item/gun/grenadelauncher/attackby(obj/item/I, mob/user, params)
+
+ if((istype(I, /obj/item/grenade)))
+ if(grenades.len < max_grenades)
+ if(!user.transferItemToLoc(I, src))
+ return
+ grenades += I
+ to_chat(user, "You put the grenade in the grenade launcher.")
+ to_chat(user, "[grenades.len] / [max_grenades] Grenades.")
+ else
+ to_chat(usr, "The grenade launcher cannot hold more grenades.")
+
+/obj/item/gun/grenadelauncher/afterattack(obj/target, mob/user , flag)
+ if(target == user)
+ return
+
+ if(grenades.len)
+ fire_grenade(target,user)
+ else
+ to_chat(user, "The grenade launcher is empty.")
+
+/obj/item/gun/grenadelauncher/proc/fire_grenade(atom/target, mob/user)
+ user.visible_message("[user] fired a grenade!", \
+ "You fire the grenade launcher!")
+ var/obj/item/grenade/F = grenades[1] //Now with less copypasta!
+ grenades -= F
+ F.forceMove(user.loc)
+ F.throw_at(target, 30, 2, user)
+ message_admins("[key_name_admin(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).")
+ log_game("[key_name(user)] fired a grenade ([F.name]) from a grenade launcher ([src.name]).")
+ F.active = 1
+ F.icon_state = initial(F.icon_state) + "_active"
+ playsound(user.loc, 'sound/weapons/armbomb.ogg', 75, 1, -3)
+ addtimer(CALLBACK(F, /obj/item/grenade.proc/prime), 15)
diff --git a/code/modules/projectiles/guns/medbeam.dm b/code/modules/projectiles/guns/misc/medbeam.dm
similarity index 96%
rename from code/modules/projectiles/guns/medbeam.dm
rename to code/modules/projectiles/guns/misc/medbeam.dm
index 79cafe0dd6..8fb07c32e3 100644
--- a/code/modules/projectiles/guns/medbeam.dm
+++ b/code/modules/projectiles/guns/misc/medbeam.dm
@@ -1,133 +1,133 @@
-/obj/item/gun/medbeam
- name = "Medical Beamgun"
- desc = "Don't cross the streams!"
- icon = 'icons/obj/chronos.dmi'
- icon_state = "chronogun"
- item_state = "chronogun"
- w_class = WEIGHT_CLASS_NORMAL
-
- var/mob/living/current_target
- var/last_check = 0
- var/check_delay = 10 //Check los as often as possible, max resolution is SSobj tick though
- var/max_range = 8
- var/active = 0
- var/datum/beam/current_beam = null
- var/mounted = 0 //Denotes if this is a handheld or mounted version
-
- weapon_weight = WEAPON_MEDIUM
-
-/obj/item/gun/medbeam/Initialize()
- . = ..()
- START_PROCESSING(SSobj, src)
-
-/obj/item/gun/medbeam/Destroy(mob/user)
- STOP_PROCESSING(SSobj, src)
- LoseTarget()
- return ..()
-
-/obj/item/gun/medbeam/dropped(mob/user)
- ..()
- LoseTarget()
-
-/obj/item/gun/medbeam/equipped(mob/user)
- ..()
- LoseTarget()
-
-/obj/item/gun/medbeam/proc/LoseTarget()
- if(active)
- qdel(current_beam)
- current_beam = null
- active = 0
- on_beam_release(current_target)
- current_target = null
-
-/obj/item/gun/medbeam/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
- if(isliving(user))
- add_fingerprint(user)
-
- if(current_target)
- LoseTarget()
- if(!isliving(target))
- return
-
- current_target = target
- active = TRUE
- current_beam = new(user,current_target,time=6000,beam_icon_state="medbeam",btype=/obj/effect/ebeam/medical)
- INVOKE_ASYNC(current_beam, /datum/beam.proc/Start)
-
- SSblackbox.record_feedback("tally", "gun_fired", 1, type)
-
-/obj/item/gun/medbeam/process()
-
- var/source = loc
- if(!mounted && !isliving(source))
- LoseTarget()
- return
-
- if(!current_target)
- LoseTarget()
- return
-
- if(world.time <= last_check+check_delay)
- return
-
- last_check = world.time
-
- if(get_dist(source, current_target)>max_range || !los_check(source, current_target))
- LoseTarget()
- if(isliving(source))
- to_chat(source, "You lose control of the beam!")
- return
-
- if(current_target)
- on_beam_tick(current_target)
-
-/obj/item/gun/medbeam/proc/los_check(atom/movable/user, mob/target)
- var/turf/user_turf = user.loc
- if(mounted)
- user_turf = get_turf(user)
- else if(!istype(user_turf))
- return 0
- var/obj/dummy = new(user_turf)
- dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE //Grille/Glass so it can be used through common windows
- for(var/turf/turf in getline(user_turf,target))
- if(mounted && turf == user_turf)
- continue //Mechs are dense and thus fail the check
- if(turf.density)
- qdel(dummy)
- return 0
- for(var/atom/movable/AM in turf)
- if(!AM.CanPass(dummy,turf,1))
- qdel(dummy)
- return 0
- for(var/obj/effect/ebeam/medical/B in turf)// Don't cross the str-beams!
- if(B.owner.origin != current_beam.origin)
- explosion(B.loc,0,3,5,8)
- qdel(dummy)
- return 0
- qdel(dummy)
- return 1
-
-/obj/item/gun/medbeam/proc/on_beam_hit(var/mob/living/target)
- return
-
-/obj/item/gun/medbeam/proc/on_beam_tick(var/mob/living/target)
- if(target.health != target.maxHealth)
- new /obj/effect/temp_visual/heal(get_turf(target), "#80F5FF")
- target.adjustBruteLoss(-4)
- target.adjustFireLoss(-4)
- return
-
-/obj/item/gun/medbeam/proc/on_beam_release(var/mob/living/target)
- return
-
-/obj/effect/ebeam/medical
- name = "medical beam"
-
-//////////////////////////////Mech Version///////////////////////////////
-/obj/item/gun/medbeam/mech
- mounted = 1
-
-/obj/item/gun/medbeam/mech/Initialize()
- . = ..()
- STOP_PROCESSING(SSobj, src) //Mech mediguns do not process until installed, and are controlled by the holder obj
+/obj/item/gun/medbeam
+ name = "Medical Beamgun"
+ desc = "Don't cross the streams!"
+ icon = 'icons/obj/chronos.dmi'
+ icon_state = "chronogun"
+ item_state = "chronogun"
+ w_class = WEIGHT_CLASS_NORMAL
+
+ var/mob/living/current_target
+ var/last_check = 0
+ var/check_delay = 10 //Check los as often as possible, max resolution is SSobj tick though
+ var/max_range = 8
+ var/active = 0
+ var/datum/beam/current_beam = null
+ var/mounted = 0 //Denotes if this is a handheld or mounted version
+
+ weapon_weight = WEAPON_MEDIUM
+
+/obj/item/gun/medbeam/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+
+/obj/item/gun/medbeam/Destroy(mob/user)
+ STOP_PROCESSING(SSobj, src)
+ LoseTarget()
+ return ..()
+
+/obj/item/gun/medbeam/dropped(mob/user)
+ ..()
+ LoseTarget()
+
+/obj/item/gun/medbeam/equipped(mob/user)
+ ..()
+ LoseTarget()
+
+/obj/item/gun/medbeam/proc/LoseTarget()
+ if(active)
+ qdel(current_beam)
+ current_beam = null
+ active = 0
+ on_beam_release(current_target)
+ current_target = null
+
+/obj/item/gun/medbeam/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
+ if(isliving(user))
+ add_fingerprint(user)
+
+ if(current_target)
+ LoseTarget()
+ if(!isliving(target))
+ return
+
+ current_target = target
+ active = TRUE
+ current_beam = new(user,current_target,time=6000,beam_icon_state="medbeam",btype=/obj/effect/ebeam/medical)
+ INVOKE_ASYNC(current_beam, /datum/beam.proc/Start)
+
+ SSblackbox.record_feedback("tally", "gun_fired", 1, type)
+
+/obj/item/gun/medbeam/process()
+
+ var/source = loc
+ if(!mounted && !isliving(source))
+ LoseTarget()
+ return
+
+ if(!current_target)
+ LoseTarget()
+ return
+
+ if(world.time <= last_check+check_delay)
+ return
+
+ last_check = world.time
+
+ if(get_dist(source, current_target)>max_range || !los_check(source, current_target))
+ LoseTarget()
+ if(isliving(source))
+ to_chat(source, "You lose control of the beam!")
+ return
+
+ if(current_target)
+ on_beam_tick(current_target)
+
+/obj/item/gun/medbeam/proc/los_check(atom/movable/user, mob/target)
+ var/turf/user_turf = user.loc
+ if(mounted)
+ user_turf = get_turf(user)
+ else if(!istype(user_turf))
+ return 0
+ var/obj/dummy = new(user_turf)
+ dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE //Grille/Glass so it can be used through common windows
+ for(var/turf/turf in getline(user_turf,target))
+ if(mounted && turf == user_turf)
+ continue //Mechs are dense and thus fail the check
+ if(turf.density)
+ qdel(dummy)
+ return 0
+ for(var/atom/movable/AM in turf)
+ if(!AM.CanPass(dummy,turf,1))
+ qdel(dummy)
+ return 0
+ for(var/obj/effect/ebeam/medical/B in turf)// Don't cross the str-beams!
+ if(B.owner.origin != current_beam.origin)
+ explosion(B.loc,0,3,5,8)
+ qdel(dummy)
+ return 0
+ qdel(dummy)
+ return 1
+
+/obj/item/gun/medbeam/proc/on_beam_hit(var/mob/living/target)
+ return
+
+/obj/item/gun/medbeam/proc/on_beam_tick(var/mob/living/target)
+ if(target.health != target.maxHealth)
+ new /obj/effect/temp_visual/heal(get_turf(target), "#80F5FF")
+ target.adjustBruteLoss(-4)
+ target.adjustFireLoss(-4)
+ return
+
+/obj/item/gun/medbeam/proc/on_beam_release(var/mob/living/target)
+ return
+
+/obj/effect/ebeam/medical
+ name = "medical beam"
+
+//////////////////////////////Mech Version///////////////////////////////
+/obj/item/gun/medbeam/mech
+ mounted = 1
+
+/obj/item/gun/medbeam/mech/Initialize()
+ . = ..()
+ STOP_PROCESSING(SSobj, src) //Mech mediguns do not process until installed, and are controlled by the holder obj
diff --git a/code/modules/projectiles/guns/syringe_gun.dm b/code/modules/projectiles/guns/misc/syringe_gun.dm
similarity index 96%
rename from code/modules/projectiles/guns/syringe_gun.dm
rename to code/modules/projectiles/guns/misc/syringe_gun.dm
index ac9f7daedf..cc1b321e3a 100644
--- a/code/modules/projectiles/guns/syringe_gun.dm
+++ b/code/modules/projectiles/guns/misc/syringe_gun.dm
@@ -1,104 +1,104 @@
-/obj/item/gun/syringe
- name = "syringe gun"
- desc = "A spring loaded rifle designed to fit syringes, used to incapacitate unruly patients from a distance."
- icon_state = "syringegun"
- item_state = "syringegun"
- w_class = WEIGHT_CLASS_NORMAL
- throw_speed = 3
- throw_range = 7
- force = 4
- materials = list(MAT_METAL=2000)
- clumsy_check = 0
- fire_sound = 'sound/items/syringeproj.ogg'
- var/list/syringes = list()
- var/max_syringes = 1
-
-/obj/item/gun/syringe/Initialize()
- . = ..()
- chambered = new /obj/item/ammo_casing/syringegun(src)
-
-/obj/item/gun/syringe/recharge_newshot()
- if(!syringes.len)
- return
- chambered.newshot()
-
-/obj/item/gun/syringe/can_shoot()
- return syringes.len
-
-/obj/item/gun/syringe/process_chamber()
- if(chambered && !chambered.BB) //we just fired
- recharge_newshot()
-
-/obj/item/gun/syringe/examine(mob/user)
- ..()
- to_chat(user, "Can hold [max_syringes] syringe\s. Has [syringes.len] syringe\s remaining.")
-
-/obj/item/gun/syringe/attack_self(mob/living/user)
- if(!syringes.len)
- to_chat(user, "[src] is empty!")
- return 0
-
- var/obj/item/reagent_containers/syringe/S = syringes[syringes.len]
-
- if(!S)
- return 0
- S.forceMove(user.loc)
-
- syringes.Remove(S)
- to_chat(user, "You unload [S] from \the [src].")
-
- return 1
-
-/obj/item/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
- if(istype(A, /obj/item/reagent_containers/syringe))
- if(syringes.len < max_syringes)
- if(!user.transferItemToLoc(A, src))
- return FALSE
- to_chat(user, "You load [A] into \the [src].")
- syringes += A
- recharge_newshot()
- return TRUE
- else
- to_chat(user, "[src] cannot hold more syringes!")
- return FALSE
-
-/obj/item/gun/syringe/rapidsyringe
- name = "rapid syringe gun"
- desc = "A modification of the syringe gun design, using a rotating cylinder to store up to six syringes."
- icon_state = "rapidsyringegun"
- max_syringes = 6
-
-/obj/item/gun/syringe/syndicate
- name = "dart pistol"
- desc = "A small spring-loaded sidearm that functions identically to a syringe gun."
- icon_state = "syringe_pistol"
- item_state = "gun" //Smaller inhand
- w_class = WEIGHT_CLASS_SMALL
- force = 2 //Also very weak because it's smaller
- suppressed = TRUE //Softer fire sound
- can_unsuppress = FALSE //Permanently silenced
-
-/obj/item/gun/syringe/dna
- name = "modified syringe gun"
- desc = "A syringe gun that has been modified to fit DNA injectors instead of normal syringes."
-
-/obj/item/gun/syringe/dna/Initialize()
- . = ..()
- chambered = new /obj/item/ammo_casing/dnainjector(src)
-
-/obj/item/gun/syringe/dna/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
- if(istype(A, /obj/item/dnainjector))
- var/obj/item/dnainjector/D = A
- if(D.used)
- to_chat(user, "This injector is used up!")
- return
- if(syringes.len < max_syringes)
- if(!user.transferItemToLoc(D, src))
- return FALSE
- to_chat(user, "You load \the [D] into \the [src].")
- syringes += D
- recharge_newshot()
- return TRUE
- else
- to_chat(user, "[src] cannot hold more syringes!")
- return FALSE
+/obj/item/gun/syringe
+ name = "syringe gun"
+ desc = "A spring loaded rifle designed to fit syringes, used to incapacitate unruly patients from a distance."
+ icon_state = "syringegun"
+ item_state = "syringegun"
+ w_class = WEIGHT_CLASS_NORMAL
+ throw_speed = 3
+ throw_range = 7
+ force = 4
+ materials = list(MAT_METAL=2000)
+ clumsy_check = 0
+ fire_sound = 'sound/items/syringeproj.ogg'
+ var/list/syringes = list()
+ var/max_syringes = 1
+
+/obj/item/gun/syringe/Initialize()
+ . = ..()
+ chambered = new /obj/item/ammo_casing/syringegun(src)
+
+/obj/item/gun/syringe/recharge_newshot()
+ if(!syringes.len)
+ return
+ chambered.newshot()
+
+/obj/item/gun/syringe/can_shoot()
+ return syringes.len
+
+/obj/item/gun/syringe/process_chamber()
+ if(chambered && !chambered.BB) //we just fired
+ recharge_newshot()
+
+/obj/item/gun/syringe/examine(mob/user)
+ ..()
+ to_chat(user, "Can hold [max_syringes] syringe\s. Has [syringes.len] syringe\s remaining.")
+
+/obj/item/gun/syringe/attack_self(mob/living/user)
+ if(!syringes.len)
+ to_chat(user, "[src] is empty!")
+ return 0
+
+ var/obj/item/reagent_containers/syringe/S = syringes[syringes.len]
+
+ if(!S)
+ return 0
+ S.forceMove(user.loc)
+
+ syringes.Remove(S)
+ to_chat(user, "You unload [S] from \the [src].")
+
+ return 1
+
+/obj/item/gun/syringe/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
+ if(istype(A, /obj/item/reagent_containers/syringe))
+ if(syringes.len < max_syringes)
+ if(!user.transferItemToLoc(A, src))
+ return FALSE
+ to_chat(user, "You load [A] into \the [src].")
+ syringes += A
+ recharge_newshot()
+ return TRUE
+ else
+ to_chat(user, "[src] cannot hold more syringes!")
+ return FALSE
+
+/obj/item/gun/syringe/rapidsyringe
+ name = "rapid syringe gun"
+ desc = "A modification of the syringe gun design, using a rotating cylinder to store up to six syringes."
+ icon_state = "rapidsyringegun"
+ max_syringes = 6
+
+/obj/item/gun/syringe/syndicate
+ name = "dart pistol"
+ desc = "A small spring-loaded sidearm that functions identically to a syringe gun."
+ icon_state = "syringe_pistol"
+ item_state = "gun" //Smaller inhand
+ w_class = WEIGHT_CLASS_SMALL
+ force = 2 //Also very weak because it's smaller
+ suppressed = TRUE //Softer fire sound
+ can_unsuppress = FALSE //Permanently silenced
+
+/obj/item/gun/syringe/dna
+ name = "modified syringe gun"
+ desc = "A syringe gun that has been modified to fit DNA injectors instead of normal syringes."
+
+/obj/item/gun/syringe/dna/Initialize()
+ . = ..()
+ chambered = new /obj/item/ammo_casing/dnainjector(src)
+
+/obj/item/gun/syringe/dna/attackby(obj/item/A, mob/user, params, show_msg = TRUE)
+ if(istype(A, /obj/item/dnainjector))
+ var/obj/item/dnainjector/D = A
+ if(D.used)
+ to_chat(user, "This injector is used up!")
+ return
+ if(syringes.len < max_syringes)
+ if(!user.transferItemToLoc(D, src))
+ return FALSE
+ to_chat(user, "You load \the [D] into \the [src].")
+ syringes += D
+ recharge_newshot()
+ return TRUE
+ else
+ to_chat(user, "[src] cannot hold more syringes!")
+ return FALSE
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 235f27e9e5..725ef9baa6 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -7,422 +7,3 @@
flag = "bullet"
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
-
-/obj/item/projectile/bullet/incendiary
- damage = 20
- var/fire_stacks = 4
-
-/obj/item/projectile/bullet/incendiary/on_hit(atom/target, blocked = FALSE)
- . = ..()
- if(iscarbon(target))
- var/mob/living/carbon/M = target
- M.adjust_fire_stacks(fire_stacks)
- M.IgniteMob()
-
-/obj/item/projectile/bullet/incendiary/Move()
- . = ..()
- var/turf/location = get_turf(src)
- if(location)
- new /obj/effect/hotspot(location)
- location.hotspot_expose(700, 50, 1)
-
-// .357 (Syndie Revolver)
-
-/obj/item/projectile/bullet/a357
- name = ".357 bullet"
- damage = 60
-
-// 7.62 (Nagant Rifle)
-
-/obj/item/projectile/bullet/a762
- name = "7.62 bullet"
- damage = 60
-
-/obj/item/projectile/bullet/a762_enchanted
- name = "enchanted 7.62 bullet"
- damage = 5
- stamina = 80
-
-// 7.62x38mmR (Nagant Revolver)
-
-/obj/item/projectile/bullet/n762
- name = "7.62x38mmR bullet"
- damage = 60
-
-// .50AE (Desert Eagle)
-
-/obj/item/projectile/bullet/a50AE
- name = ".50AE bullet"
- damage = 60
-
-// .38 (Detective's Gun)
-
-/obj/item/projectile/bullet/c38
- name = ".38 bullet"
- damage = 15
- knockdown = 60
- stamina = 50
-
-// 10mm (Stechkin)
-
-/obj/item/projectile/bullet/c10mm
- name = "10mm bullet"
- damage = 30
-
-/obj/item/projectile/bullet/c10mm_ap
- name = "10mm armor-piercing bullet"
- damage = 27
- armour_penetration = 40
-
-/obj/item/projectile/bullet/c10mm_hp
- name = "10mm hollow-point bullet"
- damage = 40
- armour_penetration = -50
-
-/obj/item/projectile/bullet/incendiary/c10mm
- name = "10mm incendiary bullet"
- damage = 15
- fire_stacks = 2
-
-// 9mm (Stechkin APS)
-
-/obj/item/projectile/bullet/c9mm
- name = "9mm bullet"
- damage = 20
-
-/obj/item/projectile/bullet/c9mm_ap
- name = "9mm armor-piercing bullet"
- damage = 15
- armour_penetration = 40
-
-/obj/item/projectile/bullet/incendiary/c9mm
- name = "9mm incendiary bullet"
- damage = 10
- fire_stacks = 1
-
-// 4.6x30mm (Autorifles)
-
-/obj/item/projectile/bullet/c46x30mm
- name = "4.6x30mm bullet"
- damage = 20
-
-/obj/item/projectile/bullet/c46x30mm_ap
- name = "4.6x30mm armor-piercing bullet"
- damage = 15
- armour_penetration = 40
-
-/obj/item/projectile/bullet/incendiary/c46x30mm
- name = "4.6x30mm incendiary bullet"
- damage = 10
- fire_stacks = 1
-
-// .45 (M1911 & C20r)
-
-/obj/item/projectile/bullet/c45
- name = ".45 bullet"
- damage = 20
- stamina = 65
-
-/obj/item/projectile/bullet/c45_nostamina
- name = ".45 bullet"
- damage = 30
-
-// 5.56mm (M-90gl Carbine)
-
-/obj/item/projectile/bullet/a556
- name = "5.56mm bullet"
- damage = 35
-
-// 40mm (Grenade Launcher
-
-/obj/item/projectile/bullet/a40mm
- name ="40mm grenade"
- desc = "USE A WEEL GUN"
- icon_state= "bolter"
- damage = 60
-
-/obj/item/projectile/bullet/a40mm/on_hit(atom/target, blocked = FALSE)
- ..()
- explosion(target, -1, 0, 2, 1, 0, flame_range = 3)
- return TRUE
-
-// .50 (Sniper)
-
-/obj/item/projectile/bullet/p50
- name =".50 bullet"
- speed = 0.4
- damage = 70
- knockdown = 100
- dismemberment = 50
- armour_penetration = 50
- var/breakthings = TRUE
-
-/obj/item/projectile/bullet/p50/on_hit(atom/target, blocked = 0)
- if((blocked != 100) && (!ismob(target) && breakthings))
- target.ex_act(rand(1,2))
- return ..()
-
-/obj/item/projectile/bullet/p50/soporific
- name =".50 soporific bullet"
- armour_penetration = 0
- nodamage = TRUE
- dismemberment = 0
- knockdown = 0
- breakthings = FALSE
-
-/obj/item/projectile/bullet/p50/soporific/on_hit(atom/target, blocked = FALSE)
- if((blocked != 100) && isliving(target))
- var/mob/living/L = target
- L.Sleeping(400)
- return ..()
-
-/obj/item/projectile/bullet/p50/penetrator
- name =".50 penetrator bullet"
- icon_state = "gauss"
- name = "penetrator round"
- damage = 60
- forcedodge = TRUE
- dismemberment = 0 //It goes through you cleanly.
- knockdown = 0
- breakthings = FALSE
-
-// 1.95x129mm (SAW)
-
-/obj/item/projectile/bullet/mm195x129
- name = "1.95x129mm bullet"
- damage = 45
- armour_penetration = 5
-
-/obj/item/projectile/bullet/mm195x129_ap
- name = "1.95x129mm armor-piercing bullet"
- damage = 40
- armour_penetration = 75
-
-/obj/item/projectile/bullet/mm195x129_hp
- name = "1.95x129mm hollow-point bullet"
- damage = 60
- armour_penetration = -60
-
-/obj/item/projectile/bullet/incendiary/mm195x129
- name = "1.95x129mm incendiary bullet"
- damage = 15
- fire_stacks = 3
-
-// Shotgun
-
-/obj/item/projectile/bullet/shotgun_slug
- name = "12g shotgun slug"
- damage = 60
-
-/obj/item/projectile/bullet/shotgun_beanbag
- name = "beanbag slug"
- damage = 5
- stamina = 80
-
-/obj/item/projectile/bullet/incendiary/shotgun
- name = "incendiary slug"
- damage = 20
-
-/obj/item/projectile/bullet/incendiary/shotgun/dragonsbreath
- name = "dragonsbreath pellet"
- damage = 5
-
-/obj/item/projectile/bullet/shotgun_stunslug
- name = "stunslug"
- damage = 5
- knockdown = 100
- stutter = 5
- jitter = 20
- range = 7
- icon_state = "spark"
- color = "#FFFF00"
-
-/obj/item/projectile/bullet/shotgun_meteorslug
- name = "meteorslug"
- icon = 'icons/obj/meteor.dmi'
- icon_state = "dust"
- damage = 20
- knockdown = 80
- hitsound = 'sound/effects/meteorimpact.ogg'
-
-/obj/item/projectile/bullet/shotgun_meteorslug/on_hit(atom/target, blocked = FALSE)
- . = ..()
- if(ismovableatom(target))
- var/atom/movable/M = target
- var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src)))
- M.throw_at(throw_target, 3, 2)
-
-/obj/item/projectile/bullet/shotgun_meteorslug/Initialize()
- . = ..()
- SpinAnimation()
-
-/obj/item/projectile/bullet/shotgun_frag12
- name ="frag12 slug"
- damage = 25
- knockdown = 50
-
-/obj/item/projectile/bullet/shotgun_frag12/on_hit(atom/target, blocked = FALSE)
- ..()
- explosion(target, -1, 0, 1)
- return TRUE
-
-/obj/item/projectile/bullet/pellet
- var/tile_dropoff = 0.75
- var/tile_dropoff_s = 1.25
-
-/obj/item/projectile/bullet/pellet/shotgun_buckshot
- name = "buckshot pellet"
- damage = 12.5
-
-/obj/item/projectile/bullet/pellet/shotgun_rubbershot
- name = "rubbershot pellet"
- damage = 3
- stamina = 25
-
-/obj/item/projectile/bullet/pellet/Range()
- ..()
- if(damage > 0)
- damage -= tile_dropoff
- if(stamina > 0)
- stamina -= tile_dropoff_s
- if(damage < 0 && stamina < 0)
- qdel(src)
-
-/obj/item/projectile/bullet/pellet/shotgun_improvised
- tile_dropoff = 0.55 //Come on it does 6 damage don't be like that.
- damage = 6
-
-/obj/item/projectile/bullet/pellet/shotgun_improvised/Initialize()
- . = ..()
- range = rand(1, 8)
-
-/obj/item/projectile/bullet/pellet/shotgun_improvised/on_range()
- do_sparks(1, TRUE, src)
- ..()
-
-// Scattershot
-
-/obj/item/projectile/bullet/scattershot
- damage = 20
- stamina = 65
-
-// LMD (exosuits)
-
-/obj/item/projectile/bullet/lmg
- damage = 20
-
-// Turrets
-
-/obj/item/projectile/bullet/manned_turret
- damage = 20
-
-/obj/item/projectile/bullet/syndicate_turret
- damage = 20
-
-// FNX-99 (Mechs)
-
-/obj/item/projectile/bullet/incendiary/fnx99
- damage = 20
-
-// C3D (Borgs)
-
-/obj/item/projectile/bullet/c3d
- damage = 20
-
-// Honker
-
-/obj/item/projectile/bullet/honker
- damage = 0
- knockdown = 60
- forcedodge = TRUE
- nodamage = TRUE
- hitsound = 'sound/items/bikehorn.ogg'
- icon = 'icons/obj/hydroponics/harvest.dmi'
- icon_state = "banana"
- range = 200
-
-/obj/item/projectile/bullet/honker/Initialize()
- . = ..()
- SpinAnimation()
-
-// Mime
-
-/obj/item/projectile/bullet/mime
- damage = 20
-
-/obj/item/projectile/bullet/mime/on_hit(atom/target, blocked = FALSE)
- . = ..()
- if(iscarbon(target))
- var/mob/living/carbon/M = target
- M.silent = max(M.silent, 10)
-
-// Darts
-
-/obj/item/projectile/bullet/dart
- name = "dart"
- icon_state = "cbbolt"
- damage = 6
- var/piercing = FALSE
-
-/obj/item/projectile/bullet/dart/Initialize()
- . = ..()
- create_reagents(50)
- reagents.set_reacting(FALSE)
-
-/obj/item/projectile/bullet/dart/on_hit(atom/target, blocked = FALSE)
- if(iscarbon(target))
- var/mob/living/carbon/M = target
- if(blocked != 100) // not completely blocked
- if(M.can_inject(null, FALSE, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
- ..()
- reagents.reaction(M, INJECT)
- reagents.trans_to(M, reagents.total_volume)
- return TRUE
- else
- blocked = 100
- target.visible_message("\The [src] was deflected!", \
- "You were protected against \the [src]!")
-
- ..(target, blocked)
- reagents.set_reacting(TRUE)
- reagents.handle_reactions()
- return TRUE
-
-/obj/item/projectile/bullet/dart/metalfoam/Initialize()
- . = ..()
- reagents.add_reagent("aluminium", 15)
- reagents.add_reagent("foaming_agent", 5)
- reagents.add_reagent("facid", 5)
-
-//This one is for future syringe guns update
-/obj/item/projectile/bullet/dart/syringe
- name = "syringe"
- icon_state = "syringeproj"
-
-// DNA injector
-
-/obj/item/projectile/bullet/dnainjector
- name = "\improper DNA injector"
- icon_state = "syringeproj"
- var/obj/item/dnainjector/injector
- damage = 5
- hitsound_wall = "shatter"
-
-/obj/item/projectile/bullet/dnainjector/on_hit(atom/target, blocked = FALSE)
- if(iscarbon(target))
- var/mob/living/carbon/M = target
- if(blocked != 100)
- if(M.can_inject(null, FALSE, def_zone, FALSE))
- if(injector.inject(M, firer))
- QDEL_NULL(injector)
- return TRUE
- else
- blocked = 100
- target.visible_message("\The [src] was deflected!", \
- "You were protected against \the [src]!")
- return ..()
-
-/obj/item/projectile/bullet/dnainjector/Destroy()
- QDEL_NULL(injector)
- return ..()
-
diff --git a/code/modules/projectiles/projectile/bullets/_incendiary.dm b/code/modules/projectiles/projectile/bullets/_incendiary.dm
new file mode 100644
index 0000000000..d0cf74421c
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/_incendiary.dm
@@ -0,0 +1,17 @@
+/obj/item/projectile/bullet/incendiary
+ damage = 20
+ var/fire_stacks = 4
+
+/obj/item/projectile/bullet/incendiary/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(iscarbon(target))
+ var/mob/living/carbon/M = target
+ M.adjust_fire_stacks(fire_stacks)
+ M.IgniteMob()
+
+/obj/item/projectile/bullet/incendiary/Move()
+ . = ..()
+ var/turf/location = get_turf(src)
+ if(location)
+ new /obj/effect/hotspot(location)
+ location.hotspot_expose(700, 50, 1)
diff --git a/code/modules/projectiles/projectile/bullets/dart_syringe.dm b/code/modules/projectiles/projectile/bullets/dart_syringe.dm
new file mode 100644
index 0000000000..023c3b9090
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/dart_syringe.dm
@@ -0,0 +1,39 @@
+/obj/item/projectile/bullet/dart
+ name = "dart"
+ icon_state = "cbbolt"
+ damage = 6
+ var/piercing = FALSE
+
+/obj/item/projectile/bullet/dart/Initialize()
+ . = ..()
+ create_reagents(50)
+ reagents.set_reacting(FALSE)
+
+/obj/item/projectile/bullet/dart/on_hit(atom/target, blocked = FALSE)
+ if(iscarbon(target))
+ var/mob/living/carbon/M = target
+ if(blocked != 100) // not completely blocked
+ if(M.can_inject(null, FALSE, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body.
+ ..()
+ reagents.reaction(M, INJECT)
+ reagents.trans_to(M, reagents.total_volume)
+ return TRUE
+ else
+ blocked = 100
+ target.visible_message("\The [src] was deflected!", \
+ "You were protected against \the [src]!")
+
+ ..(target, blocked)
+ reagents.set_reacting(TRUE)
+ reagents.handle_reactions()
+ return TRUE
+
+/obj/item/projectile/bullet/dart/metalfoam/Initialize()
+ . = ..()
+ reagents.add_reagent("aluminium", 15)
+ reagents.add_reagent("foaming_agent", 5)
+ reagents.add_reagent("facid", 5)
+
+/obj/item/projectile/bullet/dart/syringe
+ name = "syringe"
+ icon_state = "syringeproj"
diff --git a/code/modules/projectiles/projectile/bullets/dnainjector.dm b/code/modules/projectiles/projectile/bullets/dnainjector.dm
new file mode 100644
index 0000000000..861ead5393
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/dnainjector.dm
@@ -0,0 +1,24 @@
+/obj/item/projectile/bullet/dnainjector
+ name = "\improper DNA injector"
+ icon_state = "syringeproj"
+ var/obj/item/dnainjector/injector
+ damage = 5
+ hitsound_wall = "shatter"
+
+/obj/item/projectile/bullet/dnainjector/on_hit(atom/target, blocked = FALSE)
+ if(iscarbon(target))
+ var/mob/living/carbon/M = target
+ if(blocked != 100)
+ if(M.can_inject(null, FALSE, def_zone, FALSE))
+ if(injector.inject(M, firer))
+ QDEL_NULL(injector)
+ return TRUE
+ else
+ blocked = 100
+ target.visible_message("\The [src] was deflected!", \
+ "You were protected against \the [src]!")
+ return ..()
+
+/obj/item/projectile/bullet/dnainjector/Destroy()
+ QDEL_NULL(injector)
+ return ..()
diff --git a/code/modules/projectiles/projectile/bullets/grenade.dm b/code/modules/projectiles/projectile/bullets/grenade.dm
new file mode 100644
index 0000000000..965001b55f
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/grenade.dm
@@ -0,0 +1,12 @@
+// 40mm (Grenade Launcher
+
+/obj/item/projectile/bullet/a40mm
+ name ="40mm grenade"
+ desc = "USE A WEEL GUN"
+ icon_state= "bolter"
+ damage = 60
+
+/obj/item/projectile/bullet/a40mm/on_hit(atom/target, blocked = FALSE)
+ ..()
+ explosion(target, -1, 0, 2, 1, 0, flame_range = 3)
+ return TRUE
diff --git a/code/modules/projectiles/projectile/bullets/lmg.dm b/code/modules/projectiles/projectile/bullets/lmg.dm
new file mode 100644
index 0000000000..03e64976d9
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/lmg.dm
@@ -0,0 +1,44 @@
+// C3D (Borgs)
+
+/obj/item/projectile/bullet/c3d
+ damage = 20
+
+// Mech LMG
+
+/obj/item/projectile/bullet/lmg
+ damage = 20
+
+// Mech FNX-99
+
+/obj/item/projectile/bullet/incendiary/fnx99
+ damage = 20
+
+// Turrets
+
+/obj/item/projectile/bullet/manned_turret
+ damage = 20
+
+/obj/item/projectile/bullet/syndicate_turret
+ damage = 20
+
+// 1.95x129mm (SAW)
+
+/obj/item/projectile/bullet/mm195x129
+ name = "1.95x129mm bullet"
+ damage = 45
+ armour_penetration = 5
+
+/obj/item/projectile/bullet/mm195x129_ap
+ name = "1.95x129mm armor-piercing bullet"
+ damage = 40
+ armour_penetration = 75
+
+/obj/item/projectile/bullet/mm195x129_hp
+ name = "1.95x129mm hollow-point bullet"
+ damage = 60
+ armour_penetration = -60
+
+/obj/item/projectile/bullet/incendiary/mm195x129
+ name = "1.95x129mm incendiary bullet"
+ damage = 15
+ fire_stacks = 3
diff --git a/code/modules/projectiles/projectile/bullets/pistol.dm b/code/modules/projectiles/projectile/bullets/pistol.dm
new file mode 100644
index 0000000000..ac14fa563c
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/pistol.dm
@@ -0,0 +1,36 @@
+// 9mm (Stechkin APS)
+
+/obj/item/projectile/bullet/c9mm
+ name = "9mm bullet"
+ damage = 20
+
+/obj/item/projectile/bullet/c9mm_ap
+ name = "9mm armor-piercing bullet"
+ damage = 15
+ armour_penetration = 40
+
+/obj/item/projectile/bullet/incendiary/c9mm
+ name = "9mm incendiary bullet"
+ damage = 10
+ fire_stacks = 1
+
+// 10mm (Stechkin)
+
+/obj/item/projectile/bullet/c10mm
+ name = "10mm bullet"
+ damage = 30
+
+/obj/item/projectile/bullet/c10mm_ap
+ name = "10mm armor-piercing bullet"
+ damage = 27
+ armour_penetration = 40
+
+/obj/item/projectile/bullet/c10mm_hp
+ name = "10mm hollow-point bullet"
+ damage = 40
+ armour_penetration = -50
+
+/obj/item/projectile/bullet/incendiary/c10mm
+ name = "10mm incendiary bullet"
+ damage = 15
+ fire_stacks = 2
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
new file mode 100644
index 0000000000..fc4ed0fa50
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -0,0 +1,25 @@
+// 7.62x38mmR (Nagant Revolver)
+
+/obj/item/projectile/bullet/n762
+ name = "7.62x38mmR bullet"
+ damage = 60
+
+// .50AE (Desert Eagle)
+
+/obj/item/projectile/bullet/a50AE
+ name = ".50AE bullet"
+ damage = 60
+
+// .38 (Detective's Gun)
+
+/obj/item/projectile/bullet/c38
+ name = ".38 bullet"
+ damage = 15
+ knockdown = 60
+ stamina = 50
+
+// .357 (Syndie Revolver)
+
+/obj/item/projectile/bullet/a357
+ name = ".357 bullet"
+ damage = 60
diff --git a/code/modules/projectiles/projectile/bullets/rifle.dm b/code/modules/projectiles/projectile/bullets/rifle.dm
new file mode 100644
index 0000000000..a019c05ef1
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/rifle.dm
@@ -0,0 +1,16 @@
+// 5.56mm (M-90gl Carbine)
+
+/obj/item/projectile/bullet/a556
+ name = "5.56mm bullet"
+ damage = 35
+
+// 7.62 (Nagant Rifle)
+
+/obj/item/projectile/bullet/a762
+ name = "7.62 bullet"
+ damage = 60
+
+/obj/item/projectile/bullet/a762_enchanted
+ name = "enchanted 7.62 bullet"
+ damage = 5
+ stamina = 80
diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm
new file mode 100644
index 0000000000..ecbe2e96e4
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/shotgun.dm
@@ -0,0 +1,95 @@
+/obj/item/projectile/bullet/shotgun_slug
+ name = "12g shotgun slug"
+ damage = 60
+
+/obj/item/projectile/bullet/shotgun_beanbag
+ name = "beanbag slug"
+ damage = 5
+ stamina = 80
+
+/obj/item/projectile/bullet/incendiary/shotgun
+ name = "incendiary slug"
+ damage = 20
+
+/obj/item/projectile/bullet/incendiary/shotgun/dragonsbreath
+ name = "dragonsbreath pellet"
+ damage = 5
+
+/obj/item/projectile/bullet/shotgun_stunslug
+ name = "stunslug"
+ damage = 5
+ knockdown = 100
+ stutter = 5
+ jitter = 20
+ range = 7
+ icon_state = "spark"
+ color = "#FFFF00"
+
+/obj/item/projectile/bullet/shotgun_meteorslug
+ name = "meteorslug"
+ icon = 'icons/obj/meteor.dmi'
+ icon_state = "dust"
+ damage = 20
+ knockdown = 80
+ hitsound = 'sound/effects/meteorimpact.ogg'
+
+/obj/item/projectile/bullet/shotgun_meteorslug/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(ismovableatom(target))
+ var/atom/movable/M = target
+ var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src)))
+ M.throw_at(throw_target, 3, 2)
+
+/obj/item/projectile/bullet/shotgun_meteorslug/Initialize()
+ . = ..()
+ SpinAnimation()
+
+/obj/item/projectile/bullet/shotgun_frag12
+ name ="frag12 slug"
+ damage = 25
+ knockdown = 50
+
+/obj/item/projectile/bullet/shotgun_frag12/on_hit(atom/target, blocked = FALSE)
+ ..()
+ explosion(target, -1, 0, 1)
+ return TRUE
+
+/obj/item/projectile/bullet/pellet
+ var/tile_dropoff = 0.75
+ var/tile_dropoff_s = 1.25
+
+/obj/item/projectile/bullet/pellet/shotgun_buckshot
+ name = "buckshot pellet"
+ damage = 12.5
+
+/obj/item/projectile/bullet/pellet/shotgun_rubbershot
+ name = "rubbershot pellet"
+ damage = 3
+ stamina = 25
+
+/obj/item/projectile/bullet/pellet/Range()
+ ..()
+ if(damage > 0)
+ damage -= tile_dropoff
+ if(stamina > 0)
+ stamina -= tile_dropoff_s
+ if(damage < 0 && stamina < 0)
+ qdel(src)
+
+/obj/item/projectile/bullet/pellet/shotgun_improvised
+ tile_dropoff = 0.55 //Come on it does 6 damage don't be like that.
+ damage = 6
+
+/obj/item/projectile/bullet/pellet/shotgun_improvised/Initialize()
+ . = ..()
+ range = rand(1, 8)
+
+/obj/item/projectile/bullet/pellet/shotgun_improvised/on_range()
+ do_sparks(1, TRUE, src)
+ ..()
+
+// Mech Scattershot
+
+/obj/item/projectile/bullet/scattershot
+ damage = 20
+ stamina = 65
diff --git a/code/modules/projectiles/projectile/bullets/smg.dm b/code/modules/projectiles/projectile/bullets/smg.dm
new file mode 100644
index 0000000000..50532a5977
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/smg.dm
@@ -0,0 +1,26 @@
+// .45 (M1911 & C20r)
+
+/obj/item/projectile/bullet/c45
+ name = ".45 bullet"
+ damage = 20
+ stamina = 65
+
+/obj/item/projectile/bullet/c45_nostamina
+ name = ".45 bullet"
+ damage = 30
+
+// 4.6x30mm (Autorifles)
+
+/obj/item/projectile/bullet/c46x30mm
+ name = "4.6x30mm bullet"
+ damage = 20
+
+/obj/item/projectile/bullet/c46x30mm_ap
+ name = "4.6x30mm armor-piercing bullet"
+ damage = 15
+ armour_penetration = 40
+
+/obj/item/projectile/bullet/incendiary/c46x30mm
+ name = "4.6x30mm incendiary bullet"
+ damage = 10
+ fire_stacks = 1
diff --git a/code/modules/projectiles/projectile/bullets/sniper.dm b/code/modules/projectiles/projectile/bullets/sniper.dm
new file mode 100644
index 0000000000..d29cb70440
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/sniper.dm
@@ -0,0 +1,39 @@
+// .50 (Sniper)
+
+/obj/item/projectile/bullet/p50
+ name =".50 bullet"
+ speed = 0.4
+ damage = 70
+ knockdown = 100
+ dismemberment = 50
+ armour_penetration = 50
+ var/breakthings = TRUE
+
+/obj/item/projectile/bullet/p50/on_hit(atom/target, blocked = 0)
+ if((blocked != 100) && (!ismob(target) && breakthings))
+ target.ex_act(rand(1,2))
+ return ..()
+
+/obj/item/projectile/bullet/p50/soporific
+ name =".50 soporific bullet"
+ armour_penetration = 0
+ nodamage = TRUE
+ dismemberment = 0
+ knockdown = 0
+ breakthings = FALSE
+
+/obj/item/projectile/bullet/p50/soporific/on_hit(atom/target, blocked = FALSE)
+ if((blocked != 100) && isliving(target))
+ var/mob/living/L = target
+ L.Sleeping(400)
+ return ..()
+
+/obj/item/projectile/bullet/p50/penetrator
+ name =".50 penetrator bullet"
+ icon_state = "gauss"
+ name = "penetrator round"
+ damage = 60
+ forcedodge = TRUE
+ dismemberment = 0 //It goes through you cleanly.
+ knockdown = 0
+ breakthings = FALSE
diff --git a/code/modules/projectiles/projectile/bullets/special.dm b/code/modules/projectiles/projectile/bullets/special.dm
new file mode 100644
index 0000000000..091dff454c
--- /dev/null
+++ b/code/modules/projectiles/projectile/bullets/special.dm
@@ -0,0 +1,26 @@
+// Honker
+
+/obj/item/projectile/bullet/honker
+ damage = 0
+ knockdown = 60
+ forcedodge = TRUE
+ nodamage = TRUE
+ hitsound = 'sound/items/bikehorn.ogg'
+ icon = 'icons/obj/hydroponics/harvest.dmi'
+ icon_state = "banana"
+ range = 200
+
+/obj/item/projectile/bullet/honker/Initialize()
+ . = ..()
+ SpinAnimation()
+
+// Mime
+
+/obj/item/projectile/bullet/mime
+ damage = 20
+
+/obj/item/projectile/bullet/mime/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(iscarbon(target))
+ var/mob/living/carbon/M = target
+ M.silent = max(M.silent, 10)
diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm
deleted file mode 100644
index 2d7f8f5cce..0000000000
--- a/code/modules/projectiles/projectile/energy.dm
+++ /dev/null
@@ -1,203 +0,0 @@
-/obj/item/projectile/energy
- name = "energy"
- icon_state = "spark"
- damage = 0
- damage_type = BURN
- flag = "energy"
- is_reflectable = TRUE
-
-/obj/item/projectile/energy/chameleon
- nodamage = TRUE
-
-/obj/item/projectile/energy/electrode
- name = "electrode"
- icon_state = "spark"
- color = "#FFFF00"
- nodamage = 1
- knockdown = 100
- stutter = 5
- jitter = 20
- hitsound = 'sound/weapons/taserhit.ogg'
- range = 7
- tracer_type = /obj/effect/projectile/tracer/stun
- muzzle_type = /obj/effect/projectile/muzzle/stun
- impact_type = /obj/effect/projectile/impact/stun
-
-/obj/item/projectile/energy/electrode/on_hit(atom/target, blocked = FALSE)
- . = ..()
- if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks!
- do_sparks(1, TRUE, src)
- else if(iscarbon(target))
- var/mob/living/carbon/C = target
- if(C.dna && C.dna.check_mutation(HULK))
- C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
- else if((C.status_flags & CANKNOCKDOWN) && !C.has_trait(TRAIT_STUNIMMUNE))
- addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5)
-
-/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet
- do_sparks(1, TRUE, src)
- ..()
-
-/obj/item/projectile/energy/net
- name = "energy netting"
- icon_state = "e_netting"
- damage = 10
- damage_type = STAMINA
- hitsound = 'sound/weapons/taserhit.ogg'
- range = 10
-
-/obj/item/projectile/energy/net/Initialize()
- . = ..()
- SpinAnimation()
-
-/obj/item/projectile/energy/net/on_hit(atom/target, blocked = FALSE)
- if(isliving(target))
- var/turf/Tloc = get_turf(target)
- if(!locate(/obj/effect/nettingportal) in Tloc)
- new /obj/effect/nettingportal(Tloc)
- ..()
-
-/obj/item/projectile/energy/net/on_range()
- do_sparks(1, TRUE, src)
- ..()
-
-/obj/effect/nettingportal
- name = "DRAGnet teleportation field"
- desc = "A field of bluespace energy, locking on to teleport a target."
- icon = 'icons/effects/effects.dmi'
- icon_state = "dragnetfield"
- light_range = 3
- anchored = TRUE
-
-/obj/effect/nettingportal/Initialize()
- . = ..()
- var/obj/item/device/radio/beacon/teletarget = null
- for(var/obj/machinery/computer/teleporter/com in GLOB.machines)
- if(com.target)
- if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged)
- teletarget = com.target
-
- addtimer(CALLBACK(src, .proc/pop, teletarget), 30)
-
-/obj/effect/nettingportal/proc/pop(teletarget)
- if(teletarget)
- for(var/mob/living/L in get_turf(src))
- do_teleport(L, teletarget, 2)//teleport what's in the tile to the beacon
- else
- for(var/mob/living/L in get_turf(src))
- do_teleport(L, L, 15) //Otherwise it just warps you off somewhere.
-
- qdel(src)
-
-/obj/effect/nettingportal/singularity_act()
- return
-
-/obj/effect/nettingportal/singularity_pull()
- return
-
-
-/obj/item/projectile/energy/trap
- name = "energy snare"
- icon_state = "e_snare"
- nodamage = 1
- knockdown = 20
- hitsound = 'sound/weapons/taserhit.ogg'
- range = 4
-
-/obj/item/projectile/energy/trap/on_hit(atom/target, blocked = FALSE)
- if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - drop a trap
- new/obj/item/restraints/legcuffs/beartrap/energy(get_turf(loc))
- else if(iscarbon(target))
- var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy(get_turf(target))
- B.Crossed(target)
- ..()
-
-/obj/item/projectile/energy/trap/on_range()
- new /obj/item/restraints/legcuffs/beartrap/energy(loc)
- ..()
-
-/obj/item/projectile/energy/trap/cyborg
- name = "Energy Bola"
- icon_state = "e_snare"
- nodamage = 1
- knockdown = 0
- hitsound = 'sound/weapons/taserhit.ogg'
- range = 10
-
-/obj/item/projectile/energy/trap/cyborg/on_hit(atom/target, blocked = FALSE)
- if(!ismob(target) || blocked >= 100)
- do_sparks(1, TRUE, src)
- qdel(src)
- if(iscarbon(target))
- var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy/cyborg(get_turf(target))
- B.Crossed(target)
- QDEL_IN(src, 10)
- ..()
-
-/obj/item/projectile/energy/trap/cyborg/on_range()
- do_sparks(1, TRUE, src)
- qdel(src)
-
-/obj/item/projectile/energy/declone
- name = "radiation beam"
- icon_state = "declone"
- damage = 20
- damage_type = CLONE
- irradiate = 10
- impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
-
-/obj/item/projectile/energy/dart //ninja throwing dart
- name = "dart"
- icon_state = "toxin"
- damage = 5
- damage_type = TOX
- knockdown = 100
- range = 7
-
-/obj/item/projectile/energy/bolt //ebow bolts
- name = "bolt"
- icon_state = "cbbolt"
- damage = 8
- damage_type = TOX
- nodamage = 0
- knockdown = 100
- stutter = 5
-
-/obj/item/projectile/energy/bolt/halloween
- name = "candy corn"
- icon_state = "candy_corn"
-
-/obj/item/projectile/energy/bolt/large
- damage = 20
-
-/obj/item/projectile/energy/tesla
- name = "tesla bolt"
- icon_state = "tesla_projectile"
- impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
- var/chain
-
-/obj/item/projectile/energy/tesla/fire(setAngle)
- if(firer)
- chain = firer.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
- ..()
-
-/obj/item/projectile/energy/tesla/Destroy()
- qdel(chain)
- return ..()
-
-/obj/item/projectile/energy/tesla/revolver
- name = "energy orb"
-
-/obj/item/projectile/energy/tesla/revolver/on_hit(atom/target)
- . = ..()
- if(isliving(target))
- tesla_zap(target, 3, 10000)
- qdel(src)
-
-/obj/item/projectile/energy/tesla/cannon
- name = "tesla orb"
-
-/obj/item/projectile/energy/tesla/cannon/on_hit(atom/target)
- . = ..()
- tesla_zap(target, 3, 10000, explosive = FALSE, stun_mobs = FALSE)
- qdel(src)
diff --git a/code/modules/projectiles/projectile/energy/_energy.dm b/code/modules/projectiles/projectile/energy/_energy.dm
new file mode 100644
index 0000000000..3df1eb74d3
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/_energy.dm
@@ -0,0 +1,8 @@
+/obj/item/projectile/energy
+ name = "energy"
+ icon_state = "spark"
+ damage = 0
+ damage_type = BURN
+ flag = "energy"
+ is_reflectable = TRUE
+
diff --git a/code/modules/projectiles/projectile/energy/chameleon.dm b/code/modules/projectiles/projectile/energy/chameleon.dm
new file mode 100644
index 0000000000..8ed6283c51
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/chameleon.dm
@@ -0,0 +1,3 @@
+/obj/item/projectile/energy/chameleon
+ nodamage = TRUE
+
diff --git a/code/modules/projectiles/projectile/energy/ebow.dm b/code/modules/projectiles/projectile/energy/ebow.dm
new file mode 100644
index 0000000000..3e65bbfad2
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/ebow.dm
@@ -0,0 +1,15 @@
+/obj/item/projectile/energy/bolt //ebow bolts
+ name = "bolt"
+ icon_state = "cbbolt"
+ damage = 8
+ damage_type = TOX
+ nodamage = 0
+ knockdown = 100
+ stutter = 5
+
+/obj/item/projectile/energy/bolt/halloween
+ name = "candy corn"
+ icon_state = "candy_corn"
+
+/obj/item/projectile/energy/bolt/large
+ damage = 20
diff --git a/code/modules/projectiles/projectile/energy/misc.dm b/code/modules/projectiles/projectile/energy/misc.dm
new file mode 100644
index 0000000000..21c3138add
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/misc.dm
@@ -0,0 +1,15 @@
+/obj/item/projectile/energy/declone
+ name = "radiation beam"
+ icon_state = "declone"
+ damage = 20
+ damage_type = CLONE
+ irradiate = 10
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
+
+/obj/item/projectile/energy/dart //ninja throwing dart
+ name = "dart"
+ icon_state = "toxin"
+ damage = 5
+ damage_type = TOX
+ knockdown = 100
+ range = 7
diff --git a/code/modules/projectiles/projectile/energy/net_snare.dm b/code/modules/projectiles/projectile/energy/net_snare.dm
new file mode 100644
index 0000000000..01e87d26cd
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/net_snare.dm
@@ -0,0 +1,98 @@
+/obj/item/projectile/energy/net
+ name = "energy netting"
+ icon_state = "e_netting"
+ damage = 10
+ damage_type = STAMINA
+ hitsound = 'sound/weapons/taserhit.ogg'
+ range = 10
+
+/obj/item/projectile/energy/net/Initialize()
+ . = ..()
+ SpinAnimation()
+
+/obj/item/projectile/energy/net/on_hit(atom/target, blocked = FALSE)
+ if(isliving(target))
+ var/turf/Tloc = get_turf(target)
+ if(!locate(/obj/effect/nettingportal) in Tloc)
+ new /obj/effect/nettingportal(Tloc)
+ ..()
+
+/obj/item/projectile/energy/net/on_range()
+ do_sparks(1, TRUE, src)
+ ..()
+
+/obj/effect/nettingportal
+ name = "DRAGnet teleportation field"
+ desc = "A field of bluespace energy, locking on to teleport a target."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "dragnetfield"
+ light_range = 3
+ anchored = TRUE
+
+/obj/effect/nettingportal/Initialize()
+ . = ..()
+ var/obj/item/device/radio/beacon/teletarget = null
+ for(var/obj/machinery/computer/teleporter/com in GLOB.machines)
+ if(com.target)
+ if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged)
+ teletarget = com.target
+
+ addtimer(CALLBACK(src, .proc/pop, teletarget), 30)
+
+/obj/effect/nettingportal/proc/pop(teletarget)
+ if(teletarget)
+ for(var/mob/living/L in get_turf(src))
+ do_teleport(L, teletarget, 2)//teleport what's in the tile to the beacon
+ else
+ for(var/mob/living/L in get_turf(src))
+ do_teleport(L, L, 15) //Otherwise it just warps you off somewhere.
+
+ qdel(src)
+
+/obj/effect/nettingportal/singularity_act()
+ return
+
+/obj/effect/nettingportal/singularity_pull()
+ return
+
+/obj/item/projectile/energy/trap
+ name = "energy snare"
+ icon_state = "e_snare"
+ nodamage = 1
+ knockdown = 20
+ hitsound = 'sound/weapons/taserhit.ogg'
+ range = 4
+
+/obj/item/projectile/energy/trap/on_hit(atom/target, blocked = FALSE)
+ if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - drop a trap
+ new/obj/item/restraints/legcuffs/beartrap/energy(get_turf(loc))
+ else if(iscarbon(target))
+ var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy(get_turf(target))
+ B.Crossed(target)
+ ..()
+
+/obj/item/projectile/energy/trap/on_range()
+ new /obj/item/restraints/legcuffs/beartrap/energy(loc)
+ ..()
+
+/obj/item/projectile/energy/trap/cyborg
+ name = "Energy Bola"
+ icon_state = "e_snare"
+ nodamage = 1
+ knockdown = 0
+ hitsound = 'sound/weapons/taserhit.ogg'
+ range = 10
+
+/obj/item/projectile/energy/trap/cyborg/on_hit(atom/target, blocked = FALSE)
+ if(!ismob(target) || blocked >= 100)
+ do_sparks(1, TRUE, src)
+ qdel(src)
+ if(iscarbon(target))
+ var/obj/item/restraints/legcuffs/beartrap/B = new /obj/item/restraints/legcuffs/beartrap/energy/cyborg(get_turf(target))
+ B.Crossed(target)
+ QDEL_IN(src, 10)
+ ..()
+
+/obj/item/projectile/energy/trap/cyborg/on_range()
+ do_sparks(1, TRUE, src)
+ qdel(src)
diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm
new file mode 100644
index 0000000000..598b64d025
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/stun.dm
@@ -0,0 +1,28 @@
+/obj/item/projectile/energy/electrode
+ name = "electrode"
+ icon_state = "spark"
+ color = "#FFFF00"
+ nodamage = 1
+ knockdown = 100
+ stutter = 5
+ jitter = 20
+ hitsound = 'sound/weapons/taserhit.ogg'
+ range = 7
+ tracer_type = /obj/effect/projectile/tracer/stun
+ muzzle_type = /obj/effect/projectile/muzzle/stun
+ impact_type = /obj/effect/projectile/impact/stun
+
+/obj/item/projectile/energy/electrode/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks!
+ do_sparks(1, TRUE, src)
+ else if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ if(C.dna && C.dna.check_mutation(HULK))
+ C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+ else if((C.status_flags & CANKNOCKDOWN) && !C.has_trait(TRAIT_STUNIMMUNE))
+ addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5)
+
+/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet
+ do_sparks(1, TRUE, src)
+ ..()
diff --git a/code/modules/projectiles/projectile/energy/tesla.dm b/code/modules/projectiles/projectile/energy/tesla.dm
new file mode 100644
index 0000000000..6eebd6afbf
--- /dev/null
+++ b/code/modules/projectiles/projectile/energy/tesla.dm
@@ -0,0 +1,31 @@
+/obj/item/projectile/energy/tesla
+ name = "tesla bolt"
+ icon_state = "tesla_projectile"
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
+ var/chain
+
+/obj/item/projectile/energy/tesla/fire(setAngle)
+ if(firer)
+ chain = firer.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
+ ..()
+
+/obj/item/projectile/energy/tesla/Destroy()
+ qdel(chain)
+ return ..()
+
+/obj/item/projectile/energy/tesla/revolver
+ name = "energy orb"
+
+/obj/item/projectile/energy/tesla/revolver/on_hit(atom/target)
+ . = ..()
+ if(isliving(target))
+ tesla_zap(target, 3, 10000)
+ qdel(src)
+
+/obj/item/projectile/energy/tesla/cannon
+ name = "tesla orb"
+
+/obj/item/projectile/energy/tesla/cannon/on_hit(atom/target)
+ . = ..()
+ tesla_zap(target, 3, 10000, explosive = FALSE, stun_mobs = FALSE)
+ qdel(src)
diff --git a/code/modules/projectiles/projectile/reusable/_reusable.dm b/code/modules/projectiles/projectile/reusable/_reusable.dm
new file mode 100644
index 0000000000..33c9678fe4
--- /dev/null
+++ b/code/modules/projectiles/projectile/reusable/_reusable.dm
@@ -0,0 +1,20 @@
+/obj/item/projectile/bullet/reusable
+ name = "reusable bullet"
+ desc = "How do you even reuse a bullet?"
+ var/ammo_type = /obj/item/ammo_casing/caseless
+ var/dropped = FALSE
+ impact_effect_type = null
+
+/obj/item/projectile/bullet/reusable/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ handle_drop()
+
+/obj/item/projectile/bullet/reusable/on_range()
+ handle_drop()
+ ..()
+
+/obj/item/projectile/bullet/reusable/proc/handle_drop()
+ if(!dropped)
+ var/turf/T = get_turf(src)
+ new ammo_type(T)
+ dropped = TRUE
diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/reusable/foam_dart.dm
similarity index 59%
rename from code/modules/projectiles/projectile/reusable.dm
rename to code/modules/projectiles/projectile/reusable/foam_dart.dm
index ccd4b7589c..c7f99c75aa 100644
--- a/code/modules/projectiles/projectile/reusable.dm
+++ b/code/modules/projectiles/projectile/reusable/foam_dart.dm
@@ -1,69 +1,41 @@
-/obj/item/projectile/bullet/reusable
- name = "reusable bullet"
- desc = "How do you even reuse a bullet?"
- var/ammo_type = /obj/item/ammo_casing/caseless
- var/dropped = 0
- impact_effect_type = null
-
-/obj/item/projectile/bullet/reusable/on_hit(atom/target, blocked = FALSE)
- . = ..()
- handle_drop()
-
-/obj/item/projectile/bullet/reusable/on_range()
- handle_drop()
- ..()
-
-/obj/item/projectile/bullet/reusable/proc/handle_drop()
- if(!dropped)
- var/turf/T = get_turf(src)
- new ammo_type(T)
- dropped = 1
-
-/obj/item/projectile/bullet/reusable/magspear
- name = "magnetic spear"
- desc = "WHITE WHALE, HOLY GRAIL"
- damage = 30 //takes 3 spears to kill a mega carp, one to kill a normal carp
- icon_state = "magspear"
- ammo_type = /obj/item/ammo_casing/caseless/magspear
-
-/obj/item/projectile/bullet/reusable/foam_dart
- name = "foam dart"
- desc = "I hope you're wearing eye protection."
- damage = 0 // It's a damn toy.
- damage_type = OXY
- nodamage = 1
- icon = 'icons/obj/guns/toy.dmi'
- icon_state = "foamdart_proj"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart
- range = 10
- var/modified = 0
- var/obj/item/pen/pen = null
-
-/obj/item/projectile/bullet/reusable/foam_dart/handle_drop()
- if(dropped)
- return
- var/turf/T = get_turf(src)
- dropped = 1
- var/obj/item/ammo_casing/caseless/foam_dart/newcasing = new ammo_type(T)
- newcasing.modified = modified
- var/obj/item/projectile/bullet/reusable/foam_dart/newdart = newcasing.BB
- newdart.modified = modified
- newdart.damage = damage
- newdart.nodamage = nodamage
- newdart.damage_type = damage_type
- if(pen)
- newdart.pen = pen
- pen.forceMove(newdart)
- pen = null
- newdart.update_icon()
-
-
-/obj/item/projectile/bullet/reusable/foam_dart/Destroy()
- pen = null
- return ..()
-
-/obj/item/projectile/bullet/reusable/foam_dart/riot
- name = "riot foam dart"
- icon_state = "foamdart_riot_proj"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
- stamina = 25
+/obj/item/projectile/bullet/reusable/foam_dart
+ name = "foam dart"
+ desc = "I hope you're wearing eye protection."
+ damage = 0 // It's a damn toy.
+ damage_type = OXY
+ nodamage = 1
+ icon = 'icons/obj/guns/toy.dmi'
+ icon_state = "foamdart_proj"
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart
+ range = 10
+ var/modified = 0
+ var/obj/item/pen/pen = null
+
+/obj/item/projectile/bullet/reusable/foam_dart/handle_drop()
+ if(dropped)
+ return
+ var/turf/T = get_turf(src)
+ dropped = 1
+ var/obj/item/ammo_casing/caseless/foam_dart/newcasing = new ammo_type(T)
+ newcasing.modified = modified
+ var/obj/item/projectile/bullet/reusable/foam_dart/newdart = newcasing.BB
+ newdart.modified = modified
+ newdart.damage = damage
+ newdart.nodamage = nodamage
+ newdart.damage_type = damage_type
+ if(pen)
+ newdart.pen = pen
+ pen.forceMove(newdart)
+ pen = null
+ newdart.update_icon()
+
+
+/obj/item/projectile/bullet/reusable/foam_dart/Destroy()
+ pen = null
+ return ..()
+
+/obj/item/projectile/bullet/reusable/foam_dart/riot
+ name = "riot foam dart"
+ icon_state = "foamdart_riot_proj"
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
+ stamina = 25
diff --git a/code/modules/projectiles/projectile/reusable/magspear.dm b/code/modules/projectiles/projectile/reusable/magspear.dm
new file mode 100644
index 0000000000..7fcd6b80a6
--- /dev/null
+++ b/code/modules/projectiles/projectile/reusable/magspear.dm
@@ -0,0 +1,6 @@
+/obj/item/projectile/bullet/reusable/magspear
+ name = "magnetic spear"
+ desc = "WHITE WHALE, HOLY GRAIL"
+ damage = 30 //takes 3 spears to kill a mega carp, one to kill a normal carp
+ icon_state = "magspear"
+ ammo_type = /obj/item/ammo_casing/caseless/magspear
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
deleted file mode 100644
index e8f309309a..0000000000
--- a/code/modules/projectiles/projectile/special.dm
+++ /dev/null
@@ -1,617 +0,0 @@
-/obj/item/projectile/ion
- name = "ion bolt"
- icon_state = "ion"
- damage = 0
- damage_type = BURN
- nodamage = 1
- flag = "energy"
- impact_effect_type = /obj/effect/temp_visual/impact_effect/ion
-
-
-/obj/item/projectile/ion/on_hit(atom/target, blocked = FALSE)
- ..()
- empulse(target, 1, 1)
- return 1
-
-
-/obj/item/projectile/ion/weak
-
-/obj/item/projectile/ion/weak/on_hit(atom/target, blocked = FALSE)
- ..()
- empulse(target, 0, 0)
- return 1
-
-
-/obj/item/projectile/bullet/gyro
- name ="explosive bolt"
- icon_state= "bolter"
- damage = 50
-
-/obj/item/projectile/bullet/gyro/on_hit(atom/target, blocked = FALSE)
- ..()
- explosion(target, -1, 0, 2)
- return 1
-
-/obj/item/projectile/bullet/a84mm
- name ="anti-armour rocket"
- desc = "USE A WEEL GUN"
- icon_state= "atrocket"
- damage = 80
- var/anti_armour_damage = 200
- armour_penetration = 100
- dismemberment = 100
-
-/obj/item/projectile/bullet/a84mm/on_hit(atom/target, blocked = FALSE)
- ..()
- explosion(target, -1, 1, 3, 1, 0, flame_range = 4)
-
- if(ismecha(target))
- var/obj/mecha/M = target
- M.take_damage(anti_armour_damage)
- if(issilicon(target))
- var/mob/living/silicon/S = target
- S.take_overall_damage(anti_armour_damage*0.75, anti_armour_damage*0.25)
- return 1
-
-/obj/item/projectile/bullet/srmrocket
- name ="SRM-8 Rocket"
- desc = "Boom."
- icon_state = "missile"
- damage = 30
- ricochets_max = 0 //it's a MISSILE
-
-/obj/item/projectile/bullet/srmrocket/on_hit(atom/target, blocked=0)
- ..()
- if(!isliving(target)) //if the target isn't alive, so is a wall or something
- explosion(target, 0, 1, 2, 4)
- else
- explosion(target, 0, 0, 2, 4)
- return 1
-
-/obj/item/projectile/temp
- name = "freeze beam"
- icon_state = "ice_2"
- damage = 0
- damage_type = BURN
- nodamage = 1
- flag = "energy"
- var/temperature = 100
-
-
-/obj/item/projectile/temp/on_hit(atom/target, blocked = FALSE)//These two could likely check temp protection on the mob
- ..()
- if(isliving(target))
- var/mob/M = target
- M.bodytemperature = temperature
- return 1
-
-/obj/item/projectile/temp/hot
- name = "heat beam"
- temperature = 400
-
-/obj/item/projectile/meteor
- name = "meteor"
- icon = 'icons/obj/meteor.dmi'
- icon_state = "small1"
- damage = 0
- damage_type = BRUTE
- nodamage = 1
- flag = "bullet"
-
-/obj/item/projectile/meteor/Collide(atom/A)
- if(A == firer)
- forceMove(A.loc)
- return
- A.ex_act(EXPLODE_HEAVY)
- playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
- for(var/mob/M in urange(10, src))
- if(!M.stat)
- shake_camera(M, 3, 1)
- qdel(src)
-
-/obj/item/projectile/energy/floramut
- name = "alpha somatoray"
- icon_state = "energy"
- damage = 0
- damage_type = TOX
- nodamage = 1
- flag = "energy"
-
-/obj/item/projectile/energy/floramut/on_hit(atom/target, blocked = FALSE)
- . = ..()
- if(iscarbon(target))
- var/mob/living/carbon/C = target
- if(C.dna.species.id == "pod")
- C.randmuti()
- C.randmut()
- C.updateappearance()
- C.domutcheck()
-
-/obj/item/projectile/energy/florayield
- name = "beta somatoray"
- icon_state = "energy2"
- damage = 0
- damage_type = TOX
- nodamage = 1
- flag = "energy"
-
-/obj/item/projectile/beam/mindflayer
- name = "flayer ray"
-
-/obj/item/projectile/beam/mindflayer/on_hit(atom/target, blocked = FALSE)
- . = ..()
- if(ishuman(target))
- var/mob/living/carbon/human/M = target
- M.adjustBrainLoss(20)
- M.hallucination += 20
-
-/obj/item/projectile/beam/wormhole
- name = "bluespace beam"
- icon_state = "spark"
- hitsound = "sparks"
- damage = 3
- var/obj/item/gun/energy/wormhole_projector/gun
- color = "#33CCFF"
-
-/obj/item/projectile/beam/wormhole/orange
- name = "orange bluespace beam"
- color = "#FF6600"
-
-/obj/item/projectile/beam/wormhole/New(var/obj/item/ammo_casing/energy/wormhole/casing)
- if(casing)
- gun = casing.gun
-
-/obj/item/ammo_casing/energy/wormhole/New(var/obj/item/gun/energy/wormhole_projector/wh)
- gun = wh
-
-/obj/item/projectile/beam/wormhole/on_hit(atom/target)
- if(ismob(target))
- var/turf/portal_destination = pick(orange(6, src))
- do_teleport(target, portal_destination)
- return ..()
- if(!gun)
- qdel(src)
- gun.create_portal(src, get_turf(src))
-
-/obj/item/projectile/plasma
- name = "plasma blast"
- icon_state = "plasmacutter"
- damage_type = BRUTE
- damage = 20
- range = 4
- dismemberment = 20
- impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
- var/pressure_decrease_active = FALSE
- var/pressure_decrease = 0.25
- var/mine_range = 3 //mines this many additional tiles of rock
- tracer_type = /obj/effect/projectile/tracer/plasma_cutter
- muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
- impact_type = /obj/effect/projectile/impact/plasma_cutter
-
-/obj/item/projectile/plasma/Initialize()
- . = ..()
- if(!lavaland_equipment_pressure_check(get_turf(src)))
- name = "weakened [name]"
- damage = damage * pressure_decrease
- pressure_decrease_active = TRUE
-
-/obj/item/projectile/plasma/on_hit(atom/target)
- . = ..()
- if(ismineralturf(target))
- var/turf/closed/mineral/M = target
- M.gets_drilled(firer)
- if(mine_range)
- mine_range--
- range++
- if(range > 0)
- return -1
-
-/obj/item/projectile/plasma/adv
- damage = 28
- range = 5
- mine_range = 5
-
-/obj/item/projectile/plasma/adv/mech
- damage = 40
- range = 9
- mine_range = 3
-
-/obj/item/projectile/plasma/turret
- //Between normal and advanced for damage, made a beam so not the turret does not destroy glass
- name = "plasma beam"
- damage = 24
- range = 7
- pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
-
-
-/obj/item/projectile/gravityrepulse
- name = "repulsion bolt"
- icon = 'icons/effects/effects.dmi'
- icon_state = "chronofield"
- hitsound = 'sound/weapons/wave.ogg'
- damage = 0
- damage_type = BRUTE
- nodamage = 1
- color = "#33CCFF"
- var/turf/T
- var/power = 4
- var/list/thrown_items = list()
-
-/obj/item/projectile/gravityrepulse/Initialize()
- . = ..()
- var/obj/item/ammo_casing/energy/gravityrepulse/C = loc
- if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
- power = min(C.gun.power, 15)
-
-/obj/item/projectile/gravityrepulse/on_hit()
- . = ..()
- T = get_turf(src)
- for(var/atom/movable/A in range(T, power))
- if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
- continue
- var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
- A.throw_at(throwtarget,power+1,1)
- thrown_items[A] = A
- for(var/turf/F in range(T,power))
- new /obj/effect/temp_visual/gravpush(F)
-
-/obj/item/projectile/gravityattract
- name = "attraction bolt"
- icon = 'icons/effects/effects.dmi'
- icon_state = "chronofield"
- hitsound = 'sound/weapons/wave.ogg'
- damage = 0
- damage_type = BRUTE
- nodamage = 1
- color = "#FF6600"
- var/turf/T
- var/power = 4
- var/list/thrown_items = list()
-
-/obj/item/projectile/gravityattract/Initialize()
- . = ..()
- var/obj/item/ammo_casing/energy/gravityattract/C = loc
- if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
- power = min(C.gun.power, 15)
-
-/obj/item/projectile/gravityattract/on_hit()
- . = ..()
- T = get_turf(src)
- for(var/atom/movable/A in range(T, power))
- if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
- continue
- A.throw_at(T, power+1, 1)
- thrown_items[A] = A
- for(var/turf/F in range(T,power))
- new /obj/effect/temp_visual/gravpush(F)
-
-/obj/item/projectile/gravitychaos
- name = "gravitational blast"
- icon = 'icons/effects/effects.dmi'
- icon_state = "chronofield"
- hitsound = 'sound/weapons/wave.ogg'
- damage = 0
- damage_type = BRUTE
- nodamage = 1
- color = "#101010"
- var/turf/T
- var/power = 4
- var/list/thrown_items = list()
-
-/obj/item/projectile/gravitychaos/Initialize()
- . = ..()
- var/obj/item/ammo_casing/energy/gravitychaos/C = loc
- if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
- power = min(C.gun.power, 15)
-
-/obj/item/projectile/gravitychaos/on_hit()
- . = ..()
- T = get_turf(src)
- for(var/atom/movable/A in range(T, power))
- if(A == src|| (firer && A == src.firer) || A.anchored || thrown_items[A])
- continue
- A.throw_at(get_edge_target_turf(A, pick(GLOB.cardinals)), power+1, 1)
- thrown_items[A] = A
- for(var/turf/Z in range(T,power))
- new /obj/effect/temp_visual/gravpush(Z)
-
-/obj/effect/ebeam/curse_arm
- name = "curse arm"
- layer = LARGE_MOB_LAYER
-
-/obj/item/projectile/curse_hand
- name = "curse hand"
- icon_state = "cursehand"
- hitsound = 'sound/effects/curse4.ogg'
- layer = LARGE_MOB_LAYER
- damage_type = BURN
- damage = 10
- knockdown = 20
- speed = 2
- range = 16
- forcedodge = TRUE
- var/datum/beam/arm
- var/handedness = 0
-
-/obj/item/projectile/curse_hand/Initialize(mapload)
- . = ..()
- handedness = prob(50)
- update_icon()
-
-/obj/item/projectile/curse_hand/update_icon()
- icon_state = "[icon_state][handedness]"
-
-/obj/item/projectile/curse_hand/fire(setAngle)
- if(starting)
- arm = starting.Beam(src, icon_state = "curse[handedness]", time = INFINITY, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm)
- ..()
-
-/obj/item/projectile/curse_hand/prehit(atom/target)
- if(target == original)
- forcedodge = FALSE
- else if(!isturf(target))
- return FALSE
- return ..()
-
-/obj/item/projectile/curse_hand/Destroy()
- if(arm)
- arm.End()
- arm = null
- if(forcedodge)
- playsound(src, 'sound/effects/curse3.ogg', 25, 1, -1)
- var/turf/T = get_step(src, dir)
- new/obj/effect/temp_visual/dir_setting/curse/hand(T, dir, handedness)
- for(var/obj/effect/temp_visual/dir_setting/curse/grasp_portal/G in starting)
- qdel(G)
- new /obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading(starting, dir)
- var/datum/beam/D = starting.Beam(T, icon_state = "curse[handedness]", time = 32, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm, beam_sleep_time = 1)
- for(var/b in D.elements)
- var/obj/effect/ebeam/B = b
- animate(B, alpha = 0, time = 32)
- return ..()
-
-/obj/item/projectile/hallucination
- name = "bullet"
- icon = null
- icon_state = null
- hitsound = ""
- suppressed = TRUE
- ricochets_max = 0
- ricochet_chance = 0
- damage = 0
- nodamage = TRUE
- projectile_type = /obj/item/projectile/hallucination
- log_override = TRUE
- var/hal_icon_state
- var/image/fake_icon
- var/mob/living/carbon/hal_target
- var/hal_fire_sound
- var/hal_hitsound
- var/hal_hitsound_wall
- var/hal_impact_effect
- var/hal_impact_effect_wall
- var/hit_duration
- var/hit_duration_wall
-
-/obj/item/projectile/hallucination/fire()
- ..()
- fake_icon = image('icons/obj/projectiles.dmi', src, hal_icon_state, ABOVE_MOB_LAYER)
- if(hal_target.client)
- hal_target.client.images += fake_icon
-
-/obj/item/projectile/hallucination/Destroy()
- if(hal_target.client)
- hal_target.client.images -= fake_icon
- QDEL_NULL(fake_icon)
- return ..()
-
-/obj/item/projectile/hallucination/Collide(atom/A)
- if(!ismob(A))
- if(hal_hitsound_wall)
- hal_target.playsound_local(loc, hal_hitsound_wall, 40, 1)
- if(hal_impact_effect_wall)
- spawn_hit(A, TRUE)
- else if(A == hal_target)
- if(hal_hitsound)
- hal_target.playsound_local(A, hal_hitsound, 100, 1)
- target_on_hit(A)
- qdel(src)
- return TRUE
-
-/obj/item/projectile/hallucination/proc/target_on_hit(mob/M)
- if(M == hal_target)
- to_chat(hal_target, "[M] is hit by \a [src] in the chest!")
- hal_apply_effect()
- else if(M in view(hal_target))
- to_chat(hal_target, "[M] is hit by \a [src] in the chest!!")
- if(damage_type == BRUTE)
- var/splatter_dir = dir
- if(starting)
- splatter_dir = get_dir(starting, get_turf(M))
- spawn_blood(M, splatter_dir)
- else if(hal_impact_effect)
- spawn_hit(M, FALSE)
-
-/obj/item/projectile/hallucination/proc/spawn_blood(mob/M, set_dir)
- set waitfor = 0
- if(!hal_target.client)
- return
-
- var/splatter_icon_state
- if(set_dir in GLOB.diagonals)
- splatter_icon_state = "splatter[pick(1, 2, 6)]"
- else
- splatter_icon_state = "splatter[pick(3, 4, 5)]"
-
- var/image/blood = image('icons/effects/blood.dmi', M, splatter_icon_state, ABOVE_MOB_LAYER)
- var/target_pixel_x = 0
- var/target_pixel_y = 0
- switch(set_dir)
- if(NORTH)
- target_pixel_y = 16
- if(SOUTH)
- target_pixel_y = -16
- layer = ABOVE_MOB_LAYER
- if(EAST)
- target_pixel_x = 16
- if(WEST)
- target_pixel_x = -16
- if(NORTHEAST)
- target_pixel_x = 16
- target_pixel_y = 16
- if(NORTHWEST)
- target_pixel_x = -16
- target_pixel_y = 16
- if(SOUTHEAST)
- target_pixel_x = 16
- target_pixel_y = -16
- layer = ABOVE_MOB_LAYER
- if(SOUTHWEST)
- target_pixel_x = -16
- target_pixel_y = -16
- layer = ABOVE_MOB_LAYER
- hal_target.client.images += blood
- animate(blood, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = 5)
- addtimer(CALLBACK(src, .proc/cleanup_blood), 5)
-
-/obj/item/projectile/hallucination/proc/cleanup_blood(image/blood)
- hal_target.client.images -= blood
- qdel(blood)
-
-/obj/item/projectile/hallucination/proc/spawn_hit(atom/A, is_wall)
- set waitfor = 0
- if(!hal_target.client)
- return
-
- var/image/hit_effect = image('icons/effects/blood.dmi', A, is_wall ? hal_impact_effect_wall : hal_impact_effect, ABOVE_MOB_LAYER)
- hit_effect.pixel_x = A.pixel_x + rand(-4,4)
- hit_effect.pixel_y = A.pixel_y + rand(-4,4)
- hal_target.client.images += hit_effect
- sleep(is_wall ? hit_duration_wall : hit_duration)
- hal_target.client.images -= hit_effect
- qdel(hit_effect)
-
-
-/obj/item/projectile/hallucination/proc/hal_apply_effect()
- return
-
-/obj/item/projectile/hallucination/bullet
- name = "bullet"
- hal_icon_state = "bullet"
- hal_fire_sound = "gunshot"
- hal_hitsound = 'sound/weapons/pierce.ogg'
- hal_hitsound_wall = "ricochet"
- hal_impact_effect = "impact_bullet"
- hal_impact_effect_wall = "impact_bullet"
- hit_duration = 5
- hit_duration_wall = 5
-
-/obj/item/projectile/hallucination/bullet/hal_apply_effect()
- hal_target.adjustStaminaLoss(60)
-
-/obj/item/projectile/hallucination/laser
- name = "laser"
- damage_type = BURN
- hal_icon_state = "laser"
- hal_fire_sound = 'sound/weapons/laser.ogg'
- hal_hitsound = 'sound/weapons/sear.ogg'
- hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
- hal_impact_effect = "impact_laser"
- hal_impact_effect_wall = "impact_laser_wall"
- hit_duration = 4
- hit_duration_wall = 10
- pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
-
-/obj/item/projectile/hallucination/laser/hal_apply_effect()
- hal_target.adjustStaminaLoss(20)
- hal_target.blur_eyes(2)
-
-/obj/item/projectile/hallucination/taser
- name = "electrode"
- damage_type = BURN
- hal_icon_state = "spark"
- color = "#FFFF00"
- hal_fire_sound = 'sound/weapons/taser.ogg'
- hal_hitsound = 'sound/weapons/taserhit.ogg'
- hal_hitsound_wall = null
- hal_impact_effect = null
- hal_impact_effect_wall = null
-
-/obj/item/projectile/hallucination/taser/hal_apply_effect()
- hal_target.Knockdown(100)
- hal_target.stuttering += 20
- if(hal_target.dna && hal_target.dna.check_mutation(HULK))
- hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
- else if((hal_target.status_flags & CANKNOCKDOWN) && !hal_target.has_trait(TRAIT_STUNIMMUNE))
- addtimer(CALLBACK(hal_target, /mob/living/carbon.proc/do_jitter_animation, 20), 5)
-
-/obj/item/projectile/hallucination/disabler
- name = "disabler beam"
- damage_type = STAMINA
- hal_icon_state = "omnilaser"
- hal_fire_sound = 'sound/weapons/taser2.ogg'
- hal_hitsound = 'sound/weapons/tap.ogg'
- hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
- hal_impact_effect = "impact_laser_blue"
- hal_impact_effect_wall = null
- hit_duration = 4
- pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
-
-/obj/item/projectile/hallucination/disabler/hal_apply_effect()
- hal_target.adjustStaminaLoss(25)
-
-/obj/item/projectile/hallucination/ebow
- name = "bolt"
- damage_type = TOX
- hal_icon_state = "cbbolt"
- hal_fire_sound = 'sound/weapons/genhit.ogg'
- hal_hitsound = null
- hal_hitsound_wall = null
- hal_impact_effect = null
- hal_impact_effect_wall = null
-
-/obj/item/projectile/hallucination/ebow/hal_apply_effect()
- hal_target.Knockdown(100)
- hal_target.stuttering += 5
- hal_target.adjustStaminaLoss(8)
-
-/obj/item/projectile/hallucination/change
- name = "bolt of change"
- damage_type = BURN
- hal_icon_state = "ice_1"
- hal_fire_sound = 'sound/magic/staff_change.ogg'
- hal_hitsound = null
- hal_hitsound_wall = null
- hal_impact_effect = null
- hal_impact_effect_wall = null
-
-/obj/item/projectile/hallucination/change/hal_apply_effect()
- new /datum/hallucination/self_delusion(hal_target, TRUE, wabbajack = FALSE)
-
-/obj/item/projectile/hallucination/death
- name = "bolt of death"
- damage_type = BURN
- hal_icon_state = "pulse1_bl"
- hal_fire_sound = 'sound/magic/wandodeath.ogg'
- hal_hitsound = null
- hal_hitsound_wall = null
- hal_impact_effect = null
- hal_impact_effect_wall = null
-
-/obj/item/projectile/hallucination/death/hal_apply_effect()
- new /datum/hallucination/death(hal_target, TRUE)
-
-// Neurotoxin
-
-/obj/item/projectile/bullet/neurotoxin
- name = "neurotoxin spit"
- icon_state = "neurotoxin"
- damage = 5
- damage_type = TOX
- knockdown = 100
-
-/obj/item/projectile/bullet/neurotoxin/on_hit(atom/target, blocked = FALSE)
- if(isalien(target))
- knockdown = 0
- nodamage = TRUE
- return ..()
diff --git a/code/modules/projectiles/projectile/special/curse.dm b/code/modules/projectiles/projectile/special/curse.dm
new file mode 100644
index 0000000000..e5ac8126dd
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/curse.dm
@@ -0,0 +1,55 @@
+/obj/effect/ebeam/curse_arm
+ name = "curse arm"
+ layer = LARGE_MOB_LAYER
+
+/obj/item/projectile/curse_hand
+ name = "curse hand"
+ icon_state = "cursehand"
+ hitsound = 'sound/effects/curse4.ogg'
+ layer = LARGE_MOB_LAYER
+ damage_type = BURN
+ damage = 10
+ knockdown = 20
+ speed = 2
+ range = 16
+ forcedodge = TRUE
+ var/datum/beam/arm
+ var/handedness = 0
+
+/obj/item/projectile/curse_hand/Initialize(mapload)
+ . = ..()
+ handedness = prob(50)
+ update_icon()
+
+/obj/item/projectile/curse_hand/update_icon()
+ icon_state = "[icon_state][handedness]"
+
+/obj/item/projectile/curse_hand/fire(setAngle)
+ if(starting)
+ arm = starting.Beam(src, icon_state = "curse[handedness]", time = INFINITY, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm)
+ ..()
+
+/obj/item/projectile/curse_hand/prehit(atom/target)
+ if(target == original)
+ forcedodge = FALSE
+ else if(!isturf(target))
+ return FALSE
+ return ..()
+
+/obj/item/projectile/curse_hand/Destroy()
+ if(arm)
+ arm.End()
+ arm = null
+ if(forcedodge)
+ playsound(src, 'sound/effects/curse3.ogg', 25, 1, -1)
+ var/turf/T = get_step(src, dir)
+ new/obj/effect/temp_visual/dir_setting/curse/hand(T, dir, handedness)
+ for(var/obj/effect/temp_visual/dir_setting/curse/grasp_portal/G in starting)
+ qdel(G)
+ new /obj/effect/temp_visual/dir_setting/curse/grasp_portal/fading(starting, dir)
+ var/datum/beam/D = starting.Beam(T, icon_state = "curse[handedness]", time = 32, maxdistance = INFINITY, beam_type=/obj/effect/ebeam/curse_arm, beam_sleep_time = 1)
+ for(var/b in D.elements)
+ var/obj/effect/ebeam/B = b
+ animate(B, alpha = 0, time = 32)
+ return ..()
+
diff --git a/code/modules/projectiles/projectile/special/floral.dm b/code/modules/projectiles/projectile/special/floral.dm
new file mode 100644
index 0000000000..295f89148a
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/floral.dm
@@ -0,0 +1,25 @@
+/obj/item/projectile/energy/floramut
+ name = "alpha somatoray"
+ icon_state = "energy"
+ damage = 0
+ damage_type = TOX
+ nodamage = 1
+ flag = "energy"
+
+/obj/item/projectile/energy/floramut/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ if(C.dna.species.id == "pod")
+ C.randmuti()
+ C.randmut()
+ C.updateappearance()
+ C.domutcheck()
+
+/obj/item/projectile/energy/florayield
+ name = "beta somatoray"
+ icon_state = "energy2"
+ damage = 0
+ damage_type = TOX
+ nodamage = 1
+ flag = "energy"
diff --git a/code/modules/projectiles/projectile/special/gravity.dm b/code/modules/projectiles/projectile/special/gravity.dm
new file mode 100644
index 0000000000..89f753d36d
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/gravity.dm
@@ -0,0 +1,90 @@
+/obj/item/projectile/gravityrepulse
+ name = "repulsion bolt"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "chronofield"
+ hitsound = 'sound/weapons/wave.ogg'
+ damage = 0
+ damage_type = BRUTE
+ nodamage = 1
+ color = "#33CCFF"
+ var/turf/T
+ var/power = 4
+ var/list/thrown_items = list()
+
+/obj/item/projectile/gravityrepulse/Initialize()
+ . = ..()
+ var/obj/item/ammo_casing/energy/gravityrepulse/C = loc
+ if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
+ power = min(C.gun.power, 15)
+
+/obj/item/projectile/gravityrepulse/on_hit()
+ . = ..()
+ T = get_turf(src)
+ for(var/atom/movable/A in range(T, power))
+ if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
+ continue
+ var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
+ A.throw_at(throwtarget,power+1,1)
+ thrown_items[A] = A
+ for(var/turf/F in range(T,power))
+ new /obj/effect/temp_visual/gravpush(F)
+
+/obj/item/projectile/gravityattract
+ name = "attraction bolt"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "chronofield"
+ hitsound = 'sound/weapons/wave.ogg'
+ damage = 0
+ damage_type = BRUTE
+ nodamage = 1
+ color = "#FF6600"
+ var/turf/T
+ var/power = 4
+ var/list/thrown_items = list()
+
+/obj/item/projectile/gravityattract/Initialize()
+ . = ..()
+ var/obj/item/ammo_casing/energy/gravityattract/C = loc
+ if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
+ power = min(C.gun.power, 15)
+
+/obj/item/projectile/gravityattract/on_hit()
+ . = ..()
+ T = get_turf(src)
+ for(var/atom/movable/A in range(T, power))
+ if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
+ continue
+ A.throw_at(T, power+1, 1)
+ thrown_items[A] = A
+ for(var/turf/F in range(T,power))
+ new /obj/effect/temp_visual/gravpush(F)
+
+/obj/item/projectile/gravitychaos
+ name = "gravitational blast"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "chronofield"
+ hitsound = 'sound/weapons/wave.ogg'
+ damage = 0
+ damage_type = BRUTE
+ nodamage = 1
+ color = "#101010"
+ var/turf/T
+ var/power = 4
+ var/list/thrown_items = list()
+
+/obj/item/projectile/gravitychaos/Initialize()
+ . = ..()
+ var/obj/item/ammo_casing/energy/gravitychaos/C = loc
+ if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
+ power = min(C.gun.power, 15)
+
+/obj/item/projectile/gravitychaos/on_hit()
+ . = ..()
+ T = get_turf(src)
+ for(var/atom/movable/A in range(T, power))
+ if(A == src|| (firer && A == src.firer) || A.anchored || thrown_items[A])
+ continue
+ A.throw_at(get_edge_target_turf(A, pick(GLOB.cardinals)), power+1, 1)
+ thrown_items[A] = A
+ for(var/turf/Z in range(T,power))
+ new /obj/effect/temp_visual/gravpush(Z)
diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm
new file mode 100644
index 0000000000..e158ed89f0
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/hallucination.dm
@@ -0,0 +1,230 @@
+/obj/item/projectile/hallucination
+ name = "bullet"
+ icon = null
+ icon_state = null
+ hitsound = ""
+ suppressed = TRUE
+ ricochets_max = 0
+ ricochet_chance = 0
+ damage = 0
+ nodamage = TRUE
+ projectile_type = /obj/item/projectile/hallucination
+ log_override = TRUE
+ var/hal_icon_state
+ var/image/fake_icon
+ var/mob/living/carbon/hal_target
+ var/hal_fire_sound
+ var/hal_hitsound
+ var/hal_hitsound_wall
+ var/hal_impact_effect
+ var/hal_impact_effect_wall
+ var/hit_duration
+ var/hit_duration_wall
+
+/obj/item/projectile/hallucination/fire()
+ ..()
+ fake_icon = image('icons/obj/projectiles.dmi', src, hal_icon_state, ABOVE_MOB_LAYER)
+ if(hal_target.client)
+ hal_target.client.images += fake_icon
+
+/obj/item/projectile/hallucination/Destroy()
+ if(hal_target.client)
+ hal_target.client.images -= fake_icon
+ QDEL_NULL(fake_icon)
+ return ..()
+
+/obj/item/projectile/hallucination/Collide(atom/A)
+ if(!ismob(A))
+ if(hal_hitsound_wall)
+ hal_target.playsound_local(loc, hal_hitsound_wall, 40, 1)
+ if(hal_impact_effect_wall)
+ spawn_hit(A, TRUE)
+ else if(A == hal_target)
+ if(hal_hitsound)
+ hal_target.playsound_local(A, hal_hitsound, 100, 1)
+ target_on_hit(A)
+ qdel(src)
+ return TRUE
+
+/obj/item/projectile/hallucination/proc/target_on_hit(mob/M)
+ if(M == hal_target)
+ to_chat(hal_target, "[M] is hit by \a [src] in the chest!")
+ hal_apply_effect()
+ else if(M in view(hal_target))
+ to_chat(hal_target, "[M] is hit by \a [src] in the chest!!")
+ if(damage_type == BRUTE)
+ var/splatter_dir = dir
+ if(starting)
+ splatter_dir = get_dir(starting, get_turf(M))
+ spawn_blood(M, splatter_dir)
+ else if(hal_impact_effect)
+ spawn_hit(M, FALSE)
+
+/obj/item/projectile/hallucination/proc/spawn_blood(mob/M, set_dir)
+ set waitfor = 0
+ if(!hal_target.client)
+ return
+
+ var/splatter_icon_state
+ if(set_dir in GLOB.diagonals)
+ splatter_icon_state = "splatter[pick(1, 2, 6)]"
+ else
+ splatter_icon_state = "splatter[pick(3, 4, 5)]"
+
+ var/image/blood = image('icons/effects/blood.dmi', M, splatter_icon_state, ABOVE_MOB_LAYER)
+ var/target_pixel_x = 0
+ var/target_pixel_y = 0
+ switch(set_dir)
+ if(NORTH)
+ target_pixel_y = 16
+ if(SOUTH)
+ target_pixel_y = -16
+ layer = ABOVE_MOB_LAYER
+ if(EAST)
+ target_pixel_x = 16
+ if(WEST)
+ target_pixel_x = -16
+ if(NORTHEAST)
+ target_pixel_x = 16
+ target_pixel_y = 16
+ if(NORTHWEST)
+ target_pixel_x = -16
+ target_pixel_y = 16
+ if(SOUTHEAST)
+ target_pixel_x = 16
+ target_pixel_y = -16
+ layer = ABOVE_MOB_LAYER
+ if(SOUTHWEST)
+ target_pixel_x = -16
+ target_pixel_y = -16
+ layer = ABOVE_MOB_LAYER
+ hal_target.client.images += blood
+ animate(blood, pixel_x = target_pixel_x, pixel_y = target_pixel_y, alpha = 0, time = 5)
+ addtimer(CALLBACK(src, .proc/cleanup_blood), 5)
+
+/obj/item/projectile/hallucination/proc/cleanup_blood(image/blood)
+ hal_target.client.images -= blood
+ qdel(blood)
+
+/obj/item/projectile/hallucination/proc/spawn_hit(atom/A, is_wall)
+ set waitfor = 0
+ if(!hal_target.client)
+ return
+
+ var/image/hit_effect = image('icons/effects/blood.dmi', A, is_wall ? hal_impact_effect_wall : hal_impact_effect, ABOVE_MOB_LAYER)
+ hit_effect.pixel_x = A.pixel_x + rand(-4,4)
+ hit_effect.pixel_y = A.pixel_y + rand(-4,4)
+ hal_target.client.images += hit_effect
+ sleep(is_wall ? hit_duration_wall : hit_duration)
+ hal_target.client.images -= hit_effect
+ qdel(hit_effect)
+
+
+/obj/item/projectile/hallucination/proc/hal_apply_effect()
+ return
+
+/obj/item/projectile/hallucination/bullet
+ name = "bullet"
+ hal_icon_state = "bullet"
+ hal_fire_sound = "gunshot"
+ hal_hitsound = 'sound/weapons/pierce.ogg'
+ hal_hitsound_wall = "ricochet"
+ hal_impact_effect = "impact_bullet"
+ hal_impact_effect_wall = "impact_bullet"
+ hit_duration = 5
+ hit_duration_wall = 5
+
+/obj/item/projectile/hallucination/bullet/hal_apply_effect()
+ hal_target.adjustStaminaLoss(60)
+
+/obj/item/projectile/hallucination/laser
+ name = "laser"
+ damage_type = BURN
+ hal_icon_state = "laser"
+ hal_fire_sound = 'sound/weapons/laser.ogg'
+ hal_hitsound = 'sound/weapons/sear.ogg'
+ hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hal_impact_effect = "impact_laser"
+ hal_impact_effect_wall = "impact_laser_wall"
+ hit_duration = 4
+ hit_duration_wall = 10
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+
+/obj/item/projectile/hallucination/laser/hal_apply_effect()
+ hal_target.adjustStaminaLoss(20)
+ hal_target.blur_eyes(2)
+
+/obj/item/projectile/hallucination/taser
+ name = "electrode"
+ damage_type = BURN
+ hal_icon_state = "spark"
+ color = "#FFFF00"
+ hal_fire_sound = 'sound/weapons/taser.ogg'
+ hal_hitsound = 'sound/weapons/taserhit.ogg'
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/taser/hal_apply_effect()
+ hal_target.Knockdown(100)
+ hal_target.stuttering += 20
+ if(hal_target.dna && hal_target.dna.check_mutation(HULK))
+ hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+ else if((hal_target.status_flags & CANKNOCKDOWN) && !hal_target.has_trait(TRAIT_STUNIMMUNE))
+ addtimer(CALLBACK(hal_target, /mob/living/carbon.proc/do_jitter_animation, 20), 5)
+
+/obj/item/projectile/hallucination/disabler
+ name = "disabler beam"
+ damage_type = STAMINA
+ hal_icon_state = "omnilaser"
+ hal_fire_sound = 'sound/weapons/taser2.ogg'
+ hal_hitsound = 'sound/weapons/tap.ogg'
+ hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hal_impact_effect = "impact_laser_blue"
+ hal_impact_effect_wall = null
+ hit_duration = 4
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+
+/obj/item/projectile/hallucination/disabler/hal_apply_effect()
+ hal_target.adjustStaminaLoss(25)
+
+/obj/item/projectile/hallucination/ebow
+ name = "bolt"
+ damage_type = TOX
+ hal_icon_state = "cbbolt"
+ hal_fire_sound = 'sound/weapons/genhit.ogg'
+ hal_hitsound = null
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/ebow/hal_apply_effect()
+ hal_target.Knockdown(100)
+ hal_target.stuttering += 5
+ hal_target.adjustStaminaLoss(8)
+
+/obj/item/projectile/hallucination/change
+ name = "bolt of change"
+ damage_type = BURN
+ hal_icon_state = "ice_1"
+ hal_fire_sound = 'sound/magic/staff_change.ogg'
+ hal_hitsound = null
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/change/hal_apply_effect()
+ new /datum/hallucination/self_delusion(hal_target, TRUE, wabbajack = FALSE)
+
+/obj/item/projectile/hallucination/death
+ name = "bolt of death"
+ damage_type = BURN
+ hal_icon_state = "pulse1_bl"
+ hal_fire_sound = 'sound/magic/wandodeath.ogg'
+ hal_hitsound = null
+ hal_hitsound_wall = null
+ hal_impact_effect = null
+ hal_impact_effect_wall = null
+
+/obj/item/projectile/hallucination/death/hal_apply_effect()
+ new /datum/hallucination/death(hal_target, TRUE)
diff --git a/code/modules/projectiles/projectile/special/ion.dm b/code/modules/projectiles/projectile/special/ion.dm
new file mode 100644
index 0000000000..403a1bedd7
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/ion.dm
@@ -0,0 +1,20 @@
+/obj/item/projectile/ion
+ name = "ion bolt"
+ icon_state = "ion"
+ damage = 0
+ damage_type = BURN
+ nodamage = 1
+ flag = "energy"
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/ion
+
+/obj/item/projectile/ion/on_hit(atom/target, blocked = FALSE)
+ ..()
+ empulse(target, 1, 1)
+ return TRUE
+
+/obj/item/projectile/ion/weak
+
+/obj/item/projectile/ion/weak/on_hit(atom/target, blocked = FALSE)
+ ..()
+ empulse(target, 0, 0)
+ return TRUE
diff --git a/code/modules/projectiles/projectile/special/meteor.dm b/code/modules/projectiles/projectile/special/meteor.dm
new file mode 100644
index 0000000000..f4e60998e1
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/meteor.dm
@@ -0,0 +1,19 @@
+/obj/item/projectile/meteor
+ name = "meteor"
+ icon = 'icons/obj/meteor.dmi'
+ icon_state = "small1"
+ damage = 0
+ damage_type = BRUTE
+ nodamage = 1
+ flag = "bullet"
+
+/obj/item/projectile/meteor/Collide(atom/A)
+ if(A == firer)
+ forceMove(A.loc)
+ return
+ A.ex_act(EXPLODE_HEAVY)
+ playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
+ for(var/mob/M in urange(10, src))
+ if(!M.stat)
+ shake_camera(M, 3, 1)
+ qdel(src)
diff --git a/code/modules/projectiles/projectile/special/mindflayer.dm b/code/modules/projectiles/projectile/special/mindflayer.dm
new file mode 100644
index 0000000000..eaa998f7e0
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/mindflayer.dm
@@ -0,0 +1,9 @@
+/obj/item/projectile/beam/mindflayer
+ name = "flayer ray"
+
+/obj/item/projectile/beam/mindflayer/on_hit(atom/target, blocked = FALSE)
+ . = ..()
+ if(ishuman(target))
+ var/mob/living/carbon/human/M = target
+ M.adjustBrainLoss(20)
+ M.hallucination += 20
diff --git a/code/modules/projectiles/projectile/special/neurotoxin.dm b/code/modules/projectiles/projectile/special/neurotoxin.dm
new file mode 100644
index 0000000000..46027e7bdf
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/neurotoxin.dm
@@ -0,0 +1,12 @@
+/obj/item/projectile/bullet/neurotoxin
+ name = "neurotoxin spit"
+ icon_state = "neurotoxin"
+ damage = 5
+ damage_type = TOX
+ knockdown = 100
+
+/obj/item/projectile/bullet/neurotoxin/on_hit(atom/target, blocked = FALSE)
+ if(isalien(target))
+ knockdown = 0
+ nodamage = TRUE
+ return ..()
diff --git a/code/modules/projectiles/projectile/special/plasma.dm b/code/modules/projectiles/projectile/special/plasma.dm
new file mode 100644
index 0000000000..aeafb6157a
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/plasma.dm
@@ -0,0 +1,49 @@
+/obj/item/projectile/plasma
+ name = "plasma blast"
+ icon_state = "plasmacutter"
+ damage_type = BRUTE
+ damage = 20
+ range = 4
+ dismemberment = 20
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser
+ var/pressure_decrease_active = FALSE
+ var/pressure_decrease = 0.25
+ var/mine_range = 3 //mines this many additional tiles of rock
+ tracer_type = /obj/effect/projectile/tracer/plasma_cutter
+ muzzle_type = /obj/effect/projectile/muzzle/plasma_cutter
+ impact_type = /obj/effect/projectile/impact/plasma_cutter
+
+/obj/item/projectile/plasma/Initialize()
+ . = ..()
+ if(!lavaland_equipment_pressure_check(get_turf(src)))
+ name = "weakened [name]"
+ damage = damage * pressure_decrease
+ pressure_decrease_active = TRUE
+
+/obj/item/projectile/plasma/on_hit(atom/target)
+ . = ..()
+ if(ismineralturf(target))
+ var/turf/closed/mineral/M = target
+ M.gets_drilled(firer)
+ if(mine_range)
+ mine_range--
+ range++
+ if(range > 0)
+ return -1
+
+/obj/item/projectile/plasma/adv
+ damage = 28
+ range = 5
+ mine_range = 5
+
+/obj/item/projectile/plasma/adv/mech
+ damage = 40
+ range = 9
+ mine_range = 3
+
+/obj/item/projectile/plasma/turret
+ //Between normal and advanced for damage, made a beam so not the turret does not destroy glass
+ name = "plasma beam"
+ damage = 24
+ range = 7
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
diff --git a/code/modules/projectiles/projectile/special/rocket.dm b/code/modules/projectiles/projectile/special/rocket.dm
new file mode 100644
index 0000000000..6518b2a4d5
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/rocket.dm
@@ -0,0 +1,45 @@
+/obj/item/projectile/bullet/gyro
+ name ="explosive bolt"
+ icon_state= "bolter"
+ damage = 50
+
+/obj/item/projectile/bullet/gyro/on_hit(atom/target, blocked = FALSE)
+ ..()
+ explosion(target, -1, 0, 2)
+ return TRUE
+
+/obj/item/projectile/bullet/a84mm
+ name ="anti-armour rocket"
+ desc = "USE A WEEL GUN"
+ icon_state= "atrocket"
+ damage = 80
+ var/anti_armour_damage = 200
+ armour_penetration = 100
+ dismemberment = 100
+
+/obj/item/projectile/bullet/a84mm/on_hit(atom/target, blocked = FALSE)
+ ..()
+ explosion(target, -1, 1, 3, 1, 0, flame_range = 4)
+
+ if(ismecha(target))
+ var/obj/mecha/M = target
+ M.take_damage(anti_armour_damage)
+ if(issilicon(target))
+ var/mob/living/silicon/S = target
+ S.take_overall_damage(anti_armour_damage*0.75, anti_armour_damage*0.25)
+ return TRUE
+
+/obj/item/projectile/bullet/srmrocket
+ name ="SRM-8 Rocket"
+ desc = "Boom."
+ icon_state = "missile"
+ damage = 30
+ ricochets_max = 0 //it's a MISSILE
+
+/obj/item/projectile/bullet/srmrocket/on_hit(atom/target, blocked=0)
+ ..()
+ if(!isliving(target)) //if the target isn't alive, so is a wall or something
+ explosion(target, 0, 1, 2, 4)
+ else
+ explosion(target, 0, 0, 2, 4)
+ return TRUE
diff --git a/code/modules/projectiles/projectile/special/temperature.dm b/code/modules/projectiles/projectile/special/temperature.dm
new file mode 100644
index 0000000000..7fb9c6efb2
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/temperature.dm
@@ -0,0 +1,19 @@
+/obj/item/projectile/temp
+ name = "freeze beam"
+ icon_state = "ice_2"
+ damage = 0
+ damage_type = BURN
+ nodamage = FALSE
+ flag = "energy"
+ var/temperature = 100
+
+/obj/item/projectile/temp/on_hit(atom/target, blocked = FALSE)//These two could likely check temp protection on the mob
+ ..()
+ if(isliving(target))
+ var/mob/M = target
+ M.bodytemperature = temperature
+ return TRUE
+
+/obj/item/projectile/temp/hot
+ name = "heat beam"
+ temperature = 400
diff --git a/code/modules/projectiles/projectile/special/wormhole.dm b/code/modules/projectiles/projectile/special/wormhole.dm
new file mode 100644
index 0000000000..94ef5b9a23
--- /dev/null
+++ b/code/modules/projectiles/projectile/special/wormhole.dm
@@ -0,0 +1,25 @@
+/obj/item/projectile/beam/wormhole
+ name = "bluespace beam"
+ icon_state = "spark"
+ hitsound = "sparks"
+ damage = 3
+ var/obj/item/gun/energy/wormhole_projector/gun
+ color = "#33CCFF"
+
+/obj/item/projectile/beam/wormhole/orange
+ name = "orange bluespace beam"
+ color = "#FF6600"
+
+/obj/item/projectile/beam/wormhole/Initialize(mapload, obj/item/ammo_casing/energy/wormhole/casing)
+ . = ..()
+ if(casing)
+ gun = casing.gun
+
+/obj/item/projectile/beam/wormhole/on_hit(atom/target)
+ if(ismob(target))
+ var/turf/portal_destination = pick(orange(6, src))
+ do_teleport(target, portal_destination)
+ return ..()
+ if(!gun)
+ qdel(src)
+ gun.create_portal(src, get_turf(src))
diff --git a/tgstation.dme b/tgstation.dme
index a678b93d03..f6f772f8c8 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2198,29 +2198,57 @@
#include "code\modules\procedural_mapping\mapGenerators\repair.dm"
#include "code\modules\procedural_mapping\mapGenerators\shuttle.dm"
#include "code\modules\procedural_mapping\mapGenerators\syndicate.dm"
-#include "code\modules\projectiles\ammunition.dm"
-#include "code\modules\projectiles\box_magazine.dm"
-#include "code\modules\projectiles\firing.dm"
#include "code\modules\projectiles\gun.dm"
#include "code\modules\projectiles\pins.dm"
#include "code\modules\projectiles\projectile.dm"
-#include "code\modules\projectiles\ammunition\ammo_casings.dm"
-#include "code\modules\projectiles\ammunition\caseless.dm"
-#include "code\modules\projectiles\ammunition\energy.dm"
-#include "code\modules\projectiles\ammunition\plasma.dm"
-#include "code\modules\projectiles\ammunition\special.dm"
+#include "code\modules\projectiles\ammunition\_ammunition.dm"
+#include "code\modules\projectiles\ammunition\_firing.dm"
+#include "code\modules\projectiles\ammunition\ballistic\lmg.dm"
+#include "code\modules\projectiles\ammunition\ballistic\pistol.dm"
+#include "code\modules\projectiles\ammunition\ballistic\revolver.dm"
+#include "code\modules\projectiles\ammunition\ballistic\rifle.dm"
+#include "code\modules\projectiles\ammunition\ballistic\shotgun.dm"
+#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\foam.dm"
+#include "code\modules\projectiles\ammunition\caseless\misc.dm"
+#include "code\modules\projectiles\ammunition\caseless\rocket.dm"
+#include "code\modules\projectiles\ammunition\energy\_energy.dm"
+#include "code\modules\projectiles\ammunition\energy\chameleon.dm"
+#include "code\modules\projectiles\ammunition\energy\ebow.dm"
+#include "code\modules\projectiles\ammunition\energy\gravity.dm"
+#include "code\modules\projectiles\ammunition\energy\laser.dm"
+#include "code\modules\projectiles\ammunition\energy\lmg.dm"
+#include "code\modules\projectiles\ammunition\energy\plasma.dm"
+#include "code\modules\projectiles\ammunition\energy\plasma_cit.dm"
+#include "code\modules\projectiles\ammunition\energy\portal.dm"
+#include "code\modules\projectiles\ammunition\energy\special.dm"
+#include "code\modules\projectiles\ammunition\energy\stun.dm"
+#include "code\modules\projectiles\ammunition\special\magic.dm"
+#include "code\modules\projectiles\ammunition\special\syringe.dm"
+#include "code\modules\projectiles\boxes_magazines\_box_magazine.dm"
#include "code\modules\projectiles\boxes_magazines\ammo_boxes.dm"
-#include "code\modules\projectiles\boxes_magazines\external_mag.dm"
-#include "code\modules\projectiles\boxes_magazines\internal_mag.dm"
+#include "code\modules\projectiles\boxes_magazines\external\grenade.dm"
+#include "code\modules\projectiles\boxes_magazines\external\lmg.dm"
+#include "code\modules\projectiles\boxes_magazines\external\pistol.dm"
+#include "code\modules\projectiles\boxes_magazines\external\rechargable.dm"
+#include "code\modules\projectiles\boxes_magazines\external\rifle.dm"
+#include "code\modules\projectiles\boxes_magazines\external\shotgun.dm"
+#include "code\modules\projectiles\boxes_magazines\external\smg.dm"
+#include "code\modules\projectiles\boxes_magazines\external\sniper.dm"
+#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\grenade.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\misc.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\revolver.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\rifle.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\shotgun.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\toy.dm"
#include "code\modules\projectiles\guns\ballistic.dm"
-#include "code\modules\projectiles\guns\beam_rifle.dm"
-#include "code\modules\projectiles\guns\chem_gun.dm"
#include "code\modules\projectiles\guns\energy.dm"
-#include "code\modules\projectiles\guns\grenade_launcher.dm"
#include "code\modules\projectiles\guns\magic.dm"
-#include "code\modules\projectiles\guns\medbeam.dm"
-#include "code\modules\projectiles\guns\mounted.dm"
-#include "code\modules\projectiles\guns\syringe_gun.dm"
#include "code\modules\projectiles\guns\ballistic\automatic.dm"
#include "code\modules\projectiles\guns\ballistic\laser_gatling.dm"
#include "code\modules\projectiles\guns\ballistic\launchers.dm"
@@ -2232,21 +2260,58 @@
#include "code\modules\projectiles\guns\energy\kinetic_accelerator.dm"
#include "code\modules\projectiles\guns\energy\laser.dm"
#include "code\modules\projectiles\guns\energy\megabuster.dm"
-#include "code\modules\projectiles\guns\energy\plasma.dm"
+#include "code\modules\projectiles\guns\energy\mounted.dm"
+#include "code\modules\projectiles\guns\energy\plasma_cit.dm"
#include "code\modules\projectiles\guns\energy\pulse.dm"
#include "code\modules\projectiles\guns\energy\special.dm"
#include "code\modules\projectiles\guns\energy\stun.dm"
#include "code\modules\projectiles\guns\magic\staff.dm"
#include "code\modules\projectiles\guns\magic\wand.dm"
+#include "code\modules\projectiles\guns\misc\beam_rifle.dm"
#include "code\modules\projectiles\guns\misc\blastcannon.dm"
+#include "code\modules\projectiles\guns\misc\chem_gun.dm"
+#include "code\modules\projectiles\guns\misc\grenade_launcher.dm"
+#include "code\modules\projectiles\guns\misc\medbeam.dm"
+#include "code\modules\projectiles\guns\misc\syringe_gun.dm"
#include "code\modules\projectiles\projectile\beams.dm"
#include "code\modules\projectiles\projectile\bullets.dm"
-#include "code\modules\projectiles\projectile\energy.dm"
#include "code\modules\projectiles\projectile\magic.dm"
#include "code\modules\projectiles\projectile\megabuster.dm"
#include "code\modules\projectiles\projectile\plasma.dm"
-#include "code\modules\projectiles\projectile\reusable.dm"
-#include "code\modules\projectiles\projectile\special.dm"
+#include "code\modules\projectiles\projectile\bullets\_incendiary.dm"
+#include "code\modules\projectiles\projectile\bullets\dart_syringe.dm"
+#include "code\modules\projectiles\projectile\bullets\dnainjector.dm"
+#include "code\modules\projectiles\projectile\bullets\grenade.dm"
+#include "code\modules\projectiles\projectile\bullets\lmg.dm"
+#include "code\modules\projectiles\projectile\bullets\pistol.dm"
+#include "code\modules\projectiles\projectile\bullets\revolver.dm"
+#include "code\modules\projectiles\projectile\bullets\rifle.dm"
+#include "code\modules\projectiles\projectile\bullets\shotgun.dm"
+#include "code\modules\projectiles\projectile\bullets\smg.dm"
+#include "code\modules\projectiles\projectile\bullets\sniper.dm"
+#include "code\modules\projectiles\projectile\bullets\special.dm"
+#include "code\modules\projectiles\projectile\energy\_energy.dm"
+#include "code\modules\projectiles\projectile\energy\chameleon.dm"
+#include "code\modules\projectiles\projectile\energy\ebow.dm"
+#include "code\modules\projectiles\projectile\energy\misc.dm"
+#include "code\modules\projectiles\projectile\energy\net_snare.dm"
+#include "code\modules\projectiles\projectile\energy\stun.dm"
+#include "code\modules\projectiles\projectile\energy\tesla.dm"
+#include "code\modules\projectiles\projectile\reusable\_reusable.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"
+#include "code\modules\projectiles\projectile\special\floral.dm"
+#include "code\modules\projectiles\projectile\special\gravity.dm"
+#include "code\modules\projectiles\projectile\special\hallucination.dm"
+#include "code\modules\projectiles\projectile\special\ion.dm"
+#include "code\modules\projectiles\projectile\special\meteor.dm"
+#include "code\modules\projectiles\projectile\special\mindflayer.dm"
+#include "code\modules\projectiles\projectile\special\neurotoxin.dm"
+#include "code\modules\projectiles\projectile\special\plasma.dm"
+#include "code\modules\projectiles\projectile\special\rocket.dm"
+#include "code\modules\projectiles\projectile\special\temperature.dm"
+#include "code\modules\projectiles\projectile\special\wormhole.dm"
#include "code\modules\reagents\chem_splash.dm"
#include "code\modules\reagents\reagent_containers.dm"
#include "code\modules\reagents\reagent_dispenser.dm"