From db9d18e817b9f7202fa45f881821f5d7bb07dfe1 Mon Sep 17 00:00:00 2001 From: Patrick Meade Date: Sat, 10 Jan 2026 20:48:41 -0600 Subject: [PATCH 01/11] Created shell for Gun Lore module --- code/__DEFINES/__starfly.dm | 1 + code/__DEFINES/guns.dm | 12 ++++++ modular_starfly/modules/gun_lore/README.md | 40 ++++++++++++++++++++ modular_starfly/modules/gun_lore/_defines.dm | 30 +++++++++++++++ shiptest.dme | 1 + tgui/packages/tgui/starfly.ts | 1 + 6 files changed, 85 insertions(+) create mode 100644 modular_starfly/modules/gun_lore/README.md create mode 100644 modular_starfly/modules/gun_lore/_defines.dm diff --git a/code/__DEFINES/__starfly.dm b/code/__DEFINES/__starfly.dm index 5c49118a1c..8bac6b67be 100644 --- a/code/__DEFINES/__starfly.dm +++ b/code/__DEFINES/__starfly.dm @@ -38,6 +38,7 @@ #define STARFLY13_MODULE_ADMIN_VERB_FREEZE_ENABLED #define STARFLY13_MODULE_CRYOSLEEP_SYMPTOMS_FLAG_ENABLED +#define STARFLY13_MODULE_GUN_LORE_ENABLED #define STARFLY13_MODULE_OXYGEN_DAMAGE_MOD_ENABLED #define STARFLY13_MODULE_PATCH_UPSTREAM_ENABLED #define STARFLY13_MODULE_RKSH_UNATHI_ENABLED diff --git a/code/__DEFINES/guns.dm b/code/__DEFINES/guns.dm index d1a0da6c9d..e579d1f0aa 100644 --- a/code/__DEFINES/guns.dm +++ b/code/__DEFINES/guns.dm @@ -56,7 +56,19 @@ #define MANUFACTURER_SHARPLITE "the Sharplite Defense logo" #define MANUFACTURER_SHARPLITE_NEW "the Nanotrasen-Sharplite logo" #define MANUFACTURER_HUNTERSPRIDE "the Hunter's Pride Arms and Ammunition logo" +//--------------------------------------------------------------------------------------------------------------------- +// STARFLY EDIT - CHANGE BEGIN +#ifndef STARFLY13_MODULE_GUN_LORE_ENABLED +//--------------------------------------------------------------------------------------------------------------------- #define MANUFACTURER_SOLARARMORIES "the Solarbundswaffenkammer emblem" +//--------------------------------------------------------------------------------------------------------------------- +#else +//--------------------------------------------------------------------------------------------------------------------- +#define MANUFACTURER_SOLARARMORIES "the Solarian emblem" +//--------------------------------------------------------------------------------------------------------------------- +#endif // #ifndef STARFLY13_MODULE_GUN_LORE_ENABLED +// STARFLY EDIT - CHANGE END +//--------------------------------------------------------------------------------------------------------------------- #define MANUFACTURER_SCARBOROUGH "the Scarborough Arms logo" #define MANUFACTURER_EOEHOMA "the Eoehoma Firearms emblem" #define MANUFACTURER_NANOTRASEN_OLD "an outdated Nanotrasen logo" diff --git a/modular_starfly/modules/gun_lore/README.md b/modular_starfly/modules/gun_lore/README.md new file mode 100644 index 0000000000..640a033bd0 --- /dev/null +++ b/modular_starfly/modules/gun_lore/README.md @@ -0,0 +1,40 @@ +# Starfly-13 Gun Lore + +Module ID: `GUN_LORE` + +## Description + +Updates weapon availability in the marketplace, introduces new manufacturers, adds weapons, reassigns weapons, big lore changes all around. + +## TG Proc/File Changes + +- N/A + + +## Modular Overrides + +- N/A + + +## Defines + +- N/A + + +## Included files that are not contained in this module + +- N/A + + +## Credits + +- Patrick Meade created this module. +- LectroNyx is the original author of the content of this module. +- Most of this content first appeared here: https://github.com/Starfly-13/STARFLY-13/pull/55 diff --git a/modular_starfly/modules/gun_lore/_defines.dm b/modular_starfly/modules/gun_lore/_defines.dm new file mode 100644 index 0000000000..9395705bee --- /dev/null +++ b/modular_starfly/modules/gun_lore/_defines.dm @@ -0,0 +1,30 @@ +#ifdef STARFLY13_MODULE_GUN_LORE_ENABLED +//--------------------------------------------------------------------------------------------------------------------- + +// _defines.dm +// Copyright 2024 LectroNyx. +// Copyright 2026 Patrick Meade. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +//--------------------------------------------------------------------------- + +// See also: code/__DEFINES/guns.dm + +#define MANUFACTURER_ROSEUS "the Roseus Galactic logo" +#define MANUFACTURER_ADHOMAI "a Tajaran emblem" +#define MANUFACTURER_LAKVAR "the letters LKV" +#define MANUFACTURER_HEPHAESTUS "the Hephaestus Industries logo" + +//--------------------------------------------------------------------------------------------------------------------- +#endif // #ifdef STARFLY13_MODULE_GUN_LORE_ENABLED diff --git a/shiptest.dme b/shiptest.dme index 688b8a92bf..a67274acac 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -3790,6 +3790,7 @@ #include "modular_starfly\modules\admin_verb_freeze\overlays.dm" #include "modular_starfly\modules\cryosleep_symptoms\cryopod.dm" #include "modular_starfly\modules\cryosleep_symptoms\game_options.dm" +#include "modular_starfly\modules\gun_lore\_defines.dm" #include "modular_starfly\modules\rksh_unathi\_defines.dm" #include "modular_starfly\modules\rksh_unathi\lizardpeople.dm" #include "modular_starfly\modules\roseus_galactic\syndicate.dm" diff --git a/tgui/packages/tgui/starfly.ts b/tgui/packages/tgui/starfly.ts index f996d83bba..54b769c565 100644 --- a/tgui/packages/tgui/starfly.ts +++ b/tgui/packages/tgui/starfly.ts @@ -25,6 +25,7 @@ export const STARFLY13 = { MODULE_ADMIN_VERB_FREEZE_ENABLED: true, MODULE_CRYOSLEEP_SYMPTOMS_FLAG_ENABLED: true, + MODULE_GUN_LORE_ENABLED: true, MODULE_OXYGEN_DAMAGE_MOD_ENABLED: true, MODULE_PATCH_UPSTREAM_ENABLED: true, MODULE_RKSH_UNATHI_ENABLED: true, From abb80c4fe68803311f962e26f789d4555cbc96aa Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 1 Feb 2026 00:51:31 -0500 Subject: [PATCH 02/11] Fixes typo, extends to-do list. I'm great at making orders and kind of bad at following through, huh. --- modular_starfly/NOTES.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modular_starfly/NOTES.md b/modular_starfly/NOTES.md index a225b94b53..98a8dad66b 100644 --- a/modular_starfly/NOTES.md +++ b/modular_starfly/NOTES.md @@ -4,7 +4,7 @@ Some notes about the current state of modularization of previous STARFLY-13 cont ## More Needs -More things that we'll need to create to suport Modular Starfly +More things that we'll need to create to support Modular Starfly - [ ] Create a CI action to compile shiptest.dmb with each module enabled/disabled - [ ] Create a CI action to compare `code/__DEFINES/__STARFLY/__modules.dm` with `tgui/packages/tgui/starfly.ts` @@ -56,3 +56,8 @@ Upstream changes we don't intend to modularize - [-] Create MAGIC module (https://github.com/shiptest-ss13/Shiptest/pull/2877) - [-] Create MUTATION_TOXIN module (https://github.com/shiptest-ss13/Shiptest/pull/2659) + +## Further Ports +Changes to sister servers that we would like to copy/influence our own. + +- [-] MODS touch-up! (https://github.com/PentestSS13/Pentest/pull/443) \ No newline at end of file From 73a1b17bbd4fc06e0a00ffb023c8f488217db116 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 00:46:34 -0400 Subject: [PATCH 03/11] restores Donk! Co. manufacturer for toy guns --- modular_starfly/modules/gun_lore/_defines.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/modular_starfly/modules/gun_lore/_defines.dm b/modular_starfly/modules/gun_lore/_defines.dm index 9395705bee..542fb4493c 100644 --- a/modular_starfly/modules/gun_lore/_defines.dm +++ b/modular_starfly/modules/gun_lore/_defines.dm @@ -24,6 +24,7 @@ #define MANUFACTURER_ROSEUS "the Roseus Galactic logo" #define MANUFACTURER_ADHOMAI "a Tajaran emblem" #define MANUFACTURER_LAKVAR "the letters LKV" +#define MANUFACTURER_DONKCO "the Donk! Co. logo" #define MANUFACTURER_HEPHAESTUS "the Hephaestus Industries logo" //--------------------------------------------------------------------------------------------------------------------- From afb1b6abb3722935a8fca08fff210d03855736a2 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 00:49:45 -0400 Subject: [PATCH 04/11] old ammo.dmi for any missing icons good for old ammo types that simply no longer exist, like 762_40 --- .../modules/gun_lore/icons/obj/ammo.dmi | Bin 0 -> 51572 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 modular_starfly/modules/gun_lore/icons/obj/ammo.dmi diff --git a/modular_starfly/modules/gun_lore/icons/obj/ammo.dmi b/modular_starfly/modules/gun_lore/icons/obj/ammo.dmi new file mode 100644 index 0000000000000000000000000000000000000000..4f2f029a96b7f599792b1222db4819dd48052d92 GIT binary patch literal 51572 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%0AaNz8f#Ufrt50ue6!!jkE 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 Date: Sun, 19 Apr 2026 01:01:09 -0400 Subject: [PATCH 05/11] copying over the old stuff, securing toys as donk co --- .../boxes_magazines/_box_magazine.dm | 268 ++++ .../boxes_magazines/ammo_loaders.dm | 188 +++ .../ammo_stacks/_ammo_stack.dm | 82 ++ .../prefab_stacks/_premade_stacks.dm | 28 + .../prefab_stacks/premade_gauss_stacks.dm | 81 ++ .../prefab_stacks/premade_lmg_stacks.dm | 12 + .../prefab_stacks/premade_misc_stacks.dm | 24 + .../prefab_stacks/premade_pistol_stacks.dm | 479 +++++++ .../prefab_stacks/premade_rifle_stacks.dm | 224 +++ .../prefab_stacks/premade_shotshell_stacks.dm | 100 ++ .../prefab_stacks/premade_smg_stacks.dm | 187 +++ .../prefab_stacks/premade_sniper_stacks.dm | 96 ++ .../boxes_magazines/external/gauss.dm | 39 + .../boxes_magazines/external/lmg.dm | 0 .../boxes_magazines/external/pistol.dm | 80 ++ .../boxes_magazines/external/rechargable.dm | 19 + .../boxes_magazines/external/rifle.dm | 109 ++ .../boxes_magazines/external/shotgun.dm | 18 + .../boxes_magazines/external/smg.dm | 67 + .../boxes_magazines/external/sniper.dm | 22 + .../boxes_magazines/external/toy.dm | 30 + .../boxes_magazines/internal/_cylinder.dm | 96 ++ .../boxes_magazines/internal/_internal.dm | 4 + .../boxes_magazines/internal/derringer.dm | 13 + .../boxes_magazines/internal/gauss.dm | 6 + .../boxes_magazines/internal/grenade.dm | 23 + .../boxes_magazines/internal/misc.dm | 6 + .../boxes_magazines/internal/revolver.dm | 45 + .../boxes_magazines/internal/rifle.dm | 34 + .../boxes_magazines/internal/shotgun.dm | 84 ++ .../boxes_magazines/internal/toy.dm | 7 + .../gun_lore/code/modules/projectiles/gun.dm | 1227 ++++++++++++++++ .../modules/projectiles/guns/ballistic.dm | 465 ++++++ .../projectiles/guns/ballistic/assault.dm | 166 +++ .../projectiles/guns/ballistic/automatic.dm | 53 + .../guns/ballistic/blast_hammer.dm | 180 +++ .../projectiles/guns/ballistic/gauss.dm | 35 + .../modules/projectiles/guns/ballistic/hmg.dm | 177 +++ .../projectiles/guns/ballistic/launchers.dm | 147 ++ .../projectiles/guns/ballistic/marksman.dm | 15 + .../projectiles/guns/ballistic/pistol.dm | 189 +++ .../projectiles/guns/ballistic/revolver.dm | 486 +++++++ .../projectiles/guns/ballistic/rifle.dm | 100 ++ .../projectiles/guns/ballistic/shotgun.dm | 263 ++++ .../modules/projectiles/guns/ballistic/smg.dm | 170 +++ .../modules/projectiles/guns/ballistic/toy.dm | 101 ++ .../code/modules/projectiles/guns/energy.dm | 448 ++++++ .../projectiles/guns/energy/dueling.dm | 346 +++++ .../projectiles/guns/energy/energy_gun.dm | 60 + .../guns/energy/kinetic_accelerator.dm | 656 +++++++++ .../modules/projectiles/guns/energy/laser.dm | 206 +++ .../projectiles/guns/energy/laser_gatling.dm | 177 +++ .../projectiles/guns/energy/mounted.dm | 27 + .../modules/projectiles/guns/energy/pulse.dm | 95 ++ .../projectiles/guns/energy/special.dm | 447 ++++++ .../modules/projectiles/guns/energy/stun.dm | 46 + .../code/modules/projectiles/guns/gunhud.dm | 340 +++++ .../clip_lanchester/ballistics.dm | 809 +++++++++++ .../manufacturer/clip_lanchester/lasers.dm | 68 + .../guns/manufacturer/eoehoma/ballistics.dm | 229 +++ .../guns/manufacturer/eoehoma/lasers.dm | 90 ++ .../manufacturer/etherbor/energy_gunsword.dm | 393 +++++ .../frontier_import/ballistics.dm | 595 ++++++++ .../manufacturer/frontier_import/lasers.dm | 44 + .../manufacturer/hunter_pride/ballistics.dm | 1259 +++++++++++++++++ .../nanotrasen_sharplite/ballistics.dm | 494 +++++++ .../nanotrasen_sharplite/lasers.dm | 520 +++++++ .../manufacturer/scarborough/ballistics.dm | 1124 +++++++++++++++ .../serene_sporting/ballistics.dm | 442 ++++++ .../manufacturer/solar_armories/ballistic.dm | 213 +++ .../projectiles/guns/misc/beam_rifle.dm | 543 +++++++ .../projectiles/guns/misc/blastcannon.dm | 162 +++ .../code/modules/projectiles/guns/misc/bow.dm | 108 ++ .../modules/projectiles/guns/misc/chem_gun.dm | 45 + .../projectiles/guns/misc/grenade_launcher.dm | 61 + .../modules/projectiles/guns/misc/medbeam.dm | 158 +++ .../projectiles/guns/misc/syringe_gun.dm | 124 ++ .../code/modules/projectiles/guns/powered.dm | 110 ++ .../code/modules/projectiles/projectile.dm | 1049 ++++++++++++++ .../modules/projectiles/projectile/beams.dm | 427 ++++++ .../modules/projectiles/projectile/bullets.dm | 35 + .../projectile/bullets/_incendiary.dm | 17 + .../projectile/bullets/dart_syringe.dm | 46 + .../projectile/bullets/dnainjector.dm | 25 + .../projectiles/projectile/bullets/gauss.dm | 63 + .../projectiles/projectile/bullets/grenade.dm | 12 + .../projectiles/projectile/bullets/lmg.dm | 103 ++ .../projectiles/projectile/bullets/pistol.dm | 137 ++ .../projectile/bullets/revolver.dm | 197 +++ .../projectiles/projectile/bullets/rifle.dm | 129 ++ .../projectiles/projectile/bullets/shotgun.dm | 133 ++ .../projectiles/projectile/bullets/smg.dm | 102 ++ .../projectiles/projectile/bullets/sniper.dm | 101 ++ .../projectiles/projectile/bullets/turret.dm | 12 + .../projectiles/projectile/energy/_energy.dm | 7 + .../projectiles/projectile/energy/ebow.dm | 18 + .../projectiles/projectile/energy/misc.dm | 59 + .../projectile/energy/net_snare.dm | 97 ++ .../projectile/energy/nuclear_particle.dm | 60 + .../projectiles/projectile/energy/stun.dm | 23 + .../projectiles/projectile/energy/tesla.dm | 50 + .../projectile/reusable/_reusable.dm | 20 + .../projectiles/projectile/reusable/arrow.dm | 32 + .../projectile/reusable/foam_dart.dm | 44 + .../projectiles/projectile/special/curse.dm | 72 + .../projectiles/projectile/special/floral.dm | 23 + .../projectiles/projectile/special/gravity.dm | 102 ++ .../projectile/special/hallucination.dm | 230 +++ .../projectiles/projectile/special/ion.dm | 17 + .../projectiles/projectile/special/meteor.dm | 22 + .../projectile/special/mindflayer.dm | 9 + .../projectile/special/neurotoxin.dm | 13 + .../projectiles/projectile/special/plasma.dm | 29 + .../projectiles/projectile/special/rocket.dm | 62 + .../projectile/special/temperature.dm | 44 + .../projectile/special/wormhole.dm | 31 + 116 files changed, 20336 insertions(+) create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/_box_magazine.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_loaders.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/_ammo_stack.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/_premade_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_gauss_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_lmg_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_misc_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_pistol_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_rifle_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_shotshell_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_smg_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_sniper_stacks.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/gauss.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/lmg.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/pistol.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rechargable.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rifle.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/shotgun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/smg.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/sniper.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/toy.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_internal.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/derringer.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/gauss.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/grenade.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/misc.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/revolver.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/rifle.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/shotgun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/toy.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/gun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/assault.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/automatic.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/blast_hammer.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/gauss.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/hmg.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/launchers.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/marksman.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/pistol.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/revolver.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/rifle.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/shotgun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/smg.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/toy.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/dueling.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/energy_gun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/kinetic_accelerator.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser_gatling.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/mounted.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/pulse.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/special.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/stun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/gunhud.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/lasers.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/lasers.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/lasers.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/beam_rifle.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/blastcannon.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/bow.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/grenade_launcher.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/guns/powered.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/beams.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/_incendiary.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dart_syringe.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dnainjector.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/gauss.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/grenade.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/lmg.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/pistol.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/revolver.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/rifle.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/shotgun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/smg.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/sniper.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/turret.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/_energy.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/ebow.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/misc.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/net_snare.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/nuclear_particle.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/stun.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/tesla.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/_reusable.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/arrow.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/foam_dart.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/curse.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/floral.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/gravity.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/hallucination.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/ion.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/meteor.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/mindflayer.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/neurotoxin.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/plasma.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/rocket.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/temperature.dm create mode 100644 modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/wormhole.dm diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/_box_magazine.dm new file mode 100644 index 0000000000..3cd86e9431 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/_box_magazine.dm @@ -0,0 +1,268 @@ +//TODO: make this code more readable. weird var names, convoluted logic, etc + +//Boxes of ammo +/obj/item/ammo_box + name = "ammo box (null_reference_exception)" + desc = "A box of ammo." + icon = 'icons/obj/ammunition/ammo.dmi' + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BELT + item_state = "syringe_kit" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + custom_materials = list(/datum/material/iron = 15000) + throwforce = 2 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 7 + ///list containing the actual ammo within the magazine + var/list/stored_ammo = list() + ///type that the magazine will be searching for, rejects if not a subtype of + var/ammo_type = /obj/item/ammo_casing + ///maximum amount of ammo in the magazine + var/max_ammo = 7 + ///Controls how sprites are updated for the ammo box; see defines in combat.dm: AMMO_BOX_ONE_SPRITE; AMMO_BOX_PER_BULLET; AMMO_BOX_FULL_EMPTY + var/multiple_sprites = AMMO_BOX_ONE_SPRITE + ///String, used for checking if ammo of different types but still fits can fit inside it; generally used for magazines + var/caliber + ///Allows multiple bullets to be loaded in from one click of another box/magazine + var/multiload = FALSE + ///Whether or not an ammo box skips the do_after process (e.g. speedloaders) + var/instant_load = FALSE + ///Whether the magazine should start with nothing in it + var/start_empty = FALSE + ///cost of all the bullets in the magazine/box + var/list/bullet_cost + ///cost of the materials in the magazine/box itself + var/list/base_cost + +/obj/item/ammo_box/Initialize(mapload, spawn_empty) + . = ..() + if(spawn_empty) + start_empty = TRUE + if(!base_icon_state) + base_icon_state = icon_state + + if(!bullet_cost) + for (var/material in custom_materials) + var/material_amount = custom_materials[material] + LAZYSET(base_cost, material, (material_amount * 0.10)) + + material_amount *= 0.90 // 10% for the container + material_amount /= max_ammo + LAZYSET(bullet_cost, material, material_amount). + + if(!start_empty) + top_off(starting = TRUE) + + update_appearance() + +/* + * top_off is used to refill the magazine to max, in case you want to increase the size of a magazine with VV then refill it at once + * Arguments: + * load_type - if you want to specify a specific ammo casing type to load, enter the path here, otherwise it'll use the basic [/obj/item/ammo_box/var/ammo_type]. Must be a compatible round + * starting - Relevant for revolver cylinders, if FALSE then we mind the nulls that represent the empty cylinders (since those nulls don't exist yet if we haven't initialized when this is TRUE) + * amount - the amount of bullets we're putting in the mag. Otherwise fill it to full if unspecified + */ +/obj/item/ammo_box/proc/top_off(load_type, starting=FALSE, amount) + if(!load_type) //this check comes first so not defining an argument means we just go with default ammo + load_type = ammo_type + + var/obj/item/ammo_casing/round_check = load_type + if(!starting && (caliber && initial(round_check.caliber) != caliber) || (!caliber && load_type != ammo_type)) + stack_trace("Tried loading unsupported ammocasing type [load_type] into ammo box [type].") + return + + var/num_to_load = max_ammo + if(amount) + num_to_load = amount + + for(var/i = max(1, stored_ammo.len), i <= num_to_load, i++) + stored_ammo += new round_check(src) + +/obj/item/ammo_box/Destroy() + stored_ammo.Cut() + return ..() + +///gets a round from the magazine, if keep is TRUE the round will stay in the gun +/obj/item/ammo_box/proc/get_round(keep = FALSE) + 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 + +///puts a round into the magazine +/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = FALSE) + // 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 FALSE + + if(stored_ammo.len < max_ammo) + stored_ammo += R + R.forceMove(src) + return TRUE + + //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 TRUE + return FALSE + +///Whether or not the box can be loaded, used in overrides +/obj/item/ammo_box/proc/can_load(mob/user) + return TRUE + +/obj/item/ammo_box/attackby(obj/item/attacking_obj, mob/user, params, silent = FALSE, replace_spent = FALSE) + var/num_loaded = 0 + + if(!can_load(user)) + return + + if(istype(attacking_obj, /obj/item/ammo_box)) + var/obj/item/ammo_box/attacking_box = attacking_obj + for(var/obj/item/ammo_casing/casing_to_insert in attacking_box.stored_ammo) + if(!((instant_load && attacking_box.instant_load) || (stored_ammo.len >= max_ammo) || istype(attacking_obj, /obj/item/ammo_box/magazine/ammo_stack) && do_after(user, 0.5 SECONDS, attacking_box, timed_action_flags = IGNORE_USER_LOC_CHANGE))) + break + var/did_load = give_round(casing_to_insert, replace_spent) + if(!did_load) + break + attacking_box.stored_ammo -= casing_to_insert + if(!silent) + playsound(get_turf(attacking_box), 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) //src is nullspaced, which means internal magazines won't properly play sound, thus we use attacking_box + num_loaded++ + attacking_box.update_ammo_count() + update_ammo_count() + + if(istype(attacking_obj, /obj/item/ammo_casing)) + var/obj/item/ammo_casing/casing_to_insert = attacking_obj + if(give_round(casing_to_insert, replace_spent)) + user.transferItemToLoc(casing_to_insert, src, TRUE) + num_loaded++ + casing_to_insert.update_appearance() + update_ammo_count() + + if(num_loaded) + if(!silent) + to_chat(user, span_notice("You load [num_loaded] cartridge\s into \the [src]!")) + playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) + return num_loaded + +/obj/item/ammo_box/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + var/num_loaded = 0 + var/obj/item/storage/belt/bandolier/to_load + if(istype(target,/obj/item/storage/belt/bandolier)) + to_load = target + var/datum/component/storage/storage_to_load = to_load.GetComponent(/datum/component/storage) + for(var/obj/item/ammo_casing/casing_to_insert in stored_ammo) + if(!((to_load.contents.len >= storage_to_load.get_max_volume()) || do_after(user, 0.5 SECONDS, src))) + break + if(!storage_to_load.can_be_inserted(casing_to_insert,TRUE,user)) + break + storage_to_load.handle_item_insertion(casing_to_insert,TRUE,user) + stored_ammo -= casing_to_insert + playsound(get_turf(src), 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) + num_loaded++ + update_ammo_count() + if(num_loaded) + to_chat(user, span_notice("You load [num_loaded] cartridge\s into \the [to_load]!")) + return + +/obj/item/ammo_box/attack_self(mob/user) + var/obj/item/ammo_casing/A = get_round() + if(!A) + return + + A.forceMove(drop_location()) + var/mob/living/carbon/human/H = user + if(!(user.is_holding(src) || H.l_store == src || H.r_store == src) || !user.put_in_hands(A)) //incase they're using TK + A.bounce_away(FALSE, NONE) + playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) + to_chat(user, span_notice("You remove a round from [src]!")) + update_ammo_count() + +/// Updates the materials and appearance of this ammo box +/obj/item/ammo_box/proc/update_ammo_count() + update_custom_materials() + update_appearance() + +/obj/item/ammo_box/update_desc(updates) + . = ..() + var/shells_left = LAZYLEN(stored_ammo) + desc = "[initial(desc)] There [(shells_left == 1) ? "is" : "are"] [shells_left] shell\s left!" + +/obj/item/ammo_box/update_icon_state() + var/shells_left = LAZYLEN(stored_ammo) + switch(multiple_sprites) + if(AMMO_BOX_PER_BULLET) + icon_state = "[base_icon_state]-[shells_left]" + if(AMMO_BOX_FULL_EMPTY) + icon_state = "[base_icon_state]-[shells_left ? "1" : "0"]" + return ..() + +/// Updates the amount of material in this ammo box according to how many bullets are left in it. +/obj/item/ammo_box/proc/update_custom_materials() + var/temp_materials = custom_materials.Copy() + for(var/material in bullet_cost) + temp_materials[material] = (bullet_cost[material] * stored_ammo.len) + base_cost[material] + set_custom_materials(temp_materials) + +/obj/item/ammo_box/AltClick(mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if((user.is_holding(src) ||H.l_store == src || H.r_store == src) && !(caliber || istype(src, /obj/item/ammo_box/magazine) || instant_load)) //caliber because boxes have none, instant load because speedloaders use the base ammo box type with instant load on, and magazine for the obvious. + attack_self(user) + return + ..() + +/obj/item/ammo_box/examine(mob/user) + . = ..() + if(!(caliber || istype(src, /obj/item/ammo_box/magazine) || instant_load)) + . += "Alt-click on [src] while it in a pocket or your off-hand to take out a round while it is there." + +/obj/item/ammo_box/fire_act(exposed_temperature, exposed_volume) + . = ..() + for(var/obj/item/ammo_casing/bullet2pop in stored_ammo) + bullet2pop.fire_act() + +/obj/item/ammo_box/magazine + w_class = WEIGHT_CLASS_SMALL //Default magazine weight, only differs for tiny mags and drums + +///Count of number of bullets in the magazine +/obj/item/ammo_box/magazine/proc/ammo_count(countempties = TRUE) + var/boolets = 0 + for(var/obj/item/ammo_casing/bullet in stored_ammo) + if(bullet && (bullet.BB || countempties)) + boolets++ + return boolets + +///list of every bullet in the magazine +/obj/item/ammo_box/magazine/proc/ammo_list(drop_list = FALSE) + var/list/L = stored_ammo.Copy() + if(drop_list) + stored_ammo.Cut() + update_ammo_count() + return L + +///drops the entire contents of the magazine on the floor +/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 + update_ammo_count() + +/obj/item/ammo_box/magazine/handle_atom_del(atom/A) + stored_ammo -= A + update_ammo_count() + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_loaders.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_loaders.dm new file mode 100644 index 0000000000..90efc57073 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_loaders.dm @@ -0,0 +1,188 @@ +// .357 Speed Loaders + +/obj/item/ammo_box/a357 + name = "speed loader (.357)" + desc = "A 6-round speed loader for quickly reloading .357 revolvers. These rounds do good damage with average performance against armor." + icon_state = "speedloader_357-6" + base_icon_state = "speedloader_357" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 6 + multiple_sprites = AMMO_BOX_PER_BULLET + item_flags = NO_MAT_REDEMPTION + w_class = WEIGHT_CLASS_TINY + instant_load = TRUE + +/obj/item/ammo_box/a357/empty + start_empty = TRUE + +/obj/item/ammo_box/a357/match + name = "speed loader (.357 match)" + desc = "A 6-round speed loader for quickly reloading .357 revolvers. These match rounds travel faster, perform better against armor, and can ricochet off targets." + ammo_type = /obj/item/ammo_casing/a357/match + +/obj/item/ammo_box/a357/hp + name = "speed loader (.357 hollow point)" + desc = "A 6-round speed loader for quickly reloading .357 revolvers. These hollow point rounds do incredible damage against soft targets, but are nearly ineffective against armored ones." + ammo_type = /obj/item/ammo_casing/a357/hp + +// .38 special Speed Loaders + +/obj/item/ammo_box/c38 + name = "speed loader (.38 special)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These rounds do okay damage, but struggle against armor." + icon_state = "speedloader_38-6" + base_icon_state = "speedloader_38" + ammo_type = /obj/item/ammo_casing/c38 + caliber = ".38" + max_ammo = 6 + multiple_sprites = AMMO_BOX_PER_BULLET + custom_materials = list(/datum/material/iron = 15000) + w_class = WEIGHT_CLASS_TINY + instant_load = TRUE + +/obj/item/ammo_box/c38/empty + start_empty = TRUE + +/obj/item/ammo_box/c38/trac + name = "speed loader (.38 TRAC)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These TRAC rounds do pitiful damage, but embed a tracking device in targets hit." + ammo_type = /obj/item/ammo_casing/c38/trac + +/obj/item/ammo_box/c38/match + name = "speed loader (.38 match)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These match rounds travel faster, perform better against armor, and can ricochet off targets." + ammo_type = /obj/item/ammo_casing/c38/match + +/obj/item/ammo_box/c38/match/bouncy + name = "speed loader (.38 rubber)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These rounds are incredibly bouncy and MOSTLY nonlethal, making them great to show off trickshots with." + ammo_type = /obj/item/ammo_casing/c38/match/bouncy + +/obj/item/ammo_box/c38/dumdum + name = "speed loader (.38 dum-dum)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These dum-dum bullets shatter on impact and embed in the target's innards. However, they're nearly ineffective against armor and do okay damage." + ammo_type = /obj/item/ammo_casing/c38/dumdum + +/obj/item/ammo_box/c38/hotshot + name = "speed loader (.38 hot shot)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These hot shot bullets contain an incendiary payload that set targets alight." + ammo_type = /obj/item/ammo_casing/c38/hotshot + +/obj/item/ammo_box/c38/iceblox + name = "speed loader (.38 iceblox)" + desc = "A 6-round speed loader for quickly reloading .38 special revolvers. These iceblox bullets contain a cryogenic payload that chills targets." + ammo_type = /obj/item/ammo_casing/c38/iceblox + +/obj/item/ammo_box/c38/empty + start_empty = TRUE + +// 8x58mm Stripper Clip + +/obj/item/ammo_box/a858 + name = "stripper clip (8x58mm)" + desc = "A 5-round stripper clip for the SSG-669C rifle. These rounds do good damage with significant armor penetration." + icon_state = "enbloc_858" + ammo_type = /obj/item/ammo_casing/caseless/a858 + max_ammo = 5 + multiple_sprites = AMMO_BOX_PER_BULLET + instant_load = TRUE + custom_materials = list(/datum/material/iron = 500) + +/obj/item/ammo_box/a858/empty + start_empty = TRUE + +//8x50mmR Stripper Clip + +/obj/item/ammo_box/vickland_a8_50r + name = "stripper clip (8x50mmR)" + desc = "An 5-round stripper clip for the Vickland battle rifle. These rounds do good damage with significant armor penetration." + icon_state = "850-5" + base_icon_state = "850" + ammo_type = /obj/item/ammo_casing/a8_50r + caliber = "8x50mmR" + max_ammo = 5 + multiple_sprites = AMMO_BOX_PER_BULLET + w_class = WEIGHT_CLASS_TINY + instant_load = TRUE + custom_materials = list(/datum/material/iron = 500) + +/obj/item/ammo_box/vickland_a8_50r/empty + start_empty = TRUE + +// .300 Magnum Stripper Clip + +/obj/item/ammo_box/a300 + name = "stripper clip (.300 Magnum)" + desc = "A 5-round stripper clip for the Scout Rifle. These rounds do great damage with significant armor penetration." + icon_state = "300m" + ammo_type = /obj/item/ammo_casing/a300 + caliber = "a300" + max_ammo = 5 + multiple_sprites = AMMO_BOX_PER_BULLET + w_class = WEIGHT_CLASS_TINY + instant_load = TRUE + custom_materials = list(/datum/material/iron = 500) + +/obj/item/ammo_box/a300/empty + start_empty = TRUE + +// .300 Blackout Stripper Clip + +/obj/item/ammo_box/a762_stripper + name = "stripper clip (7.62)" + desc = "A 5-round stripper clip for makeshift bolt-action rifles. These rounds do good damage with good armor penetration." + icon_state = "300m-5" + base_icon_state = "300m" + ammo_type = /obj/item/ammo_casing/a762_40 + caliber = "7.62x40mm" + max_ammo = 5 + multiple_sprites = AMMO_BOX_PER_BULLET + w_class = WEIGHT_CLASS_TINY + instant_load = TRUE + custom_materials = list(/datum/material/iron = 500) + +/obj/item/ammo_box/a762_stripper/empty + start_empty = TRUE + +// Ferromagnetic Pellet Speed Loader + +/obj/item/ammo_box/amagpellet_claris + name = "\improper Claris speed loader (ferromagnetic pellet)" + desc = "A 22-round speed loader for quickly reloading the Claris rifle. Ferromagnetic pellets do okay damage with significant armor penetration." + icon_state = "claris-sl-1" + base_icon_state = "claris-sl" + ammo_type = /obj/item/ammo_casing/caseless/gauss + max_ammo = 22 + multiple_sprites = AMMO_BOX_FULL_EMPTY + item_flags = NO_MAT_REDEMPTION + instant_load = TRUE + +/obj/item/ammo_box/amagpellet_claris/empty + start_empty = TRUE + +/obj/item/ammo_box/a40mm + name = "ammo box (40mm grenades)" + icon_state = "40mm" + ammo_type = /obj/item/ammo_casing/a40mm + max_ammo = 4 + multiple_sprites = AMMO_BOX_PER_BULLET + w_class = WEIGHT_CLASS_NORMAL + +// .44 Roumain speedloader + +/obj/item/ammo_box/a44roum_speedloader + name = "speed loader (.44)" + desc = "Designed to quickly reload revolvers." + icon_state = "speedloader_38-6" + base_icon_state = "speedloader_38" + ammo_type = /obj/item/ammo_casing/a44roum + caliber = ".44 Roumain" + max_ammo = 6 + multiple_sprites = AMMO_BOX_PER_BULLET + custom_materials = list(/datum/material/iron = 15000) + w_class = WEIGHT_CLASS_TINY + instant_load = TRUE + +/obj/item/ammo_box/a44roum_speedloader/empty + start_empty = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/_ammo_stack.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/_ammo_stack.dm new file mode 100644 index 0000000000..f1e84780cb --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/_ammo_stack.dm @@ -0,0 +1,82 @@ +/** + * The ammo stack object itself, making this a magazine was the easiest way to handle it + * Practically every casing type needs an associated ammo stack type, because that was the easiest + * way for me to handle it. + */ +/obj/item/ammo_box/magazine/ammo_stack + name = "ammo stack" + desc = "A pile of live rounds." + icon = 'icons/obj/ammunition/ammo_bullets.dmi' + icon_state = "pistol-brass" + base_icon_state = "pistol-brass" + item_flags = NO_PIXEL_RANDOM_DROP + multiple_sprites = AMMO_BOX_ONE_SPRITE + multiload = FALSE + start_empty = TRUE + max_ammo = 12 + +/obj/item/ammo_box/magazine/ammo_stack/update_icon(updates) + icon = initial(icon) + cut_overlays() + return ..() + +/obj/item/ammo_box/magazine/ammo_stack/update_icon_state() + . = ..() + cut_overlays() + icon_state = "" + for(var/casing in stored_ammo) + var/image/bullet = image(initial(icon), src, "[base_icon_state]") + bullet.pixel_x = rand(-8, 8) + bullet.pixel_y = rand(-8, 8) + bullet.transform = bullet.transform.Turn(round(45 * rand(0, 32) / 2)) //this is the equation Eris uses on their bullet stacks + add_overlay(bullet) + return UPDATE_ICON_STATE | UPDATE_OVERLAYS + +/obj/item/ammo_box/magazine/ammo_stack/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + var/loc_before_del = loc + while(LAZYLEN(stored_ammo)) + var/obj/item/ammo = get_round(FALSE) + ammo.forceMove(loc_before_del) + ammo.throw_at(loc_before_del) + update_ammo_count() + +/obj/item/ammo_box/magazine/ammo_stack/update_ammo_count() + . = ..() + check_for_del() + +/obj/item/ammo_box/magazine/ammo_stack/proc/check_for_del() + . = FALSE + if((ammo_count() <= 0) && !QDELETED(src)) + qdel(src) + return + +/obj/item/ammo_box/magazine/ammo_stack/attackby(obj/item/handful, mob/user, params, silent = FALSE, replace_spent = 0) + var/num_loaded = 0 + if(!can_load(user)) + return + + if(istype(handful, /obj/item/ammo_box)) + var/obj/item/ammo_box/ammo_box = handful + for(var/obj/item/ammo_casing/casing in ammo_box.stored_ammo) + var/did_load = give_round(casing, replace_spent) + if(did_load) + ammo_box.stored_ammo -= casing + num_loaded++ + if(!did_load || !multiload) + break + if(num_loaded) + ammo_box.update_ammo_count() + + if(istype(handful, /obj/item/ammo_casing)) + var/obj/item/ammo_casing/casing = handful + if(give_round(casing, replace_spent)) + user.transferItemToLoc(casing, src, TRUE) + num_loaded++ + casing.update_appearance() + + if(num_loaded) + if(!silent) + to_chat(user, span_notice("You load [num_loaded] shell\s into \the [src]!")) + playsound(src, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) + update_ammo_count() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/_premade_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/_premade_stacks.dm new file mode 100644 index 0000000000..ae797547c8 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/_premade_stacks.dm @@ -0,0 +1,28 @@ +/obj/item/ammo_box/magazine/ammo_stack/prefilled + var/load_override //if we want the stack to spawn with a certain amount of ammo for whatever reason + var/obj/item/ammo_casing/set_ammo + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/Initialize(mapload, amt, to_copy) + if(amt) + load_override = amt + if(to_copy) + ammo_type = to_copy + make_stack() + update_appearance() + . = ..() + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/proc/make_stack() + var/obj/item/ammo_casing/to_copy = ammo_type + src.top_off(amount = load_override) + caliber = to_copy.caliber + base_icon_state = "[initial(to_copy.icon_state)][to_copy.bullet_skin ? "-[to_copy.bullet_skin]" : ""]" + name = "handful of [to_copy.name]s" + +/obj/item/storage/box/ammo //base type, don't use this! + name = "box of default ammo" + desc = "A box of ammunition. Not for consumption." + icon = 'icons/obj/ammunition/ammo_boxes.dmi' + icon_state = "9mmbox" + custom_materials = list(/datum/material/iron = 200) + illustration = null + foldable = null diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_gauss_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_gauss_stacks.dm new file mode 100644 index 0000000000..84ce4957da --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_gauss_stacks.dm @@ -0,0 +1,81 @@ +/obj/item/ammo_box/magazine/ammo_stack/prefilled/ferropellet + ammo_type = /obj/item/ammo_casing/caseless/gauss + max_ammo = 22 + +/obj/item/storage/box/ammo/ferropellet + name = "box of ferromagnetic pellets" + desc = "A box of ferromagnetic pellets for gauss firearms." + icon_state = "ferropelletsbox" + +/obj/item/storage/box/ammo/ferropellet/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/ferropellet = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/ferropellet/hc + ammo_type = /obj/item/ammo_casing/caseless/gauss/hc + + +/obj/item/storage/box/ammo/ferropellet/hc + name = "box of high conductivity pellets" + desc = "A box of high conductivity pellets for gauss firearms." + icon_state = "ferropelletsbox-hc" + +/obj/item/storage/box/ammo/ferropellet/hc/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/ferropellet/hc = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/ferroslug + ammo_type = /obj/item/ammo_casing/caseless/gauss/slug + max_ammo = 10 + +/obj/item/storage/box/ammo/ferroslug + name = "box of ferromagnetic slugs" + desc = "A box of standard ferromagnetic slugs for gauss firearms." + icon_state = "ferroslugsbox" + +/obj/item/storage/box/ammo/ferroslug/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/ferroslug = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/ferroslug/hc + ammo_type = /obj/item/ammo_casing/caseless/gauss/slug/hc + +/obj/item/storage/box/ammo/ferroslug/hc + name = "box of high conductivity slugs" + desc = "A box of high conductivity slugs for gauss firearms." + icon_state = "ferroslugsbox-hc" + +/obj/item/storage/box/ammo/ferroslug/hc/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/ferroslug/hc = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/ferrolance + ammo_type = /obj/item/ammo_casing/caseless/gauss/lance + max_ammo = 16 + +/obj/item/storage/box/ammo/ferrolance + name = "box of ferromagnetic lances" + desc = "A box of standard ferromagnetic lances for gauss firearms." + icon_state = "ferrolancesbox" + +/obj/item/storage/box/ammo/ferrolance/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/ferrolance = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/ferrolance/hc + ammo_type = /obj/item/ammo_casing/caseless/gauss/lance/hc + +/obj/item/storage/box/ammo/ferrolance/hc + name = "box of high conductivity lances" + desc = "A box of high conductivity lances for gauss firearms." + icon_state = "ferrolancesbox-hc" + +/obj/item/storage/box/ammo/ferrolance/hc/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/ferrolance/hc = 4) + generate_items_inside(items_inside,src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_lmg_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_lmg_stacks.dm new file mode 100644 index 0000000000..4b50912e5e --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_lmg_stacks.dm @@ -0,0 +1,12 @@ +// 7.12x82mm (L6 SAW) +/obj/item/ammo_box/magazine/ammo_stack/prefilled/mm712x82 + ammo_type = /obj/item/ammo_casing/mm712x82 + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/mm712x82/ap + ammo_type = /obj/item/ammo_casing/mm712x82/ap + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/mm712x82/hp + ammo_type = /obj/item/ammo_casing/mm712x82/hp + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/mm712x82/match + ammo_type = /obj/item/ammo_casing/mm712x82/match diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_misc_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_misc_stacks.dm new file mode 100644 index 0000000000..871c25d84a --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_misc_stacks.dm @@ -0,0 +1,24 @@ +/obj/item/ammo_box/magazine/ammo_stack/prefilled/foam_darts + ammo_type = /obj/item/ammo_casing/caseless/foam_dart + +/obj/item/storage/box/ammo/foam_darts + name = "box of foam darts" + icon = 'icons/obj/guns/toy.dmi' + icon_state = "foambox" + +/obj/item/storage/box/ammo/foam_darts/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/foam_darts = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/foam_darts/riot + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + +/obj/item/storage/box/ammo/foam_darts/riot + name = "box of foam darts" + icon_state = "foambox_riot" + +/obj/item/storage/box/ammo/foam_darts/riot/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/foam_darts/riot = 4) + generate_items_inside(items_inside,src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_pistol_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_pistol_stacks.dm new file mode 100644 index 0000000000..8b68b28500 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_pistol_stacks.dm @@ -0,0 +1,479 @@ +// 10x22mm (Stechkin) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm + ammo_type = /obj/item/ammo_casing/c10mm + +/obj/item/storage/box/ammo/c10mm + name = "box of 10x22mm ammo" + desc = "A box of standard 10x22mm ammo." + icon_state = "10mmbox" + +/obj/item/storage/box/ammo/c10mm/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/surplus + ammo_type = /obj/item/ammo_casing/c10mm/surplus + custom_materials = list(/datum/material/iron = 4000) + +/obj/item/storage/box/ammo/c10mm_surplus + name = "box of surplus 10x22mm ammo" + desc = "A box of low-quality 10x22mm ammo." + icon_state = "10mmbox-surplus" + +/obj/item/storage/box/ammo/c10mm_surplus/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/ap + ammo_type = /obj/item/ammo_casing/c10mm/ap + +/obj/item/storage/box/ammo/c10mm_ap + name = "box of AP 10x22mm ammo" + desc = "A box of 10x22mm armor-piercing ammo, designed to penetrate through armor at the cost of total damage." + icon_state = "10mmbox-ap" + +/obj/item/storage/box/ammo/c10mm_ap/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/hp + ammo_type = /obj/item/ammo_casing/c10mm/hp + +/obj/item/storage/box/ammo/c10mm_hp + name = "box of HP 10x22mm ammo" + desc = "A box of 10x22mm hollow point ammo, designed to cause massive tissue damage at the cost of armor penetration." + icon_state = "10mmbox-hp" + +/obj/item/storage/box/ammo/c10mm_hp/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/rubber + ammo_type = /obj/item/ammo_casing/c10mm/rubber + +/obj/item/storage/box/ammo/c10mm_rubber + name = "box of rubber 10x22mm ammo" + desc = "A box of 10x22mm rubbershot ammo, designed to disable targets without causing serious damage." + icon_state = "10mmbox-rubbershot" + +/obj/item/storage/box/ammo/c10mm_rubber/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c10mm/rubber = 4) + generate_items_inside(items_inside,src) + +// 9x18mm (Commander + SABR) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm + ammo_type = /obj/item/ammo_casing/c9mm + max_ammo = 15 + +/obj/item/storage/box/ammo/c9mm + name = "box of 9x18mm ammo" + desc = "A box of standard 9x18mm ammo." + icon_state = "9mmbox" + +/obj/item/storage/box/ammo/c9mm/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/surplus + ammo_type = /obj/item/ammo_casing/c9mm/surplus + custom_materials = list(/datum/material/iron = 4000) + +/obj/item/storage/box/ammo/c9mm_surplus + name = "box of surplus 9x18mm ammo" + desc = "A box of low-quality 9x18mm ammo." + icon_state = "9mmbox-surplus" + +/obj/item/storage/box/ammo/c9mm_surplus/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/surplus = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/ap + ammo_type = /obj/item/ammo_casing/c9mm/ap + +/obj/item/storage/box/ammo/c9mm_ap + name = "box of AP 9x18mm ammo" + desc = "A box of 9x18mm armor-piercing ammo, designed to penetrate through armor at the cost of total damage." + icon_state = "9mmbox-ap" + +/obj/item/storage/box/ammo/c9mm_ap/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/hp + ammo_type = /obj/item/ammo_casing/c9mm/hp + +/obj/item/storage/box/ammo/c9mm_hp + name = "box of HP 9x18mm ammo" + desc = "A box of 9x18mm hollow point ammo, designed to cause massive tissue damage at the cost of armor penetration." + icon_state = "9mmbox-hp" + +/obj/item/storage/box/ammo/c9mm_hp/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/rubber + ammo_type = /obj/item/ammo_casing/c9mm/rubber + +/obj/item/storage/box/ammo/c9mm_rubber + name = "box of rubber 9x18mm ammo" + desc = "A box of 9x18mm rubbershot ammo, designed to disable targets without causing serious damage." + icon_state = "9mmbox-rubbershot" + +/obj/item/storage/box/ammo/c9mm_rubber/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c9mm/rubber = 4) + generate_items_inside(items_inside,src) + +// .45 (Candor + C20R) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c45 + ammo_type = /obj/item/ammo_casing/c45 + +/obj/item/storage/box/ammo/c45 + name = "box of .45 ammo" + desc = "A box of standard .45 ammo." + icon_state = "45box" + +/obj/item/storage/box/ammo/c45/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c45 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/surplus + ammo_type = /obj/item/ammo_casing/c45/surplus + custom_materials = list(/datum/material/iron = 4000) + +/obj/item/storage/box/ammo/c45_surplus + name = "box of surplus .45 ammo" + desc = "A box of low-quality .45 ammo." + icon_state = "45box-surplus" + +/obj/item/storage/box/ammo/c45_surplus/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/surplus = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/ap + ammo_type = /obj/item/ammo_casing/c45/ap + +/obj/item/storage/box/ammo/c45_ap + name = "box of AP .45 ammo" + desc = "A box of .45 armor-piercing ammo, designed to penetrate through armor at the cost of total damage." + icon_state = "45box-ap" + +/obj/item/storage/box/ammo/c45_ap/PopulateContents() + ..() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/hp + ammo_type = /obj/item/ammo_casing/c45/hp + +/obj/item/storage/box/ammo/c45_hp + name = "box of HP .45 ammo" + desc = "A box of .45 hollow point ammo, designed to cause massive tissue damage at the cost of armor penetration." + icon_state = "45box-hp" + +/obj/item/storage/box/ammo/c45_hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/rubber + ammo_type = /obj/item/ammo_casing/c45/rubber + +/obj/item/storage/box/ammo/c45_rubber + name = "box of rubbershot .45 ammo" + desc = "A box of .45 rubbershot ammo, designed to disable targets without causing serious damage." + icon_state = "45box-rubbershot" + +/obj/item/storage/box/ammo/c45_rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c45/rubber = 4) + generate_items_inside(items_inside,src) + +// .22 LR (Himehabu, Pounder) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr + ammo_type = /obj/item/ammo_casing/c22lr + max_ammo = 25 + +/obj/item/storage/box/ammo/c22lr + name = "box of .22 LR ammo" + desc = "A box of standard .22 LR ammo." + icon_state = "22lrbox" + +/obj/item/storage/box/ammo/c22lr/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr/ap + ammo_type = /obj/item/ammo_casing/c22lr/ap + max_ammo = 25 + +/obj/item/storage/box/ammo/c22lr/ap + name = "box of .22 LR AP ammo" + desc = "A box of standard .22 LR AP ammo, designed to penetrate through armor at the cost of total damage." + icon_state = "22lrbox-ap" + +/obj/item/storage/box/ammo/c22lr/ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr/hp + ammo_type = /obj/item/ammo_casing/c22lr/hp + max_ammo = 25 + +/obj/item/storage/box/ammo/c22lr/hp + name = "box of .22 LR HP ammo" + desc = "A box of standard .22 LR HP ammo, designed to cause massive tissue damage at the cost of armor penetration." + icon_state = "22lrbox-hp" + +/obj/item/storage/box/ammo/c22lr/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr/rubber + ammo_type = /obj/item/ammo_casing/c22lr/rubber + max_ammo = 25 + +/obj/item/storage/box/ammo/c22lr/rubber + name = "box of .22 LR rubber ammo" + desc = "A box of standard .22 LR rubber ammo." + icon_state = "22lrbox-rubbershot" + +/obj/item/storage/box/ammo/c22lr/rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c22lr/rubber = 4) + generate_items_inside(items_inside,src) + +// .357 + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a357 + ammo_type = /obj/item/ammo_casing/a357 + +/obj/item/storage/box/ammo/a357 + name = "box of .357 ammo" + desc = "A box of standard .357 ammo." + icon_state = "357box" + +/obj/item/storage/box/ammo/a357/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a357 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a357/match + ammo_type = /obj/item/ammo_casing/a357/match + +/obj/item/storage/box/ammo/a357_match + name = "box of match .357 ammo" + desc = "A box of match .357 ammo." + icon_state = "357box-match" + +/obj/item/storage/box/ammo/a357_match/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a357/match = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a357/hp + ammo_type = /obj/item/ammo_casing/a357/hp + +/obj/item/storage/box/ammo/a357_hp + name = "box of HP .357 ammo" + desc = "A box of hollow point .357 ammo." + icon_state = "357box-hp" + +/obj/item/storage/box/ammo/a357_hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a357/hp = 4) + generate_items_inside(items_inside,src) + +// .45-70 (Hunting Revolver, Beacon) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570 + ammo_type = /obj/item/ammo_casing/a4570 + max_ammo = 6 + +/obj/item/storage/box/ammo/a4570 + name = "box of .45-70 ammo" + desc = "A box of top grade .45-70 ammo. These rounds do significant damage with average performance against armor." + icon_state = "4570" + +/obj/item/storage/box/ammo/a4570/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570/match + ammo_type = /obj/item/ammo_casing/a4570/match + +/obj/item/storage/box/ammo/a4570_match + name = "box of HP match .45-70 ammo" + desc = "A 12-round ammo box for .45-70 revolvers. These match rounds travel faster, perform better against armor, and can ricochet off targets." + icon_state = "4570-match" + +/obj/item/storage/box/ammo/a4570_match/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570/match = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570/hp + ammo_type = /obj/item/ammo_casing/a4570/hp + +/obj/item/storage/box/ammo/a4570_hp + name = "box of HP .45-70 ammo" + desc = "A 12-round ammo box for .45-70 revolvers. These hollow point rounds do legendary damage against soft targets, but are nearly ineffective against armored ones." + icon_state = "4570-hp" + +/obj/item/storage/box/ammo/a4570_hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570/explosive + ammo_type = /obj/item/ammo_casing/a4570/explosive + +/obj/item/storage/box/ammo/a4570_explosive + name = "box of explosive .45-70 ammo" + desc = "A 12-round ammo box for .45-70 revolvers. These explosive rounds contain a small explosive charge that detonates on impact, creating large wounds and potentially removing limbs." + icon_state = "4570-explosive" + +/obj/item/storage/box/ammo/a4570_explosive/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a4570/explosive = 4) + generate_items_inside(items_inside,src) + +// .38 Special + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38 + ammo_type = /obj/item/ammo_casing/c38 + max_ammo = 15 + +/obj/item/storage/box/ammo/c38 + name = "box of .38 ammo" + desc = "A box of standard .38 Special ammo." + icon_state = "38box" + +/obj/item/storage/box/ammo/c38/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c38 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/surplus + ammo_type = /obj/item/ammo_casing/c38/surplus + custom_materials = list(/datum/material/iron = 4000) + +/obj/item/storage/box/ammo/c38_surplus + name = "box of surplus .38 ammo" + desc = "A box of low-quality .38 Special ammo." + icon_state = "38box-surplus" + +/obj/item/storage/box/ammo/c38_surplus/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/surplus = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/trac + ammo_type = /obj/item/ammo_casing/c38/trac + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/match + ammo_type = /obj/item/ammo_casing/c38/match + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/bouncy + ammo_type = /obj/item/ammo_casing/c38/match/bouncy + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/dumdum + ammo_type = /obj/item/ammo_casing/c38/dumdum + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/hotshot + ammo_type = /obj/item/ammo_casing/c38/hotshot + +/obj/item/storage/box/ammo/c38_hotshot + name = "box of .38 hearth ammo" + desc = "An unorthodox .38 Special cartridge infused with hearthflame. Catches the target on fire." + icon_state = "38hotshot" + +/obj/item/storage/box/ammo/c38_hotshot/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/hotshot = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/iceblox + ammo_type = /obj/item/ammo_casing/c38/iceblox + +/obj/item/storage/box/ammo/c38_iceblox + name = "box of .38 chilled ammo" + desc = "An unorthodox .38 Special cartridge infused with wine of ice. Chills the target, slowing them down." + icon_state = "38iceblox" + +/obj/item/storage/box/ammo/c38_iceblox/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c38/iceblox = 4) + generate_items_inside(items_inside,src) + +// 44 Roumain + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a44roum + ammo_type = /obj/item/ammo_casing/a44roum + +/obj/item/storage/box/ammo/a44roum + name = "box of .44 roumain ammo" + desc = "A box of standard .44 roumain ammo." + icon_state = "a44roum" + +/obj/item/storage/box/ammo/a44roum/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a44roum = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a44roum/rubber + ammo_type = /obj/item/ammo_casing/a44roum/rubber + +/obj/item/storage/box/ammo/a44roum_rubber + name = "box of rubber .44 roumain ammo" + desc = "A box of .44 roumain rubbershot ammo, designed to disable targets without causing serious damage." + icon_state = "a44roum-rubber" + +/obj/item/storage/box/ammo/a44roum_rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a44roum/rubber = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a44roum/hp + ammo_type = /obj/item/ammo_casing/a44roum/hp + +/obj/item/storage/box/ammo/a44roum_hp + name = "box of HP .44 roumain ammo" + desc = "A box of .44 roumain hollowpoint ammo, designed to disable targets without causing serious damage." + icon_state = "a44roum-hp" + +/obj/item/storage/box/ammo/a44roum_hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a44roum/hp = 4) + generate_items_inside(items_inside,src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_rifle_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_rifle_stacks.dm new file mode 100644 index 0000000000..b8db21cd41 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_rifle_stacks.dm @@ -0,0 +1,224 @@ +// 8x50mmR (Illestren Hunting Rifle) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r + ammo_type = /obj/item/ammo_casing/a8_50r + max_ammo = 10 + +/obj/item/storage/box/ammo/a8_50r + name = "box of 8x50mm ammo" + desc = "A box of standard 8x50mm ammo." + icon_state = "8x50mmbox" + +/obj/item/storage/box/ammo/a8_50r/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r/hp + ammo_type = /obj/item/ammo_casing/a8_50r/hp + +/obj/item/storage/box/ammo/a8_50r/hp + name = "box of HP 8x50mm ammo" + desc = "A box of hollow point 8x50mm ammo, designed to cause massive damage at the cost of armor penetration." + icon_state = "8x50mmbox-hp" + +/obj/item/storage/box/ammo/a8_50r/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r/match + ammo_type = /obj/item/ammo_casing/a8_50r/match + max_ammo = 10 + +/obj/item/storage/box/ammo/a8_50r/match + name = "box of 8x50mm match ammo" + desc = "A box of standard 8x50mm ammo." + icon_state = "8x50mmbox-match" + +/obj/item/storage/box/ammo/a8_50r/match/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r/match = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r/trac + ammo_type = /obj/item/ammo_casing/a8_50r/trac + max_ammo = 10 + +/obj/item/storage/box/ammo/a8_50r/trac + name = "box of 8x50mm trac ammo" + desc = "A box of 8x50mm trackers." + icon_state = "8x50mmbox-trac" + +/obj/item/storage/box/ammo/a8_50r/trac/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a8_50r/trac = 3) + generate_items_inside(items_inside,src) + +// 5.56x42mm CLIP (CM82, Hydra variants) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a556_42 + ammo_type = /obj/item/ammo_casing/a556_42 + max_ammo = 15 + +/obj/item/storage/box/ammo/a556_42 + name = "box of 5.56x42mm CLIP ammo" + desc = "A box of standard 5.56x42mm CLIP ammo." + icon_state = "a556_42box_big" + +/obj/item/storage/box/ammo/a556_42/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a556_42 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a556_42/hp + ammo_type = /obj/item/ammo_casing/a556_42/hp + max_ammo = 15 + +/obj/item/storage/box/ammo/a556_42/hp + name = "box of 5.56x42mm CLIP HP ammo" + desc = "A box of standard 5.56x42mm CLIP HP ammo." + icon_state = "a556_42box_big-hp" + +/obj/item/storage/box/ammo/a556_42/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a556_42/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a556_42/ap + ammo_type = /obj/item/ammo_casing/a556_42/ap + max_ammo = 15 + +/obj/item/storage/box/ammo/a556_42/ap + name = "box of 5.56x42mm CLIP AP ammo" + desc = "A box of standard 5.56x42mm CLIP AP ammo." + icon_state = "a556_42box_big-ap" + +/obj/item/storage/box/ammo/a556_42/ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a556_42/ap = 4) + generate_items_inside(items_inside,src) + +// 7.62x40mm CLIP (SKM Rifles) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40 + ammo_type = /obj/item/ammo_casing/a762_40 + max_ammo = 15 + +/obj/item/storage/box/ammo/a762_40 + name = "box of 7.62x40mm CLIP ammo" + desc = "A box of standard 7.62x40mm CLIP ammo." + icon_state = "a762_40box_big" + +/obj/item/storage/box/ammo/a762_40/inteq + icon_state = "a762_40box_big_inteq" + +/obj/item/storage/box/ammo/a762_40/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40/hp + ammo_type = /obj/item/ammo_casing/a762_40/hp + max_ammo = 15 + +/obj/item/storage/box/ammo/a762_40/hp + name = "box of 7.62x40mm CLIP Hollow Point ammo" + desc = "A box of standard 7.62x40mm CLIP Hollow Point ammo." + icon_state = "a762_40box_big-hp" + +/obj/item/storage/box/ammo/a762_40/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40/ap + ammo_type = /obj/item/ammo_casing/a762_40/ap + max_ammo = 15 + +/obj/item/storage/box/ammo/a762_40/ap + name = "box of 7.62x40mm CLIP Armour Piercing ammo" + desc = "A box of standard 7.62x40mm CLIP Armour Piercing ammo." + icon_state = "a762_40box_big-ap" + +/obj/item/storage/box/ammo/a762_40/ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40/rubber + ammo_type = /obj/item/ammo_casing/a762_40/rubber + max_ammo = 15 + +/obj/item/storage/box/ammo/a762_40/rubber + name = "box of 7.62x40mm CLIP rubber ammo" + desc = "A box of standard 7.62x40mm CLIP rubber ammo." + icon_state = "a762_40box_big-rubbershot" + +/obj/item/storage/box/ammo/a762_40/rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a762_40/rubber = 4) + generate_items_inside(items_inside,src) + +//.308 (M514 EBR & CM-GAL-S) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a308 + ammo_type = /obj/item/ammo_casing/a308 + max_ammo = 10 + +/obj/item/storage/box/ammo/a308 + name = "box of .308 ammo" + desc = "A box of standard .308 ammo." + icon_state = "a308box-HP" + +/obj/item/storage/box/ammo/a308/inteq + icon_state = "a308box" + +/obj/item/storage/box/ammo/a308/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a308 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a308/hp + ammo_type = /obj/item/ammo_casing/a308/hp + max_ammo = 10 + +/obj/item/storage/box/ammo/a308/hp + name = "box of .308 HP ammo" + desc = "A box of standard .308 HP ammo." + icon_state = "a308box-hp-hp" + +/obj/item/storage/box/ammo/a308/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a308/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a308/ap + ammo_type = /obj/item/ammo_casing/a308/ap + max_ammo = 10 + +/obj/item/storage/box/ammo/a308/ap + name = "box of .308 AP ammo" + desc = "A box of standard .308 AP ammo." + icon_state = "a308box-hp-ap" + +/obj/item/storage/box/ammo/a308/ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a308/ap = 4) + generate_items_inside(items_inside,src) + +//.299 Eoehoma Caseless (E-40) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c299 + ammo_type = /obj/item/ammo_casing/caseless/c299 + max_ammo = 15 + +/obj/item/storage/box/ammo/c299 + name = "box of .299 Eoehoma caseless ammo" + desc = "A box of .299 Eoehoma caseless, for use with the E-40 hybrid assault rifle." + icon_state = "299box" + +/obj/item/storage/box/ammo/c299/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c299 = 4) + generate_items_inside(items_inside,src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_shotshell_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_shotshell_stacks.dm new file mode 100644 index 0000000000..19ed0d0746 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_shotshell_stacks.dm @@ -0,0 +1,100 @@ +// Shotshells +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun + max_ammo = 8 //make sure these values are consistent across the board with stack_size variable on respective ammo_casing + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/buckshot + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/storage/box/ammo/a12g_buckshot + name = "box of 12ga buckshot" + desc = "A box of 12-gauge buckshot shells, devastating at close range." + icon_state = "12gbox-buckshot" + +/obj/item/storage/box/ammo/a12g_buckshot/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/buckshot = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/slug + ammo_type = /obj/item/ammo_casing/shotgun + +/obj/item/storage/box/ammo/a12g_slug + name = "box of 12ga slugs" + desc = "A box of 12-gauge slugs, for improved accuracy and penetration." + icon_state = "12gbox-slug" + +/obj/item/storage/box/ammo/a12g_slug/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/slug = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/beanbag + ammo_type = /obj/item/ammo_casing/shotgun/beanbag + +/obj/item/storage/box/ammo/a12g_beanbag + name = "box of 12ga beanbags" + desc = "A box of 12-gauge beanbag shells, for incapacitating targets." + icon_state = "12gbox-beanbag" + +/obj/item/storage/box/ammo/a12g_beanbag/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/beanbag = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/rubber + ammo_type = /obj/item/ammo_casing/shotgun/rubbershot + +/obj/item/storage/box/ammo/a12g_rubbershot + name = "box of 12ga rubbershot" + desc = "A box of 12-gauge rubbershot shells, designed for riot control." + icon_state = "12gbox-rubbershot" + +/obj/item/storage/box/ammo/a12g_rubbershot/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/rubber = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/blank + ammo_type = /obj/item/ammo_casing/shotgun/blank + +/obj/item/storage/box/ammo/a12g_blank + name = "box of 12ga blanks" + desc = "A box of 12-gauge blank shells, designed for training." + icon_state = "12gbox-slug" //needs icon + +/obj/item/storage/box/ammo/a12g_blank/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/blank = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/incendiary + ammo_type = /obj/item/ammo_casing/shotgun/incendiary + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/improvised + ammo_type = /obj/item/ammo_casing/shotgun/improvised + +/obj/item/storage/box/ammo/pulseslug + name = "box of 12ga pulse slugs" + desc = "A box of 12-gauge pulse shells, designed for increased accuracy and destruction." + icon_state = "12gbox-rubbershot" + +/obj/item/storage/box/ammo/pulseslug/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/pulseslug = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/pulseslug + ammo_type = /obj/item/ammo_casing/shotgun/pulseslug + +/obj/item/storage/box/ammo/a12g_dart + name = "box of 12ga dart" + desc = "A box of 12-gauge dart shells, for injecting targets." + icon_state = "12gbox-beanbag" + +/obj/item/storage/box/ammo/a12g_dart/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/dart = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/shotgun/dart + ammo_type = /obj/item/ammo_casing/shotgun/dart diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_smg_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_smg_stacks.dm new file mode 100644 index 0000000000..92f8e644ca --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_smg_stacks.dm @@ -0,0 +1,187 @@ +// 4.6x30mm (WT-550 Automatic Rifle & SKM-24v) +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm + ammo_type = /obj/item/ammo_casing/c46x30mm + max_ammo = 20 + +/obj/item/storage/box/ammo/c46x30mm + name = "box of 4.6x30mm ammo" + desc = "A box of standard 4.6x30mm ammo." + icon_state = "46x30mmbox" + +/obj/item/storage/box/ammo/c46x30mm/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/ap + ammo_type = /obj/item/ammo_casing/c46x30mm/ap + max_ammo = 20 + +/obj/item/storage/box/ammo/c46x30mm/ap + name = "box of 4.6x30mm AP ammo" + desc = "A box of standard 4.6x30mm AP ammo." + icon_state = "46x30mmbox-ap" + +/obj/item/storage/box/ammo/c46x30mm/ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/hp + ammo_type = /obj/item/ammo_casing/c46x30mm/hp + max_ammo = 20 + +/obj/item/storage/box/ammo/c46x30mm/hp + name = "box of 4.6x30mm HP ammo" + desc = "A box of standard 4.6x30mm HP ammo." + icon_state = "46x30mmbox-hp" + +/obj/item/storage/box/ammo/c46x30mm/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/rubber + ammo_type = /obj/item/ammo_casing/c46x30mm/rubber + max_ammo = 20 + +/obj/item/storage/box/ammo/c46x30mm/rubber + name = "box of 4.6x30mm rubber ammo" + desc = "A box of standard 4.6x30mm rubber ammo." + icon_state = "46x30mmbox-rubbershot" + +/obj/item/storage/box/ammo/c46x30mm/rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/rubber = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c46x30mm/tesla + ammo_type = /obj/item/ammo_casing/c46x30mm/tesla + max_ammo = 20 + +// 4.73x33mm caseless (Solar) +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c47x33mm + ammo_type = /obj/item/ammo_casing/caseless/c47x33mm + +// 5.56mm HITP caseless (Pistole C) +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm + ammo_type = /obj/item/ammo_casing/caseless/c556mm + max_ammo = 15 + +/obj/item/storage/box/ammo/c556mm + name = "box of 5.56mm HITP caseless ammo" + desc = "A box of 5.56mm HITP caseless ammo, a SolGov standard." + icon_state = "556mmHITPbox" + +/obj/item/storage/box/ammo/c556mm/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/surplus + ammo_type = /obj/item/ammo_casing/caseless/c556mm/surplus + custom_materials = list(/datum/material/iron = 4000) + +/obj/item/storage/box/ammo/c556mm_surplus + name = "box of surplus 5.56mm HITP caseless ammo" + desc = "A box of low-quality 5.56mm HITP caseless ammo." + icon_state = "556mmHITPbox-surplus" + +/obj/item/storage/box/ammo/c556mm_surplus/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/surplus = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/ap + ammo_type = /obj/item/ammo_casing/caseless/c556mm/ap + +/obj/item/storage/box/ammo/c556mm_ap + name = "box of AP 5.56mm HITP caseless ammo" + desc = "A box of 5.56mm HITP caseless armor-piercing ammo, designed to penetrate through armor at the cost of total damage." + icon_state = "556mmHITPbox-ap" + +/obj/item/storage/box/ammo/c556mm_ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/hp + ammo_type = /obj/item/ammo_casing/caseless/c556mm/hp + +/obj/item/storage/box/ammo/c556mm_hp + name = "box of HP 5.56mm HITP caseless ammo" + desc = "A box of 5.56mm HITP caseless hollow point ammo, designed to cause massive tissue damage at the cost of armor penetration." + icon_state = "556mmHITPbox-hp" + +/obj/item/storage/box/ammo/c556mm_hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/rubbershot + ammo_type = /obj/item/ammo_casing/caseless/c556mm/rubbershot + +/obj/item/storage/box/ammo/c556mm_rubber + name = "box of rubber 5.56mm HITP caseless ammo" + desc = "A box of 5.56mm HITP caseless rubbershot ammo, designed to disable targets without causing serious damage." + icon_state = "556mmHITPbox-rubbershot" + +/obj/item/storage/box/ammo/c556mm_rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c556mm/rubbershot = 4) + generate_items_inside(items_inside,src) + +// 5.7x39mm (Asp and Sidewinder) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39 + max_ammo = 20 + ammo_type = /obj/item/ammo_casing/c57x39mm + +/obj/item/storage/box/ammo/c57x39 + name = "box of 5.7x39mm ammo" + desc = "A box of standard 5.7x39mm ammo." + icon_state = "57x39mmbox" + +/obj/item/storage/box/ammo/c57x39/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39/hp + ammo_type = /obj/item/ammo_casing/c57x39mm/hp + +/obj/item/storage/box/ammo/c57x39/hp + name = "box of 5.7x39mm HP ammo" + desc = "A box of standard 5.7x39mm HP ammo." + icon_state = "57x39mmbox-hp" + +/obj/item/storage/box/ammo/c57x39/hp/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39/hp = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39/ap + ammo_type = /obj/item/ammo_casing/c57x39mm/ap + +/obj/item/storage/box/ammo/c57x39/ap + name = "box of 5.7x39mm AP ammo" + desc = "A box of standard 5.7x39mm AP ammo." + icon_state = "57x39mmbox-ap" + +/obj/item/storage/box/ammo/c57x39/ap/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39/ap = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39/rubber + ammo_type = /obj/item/ammo_casing/c57x39mm/rubber + +/obj/item/storage/box/ammo/c57x39/rubber + name = "box of 5.7x39mm rubber ammo" + desc = "A box of standard 5.7x39mm rubber ammo." + icon_state = "57x39mmbox-rubbershot" + +/obj/item/storage/box/ammo/c57x39/rubber/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/c57x39/rubber = 4) + generate_items_inside(items_inside,src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_sniper_stacks.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_sniper_stacks.dm new file mode 100644 index 0000000000..df0ec8c2e9 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/ammo_stacks/prefab_stacks/premade_sniper_stacks.dm @@ -0,0 +1,96 @@ +// .50 BMG (Sniper) + +/obj/item/storage/box/ammo/a50box + name = "box of .50BMG ammo" + desc = "A box of standard .50BMG ammo." + icon_state = "a50box" + +/obj/item/storage/box/ammo/a50box/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/p50 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/p50 + ammo_type = /obj/item/ammo_casing/p50 + max_ammo = 5 + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/p50/soporific + ammo_type = /obj/item/ammo_casing/p50/soporific + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/p50/penetrator + ammo_type = /obj/item/ammo_casing/p50/penetrator + +// 8x58mm Caseless (SSG-669C) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a858 + ammo_type = /obj/item/ammo_casing/caseless/a858 + max_ammo = 5 + +/obj/item/storage/box/ammo/a858 + name = "box of 8x58mm caseless ammo" + desc = "A box of 8x58mm caseless rounds." + icon_state = "8x58mmbox" + +/obj/item/storage/box/ammo/a858/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a858 = 4) + generate_items_inside(items_inside,src) + +// .300 Magnum (Smile Rifle) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a300 + ammo_type = /obj/item/ammo_casing/a300 + max_ammo = 5 + +/obj/item/storage/box/ammo/a300 + name = "box of .300 magnum ammo" + desc = "A box of standard .300 Magnum ammo." + icon_state = "300box" + +/obj/item/storage/box/ammo/a300/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a300 = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a300/trac + ammo_type = /obj/item/ammo_casing/a300/trac + max_ammo = 5 + +/obj/item/storage/box/ammo/a300/trac + name = "box of .300 trac ammo" + desc = "A box of standard .300 Magnum ammo." + icon_state = "300box" + +/obj/item/storage/box/ammo/a300/trac/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a300/trac = 2) + generate_items_inside(items_inside,src) + +//6.5mm CLIP + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a65clip + ammo_type = /obj/item/ammo_casing/a65clip + max_ammo = 5 + +/obj/item/storage/box/ammo/a65clip + name = "box of 6.5mm CLIP ammo" + desc = "A box of standard 6.5mm CLIP ammo." + icon_state = "65box" + +/obj/item/storage/box/ammo/a65clip/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a65clip = 4) + generate_items_inside(items_inside,src) + +/obj/item/ammo_box/magazine/ammo_stack/prefilled/a65clip/trac + ammo_type = /obj/item/ammo_casing/a65clip/trac + max_ammo = 5 + +/obj/item/storage/box/ammo/a65clip/trac + name = "box of 6.5mm CLIP tracker ammo" + desc = "A box of standard 6.5mm CLIP tracker ammo." + +/obj/item/storage/box/ammo/a65clip/trac/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/magazine/ammo_stack/prefilled/a65clip/trac = 2) + generate_items_inside(items_inside,src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/gauss.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/gauss.dm new file mode 100644 index 0000000000..8095cd390e --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/gauss.dm @@ -0,0 +1,39 @@ +/obj/item/ammo_box/magazine/gauss + name = "gauss magazine (ferromagnetic pellets)" + desc = "A 24-round magazine for the prototype gauss rifle. Ferromagnetic pellets do okay damage with significant armor penetration." + icon_state = "mediummagmag" + ammo_type = /obj/item/ammo_casing/caseless/gauss + caliber = "pellet" + max_ammo = 24 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/gauss/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/modelh + name = "Model H magazine (ferromagnetic slugs)" + desc = "A 10-round magazine for the Model H pistol. Ferromagnetic slugs are slow and incredibly powerful bullets, but are easily stopped by even a sliver of armor." + icon_state = "smallmagmag" + ammo_type = /obj/item/ammo_casing/caseless/gauss/slug + caliber = "slug" + max_ammo = 10 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/modelh/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/gar + name = "GAR tube magazine (ferromagnetic lances)" + desc = "A 32-round magazined for the GAR assault rifle. Ferromagnetic lances do good damage with significant armor penetration." + icon_state = "gar-mag" + ammo_type = /obj/item/ammo_casing/caseless/gauss/lance + caliber = "lance" + max_ammo = 32 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/gar/update_icon() + . = ..() + icon_state = "gar-mag-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/gar/empty + start_empty = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/lmg.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/lmg.dm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/pistol.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/pistol.dm new file mode 100644 index 0000000000..5c440ee682 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/pistol.dm @@ -0,0 +1,80 @@ +/obj/item/ammo_box/magazine/m45 + name = "pistol magazine (.45)" + desc = "An 8-round single-stack magazine for the Candor pistol. These rounds do moderate damage, but struggle against armor." + icon_state = "candor_mag-8" + base_icon_state = "candor_mag" + ammo_type = /obj/item/ammo_casing/c45 + caliber = ".45" + max_ammo = 8 + +/obj/item/ammo_box/magazine/m45/empty + start_empty = TRUE + + +/obj/item/ammo_box/magazine/m45/hp + name = "pistol magazine (.45 HP)" + desc= "An 8-round single-stack magazine for the Candor pistol. These hollow point rounds do incredible damage against soft targets, but are nearly ineffective against armored ones." + ammo_type = /obj/item/ammo_casing/c45/hp + +/obj/item/ammo_box/magazine/m45/ap + name = "pistol magazine (.45 AP)" + desc= "An 8-round single-stack magazine for the Candor pistol. These armor-piercing rounds are okay at piercing protective equipment, but lose some stopping power." + ammo_type = /obj/item/ammo_casing/c45/ap + +/obj/item/ammo_box/magazine/m45/rubber + name = "pistol magazine (.45 rubber)" + desc = "An 8-round single-stack magazine for the Candor pistol. These rubber rounds trade lethality for a heavy impact which can incapacitate targets. Performs even worse against armor." + ammo_type = /obj/item/ammo_casing/c45/rubber + +/obj/item/ammo_box/magazine/m45/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[min(ammo_count(), 8)]" + +/obj/item/ammo_box/magazine/pistol556mm + name = "Pistole C magazine (5.56mm HITP caseless)" + desc = "A 12-round, double-stack magazine for the Pistole C pistol. These rounds do okay damage with average performance against armor." + icon_state = "pistolec_mag-12" //ok i did it + base_icon_state = "pistolec_mag" + ammo_type = /obj/item/ammo_casing/caseless/c556mm + caliber = "5.56mm caseless" + max_ammo = 12 + +/obj/item/ammo_box/magazine/pistol556mm/update_icon_state() + . = ..() + if(ammo_count() == 12) + icon_state = "[base_icon_state]-12" + else if(ammo_count() >= 10) + icon_state = "[base_icon_state]-10" + else if(ammo_count() >= 5) + icon_state = "[base_icon_state]-5" + else if(ammo_count() >= 1) + icon_state = "[base_icon_state]-1" + else + icon_state = "[base_icon_state]-0" + +/obj/item/ammo_box/magazine/pistol556mm/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/disposable + name = "part of a disposable gun" + desc = "You ripped out part of the gun, somehow, rendering it unusuable. I hope you're happy." + icon_state = "himehabu_mag-10" + base_icon_state = "himehabu_mag" + ammo_type = /obj/item/ammo_casing/c22lr + caliber = ".22lr" + max_ammo = 10 + w_class = WEIGHT_CLASS_TINY + +/obj/item/ammo_box/magazine/zip_ammo_9mm + name = "budget pistol magazine (9x18mm)" + desc = "A cheaply-made, 8-round surplus magazine that fits standard-issue 9x18mm pistols. These rounds do okay damage, but struggle against armor." + icon_state = "zip_mag-1" + base_icon_state = "zip_mag" + ammo_type = /obj/item/ammo_casing/c9mm/surplus + caliber = "9x18mm" + max_ammo = 8 + custom_materials = list(/datum/material/iron = 20000) + +/obj/item/ammo_box/magazine/zip_ammo_9mm/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[min(ammo_count(), 1)]" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rechargable.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rechargable.dm new file mode 100644 index 0000000000..f5cb7e7ee9 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rechargable.dm @@ -0,0 +1,19 @@ +/obj/item/ammo_box/magazine/recharge + name = "power pack" + desc = "A detachable, rechargeable battery for the laser rifle. Grants 20 shots at full charge." + icon_state = "oldrifle-20" + base_icon_state = "oldrifle" + ammo_type = /obj/item/ammo_casing/caseless/laser + caliber = "laser" + max_ammo = 20 + +/obj/item/ammo_box/magazine/recharge/update_desc() + . = ..() + desc = "[initial(desc)] It has [stored_ammo.len] shot\s left." + +/obj/item/ammo_box/magazine/recharge/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[round(ammo_count(), 4)]" + +/obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets" + return diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rifle.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rifle.dm new file mode 100644 index 0000000000..6634d2070f --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/rifle.dm @@ -0,0 +1,109 @@ +/obj/item/ammo_box/magazine/rifle47x33mm + name = "\improper Solarian LMG magazine (4.73x33mm caseless)" + desc = "A large, 50-round magazine for the Solar machine gun. These rounds do moderate damage with good armor penetration." + icon_state = "47x33mm-50" + base_icon_state = "47x33mm" + ammo_type = /obj/item/ammo_casing/caseless/c47x33mm + caliber = "4.73x33mm caseless" + max_ammo = 100 //brrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/rifle47x33mm/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[round(ammo_count(),5)]" + +/obj/item/ammo_box/magazine/skm_46_30 + name = "subcaliber assault rifle magazine (4.6x30mm)" + desc = "A slightly-curved, 30-round magazine for the SKM-24v. These rounds do okay damage with average performance against armor" + ammo_type = /obj/item/ammo_casing/c46x30mm + caliber = "4.6x30mm" + max_ammo = 30 + base_icon_state = "skmcarbine_mag" + icon_state = "skmcarbine_mag-1" + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/skm_46_30/recycled + ammo_type = /obj/item/ammo_casing/c46x30mm/recycled + +/obj/item/ammo_box/magazine/skm_762_40 + name = "assault rifle magazine (7.62x40mm CLIP)" + desc = "A slightly curved, 20-round magazine for the 7.62x40mm CLIP variants of the SKM assault rifle family. These rounds do good damage with good armor penetration." + base_icon_state = "skm_mag" + icon_state = "skm_mag-1" + ammo_type = /obj/item/ammo_casing/a762_40 + caliber = "7.62x40mm" + max_ammo = 20 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/skm_762_40/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/skm_762_40/extended + name = "extended assault rifle magazine (7.62x40mm CLIP)" + desc = "A very curved, 40-round magazine for the 7.62x40mm CLIP variants of the SKM assault rifle family. These rounds do good damage with good armor penetration." + base_icon_state = "skm_extended_mag" + icon_state = "skm_extended_mag-1" + max_ammo = 40 + +/obj/item/ammo_box/magazine/skm_762_40/extended/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/skm_762_40/drum + name = "assault rifle drum (7.62x40mm CLIP)" + desc = "A 75-round drum for the 7.62x40mm CLIP variants of the SKM assault rifle family. These rounds do good damage with good armor penetration." + base_icon_state = "skm_drum" + icon_state = "skm_drum-1" + max_ammo = 75 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/f4_308 + name = "\improper F4 Magazine (.308)" + desc = "A standard 10-round magazine for F4 platform DMRs. These rounds do good damage with significant armor penetration." + icon_state = "gal_mag-1" + base_icon_state = "gal_mag" + ammo_type = /obj/item/ammo_casing/a308 + caliber = ".308" + max_ammo = 10 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/f4_308/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/p16 //repath to /obj/item/ammo_box/magazine/generic_556 sometime + name = "assault rifle magazine (5.56x42mm CLIP)" + desc = "A simple, 30-round magazine for 5.56x42mm CLIP assault rifles. These rounds do moderate damage with good armor penetration." + icon_state = "p16_mag-1" + base_icon_state = "p16_mag" + ammo_type = /obj/item/ammo_casing/a556_42 + caliber = "5.56x42mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/p16/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/swiss + name = "\improper Swiss Cheese Magazine (5.56x42mm CLIP)" + desc = "A deft, 30-round magazine for the Swiss Cheese assault rifle. These rounds do moderate damage with good armor penetration." + icon_state = "swissmag-1" + base_icon_state = "swissmag" + ammo_type = /obj/item/ammo_casing/a556_42 + caliber = "5.56x42mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +// 8x50mmR En Bloc Clip (Illestren Hunting Rifle) + +/obj/item/ammo_box/magazine/illestren_a850r //this is a magazine codewise do nothing breaks + name = "en bloc clip (8x50mmR)" + desc = "A 5-round en bloc clip for the Illestren Hunting Rifle. These rounds do good damage with significant armor penetration." + icon_state = "enbloc_858" + ammo_type = /obj/item/ammo_casing/a8_50r + caliber = "8x50mmR" + max_ammo = 5 + multiple_sprites = AMMO_BOX_PER_BULLET + w_class = WEIGHT_CLASS_TINY + custom_materials = list(/datum/material/iron = 500) + +/obj/item/ammo_box/magazine/illestren_a850r/empty + start_empty = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/shotgun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/shotgun.dm new file mode 100644 index 0000000000..60d09eef0f --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/shotgun.dm @@ -0,0 +1,18 @@ +/obj/item/ammo_box/magazine/cm15_12g + name = "CM-15 magazine (12g buckshot)" + desc = "An almost straight, 8-round magazine designed for the CM-15 shotgun." + icon_state = "cm15_mag-1" + base_icon_state = "cm15_mag" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 8 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/cm15_12g/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/cm15_12g/incendiary + name = "CM-15 magazine (12g incendiary)" + desc = "An almost straight, 8-round magazine designed for the CM-15 shotgun. This one was loaded with incendiary slugs. Be careful!" + ammo_type = /obj/item/ammo_casing/shotgun/incendiary + caliber = "12ga incendiary" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/smg.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/smg.dm new file mode 100644 index 0000000000..598b1845ca --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/smg.dm @@ -0,0 +1,67 @@ +/obj/item/ammo_box/magazine/smgm10mm + name = "Mongrel magazine (10x22mm)" + desc = "A 24-round magazine for the SKM-44v. These rounds do moderate damage, but struggle against armor." + icon_state = "mongrel_mag-24" + base_icon_state = "mongrel_mag" + ammo_type = /obj/item/ammo_casing/c10mm + caliber = "10x22mm" + max_ammo = 24 + +/obj/item/ammo_box/magazine/smgm10mm/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[ammo_count() == 1 ? 1 : round(ammo_count(),3)]" + +/obj/item/ammo_box/magazine/smgm10mm/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/smgm10mm/rubber + name = "SMG magazine (10x22mm rubber)" + desc = "A 24-round magazine for the SkM-44(k). These rubber rounds trade lethality for a heavy impact which can incapacitate targets. Performs even worse against armor." + ammo_type = /obj/item/ammo_casing/c10mm/rubber + +/obj/item/ammo_box/magazine/m45_cobra + name = "SMG magazine (.45)" + desc = "A 24-round magazine for .45 submachine guns. These rounds do moderate damage, but struggle against armor." + icon_state = "c20r45-24" + base_icon_state = "c20r45" + ammo_type = /obj/item/ammo_casing/c45 + caliber = ".45" + max_ammo = 24 + +/obj/item/ammo_box/magazine/m45_cobra/update_icon_state() //This is stupid (whenever ammo is spent, it updates the icon path) + . = ..() + icon_state = "c20r45-[round(ammo_count(),2)]" + +/obj/item/ammo_box/magazine/m45_cobra/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/c44_firestorm_mag + name = "stick magazine (.44 Roumain)" + desc = "A 24-round stick magazine for the toploading Firestorm submachine gun. These rounds do moderate damage, and perform adequately against armor." + icon_state = "firestorm_mag-1" + base_icon_state = "firestorm_mag" + ammo_type = /obj/item/ammo_casing/a44roum + caliber = ".44 Roumain" + max_ammo = 24 + +/obj/item/ammo_box/magazine/c44_firestorm_mag/update_icon_state() + . = ..() + icon_state = "firestorm_mag-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/c44_firestorm_mag/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/c44_firestorm_mag/pan + name = "pan magazine (.44 Roumain)" + desc = "A bulky, 40-round pan magazine for the toploading Firestorm submachine gun. The rate of fire may be low, but this much ammo can mow through anything." + icon_state = "firestorm_pan" + base_icon_state = "firestorm_pan" + max_ammo = 40 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/c44_firestorm_mag/pan/update_icon_state() //Causes the mag to NOT inherit the parent's update_icon oooh the misery + . = ..() + icon_state = "firestorm_pan" + +/obj/item/ammo_box/magazine/c44_firestorm_mag/pan/empty + start_empty = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/sniper.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/sniper.dm new file mode 100644 index 0000000000..b48d76c129 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/sniper.dm @@ -0,0 +1,22 @@ +/obj/item/ammo_box/magazine/sniper_rounds + name = "anti-material rifle magazine (.50 BMG)" + desc = "A large, heavy 6-round box magazine designed for the sniper rifle. These rounds deal absurd damage, able to delimb targets, knock them on their feet, and bypass most protective equipment." + icon_state = "50bmgsniper_mag-1" + base_icon_state = "50bmgsniper_mag" + ammo_type = /obj/item/ammo_casing/p50 + max_ammo = 6 + caliber = ".50 BMG" + w_class = WEIGHT_CLASS_NORMAL + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/sniper_rounds/soporific + name = "anti-material rifle magazine (.50 BMG soporific)" + desc = "A large, heavy 3-round box magazine designed for the sniper rifle. These soporific rounds are completely non-lethal, but render targets asleep for a little under a minute." + ammo_type = /obj/item/ammo_casing/p50/soporific + max_ammo = 3 + +/obj/item/ammo_box/magazine/sniper_rounds/penetrator + name = "anti-material rifle magazine (.50 BMG penetrator)" + desc = "A large, heavy 5-round box magazine designed for the sniper rifle. These penetrator rounds deal incredible damage and will penetrate most structures, though they don't knock down or delimb targets." + ammo_type = /obj/item/ammo_casing/p50/penetrator + max_ammo = 5 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/toy.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/toy.dm new file mode 100644 index 0000000000..c3cbecfcc4 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/external/toy.dm @@ -0,0 +1,30 @@ +/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" + desc = "A toy submachine gun magazine designed to fit harmless foam darts." + icon_state = "smg9mm-42" + base_icon_state = "smg9mm" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart + max_ammo = 20 + +/obj/item/ammo_box/magazine/toy/smg/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[ammo_count() ? 42 : 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" + desc = "A toy pistol magazine designed to fit harmless foam darts." + icon_state = "toy_magazine-1" + base_icon_state = "toy_magazine" + max_ammo = 8 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/toy/pistol/riot + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm new file mode 100644 index 0000000000..00e40c05bf --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm @@ -0,0 +1,96 @@ +/obj/item/ammo_box/magazine/internal/cylinder + name = "revolver cylinder" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 6 + instant_load = TRUE + +/obj/item/ammo_box/magazine/internal/cylinder/get_round(keep = FALSE, counter_clockwise = FALSE) + rotate(counter_clockwise) + + var/b = stored_ammo[1] + if(!keep) + stored_ammo[1] = null + + return b + +/obj/item/ammo_box/magazine/internal/cylinder/proc/rotate(counter_clockwise = FALSE) + var/b + if(!counter_clockwise) + b = stored_ammo[1] + stored_ammo.Cut(1,2) + stored_ammo.Insert(0, b) + else + b = stored_ammo[max_ammo] + stored_ammo.Cut(max_ammo,max_ammo+1) + stored_ammo.Insert(1, 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/ammo_list(drop_list = FALSE) + var/list/L = list() + for(var/i=1 to stored_ammo.len) + var/obj/item/ammo_casing/bullet = stored_ammo[i] + if(bullet) + L.Add(bullet) + if(drop_list)//We have to maintain the list size, to emulate a cylinder + stored_ammo[i] = null + else + L.Add(null) + return L + +/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 + +/obj/item/ammo_box/magazine/internal/cylinder/attackby(obj/item/attacking_obj, mob/user, params, silent = FALSE, replace_spent = FALSE) + var/num_loaded = 0 + if(!can_load(user)) + return + if(istype(attacking_obj, /obj/item/ammo_box)) + var/obj/item/ammo_box/attacking_box = attacking_obj + var/list/ammo_list_no_empty = ammo_list(FALSE) + listclearnulls(ammo_list_no_empty) + for(var/obj/item/ammo_casing/casing_to_insert in attacking_box.stored_ammo) + if(!((instant_load && attacking_box.instant_load) || (ammo_list_no_empty.len >= max_ammo) || do_after(user, 1 SECONDS, attacking_box))) //stupid work around for revolvers + break + var/did_load = give_round(casing_to_insert, replace_spent) + if(!did_load) + break + attacking_box.stored_ammo -= casing_to_insert + if(!silent) + playsound(get_turf(attacking_box), 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) //src is nullspaced, which means internal magazines won't properly play sound, thus we use attacking_box + num_loaded++ + ammo_list_no_empty = ammo_list(FALSE) + listclearnulls(ammo_list_no_empty) + attacking_obj.update_appearance() + update_appearance() + + if(istype(attacking_obj, /obj/item/ammo_casing)) + var/obj/item/ammo_casing/casing_to_insert = attacking_obj + if(give_round(casing_to_insert, replace_spent)) + user.transferItemToLoc(casing_to_insert, src, TRUE) + if(!silent) + playsound(casing_to_insert, 'sound/weapons/gun/general/mag_bullet_insert.ogg', 60, TRUE) + num_loaded++ + update_appearance() + + + if(num_loaded) + if(!silent) + to_chat(user, span_notice("You load [num_loaded] cartridge\s into \the [src]!")) + return num_loaded diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_internal.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_internal.dm new file mode 100644 index 0000000000..1f9b6fcdae --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/_internal.dm @@ -0,0 +1,4 @@ +/obj/item/ammo_box/magazine/internal + desc = "Oh god, this shouldn't be here" + flags_1 = CONDUCT_1 + item_flags = ABSTRACT diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/derringer.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/derringer.dm new file mode 100644 index 0000000000..2cd8f830b0 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/derringer.dm @@ -0,0 +1,13 @@ +/obj/item/ammo_box/magazine/internal/derr38 + name = "derringer muzzle" + ammo_type = /obj/item/ammo_casing/c38 + caliber = ".38" + max_ammo = 2 + instant_load = TRUE + +/obj/item/ammo_box/magazine/internal/derr357 + name = "derringer muzzle" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 2 + instant_load = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/gauss.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/gauss.dm new file mode 100644 index 0000000000..6e561f6d26 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/gauss.dm @@ -0,0 +1,6 @@ +/obj/item/ammo_box/magazine/internal/claris + name = "claris internal magazine" + ammo_type = /obj/item/ammo_casing/caseless/gauss + caliber = "pellet" + max_ammo = 22 + instant_load = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/grenade.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/grenade.dm new file mode 100644 index 0000000000..4fe1e3f5ae --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/grenade.dm @@ -0,0 +1,23 @@ +/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 = "rocket launcher internal magazine" + ammo_type = /obj/item/ammo_casing/caseless/rocket + caliber = "84mm" + max_ammo = 1 + +/obj/item/ammo_casing/a40mm + name = "40mm HE shell" + icon_state = "40mmHE" + caliber = "40mm" + projectile_type = /obj/projectile/bullet/a40mm diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/misc.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/misc.dm new file mode 100644 index 0000000000..55b749ac33 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/misc.dm @@ -0,0 +1,6 @@ +/obj/item/ammo_box/magazine/internal/bow + name = "bowstring" + ammo_type = /obj/item/ammo_casing/caseless/arrow + max_ammo = 1 + start_empty = TRUE + caliber = "arrow" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/revolver.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/revolver.dm new file mode 100644 index 0000000000..1198970c51 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/revolver.dm @@ -0,0 +1,45 @@ +/obj/item/ammo_box/magazine/internal/cylinder/rev38 + name = "detective revolver cylinder" + ammo_type = /obj/item/ammo_casing/c38 + caliber = ".38" + max_ammo = 6 + instant_load = TRUE + +/obj/item/ammo_box/magazine/internal/cylinder/rev38/big + name = "\improper Montagne cylinder" + max_ammo = 7 + +/obj/item/ammo_box/magazine/internal/cylinder/rev4570 + name = "hunting revolver cylinder" + ammo_type = /obj/item/ammo_casing/a4570 + caliber = ".45-70" + max_ammo = 6 + +/obj/item/ammo_box/magazine/internal/cylinder/rus357 + name = "\improper Russian revolver cylinder" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 6 + instant_load = FALSE + +/obj/item/ammo_box/magazine/internal/rus357/Initialize() + stored_ammo += new ammo_type(src) + . = ..() + +/obj/item/ammo_box/magazine/internal/cylinder/pepperbox + name = "pepperbox revolver cylinder" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 5 + instant_load = FALSE + +/obj/item/ammo_box/magazine/internal/cylinder/rev44 + name = "cattleman revolver cylinder" + ammo_type = /obj/item/ammo_casing/a44roum + caliber = ".44 Roumain" + max_ammo = 6 + instant_load = FALSE + +/obj/item/ammo_box/magazine/internal/cylinder/rev44/montagne + name = "montagne revolver cylinder" + instant_load = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/rifle.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/rifle.dm new file mode 100644 index 0000000000..2e361dad48 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/rifle.dm @@ -0,0 +1,34 @@ +/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/a8_50r + caliber = "8x50mmR" + max_ammo = 5 + instant_load = TRUE + +/obj/item/ammo_box/magazine/internal/boltaction/solgov + name = "SSG-669C internal magazine" + ammo_type = /obj/item/ammo_casing/caseless/a858 + caliber = "a858" + max_ammo = 5 + multiload = TRUE + +/obj/item/ammo_box/magazine/internal/boltaction/smile + name = "smile internal magazine" + ammo_type = /obj/item/ammo_casing/a300 + caliber = "a300" + max_ammo = 5 + multiload = TRUE + +/obj/item/ammo_box/magazine/internal/boltaction/polymer + name = "polymer bolt action rifle internal magazine" + ammo_type = /obj/item/ammo_casing/a762_40 + caliber = "7.62x40mm" + max_ammo = 5 + +/obj/item/ammo_box/magazine/internal/vickland + name = "Vickland battle rifle internal magazine" + ammo_type = /obj/item/ammo_casing/a8_50r + caliber = "8x50mmR" + max_ammo = 10 + instant_load = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/shotgun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/shotgun.dm new file mode 100644 index 0000000000..bd8418c85d --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/shotgun.dm @@ -0,0 +1,84 @@ +/obj/item/ammo_box/magazine/internal/shot + name = "shotgun internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/beanbag + caliber = "12ga" + max_ammo = 4 + +/obj/item/ammo_box/magazine/internal/shot/tube + name = "dual feed shotgun internal tube" + ammo_type = /obj/item/ammo_casing/shotgun/rubbershot + max_ammo = 5 + +/obj/item/ammo_box/magazine/internal/shot/lethal + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/ammo_box/magazine/internal/shot/dual + name = "double-barrel shotgun internal magazine" + max_ammo = 2 + instant_load = TRUE + +/obj/item/ammo_box/magazine/internal/shot/dual/lethal + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/ammo_box/magazine/internal/shot/improvised + name = "improvised shotgun internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/improvised + max_ammo = 1 + start_empty = TRUE + +/obj/item/ammo_box/magazine/internal/shot/riot + name = "riot shotgun internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + max_ammo = 8 + +/obj/item/ammo_box/magazine/internal/shot/bounty + name = "triple-barrel shotgun internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/incapacitate + max_ammo = 3 + +/obj/item/ammo_box/magazine/internal/shot/winchester + name = "winchester internal magazine" + ammo_type = /obj/item/ammo_casing/c38 + caliber = ".38" + max_ammo = 12 + +/obj/item/ammo_box/magazine/internal/shot/winchester/absolution + name = "absolution internal magazine" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 8 + +/obj/item/ammo_box/magazine/internal/shot/winchester/conflagration + name = "conflagration internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 5 + +/obj/item/ammo_box/magazine/internal/shot/beacon + name = "beacon internal magazine" + ammo_type = /obj/item/ammo_casing/a4570 + caliber = ".45-70" + max_ammo = 2 + multiload = FALSE + +/obj/item/ammo_box/magazine/internal/shot/underbarrel + name = "underbarrel shotgun internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + max_ammo = 1 + start_empty = TRUE + +/obj/item/ammo_box/magazine/internal/shot/sex + name = "six-barrel shotgun internal magazine" + max_ammo = 6 + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/ammo_box/magazine/internal/shot/hundred + name = "hundred-barrel shotgun internal magazine" + max_ammo = 100 + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/ammo_box/magazine/internal/shot/twobore + name = "two-bore shotgun internal magazine" + max_ammo = 2 + caliber = "twobore" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot/twobore diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/toy.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/boxes_magazines/internal/toy.dm new file mode 100644 index 0000000000..7a520c6a1f --- /dev/null +++ b/modular_starfly/modules/gun_lore/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/modular_starfly/modules/gun_lore/code/modules/projectiles/gun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/gun.dm new file mode 100644 index 0000000000..067d1c55de --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/gun.dm @@ -0,0 +1,1227 @@ +/obj/item/gun + name = "gun" + desc = "It's a gun. It's pretty terrible, though." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "flatgun" + item_state = "gun" + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_SUITSTORE + custom_materials = list(/datum/material/iron=2000) + w_class = WEIGHT_CLASS_NORMAL + throwforce = 5 + throw_speed = 3 + throw_range = 5 + force = 5 + + bad_type = /obj/item/gun + + item_flags = NEEDS_PERMIT + attack_verb = list("struck", "hit", "bashed") + pickup_sound = 'sound/items/handling/gun_pickup.ogg' + drop_sound = 'sound/items/handling/gun_drop.ogg' + //trigger guard on the weapon, hulks can't fire them with their big meaty fingers + trigger_guard = TRIGGER_GUARD_NORMAL + + light_system = MOVABLE_LIGHT_DIRECTIONAL + + ///The manufacturer of this weapon. For flavor mostly. If none, this will not show. + var/manufacturer = MANUFACTURER_NONE + +/* + * Muzzle +*/ + ///Effect for the muzzle flash of the gun. + var/obj/effect/muzzle_flash/muzzle_flash + + light_range = 3 + light_color = COLOR_VERY_SOFT_YELLOW + light_on = FALSE + + ///Icon state of the muzzle flash effect. + var/muzzleflash_iconstate + +/* + * Firing +*/ + var/actually_shoots = TRUE //is this gun a brick and doesnt fire bullet + var/fire_sound = 'sound/weapons/gun/pistol/shot.ogg' + var/vary_fire_sound = TRUE + var/fire_sound_volume = 50 + var/dry_fire_sound = 'sound/weapons/gun/general/dry_fire.ogg' + var/dry_fire_text = "click" + +/* + * Reloading +*/ + var/obj/item/ammo_casing/chambered = null + ///Whether the gun can be tacloaded by slapping a fresh magazine directly on it + var/tac_reloads = TRUE + ///If we have the 'snowflake mechanic,' how long should it take to reload? + var/tactical_reload_delay = 1 SECONDS + +//BALLISTIC + ///Compatible magazines with the gun + var/default_ammo_type + ///Allowed base types of magazines with the gun + var/allowed_ammo_types + ///Incompatible magazines with the gun + var/blacklisted_ammo_types + ///Whether the gun alarms when empty or not. + var/empty_alarm = FALSE + ///Do we eject the magazine upon runing out of ammo? + var/empty_autoeject = FALSE + ///Whether the gun supports multiple special mag types + var/special_mags = FALSE + + ///Actual magazine currently contained within the gun + var/obj/item/ammo_box/magazine/magazine + ///whether the gun ejects the chambered casing + var/casing_ejector = TRUE + ///Whether the gun has an internal magazine or a detatchable one. Overridden by BOLT_TYPE_NO_BOLT. + var/internal_magazine = FALSE + ///Whether the gun *can* be reloaded + var/sealed_magazine = FALSE + + + ///Phrasing of the magazine in examine and notification messages; ex: magazine, box, etx + var/magazine_wording = "magazine" + ///Phrasing of the cartridge in examine and notification messages; ex: bullet, shell, dart, etc. + var/cartridge_wording = "bullet" + + ///sound when inserting magazine + var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg' + ///sound when inserting an empty magazine + var/load_empty_sound = 'sound/weapons/gun/general/magazine_insert_empty.ogg' + ///volume of loading sound + var/load_sound_volume = 40 + ///whether loading sound should vary + var/load_sound_vary = TRUE + ///Sound of ejecting a magazine + var/eject_sound = 'sound/weapons/gun/general/magazine_remove_full.ogg' + ///sound of ejecting an empty magazine + var/eject_empty_sound = 'sound/weapons/gun/general/magazine_remove_empty.ogg' + ///volume of ejecting a magazine + var/eject_sound_volume = 40 + ///whether eject sound should vary + var/eject_sound_vary = TRUE + +//ENERGY + //What type of power cell this uses + var/obj/item/stock_parts/cell/gun/cell + //Can it be charged in a recharger? + var/can_charge = TRUE + var/selfcharge = FALSE + var/charge_timer = 0 + var/charge_delay = 8 + //whether the gun's cell drains the cyborg user's cell to recharge + var/use_cyborg_cell = FALSE + //Time it takes to unscrew the cell + var/unscrewing_time = 2 SECONDS + + ///if the gun's cell cannot be replaced + var/internal_cell = FALSE + + var/list/ammo_type = list(/obj/item/ammo_casing/energy) + //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. + var/select = 1 + +/* + * Operation +*/ + //whether or not a message is displayed when fired + var/suppressed = FALSE + var/suppressed_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg' + var/suppressed_volume = 60 + + //true if the gun is wielded via twohanded component, shouldnt affect anything else + var/wielded = FALSE + //true if the gun is wielded after delay, should affects accuracy + var/wielded_fully = FALSE + ///Slowdown for wielding + var/wield_slowdown = 0.1 + ///slowdown for aiming whilst wielding + var/aimed_wield_slowdown = 0.1 + ///How long between wielding and firing in tenths of seconds + var/wield_delay = 0.4 SECONDS + ///Storing value for above + var/wield_time = 0 + +// BALLISTIC + ///Whether the gun has to be racked each shot or not. + var/semi_auto = TRUE + ///The bolt type of the gun, affects quite a bit of functionality, see gun.dm in defines for bolt types: BOLT_TYPE_STANDARD; BOLT_TYPE_LOCKING; BOLT_TYPE_OPEN; BOLT_TYPE_NO_BOLT + var/bolt_type = BOLT_TYPE_STANDARD + ///Used for locking bolt and open bolt guns. Set a bit differently for the two but prevents firing when true for both. + var/bolt_locked = FALSE + ///Phrasing of the bolt in examine and notification messages; ex: bolt, slide, etc. + var/bolt_wording = "bolt" + ///length between individual racks + var/rack_delay = 5 + ///time of the most recent rack, used for cooldown purposes + var/recent_rack = 0 + + ///Whether the gun can be sawn off by sawing tools + var/can_be_sawn_off = FALSE + //description change if weapon is sawn-off + var/sawn_desc = null + var/sawn_off = FALSE + + ///sound of racking + var/rack_sound = 'sound/weapons/gun/general/bolt_rack.ogg' + ///volume of racking + var/rack_sound_volume = 60 + ///whether racking sound should vary + var/rack_sound_vary = TRUE + ///sound of when the bolt is locked back manually + var/lock_back_sound = 'sound/weapons/gun/general/slide_lock_1.ogg' + ///volume of lock back + var/lock_back_sound_volume = 60 + ///whether lock back varies + var/lock_back_sound_vary = TRUE + + ///sound of dropping the bolt or releasing a slide + var/bolt_drop_sound = 'sound/weapons/gun/general/bolt_drop.ogg' + ///volume of bolt drop/slide release + var/bolt_drop_sound_volume = 60 + ///empty alarm sound (if enabled) + var/empty_alarm_sound = 'sound/weapons/gun/general/empty_alarm.ogg' + ///empty alarm volume sound + var/empty_alarm_volume = 70 + ///whether empty alarm sound varies + var/empty_alarm_vary = TRUE + +/* + * Stats +*/ + var/weapon_weight = WEAPON_LIGHT + //Alters projectile damage multiplicatively based on this value. Use it for "better" or "worse" weapons that use the same ammo. + var/projectile_damage_multiplier = 1 + //Speed someone can be flung if its point blank + var/pb_knockback = 0 + + //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once. + var/randomspread = TRUE + ///How much the bullet scatters when fired while wielded. + var/spread = 4 + ///How much the bullet scatters when fired while unwielded. + var/spread_unwielded = 12 + //additional spread when dual wielding + var/dual_wield_spread = 24 + + + ///Screen shake when the weapon is fired while wielded. + var/recoil = 0 + ///Screen shake when the weapon is fired while unwielded. + var/recoil_unwielded = 0 + ///a multiplier of the duration the recoil takes to go back to normal view, this is (recoil*recoil_backtime_multiplier)+1 + var/recoil_backtime_multiplier = 2 + ///this is how much deviation the gun recoil can have, recoil pushes the screen towards the reverse angle you shot + some deviation which this is the max. + var/recoil_deviation = 22.5 + + ///Used if the guns recoil is lower then the min, it clamps the highest recoil + var/min_recoil = 0 + ///if we want a min recoil (or lack of it) whilst aiming + var/min_recoil_aimed = 0 + + var/gunslinger_recoil_bonus = 0 + var/gunslinger_spread_bonus = 0 + + /// how many shots per burst, Ex: most machine pistols, M90, some ARs are 3rnd burst, while others like the GAR and laser minigun are 2 round burst. + var/burst_size = 3 + ///The rate of fire when firing in a burst. Not the delay between bursts + var/burst_delay = 0.15 SECONDS + ///The rate of fire when firing full auto and semi auto, and between bursts; for bursts its fire delay + burst_delay after every burst + var/fire_delay = 0.2 SECONDS + //Prevent the weapon from firing again while already firing + var/firing_burst = 0 + +/* + * Overlay +*/ + ///Used for positioning ammo count overlay on sprite + var/ammo_x_offset = 0 + var/ammo_y_offset = 0 + +//BALLISTIC + ///Whether the sprite has a visible magazine or not + var/mag_display = FALSE + ///Whether the sprite has a visible ammo display or not + var/mag_display_ammo = FALSE + ///Whether the sprite has a visible indicator for being empty or not. + var/empty_indicator = FALSE + ///Whether the sprite has a visible magazine or not + var/show_magazine_on_sprite = FALSE + ///Do we show how much ammo is left on the sprite? In increments of 20. + var/show_ammo_capacity_on_magazine_sprite = FALSE + ///Whether the sprite has a visible ammo display or not + var/show_magazine_on_sprite_ammo = FALSE + ///Whether the gun supports multiple special mag types + var/unique_mag_sprites_for_variants = FALSE + +//ENERGY + //Do we handle overlays with base update_appearance()? + var/automatic_charge_overlays = TRUE + var/charge_sections = 4 + //if this gun uses a stateful charge bar for more detail + var/shaded_charge = FALSE + //Modifies WHOS state //im SOMEWHAT this is wether or not the overlay changes based on the ammo type selected + var/modifystate = TRUE + +/* + * Attachment +*/ + ///The types of attachments allowed, a list of types. SUBTYPES OF AN ALLOWED TYPE ARE ALSO ALLOWED. + var/list/valid_attachments = list() + ///The types of attachments that are unique to this gun. Adds it to the base valid_attachments list. So if this gun takes a special stock, add it here. + var/list/unique_attachments = list() + ///The types of attachments that aren't allowed. Removes it from the base valid_attachments list. + var/list/refused_attachments + ///Number of attachments that can fit on a given slot + var/list/slot_available = ATTACHMENT_DEFAULT_SLOT_AVAILABLE + ///Offsets for the slots on this gun. should be indexed by SLOT and then by X/Y + var/list/slot_offsets = list() + var/underbarrel_prefix = "" // so the action has the right icon for underbarrel gun + +/* + * Zooming +*/ + ///Whether the gun generates a Zoom action on creation + var/zoomable = TRUE + //Zoom toggle + var/zoomed = FALSE + ///Distance in TURFs to move the user's screen forward (the "zoom" effect) + var/zoom_amt = 3 + var/zoom_out_amt = 0 + var/datum/action/toggle_scope_zoom/azoom + +/* + * Safety +*/ + ///Does this gun have a saftey and thus can toggle it? + var/has_safety = FALSE + ///If the saftey on? If so, we can't fire the weapon + var/safety = FALSE + ///The wording of safety. Useful for guns that have a non-standard safety system, like a revolver + var/safety_wording = "safety" + ///multiplier for this gun's misfire chances. Closer to 0 is better. + var/safety_multiplier = 1 + +/* + * Spawn Info (Stuff that becomes useless onces the gun is spawned, mostly here for mappers) +*/ + ///Attachments spawned on initialization. Should also be in valid attachments or it SHOULD(once i add that) fail + var/list/default_attachments = list() + +//ENERGY + //set to true so the gun is given an empty cell + var/spawn_no_ammo = FALSE + +// Need to sort + ///trigger guard on the weapon. Used for hulk mutations and ashies. I honestly dont know how usefult his is, id avoid touching it + trigger_guard = TRIGGER_GUARD_NORMAL + + /// after initializing, we set the firemode to this + var/default_firemode = FIREMODE_SEMIAUTO + ///Firemode index, due to code shit this is the currently selected firemode + var/firemode_index + /// Our firemodes, subtract and add to this list as needed. NOTE that the autofire component is given on init when FIREMODE_FULLAUTO is here. + var/list/gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_FULLAUTO, FIREMODE_OTHER, FIREMODE_OTHER_TWO) + /// A acoc list that determines the names of firemodes. Use if you wanna be weird and set the name of say, FIREMODE_OTHER to "Underbarrel grenade launcher" for example. + var/list/gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto", FIREMODE_OTHER = "misc. fire", FIREMODE_OTHER_TWO = "very misc. fire", FIREMODE_UNDERBARREL = "underbarrel weapon") + ///BASICALLY: the little button you select firing modes from? this is jsut the prefix of the icon state of that. For example, if we set it as "laser", the fire select will use "laser_single" and so on. + var/fire_select_icon_state_prefix = "" + ///If true, we put "safety_" before fire_select_icon_state_prefix's prefix. ex. "safety_laser_single" + var/adjust_fire_select_icon_state_on_safety = FALSE + + ///Are we firing a burst? If so, dont fire again until burst is done + var/currently_firing_burst = FALSE + ///This prevents gun from firing until the coodown is done, affected by lag + var/current_cooldown = 0 + +/obj/item/gun/Initialize(mapload, spawn_empty) + . = ..() + RegisterSignal(src, COMSIG_TWOHANDED_WIELD, PROC_REF(on_wield)) + RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, PROC_REF(on_unwield)) + muzzle_flash = new(src, muzzleflash_iconstate) + build_zooming() + build_firemodes() + if(sawn_off) + sawoff(forced = TRUE) + if(slot_flags & ITEM_SLOT_SUITSTORE) + ADD_TRAIT(src, TRAIT_FORCE_SUIT_STORAGE, REF(src)) + +/obj/item/gun/ComponentInitialize() + . = ..() + var/list/attachment_list = valid_attachments + attachment_list += unique_attachments + if(refused_attachments) + for(var/to_remove in attachment_list) + if(refused_attachments.Find(to_remove)) + attachment_list -= to_remove + + AddComponent(/datum/component/attachment_holder, slot_available, attachment_list, slot_offsets, default_attachments) + AddComponent(/datum/component/two_handed) + +/// triggered on wield of two handed item +/obj/item/gun/proc/on_wield(obj/item/source, mob/user, instant) + wielded = TRUE + INVOKE_ASYNC(src, PROC_REF(do_wield), user, instant) + +/obj/item/gun/proc/do_wield(mob/user, instant) + user.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/gun, multiplicative_slowdown = wield_slowdown) + wield_time = world.time + wield_delay + if(azoom) + azoom.Grant(user) + if(wield_time > 0 && !instant) + if(do_after( + user, + wield_delay, + user, + IGNORE_USER_LOC_CHANGE | IGNORE_TARGET_LOC_CHANGE, + TRUE, + CALLBACK(src, PROC_REF(is_wielded)) + ) + ) + wielded_fully = TRUE + return TRUE + else + wielded_fully = TRUE + return TRUE + +/// triggered on unwield of two handed item +/obj/item/gun/proc/on_unwield(obj/item/source, mob/user) + wielded = FALSE + wielded_fully = FALSE + zoom(user, forced_zoom = FALSE) + user.remove_movespeed_modifier(/datum/movespeed_modifier/gun) + if(azoom) + azoom.Remove(user) + +/obj/item/gun/proc/is_wielded() + return wielded + +/obj/item/gun/Destroy() + if(chambered) //Not all guns are chambered (EMP'ed energy guns etc) + QDEL_NULL(chambered) + if(azoom) + QDEL_NULL(azoom) + if(muzzle_flash) + QDEL_NULL(muzzle_flash) + if(magazine) + QDEL_NULL(magazine) + return ..() + +/obj/item/gun/handle_atom_del(atom/A) + if(A == chambered) + chambered = null + update_icon() + return ..() + +/obj/item/gun/examine(mob/user) + . = ..() + if(manufacturer) + . += span_notice("It has [manufacturer] engraved on it.") + if(HAS_TRAIT(src,TRAIT_FORCE_SUIT_STORAGE)) + . += span_notice("It has clips and hooks for easy carrying.") + +/obj/item/gun/examine_more(mob/user) + . = ..() + if(has_safety) + . += "The safety is [safety ? span_green("ON") : span_red("OFF")]. Right-Click to toggle the safety." + +/obj/item/gun/attackby(obj/item/I, mob/living/user, params) + . = ..() + if(gun_firemodes[firemode_index] == FIREMODE_UNDERBARREL) + return TRUE + +/obj/item/gun/equipped(mob/living/user, slot) + . = ..() + if(zoomed && user.get_active_held_item() != src) + zoom(user, user.dir, FALSE) //we can only stay zoomed in if it's in our hands //yeah and we only unzoom if we're actually zoomed using the gun!! + +/obj/item/gun/attack(mob/M as mob, mob/user) + if(user.a_intent == INTENT_HARM || !actually_shoots) //Flogging + return ..() + return + +//called after the gun has successfully fired its chambered ammo. +/obj/item/gun/proc/process_chamber(atom/shooter) + SEND_SIGNAL(src, COMSIG_GUN_CHAMBER_PROCESSED) + return FALSE + +//check if there's enough ammo/energy/whatever to shoot one time +//i.e if clicking would make it shoot +/obj/item/gun/proc/can_shoot() + if(safety) + return FALSE + return TRUE + +/obj/item/gun/emp_act(severity) + . = ..() + if(!(. & EMP_PROTECT_CONTENTS)) + for(var/obj/O in contents) + O.emp_act(severity) + + +/obj/item/gun/proc/recharge_newshot() + return + +/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) + . = ..() + if(!actually_shoots)// this gun doesn't actually fire bullets. Dont shoot. + return + //No target? Why are we even firing anyways... + if(!target) + return + //If we are burst firing, don't fire, obviously + if(currently_firing_burst) + return + //This var happens when we are either clicking someone next to us or ourselves. Check if we don't want to fire... + if(flag) + 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 != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected) + return + if(ismob(target) && user.a_intent == INTENT_GRAB) + if(user.GetComponent(/datum/component/gunpoint)) + to_chat(user, span_warning("You are already holding someone up!")) + return + user.AddComponent(/datum/component/gunpoint, target, src) + return + if(iscarbon(target)) + var/mob/living/carbon/C = target + for(var/i in C.all_wounds) + var/datum/wound/W = i + if(W.try_treating(src, user)) + return // another coward cured! + + // Good job, but we have exta checks to do... + return pre_fire(target, user, TRUE, flag, params, null) + +/obj/item/gun/proc/pre_fire(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0, dual_wielded_gun = FALSE) + add_fingerprint(user) + + // If we have a cooldown, don't do anything, obviously + if(current_cooldown) + return + + //We check if the user can even use the gun, if not, we assume the user isn't alive(turrets) so we go ahead. + if(istype(user)) + var/mob/living/living_user = user + if(!can_trigger_gun(living_user)) + return + + //If targetting the mouth, we do suicide instead. + if(flag) + if(user.zone_selected == BODY_ZONE_PRECISE_MOUTH) + handle_suicide(user, target, params) + return + + //Just because we can pull the trigger doesn't mean it can fire. Mostly for safties. + if(!can_shoot()) + shoot_with_empty_chamber(user) + return + + //we then check our weapon weight vs if we are being wielded... + if(weapon_weight == WEAPON_VERY_HEAVY && (!wielded_fully)) + to_chat(user, span_warning("You need a fully secure grip to fire [src]!")) + return + + if(weapon_weight == WEAPON_HEAVY && (!wielded)) + to_chat(user, span_warning("You need a more secure grip to fire [src]!")) + return + //If we have the pacifist trait and a chambered round, don't fire. Honestly, pacifism quirk is pretty stupid, and as such we check again in process_fire() anyways + if(chambered) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. + if(chambered.harmful) // Is the bullet chambered harmful? + to_chat(user, span_warning("[src] is lethally chambered! You don't want to risk harming anyone...")) + return + + //Dual wielding handling. Not the biggest fan of this, but it's here. Dual berettas not included + var/loop_counter = 0 + if(ishuman(user) && user.a_intent == INTENT_HARM && !dual_wielded_gun) + var/mob/living/carbon/human/our_cowboy = user + for(var/obj/item/gun/found_gun in our_cowboy.held_items) + if(found_gun == src || found_gun.weapon_weight >= WEAPON_MEDIUM) + continue + else if(found_gun.can_trigger_gun(user)) + bonus_spread += dual_wield_spread + loop_counter++ + addtimer(CALLBACK(found_gun, TYPE_PROC_REF(/obj/item/gun, pre_fire), target, user, TRUE, FALSE, params, null, bonus_spread, TRUE), loop_counter) + + //get current firemode + var/current_firemode = gun_firemodes[firemode_index] + //FIREMODE_OTHER and its sister directs you to another proc for special handling + if(current_firemode == FIREMODE_OTHER) + return process_other(target, user, message, flag, params, zone_override, bonus_spread) + if(current_firemode == FIREMODE_OTHER_TWO) + return process_other_two(target, user, message, flag, params, zone_override, bonus_spread) + + //if all of that succeded, we finally get to process firing + return process_fire(target, user, TRUE, params, null, bonus_spread) + +/obj/item/gun/proc/process_other(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0) + return //use this for 'underbarrels!! + +/obj/item/gun/proc/process_other_two(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0) + return //reserved in case another fire mode is needed, if you need special behavior, put it here then call process_fire, or call process_fire and have the special behavior there + +/** + * Handles everything involving firing. + * * gun.dm is still a fucking mess, and I will document everything next time i get to it... for now this will suffice. + * + * Returns TRUE or FALSE depending on if it actually fired a shot. + * Arguments: + * * target - The atom we are trying to hit. + * * user - The living mob firing the gun, if any. + * * message - Do we show the usual messages? eg. "x fires the y!" + * * params - Is the params string from byond [/atom/proc/Click] code, see that documentation. + * * zone_override - The bodypart we attempt to hit, sometimes hits another. + * * bonus_spread - Adds this value to spread, in this case used by dual wielding. + * * burst_firing - Not to be confused with currently_firing_burst. This var is TRUE when we are doing a burst except for the first shot in a burst, as to override the spam burst checks. + * * spread_override - Bullet spread is forcibly set to this. This is usually because of bursts attempting to share the same burst trajectory. + * * iteration - Which shot in a burst are we in. + */ +/obj/item/gun/proc/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, burst_firing = FALSE, spread_override = 0, iteration = 0) + //OKAY, this prevents us from firing until our cooldown is done + if(!burst_firing) //if we're firing a burst, dont interfere to avoid issues + if(current_cooldown) + return FALSE + + //Check one last time for safeties... + if(!can_shoot()) + shoot_with_empty_chamber(user) + currently_firing_burst = FALSE + return FALSE + + //special hahnding for burst firing + if(burst_firing) + if(!user || !currently_firing_burst) + currently_firing_burst = FALSE + return FALSE + if(!issilicon(user)) + //If we aren't holding the gun, what are we doing, stop firing! + if(iteration > 1 && !(user.is_holding(src))) + currently_firing_burst = FALSE + return FALSE + + //Do we have a round? If not, stop the whole chain, and if we do, check if the gun is chambered. Pacisim is pretty lame anyways. + if(chambered) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. + if(chambered.harmful) // Is the bullet chambered harmful? + to_chat(user, span_warning("[src] is lethally chambered! You don't want to risk harming anyone...")) + currently_firing_burst = FALSE //no burst 4 u + return FALSE + else + shoot_with_empty_chamber(user) + currently_firing_burst = FALSE + return FALSE + + // we hold the total spread in this var + var/sprd + // if we ARE burst firing and don't have "randomspread", we add the burst's penalty on top of it. + if(burst_firing && !randomspread) + bonus_spread += burst_size * iteration + + //override spread? usually happens only in bursts + if(spread_override && !randomspread) + sprd = spread_override + else + //Calculate spread + sprd = calculate_spread(user, bonus_spread) + + before_firing(target,user) + //If we cant fire the round, just end the proc here. Otherwise, continue + if(!chambered.fire_casing(target, user, params, , suppressed, zone_override, sprd, src)) + shoot_with_empty_chamber(user) + currently_firing_burst = FALSE + return FALSE + //Are we PBing someone? If so, set pointblank to TRUE + shoot_live_shot(user, (get_dist(user, target) <= 1), target, message) //Making sure whether the target is in vicinity for the pointblank shot + + //process the chamber... + process_chamber(shooter = user) + update_appearance() + //get our current firemode... + var/current_firemode = gun_firemodes[firemode_index] + + //If we are set to burst fire, then we burst fire! + if(burst_size > 1 && (current_firemode == FIREMODE_BURST) && !burst_firing) + currently_firing_burst = TRUE + for(var/i = 2 to burst_size) //we fire the first burst normally, hence why its 2 + addtimer(CALLBACK(src, PROC_REF(process_fire), target, user, message, params, zone_override, 0, TRUE, sprd, i), burst_delay * (i - 1)) + + //if we have a fire delay, set up a cooldown + if(fire_delay && (!burst_firing && !currently_firing_burst)) + current_cooldown = TRUE + addtimer(CALLBACK(src, PROC_REF(reset_current_cooldown)), fire_delay) + if(burst_firing && iteration >= burst_size) + current_cooldown = TRUE + addtimer(CALLBACK(src, PROC_REF(reset_current_cooldown)), fire_delay+burst_delay) + currently_firing_burst = FALSE + + // update our inhands... + if(user) + user.update_inv_hands() + + SSblackbox.record_feedback("tally", "gun_fired", 1, type) + return TRUE + +/obj/item/gun/proc/reset_current_cooldown() + current_cooldown = FALSE + +/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) + if(!safety) + to_chat(user, span_danger("*[dry_fire_text]*")) + playsound(src, dry_fire_sound, 30, TRUE) + return + to_chat(user, span_danger("Safeties are active on the [src]! Turn them off to fire!")) + + +/obj/item/gun/proc/shoot_live_shot(mob/living/user, pointblank = FALSE, atom/pbtarget = null, message = TRUE) + var/actual_angle = get_angle_with_scatter((user || get_turf(src)), pbtarget, rand(-recoil_deviation, recoil_deviation) + 180) + var/muzzle_angle = Get_Angle(get_turf(src), pbtarget) + + user.changeNext_move(clamp(fire_delay, 0, CLICK_CD_RANGE)) + + if(muzzle_flash && !muzzle_flash.applied) + handle_muzzle_flash(user, muzzle_angle) + + if(wielded_fully) + simulate_recoil(user, recoil, actual_angle) + else if(!wielded_fully) + var/recoil_temp = recoil_unwielded + var/obj/item/shield/shield = user.get_inactive_held_item() + if(istype(shield)) + recoil_temp += shield.recoil_bonus + simulate_recoil(user, recoil_temp, actual_angle) + + if(suppressed) + playsound(user, suppressed_sound, suppressed_volume, vary_fire_sound, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0) + else + playsound(user, fire_sound, fire_sound_volume, vary_fire_sound) + if(message) + if(pointblank) + user.visible_message( + span_danger("[user] fires [src] point blank at [pbtarget]!"), + span_danger("You fire [src] point blank at [pbtarget]!"), + span_hear("You hear a gunshot!"), COMBAT_MESSAGE_RANGE, pbtarget + ) + to_chat(pbtarget, span_userdanger("[user] fires [src] point blank at you!")) + if(pb_knockback > 0 && ismob(pbtarget)) + var/mob/PBT = pbtarget + var/atom/throw_target = get_edge_target_turf(PBT, user.dir) + PBT.throw_at(throw_target, pb_knockback, 2) + else + user.visible_message( + span_danger("[user] fires [src]!"), + blind_message = span_hear("You hear a gunshot!"), + vision_distance = COMBAT_MESSAGE_RANGE, + ignored_mobs = user + ) + + //cloudy sent a meme in the discord. i dont know if its true, but i made this piece of code in honor of it + var/mob/living/carbon/human/living_human = user + if(istype(living_human)) + if(!living_human.wear_neck) + return //if nothing on the neck, don't do anything + var/current_month = text2num(time2text(world.timeofday, "MM")) + var/static/regex/bian = regex("(?:^\\W*lesbian)", "i") + + if(current_month == JUNE) + return //if it isn't june, don't do this easter egg + + if(!findtext(bian, living_human.generic_adjective)) + return //dont bother if we already are affected by it + + if(istype(living_human.wear_neck, /obj/item/clothing/neck/tie/lesbian) || living_human.wear_neck.icon_state == "lesbian") + var/use_space = "[living_human.generic_adjective ? " " : ""]" + living_human.generic_adjective = "lesbian[use_space][living_human.generic_adjective]" //i actually don't remember the meme. it was something like lesbians will stop working if they see another with a gun. or something. + +/obj/item/gun/CtrlClick(mob/user) + . = ..() + if(isliving(user) && in_range(src, user)) + toggle_safety(user) + +// /obj/item/gun/attack_hand_secondary(mob/user, list/modifiers) +// if(toggle_safety(user)) +// return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN +// return ..() + +// /obj/item/gun/attackby_secondary(obj/item/weapon, mob/user, params) +// if(toggle_safety(user)) +// return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN +// return ..() + +// /obj/item/gun/attack_self_secondary(mob/user, modifiers) +// if(toggle_safety(user)) +// return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN +// return ..() + +/obj/item/gun/proc/toggle_safety(mob/user, silent=FALSE, override_check = FALSE) + if(!has_safety) + return FALSE + + // only checks for first level storage e.g pockets, hands, suit storage, belts, nothing in containers + if(!in_contents_of(user) && !override_check) + return FALSE + + safety = !safety + + if(!silent) + playsound(user, 'sound/weapons/gun/general/selector.ogg', 100, TRUE) + user.visible_message( + span_notice("[user] turns the [safety_wording] on [src] [safety ? span_green("ON") : span_red("OFF")]."), + span_notice("You turn the [safety_wording] on [src] [safety ? span_green("ON") : span_red("OFF")]."), + ) + + update_appearance() + return TRUE + +/obj/item/gun/attack_hand(mob/user, list/modifiers) + . = ..() + update_appearance() + +/obj/item/gun/pickup(mob/user) + . = ..() + update_appearance() + +/obj/item/gun/dropped(mob/user) + . = ..() + update_appearance() + if(zoomed) + zoom(user, user.dir) + +/obj/item/gun/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(prob(GUN_NO_SAFETY_MALFUNCTION_CHANCE_HIGH)) + discharge("hits the ground hard") + +/obj/item/gun/update_overlays() + . = ..() + if(ismob(loc) && has_safety) + var/mutable_appearance/safety_overlay + safety_overlay = mutable_appearance('icons/obj/guns/safety.dmi') + if(safety) + safety_overlay.icon_state = "[safety_wording]-on" + else + safety_overlay.icon_state = "[safety_wording]-off" + . += safety_overlay + +#define BRAINS_BLOWN_THROW_RANGE 2 +#define BRAINS_BLOWN_THROW_SPEED 1 + +/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer) + if(!ishuman(user) || !ishuman(target)) + return + + if(current_cooldown) + return + + if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can shoot. + shoot_with_empty_chamber(user) + return + + if(user == target) + target.visible_message(span_warning("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger..."), \ + span_userdanger("You stick [src] in your mouth, ready to pull the trigger...")) + else + target.visible_message(span_warning("[user] points [src] at [target]'s head, ready to pull the trigger..."), \ + span_userdanger("[user] points [src] at your head, ready to pull the trigger...")) + + current_cooldown = TRUE + + if(!bypass_timer && (!do_after(user, 100, target) || user.zone_selected != BODY_ZONE_PRECISE_MOUTH)) + if(user) + if(user == target) + user.visible_message(span_notice("[user] decided not to shoot.")) + else if(target && target.Adjacent(user)) + target.visible_message(span_notice("[user] has decided to spare [target]."), span_notice("[user] has decided to spare your life!")) + current_cooldown = FALSE + return + + current_cooldown = FALSE + + target.visible_message(span_warning("[user] pulls the trigger!"), span_userdanger("[(user == target) ? "You pull" : "[user] pulls"] the trigger!")) + + if(chambered && chambered.BB && can_trigger_gun(user)) + chambered.BB.damage *= 3 + //Check is here for safeties and such, brain will be removed after + if(!pre_fire(target, user, TRUE, FALSE, params, BODY_ZONE_HEAD)) // We're already in handle_suicide, hence the 4th parameter needs to be FALSE to avoid circular logic. Also, BODY_ZONE_HEAD because we want to damage the head as a whole. + return + + var/obj/item/organ/brain/brain_to_blast = target.getorganslot(ORGAN_SLOT_BRAIN) + if(brain_to_blast) + + //Check if the projectile is actually damaging and not of type STAMINA + if(chambered.BB.nodamage || !chambered.BB.damage || chambered.BB.damage_type == STAMINA) + return + + //Remove brain of the mob shot + brain_to_blast.Remove(target) + + var/turf/splat_turf = get_turf(target) + //Move the brain of the person shot to selected turf + brain_to_blast.forceMove(splat_turf) + + var/turf/splat_target = get_ranged_target_turf(target, REVERSE_DIR(target.dir), BRAINS_BLOWN_THROW_RANGE) + var/datum/callback/gibspawner = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(spawn_atom_to_turf), /obj/effect/gibspawner/generic, brain_to_blast, 1, FALSE, target) + //Throw the brain that has been removed away and place a gibspawner on landing + brain_to_blast.throw_at(splat_target, BRAINS_BLOWN_THROW_RANGE, BRAINS_BLOWN_THROW_SPEED, callback = gibspawner) + +#undef BRAINS_BLOWN_THROW_RANGE +#undef BRAINS_BLOWN_THROW_SPEED + +//Happens before the actual projectile creation +/obj/item/gun/proc/before_firing(atom/target,mob/user) + return + +/obj/item/gun/proc/calculate_recoil(mob/living/user, recoil_bonus = 0) + if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) + recoil_bonus += gunslinger_recoil_bonus + recoil_bonus *= user.recoil_effect + return clamp(recoil_bonus, min_recoil, INFINITY) + +/obj/item/gun/proc/calculate_spread(mob/user, bonus_spread) + var/final_spread = 0 + var/randomized_gun_spread = 0 + var/randomized_bonus_spread = 0 + + final_spread += bonus_spread + + if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) + randomized_bonus_spread += min(gunslinger_spread_bonus, rand(0, gunslinger_spread_bonus)) + + if(HAS_TRAIT(user, TRAIT_POOR_AIM)) + randomized_bonus_spread += rand(0, 25) + + //We will then calculate gun spread depending on if we are fully wielding (after do_after) the gun or not + randomized_gun_spread = rand(0, wielded_fully ? spread : spread_unwielded) + + final_spread += randomized_gun_spread + randomized_bonus_spread + + //Clamp it down to avoid guns with negative spread to have worse recoil... + final_spread = clamp(final_spread, 0, INFINITY) + + //So spread isn't JUST to the right + if(prob(50)) + final_spread *= -1 + + final_spread = round(final_spread) + + return final_spread + +/obj/item/gun/proc/simulate_recoil(mob/living/user, recoil_bonus = 0, firing_angle) + var/total_recoil = calculate_recoil(user, recoil_bonus) + + var/actual_angle = firing_angle + rand(-recoil_deviation, recoil_deviation) + 180 + if(actual_angle > 360) + actual_angle -= 360 + if(total_recoil > 0) + recoil_camera(user, total_recoil + 1, (total_recoil * recoil_backtime_multiplier)+1, total_recoil, actual_angle) + return TRUE + +/obj/item/gun/proc/handle_muzzle_flash(mob/living/user, firing_angle) + var/atom/movable/flash_loc = user + if(!light_on) + set_light_on(TRUE) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, set_light_on), FALSE), 3) + + //Offset the pixels. + switch(firing_angle) + if(0, 360) + muzzle_flash.pixel_x = 0 + muzzle_flash.pixel_y = 8 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(1 to 44) + muzzle_flash.pixel_x = round(4 * ((firing_angle) / 45)) + muzzle_flash.pixel_y = 8 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(45) + muzzle_flash.pixel_x = 8 + muzzle_flash.pixel_y = 8 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(46 to 89) + muzzle_flash.pixel_x = 8 + muzzle_flash.pixel_y = round(4 * ((90 - firing_angle) / 45)) + muzzle_flash.layer = initial(muzzle_flash.layer) + if(90) + muzzle_flash.pixel_x = 8 + muzzle_flash.pixel_y = 0 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(91 to 134) + muzzle_flash.pixel_x = 8 + muzzle_flash.pixel_y = round(-3 * ((firing_angle - 90) / 45)) + muzzle_flash.layer = initial(muzzle_flash.layer) + if(135) + muzzle_flash.pixel_x = 8 + muzzle_flash.pixel_y = -6 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(136 to 179) + muzzle_flash.pixel_x = round(4 * ((180 - firing_angle) / 45)) + muzzle_flash.pixel_y = -6 + muzzle_flash.layer = ABOVE_MOB_LAYER + if(180) + muzzle_flash.pixel_x = 0 + muzzle_flash.pixel_y = -6 + muzzle_flash.layer = ABOVE_MOB_LAYER + if(181 to 224) + muzzle_flash.pixel_x = round(-6 * ((firing_angle - 180) / 45)) + muzzle_flash.pixel_y = -6 + muzzle_flash.layer = ABOVE_MOB_LAYER + if(225) + muzzle_flash.pixel_x = -6 + muzzle_flash.pixel_y = -6 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(226 to 269) + muzzle_flash.pixel_x = -6 + muzzle_flash.pixel_y = round(-6 * ((270 - firing_angle) / 45)) + muzzle_flash.layer = initial(muzzle_flash.layer) + if(270) + muzzle_flash.pixel_x = -6 + muzzle_flash.pixel_y = 0 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(271 to 314) + muzzle_flash.pixel_x = -6 + muzzle_flash.pixel_y = round(8 * ((firing_angle - 270) / 45)) + muzzle_flash.layer = initial(muzzle_flash.layer) + if(315) + muzzle_flash.pixel_x = -6 + muzzle_flash.pixel_y = 8 + muzzle_flash.layer = initial(muzzle_flash.layer) + if(316 to 359) + muzzle_flash.pixel_x = round(-6 * ((360 - firing_angle) / 45)) + muzzle_flash.pixel_y = 8 + muzzle_flash.layer = initial(muzzle_flash.layer) + + muzzle_flash.transform = null + muzzle_flash.transform = turn(muzzle_flash.transform, firing_angle) + flash_loc.vis_contents += muzzle_flash + muzzle_flash.applied = TRUE + + addtimer(CALLBACK(src, PROC_REF(remove_muzzle_flash), flash_loc, muzzle_flash), 0.2 SECONDS) + +/obj/item/gun/proc/remove_muzzle_flash(atom/movable/flash_loc, obj/effect/muzzle_flash/muzzle_flash) + if(!QDELETED(flash_loc)) + flash_loc.vis_contents -= muzzle_flash + muzzle_flash.applied = FALSE + +// for guns firing on their own without a user +/obj/item/gun/proc/discharge(cause, seek_chance = 10) + var/target + if(!safety && has_safety) + // someone is very unlucky and about to be shot + if(prob(seek_chance)) + for(var/mob/living/target_mob in range(6, get_turf(src))) + if(!isInSight(src, target_mob)) + continue + target = target_mob + break + if(!target) + var/fire_dir = pick(GLOB.alldirs) + target = get_ranged_target_turf(get_turf(src),fire_dir,6) + if(!chambered || !chambered.BB) + visible_message(span_danger("\The [src] [cause ? "[cause], suddenly going off" : "suddenly goes off"] without its safteies on! Luckily it wasn't live.")) + playsound(src, dry_fire_sound, 30, TRUE) + else + visible_message(span_danger("\The [src] [cause ? "[cause], suddenly going off" : "suddenly goes off"] without its safeties on!")) + unsafe_shot(target) + +/obj/item/gun/proc/unsafe_shot(target) + if(chambered) + chambered.fire_casing(target,null, null, null, suppressed, ran_zone(BODY_ZONE_CHEST, 50), 0, src,TRUE) + playsound(src, fire_sound, 100, TRUE) + +/mob/living/proc/trip_with_gun(cause) + var/mob/living/carbon/human/human_holder + if(ishuman(src)) + human_holder = src + for(var/obj/item/gun/at_risk in get_all_contents()) + var/chance_to_fire = round(GUN_NO_SAFETY_MALFUNCTION_CHANCE_MEDIUM * at_risk.safety_multiplier) + var/bodyzone = pick_weight(list(BODY_ZONE_HEAD = 1, BODY_ZONE_CHEST = 9, BODY_ZONE_L_ARM = 4, BODY_ZONE_R_ARM = 4, BODY_ZONE_L_LEG = 41, BODY_ZONE_R_LEG = 41)) + if(human_holder) + // gun is less likely to go off in a holster + if(at_risk == human_holder.s_store) + chance_to_fire = round(GUN_NO_SAFETY_MALFUNCTION_CHANCE_LOW * at_risk.safety_multiplier) + bodyzone = pick_weight(list(BODY_ZONE_CHEST = 10, BODY_ZONE_L_LEG = 45, BODY_ZONE_R_LEG = 45)) + if(at_risk.safety == FALSE && prob(chance_to_fire)) + if(at_risk.process_fire(src,src,FALSE, null, bodyzone) == TRUE) + log_combat(src,src,"misfired",at_risk,"caused by [cause]") + visible_message(span_danger("\The [at_risk.name]'s trigger gets caught as [src] falls, suddenly going off into [src]'s [get_bodypart(bodyzone)]!"), span_danger("\The [at_risk.name]'s trigger gets caught on something as you fall, suddenly going off into your [get_bodypart(bodyzone)]!")) + human_holder.force_scream() + +//I need to refactor this into an attachment +/datum/action/toggle_scope_zoom + name = "Aim Down Sights" + check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_HANDS_BLOCKED|AB_CHECK_IMMOBILE|AB_CHECK_LYING + icon_icon = 'icons/mob/actions/actions_items.dmi' + button_icon_state = "sniper_zoom" + +/datum/action/toggle_scope_zoom/Trigger() + if(!istype(target, /obj/item/gun) || !..()) + return + + var/obj/item/gun/gun = target + gun.zoom(owner, owner.dir) + gun.min_recoil = gun.min_recoil_aimed + +/datum/action/toggle_scope_zoom/Remove(mob/user) + if(!istype(target, /obj/item/gun)) + return ..() + + var/obj/item/gun/gun = target + gun.zoom(user, user.dir, FALSE) + + ..() + +/obj/item/gun/proc/rotate(atom/thing, old_dir, new_dir) + SIGNAL_HANDLER + + if(ismob(thing)) + var/mob/lad = thing + lad.client.view_size.zoomOut(zoom_out_amt, zoom_amt, new_dir) + +/obj/item/gun/proc/zoom(mob/living/user, direc, forced_zoom) + if(!user || !user.client) + return + + if(isnull(forced_zoom)) + if((!zoomed && wielded_fully) || zoomed) + zoomed = !zoomed + else + to_chat(user, span_danger("You can't look down the sights without wielding [src]!")) + zoomed = FALSE + else + zoomed = forced_zoom + + if(zoomed) + RegisterSignal(user, COMSIG_ATOM_DIR_CHANGE, PROC_REF(rotate)) + ADD_TRAIT(user, TRAIT_AIMING, ref(src)) + user.client.view_size.zoomOut(zoom_out_amt, zoom_amt, direc) + min_recoil = min_recoil_aimed + user.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/aiming, multiplicative_slowdown = aimed_wield_slowdown) + else + UnregisterSignal(user, COMSIG_ATOM_DIR_CHANGE) + REMOVE_TRAIT(user, TRAIT_AIMING, ref(src)) + user.client.view_size.zoomIn() + min_recoil = initial(min_recoil) + user.remove_movespeed_modifier(/datum/movespeed_modifier/aiming) + return zoomed + +//Proc, so that gun accessories/scopes/etc. can easily add zooming. +/obj/item/gun/proc/build_zooming() + if(azoom) + return + + if(zoomable) + azoom = new(src) + +/obj/item/gun/proc/build_firemodes() + if(FIREMODE_FULLAUTO in gun_firemodes) + if(!GetComponent(/datum/component/automatic_fire)) + AddComponent(/datum/component/automatic_fire, fire_delay) + SEND_SIGNAL(src, COMSIG_GUN_DISABLE_AUTOFIRE) + for(var/datum/action/item_action/toggle_firemode/old_firemode in actions) + old_firemode.Destroy() + var/datum/action/item_action/our_action + + if(gun_firemodes.len > 1) + our_action = new /datum/action/item_action/toggle_firemode(src) + + for(var/i=1, i <= gun_firemodes.len+1, i++) + if(default_firemode == gun_firemodes[i]) + firemode_index = i + if(gun_firemodes[i] == FIREMODE_FULLAUTO) + SEND_SIGNAL(src, COMSIG_GUN_ENABLE_AUTOFIRE) + if(our_action) + our_action.UpdateButtonIcon() + return + + firemode_index = 1 + CRASH("default_firemode isn't in the gun_firemodes list of [src.type]!! Defaulting to 1!!") + +/obj/item/gun/ui_action_click(mob/user, actiontype) + if(istype(actiontype, /datum/action/item_action/toggle_firemode)) + fire_select(user) + else + ..() + +/obj/item/gun/proc/fire_select(mob/living/carbon/human/user) + + //gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_FULLAUTO, FIREMODE_OTHER) + + firemode_index++ + if(firemode_index > gun_firemodes.len) + firemode_index = 1 //reset to the first index if it's over the limit. Byond arrays start at 1 instead of 0, hence why its set to 1. + + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode == FIREMODE_FULLAUTO) + SEND_SIGNAL(src, COMSIG_GUN_ENABLE_AUTOFIRE) + else + SEND_SIGNAL(src, COMSIG_GUN_DISABLE_AUTOFIRE) +//wawa + to_chat(user, span_notice("Switched to [gun_firenames[current_firemode]].")) + playsound(user, 'sound/weapons/gun/general/selector.ogg', 100, TRUE) + update_appearance() + for(var/datum/action/current_action as anything in actions) + current_action.UpdateButtonIcon() + +/datum/action/item_action/toggle_firemode/UpdateButtonIcon(status_only = FALSE, force = FALSE) + var/obj/item/gun/our_gun = target + + var/current_firemode = our_gun.gun_firemodes[our_gun.firemode_index] + //tldr; if we have adjust_fire_select_icon_state_on_safety as true, we append "safety_" to the prefix, otherwise nothing. + var/safety_prefix = "[our_gun.adjust_fire_select_icon_state_on_safety ? "[our_gun.safety ? "safety_" : ""]" : ""]" + if(current_firemode == FIREMODE_UNDERBARREL) + button_icon_state = "[safety_prefix][our_gun.underbarrel_prefix][current_firemode]" + else + button_icon_state = "[safety_prefix][our_gun.fire_select_icon_state_prefix][current_firemode]" + return ..() + +GLOBAL_LIST_INIT(gun_saw_types, typecacheof(list( + /obj/item/gun/energy/plasmacutter, + /obj/item/melee/energy, + /obj/item/gear_handle/anglegrinder, + ))) + +///Handles all the logic of sawing off guns, +/obj/item/gun/proc/try_sawoff(mob/user, obj/item/saw) + if(!saw.get_sharpness() || !is_type_in_typecache(saw, GLOB.gun_saw_types) && saw.tool_behaviour != TOOL_SAW) //needs to be sharp. Otherwise turned off eswords can cut this. + return + if(sawn_off) + to_chat(user, span_warning("\The [src] is already shortened!")) + return + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message(span_notice("[user] begins to shorten \the [src]."), span_notice("You begin to shorten \the [src]...")) + + //if there's any live ammo inside the gun, makes it go off + if(blow_up(user)) + user.visible_message(span_danger("\The [src] goes off!"), span_danger("\The [src] goes off in your face!")) + return + + if(do_after(user, 30, target = src)) + user.visible_message(span_notice("[user] shortens \the [src]!"), span_notice("You shorten \the [src].")) + sawoff(user, saw) + +///Used on init or try_sawoff +/obj/item/gun/proc/sawoff(forced = FALSE) + if(sawn_off && !forced) + return + name = "sawn-off [src.name]" + desc = sawn_desc + w_class = WEIGHT_CLASS_NORMAL + item_state = "gun" + slot_flags &= ~ITEM_SLOT_BACK //you can't sling it on your back + slot_flags |= ITEM_SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) + recoil = SAWN_OFF_RECOIL + sawn_off = TRUE + update_appearance() + return TRUE + +///used for sawing guns, causes the gun to fire without the input of the user +/obj/item/gun/proc/blow_up(mob/user) + return diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic.dm new file mode 100644 index 0000000000..ccc4fba1b8 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic.dm @@ -0,0 +1,465 @@ +#define NO_MAG_GUN_HELPER(gun_type) \ + /obj/item/gun/ballistic/##gun_type/no_mag { \ + default_ammo_type = FALSE; \ + } + +#define EMPTY_GUN_HELPER(gun_type) \ + /obj/item/gun/ballistic/##gun_type/empty { \ + spawn_no_ammo = TRUE; \ + } + + +#define JAM_CHANCE_MINOR 10 +#define JAM_GRACE_MINOR 4 +#define JAM_CHANCE_MAJOR 30 + +///Subtype for any kind of ballistic gun +///This has a shitload of vars on it, and I'm sorry for that, but it does make making new subtypes really easy +/obj/item/gun/ballistic + desc = "Now comes in flavors like GUN. Uses 10x22mm ammo, for some reason." + name = "projectile gun" + + bad_type = /obj/item/gun/ballistic + + w_class = WEIGHT_CLASS_NORMAL + has_safety = TRUE + safety = TRUE + // when we load the gun, should it instantly chamber the next round? + var/always_chambers = FALSE + /// How utterly fucked the gun is. High gun_wear can cause failure to cycle rounds in some guns + var/gun_wear = 0 + /// How much gun_wear is generated when we shoot. Increased when using surplus rounds + var/wear_rate = 1 // 60 to malfunction, 180 to critical + /// Multiplier for wear reduction + var/clean_rate = 1 + /// Number of times we have successfully fired since the last time the the gun has jammed. Low but not abysmal condition will only jam so often. + var/last_jam = 0 + /// Gun will start to jam at this level of wear + var/wear_minor_threshold = 60 + /// Gun will start to jam more at this level of wear. The grace period between jams is also removed for extra fun + var/wear_major_threshold = 180 + /// Highest wear value so the gun doesn't end up completely irreperable + var/wear_maximum = 300 + var/ignores_wear = FALSE + /// Doesn't ever keep ammo when loading a new round into the chamber. Mainly for BOLT_TYPE_NO_BOLT guns. + var/doesnt_keep_bullet = FALSE + + ///If you can examine a gun to see its current ammo count + var/ammo_counter = FALSE + + min_recoil = 0.1 + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet, + /obj/item/attachment/ammo_counter, + /obj/item/attachment/gun + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 26, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ) + ) + +/obj/item/gun/ballistic/Initialize(mapload, spawn_empty) + . = ..() + + allowed_ammo_types = typecacheof(allowed_ammo_types) - blacklisted_ammo_types + + if(spawn_empty) + if(internal_magazine) + spawn_no_ammo = TRUE + else + default_ammo_type = FALSE + + if (!default_ammo_type && !internal_magazine) + bolt_locked = TRUE + update_appearance() + return + if (ispath(default_ammo_type)) + magazine = new default_ammo_type(src) + if (spawn_no_ammo) + get_ammo_list(drop_all = TRUE) + else + chamber_round() + update_appearance() + +/obj/item/gun/ballistic/update_icon_state() + if(current_skin) + icon_state = "[unique_reskin[current_skin]][sawn_off ? "_sawn" : ""]" + else + icon_state = "[base_icon_state || initial(icon_state)][sawn_off ? "_sawn" : ""]" + return ..() + +/obj/item/gun/ballistic/update_overlays() + . = ..() + if (bolt_type == BOLT_TYPE_LOCKING) + . += "[icon_state]_bolt[bolt_locked ? "_locked" : ""]" + if (bolt_type == BOLT_TYPE_OPEN && bolt_locked) + . += "[icon_state]_bolt" + if (show_magazine_on_sprite && magazine) + if (unique_mag_sprites_for_variants) + . += "[icon_state]_mag_[magazine.base_icon_state]" + if (!magazine.ammo_count()) + . += "[icon_state]_mag_[magazine.base_icon_state]_empty" + else + . += "[icon_state]_mag" + if(show_ammo_capacity_on_magazine_sprite) + var/capacity_number = 0 + switch(get_ammo() / magazine.max_ammo) + if(0.2 to 0.39) + capacity_number = 20 + if(0.4 to 0.59) + capacity_number = 40 + if(0.6 to 0.79) + capacity_number = 60 + if(0.8 to 0.99) + capacity_number = 80 + if(1.0 to 2.0) //to catch the chambered round + capacity_number = 100 + if (capacity_number && unique_mag_sprites_for_variants) + . += "[icon_state]_mag_[magazine.base_icon_state]_[capacity_number]" + else if (capacity_number) + . += "[icon_state]_mag_[capacity_number]" + if(!chambered && empty_indicator) + . += "[icon_state]_empty" + if(chambered && mag_display_ammo) + . += "[icon_state]_chambered" + +/obj/item/gun/ballistic/process_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE, atom/shooter) + if(!semi_auto && from_firing) + return + var/obj/item/ammo_casing/casing = chambered //Find chambered round + if(istype(casing)) //there's a chambered round + if(casing_ejector || !from_firing) + casing.on_eject(shooter) + chambered = null + else if(empty_chamber) + chambered = null + if (chamber_next_round && (magazine?.max_ammo >= 1) && !condition_check(from_firing, shooter)) + chamber_round() + SEND_SIGNAL(src, COMSIG_GUN_CHAMBER_PROCESSED) + +/// Handles weapon condition. Returning TRUE prevents process_chamber from automatically loading a new round +/obj/item/gun/ballistic/proc/condition_check(from_firing = TRUE, atom/shooter) + if(bolt_type == BOLT_TYPE_NO_BOLT || !from_firing || !magazine.ammo_count(FALSE)) //The revolver is one of the most reliable firearms ever designed, as long as you don't need to fire any more than six bullets at something. Which, of course, you do not. + return FALSE + last_jam++ + if(gun_wear < wear_minor_threshold) + return FALSE + if(gun_wear >= wear_major_threshold ? prob(JAM_CHANCE_MAJOR) : prob(JAM_CHANCE_MINOR) && last_jam >= JAM_GRACE_MINOR) + bolt_locked = TRUE + last_jam = 0 // sighs and erases number on whiteboard + balloon_alert(shooter, "jammed!") + playsound(src, 'sound/weapons/gun/general/dry_fire_old.ogg', 50, TRUE, -15) //click. uhoh. + return TRUE + +///Used to chamber a new round and eject the old one +/obj/item/gun/ballistic/proc/chamber_round(keep_bullet = FALSE) + if (chambered || !magazine) + return + if (magazine.ammo_count()) + if(doesnt_keep_bullet) + chambered = magazine.get_round(FALSE) + else + chambered = magazine.get_round(keep_bullet || bolt_type == BOLT_TYPE_NO_BOLT) + if (bolt_type != BOLT_TYPE_OPEN) + chambered.forceMove(src) + +///updates a bunch of racking related stuff and also handles the sound effects and the like +/obj/item/gun/ballistic/proc/rack(mob/user = null, chamber_new_round = TRUE) + if (bolt_type == BOLT_TYPE_NO_BOLT) //If there's no bolt, nothing to rack + return + if (bolt_type == BOLT_TYPE_OPEN) + if(!bolt_locked) //If it's an open bolt, racking again would do nothing + if (user) + to_chat(user, span_notice("\The [src]'s [bolt_wording] is already cocked!")) + return + bolt_locked = FALSE + if (user) + to_chat(user, span_notice("You rack the [bolt_wording] of \the [src].")) + process_chamber(!chambered, FALSE, chamber_new_round, user) + if ((bolt_type == BOLT_TYPE_LOCKING && !chambered) || bolt_type == BOLT_TYPE_CLIP) + bolt_locked = TRUE + playsound(src, lock_back_sound, lock_back_sound_volume, lock_back_sound_vary) + else + playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) + + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + +///Drops the bolt from a locked position +/obj/item/gun/ballistic/proc/drop_bolt(mob/user = null, chamber_new_round = TRUE) + playsound(src, bolt_drop_sound, bolt_drop_sound_volume, FALSE) + if (user) + to_chat(user, span_notice("You drop the [bolt_wording] of \the [src].")) + if(chamber_new_round) + chamber_round() + bolt_locked = FALSE + update_appearance() + +///Handles all the logic needed for magazine insertion +/obj/item/gun/ballistic/proc/insert_magazine(mob/user, obj/item/ammo_box/magazine/inserted_mag, display_message = TRUE) + if(!(inserted_mag.type in allowed_ammo_types)) + to_chat(user, span_warning("\The [inserted_mag] doesn't seem to fit into \the [src]...")) + return FALSE + if(user.transferItemToLoc(inserted_mag, src)) + magazine = inserted_mag + if (display_message) + to_chat(user, span_notice("You load a new [magazine_wording] into \the [src].")) + if (magazine.ammo_count()) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + else + playsound(src, load_empty_sound, load_sound_volume, load_sound_vary) + if (bolt_type == BOLT_TYPE_OPEN && !bolt_locked) + chamber_round(TRUE) + update_appearance() + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + return TRUE + else + to_chat(user, span_warning("You cannot seem to get \the [src] out of your hands!")) + return FALSE + +///Handles all the logic of magazine ejection, if tac_load is set that magazine will be tacloaded in the place of the old eject +/obj/item/gun/ballistic/proc/eject_magazine(mob/user, display_message = TRUE, obj/item/ammo_box/magazine/tac_load = null) + if(bolt_type == BOLT_TYPE_OPEN) + chambered = null + if (magazine.ammo_count()) + playsound(src, eject_sound, eject_sound_volume, eject_sound_vary) + else + playsound(src, eject_empty_sound, eject_sound_volume, eject_sound_vary) + magazine.forceMove(drop_location()) + var/obj/item/ammo_box/magazine/old_mag = magazine + old_mag.update_appearance() + magazine = null + if (display_message) + to_chat(user, span_notice("You pull the [magazine_wording] out of \the [src].")) + update_appearance() + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + if (tac_load) + if(do_after(user, tactical_reload_delay, src, hidden = TRUE)) + if (insert_magazine(user, tac_load, FALSE)) + to_chat(user, span_notice("You perform a tactical reload on \the [src].")) + else + to_chat(user, span_warning("You dropped the old [magazine_wording], but the new one doesn't fit. How embarassing.")) + else + to_chat(user, span_warning("Your reload was interupted!")) + return + if(user) + user.put_in_hands(old_mag) + update_appearance() + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + +/obj/item/gun/ballistic/can_shoot() + if(safety) + return FALSE + return chambered + +/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params) + if(..()) + return FALSE + + if(sealed_magazine) + to_chat(user, span_warning("The [magazine_wording] on [src] is sealed and cannot be reloaded!")) + return + if(!internal_magazine && istype(A, /obj/item/ammo_box/magazine)) + var/obj/item/ammo_box/magazine/AM = A + if (!magazine) + insert_magazine(user, AM) + else + if (tac_reloads) + eject_magazine(user, FALSE, AM) + else + to_chat(user, span_notice("There's already a [magazine_wording] in \the [src].")) + return + + if(istype(A, /obj/item/ammo_casing) || istype(A, /obj/item/ammo_box)) + if (bolt_type == BOLT_TYPE_NO_BOLT || internal_magazine) + if ((chambered && !chambered.BB) || (chambered && always_chambers)) + chambered.on_eject(shooter = user) + chambered = null + var/num_loaded = magazine.attackby(A, user, params) + if (num_loaded) + to_chat(user, span_notice("You load [num_loaded] [cartridge_wording]\s into \the [src].")) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + if ((chambered == null && bolt_type == BOLT_TYPE_NO_BOLT) || always_chambers) + chamber_round() + A.update_appearance() + update_appearance() + return + if (can_be_sawn_off) + if (try_sawoff(user, A)) + return + + return FALSE + +///Prefire empty checks for the bolt drop +/obj/item/gun/ballistic/proc/prefire_empty_checks() + if (!chambered && !get_ammo()) + if (bolt_type == BOLT_TYPE_OPEN && !bolt_locked) + bolt_locked = TRUE + playsound(src, bolt_drop_sound, bolt_drop_sound_volume) + update_appearance() + +///postfire empty checks for bolt locking and sound alarms +/obj/item/gun/ballistic/proc/postfire_empty_checks(last_shot_succeeded) + if (!chambered && !get_ammo()) + if (empty_alarm && last_shot_succeeded) + playsound(src, empty_alarm_sound, empty_alarm_volume, empty_alarm_vary) + update_appearance() + if (empty_autoeject && last_shot_succeeded && !internal_magazine) + eject_magazine(display_message = FALSE) + update_appearance() + if (last_shot_succeeded && bolt_type == BOLT_TYPE_LOCKING) + bolt_locked = TRUE + update_appearance() + if (last_shot_succeeded && bolt_type == BOLT_TYPE_CLIP) + update_appearance() + +/obj/item/gun/ballistic/pre_fire(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0, dual_wielded_gun = FALSE) + prefire_empty_checks() + return ..() + +/obj/item/gun/ballistic/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, burst_firing = FALSE, spread_override = 0, iteration = 0) + . = ..() //The gun actually firing + postfire_empty_checks(.) + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/gun/ballistic/attack_hand(mob/user) + // the main calls it's own eject mag before the underbarrel. fix this + if(user.is_holding(src) && loc == user && !(gun_firemodes[firemode_index] == FIREMODE_UNDERBARREL)) + if(sealed_magazine) + to_chat(user, span_warning("The [magazine_wording] on [src] is sealed and cannot be accessed!")) + return + if(bolt_type == BOLT_TYPE_NO_BOLT && (chambered || internal_magazine)) + chambered = null + var/num_unloaded = 0 + for(var/obj/item/ammo_casing/CB in get_ammo_list(FALSE, TRUE)) + CB.forceMove(drop_location()) + + var/angle_of_movement =(rand(-3000, 3000) / 100) + dir2angle(turn(user.dir, 180)) + CB.AddComponent(/datum/component/movable_physics, _horizontal_velocity = rand(350, 450) / 100, _vertical_velocity = rand(400, 450) / 100, _horizontal_friction = rand(20, 24) / 100, _z_gravity = PHYSICS_GRAV_STANDARD, _z_floor = 0, _angle_of_movement = angle_of_movement, _bounce_sound = CB.bounce_sfx_override) + + num_unloaded++ + SSblackbox.record_feedback("tally", "station_mess_created", 1, CB.name) + if (num_unloaded) + to_chat(user, span_notice("You unload [num_unloaded] [cartridge_wording]\s from [src].")) + playsound(user, eject_sound, eject_sound_volume, eject_sound_vary) + update_appearance() + else + to_chat(user, span_warning("[src] is empty!")) + return + if(!internal_magazine && magazine) + eject_magazine(user) + return + return ..() + return ..() + +/obj/item/gun/ballistic/unique_action(mob/living/user) + if((bolt_type == BOLT_TYPE_LOCKING || bolt_type == BOLT_TYPE_CLIP) && bolt_locked) + drop_bolt(user) + return + + if (recent_rack > world.time) + return + recent_rack = world.time + rack_delay + if(bolt_type == BOLT_TYPE_CLIP) + rack(user, FALSE) + update_appearance() + return + rack(user) + update_appearance() + return + + +/obj/item/gun/ballistic/examine(mob/user) + . = ..() + if(!chambered) + . += "It does not seem to have a round chambered." + if(bolt_locked) + . += "The [bolt_wording] is locked back and needs to be released before firing." + if(ammo_counter) + var/count_chambered = !(bolt_type == BOLT_TYPE_NO_BOLT || bolt_type == BOLT_TYPE_OPEN) + . += span_notice("It has [get_ammo(count_chambered)] round\s remaining.") + +/obj/item/gun/ballistic/examine_more(mob/user) + . = ..() + if(bolt_type != BOLT_TYPE_NO_BOLT && wear_rate) + . += "You can [bolt_wording] [src] by pressing the unique action key. By default, this is space" + var/conditionstr = span_boldwarning("critical") + var/minorhalf = wear_minor_threshold / 2 + var/majorhalf = wear_minor_threshold + (wear_major_threshold-wear_minor_threshold) / 2 + if(gun_wear <= minorhalf) + conditionstr = span_green("good") + else if(gun_wear <= wear_minor_threshold) + conditionstr = span_nicegreen("decent") //nicegreen is less neon than green so it looks less :))) + else if(gun_wear <= majorhalf) + conditionstr = span_red("poor") + else if(gun_wear <= wear_major_threshold) //TTD: switch doesn't play nice with variables but this sucks + conditionstr = span_warning("terrible") + . += "it is in [conditionstr] condition[gun_wear >= wear_minor_threshold ? gun_wear >= wear_major_threshold ? " and will suffer constant malfunctions" : " and will suffer from regular malfunctions" :""]." + +///Gets the number of bullets in the gun +/obj/item/gun/ballistic/proc/get_ammo(countchambered = TRUE) + var/boolets = 0 //mature var names for mature people + if (chambered && countchambered) + boolets++ + if (magazine) + boolets += magazine.ammo_count() + return boolets + +///gets a list of every bullet in the gun +/obj/item/gun/ballistic/proc/get_ammo_list(countchambered = TRUE, drop_all = FALSE) + var/list/rounds = list() + if(chambered && countchambered) + rounds.Add(chambered) + if(drop_all) + chambered = null + if(magazine) + rounds.Add(magazine.ammo_list(drop_all)) + return rounds + +/obj/item/gun/ballistic/blow_up(mob/user) + . = FALSE + for(var/obj/item/ammo_casing/AC in magazine.stored_ammo) + if(AC.BB) + process_fire(user, user, FALSE) + . = TRUE + +/obj/item/gun/ballistic/unsafe_shot(target, empty_chamber = TRUE) + . = ..() + process_chamber(empty_chamber,TRUE) + +/obj/item/gun/ballistic/proc/adjust_wear(amt) + if(amt > 0) + gun_wear = round(clamp(gun_wear + wear_rate * amt, 0, wear_maximum), 0.01) + else + gun_wear = round(clamp(gun_wear + clean_rate * amt, 0, wear_maximum), 0.01) + +/// Remember: you can always trust a loaded gun to go off at least once. +/obj/item/gun/ballistic/proc/accidents_happen(mob/darwin) + . = TRUE + if(safety) + return FALSE + if(!magazine && !chambered) + return + if(internal_magazine && !magazine.ammo_count(TRUE)) + return + if(prob(0.5)) //this gets called I think once per decisecond so we don't really want a high chance here + if(!chambered) + to_chat(darwin, span_warning("You accidentally chamber a round-")) + chamber_round() + return + to_chat(darwin, span_boldwarning("The trigger on [src] gets caught-")) + unsafe_shot(darwin) + return FALSE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/assault.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/assault.dm new file mode 100644 index 0000000000..2e8c4ffbae --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/assault.dm @@ -0,0 +1,166 @@ +/obj/item/gun/ballistic/automatic/assault + bad_type = /obj/item/gun/ballistic/automatic/assault + + show_magazine_on_sprite = TRUE + w_class = WEIGHT_CLASS_BULKY + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + wield_delay = 0.8 SECONDS + wield_slowdown = RIFLE_SLOWDOWN + aimed_wield_slowdown = RIFLE_AIM_SLOWDOWN + + zoom_amt = RIFLE_ZOOM + + fire_delay = 0.2 SECONDS + + load_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + + rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg' + spread_unwielded = 20 + + gunslinger_recoil_bonus = 2 + gunslinger_spread_bonus = 16 + + light_range = 2 + wear_minor_threshold = 200 + wear_major_threshold = 600 + wear_maximum = 1200 + +/obj/item/gun/ballistic/automatic/assault/skm + name = "\improper SKM-24" + desc = "An obsolete model of assault rifle once used by CLIP. Legendary for its durability and low cost, surplus rifles are commonplace on the Frontier, and the design has been widely copied. Chambered in 7.62x40mm CLIP." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + fire_sound = 'sound/weapons/gun/rifle/skm.ogg' + + rack_sound = 'sound/weapons/gun/rifle/skm_cocked.ogg' + load_sound = 'sound/weapons/gun/rifle/skm_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/skm_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' + + icon_state = "skm" + item_state = "skm" + show_magazine_on_sprite = TRUE + unique_mag_sprites_for_variants = TRUE + weapon_weight = WEAPON_MEDIUM + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + manufacturer = MANUFACTURER_IMPORT + default_ammo_type = /obj/item/ammo_box/magazine/skm_762_40 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/skm_762_40, + ) + + unique_reskin = list(\ + "SKM" = "skm", + "Polymer" = "skm_polymer", + "Bright" = "skm_bright", + ) + unique_reskin_changes_inhand = TRUE + + //truly a doohickey for every occasion + unique_attachments = list ( + /obj/item/attachment/energy_bayonet, + ) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 33, + "y" = 15, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 16, + "y" = 22, + ) + ) + + spread = 1 + wield_delay = 0.7 SECONDS + + fire_delay = 0.2 SECONDS + +/obj/item/gun/ballistic/automatic/assault/skm/no_mag + default_ammo_type = FALSE + +/obj/item/gun/ballistic/automatic/assault/skm/pirate + name = "\improper Chopper" + desc = "An SKM-24 in a state of shockingly poor repair: Several parts are missing and the 'grip' is improvised from scrap wood. It's a miracle it still works at all. Chambered in 7.62x40mm CLIP." + + icon_state = "skm_pirate" + item_state = "skm_pirate" + manufacturer = MANUFACTURER_NONE + wear_rate = 2 + +/obj/item/gun/ballistic/automatic/assault/skm/inteq + name = "\improper SKM-44" + desc = "An obsolete model of assault rifle once used by CLIP. Most of these were seized from Frontiersmen armories or purchased in CLIP, then modified to IRMG standards. Chambered in 7.62x40mm CLIP." + + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + + icon_state = "skm_inteq" + item_state = "skm_inteq" + manufacturer = MANUFACTURER_INTEQ + +/obj/item/gun/ballistic/automatic/assault/cm82 + name = "\improper CM-16" + desc = "The standard-issue rifle of CLIP and an extensively modified reproduction of the P-16. Chambered in 5.56mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + icon_state = "cm16" + item_state = "cm16" + +/obj/item/gun/ballistic/automatic/assault/swiss_cheese + name = "\improper Swiss Cheese" + desc = "An ancient longarm famous for its boxy, modular design. Mass produced by the Terran Confederation in ages past, these often mutiple century old designs have survied due to their sheer ruggedness. The DMA on this unit is sadly broken, but these rifles are known for their excellent burst fire. Uses 5.56mm ammunition for Matter mode." + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + fire_sound = 'sound/weapons/gun/rifle/swiss.ogg' + icon_state = "swiss" + item_state = "swiss" + show_magazine_on_sprite = TRUE + empty_indicator = TRUE + burst_size = 3 + burst_delay = 0.08 SECONDS + fire_delay = 0.25 SECONDS + spread = 8 + weapon_weight = WEAPON_MEDIUM + gun_firenames = list(FIREMODE_SEMIAUTO = "matter semi-auto", FIREMODE_BURST = "matter burst fire", FIREMODE_FULLAUTO = "matter full auto", FIREMODE_OTHER = "hybrid") + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_FULLAUTO, FIREMODE_OTHER) + + fire_select_icon_state_prefix = "swisschesse_" + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + default_ammo_type = /obj/item/ammo_box/magazine/swiss + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/swiss, + ) + manufacturer = MANUFACTURER_SOLARARMORIES + spread = 8 + spread_unwielded = 15 + +/obj/item/gun/ballistic/automatic/assault/swiss_cheese/process_other(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0) + to_chat(user, span_danger("You hear a strange sound from the DMA unit. It doesn't appear to be operational.")) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/automatic.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/automatic.dm new file mode 100644 index 0000000000..fd813a17e3 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/automatic.dm @@ -0,0 +1,53 @@ + +/obj/item/gun/ballistic/automatic + bad_type = /obj/item/gun/ballistic/automatic + w_class = WEIGHT_CLASS_BULKY + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + semi_auto = TRUE + fire_sound = 'sound/weapons/gun/smg/shot.ogg' + fire_sound_volume = 90 + vary_fire_sound = FALSE + rack_sound = 'sound/weapons/gun/smg/smgrack.ogg' + suppressed_sound = 'sound/weapons/gun/smg/shot_suppressed.ogg' + weapon_weight = WEAPON_MEDIUM + pickup_sound = 'sound/items/handling/rifle_pickup.ogg' + + fire_delay = 0.4 SECONDS + wield_delay = 1 SECONDS + spread = 0 + spread_unwielded = 13 + recoil = 0 + recoil_unwielded = 4 + wield_slowdown = PDW_SLOWDOWN + +/obj/item/gun/ballistic/automatic/zip_pistol + name = "makeshift pistol" + desc = "A makeshift open-bolt automatic pistol cobbled together from various scrap bits and stamped steel. the barrel is smoothbore, is lacking a front-sight, and the balancing is uneven." + icon_state = "brazilpistol" + item_state = "brazilpistol" + icon = 'icons/obj/guns/manufacturer/hermits/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hermits/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hermits/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hermits/onmob.dmi' + recoil_unwielded = 4 + recoil = 1.2 + spread = 15 + spread_unwielded = 35 + dual_wield_spread = 35 + has_safety = FALSE + safety = FALSE + safety_multiplier = 2 + fire_delay = 0.15 SECONDS + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + default_ammo_type = /obj/item/ammo_box/magazine/zip_ammo_9mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/zip_ammo_9mm, + ) + actions_types = list() + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_LIGHT + wear_rate = 3 //it's a. piece of shit. + w_class = WEIGHT_CLASS_NORMAL diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/blast_hammer.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/blast_hammer.dm new file mode 100644 index 0000000000..1ff08c80e4 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/blast_hammer.dm @@ -0,0 +1,180 @@ +/obj/item/gun/ballistic/shotgun/blasting_hammer + name = "blasting hammer" + icon_state = "blasting-0" + base_icon_state = "blasting" + item_state = "blasting" + + desc = "A heavily modified breaching hammer with what appears to be some kind of makeshift loading mechanism bolted on. A brutishly powerful tool for breaking both hull and heads. Loads 12g blanks as propellent to increase it's already impressive destructive power." + icon = 'icons/obj/weapon/blunt.dmi' + mob_overlay_icon = 'icons/mob/clothing/back.dmi' + lefthand_file = 'icons/mob/inhands/weapons/blunt_lefthand.dmi' + righthand_file = 'icons/mob/inhands/weapons/blunt_righthand.dmi' + has_safety = FALSE + safety = FALSE + + force = 5 + throwforce = 15 + armour_penetration = 40 + demolition_mod = 2 + attack_cooldown = 12 + attack_verb = list("bashed", "smashed", "crushed", "smacked") + hitsound = list('sound/weapons/melee/heavyblunt_hit1.ogg', 'sound/weapons/melee/heavyblunt_hit2.ogg', 'sound/weapons/melee/heavyblunt_hit3.ogg') + pickup_sound = 'sound/weapons/melee/heavy_pickup.ogg' + fire_sound = 'sound/weapons/gun/shotgun/brimstone.ogg' + + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/blasting_hammer + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/blasting_hammer, + ) + recoil = 4 + + actually_shoots = FALSE + door_breaching_weapon = FALSE //this doesn't breach doors. it OBLITERATES THEM + ignores_wear = TRUE + manufacturer = null + gunslinger_recoil_bonus = 0 + wield_slowdown = 0 + zoomable = FALSE + ammo_counter = TRUE + + valid_attachments = list() + unique_attachments = list() + slot_available = list() + + + var/throw_min = 1 + var/throw_max = 2 + var/charging = FALSE + + +/obj/item/gun/ballistic/shotgun/blasting_hammer/Initialize(mapload, spawn_empty) + . = ..() + update_appearance() + +/obj/item/gun/ballistic/shotgun/blasting_hammer/pre_attack(atom/A, mob/living/user, params) + if(charging) + charge(A,user) + else + return ..() + +/obj/item/gun/ballistic/shotgun/blasting_hammer/proc/toggle_charge(mob/user) + charging = !charging + to_chat(user, "You [charging ? "prepare" : "stop preparing"] to charge.") + +/obj/item/gun/ballistic/shotgun/blasting_hammer/proc/charge(target, mob/user) + charging = FALSE + var/destination = get_turf(target) + RegisterSignal(user,COMSIG_MOVABLE_BUMP,PROC_REF(smack)) + user.throw_at(destination, get_dist(target, user), 2, user, gentle = TRUE) + sleep(get_dist(user, destination) * 0.7) + UnregisterSignal(user,COMSIG_MOVABLE_BUMP) + charging = TRUE + +/obj/item/gun/ballistic/shotgun/blasting_hammer/proc/smack(mob/user,target) + if(isliving(target)) + var/mob/living/victim = target + attack(victim,user) + rack(user,TRUE) + +/obj/item/gun/ballistic/shotgun/blasting_hammer/ComponentInitialize() + . = ..() + AddComponent(/datum/component/two_handed, force_unwielded = force, force_wielded = 30) + +/obj/item/gun/ballistic/shotgun/blasting_hammer/on_wield(obj/item/source, mob/user, instant) + . = ..() + tool_behaviour = TOOL_MINING + +/obj/item/gun/ballistic/shotgun/blasting_hammer/on_unwield(obj/item/source, mob/user) + . = ..() + tool_behaviour = null + +/obj/item/gun/ballistic/shotgun/blasting_hammer/can_shoot(mob/living/target, mob/living/user) + if(!..()) + return FALSE + if(HAS_TRAIT(src, TRAIT_WIELDED) && chambered.BB) + expend_round(target, user) + return TRUE + else + return FALSE + +/obj/item/gun/ballistic/shotgun/blasting_hammer/proc/expend_round(target, mob/living/user) + if(!chambered.BB) + return + if(!istype(chambered, /obj/item/ammo_casing/shotgun/blank)) //loading a live round into your hammer when it has nowhere to go is a bad idea. + unsafe_shot(user) + user.visible_message(span_warning("\The [chambered] in \the [src] backfires into \the [user]!"), span_danger("\The [chambered] in \the [src] goes off right at you!")) + else + chambered.BB = null //fakes the shot + chambered.update_appearance() + playsound(src,fire_sound,100) + var/firing_angle = get_angle(user,target) + simulate_recoil(user,recoil,firing_angle) + +/obj/item/gun/ballistic/shotgun/blasting_hammer/attack(mob/living/target, mob/user) + . = ..() + if(can_shoot(target, user)) + throw_min = 5 + throw_max = 6 + target.Knockdown(1) + target.flash_act(1,1) + target.apply_damage(40, BRUTE, user.zone_selected) + new /obj/effect/temp_visual/kinetic_blast(target.loc) + else + throw_min = initial(throw_min) + throw_max = initial(throw_max) + + if(HAS_TRAIT(src, TRAIT_WIELDED)) + var/atom/throw_target = get_edge_target_turf(target, user.dir) + if(!target.anchored) + target.throw_at(throw_target, rand(throw_min,throw_max), 2, user, gentle = TRUE) + +/obj/item/gun/ballistic/shotgun/blasting_hammer/attack_obj(obj/O, mob/living/user) + if(can_shoot(O, user)) + demolition_mod = 10 + else + demolition_mod = initial(demolition_mod) + return ..() + +/obj/item/gun/ballistic/shotgun/blasting_hammer/closed_turf_attack(turf/closed/wall, mob/living/user, params) + . = ..() + if(can_shoot(wall, user)) + demolition_mod = 10 + else + demolition_mod = initial(demolition_mod) + +/obj/item/gun/ballistic/shotgun/blasting_hammer/update_icon_state() + . = ..() + var/is_loaded = chambered ? 1 : 0 + icon_state = "[base_icon_state]-[min((is_loaded + magazine.ammo_count()), 3)]" + if(HAS_TRAIT(src, TRAIT_WIELDED)) + item_state = "[base_icon_state]_w" + else + item_state = base_icon_state + +/obj/item/ammo_box/magazine/internal/shot/blasting_hammer + name = "blasting hammer magazine" + ammo_type = /obj/item/ammo_casing/shotgun/blank + caliber = "12ga" + max_ammo = 2 + start_empty = TRUE + +/datum/action/item_action/ramzislayer_charge + name = "Charge" + +/datum/action/item_action/ramzislayer_charge/Trigger() + if(istype(target, /obj/item/gun/ballistic/shotgun/blasting_hammer)) + var/obj/item/gun/ballistic/shotgun/blasting_hammer/hammer = target + hammer.toggle_charge(owner) + +/obj/item/gun/ballistic/shotgun/blasting_hammer/ramzislayer //Admin item. Don't actually map this anywhere. + name = "ramzislayers hammer" + desc = "Despite what the name may imply, not this is not a hammer for killing Ramzi. It's a hammer for Ramzi to kill you with." + actions_types = list(/datum/action/item_action/ramzislayer_charge) + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/blasting_hammer/ramzislayer + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/blasting_hammer/ramzislayer, + ) + +/obj/item/ammo_box/magazine/internal/shot/blasting_hammer/ramzislayer + max_ammo = 12 + start_empty = FALSE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/gauss.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/gauss.dm new file mode 100644 index 0000000000..55f4b289af --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/gauss.dm @@ -0,0 +1,35 @@ +/obj/item/gun/ballistic/automatic/powered/gauss + name = "prototype gauss rifle" + desc = "An experimental Nanotrasen rifle with a high capacity. Useful for putting down crowds. Chambered in ferromagnetic pellets." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "gauss" + item_state = "arg" + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + default_ammo_type = /obj/item/ammo_box/magazine/gauss + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/gauss, + ) + fire_sound = 'sound/weapons/gun/gauss/magrifle.ogg' + load_sound = 'sound/weapons/gun/gauss/rifle_reload.ogg' + burst_size = 1 + fire_delay = 0.3 SECONDS + spread = 0 + show_magazine_on_sprite = TRUE + empty_indicator = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + charge_sections = 4 + ammo_x_offset = 2 + manufacturer = MANUFACTURER_NANOTRASEN + + spread = 0 + spread_unwielded = 25 + recoil = 0 + recoil_unwielded = 4 + wield_slowdown = HEAVY_RIFLE_SLOWDOWN + wield_delay = 1 SECONDS + fire_select_icon_state_prefix = "pellet_" + wear_rate = 0 // magnetically accelerated, very little if any wear to the gun diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/hmg.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/hmg.dm new file mode 100644 index 0000000000..a92b1ceddc --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/hmg.dm @@ -0,0 +1,177 @@ +//TODO: rename this file to lmg.dm and: /obj/item/gun/ballistic/automatic/hmg --> /obj/item/gun/ballistic/automatic/lmg + +/obj/item/gun/ballistic/automatic/hmg + bad_type = /obj/item/gun/ballistic/automatic/hmg + w_class = WEIGHT_CLASS_HUGE + slot_flags = 0 + weapon_weight = WEAPON_HEAVY + burst_size = 1 + actions_types = list(/datum/action/item_action/deploy_bipod) //this is on hmg, as I need the same mechanics for a future gun. ideally, this would be an attachment, but that's still pending + drag_slowdown = 1.5 + fire_delay = 0.1 SECONDS + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + wield_slowdown = HMG_SLOWDOWN + + spread = 4 + spread_unwielded = 80 + recoil = 1 + recoil_unwielded = 4 + + gunslinger_recoil_bonus = 2 + gunslinger_spread_bonus = 20 + + ///does this have a bipod? + var/has_bipod = FALSE + ///is the bipod deployed? + var/bipod_deployed = FALSE + ///how long do we need to deploy the bipod? + var/deploy_time = 0.5 SECONDS + + ///we add these two values to recoi/spread when we have the bipod deployed + var/deploy_recoil_bonus = -1 + var/deploy_spread_bonus = -5 + + var/list/deployable_on_structures = list( + /obj/structure/table, + /obj/structure/barricade, + /obj/structure/bed, + /obj/structure/chair, + /obj/structure/railing, + /obj/structure/flippedtable + ) + wear_minor_threshold = 300 + wear_major_threshold = 900 + wear_maximum = 1500 + + +/obj/item/gun/ballistic/automatic/hmg/Initialize() + . = ..() + for(var/datum/action/item_action/deploy_bipod/action as anything in actions) + if(!has_bipod) + qdel(action) + +/obj/item/gun/ballistic/automatic/hmg/ComponentInitialize() + . = ..() + RegisterSignals(src, list(COMSIG_ITEM_EQUIPPED,COMSIG_MOVABLE_MOVED), PROC_REF(retract_bipod)) + +/datum/action/item_action/deploy_bipod //TODO: Make this an accessory when that's added + name = "Deploy Bipod" + desc = "Deploy the bipod when bracing against something to increase accuracy." + +/obj/item/gun/ballistic/automatic/hmg/ui_action_click(mob/user, action) + if(!istype(action, /datum/action/item_action/deploy_bipod)) + return ..() + if(!bipod_deployed) + deploy_bipod(user) + else + retract_bipod(user=user) + +/obj/item/gun/ballistic/automatic/hmg/calculate_recoil(mob/user, recoil_bonus = 0) + var/total_recoil = recoil_bonus + + if(bipod_deployed) + total_recoil += deploy_recoil_bonus + + return ..(user, total_recoil) + +/obj/item/gun/ballistic/automatic/hmg/calculate_spread(mob/user, bonus_spread) + var/total_spread = bonus_spread + + if(bipod_deployed) + total_spread += deploy_spread_bonus + + return ..(user, total_spread) + +/obj/item/gun/ballistic/automatic/hmg/proc/deploy_bipod(mob/user) + //we check if we can actually deploy the thing + var/can_deploy = TRUE + var/mob/living/wielder = user + + if(!wielder) + return + + if(!wielded_fully) + to_chat(user, span_warning("You need to fully grip [src] to deploy it's bipod!")) + return + + if(wielder.body_position != LYING_DOWN) //are we braced against the ground? if not, we check for objects to brace against + can_deploy = FALSE + + for(var/direction_to_check as anything in GLOB.cardinals) //help + var/turf/open/turf_to_check = get_step(get_turf(src),direction_to_check) + for(var/obj/structure/checked_struct as anything in turf_to_check.contents) //while you can fire in non-braced directions, this makes it so you have to get good positioning to fire standing up. + for(var/checking_allowed as anything in deployable_on_structures) + if(istype(checked_struct, checking_allowed)) //help if you know how to write this better + can_deploy = TRUE + break + + + if(!can_deploy) + to_chat(user, span_warning("You need to brace against something to deploy [src]'s bipod! Either lie on the floor or stand next to a waist high object like a table!")) + return + if(!do_after(user, deploy_time, src, NONE, TRUE, CALLBACK(src, PROC_REF(is_wielded)))) + to_chat(user, span_warning("You need to hold still to deploy [src]'s bipod!")) + return + playsound(src, 'sound/machines/click.ogg', 75, TRUE) + to_chat(user, span_notice("You deploy [src]'s bipod.")) + bipod_deployed = TRUE + + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(retract_bipod)) + update_appearance() + +/obj/item/gun/ballistic/automatic/hmg/proc/retract_bipod(atom/source, mob/user) + SIGNAL_HANDLER + if(!bipod_deployed) + return + if(!user || !ismob(user)) + user = loc + playsound(src, 'sound/machines/click.ogg', 75, TRUE) + to_chat(user, span_notice("The bipod undeploys itself.")) + bipod_deployed = FALSE + + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + update_appearance() + + +/obj/item/gun/ballistic/automatic/hmg/on_unwield(obj/item/source, mob/user) + . = ..() + retract_bipod(user=user) + +/obj/item/gun/ballistic/automatic/hmg/update_icon_state() + . = ..() + item_state = "[initial(item_state)][bipod_deployed ? "_deployed" : ""]" + +/obj/item/gun/ballistic/automatic/hmg/update_overlays() + . = ..() + if(has_bipod) + . += "[base_icon_state || initial(icon_state)][bipod_deployed ? "_deployed" : "_undeployed"]" + +/obj/item/gun/ballistic/automatic/hmg/solar //This thing fires a 5.56 equivalent, that's an LMG, not an HMG, get out + name = "\improper Solar" + desc = "A TerraGov LMG-169 designed in 169 FS, nicknamed 'Solar.' A inscription reads: 'PROPERTY OF TERRAGOV', with 'TERRAGOV' poorly scribbled out, and replaced by 'SOLAR ARMORIES'. Chambered in 4.73×33mm caseless ammunition." + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + + icon_state = "solar" + + fire_sound = 'sound/weapons/gun/l6/shot.ogg' + default_ammo_type = /obj/item/ammo_box/magazine/rifle47x33mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/rifle47x33mm, + ) + spread = 7 + + fire_delay = 0.1 SECONDS + + fire_select_icon_state_prefix = "caseless_" + + show_magazine_on_sprite = TRUE + w_class = WEIGHT_CLASS_BULKY + manufacturer = MANUFACTURER_SOLARARMORIES + + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/launchers.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/launchers.dm new file mode 100644 index 0000000000..cc571b7347 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/launchers.dm @@ -0,0 +1,147 @@ +//KEEP IN MIND: These are different from gun/grenadelauncher. These are designed to shoot premade rocket and grenade projectiles, not flashbangs or chemistry casings etc. +//Put handheld rocket launchers here if someone ever decides to make something so hilarious ~Paprika + +/obj/item/gun/ballistic/revolver/grenadelauncher//this is only used for underbarrel grenade launchers at the moment, but admins can still spawn it if they feel like being assholes + desc = "A break-action, single-shot grenade launcher. A compact way to deliver a big boom." + name = "grenade launcher" + default_ammo_type = /obj/item/ammo_box/magazine/internal/grenadelauncher + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/grenadelauncher, + ) + fire_sound = 'sound/weapons/gun/general/grenade_launch.ogg' + w_class = WEIGHT_CLASS_NORMAL + bolt_type = BOLT_TYPE_NO_BOLT + fire_delay = 1 SECONDS + semi_auto = TRUE + has_safety = FALSE + safety = FALSE + gate_offset = 0 + +/obj/item/gun/ballistic/revolver/grenadelauncher/attackby(obj/item/A, mob/user, params) + ..() + if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing)) + chamber_round() + +/obj/item/gun/ballistic/revolver/grenadelauncher/cyborg + desc = "A heavy grenade launcher with an oversized 6-shot cylinder." + name = "multi grenade launcher" + icon = 'icons/mecha/mecha_equipment.dmi' + icon_state = "mecha_grenadelnchr" + bad_type = /obj/item/gun/ballistic/revolver/grenadelauncher/cyborg + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder/grenademulti + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder/grenademulti, + ) + +/obj/item/gun/ballistic/revolver/grenadelauncher/cyborg/attack_self() + return + +GLOBAL_LIST_INIT(rpg_scrawlings, list( + "\"FRONT TOWARDS ENEMY\"", + "\"MY WIFE LEFT ME\"", + "A Kepori inset in a stylized crimson heart", + "\"Eat lead psychohazard!\"", + "\"Portable Demotion\"", + "A drawing of the Rilena character 'T4L1' smoking a boof", + "\"Eat it corpo!\"", +//--------------------------------------------------------------------------------------------------------------------- +// STARFLY EDIT - CHANGE BEGIN +#ifndef STARFLY13_MODULE_SINTA_UNATHI_ENABLED +//--------------------------------------------------------------------------------------------------------------------- + "A Sarathi woman in a suggestive pose", + "A masculine Sarathi shouldering a launcher", +//--------------------------------------------------------------------------------------------------------------------- +#else +//--------------------------------------------------------------------------------------------------------------------- + "A Unathi woman in a suggestive pose", + "A masculine Unathi shouldering a launcher", +//--------------------------------------------------------------------------------------------------------------------- +#endif // #ifndef STARFLY13_MODULE_SINTA_UNATHI_ENABLED +// STARFLY EDIT - CHANGE END +//--------------------------------------------------------------------------------------------------------------------- + "A Vox woman with a sledgehammer over their shoulder", + "A man in a floral patterned shirt and nothing else, drawn leaning against the rocket's tube", + "A crudely-drawn picture of a Gorlex Marauder exploding", + "A scratched-out link to some kind of website", + ".:|:;", + "\"SPEAR TO THE SHOAL, FOR A FREE FRONTIER!\"", + "\"Arm this!\"", +)) + + +/obj/item/gun/ballistic/rocketlauncher + name = "\improper PML-9" + desc = "A reusable rocket-propelled grenade launcher." + + icon_state = "rocketlauncher" + item_state = "rocketlauncher" + default_ammo_type = /obj/item/ammo_box/magazine/internal/rocketlauncher + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/rocketlauncher, + ) + fire_sound = 'sound/weapons/gun/general/rocket_launch.ogg' + load_sound = 'sound/weapons/gun/general/rocket_load.ogg' + gun_firemodes = list(FIREMODE_SEMIAUTO) + burst_size = 1 + fire_delay = 0.4 SECONDS + + weapon_weight = WEAPON_HEAVY + w_class = WEIGHT_CLASS_BULKY + + //Bolt + bolt_type = BOLT_TYPE_NO_BOLT + doesnt_keep_bullet = TRUE + + ///Magazine stuff + cartridge_wording = "rocket" + internal_magazine = TRUE + empty_indicator = TRUE + tac_reloads = FALSE + casing_ejector = FALSE + + manufacturer = MANUFACTURER_SCARBOROUGH + + attack_verb = list("bludgeoned", "hit", "slammed", "whacked") + + valid_attachments = list() + slot_available = list() + + var/rpg_scribble = null + +/obj/item/gun/ballistic/rocketlauncher/Initialize() + . = ..() + rpg_scribble = pick(GLOB.rpg_scrawlings) + desc += " [rpg_scribble] is scrawled on the tube" + +/obj/item/gun/ballistic/rocketlauncher/attackby(obj/item/A, mob/user, params) + . = ..() + if(istype(A, /obj/item/pen)) + rpg_scribble = stripped_input(user, "What are you putting on [src]?", "Rocket Launcher Doodle") + if(!rpg_scribble || !length(rpg_scribble)) + desc = "[src::desc]" + return + desc = "[src::desc] [rpg_scribble] is scribbled on the body." + + +/obj/item/gun/ballistic/rocketlauncher/afterattack() + . = ..() + magazine.get_round(FALSE) //Hack to clear the mag after it's fired + +/obj/item/gun/ballistic/rocketlauncher/attack_self_tk(mob/user) + return //too difficult to remove the rocket with TK + +/obj/item/gun/ballistic/rocketlauncher/solgov + name = "Panzerfaust XII" + desc = "The standard recoiless rifle of the Solarian Confederation. Barely varies from previous models." + + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + + //recoiless rifles use shells + cartridge_wording = "shell" + + icon_state = "panzerfaust" + item_state = "panzerfaust" + manufacturer = MANUFACTURER_SOLARARMORIES diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/marksman.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/marksman.dm new file mode 100644 index 0000000000..15f7f7da79 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/marksman.dm @@ -0,0 +1,15 @@ +/obj/item/gun/ballistic/automatic/marksman + bad_type = /obj/item/gun/ballistic/automatic/marksman + show_magazine_on_sprite = TRUE + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + burst_size = 1 + zoomable = TRUE //this var as true without setting anything else produces a 2x zoom + wield_slowdown = DMR_SLOWDOWN + aimed_wield_slowdown = LONG_RIFLE_AIM_SLOWDOWN + zoom_amt = DMR_ZOOM + wield_delay = 1 SECONDS + + min_recoil = 0.1 + + light_range = 2 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/pistol.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/pistol.dm new file mode 100644 index 0000000000..aefb02186f --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/pistol.dm @@ -0,0 +1,189 @@ +/obj/item/gun/ballistic/automatic/pistol + bad_type = /obj/item/gun/ballistic/automatic/pistol + bolt_type = BOLT_TYPE_LOCKING + w_class = WEIGHT_CLASS_NORMAL + + vary_fire_sound = FALSE + fire_sound_volume = 90 + bolt_wording = "slide" + weapon_weight = WEAPON_LIGHT + pickup_sound = 'sound/items/handling/gun_pickup.ogg' + + //recoil = 0.5 // apogee wants bloom, this is a placeholder until then to simulate the same concept. //UPDATE ive changed my mind on this, however i would + recoil_unwielded = 3 + recoil_backtime_multiplier = 1 + + zoom_amt = PISTOL_ZOOM + + wield_delay = 0.2 SECONDS + fire_delay = 0.2 SECONDS + spread = 5 + spread_unwielded = 7 + + wield_slowdown = PISTOL_SLOWDOWN + aimed_wield_slowdown = PISTOL_AIM_SLOWDOWN + + muzzleflash_iconstate = "muzzle_flash_light" + light_range = 1 + + refused_attachments = list( + /obj/item/attachment/gun, + ) + +/obj/item/gun/ballistic/automatic/pistol/commissar + name = "\improper Commissar" + desc = "A Nanotrasen-issue handgun, modified with a voice box to further enhance its effectiveness in troop discipline." + icon_state = "challenger" + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/co9mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/co9mm, + ) + var/funnysounds = TRUE + var/cooldown = 0 + load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + +/obj/item/gun/ballistic/automatic/pistol/commissar/equipped(mob/living/user, slot) + ..() + if(slot == ITEM_SLOT_HANDS && funnysounds) // We do this instead of equip_sound as we only want this to play when it's wielded + playsound(src, 'sound/weapons/gun/commissar/pickup.ogg', 30, 0) + +/obj/item/gun/ballistic/automatic/pistol/commissar/shoot_live_shot(mob/living/user, pointblank, atom/pbtarget, message) + . = ..() + if(prob(50) && funnysounds) + playsound(src, 'sound/weapons/gun/commissar/shot.ogg', 30, 0) + +/obj/item/gun/ballistic/automatic/pistol/commissar/shoot_with_empty_chamber(mob/living/user) + . = ..() + if(prob(50) && funnysounds) + playsound(src, 'sound/weapons/gun/commissar/dry.ogg', 30, 0) + +/obj/item/gun/ballistic/automatic/pistol/commissar/insert_magazine(mob/user, obj/item/ammo_box/magazine/AM, display_message) + . = ..() + if(bolt_locked) + drop_bolt(user) + if(. && funnysounds) + playsound(src, 'sound/weapons/gun/commissar/magazine.ogg', 30, 0) + +/obj/item/gun/ballistic/automatic/pistol/commissar/multitool_act(mob/living/user, obj/item/I) + . = ..() + funnysounds = !funnysounds + to_chat(user, span_notice("You toggle [src]'s vox audio functions.")) + +/obj/item/gun/ballistic/automatic/pistol/commissar/AltClick(mob/user) + . = ..() + if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + if((cooldown < world.time - 200) && funnysounds) + user.audible_message("DON'T TURN AROUND!") + playsound(src, 'sound/weapons/gun/commissar/dontturnaround.ogg', 50, 0, 4) + cooldown = world.time + +/obj/item/gun/ballistic/automatic/pistol/commissar/examine(mob/user) + . = ..() + if(funnysounds) + . += span_info("Alt-click to use \the [src] vox hailer.") + +/obj/item/gun/ballistic/automatic/pistol/disposable + name = "disposable gun" + desc = "An exceedingly flimsy plastic gun that is extremely cheap to produce. You get what you pay for." + fire_sound = 'sound/weapons/gun/pistol/himehabu.ogg' + + icon_state = "disposable" //credit to discord user 20nypercent for the sprite + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/disposable + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/disposable, + ) + custom_materials = list(/datum/material/plastic=2000) + manufacturer = MANUFACTURER_NONE + has_safety = FALSE //thing barely costs anything, why would it have a safety? + safety = FALSE + +/obj/item/gun/ballistic/automatic/pistol/disposable/eject_magazine(mob/user, display_message = TRUE) + to_chat(user, span_warning("Theres no magazine to eject!")) + return + +/obj/item/gun/ballistic/automatic/pistol/disposable/insert_magazine(mob/user) + to_chat(user, span_warning("Theres no magazine to replace!")) + return + +//not technically a pistol but whatever +/obj/item/gun/ballistic/derringer + name = ".38 Derringer" + desc = "An easily concealable derringer. Uses .38 special ammo." + icon_state = "derringer" + item_state = "hp_generic" + + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + default_ammo_type = /obj/item/ammo_box/magazine/internal/derr38 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/derr38, + ) + fire_sound = 'sound/weapons/gun/revolver/shot.ogg' + load_sound = 'sound/weapons/gun/revolver/load_bullet.ogg' + eject_sound = 'sound/weapons/gun/revolver/empty.ogg' + fire_sound_volume = 60 + dry_fire_sound = 'sound/weapons/gun/revolver/dry_fire.ogg' + casing_ejector = FALSE + internal_magazine = TRUE + bolt_type = BOLT_TYPE_NO_BOLT + tac_reloads = FALSE + w_class = WEIGHT_CLASS_TINY + +/obj/item/gun/ballistic/derringer/get_ammo(countchambered = FALSE, countempties = TRUE) + var/boolets = 0 //legacy var name maturity + if (chambered && countchambered) + boolets++ + if (magazine) + boolets += magazine.ammo_count(countempties) + return boolets + +/obj/item/gun/ballistic/derringer/examine(mob/user) + . = ..() + var/live_ammo = get_ammo(FALSE, FALSE) + . += "[live_ammo ? live_ammo : "None"] of those are live rounds." + +/obj/item/gun/ballistic/derringer/traitor + name = "\improper .357 Syndicate Derringer" + desc = "An easily concealable derriger, if not for the bright red-and-black. Uses .357 ammo." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + icon_state = "derringer_syndie" + item_state = "sa_generic" + default_ammo_type = /obj/item/ammo_box/magazine/internal/derr357 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/derr357, + ) + fire_sound_volume = 50 //Tactical stealth firing + +/obj/item/gun/ballistic/derringer/gold + name = "\improper Golden Derringer" + desc = "The golden sheen is somewhat counter-intuitive on a holdout weapon, but it looks cool. Uses .357 ammo." + icon_state = "derringer_gold" + default_ammo_type = /obj/item/ammo_box/magazine/internal/derr357 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/derr357, + ) + +EMPTY_GUN_HELPER(derringer) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/revolver.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/revolver.dm new file mode 100644 index 0000000000..6fe8376f3b --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/revolver.dm @@ -0,0 +1,486 @@ +#define REVOLVER_ROTATE_LEFT "rotate chamber left" +#define REVOLVER_ROTATE_RIGHT "rotate chamber right" +#define REVOLVER_AUTO_ROTATE_RIGHT_LOADING "auto rotate right when loading ammo" +#define REVOLVER_AUTO_ROTATE_LEFT_LOADING "auto rotate left when loading ammo" +#define REVOLVER_EJECT_CURRENT "eject current bullet" +#define REVOLVER_EJECT_ALL "auto eject all bullets" +#define REVOLVER_FLIP "flip the revolver by the trigger" + +/obj/item/gun/ballistic/revolver + name = "i demand" + desc = "You feel as if you should make a 'adminhelp' if you see one of these, along with a 'github' report. You don't really understand what this means though." + icon_state = "revolver" + bad_type = /obj/item/gun/ballistic/revolver + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder, + ) + fire_sound = 'sound/weapons/gun/revolver/shot.ogg' + rack_sound = 'sound/weapons/gun/revolver/revolver_prime.ogg' + load_sound = 'sound/weapons/gun/revolver/load_bullet.ogg' + eject_sound = 'sound/weapons/gun/revolver/empty.ogg' + vary_fire_sound = FALSE + fire_sound_volume = 90 + dry_fire_sound = 'sound/weapons/gun/revolver/dry_fire.ogg' + casing_ejector = FALSE + internal_magazine = TRUE + bolt_type = BOLT_TYPE_NO_BOLT + tac_reloads = FALSE + var/spin_delay = 10 + var/recent_spin = 0 + manufacturer = MANUFACTURER_SCARBOROUGH + + valid_attachments = list() + slot_available = list() + fire_delay = 0.4 SECONDS + spread_unwielded = 15 + recoil = 0.5 + recoil_unwielded = 2 + semi_auto = FALSE + bolt_wording = "hammer" + dry_fire_sound = 'sound/weapons/gun/general/bolt_drop.ogg' + dry_fire_text = "snap" + + wield_slowdown = REVOLVER_SLOWDOWN + aimed_wield_slowdown = PISTOL_AIM_SLOWDOWN + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + + safety_wording = "hammer" + + gunslinger_recoil_bonus = -1 + gunslinger_spread_bonus = -8 + + var/gate_loaded = FALSE //for stupid wild west shit + var/gate_offset = 5 //for wild west shit 2: instead of ejecting the chambered round, eject the next round if 1 + var/gate_load_direction = REVOLVER_AUTO_ROTATE_RIGHT_LOADING //when we load ammo with a box, which direction do we rotate the cylinder? unused with normal revolvers + + COOLDOWN_DECLARE(flip_cooldown) + +/obj/item/gun/ballistic/revolver/examine(mob/user) + . = ..() + . += "You can use the revolver with your other empty hand to empty the cylinder." + +/obj/item/gun/ballistic/revolver/update_overlays() + . = ..() + if(semi_auto) + return + if(current_skin) + . += "[unique_reskin[current_skin]][safety ? "_hammer_up" : "_hammer_down"]" + else + . += "[base_icon_state || initial(icon_state)][safety ? "_hammer_up" : "_hammer_down"]" + + +/obj/item/gun/ballistic/revolver/process_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE, atom/shooter) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + return ..() + +/obj/item/gun/ballistic/revolver/attack_hand(mob/user) + if(loc == user && user.is_holding(src)) + var/num_unloaded = unload_all_ammo(user) + if (num_unloaded) + to_chat(user, "You unload [num_unloaded] [cartridge_wording]\s from [src].") + if(!gate_loaded) + playsound(user, eject_sound, eject_sound_volume, eject_sound_vary) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + update_appearance() + return + else + return ..() + else + return ..() + +/obj/item/gun/ballistic/revolver/proc/unload_all_ammo(mob/living/user) + var/num_unloaded = 0 + + if(!gate_loaded) //"normal" revolvers + for(var/obj/item/ammo_casing/casing_to_eject in get_ammo_list(FALSE, TRUE)) + if(!casing_to_eject) + continue + casing_to_eject.forceMove(drop_location()) + var/angle_of_movement =(rand(-3000, 3000) / 100) + dir2angle(turn(user.dir, 180)) + casing_to_eject.AddComponent(/datum/component/movable_physics, _horizontal_velocity = rand(450, 550) / 100, _vertical_velocity = rand(400, 450) / 100, _horizontal_friction = rand(20, 24) / 100, _z_gravity = PHYSICS_GRAV_STANDARD, _z_floor = 0, _angle_of_movement = angle_of_movement, _bounce_sound = casing_to_eject.bounce_sfx_override) + + num_unloaded++ + SSblackbox.record_feedback("tally", "station_mess_created", 1, casing_to_eject.name) + chamber_round(FALSE) + return num_unloaded + else + var/num_to_unload = magazine.max_ammo + if(!get_ammo_list(FALSE)) + return num_unloaded + + for(var/i in 1 to num_to_unload) + var/doafter_time = 0.4 SECONDS + if(!do_after(user, doafter_time, user)) + break + if(!eject_casing(user)) + doafter_time = 0 SECONDS + else + num_unloaded++ + if(!do_after(user, doafter_time, user)) + break + chamber_round(TRUE, TRUE) + + if (num_unloaded) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + update_appearance() + return num_unloaded + +/obj/item/gun/ballistic/revolver/proc/eject_casing(mob/living/user, obj/item/ammo_casing/casing_to_eject, casing_index) + var/list/rounds = magazine.ammo_list() + if(!casing_to_eject) + casing_to_eject = rounds[gate_offset+1] //byond arrays start at 1, so we add 1 to get the correct index + if(!casing_to_eject) //if theres STILL nothing, we cancel this + if(user) + to_chat(user, "There's nothing in the gate to eject from [src]!") + return FALSE + playsound(src, eject_sound, eject_sound_volume, eject_sound_vary) + casing_to_eject.forceMove(drop_location()) + var/angle_of_movement =(rand(-3000, 3000) / 100) + dir2angle(turn(user.dir, 180)) + casing_to_eject.AddComponent(/datum/component/movable_physics, _horizontal_velocity = rand(350, 450) / 100, _vertical_velocity = rand(400, 450) / 100, _horizontal_friction = rand(20, 24) / 100, _z_gravity = PHYSICS_GRAV_STANDARD, _z_floor = 0, _angle_of_movement = angle_of_movement, _bounce_sound = casing_to_eject.bounce_sfx_override) + + SSblackbox.record_feedback("tally", "station_mess_created", 1, casing_to_eject.name) + if(!gate_loaded) + magazine.stored_ammo[casing_index] = null + chamber_round(FALSE) + else + magazine.stored_ammo[gate_offset+1] = null + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + update_appearance() + + + if(user) + to_chat(user, "You eject the [cartridge_wording] from [src].") + return TRUE + +/obj/item/gun/ballistic/revolver/proc/insert_casing(mob/living/user, obj/item/ammo_casing/casing_to_insert, allow_ejection) + if(!casing_to_insert) + return FALSE + +// Check if the bullet's caliber matches the magazine's caliber.If not, send a warning message to the user and return FALSE. + if(casing_to_insert.caliber != magazine.caliber) + to_chat(user, "\The [casing_to_insert] is not suitable for [src].") + return FALSE + + var/list/rounds = magazine.ammo_list() + var/obj/item/ammo_casing/slot = rounds[gate_offset+1] //byond arrays start at 1, so we add 1 to get the correct index + var/doafter_time = 0.4 SECONDS + if(!gate_loaded) //"normal" revolvers + for(var/i in 1 to magazine.stored_ammo.len) + var/obj/item/ammo_casing/casing_to_eject = magazine.stored_ammo[i] + if(casing_to_eject) + if(!casing_to_eject.BB && allow_ejection) + eject_casing(user, casing_to_eject, i) + + casing_to_eject = magazine.stored_ammo[i] //check again + if(casing_to_eject) + continue + else + magazine.stored_ammo[i] = casing_to_insert + casing_to_insert.forceMove(magazine) + chamber_round(FALSE) + break + else + if(slot) + if(!slot.BB && allow_ejection) + if(!do_after(user, doafter_time, user)) + eject_casing(user) + + rounds = magazine.ammo_list() + slot = rounds[gate_offset+1] //check again + if(slot) + to_chat(user, "There's already a casing in the gate of [src]!") + return FALSE + + magazine.stored_ammo[gate_offset+1] = casing_to_insert + casing_to_insert.forceMove(magazine) + + playsound(src, load_sound, load_sound_volume, load_sound_vary) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + update_appearance() + if(user) + to_chat(user, "You load the [cartridge_wording] into [src].") + return TRUE + +/obj/item/gun/ballistic/revolver/attackby(obj/item/attacking_obj, mob/user, params) + if (istype(attacking_obj, /obj/item/ammo_casing) || istype(attacking_obj, /obj/item/ammo_box)) + if(istype(attacking_obj, /obj/item/ammo_casing)) + insert_casing(user, attacking_obj, TRUE) + else + var/obj/item/ammo_box/attacking_box = attacking_obj + var/num_loaded = 0 + var/list/ammo_list_no_empty = get_ammo_list(FALSE) + listclearnulls(ammo_list_no_empty) + var/num_to_load = magazine.max_ammo - LAZYLEN(ammo_list_no_empty) //get the number of empty bits + + if(!num_to_load) + to_chat(user, span_warning("There's no empty space in [src]!")) + return TRUE + + if(!gate_loaded) //"normal" revolvers + var/i = 0 + for(var/obj/item/ammo_casing/casing_to_insert in attacking_box.stored_ammo) + if(!casing_to_insert || (magazine.caliber && casing_to_insert.caliber != magazine.caliber) || (!magazine.caliber && casing_to_insert.type != magazine.ammo_type)) + break + var/doafter_time = 0.5 SECONDS + if(magazine.instant_load && attacking_box.instant_load) + doafter_time = 0 SECONDS + if(!do_after(user, doafter_time, user)) + break + if(!insert_casing(user, casing_to_insert, FALSE)) + break + else + num_loaded++ + attacking_box.update_ammo_count() + attacking_box.stored_ammo -= casing_to_insert + i++ + if(i >= num_to_load) + break + else + var/i = 0 + for(var/obj/item/ammo_casing/casing_to_insert in attacking_box.stored_ammo) + if(!casing_to_insert || (magazine.caliber && casing_to_insert.caliber != magazine.caliber) || (!magazine.caliber && casing_to_insert.type != magazine.ammo_type)) + break + var/doafter_time = 0.4 SECONDS + if(!do_after(user, doafter_time, user)) + break + if(!insert_casing(null, casing_to_insert, FALSE)) + doafter_time = 0 SECONDS + else + num_loaded++ + attacking_box.update_ammo_count() + attacking_box.stored_ammo -= casing_to_insert + if(!do_after(user, doafter_time, user)) + break + switch(gate_load_direction) + if(REVOLVER_AUTO_ROTATE_RIGHT_LOADING) + chamber_round(TRUE) + if(REVOLVER_AUTO_ROTATE_LEFT_LOADING) + chamber_round(TRUE, TRUE) + i++ + if(i >= num_to_load) + break + + if(num_loaded) + to_chat(user, span_notice("You load [num_loaded] [cartridge_wording]\s into [src].")) + attacking_box.update_ammo_count() + update_appearance() + return TRUE + else + return ..() + +/obj/item/gun/ballistic/revolver/unique_action(mob/living/user) + rack(user) + return + +///updates a bunch of racking related stuff and also handles the sound effects and the like +/obj/item/gun/ballistic/revolver/rack(mob/user = null, toggle_hammer = TRUE) + if(user && !semi_auto) + if(safety && toggle_hammer) + toggle_safety(user, FALSE, FALSE) + else if(toggle_hammer) + to_chat(user, "The [safety_wording] is already [safety ? "UP" : "DOWN"]! Use Ctrl-Click to disengage the [safety_wording]!") + return + else if(!semi_auto) + if(safety && toggle_hammer) + toggle_safety(null, FALSE, FALSE) + else if (toggle_hammer) + return + if(user && semi_auto) + to_chat(user, "You rack the [bolt_wording] of \the [src].") + playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) + + if(!safety && !semi_auto) + chamber_round(TRUE) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + update_appearance() + +/obj/item/gun/ballistic/revolver/chamber_round(spin_cylinder = TRUE, counter_clockwise = FALSE) + if(spin_cylinder) + chambered = magazine.get_round(TRUE, counter_clockwise) + playsound(src, 'sound/weapons/gun/revolver/spin_single.ogg', 100, FALSE) + else + chambered = magazine.stored_ammo[1] + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + +/obj/item/gun/ballistic/revolver/AltClick(mob/user) + if (unique_reskin && !current_skin && user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)) + return ..() + + var/chamber_options = list( + REVOLVER_ROTATE_LEFT = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_left"), + REVOLVER_ROTATE_RIGHT = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_right"), + REVOLVER_AUTO_ROTATE_LEFT_LOADING = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_auto_left"), + REVOLVER_EJECT_ALL = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_eject_all"), + REVOLVER_EJECT_CURRENT = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_eject_one"), + REVOLVER_AUTO_ROTATE_RIGHT_LOADING = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_auto_right"), + REVOLVER_FLIP = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_revolver_flip"), + ) + + var/image/editing_image = chamber_options[gate_load_direction] + editing_image.icon_state = "radial_revolver_auto_[gate_load_direction == REVOLVER_AUTO_ROTATE_RIGHT_LOADING ? "right":"left"]_on" + + if(!HAS_TRAIT(user, TRAIT_GUNSLINGER)) //only gunslingers are allowed to flip + chamber_options -= REVOLVER_FLIP + + if(!gate_loaded) //these are completely redundant if you can reload everything with a speedloader + chamber_options -= REVOLVER_AUTO_ROTATE_LEFT_LOADING + chamber_options -= REVOLVER_AUTO_ROTATE_RIGHT_LOADING + chamber_options -= REVOLVER_EJECT_CURRENT + + + var/pick = show_radial_menu(user, src, chamber_options, custom_check = CALLBACK(src, PROC_REF(can_use_radial), user), require_near = TRUE) + switch(pick) + if(REVOLVER_ROTATE_LEFT) + chamber_round(TRUE, TRUE) + if(REVOLVER_ROTATE_RIGHT) + chamber_round(TRUE) + if(REVOLVER_AUTO_ROTATE_RIGHT_LOADING) + gate_load_direction = REVOLVER_AUTO_ROTATE_RIGHT_LOADING + if(REVOLVER_AUTO_ROTATE_LEFT_LOADING) + gate_load_direction = REVOLVER_AUTO_ROTATE_LEFT_LOADING + if(REVOLVER_EJECT_ALL) + unload_all_ammo(user) + return + if(REVOLVER_EJECT_CURRENT) + eject_casing(user) + if(REVOLVER_FLIP) + tryflip(user) + if(null) + return + AltClick(user) + +/obj/item/gun/ballistic/revolver/proc/can_use_radial(mob/user) + if(QDELETED(src)) + return FALSE + if(!istype(user)) + return FALSE + if(user.incapacitated()) + return FALSE + return TRUE + +/obj/item/gun/ballistic/revolver/verb/spin() + set name = "Spin Chamber" + set category = "Object" + set desc = "Click to spin your revolver's chamber." + + var/mob/M = usr + + if(M.stat || !in_range(M,src)) + return + + if (recent_spin > world.time) + return + recent_spin = world.time + spin_delay + + if(do_spin()) + playsound(usr, "revolver_spin", 30, FALSE) + usr.visible_message("[usr] spins [src]'s chamber.", "You spin [src]'s chamber.") + else + verbs -= /obj/item/gun/ballistic/revolver/verb/spin + +/obj/item/gun/ballistic/revolver/proc/do_spin() + var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine + . = istype(C) + if(.) + C.spin() + chamber_round(FALSE) + +/obj/item/gun/ballistic/revolver/get_ammo(countchambered = FALSE, countempties = TRUE) + var/boolets = 0 //mature var names for mature people + if (chambered && countchambered) + boolets++ + if (magazine) + boolets += magazine.ammo_count(countempties) + return boolets + +/obj/item/gun/ballistic/revolver/toggle_safety(mob/user, silent=FALSE, rack_gun=TRUE) + if(semi_auto)//apogee said double actions should have normal safeties, so... + return ..() + safety = !safety + + if(!silent) + playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) + if(user) + user.visible_message( + span_notice("[user] pulls the [safety_wording] on [src] [safety ? "UP" : "DOWN"]."), + span_notice("You pull the [safety_wording] on [src] [safety ? "UP" : "DOWN"]."), + ) + + update_appearance() + + if(rack_gun) + rack(toggle_hammer= FALSE) + +/obj/item/gun/ballistic/revolver/examine(mob/user) + . = ..() + var/live_ammo = get_ammo(FALSE, FALSE) + . += "[live_ammo ? live_ammo : "None"] of those are live rounds." + if (current_skin) + . += "It can be spun with alt+click" + +/obj/item/gun/ballistic/revolver/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + var/fan = FALSE + if(HAS_TRAIT(user, TRAIT_GUNSLINGER) && !semi_auto && !wielded && loc == user && !safety && !user.get_inactive_held_item()) + fan = TRUE + fire_delay = 0 SECONDS + . = ..() + fire_delay = src::fire_delay + if(fan) + rack() + to_chat(user, span_notice("You fan the [bolt_wording] of \the [src]!")) + balloon_alert_to_viewers("fans revolver!") + fire_delay = 0 SECONDS + +/obj/item/gun/ballistic/revolver/shoot_live_shot(mob/living/user, pointblank, atom/pbtarget, message) + . = ..() + if(!semi_auto) + toggle_safety(silent = TRUE, rack_gun = FALSE) + +/obj/item/gun/ballistic/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj) + if(!safety) + to_chat(user, "*[dry_fire_text]*") + playsound(src, dry_fire_sound, 30, TRUE) + if(!semi_auto) + toggle_safety(silent = TRUE, rack_gun = FALSE) + return + to_chat(user, "The hammer is up on [src]! Pull it down to fire!") + +/obj/item/gun/ballistic/revolver/pickup(mob/user) + . = ..() + tryflip(user) + +/obj/item/gun/ballistic/revolver/proc/tryflip(mob/living/user) + if(HAS_TRAIT(user, TRAIT_GUNSLINGER)) + if(COOLDOWN_FINISHED(src, flip_cooldown)) + COOLDOWN_START(src, flip_cooldown, 0.3 SECONDS) + SpinAnimation(5,1) + user.visible_message("[user] spins the [src] around their finger by the trigger. That’s pretty badass.") + playsound(src, 'sound/items/handling/ammobox_pickup.ogg', 20, FALSE) + return + +EMPTY_GUN_HELPER(revolver/viper) + +/obj/item/gun/ballistic/revolver/rhino + name = "\improper Unica 6 auto-revolver" + desc = "A high-powered revolver with a unique auto-reloading system. Uses .357 ammo." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "mateba" + manufacturer = MANUFACTURER_NONE + semi_auto = TRUE + safety_wording = "safety" + spread = 0 + spread_unwielded = 7 + +/obj/item/gun/ballistic/revolver/golden + name = "\improper Golden revolver" + desc = "This ain't no game, ain't never been no show, And I'll gladly gun down the oldest lady you know. Uses .357 ammo." + icon_state = "goldrevolver" + fire_sound = 'sound/weapons/resonator_blast.ogg' + recoil = 8 + manufacturer = MANUFACTURER_NONE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/rifle.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/rifle.dm new file mode 100644 index 0000000000..4847c12e4b --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/rifle.dm @@ -0,0 +1,100 @@ +/obj/item/gun/ballistic/rifle + name = "Bolt Rifle" + desc = "Some kind of bolt-action rifle. You get the feeling you shouldn't have this." + icon = 'icons/obj/guns/48x32guns.dmi' + mob_overlay_icon = 'icons/mob/clothing/back.dmi' + icon_state = "hunting" + item_state = "hunting" + bad_type = /obj/item/gun/ballistic/rifle + default_ammo_type = /obj/item/ammo_box/magazine/internal/boltaction + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/boltaction, + ) + bolt_wording = "bolt" + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + bolt_type = BOLT_TYPE_STANDARD + semi_auto = FALSE + internal_magazine = TRUE + fire_sound = 'sound/weapons/gun/rifle/mosin.ogg' + fire_sound_volume = 90 + vary_fire_sound = FALSE + rack_sound = 'sound/weapons/gun/rifle/bolt_out.ogg' + bolt_drop_sound = 'sound/weapons/gun/rifle/bolt_in.ogg' + tac_reloads = FALSE + weapon_weight = WEAPON_MEDIUM + pickup_sound = 'sound/items/handling/rifle_pickup.ogg' + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + + zoom_amt = RIFLE_ZOOM + aimed_wield_slowdown = RIFLE_AIM_SLOWDOWN + + spread = -1 + spread_unwielded = 48 + recoil = -3 + recoil_unwielded = 4 + wield_slowdown = RIFLE_SLOWDOWN + wield_delay = 1.2 SECONDS + +/obj/item/gun/ballistic/rifle/update_overlays() + . = ..() + . += "[icon_state]_bolt[bolt_locked ? "_locked" : ""]" + +/obj/item/gun/ballistic/rifle/rack(mob/living/user) + if (bolt_locked == FALSE) + to_chat(user, span_notice("You open the bolt of \the [src].")) + playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) + process_chamber(FALSE, FALSE, FALSE, shooter = user) + bolt_locked = TRUE + update_appearance() + if (magazine && !magazine?.ammo_count() && empty_autoeject && !internal_magazine) + eject_magazine(display_message = FALSE) + update_appearance() + return + drop_bolt(user) + +/obj/item/gun/ballistic/rifle/eject_magazine(mob/user, display_message = TRUE, obj/item/ammo_box/magazine/tac_load = null) + if (!bolt_locked && empty_autoeject) + to_chat(user, span_notice("The bolt is closed!")) + return + return ..() + +/obj/item/gun/ballistic/rifle/can_shoot() + if (bolt_locked) + return FALSE + return ..() + +/obj/item/gun/ballistic/rifle/attackby(obj/item/A, mob/user, params) + if (!bolt_locked) + if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACKBY, A, user, params) & COMPONENT_NO_AFTERATTACK) + return TRUE + to_chat(user, span_notice("The bolt is closed!")) + return + return ..() + +/obj/item/gun/ballistic/rifle/examine(mob/user) + . = ..() + . += "The bolt is [bolt_locked ? "open" : "closed"]." + +/obj/item/gun/ballistic/rifle/polymer + name = "polymer survivor rifle" + desc = "A bolt-action rifle made of scrap, desperation, and luck. Likely to shatter at any moment. Chambered in 7.62x40mm." + icon_state = "methrifle" + item_state = "methrifle" + icon = 'icons/obj/guns/manufacturer/hermits/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hermits/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hermits/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hermits/onmob.dmi' + has_safety = FALSE + safety = FALSE + safety_multiplier = 2 + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + default_ammo_type = /obj/item/ammo_box/magazine/internal/boltaction/polymer + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/boltaction/polymer, + ) + can_be_sawn_off = FALSE + manufacturer = MANUFACTURER_NONE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/shotgun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/shotgun.dm new file mode 100644 index 0000000000..1c8e87945a --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -0,0 +1,263 @@ +/obj/item/gun/ballistic/shotgun + name = "shotgun" + desc = "You feel as if you should make a 'adminhelp' if you see one of these, along with a 'github' report. You don't really understand what this means though." + item_state = "shotgun" + bad_type = /obj/item/gun/ballistic/shotgun + fire_sound = 'sound/weapons/gun/shotgun/shot.ogg' + vary_fire_sound = FALSE + fire_sound_volume = 90 + rack_sound = 'sound/weapons/gun/shotgun/rack.ogg' + load_sound = 'sound/weapons/gun/shotgun/insert_shell.ogg' + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot, + ) + semi_auto = FALSE + internal_magazine = TRUE + casing_ejector = FALSE + bolt_wording = "pump" + cartridge_wording = "shell" + tac_reloads = FALSE + pickup_sound = 'sound/items/handling/shotgun_pickup.ogg' + fire_delay = 0.7 SECONDS + pb_knockback = 2 + manufacturer = MANUFACTURER_HUNTERSPRIDE + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + fire_select_icon_state_prefix = "sg_" + + wield_slowdown = SHOTGUN_SLOWDOWN + aimed_wield_slowdown = SHOTGUN_AIM_SLOWDOWN + wield_delay = 0.8 SECONDS + + zoom_amt = SHOTGUN_ZOOM + + spread = 3 + spread_unwielded = 9 + recoil = 1 + recoil_unwielded = 4 + + gunslinger_recoil_bonus = -1 + + min_recoil = 0.1 + wear_rate = 0 + + //in an ideal world this would be a component but I don't wanna untangle all the sleeps doors pull + ///can this shotgun breach doors + var/door_breaching_weapon = TRUE + +/obj/item/gun/ballistic/shotgun/attack_obj(obj/O, mob/living/user) + if(door_breaching_weapon && istype(O, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/breaching = O + if(chambered && chambered.BB && !is_type_in_list(chambered, list(/obj/item/ammo_casing/shotgun/blank, /obj/item/ammo_casing/shotgun/beanbag, /obj/item/ammo_casing/shotgun/rubbershot))) + user.visible_message( + span_warning("[user] put the barrel of [src] to [breaching]'s electronics!"), + span_warning("You puts the barrel of [src] to [breaching]'s electronics, preparing to shred them!"), + span_warning("Metal brushes against metal") + ) + if(do_after(user, 5 SECONDS, breaching)) + if(process_fire(breaching, user, FALSE)) + switch(breaching.security_level) + if(0) + EMPTY_BLOCK_GUARD + if (1) // thin metal plate. blast through it and create debris + var/obj/item/debris = new /obj/item/stack/ore/salvage/scrapmetal + debris.safe_throw_at(user, 2, 5, null, 1) + breaching.security_level = 0 + if(2 to 6) //two shots to break + var/obj/item/debris = new /obj/item/stack/ore/salvage/scrapmetal + debris.safe_throw_at(user, 2, 5, null, 1) + breaching.security_level -= 3 + breaching.security_level = max(breaching.security_level, 0) + return + if(!breaching.open()) + update_icon(ALL, 1, 1) + //im not rewriting the behavior to do exactly what this does with a different name + breaching.obj_flags |= EMAGGED + breaching.lights = FALSE + breaching.locked = TRUE + breaching.loseMainPower() + breaching.loseBackupPower() + return TRUE + return FALSE + return FALSE + . = ..() + +/obj/item/gun/ballistic/shotgun/blow_up(mob/user) + if(chambered && chambered.BB) + process_fire(user, user, FALSE) + return TRUE + for(var/obj/item/ammo_casing/ammo in magazine.stored_ammo) + if(ammo.BB) + process_chamber(FALSE, FALSE) + process_fire(user, user, FALSE) + return TRUE + return FALSE + +// Automatic Shotguns// +/obj/item/gun/ballistic/shotgun/automatic + bad_type = /obj/item/gun/ballistic/shotgun/automatic + spread = 3 + spread_unwielded = 15 + recoil = 1 + recoil_unwielded = 4 + wield_delay = 0.65 SECONDS + manufacturer = MANUFACTURER_NANOTRASEN + semi_auto = TRUE + + gunslinger_recoil_bonus = 1 + wear_rate = 1 + wear_minor_threshold = 60 + wear_major_threshold = 180 + wear_maximum = 300 + + +/obj/item/gun/ballistic/shotgun/automatic/bulldog/inteq + name = "\improper Mastiff Shotgun" + desc = "A variation of the Bulldog, seized from Syndicate armories by deserting troopers then modified to IRMG's standards." + icon_state = "bulldog_inteq" + item_state = "bulldog_inteq" + default_ammo_type = /obj/item/ammo_box/magazine/m12g_bulldog + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m12g_bulldog, + ) + manufacturer = MANUFACTURER_INTEQ + +NO_MAG_GUN_HELPER(shotgun/automatic/bulldog/inteq) + + + +// IMPROVISED SHOTGUN // + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised + name = "improvised shotgun" + desc = "A length of pipe and miscellaneous bits of scrap fashioned into a rudimentary single-shot shotgun." + icon = 'icons/obj/guns/projectile.dmi' + lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' + righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' + mob_overlay_icon = null + inhand_x_dimension = 64 + inhand_y_dimension = 64 + base_icon_state = "ishotgun" + icon_state = "ishotgun" + item_state = "ishotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + slot_flags = null + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/improvised + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/improvised, + ) + sawn_desc = "I'm just here for the gasoline." + unique_reskin = null + var/slung = FALSE + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) + ..() + if(istype(A, /obj/item/stack/cable_coil) && !sawn_off) + var/obj/item/stack/cable_coil/C = A + if(C.use(10)) + slot_flags = ITEM_SLOT_BACK + to_chat(user, span_notice("You tie the lengths of cable to the shotgun, making a sling.")) + slung = TRUE + update_appearance() + else + to_chat(user, span_warning("You need at least ten lengths of cable if you want to make a sling!")) + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/update_icon_state() + . = ..() + if(slung) + item_state = "ishotgunsling" + if(sawn_off) + item_state = "ishotgun_sawn" + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/update_overlays() + . = ..() + if(slung) + . += "ishotgunsling" + if(sawn_off) + . += "ishotgun_sawn" + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/sawoff(forced = FALSE) + . = ..() + if(. && slung) //sawing off the gun removes the sling + new /obj/item/stack/cable_coil(get_turf(src), 10) + slung = 0 + update_appearance() + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/sawn + sawn_off = TRUE + +//god fucking bless brazil +/obj/item/gun/ballistic/shotgun/doublebarrel/brazil + name = "six-barreled \"TRABUCO\" shotgun" + desc = "Dear fucking god, what the fuck even is this!? The recoil caused by the sheer act of firing this thing would probably kill you, if the gun itself doesn't explode in your face first! Theres a green flag with a blue circle and a yellow diamond around it. Some text in the circle says: \"ORDEM E PROGRESSO.\"" + base_icon_state = "shotgun_brazil" + icon_state = "shotgun_brazil" + icon = 'icons/obj/guns/48x32guns.dmi' + lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' + righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' + inhand_x_dimension = 64 + inhand_y_dimension = 64 + item_state = "shotgun_qb" + w_class = WEIGHT_CLASS_BULKY + force = 15 //blunt edge and really heavy + attack_verb = list("bludgeoned", "smashed") + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/sex + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/sex, + ) + burst_size = 6 + burst_delay = 0.04 SECONDS //?? very weird number + pb_knockback = 12 + unique_reskin = null + recoil = 10 + recoil_unwielded = 30 + weapon_weight = WEAPON_LIGHT + fire_sound = 'sound/weapons/gun/shotgun/quadfire.ogg' + rack_sound = 'sound/weapons/gun/shotgun/quadrack.ogg' + load_sound = 'sound/weapons/gun/shotgun/quadinsert.ogg' + fire_sound_volume = 50 + rack_sound_volume = 50 + can_be_sawn_off = FALSE + manufacturer = MANUFACTURER_BRAZIL + gun_firemodes = list(FIREMODE_BURST) + default_firemode = FIREMODE_BURST + +/obj/item/gun/ballistic/shotgun/doublebarrel/brazil/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(prob(0 + (magazine.ammo_count() * 10))) + if(prob(10)) + to_chat(user, "Something isn't right. \the [src] doesn't fire for a brief moment. Then, the following words come to mind: \ + Ó Pátria amada, \n\ + Idolatrada, \n\ + Salve! Salve!") + + message_admins("A [src] misfired and exploded at [ADMIN_VERBOSEJMP(src)], which was fired by [user].") //logging + log_admin("A [src] misfired and exploded at [ADMIN_VERBOSEJMP(src)], which was fired by [user].") + user.take_bodypart_damage(0,50) + explosion(src, 0, 2, 4, 6, TRUE, TRUE) + ..() +/obj/item/gun/ballistic/shotgun/doublebarrel/brazil/death + name = "Force of Nature" + desc = "So you have chosen death." + base_icon_state = "shotgun_e" + icon_state = "shotgun_e" + burst_size = 100 + fire_delay = 0.01 SECONDS + pb_knockback = 40 + recoil = 100 + recoil_unwielded = 200 + recoil_backtime_multiplier = 1 + fire_sound_volume = 100 + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/hundred + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/hundred, + ) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/smg.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/smg.dm new file mode 100644 index 0000000000..0546a2e43c --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/smg.dm @@ -0,0 +1,170 @@ +/obj/item/gun/ballistic/automatic/smg + bad_type = /obj/item/gun/ballistic/automatic/smg + show_magazine_on_sprite = TRUE + + burst_size = 2 + actions_types = list() + fire_delay = 0.1 SECONDS + + spread = 6 + spread_unwielded = 10 + wield_slowdown = SMG_SLOWDOWN + aimed_wield_slowdown = SMG_AIM_SLOWDOWN + zoom_amt = SMG_ZOOM + recoil_unwielded = 4 + w_class = WEIGHT_CLASS_BULKY + + light_range = 1 + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + wield_delay = 0.5 SECONDS + + load_sound = 'sound/weapons/gun/smg/smg_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/smg_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/smg_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/smg_unload.ogg' + + gunslinger_recoil_bonus = 2 + gunslinger_spread_bonus = 16 + wear_minor_threshold = 240 + wear_major_threshold = 720 + wear_maximum = 1200 + + +/obj/item/gun/ballistic/automatic/smg/skm_carbine + name = "\improper SKM-24v" + desc = "The SKM-24v was a carbine modification of the SKM-24 during the Frontiersmen War. This, however, is just a shoddy imitation of that carbine, effectively an SKM-24 with a sawed down barrel and a folding wire stock. Can be fired with the stock folded, though accuracy suffers. Chambered in 4.6x30mm." + + icon_state = "skm_carbine" + item_state = "skm_carbine" + icon = 'icons/obj/guns/manufacturer/hermits/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hermits/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hermits/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hermits/onmob.dmi' + fire_sound = 'sound/weapons/gun/rifle/skm_smg.ogg' + + rack_sound = 'sound/weapons/gun/rifle/skm_cocked.ogg' + load_sound = 'sound/weapons/gun/rifle/skm_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/skm_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/skm_46_30 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/skm_46_30, + ) + + recoil = 2 + recoil_unwielded = 6 + + spread = 8 + spread_unwielded = 14 + + wield_delay = 0.6 SECONDS + wield_slowdown = SMG_SLOWDOWN + + wear_rate = 1.6 + + unique_attachments = list( + /obj/item/attachment/foldable_stock + ) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_STOCK = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 26, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = 11, + "y" = 20, + ) + ) + + default_attachments = list(/obj/item/attachment/foldable_stock) + +/obj/item/gun/ballistic/automatic/smg/skm_carbine/inteq + name = "\improper SKM-44v Mongrel" + desc = "An SKM-44, further modified into a sub-machine gun by Inteq artificers with a new magazine well, collapsing stock, and shortened barrel. Faced with a surplus of SKM-44s and a shortage of other firearms, IRMG has made the most of their available materiel with conversions such as this. Chambered in 10x22mm." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "skm_inteqsmg" + item_state = "skm_inteqsmg" + + default_ammo_type = /obj/item/ammo_box/magazine/smgm10mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/smgm10mm, + ) + manufacturer = MANUFACTURER_INTEQ + + fire_sound = 'sound/weapons/gun/smg/vector_fire.ogg' + + load_sound = 'sound/weapons/gun/smg/smg_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/smg_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/smg_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/smg_unload.ogg' + + spread = 8 + spread_unwielded = 10 + + recoil = 0 + recoil_unwielded = 4 + + wield_delay = 0.4 SECONDS + + unique_attachments = list( + /obj/item/attachment/foldable_stock/inteq + ) + default_attachments = list(/obj/item/attachment/foldable_stock/inteq) + +NO_MAG_GUN_HELPER(automatic/smg/skm_carbine/inteq) + +//TODO: REMOVE +/obj/item/gun/ballistic/automatic/smg/skm_carbine/saber + name = "\improper Nanotrasen Saber SMG" + desc = "A prototype full-auto 9x18mm submachine gun, designated 'SABR'. Has a threaded barrel for suppressors and a folding stock." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "saber" + item_state = "gun" + + default_ammo_type = /obj/item/ammo_box/magazine/m9mm_expedition + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m9mm_expedition, + ) + + fire_sound = 'sound/weapons/gun/smg/vector_fire.ogg' + + load_sound = 'sound/weapons/gun/smg/smg_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/smg_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/smg_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/smg_unload.ogg' + + spread = 7 + spread_unwielded = 10 + + recoil = 0 + recoil_unwielded = 4 + + wield_delay = 0.4 SECONDS + + unique_attachments = list( + /obj/item/attachment/foldable_stock + ) + default_attachments = list(/obj/item/attachment/foldable_stock) + bolt_type = BOLT_TYPE_LOCKING + show_magazine_on_sprite = TRUE + manufacturer = MANUFACTURER_NANOTRASEN_OLD diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/toy.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/toy.dm new file mode 100644 index 0000000000..8344836e72 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/ballistic/toy.dm @@ -0,0 +1,101 @@ +/obj/item/gun/ballistic/automatic/toy + name = "foam force SMG" + desc = "A prototype three-round burst toy submachine gun. Ages 8 and up." + + icon = 'icons/obj/guns/manufacturer/toys/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/toys/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/toys/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/toys/onmob.dmi' + + icon_state = "toysmg" + item_state = "toysmg" + default_ammo_type = /obj/item/ammo_box/magazine/toy/smg + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/toy/smg, + ) + fire_sound = 'sound/items/syringeproj.ogg' + force = 0 + throwforce = 0 + burst_size = 3 + item_flags = NONE + casing_ejector = FALSE + manufacturer = MANUFACTURER_DONKCO + recoil = -10 //its a toy... + recoil_unwielded = -10 + wear_rate = 0 + + +/obj/item/gun/ballistic/automatic/toy/pistol + name = "foam force pistol" + desc = "A small, easily concealable toy handgun. Ages 8 and up." + + icon_state = "toypistol" + item_state = "toypistol" + bolt_type = BOLT_TYPE_LOCKING + w_class = WEIGHT_CLASS_SMALL + default_ammo_type = /obj/item/ammo_box/magazine/toy/pistol + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/toy/pistol, + ) + fire_sound = 'sound/items/syringeproj.ogg' + burst_size = 1 + fire_delay = 0.2 SECONDS + actions_types = list() + recoil = -10 //its a toy... + recoil_unwielded = -10 + manufacturer = MANUFACTURER_DONKCO +/obj/item/gun/ballistic/automatic/toy/pistol/riot + default_ammo_type = /obj/item/ammo_box/magazine/toy/pistol/riot + +/obj/item/gun/ballistic/automatic/toy/pistol/riot/Initialize() + magazine = new /obj/item/ammo_box/magazine/toy/pistol/riot(src) + return ..() + +/obj/item/gun/ballistic/shotgun/toy + name = "foam force shotgun" + desc = "A toy shotgun with wood furniture and a four-shell capacity underneath. Ages 8 and up." + + icon = 'icons/obj/guns/manufacturer/toys/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/toys/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/toys/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/toys/onmob.dmi' + + icon_state = "toyshotgun" + item_state = "toyshotgun" + + force = 0 + throwforce = 0 + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/toy + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/toy, + ) + fire_sound = 'sound/items/syringeproj.ogg' + item_flags = NONE + casing_ejector = FALSE + manufacturer = MANUFACTURER_DONKCO + pb_knockback = 0 + recoil = -10 //its a toy... + recoil_unwielded = -10 + wear_rate = 0 + door_breaching_weapon = FALSE + +/obj/item/gun/ballistic/shotgun/toy/process_chamber(empty_chamber = 0, from_firing = TRUE, chamber_next_round = TRUE, atom/shooter) + . = ..() + if(chambered && !chambered.BB) + qdel(chambered) + +/obj/item/gun/ballistic/shotgun/toy/crossbow + name = "foam force crossbow" + desc = "A weapon favored by many overactive children. Ages 8 and up." + icon_state = "foamcrossbow" + item_state = "crossbow" + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/toy/crossbow + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/toy/crossbow, + ) + fire_sound = 'sound/items/syringeproj.ogg' + slot_flags = ITEM_SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + recoil = -10 //its a toy... + recoil_unwielded = -10 + manufacturer = MANUFACTURER_DONKCO diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy.dm new file mode 100644 index 0000000000..9bee79f5c8 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy.dm @@ -0,0 +1,448 @@ +/obj/item/gun/energy + name = "energy gun" + desc = "A basic energy-based gun." + icon = 'icons/obj/guns/energy.dmi' + icon_state = "laser" + item_state = "spur" + + bad_type = /obj/item/gun/energy + + muzzleflash_iconstate = "muzzle_flash_laser" + light_color = COLOR_SOFT_RED + + has_safety = TRUE + safety = TRUE + + modifystate = FALSE + ammo_x_offset = 2 + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + + fire_select_icon_state_prefix = "laser_" + + ///Ammotype index -- this is the currently selected ammo type + var/ammotype_index + + default_ammo_type = /obj/item/stock_parts/cell/gun + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun, + /obj/item/stock_parts/cell/gun/upgraded, + /obj/item/stock_parts/cell/gun/empty, + /obj/item/stock_parts/cell/gun/upgraded/empty, + ) + var/ammotype_string = "fallback_laser_fallback" + + tac_reloads = FALSE + tactical_reload_delay = 1.2 SECONDS + + var/latch_closed = TRUE + var/latch_toggle_delay = 0.6 SECONDS + + valid_attachments = list( + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet, + /obj/item/attachment/gun + ) + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_MUZZLE = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 26, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 21, + "y" = 24, + ) + ) + +/obj/item/gun/energy/emp_act(severity) + . = ..() + if(!(. & EMP_PROTECT_CONTENTS)) + if(prob(GUN_NO_SAFETY_MALFUNCTION_CHANCE_HIGH)) + discharge("malfunctions from the EMP") + cell.use(round(cell.charge / severity)) + chambered = null //we empty the chamber + recharge_newshot() //and try to charge a new shot + update_appearance() + +/obj/item/gun/energy/get_cell() + return cell + +/obj/item/gun/energy/Initialize(mapload, spawn_empty) + . = ..() + if(spawn_empty) + if(internal_magazine) + spawn_no_ammo = TRUE + else + default_ammo_type = FALSE + + if(default_ammo_type) + cell = new default_ammo_type(src, spawn_no_ammo) + build_ammotypes() + update_ammo_types() + recharge_newshot(TRUE) + if(selfcharge) + START_PROCESSING(SSobj, src) + update_appearance() + +/obj/item/gun/energy/ComponentInitialize() + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/gun/energy/proc/update_ammo_types() + var/obj/item/ammo_casing/energy/shot + for (var/i = 1, i <= ammo_type.len, i++) + var/shottype = ammo_type[i] + shot = new shottype(src) + ammo_type[i] = shot + shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + +/obj/item/gun/energy/Destroy() + if (cell) + QDEL_NULL(cell) + STOP_PROCESSING(SSobj, src) + . = ..() + ammo_type.Cut() + +/obj/item/gun/energy/handle_atom_del(atom/A) + if(A == cell) + cell = null + update_appearance() + return ..() + +/obj/item/gun/energy/process(seconds_per_tick) + if(selfcharge && cell && cell.percent() < 100) + charge_timer += seconds_per_tick + if(charge_timer < charge_delay) + return + charge_timer = 0 + cell.give(1000) //WS Edit - Egun energy cells + if(!chambered) //if empty chamber we try to charge a new shot + recharge_newshot(TRUE) + update_appearance() + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/gun/energy/attack_hand(mob/user) + if(!internal_magazine && loc == user && user.is_holding(src) && cell && tac_reloads && !(gun_firemodes[firemode_index] == FIREMODE_UNDERBARREL)) + eject_cell(user) + return + return ..() + +/obj/item/gun/energy/attackby(obj/item/A, mob/user, params) + if(..()) + return FALSE + + if (!internal_magazine && (A.type in (allowed_ammo_types - blacklisted_ammo_types))) + var/obj/item/stock_parts/cell/gun/C = A + if (!cell) + return insert_cell(user, C) + else + if (tac_reloads) + eject_cell(user, C) + +/obj/item/gun/energy/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C) + if(!latch_closed) + if(user.transferItemToLoc(C, src)) + cell = C + to_chat(user, span_notice("You load the [C] into \the [src].")) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + update_appearance() + return TRUE + else + to_chat(user, span_warning("You cannot seem to get \the [src] out of your hands!")) + return FALSE + else + to_chat(user, span_warning("The [src]'s cell retainment clip is latched!")) + return FALSE + +/obj/item/gun/energy/proc/eject_cell(mob/user, obj/item/stock_parts/cell/gun/tac_load = null) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + cell.forceMove(drop_location()) + var/obj/item/stock_parts/cell/gun/old_cell = cell + old_cell.update_appearance() + cell = null + update_appearance() + if(user) + to_chat(user, span_notice("You pull the cell out of \the [src].")) + if(tac_load && tac_reloads) + if(do_after(user, tactical_reload_delay, src, hidden = TRUE)) + if(insert_cell(user, tac_load)) + to_chat(user, span_notice("You perform a tactical reload on \the [src].")) + else + to_chat(user, span_warning("You dropped the old cell, but the new one doesn't fit. How embarassing.")) + else + to_chat(user, span_warning("Your reload was interupted!")) + return + + user.put_in_hands(old_cell) + update_appearance() + +//special is_type_in_list method to counteract problem with current method +/obj/item/gun/energy/proc/is_attachment_in_contents_list() + for(var/content_item in contents) + if(istype(content_item, /obj/item/attachment/)) + return TRUE + return FALSE + +/obj/item/gun/energy/unique_action(mob/living/user) + if(..()) + return + + if(!internal_magazine && latch_closed) + to_chat(user, span_notice("You start to unlatch the [src]'s power cell retainment clip...")) + if(do_after(user, latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE)) + to_chat(user, span_notice("You unlatch the [src]'s power cell retainment clip " + span_red("OPEN") + ".")) + playsound(src, 'sound/items/taperecorder/taperecorder_play.ogg', 50, FALSE) + tac_reloads = TRUE + latch_closed = FALSE + update_appearance() + + else if(!internal_magazine && !latch_closed) + to_chat(user, span_warning("You start to latch the [src]'s power cell retainment clip...")) + + if (do_after(user, latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE)) + to_chat(user, span_notice("You latch the [src]'s power cell retainment clip " + span_green("CLOSED") + ".")) + playsound(src, 'sound/items/taperecorder/taperecorder_close.ogg', 50, FALSE) + tac_reloads = FALSE + latch_closed = TRUE + update_appearance() + return + +/obj/item/gun/energy/can_shoot(visuals) + if(safety && !visuals) + return FALSE + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + return !QDELETED(cell) ? (cell.charge >= shot.e_cost) : FALSE + +/obj/item/gun/energy/recharge_newshot(no_cyborg_drain) + if (!ammo_type || !cell) + return + if(use_cyborg_cell && !no_cyborg_drain) + if(!iscyborg(loc)) + return + var/mob/living/silicon/robot/R = loc + if(!R.cell) + return + var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot + if(!R.cell.use(shot.e_cost)) //Take power from the borg... + shoot_with_empty_chamber(R) + return + cell.give(shot.e_cost) //... to recharge the shot + if(!chambered) + var/obj/item/ammo_casing/energy/AC = ammo_type[select] + if(cell.charge >= AC.e_cost) //if there's enough power in the cell cell... + chambered = AC //...prepare a new shot based on the current ammo type selected + if(!chambered.BB) + chambered.newshot() + +/obj/item/gun/energy/process_chamber(atom/shooter) + if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... + var/obj/item/ammo_casing/energy/shot = chambered + cell.use(shot.e_cost)//... drain the cell cell + chambered = null //either way, released the prepared shot + recharge_newshot() //try to charge a new shot + SEND_SIGNAL(src, COMSIG_GUN_CHAMBER_PROCESSED) + +/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(!chambered && can_shoot()) + process_chamber() // If the gun was drained and then recharged, load a new shot. + ..() //process the gunshot as normal + if((!latch_closed && prob(65)) && (cell != null)) //make the cell slide out if it's fired while the retainment clip is unlatched, with a 65% probability + to_chat(user, span_warning("The [src]'s cell falls out!")) + eject_cell() + return + +/obj/item/gun/energy/proc/build_ammotypes() + for(var/datum/action/item_action/toggle_ammotype/old_ammotype in actions) + old_ammotype.Destroy() + var/datum/action/item_action/our_action + + if(ammo_type.len > 1) + our_action = new /datum/action/item_action/toggle_ammotype(src) + + for(var/i=1, i <= ammo_type.len, i++) + if(default_ammo_type == ammo_type[i]) + ammotype_index = i + if(our_action) + our_action.UpdateButtonIcon() + return + ammotype_index = 1 + +/obj/item/gun/energy/ui_action_click(mob/user, actiontype) + if (istype(actiontype, /datum/action/item_action/toggle_ammotype)) + select_fire(user) + update_appearance() + else + ..() + +/datum/action/item_action/toggle_ammotype/UpdateButtonIcon(status_only = FALSE, force = FALSE) + var/obj/item/gun/energy/our_gun = target + var/obj/item/ammo_casing/energy/shot = our_gun.ammo_type[our_gun.select] + var/current_ammotype = shot.select_name + + var/manufacturer_prefix = "fallback" + if (our_gun.manufacturer == MANUFACTURER_EOEHOMA) + manufacturer_prefix = "eoehoma" + else if (our_gun.manufacturer == MANUFACTURER_SHARPLITE_NEW) + manufacturer_prefix = "sharplite" + else if (our_gun.manufacturer == MANUFACTURER_PGF) + manufacturer_prefix = "etherbor" + else + current_ammotype = "fallback" + + current_ammotype = lowertext(current_ammotype) + + // A list of all ammotypes that have icons for them + if (!(current_ammotype in list("kill", "disable", "overcharge", "stun", "ion", "energy", "ar", "dmr"))) + current_ammotype = "fallback" + + button_icon_state = "[manufacturer_prefix]["_laser_"][current_ammotype]" + + return ..() + +/obj/item/gun/energy/proc/select_fire(mob/living/user) + select++ + if (select > ammo_type.len) + select = 1 + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if (shot.select_name) + to_chat(user, span_notice("[src] is now set to [shot.select_name].")) + chambered = null + playsound(user, 'sound/weapons/gun/general/selector.ogg', 100, TRUE) + recharge_newshot(TRUE) + update_appearance() + return + +/obj/item/gun/energy/update_icon_state() + if(initial(item_state)) + return ..() + var/ratio = get_charge_ratio() + var/new_item_state = "" + new_item_state = initial(icon_state) + if(modifystate) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + new_item_state += "[shot.select_name]" + new_item_state += "[ratio]" + item_state = new_item_state + return ..() + +/obj/item/gun/energy/update_overlays() + . = ..() + if(!automatic_charge_overlays || QDELETED(src)) + return + // Every time I see code this "flexible", a kitten fucking dies //it got worse + //todo: refactor this a bit to allow showing of charge on a gun's cell + var/overlay_icon_state = "[icon_state]_charge" + var/obj/item/ammo_casing/energy/shot = ammo_type[modifystate ? select : 1] + var/ratio = get_charge_ratio() + if(ismob(loc) && !internal_magazine) + var/mutable_appearance/latch_overlay + latch_overlay = mutable_appearance('icons/obj/guns/cell_latch.dmi') + if(latch_closed) + if(cell) + latch_overlay.icon_state = "latch-on-full" + else + latch_overlay.icon_state = "latch-on-empty" + else + if(cell) + latch_overlay.icon_state = "latch-off-full" + else + latch_overlay.icon_state = "latch-off-empty" + . += latch_overlay + if(cell) + . += "[icon_state]_cell" + if(ratio == 0) + . += "[icon_state]_cellempty" + if(ratio == 0) + if(modifystate) + . += "[icon_state]_[shot.select_name]" + . += "[icon_state]_empty" + else + if(!shaded_charge) + if(modifystate) + . += "[icon_state]_[shot.select_name]" + overlay_icon_state += "_[shot.select_name]" + var/mutable_appearance/charge_overlay = mutable_appearance(icon, overlay_icon_state) + for(var/i = ratio, i >= 1, i--) + charge_overlay.pixel_x = ammo_x_offset * (i - 1) + charge_overlay.pixel_y = ammo_y_offset * (i - 1) + . += new /mutable_appearance(charge_overlay) + else + if(modifystate) + . += "[icon_state]_charge[ratio]_[shot.select_name]" //:drooling_face: + else + . += "[icon_state]_charge[ratio]" + +///Used by update_icon_state() and update_overlays() +/obj/item/gun/energy/proc/get_charge_ratio() + return can_shoot(visuals = TRUE) ? CEILING(clamp(cell.charge / cell.maxcharge, 0, 1) * charge_sections, 1) : 0 + // Sets the ratio to 0 if the gun doesn't have enough charge to fire, or if its power cell is removed. + +/obj/item/gun/energy/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, selfcharge)) + if(var_value) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + . = ..() + + +/obj/item/gun/energy/ignition_effect(atom/A, mob/living/user) + if(!can_shoot() || !ammo_type[select]) + shoot_with_empty_chamber() + . = "" + else + var/obj/item/ammo_casing/energy/E = ammo_type[select] + var/obj/projectile/energy/BB = E.BB + if(!BB) + . = "" + else if(BB.nodamage || !BB.damage || BB.damage_type == STAMINA) + user.visible_message(span_danger("[user] tries to light [user.p_their()] [A.name] with [src], but it doesn't do anything. Dumbass.")) + playsound(user, E.fire_sound, 50, TRUE) + playsound(user, BB.hitsound_non_living, 50, TRUE) + cell.use(E.e_cost) + . = "" + else if(BB.damage_type != BURN) + user.visible_message(span_danger("[user] tries to light [user.p_their()] [A.name] with [src], but only succeeds in utterly destroying it. Dumbass.")) + playsound(user, E.fire_sound, 50, TRUE) + playsound(user, BB.hitsound_non_living, 50, TRUE) + cell.use(E.e_cost) + qdel(A) + . = "" + else + playsound(user, E.fire_sound, 50, TRUE) + playsound(user, BB.hitsound_non_living, 50, TRUE) + cell.use(E.e_cost) + . = span_danger("[user] casually lights their [A.name] with [src]. Damn.") + + +/obj/item/gun/energy/examine(mob/user) + . = ..() + if(!internal_magazine) + . += "The cell retainment latch is [latch_closed ? span_green("CLOSED") : span_red("OPEN")]. Press the Unique Action Key to toggle the latch. By default, this is space." + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(ammo_type.len > 1) + . += "You can switch ammo modes by pressing the Ammo Toggle button." + if(cell) + . += "\The [name]'s cell has [cell.percent()]% charge remaining." + . += "\The [name] has [round(cell.charge/shot.e_cost)] shots remaining on [shot.select_name] mode." + else + . += span_notice("\The [name] doesn't seem to have a cell!") + +/obj/item/gun/energy/unsafe_shot(target) + . = ..() + process_chamber() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/dueling.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/dueling.dm new file mode 100644 index 0000000000..b22dae410e --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/dueling.dm @@ -0,0 +1,346 @@ +#define DUEL_IDLE 1 +#define DUEL_PREPARATION 2 +#define DUEL_READY 3 +#define DUEL_COUNTDOWN 4 +#define DUEL_FIRING 5 + +//paper rock scissors +#define DUEL_SETTING_A "wide" +#define DUEL_SETTING_B "cone" +#define DUEL_SETTING_C "pinpoint" + +/datum/duel + var/obj/item/gun/energy/dueling/gun_A + var/obj/item/gun/energy/dueling/gun_B + var/state = DUEL_IDLE + var/required_distance = 5 + var/list/confirmations = list() + var/list/fired = list() + var/countdown_length = 10 + var/countdown_step = 0 + +/datum/duel/proc/try_begin() + //Check if both guns are held and if so begin. + var/mob/living/A = get_duelist(gun_A) + var/mob/living/B = get_duelist(gun_B) + if(!A || !B) + message_duelists(span_warning("To begin the duel, both participants need to be holding paired dueling pistols.")) + return + begin() + +/datum/duel/proc/begin() + state = DUEL_PREPARATION + confirmations.Cut() + fired.Cut() + countdown_step = countdown_length + + message_duelists(span_notice("Set your gun setting and move [required_distance] steps away from your opponent.")) + + START_PROCESSING(SSobj,src) + +/datum/duel/proc/get_duelist(obj/gun) + var/mob/living/G = gun.loc + if(!istype(G) || !G.is_holding(gun)) + return null + return G + +/datum/duel/proc/message_duelists(message) + var/mob/living/LA = get_duelist(gun_A) + if(LA) + to_chat(LA,message) + var/mob/living/LB = get_duelist(gun_B) + if(LB) + to_chat(LB,message) + +/datum/duel/proc/other_gun(obj/item/gun/energy/dueling/G) + return G == gun_A ? gun_B : gun_A + +/datum/duel/proc/end() + message_duelists(span_notice("Duel finished. Re-engaging safety.")) + STOP_PROCESSING(SSobj,src) + state = DUEL_IDLE + +/datum/duel/process(seconds_per_tick) + switch(state) + if(DUEL_PREPARATION) + if(check_positioning()) + confirm_positioning() + else if (!get_duelist(gun_A) && !get_duelist(gun_B)) + end() + if(DUEL_READY) + if(!check_positioning()) + back_to_prep() + else if(confirmations.len == 2) + confirm_ready() + if(DUEL_COUNTDOWN) + if(!check_positioning()) + back_to_prep() + else + countdown_step() + if(DUEL_FIRING) + if(check_fired()) + end() + + +/datum/duel/proc/back_to_prep() + message_duelists(span_notice("Positions invalid. Please move to valid positions [required_distance] steps aways from each other to continue.")) + state = DUEL_PREPARATION + confirmations.Cut() + countdown_step = countdown_length + +/datum/duel/proc/confirm_positioning() + message_duelists(span_notice("Position confirmed. Confirm readiness by pulling the trigger once.")) + state = DUEL_READY + +/datum/duel/proc/confirm_ready() + message_duelists(span_notice("Readiness confirmed. Starting countdown. Commence firing at zero mark.")) + state = DUEL_COUNTDOWN + +/datum/duel/proc/countdown_step() + countdown_step-- + if(countdown_step == 0) + state = DUEL_FIRING + message_duelists(span_userdanger("Fire!")) + else + message_duelists(span_userdanger("[countdown_step]!")) + +/datum/duel/proc/check_fired() + if(fired.len == 2) + return TRUE + //Let's say if gun was dropped/stowed the user is finished + if(!get_duelist(gun_A)) + return TRUE + if(!get_duelist(gun_B)) + return TRUE + return FALSE + +/datum/duel/proc/check_positioning() + var/mob/living/A = get_duelist(gun_A) + var/mob/living/B = get_duelist(gun_B) + if(!A || !B) + return FALSE + if(!isturf(A.loc) || !isturf(B.loc)) + return FALSE + if(get_dist(A,B) != required_distance) + return FALSE + for(var/turf/T in getline(get_turf(A),get_turf(B))) + if(T.is_blocked_turf(TRUE)) + return FALSE + return TRUE + +/obj/item/gun/energy/dueling + name = "dueling pistol" + desc = "High-tech dueling pistol. Launches chaff and projectile according to preset settings." + icon_state = "dueling_pistol" + spawn_blacklisted = TRUE + item_state = "gun" + ammo_x_offset = 2 + w_class = WEIGHT_CLASS_SMALL + ammo_type = list(/obj/item/ammo_casing/energy/duel) + automatic_charge_overlays = FALSE + var/unlocked = FALSE + var/setting = DUEL_SETTING_A + var/datum/duel/duel + var/mutable_appearance/setting_overlay + +/obj/item/gun/energy/dueling/Initialize() + . = ..() + setting_overlay = mutable_appearance(icon,setting_iconstate()) + add_overlay(setting_overlay) + +/obj/item/gun/energy/dueling/proc/setting_iconstate() + switch(setting) + if(DUEL_SETTING_A) + return "duel_red" + if(DUEL_SETTING_B) + return "duel_green" + if(DUEL_SETTING_C) + return "duel_blue" + return "duel_red" + +/obj/item/gun/energy/dueling/attack_self(mob/living/user) + . = ..() + if(duel.state == DUEL_IDLE) + duel.try_begin() + else + toggle_setting(user) + +/obj/item/gun/energy/dueling/proc/toggle_setting(mob/living/user) + switch(setting) + if(DUEL_SETTING_A) + setting = DUEL_SETTING_B + if(DUEL_SETTING_B) + setting = DUEL_SETTING_C + if(DUEL_SETTING_C) + setting = DUEL_SETTING_A + to_chat(user,span_notice("You switch [src] setting to [setting] mode.")) + update_appearance() + +/obj/item/gun/energy/dueling/update_overlays() + . = ..() + if(setting_overlay) + setting_overlay.icon_state = setting_iconstate() + . += setting_overlay + +/obj/item/gun/energy/dueling/Destroy() + . = ..() + if(duel) + if(duel.gun_A == src) + duel.gun_A = null + if(duel.gun_B == src) + duel.gun_B = null + duel = null + +/obj/item/gun/energy/dueling/can_trigger_gun(mob/living/user) + . = ..() + switch(duel.state) + if(DUEL_FIRING) + return . && !duel.fired[src] + if(DUEL_READY) + return . + else + to_chat(user,span_warning("[src] is locked. Wait for FIRE signal before shooting.")) + return FALSE + +/obj/item/gun/energy/dueling/proc/is_duelist(mob/living/L) + if(!istype(L)) + return FALSE + if(!L.is_holding(duel.other_gun(src))) + return FALSE + return TRUE + +/obj/item/gun/energy/dueling/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread) + if(duel.state == DUEL_READY) + duel.confirmations[src] = TRUE + to_chat(user,span_notice("You confirm your readiness.")) + return + else if(!is_duelist(target)) //I kinda want to leave this out just to see someone shoot a bystander or missing. + to_chat(user,span_warning("[src] safety system prevents shooting anyone but your designated opponent.")) + return + else + duel.fired[src] = TRUE + . = ..() + +/obj/item/gun/energy/dueling/before_firing(target,user) + var/obj/item/ammo_casing/energy/duel/D = chambered + D.setting = setting + +/obj/effect/temp_visual/dueling_chaff + icon = 'icons/effects/effects.dmi' + icon_state = "shield-old" + duration = 30 + var/setting + +/obj/effect/temp_visual/dueling_chaff/update_icon() + . = ..() + switch(setting) + if(DUEL_SETTING_A) + color = "red" + if(DUEL_SETTING_B) + color = "green" + if(DUEL_SETTING_C) + color = "blue" + +//Casing + +/obj/item/ammo_casing/energy/duel + e_cost = 0 + projectile_type = /obj/projectile/energy/duel + var/setting + +/obj/item/ammo_casing/energy/duel/ready_proj(atom/target, mob/living/user, quiet, zone_override) + . = ..() + var/obj/projectile/energy/duel/D = BB + D.setting = setting + D.update_appearance() + +/obj/item/ammo_casing/energy/duel/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from) + . = ..() + var/obj/effect/temp_visual/dueling_chaff/C = new(get_turf(user)) + C.setting = setting + C.update_appearance() + +//Projectile + +/obj/projectile/energy/duel + name = "dueling beam" + icon_state = "declone" + reflectable = FALSE + homing = TRUE + var/setting + +/obj/projectile/energy/duel/update_icon() + . = ..() + switch(setting) + if(DUEL_SETTING_A) + color = "red" + if(DUEL_SETTING_B) + color = "green" + if(DUEL_SETTING_C) + color = "blue" + +/obj/projectile/energy/duel/on_hit(atom/target, blocked) + . = ..() + var/turf/T = get_turf(target) + var/obj/effect/temp_visual/dueling_chaff/C = locate() in T + if(C) + var/counter_setting + switch(setting) + if(DUEL_SETTING_A) + counter_setting = DUEL_SETTING_B + if(DUEL_SETTING_B) + counter_setting = DUEL_SETTING_C + if(DUEL_SETTING_C) + counter_setting = DUEL_SETTING_A + if(C.setting == counter_setting) + return BULLET_ACT_BLOCK + + var/mob/living/L = target + if(!istype(target)) + return BULLET_ACT_BLOCK + + var/obj/item/bodypart/B = L.get_bodypart(BODY_ZONE_HEAD) + B.dismember() + qdel(B) + +//Storage case. +/obj/item/storage/lockbox/dueling + name = "dueling pistol case" + desc = "Let's solve this like gentlespacemen." + icon_state = "medalbox+l" + item_state = "syringe_kit" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + w_class = WEIGHT_CLASS_NORMAL + req_access = list(ACCESS_CAPTAIN) + icon_locked = "medalbox+l" + icon_closed = "medalbox" + icon_broken = "medalbox+b" + base_icon_state = "medalbox" + +/obj/item/storage/lockbox/dueling/ComponentInitialize() + . = ..() + var/datum/component/storage/STR = GetComponent(/datum/component/storage) + STR.max_w_class = WEIGHT_CLASS_SMALL + STR.max_items = 2 + STR.set_holdable(list(/obj/item/gun/energy/dueling)) + +/obj/item/storage/lockbox/dueling/update_icon_state() + if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)) + icon_state = icon_locked + return ..() + if(broken) + icon_state = icon_broken + return ..() + icon_state = open ? "[base_icon_state]open" : icon_closed + return ..() + +/obj/item/storage/lockbox/dueling/PopulateContents() + . = ..() + var/obj/item/gun/energy/dueling/gun_A = new(src) + var/obj/item/gun/energy/dueling/gun_B = new(src) + var/datum/duel/D = new + gun_A.duel = D + gun_B.duel = D + D.gun_A = gun_A + D.gun_B = gun_B diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/energy_gun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/energy_gun.dm new file mode 100644 index 0000000000..47832f6e3d --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/energy_gun.dm @@ -0,0 +1,60 @@ +/obj/item/gun/energy/e_gun + name = "energy rifle" + desc = "A basic hybrid energy gun with two settings: disable and kill." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "x12" + item_state = null //so the human update icon uses the icon_state instead. + ammo_type = list(/obj/item/ammo_casing/energy/disabler/sharplite, /obj/item/ammo_casing/energy/laser/sharplite) + modifystate = TRUE + ammo_x_offset = 2 + dual_wield_spread = 60 + wield_slowdown = LASER_RIFLE_SLOWDOWN + manufacturer = MANUFACTURER_SHARPLITE_NEW + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/gun/energy/e_gun/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/e_gun/turret + name = "hybrid turret gun" + desc = "A heavy hybrid energy cannon with two settings: Stun and kill. ...It doesn't seem have a trigger, seems it can only be used as a turret." + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON + icon_state = "turretlaser" + item_state = "turretlaser" + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + default_ammo_type = null + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + weapon_weight = WEAPON_HEAVY + trigger_guard = TRIGGER_GUARD_NONE + ammo_x_offset = 2 + +/obj/item/gun/energy/e_gun/turret/pre_fire(atom/target, mob/living/user, message, flag, params, zone_override, bonus_spread, dual_wielded_gun) + to_chat(user, span_notice("[src] is not designed to be fired by hand.")) + return FALSE + +/obj/item/gun/energy/e_gun/rdgun + name = "research director's PDW" + desc = "A custom energy revolver made with scientific intent, but more importantly booze. That was long ago. It's just a curiosity now." + icon_state = "rdpdw" + item_state = "gun" + ammo_x_offset = 2 + charge_sections = 6 + + wield_delay = 0.2 SECONDS + wield_slowdown = LASER_PISTOL_SLOWDOWN + + spread = 2 + spread_unwielded = 5 + + ammo_type = list(/obj/item/ammo_casing/energy/disabler/hitscan, /obj/item/ammo_casing/energy/ion/cheap) + default_ammo_type = /obj/item/stock_parts/cell/gun/mini + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/mini, + ) + + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/kinetic_accelerator.dm new file mode 100644 index 0000000000..6bf074fd22 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -0,0 +1,656 @@ +/obj/item/gun/energy/kinetic_accelerator + name = "kinetic accelerator" + desc = "A self recharging, ranged self-defense and rock pulverizing tool that does increased damage in low pressure. EXOCOM does not condone use of this weapon against other sentient life." + icon_state = "kineticgun" + item_state = "kineticgun" + ammo_type = list(/obj/item/ammo_casing/energy/kinetic) + default_ammo_type = /obj/item/stock_parts/cell/emproof + allowed_ammo_types = list( + /obj/item/stock_parts/cell/emproof, + ) + item_flags = NONE + obj_flags = UNIQUE_RENAME + weapon_weight = WEAPON_LIGHT + automatic_charge_overlays = FALSE + internal_magazine = TRUE //prevents you from giving it an OP cell - WS Edit + custom_price = 750 + w_class = WEIGHT_CLASS_BULKY + + muzzleflash_iconstate = "muzzle_flash_light" + light_color = COLOR_WHITE + + var/overheat_time = 16 + var/holds_charge = FALSE + var/unique_frequency = FALSE // modified by KA modkits + var/overheat = FALSE + var/mob/holder + + var/max_mod_capacity = 100 + var/list/modkits = list() + + var/recharge_timerid + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 24, + "y" = 13, + ) + ) + +/obj/item/gun/energy/kinetic_accelerator/shoot_with_empty_chamber(mob/living/user) + playsound(src, dry_fire_sound, 30, TRUE) //click sound but no to_chat message to cut on spam + return + +/obj/item/gun/energy/kinetic_accelerator/examine(mob/user) + . = ..() + if(max_mod_capacity) + . += "[get_remaining_mod_capacity()]% mod capacity remaining." + . += span_info("You can use a crowbar to remove modules.") + for(var/A in get_modkits()) + var/obj/item/borg/upgrade/modkit/M = A + . += span_notice("There is \a [M] installed, using [M.cost]% capacity.") + +/obj/item/gun/energy/kinetic_accelerator/crowbar_act(mob/living/user, obj/item/I) + . = TRUE + if(LAZYLEN(modkits)) + var/list/choose_options = list() + for(var/obj/item/borg/upgrade/modkit/M in modkits) + choose_options += list(M.name = image(icon = M.icon, icon_state = M.icon_state)) + var/picked_option = show_radial_menu(user, src, choose_options, radius = 38, require_near = TRUE) + if(picked_option) + to_chat(user, span_notice("You remove [picked_option].")) + I.play_tool_sound(src, 100) + for(var/obj/item/borg/upgrade/modkit/M in modkits) + if(M.name == picked_option) + M.uninstall(src) + else + to_chat(user, span_notice("There are no modifications currently installed.")) + +/obj/item/gun/energy/kinetic_accelerator/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/borg/upgrade/modkit)) + var/obj/item/borg/upgrade/modkit/MK = I + MK.install(src, user) + else + ..() + +/obj/item/gun/energy/kinetic_accelerator/proc/get_remaining_mod_capacity() + var/current_capacity_used = 0 + for(var/A in get_modkits()) + var/obj/item/borg/upgrade/modkit/M = A + current_capacity_used += M.cost + return max_mod_capacity - current_capacity_used + +/obj/item/gun/energy/kinetic_accelerator/proc/get_modkits() + . = list() + for(var/A in modkits) + . += A + +/obj/item/gun/energy/kinetic_accelerator/proc/modify_projectile(obj/projectile/kinetic/K) + K.kinetic_gun = src //do something special on-hit, easy! + for(var/A in get_modkits()) + var/obj/item/borg/upgrade/modkit/M = A + M.modify_projectile(K) + +/obj/item/gun/energy/kinetic_accelerator/cyborg + name = "chassis_mounted kinetic accelerator" + icon_state = "kineticgun_b" + bad_type = /obj/item/gun/energy/kinetic_accelerator/cyborg + holds_charge = TRUE + unique_frequency = TRUE + max_mod_capacity = 80 + +/obj/item/gun/energy/kinetic_accelerator/minebot + name = "chassis_mounted kinetic accelerator" + bad_type = /obj/item/gun/energy/kinetic_accelerator/minebot + trigger_guard = TRIGGER_GUARD_ALLOW_ALL + overheat_time = 20 + holds_charge = TRUE + unique_frequency = TRUE + +/obj/item/gun/energy/kinetic_accelerator/Initialize() + . = ..() + if(!holds_charge) + empty() + +/obj/item/gun/energy/kinetic_accelerator/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1) + . = ..() + attempt_reload() + +/obj/item/gun/energy/kinetic_accelerator/equipped(mob/user) + . = ..() + holder = user + if(!can_shoot()) + attempt_reload() + +/obj/item/gun/energy/kinetic_accelerator/dropped() + . = ..() + holder = null + if(!QDELING(src) && !holds_charge) + // Put it on a delay because moving item from slot to hand + // calls dropped(). + addtimer(CALLBACK(src, PROC_REF(empty_if_not_held)), 2) + +/obj/item/gun/energy/kinetic_accelerator/proc/empty_if_not_held() + if(!ismob(loc)) + empty() + +/obj/item/gun/energy/kinetic_accelerator/proc/empty() + if(cell) + cell.use(cell.charge) + update_appearance() + +/obj/item/gun/energy/kinetic_accelerator/proc/attempt_reload(recharge_time) + if(!cell) + return + if(overheat) + return + if(!recharge_time) + recharge_time = overheat_time + overheat = TRUE + + var/carried = 0 + if(!unique_frequency) + for(var/obj/item/gun/energy/kinetic_accelerator/K in loc.GetAllContents()) + if(!K.unique_frequency) + carried++ + + carried = max(carried, 1) + else + carried = 1 + + deltimer(recharge_timerid) + recharge_timerid = addtimer(CALLBACK(src, PROC_REF(reload)), recharge_time * carried, TIMER_STOPPABLE) + +/obj/item/gun/energy/kinetic_accelerator/emp_act(severity) + return + +/obj/item/gun/energy/kinetic_accelerator/proc/reload() + cell.give(cell.maxcharge) + if(!suppressed) + playsound(src.loc, 'sound/weapons/kenetic_reload.ogg', 60, TRUE) + else + to_chat(loc, span_warning("[src] silently charges up.")) + update_appearance() + overheat = FALSE + +/obj/item/gun/energy/kinetic_accelerator/update_overlays() + . = ..() + if(!can_shoot()) + . += "[icon_state]_empty" + +//Casing +/obj/item/ammo_casing/energy/kinetic + projectile_type = /obj/projectile/kinetic + select_name = "kinetic" + e_cost = 500 + fire_sound = 'sound/weapons/kenetic_accel.ogg' // fine spelling there chap + +/obj/item/ammo_casing/energy/kinetic/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") + ..() + if(loc && istype(loc, /obj/item/gun/energy/kinetic_accelerator)) + var/obj/item/gun/energy/kinetic_accelerator/KA = loc + KA.modify_projectile(BB) + +//Projectiles +/obj/projectile/kinetic + name = "kinetic force" + icon_state = null + damage = 20 + damage_type = BRUTE + wall_damage_flags = PROJECTILE_BONUS_DAMAGE_MINERALS + wall_damage_override = MINERAL_WALL_INTEGRITY + flag = "bomb" + range = 3 + log_override = TRUE + + var/pressure_decrease_active = FALSE + var/pressure_decrease = 0.25 + var/obj/item/gun/energy/kinetic_accelerator/kinetic_gun + +/obj/projectile/kinetic/Destroy() + kinetic_gun = null + return ..() + +/obj/projectile/kinetic/prehit_pierce(atom/target) + . = ..() + if(.) + if(kinetic_gun) + var/list/mods = kinetic_gun.get_modkits() + for(var/obj/item/borg/upgrade/modkit/M in mods) + M.projectile_prehit(src, target, kinetic_gun) + if(!pressure_decrease_active && !lavaland_equipment_pressure_check(get_turf(target))) + name = "weakened [name]" + damage = damage * pressure_decrease + pressure_decrease_active = TRUE + +/obj/projectile/kinetic/on_range() + strike_thing() + ..() + +/obj/projectile/kinetic/on_hit(atom/target) + strike_thing(target) + . = ..() + +/obj/projectile/kinetic/proc/strike_thing(atom/target) + var/turf/target_turf = get_turf(target) + if(!target_turf) + target_turf = get_turf(src) + if(kinetic_gun) //hopefully whoever shot this was not very, very unfortunate. + var/list/mods = kinetic_gun.get_modkits() + for(var/obj/item/borg/upgrade/modkit/M in mods) + M.projectile_strike_predamage(src, target_turf, target, kinetic_gun) + for(var/obj/item/borg/upgrade/modkit/M in mods) + M.projectile_strike(src, target_turf, target, kinetic_gun) + if(ismineralturf(target_turf)) + if(iscarbon(firer)) + var/mob/living/carbon/C = firer + var/skill_modifier = C?.mind.get_skill_modifier(/datum/skill/mining, SKILL_SPEED_MODIFIER) + kinetic_gun.attempt_reload(kinetic_gun.overheat_time * skill_modifier) //If you hit a mineral, you might get a quicker reload. epic gamer style. + var/obj/effect/temp_visual/kinetic_blast/K = new /obj/effect/temp_visual/kinetic_blast(target_turf) + K.color = color + +//Mecha version of the KA projectile + +/obj/projectile/kinetic/mech + range = 5 + pressure_decrease = 0.5 + +/obj/projectile/kinetic/mech/strike_thing(atom/target) //has no skill check for mechs + var/turf/target_turf = get_turf(target) + if(!target_turf) + target_turf = get_turf(src) + if(kinetic_gun) + var/list/mods = kinetic_gun.get_modkits() + for(var/obj/item/borg/upgrade/modkit/M in mods) + M.projectile_strike_predamage(src, target_turf, target, kinetic_gun) + for(var/obj/item/borg/upgrade/modkit/M in mods) + M.projectile_strike(src, target_turf, target, kinetic_gun) + if(ismineralturf(target_turf)) + var/turf/closed/mineral/M = target_turf + M.gets_drilled(firer, TRUE) + var/obj/effect/temp_visual/kinetic_blast/K = new /obj/effect/temp_visual/kinetic_blast(target_turf) + K.color = color + +//Modkits +/obj/item/borg/upgrade/modkit + name = "kinetic accelerator modification kit" + desc = "An upgrade for kinetic accelerators." + icon = 'icons/obj/objects.dmi' + icon_state = "modkit" + w_class = WEIGHT_CLASS_SMALL + require_module = 1 + module_type = list(/obj/item/robot_module/miner) + var/denied_type = null + var/maximum_of_type = 1 + var/cost = 30 + var/modifier = 1 //For use in any mod kit that has numerical modifiers + var/minebot_upgrade = TRUE + var/minebot_exclusive = FALSE + +/obj/item/borg/upgrade/modkit/examine(mob/user) + . = ..() + . += span_notice("Occupies [cost]% of mod capacity.") + +/obj/item/borg/upgrade/modkit/attackby(obj/item/A, mob/user) + if(istype(A, /obj/item/gun/energy/kinetic_accelerator) && !issilicon(user)) + install(A, user) + else + ..() + +/obj/item/borg/upgrade/modkit/action(mob/living/silicon/robot/R) + . = ..() + if (.) + for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/H in R.module.modules) + return install(H, usr) + +/obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user) + . = TRUE + if(minebot_upgrade) + if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone)) + to_chat(user, span_notice("The modkit you're trying to install is only rated for minebot use.")) + return FALSE + else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone)) + to_chat(user, span_notice("The modkit you're trying to install is not rated for minebot use.")) + return FALSE + if(denied_type) + var/number_of_denied = 0 + for(var/A in KA.get_modkits()) + var/obj/item/borg/upgrade/modkit/M = A + if(istype(M, denied_type)) + number_of_denied++ + if(number_of_denied >= maximum_of_type) + . = FALSE + break + if(KA.get_remaining_mod_capacity() >= cost) + if(.) + if(!user.transferItemToLoc(src, KA)) + return + to_chat(user, span_notice("You install the modkit.")) + playsound(loc, 'sound/items/screwdriver.ogg', 100, TRUE) + KA.modkits += src + else + to_chat(user, span_notice("The modkit you're trying to install would conflict with an already installed modkit. Use a crowbar to remove existing modkits.")) + else + to_chat(user, span_notice("You don't have room([KA.get_remaining_mod_capacity()]% remaining, [cost]% needed) to install this modkit. Use a crowbar to remove existing modkits.")) + . = FALSE + +/obj/item/borg/upgrade/modkit/deactivate(mob/living/silicon/robot/R, user = usr) + . = ..() + if (.) + for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA in R.module.modules) + uninstall(KA) + +/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA) + forceMove(get_turf(KA)) + KA.modkits -= src + + + +/obj/item/borg/upgrade/modkit/proc/modify_projectile(obj/projectile/kinetic/K) + +//use this one for effects you want to trigger before any damage is done at all and before damage is decreased by pressure +/obj/item/borg/upgrade/modkit/proc/projectile_prehit(obj/projectile/kinetic/K, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) +//use this one for effects you want to trigger before mods that do damage +/obj/item/borg/upgrade/modkit/proc/projectile_strike_predamage(obj/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) +//and this one for things that don't need to trigger before other damage-dealing mods +/obj/item/borg/upgrade/modkit/proc/projectile_strike(obj/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + +//Range +/obj/item/borg/upgrade/modkit/range + name = "range increase" + desc = "Increases the range of a kinetic accelerator when installed." + modifier = 1 + cost = 25 + custom_price = 1000 + +/obj/item/borg/upgrade/modkit/range/modify_projectile(obj/projectile/kinetic/K) + K.range += modifier + + +//Damage +/obj/item/borg/upgrade/modkit/damage + name = "damage increase" + desc = "Increases the damage of kinetic accelerator when installed." + modifier = 10 + custom_price = 1000 + +/obj/item/borg/upgrade/modkit/damage/modify_projectile(obj/projectile/kinetic/K) + K.damage += modifier + + +//Cooldown +/obj/item/borg/upgrade/modkit/cooldown + name = "cooldown decrease" + desc = "Decreases the cooldown of a kinetic accelerator. Not rated for minebot use." + modifier = 3.2 + minebot_upgrade = FALSE + custom_price = 1000 + +/obj/item/borg/upgrade/modkit/cooldown/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user) + . = ..() + if(.) + KA.overheat_time -= modifier + +/obj/item/borg/upgrade/modkit/cooldown/uninstall(obj/item/gun/energy/kinetic_accelerator/KA) + KA.overheat_time += modifier + ..() + +/obj/item/borg/upgrade/modkit/cooldown/minebot + name = "minebot cooldown decrease" + desc = "Decreases the cooldown of a kinetic accelerator. Only rated for minebot use." + icon_state = "door_electronics" + icon = 'icons/obj/module.dmi' + denied_type = /obj/item/borg/upgrade/modkit/cooldown/minebot + modifier = 10 + cost = 0 + minebot_upgrade = TRUE + minebot_exclusive = TRUE + + +//AoE blasts +/obj/item/borg/upgrade/modkit/aoe + modifier = 0 + var/turf_aoe = FALSE + var/stats_stolen = FALSE + custom_price = 2000 + +/obj/item/borg/upgrade/modkit/aoe/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user) + . = ..() + if(.) + for(var/obj/item/borg/upgrade/modkit/aoe/AOE in KA.modkits) //make sure only one of the aoe modules has values if somebody has multiple + if(AOE.stats_stolen || AOE == src) + continue + modifier += AOE.modifier //take its modifiers + AOE.modifier = 0 + turf_aoe += AOE.turf_aoe + AOE.turf_aoe = FALSE + AOE.stats_stolen = TRUE + +/obj/item/borg/upgrade/modkit/aoe/uninstall(obj/item/gun/energy/kinetic_accelerator/KA) + ..() + modifier = initial(modifier) //get our modifiers back + turf_aoe = initial(turf_aoe) + stats_stolen = FALSE + +/obj/item/borg/upgrade/modkit/aoe/modify_projectile(obj/projectile/kinetic/K) + K.name = "kinetic explosion" + +/obj/item/borg/upgrade/modkit/aoe/projectile_strike(obj/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + if(stats_stolen) + return + new /obj/effect/temp_visual/explosion/fast(target_turf) + if(turf_aoe) + for(var/T in RANGE_TURFS(1, target_turf) - target_turf) + if(ismineralturf(T)) + var/turf/closed/mineral/M = T + M.gets_drilled(K.firer, TRUE) + if(modifier) + for(var/mob/living/L in range(1, target_turf) - K.firer - target) + var/armor = L.run_armor_check(K.def_zone, K.flag, K.armour_penetration, silent = TRUE) + L.apply_damage(K.damage*modifier, K.damage_type, K.def_zone, armor) + to_chat(L, span_userdanger("You're struck by a [K.name]!")) + +/obj/item/borg/upgrade/modkit/aoe/turfs + name = "mining explosion" + desc = "Causes the kinetic accelerator to destroy rock in an AoE." + denied_type = /obj/item/borg/upgrade/modkit/aoe/turfs + turf_aoe = TRUE + +/obj/item/borg/upgrade/modkit/aoe/turfs/andmobs + name = "offensive mining explosion" + desc = "Causes the kinetic accelerator to destroy rock and damage mobs in an AoE." + maximum_of_type = 3 + modifier = 0.25 + +/obj/item/borg/upgrade/modkit/aoe/mobs + name = "offensive explosion" + desc = "Causes the kinetic accelerator to damage mobs in an AoE." + modifier = 0.2 + +//Minebot passthrough +/obj/item/borg/upgrade/modkit/minebot_passthrough + name = "minebot passthrough" + desc = "Causes kinetic accelerator shots to pass through minebots." + cost = 0 + +//Tendril-unique modules +/obj/item/borg/upgrade/modkit/cooldown/repeater + name = "rapid repeater" + desc = "Quarters the kinetic accelerator's cooldown on striking a living target, but greatly increases the base cooldown." + denied_type = /obj/item/borg/upgrade/modkit/cooldown/repeater + modifier = -14 //Makes the cooldown 3 seconds(with no cooldown mods) if you miss. Don't miss. + cost = 50 + +/obj/item/borg/upgrade/modkit/cooldown/repeater/projectile_strike_predamage(obj/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + var/valid_repeat = FALSE + if(isliving(target)) + var/mob/living/L = target + if(L.stat != DEAD) + valid_repeat = TRUE + if(ismineralturf(target_turf)) + valid_repeat = TRUE + if(valid_repeat) + KA.overheat = FALSE + KA.attempt_reload(KA.overheat_time * 0.25) //If you hit, the cooldown drops to 0.75 seconds. + +/obj/item/borg/upgrade/modkit/lifesteal + name = "lifesteal crystal" + desc = "Causes kinetic accelerator shots to slightly heal the firer on striking a living target." + icon_state = "modkit_crystal" + modifier = 2.5 //Not a very effective method of healing. + cost = 20 + var/static/list/damage_heal_order = list(BRUTE, BURN, OXY) + +/obj/item/borg/upgrade/modkit/lifesteal/projectile_prehit(obj/projectile/kinetic/K, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + if(isliving(target) && isliving(K.firer)) + var/mob/living/L = target + if(L.stat == DEAD) + return + L = K.firer + L.heal_ordered_damage(modifier, damage_heal_order) + +/obj/item/borg/upgrade/modkit/resonator_blasts + name = "resonator blast" + desc = "Causes kinetic accelerator shots to leave and detonate resonator blasts." + denied_type = /obj/item/borg/upgrade/modkit/resonator_blasts + cost = 30 + modifier = 0.25 //A bonus 15 damage if you burst the field on a target, 60 if you lure them into it. + +/obj/item/borg/upgrade/modkit/resonator_blasts/projectile_strike(obj/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + if(target_turf && !ismineralturf(target_turf)) //Don't make fields on mineral turfs. + var/obj/effect/temp_visual/resonance/R = locate(/obj/effect/temp_visual/resonance) in target_turf + if(R) + R.damage_multiplier = modifier + R.burst() + return + new /obj/effect/temp_visual/resonance(target_turf, K.firer, null, 30) + +/obj/item/borg/upgrade/modkit/bounty + name = "death syphon" + desc = "Killing or assisting in killing a creature permanently increases your damage against that type of creature." + denied_type = /obj/item/borg/upgrade/modkit/bounty + modifier = 1.25 + cost = 30 + var/maximum_bounty = 25 + var/list/bounties_reaped = list() + +/obj/item/borg/upgrade/modkit/bounty/projectile_prehit(obj/projectile/kinetic/K, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + if(isliving(target)) + var/mob/living/L = target + var/list/existing_marks = L.has_status_effect_list(STATUS_EFFECT_SYPHONMARK) + for(var/i in existing_marks) + var/datum/status_effect/syphon_mark/SM = i + if(SM.reward_target == src) //we want to allow multiple people with bounty modkits to use them, but we need to replace our own marks so we don't multi-reward + SM.reward_target = null + qdel(SM) + L.apply_status_effect(STATUS_EFFECT_SYPHONMARK, src) + +/obj/item/borg/upgrade/modkit/bounty/projectile_strike(obj/projectile/kinetic/K, turf/target_turf, atom/target, obj/item/gun/energy/kinetic_accelerator/KA) + if(isliving(target)) + var/mob/living/L = target + if(bounties_reaped[L.type]) + var/kill_modifier = 1 + if(K.pressure_decrease_active) + kill_modifier *= K.pressure_decrease + var/armor = L.run_armor_check(K.def_zone, K.flag, K.armour_penetration, silent = TRUE) + L.apply_damage(bounties_reaped[L.type]*kill_modifier, K.damage_type, K.def_zone, armor) + +/obj/item/borg/upgrade/modkit/bounty/proc/get_kill(mob/living/L) + var/bonus_mod = 1 + if(ismegafauna(L)) //megafauna reward + bonus_mod = 4 + if(!bounties_reaped[L.type]) + bounties_reaped[L.type] = min(modifier * bonus_mod, maximum_bounty) + else + bounties_reaped[L.type] = min(bounties_reaped[L.type] + (modifier * bonus_mod), maximum_bounty) + +//Indoors +/obj/item/borg/upgrade/modkit/indoors + name = "decrease pressure penalty" + desc = "A syndicate modification kit that increases the damage a kinetic accelerator does in high pressure environments." + modifier = 2 + denied_type = /obj/item/borg/upgrade/modkit/indoors + maximum_of_type = 2 + cost = 35 + +/obj/item/borg/upgrade/modkit/indoors/modify_projectile(obj/projectile/kinetic/K) + K.pressure_decrease *= modifier + + +//Trigger Guard +/obj/item/borg/upgrade/modkit/trigger_guard + name = "modified trigger guard" + desc = "Allows creatures normally incapable of firing guns to operate the weapon when installed." + cost = 20 + denied_type = /obj/item/borg/upgrade/modkit/trigger_guard + custom_price = 1700 + +/obj/item/borg/upgrade/modkit/trigger_guard/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user) + . = ..() + if(.) + KA.trigger_guard = TRIGGER_GUARD_ALLOW_ALL + +/obj/item/borg/upgrade/modkit/trigger_guard/uninstall(obj/item/gun/energy/kinetic_accelerator/KA) + KA.trigger_guard = TRIGGER_GUARD_NORMAL + ..() + + +//Cosmetic + +/obj/item/borg/upgrade/modkit/chassis_mod + name = "super chassis" + desc = "Makes your KA yellow. All the fun of having a more powerful KA without actually having a more powerful KA." + cost = 0 + denied_type = /obj/item/borg/upgrade/modkit/chassis_mod + custom_price = 200 + var/chassis_icon = "kineticgun_u" + var/chassis_name = "super-kinetic accelerator" + +/obj/item/borg/upgrade/modkit/chassis_mod/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user) + . = ..() + if(.) + KA.icon_state = chassis_icon + KA.name = chassis_name + KA.item_state = chassis_icon + if(iscarbon(KA.loc)) + var/mob/living/carbon/holder = KA.loc + holder.update_inv_hands() + +/obj/item/borg/upgrade/modkit/chassis_mod/uninstall(obj/item/gun/energy/kinetic_accelerator/KA) + KA.icon_state = initial(KA.icon_state) + KA.item_state = initial(KA.item_state) + KA.name = initial(KA.name) + ..() + +/obj/item/borg/upgrade/modkit/chassis_mod/orange + name = "hyper chassis" + desc = "Makes your KA orange. All the fun of having explosive blasts without actually having explosive blasts." + chassis_icon = "kineticgun_h" + chassis_name = "hyper-kinetic accelerator" + custom_premium_price = 300 + +/obj/item/borg/upgrade/modkit/tracer + name = "white tracer bolts" + desc = "Causes kinetic accelerator bolts to have a white tracer trail and explosion." + cost = 0 + denied_type = /obj/item/borg/upgrade/modkit/tracer + var/bolt_color = "#FFFFFF" + +/obj/item/borg/upgrade/modkit/tracer/modify_projectile(obj/projectile/kinetic/K) + K.icon_state = "ka_tracer" + K.color = bolt_color + +/obj/item/borg/upgrade/modkit/tracer/adjustable + name = "adjustable tracer bolts" + desc = "Causes kinetic accelerator bolts to have an adjustable-colored tracer trail and explosion. Use in-hand to change color." + custom_price = 150 + +/obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user) + bolt_color = input(user,"","Choose Color",bolt_color) as color|null + +/obj/item/gun/energy/kinetic_accelerator/old + name = "proto-kinetic accelerator" + desc = "A self-recharging concussive blast mining tool, heavily used by Nanotrasen Mining Corps both for extracting minerals and dealing with unruly locals. NT's prototype line was produced with top-of-the-line cooling mechanisms. " + icon_state = "kineticgunold" + item_state = "kineticgunold" + overheat_time = 10 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser.dm new file mode 100644 index 0000000000..d0b967c67c --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser.dm @@ -0,0 +1,206 @@ +/obj/item/gun/energy/laser + name = "E-20 mining emitter" + desc = "A basic mining tool that fires concentrated bolts of light, which easily cause flesh, stone, and metal to yield." + //desc = "A basic mining laser that fires concentrated beams of light which break down rock. Notably, these beams of light melt down flesh, and the design, while literally ancient, is well known and suprisngly modular, leading to many modifications and upgrades over the years." + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + item_state = "laser" + w_class = WEIGHT_CLASS_BULKY + custom_materials = list(/datum/material/iron=2000) + ammo_type = list(/obj/item/ammo_casing/energy/laser/eoehoma) + ammo_x_offset = 1 + shaded_charge = TRUE + supports_variations = VOX_VARIATION + manufacturer = MANUFACTURER_EOEHOMA + + spread = 0 + spread_unwielded = 10 + +/obj/item/gun/energy/laser/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/laser/practice + name = "practice laser gun" + desc = "A modified version of the L104 laser gun, this one fires less concentrated energy bolts designed for target practice." + ammo_type = list(/obj/item/ammo_casing/energy/laser/practice/sharplite) + item_flags = NONE + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + icon_state = "laser" + item_state = "laser" + + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/laser/retro + name ="SL L104" + desc = "An antiquated model of the basic lasergun, no longer used or sold by Sharplite. Nevertheless, the sheer popularity of this model makes it a somewhat common sight to this day." + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + icon_state = "laser" + item_state = "laser" + + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/laser/captain + name = "antique laser gun" + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "caplaser" + item_state = "caplaser" + desc = "This is the SL X-00, an antique laser gun, out of production for decades and well beyond anyone's capacity to recreate. All craftsmanship is of the highest quality. It is decorated with ashdrake leather and chrome. The gun menaces with spikes of energy. On the item is an image of a space station. The station is exploding." + force = 10 + ammo_x_offset = 3 + selfcharge = TRUE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/laser/captain/brazil + icon_state = "capgun_brazil" + item_state = "caplaser" + desc = "This is the SL X-00, an antique laser gun, out of production for decades and well beyond anyone's capacity to recreate. It seems all the high quality materials it was once made of are now scratched up and torn. The nuclear power cell has been removed, and the gun will no longer automatically recharge." + selfcharge = FALSE + +/obj/item/gun/energy/laser/captain/scattershot + name = "scatter shot laser rifle" + desc = "An industrial-grade heavy-duty laser rifle with a modified laser lens to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theoretically infinite use." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser/slug) + shaded_charge = FALSE + +/obj/item/gun/energy/laser/cyborg + can_charge = FALSE + desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" + use_cyborg_cell = TRUE + manufacturer = MANUFACTURER_NONE + +/obj/item/gun/energy/laser/cyborg/emp_act() + return + +/obj/item/gun/energy/laser/scatter + name = "scatter laser gun" + desc = "A laser gun mimicking the function of a shotgun, manufactured with an adjustable lens capable of projecting scattershot or slugs." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser/slug) + manufacturer = MANUFACTURER_NONE + +/obj/item/gun/energy/laser/scatter/shotty + name = "energy shotgun" + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "cshotgun" + item_state = "shotgun" + desc = "A combat shotgun gutted and refitted with an internal laser system. Can switch between taser and scattered disabler shots." + shaded_charge = FALSE + ammo_type = list(/obj/item/ammo_casing/energy/disabler/scatter, /obj/item/ammo_casing/energy/electrode) + manufacturer = MANUFACTURER_NONE + +///Laser Cannon + +/obj/item/gun/energy/lasercannon + name = "accelerator laser cannon" + desc = "An advanced laser cannon that does more damage the farther away the target is." + icon_state = "lasercannon" + item_state = "lasercannon" + w_class = WEIGHT_CLASS_BULKY + default_ammo_type = /obj/item/stock_parts/cell/gun/large + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/large, + ) + force = 10 + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) + ammo_x_offset = 3 + manufacturer = MANUFACTURER_NONE + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + +/obj/item/ammo_casing/energy/laser/accelerator + projectile_type = /obj/projectile/beam/laser/accelerator + select_name = "accelerator" + fire_sound = 'sound/weapons/lasercannonfire.ogg' + e_cost = 5000 + +/obj/projectile/beam/laser/accelerator + name = "accelerator laser" + icon_state = "scatterlaser" + range = 255 + damage = 6 + +/obj/projectile/beam/laser/accelerator/Range() + ..() + damage += 7 + transform *= 1 + ((damage/7) * 0.2)//20% larger per tile + +/obj/item/gun/energy/xray + name = "\improper X-ray laser gun" + desc = "A high-power laser gun capable of expelling concentrated X-ray blasts that pass through multiple soft targets and heavier materials." + icon_state = "xray" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/xray) + ammo_x_offset = 3 + w_class = WEIGHT_CLASS_BULKY + +////////Laser Tag//////////////////// + +/obj/item/gun/energy/laser/bluetag + name = "laser tag gun" + icon_state = "bluetag" + desc = "A retro laser gun modified to fire harmless blue beams of light. Sound effects included!" + ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag) + item_flags = NONE + ammo_x_offset = 2 + selfcharge = TRUE + manufacturer = MANUFACTURER_NONE + +/obj/item/gun/energy/laser/bluetag/hitscan + ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag/hitscan) + +/obj/item/gun/energy/laser/redtag + name = "laser tag gun" + icon_state = "redtag" + desc = "A retro laser gun modified to fire harmless beams red of light. Sound effects included!" + ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag) + item_flags = NONE + ammo_x_offset = 2 + selfcharge = TRUE + manufacturer = MANUFACTURER_NONE + +/obj/item/gun/energy/laser/redtag/hitscan + ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag/hitscan) + +/obj/item/gun/energy/laser/hitscanpistol + name = "experimental laser gun" + desc = "A highly experimental laser gun, with unknown inner workings. It has no markings besides a \"GROUP A\" inscription on the barrel." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "hitscangun" + item_state = "gun" + ammo_x_offset = 2 + charge_sections = 4 + default_ammo_type = /obj/item/stock_parts/cell/gun/mini + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/mini, + ) + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/hitscan) + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/laser/hitscanpistol/examine_more(mob/user) + . = ..() + if(in_range(src, user) || isobserver(user)) + . += span_notice("You examine [src] closer. Under the grip is a small inscription: \"NT CN SVALINN 462\".") + else + . += span_warning("You try to examine [src] closer, but you're too far away.") diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser_gatling.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser_gatling.dm new file mode 100644 index 0000000000..db9a97c9f2 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/laser_gatling.dm @@ -0,0 +1,177 @@ + + +//The ammo/gun is stored in a back slot item +/obj/item/minigunpack + name = "backpack power source" + desc = "The massive external power source for the laser gatling gun." + icon = 'icons/obj/guns/minigun.dmi' + icon_state = "holstered" + item_state = "backpack" + lefthand_file = 'icons/mob/inhands/equipment/backpack_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/backpack_righthand.dmi' + slot_flags = ITEM_SLOT_BACK + w_class = WEIGHT_CLASS_HUGE + var/obj/item/gun/energy/minigun/gun + var/obj/item/stock_parts/cell/minigun/battery + var/armed = 0 //whether the gun is attached, 0 is attached, 1 is the gun is wielded. + var/overheat = 0 + var/overheat_max = 40 + var/heat_diffusion = 1 + var/spawn_with_gun = TRUE + +/obj/item/minigunpack/Initialize() + . = ..() + battery = new(src) + if(spawn_with_gun) + gun = new(src) + gun.cell = battery + START_PROCESSING(SSobj, src) + +/obj/item/minigunpack/Destroy() + if(!QDELETED(gun)) + qdel(gun) + gun = null + QDEL_NULL(battery) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/minigunpack/process(seconds_per_tick) + overheat = max(0, overheat - heat_diffusion) + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/minigunpack/attack_hand(mob/living/carbon/user) + if(src.loc == user) + if(!armed) + if(user.get_item_by_slot(ITEM_SLOT_BACK) == src) + armed = 1 + if(!user.put_in_hands(gun)) + armed = 0 + to_chat(user, span_warning("You need a free hand to hold the gun!")) + return + update_appearance() + user.update_inv_back() + else + to_chat(user, span_warning("You are already holding the gun!")) + else + ..() + +/obj/item/minigunpack/attackby(obj/item/W, mob/user, params) + if(W == gun) //Don't need armed check, because if you have the gun assume its armed. + user.dropItemToGround(gun, TRUE) + else + ..() + +/obj/item/minigunpack/dropped(mob/user) + . = ..() + if(armed) + user.dropItemToGround(gun, TRUE) + +/obj/item/minigunpack/MouseDrop(atom/over_object) + . = ..() + if(armed) + return + if(iscarbon(usr)) + var/mob/M = usr + + if(!over_object) + return + + if(!M.incapacitated()) + + if(istype(over_object, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over_object + M.putItemFromInventoryInHandIfPossible(src, H.held_index) + + +/obj/item/minigunpack/update_icon_state() + icon_state = "[(armed ? "not" : "")]holstered" + return ..() + +/obj/item/minigunpack/proc/attach_gun(mob/user) + if(!gun) + gun = new(src) + gun.forceMove(src) + armed = 0 + if(user) + to_chat(user, span_notice("You attach the [gun.name] to the [name].")) + else + src.visible_message(span_warning("The [gun.name] snaps back onto the [name]!")) + update_appearance() + user.update_inv_back() + +/obj/item/minigunpack/no_gun + spawn_with_gun = FALSE + +/obj/item/gun/energy/minigun + name = "laser gatling gun" + desc = "An advanced laser cannon with an incredible rate of fire. Requires a bulky backpack power source to use." + icon = 'icons/obj/guns/minigun.dmi' + icon_state = "minigun_spin" + item_state = "minigun" + slowdown = 1 + + fire_delay = 0.1 SECONDS + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + custom_materials = null + weapon_weight = WEAPON_MEDIUM + ammo_type = list(/obj/item/ammo_casing/energy/laser/minigun) + default_ammo_type = /obj/item/stock_parts/cell/crap + allowed_ammo_types = list( + /obj/item/stock_parts/cell/crap, + ) + item_flags = NEEDS_PERMIT | SLOWS_WHILE_IN_HAND + can_charge = FALSE + var/obj/item/minigunpack/ammo_pack + +/obj/item/gun/energy/minigun/Initialize() + if(!istype(loc, /obj/item/minigunpack)) //We should spawn inside an ammo pack so let's use that one. + return INITIALIZE_HINT_QDEL //No pack, no gun + + ammo_pack = loc + AddElement(/datum/element/update_icon_blocker) + return ..() + +/obj/item/gun/energy/minigun/Destroy() + if(!QDELETED(ammo_pack)) + qdel(ammo_pack) + ammo_pack = null + return ..() + +/obj/item/gun/energy/minigun/attack_self(mob/living/user) + return + +/obj/item/gun/energy/minigun/dropped(mob/user) + SHOULD_CALL_PARENT(0) + if(ammo_pack) + ammo_pack.attach_gun(user) + else + qdel(src) + +/obj/item/gun/energy/minigun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(ammo_pack && ammo_pack.overheat >= ammo_pack.overheat_max) + to_chat(user, span_warning("The gun's heat sensor locked the trigger to prevent lens damage!")) + return + ..() + ammo_pack.overheat += burst_size + if(ammo_pack.battery) + var/totransfer = min(100, ammo_pack.battery.charge) + var/transferred = cell.give(totransfer) + ammo_pack.battery.use(transferred) + +/obj/item/gun/energy/minigun/afterattack(atom/target, mob/living/user, flag, params) + if(!ammo_pack || ammo_pack.loc != user) + to_chat(user, span_warning("You need the backpack power source to fire the gun!")) + . = ..() + + +/obj/item/stock_parts/cell/minigun + name = "gatling gun fusion core" + desc = "Where did these come from?" + icon_state = "h+cell" + maxcharge = 500000 + chargerate = 5000 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/mounted.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/mounted.dm new file mode 100644 index 0000000000..d32c5f13b1 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/mounted.dm @@ -0,0 +1,27 @@ +/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" + lefthand_file = GUN_LEFTHAND_ICON + righthand_file = GUN_RIGHTHAND_ICON + item_state = "armcannonstun4" + force = 5 + selfcharge = 1 + 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_cyborg" + item_state = "armcannonlase" + force = 5 + selfcharge = 1 + trigger_guard = TRIGGER_GUARD_ALLOW_ALL + +/obj/item/gun/energy/laser/mounted/dropped() + ..() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/pulse.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/pulse.dm new file mode 100644 index 0000000000..727a99b41f --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/pulse.dm @@ -0,0 +1,95 @@ +/obj/item/gun/energy/pulse + name = "pulse rifle" + desc = "A top-of-the-line, heavy-duty, multifaceted energy rifle with three firing modes. The gold standard for Nanotrasen's heavier security specialists." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "pulse" + item_state = null + w_class = WEIGHT_CLASS_BULKY + force = 10 + modifystate = TRUE + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/laser) + internal_magazine = TRUE //prevents you from giving it an OP cell - WS Edit + default_ammo_type = /obj/item/stock_parts/cell/pulse + allowed_ammo_types = list( + /obj/item/stock_parts/cell/pulse, + ) //somone make this backpack mounted, or connected to the deathsquad suit at some point + manufacturer = MANUFACTURER_SHARPLITE_NEW + ammo_x_offset = 2 + charge_sections = 6 + + spread_unwielded = 25 + + muzzleflash_iconstate = "muzzle_flash_pulse" + light_color = COLOR_BRIGHT_BLUE + +/obj/item/gun/energy/pulse/emp_act(severity) + return + +/obj/item/gun/energy/pulse/carbine + name = "pulse carbine" + desc = "A next-generation pulse weapon for Nanotrasen's security forces. High production costs and logistical issues have limited its deployment to specialist Loss Prevention and Emergency Response units." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + icon_state = "pulse_carbine" + item_state = null + internal_magazine = FALSE + default_ammo_type = /obj/item/stock_parts/cell/gun/large + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/large, + ) + ammo_x_offset = 2 + charge_sections = 4 + +/obj/item/gun/energy/pulse/prize/Initialize() + . = ..() + SSpoints_of_interest.make_point_of_interest(src) + var/turf/T = get_turf(src) + + message_admins("A pulse rifle prize has been created at [ADMIN_VERBOSEJMP(T)]") + log_game("A pulse rifle prize has been created at [AREACOORD(T)]") + + notify_ghosts("Someone won a pulse rifle as a prize!", source = src, action = NOTIFY_ORBIT, header = "Pulse rifle prize") + +/obj/item/gun/energy/pulse/prize/Destroy() + SSpoints_of_interest.remove_point_of_interest(src) + . = ..() + +/obj/item/gun/energy/pulse/pistol + name = "pulse pistol" + desc = "A pulse rifle in an easily concealed handgun package with low capacity." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + w_class = WEIGHT_CLASS_SMALL + slot_flags = ITEM_SLOT_BELT + icon_state = "pulse_pistol" + item_state = "gun" + default_ammo_type = /obj/item/stock_parts/cell/pulse/pistol + allowed_ammo_types = list( + /obj/item/stock_parts/cell/pulse/pistol, + ) + ammo_x_offset = 2 + charge_sections = 4 + +/obj/item/gun/energy/pulse/destroyer + name = "pulse destroyer" + desc = "A heavy-duty energy rifle built for pure destruction." + spawn_blacklisted = TRUE + default_ammo_type = /obj/item/stock_parts/cell/infinite + allowed_ammo_types = list( + /obj/item/stock_parts/cell/infinite, + ) + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) + +/obj/item/gun/energy/pulse/destroyer/attack_self(mob/living/user) + to_chat(user, span_danger("[src.name] has three settings, and they are all DESTROY.")) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/special.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/special.dm new file mode 100644 index 0000000000..9c92e94e99 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/special.dm @@ -0,0 +1,447 @@ +/obj/item/gun/energy/ionrifle + name = "ion rifle" + desc = "A man-portable anti-armor weapon designed to disable mechanical threats at range. The high energy load requires the gun to cooldown between each shot." + icon_state = "ionrifle" + item_state = null //so the human update icon uses the icon_state instead. + shaded_charge = FALSE + ammo_x_offset = 2 + ammo_y_offset = 2 + w_class = WEIGHT_CLASS_BULKY + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + ammo_type = list(/obj/item/ammo_casing/energy/ion) + manufacturer = MANUFACTURER_SHARPLITE_NEW + +/obj/item/gun/energy/ionrifle/emp_act(severity) + return + +/obj/item/gun/energy/ionrifle/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/ionrifle/update_overlays() + . = ..() + var/mutable_appearance/cooldown_overlay = mutable_appearance('icons/obj/guns/energy.dmi') + if(current_cooldown) + cooldown_overlay = "[icon_state]_cooldown" + .+= cooldown_overlay + +/obj/item/gun/energy/ionrifle/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread) + . = ..() + update_appearance() + +/obj/item/gun/energy/ionrifle/reset_current_cooldown() + . = ..() + update_appearance() + playsound(src, 'sound/machines/synth_yes.ogg', 100, TRUE, frequency = 6120) + + +/obj/item/gun/energy/ionrifle/carbine + name = "ion carbine" + desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient." + icon_state = "ioncarbine" + w_class = WEIGHT_CLASS_NORMAL + slot_flags = ITEM_SLOT_BELT + ammo_x_offset = 2 + ammo_y_offset = 0 + +/obj/item/gun/energy/decloner + name = "biological demolecularisor" + desc = "A gun that discharges high amounts of controlled radiation to slowly break a target into component elements." + icon_state = "decloner" + ammo_type = list(/obj/item/ammo_casing/energy/declone) + ammo_x_offset = 1 + +/obj/item/gun/energy/decloner/update_overlays() + . = ..() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(!QDELETED(cell) && (cell.charge > shot.e_cost)) + . += "decloner_spin" + +/obj/item/gun/energy/floragun + name = "floral somatoray" + desc = "A tool that discharges controlled radiation which induces mutation in plant cells." + icon_state = "flora" + item_state = "gun" + ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut, /obj/item/ammo_casing/energy/flora/revolution) + modifystate = TRUE + ammo_x_offset = 1 + selfcharge = 1 + shaded_charge = TRUE + +/obj/item/gun/energy/meteorgun + name = "meteor gun" + desc = "For the love of god, make sure you're aiming this the right way!" + icon_state = "meteor_gun" + item_state = "c20r" + w_class = WEIGHT_CLASS_BULKY + ammo_type = list(/obj/item/ammo_casing/energy/meteor) + default_ammo_type = /obj/item/stock_parts/cell/potato + allowed_ammo_types = list( + /obj/item/stock_parts/cell/potato, + ) + selfcharge = 1 + +/obj/item/gun/energy/meteorgun/pen + name = "meteor pen" + desc = "The pen is mightier than the sword." + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "pen" + item_state = "pen" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + +/obj/item/gun/energy/mindflayer + name = "\improper Mind Flayer" + desc = "A prototype weapon recovered from the ruins of Research-Station Epsilon." + icon_state = "xray" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) + ammo_x_offset = 2 + +/obj/item/gun/energy/kinetic_accelerator/crossbow + name = "mini energy crossbow" + desc = "A weapon favored by syndicate stealth specialists." + icon_state = "crossbow" + item_state = "crossbow" + w_class = WEIGHT_CLASS_SMALL + custom_materials = list(/datum/material/iron=2000) + suppressed = TRUE + ammo_type = list(/obj/item/ammo_casing/energy/bolt) + weapon_weight = WEAPON_LIGHT + obj_flags = 0 + overheat_time = 20 + holds_charge = TRUE + unique_frequency = TRUE + max_mod_capacity = 0 + manufacturer = MANUFACTURER_SCARBOROUGH + +/obj/item/gun/energy/kinetic_accelerator/crossbow/large + name = "energy crossbow" + desc = "A reverse engineered weapon using syndicate technology." + icon_state = "crossbowlarge" + w_class = WEIGHT_CLASS_NORMAL + custom_materials = list(/datum/material/iron=4000) + suppressed = FALSE + ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) + manufacturer = MANUFACTURER_NONE + + +/obj/item/gun/energy/plasmacutter + name = "plasma cutter" + desc = "An engineering tool capable of expelling concentrated plasma bursts. You could use it to cut limbs off xenos! Or, you know, cut through walls." + icon_state = "plasmacutter" + item_state = "plasmacutter" + ammo_type = list(/obj/item/ammo_casing/energy/plasma) + flags_1 = CONDUCT_1 + attack_verb = list("attacked", "slashed", "cut", "sliced") + force = 12 + sharpness = SHARP_EDGED + can_charge = FALSE + + heat = 3800 + usesound = list('sound/items/welder.ogg', 'sound/items/welder2.ogg') + tool_behaviour = TOOL_DECONSTRUCT + wall_decon_damage = 200 + toolspeed = 1 //plasmacutters can be used like angle grinders + internal_magazine = TRUE //so you don't cheese through the need for plasma - WS EDIT + var/charge_cut = 100 //amount of charge used up to start action (multiplied by amount) and per progress_flash_divisor ticks of cutting + var/adv = FALSE + +/obj/item/gun/energy/plasmacutter/ComponentInitialize() + . = ..() + AddComponent(/datum/component/butchering, 25, 105, 0, 'sound/weapons/plasma_cutter.ogg') + AddElement(/datum/element/update_icon_blocker) + AddElement(/datum/element/tool_flash, 1) + +/obj/item/gun/energy/plasmacutter/examine(mob/user) + . = ..() + if(cell) + . += span_notice("[src] is [round(cell.percent())]% charged.") + +/obj/item/gun/energy/plasmacutter/attackby(obj/item/I, mob/user) + var/charge_multiplier = 0 //2 = Refined stack, 1 = Ore + if(istype(I, /obj/item/stack/sheet/mineral/plasma)) + charge_multiplier = 2 + if(istype(I, /obj/item/stack/ore/plasma)) + charge_multiplier = 1 + if(charge_multiplier) + if(cell.charge == cell.maxcharge) + to_chat(user, span_notice("You try to insert [I] into [src], but it's fully charged.")) //my cell is round and full + return + I.use(1) + cell.give(500*charge_multiplier) + to_chat(user, span_notice("You insert [I] in [src], recharging it.")) + else + ..() + +// Can we cut? Plasma cutter does not use charge continuously. +// Amount cannot be defaulted to 1: most of the code specifies 0 in the call. +/obj/item/gun/energy/plasmacutter/tool_use_check(mob/living/user, atom/target, amount) + if(QDELETED(cell)) + to_chat(user, span_warning("[src] does not have a cell, and cannot be used!")) + return FALSE + // Amount cannot be used if drain is made continuous, e.g. amount = 5, charge_cut = 25 + // Then it'll drain 125 at first and 25 periodically, but fail if charge dips below 125 even though it still can finish action + // Alternately it'll need to drain amount*charge_cut every period, which is either obscene or makes it free for other uses + if(amount ? cell.charge < charge_cut * amount : cell.charge < charge_cut) + to_chat(user, span_warning("You need more charge to complete this task!")) + return FALSE + + return TRUE + +/obj/item/gun/energy/plasmacutter/attack(mob/living/carbon/human/target, mob/user) + if(!istype(target)) + return ..() + var/obj/item/bodypart/attackedLimb = target.get_bodypart(check_zone(user.zone_selected)) + if(!attackedLimb || IS_ORGANIC_LIMB(attackedLimb) || (user.a_intent == INTENT_HARM)) + return ..() + if(!target.is_exposed(user, TRUE, user.zone_selected)) + return TRUE + if(!tool_start_check(user, amount = 1)) + return TRUE + user.visible_message(span_notice("[user] starts to fix some of the dents on [target]'s [parse_zone(attackedLimb.body_zone)]."), + span_notice("You start fixing some of the dents on [target == user ? "your" : "[target]'s"] [parse_zone(attackedLimb.body_zone)].")) + if(!use_tool(target, user, delay = (target == user ? 5 SECONDS : 0.5 SECONDS), amount = 1, volume = 25)) + return TRUE + item_heal_robotic(target, user, brute_heal = 15, burn_heal = 0) + return TRUE + +/obj/item/gun/energy/plasmacutter/use(amount) + return (!QDELETED(cell) && cell.use(amount ? amount * charge_cut : charge_cut)) + +/obj/item/gun/energy/plasmacutter/use_tool(atom/target, mob/living/user, delay, amount=1, volume=0, datum/callback/extra_checks) + if(amount) + if(adv) + target.add_overlay(GLOB.advanced_cutting_effect) + else + target.add_overlay(GLOB.cutting_effect) + . = ..() + if(adv) + target.cut_overlay(GLOB.advanced_cutting_effect) + else + target.cut_overlay(GLOB.cutting_effect) + else + . = ..(amount=1) + +/obj/item/gun/energy/plasmacutter/adv + name = "advanced plasma cutter" + icon_state = "adv_plasmacutter" + item_state = "adv_plasmacutter" + force = 15 + wall_decon_damage = 300 + ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv) + +/obj/item/gun/energy/wormhole_projector + name = "bluespace wormhole projector" + desc = "A projector that emits high density quantum-coupled bluespace beams." //WS Edit - Any anomaly core for phazons + ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange) + item_state = null + icon_state = "wormhole_projector" + base_icon_state = "wormhole_projector" + var/obj/effect/portal/p_blue + var/obj/effect/portal/p_orange + var/atmos_link = FALSE + +/obj/item/gun/energy/wormhole_projector/update_icon_state() + . = ..() + icon_state = item_state = "[base_icon_state][select]" + +/obj/item/gun/energy/wormhole_projector/update_ammo_types() + . = ..() + for(var/i in 1 to ammo_type.len) + var/obj/item/ammo_casing/energy/wormhole/W = ammo_type[i] + if(istype(W)) + W.gun = WEAKREF(src) + var/obj/projectile/beam/wormhole/WH = W.BB + if(istype(WH)) + WH.gun = WEAKREF(src) + +/obj/item/gun/energy/wormhole_projector/process_chamber(atom/shooter) + ..() + select_fire() + +/obj/item/gun/energy/wormhole_projector/proc/on_portal_destroy(obj/effect/portal/P) + if(P == p_blue) + p_blue = null + else if(P == p_orange) + p_orange = null + +/obj/item/gun/energy/wormhole_projector/proc/has_blue_portal() + if(istype(p_blue) && !QDELETED(p_blue)) + return TRUE + return FALSE + +/obj/item/gun/energy/wormhole_projector/proc/has_orange_portal() + if(istype(p_orange) && !QDELETED(p_orange)) + return TRUE + return FALSE + +/obj/item/gun/energy/wormhole_projector/proc/crosslink() + if(!has_blue_portal() && !has_orange_portal()) + return + if(!has_blue_portal() && has_orange_portal()) + p_orange.link_portal(null) + return + if(!has_orange_portal() && has_blue_portal()) + p_blue.link_portal(null) + return + p_orange.link_portal(p_blue) + p_blue.link_portal(p_orange) + +/obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/projectile/beam/wormhole/W, turf/target) + var/obj/effect/portal/P = new /obj/effect/portal(target, 300, null, FALSE, null, atmos_link) + RegisterSignal(P, COMSIG_QDELETING, PROC_REF(on_portal_destroy)) + if(istype(W, /obj/projectile/beam/wormhole/orange)) + qdel(p_orange) + p_orange = P + P.icon_state = "portal1" + else + qdel(p_blue) + p_blue = P + crosslink() + +/* 3d printer 'pseudo guns' for borgs */ + +/obj/item/gun/energy/printer + name = "integrated LMG" + desc = "A modified energy weapon re-designed to fire 3D-printed flechettes, pulled directly from the cyborg's internal power source." + icon_state = "l6_cyborg" + icon = 'icons/obj/guns/projectile.dmi' + default_ammo_type = /obj/item/stock_parts/cell/secborg + allowed_ammo_types = list( + /obj/item/stock_parts/cell/secborg, + ) + ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) + can_charge = FALSE + use_cyborg_cell = TRUE + + fire_delay = 0.3 SECONDS + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + +/obj/item/gun/energy/printer/ComponentInitialize() + . = ..() + AddElement(/datum/element/update_icon_blocker) + +/obj/item/gun/energy/printer/emp_act() + return + +//the future of mid-range borg marksmanship. Get tactical. +/obj/item/gun/energy/printer/commando + name = "integrated TAC-rifle" + desc = "A shoulder-mounted high-caliber ballistic weapon. Capable of supporting prolonged encounters by printing heavy rounds directly off the host cyborg's power supplies." + ammo_type = list(/obj/item/ammo_casing/energy/ctac, /obj/item/ammo_casing/energy/csour, /obj/item/ammo_casing/energy/csweet) + var/tac_ammo = 1 + +/obj/item/gun/energy/printer/commando/examine() + . = ..() + . += span_notice(" Can be reconfigured inhand to print different projectile designs.") + +/obj/item/gun/energy/printer/commando/attack_self(mob/living/user as mob) + if(ammo_type.len > 1) + tac_fire(user) + update_appearance() + +/obj/item/gun/energy/printer/commando/proc/tac_fire(mob/living/user) + select++ + if (select > ammo_type.len) + select = 1 + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if (shot.select_name) + playsound(get_turf(user), 'sound/items/change_drill.ogg', 50, TRUE) + if(shot.select_name == "tactical") + to_chat(user, span_notice("You configure the [src] to fire CY-TACTICAL high-velocity impact rounds.")) + if(shot.select_name == "sweet") + to_chat(user, span_notice("You set your [src] to fire CY-SWEET distruptor rounds, which travel slowly and do little damage, but irradiate and ignite targets.")) + if(shot.select_name == "sour") + to_chat(user, span_notice("You rearm your [src] with CY-SOUR nonlethal rounds, which cause stamina damage and distrupt the focus of enemies.")) + chambered = null + recharge_newshot(TRUE) + update_appearance() + return + +/obj/item/gun/energy/temperature + name = "temperature gun" + icon_state = "freezegun" + desc = "A gun that changes temperatures." + ammo_type = list(/obj/item/ammo_casing/energy/temp, /obj/item/ammo_casing/energy/temp/hot) + default_ammo_type = /obj/item/stock_parts/cell/gun/upgraded + ammo_x_offset = 2 + +/obj/item/gun/energy/temperature/security + name = "security temperature gun" + desc = "A weapon that can only be used to its full potential by the truly robust." + +/obj/item/gun/energy/laser/instakill + name = "instakill rifle" + icon_state = "instagib" + item_state = "instagib" + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." + ammo_type = list(/obj/item/ammo_casing/energy/instakill) + force = 60 + charge_sections = 5 + ammo_x_offset = 2 + shaded_charge = FALSE + +/obj/item/gun/energy/laser/instakill/red + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." + icon_state = "instagibred" + item_state = "instagibred" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/red) + +/obj/item/gun/energy/laser/instakill/blue + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a blue design." + icon_state = "instagibblue" + item_state = "instagibblue" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/blue) + +/obj/item/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib + return + +/obj/item/gun/energy/gravity_gun + name = "one-point gravitational manipulator" + desc = "An experimental, multi-mode device that fires bolts of Zero-Point Energy, causing local distortions in gravity. Requires an anomaly core to function." //WS Edit - Any anomaly core for phazons + ammo_type = list(/obj/item/ammo_casing/energy/gravity/repulse, /obj/item/ammo_casing/energy/gravity/attract, /obj/item/ammo_casing/energy/gravity/chaos) + item_state = "gravity_gun" + icon_state = "gravity_gun" + var/power = 4 + var/firing_core = FALSE + +/obj/item/gun/energy/gravity_gun/attackby(obj/item/C, mob/user) + if(istype(C, /obj/item/assembly/signaler/anomaly)) //WS Edit - Any anomaly core for phazons + to_chat(user, span_notice("You insert [C] into the gravitational manipulator and the weapon gently hums to life.")) + firing_core = TRUE + playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + qdel(C) + return + return ..() + +/obj/item/gun/energy/gravity_gun/can_shoot(visuals) + if(!firing_core) + return FALSE + return ..() + +/obj/item/gun/energy/tesla_cannon + name = "tesla cannon" + icon_state = "tesla" + item_state = "tesla" + desc = "A gun that shoots balls of \"tesla\", whatever that is." + ammo_type = list(/obj/item/ammo_casing/energy/tesla_cannon) + shaded_charge = TRUE + weapon_weight = WEAPON_HEAVY + + fire_delay = 0.1 SECONDS + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + +/obj/item/gun/energy/buster + name = "replica buster cannon" + icon_state = "buster" + item_state = "buster" + desc = "A replica of T4L1's buster cannon from the popular webseries RILENA. Fires a harmless energy pellet at the target." + ammo_type = list(/obj/item/ammo_casing/energy/buster) + weapon_weight = WEAPON_LIGHT diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/stun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/stun.dm new file mode 100644 index 0000000000..fdb3770b7c --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/energy/stun.dm @@ -0,0 +1,46 @@ +/obj/item/gun/energy/taser + name = "taser gun" + desc = "A low-capacity, energy-based stun gun used by security teams to subdue targets at range." + icon_state = "advtaser" + item_state = null //so the human update icon uses the icon_state instead. + ammo_type = list(/obj/item/ammo_casing/energy/electrode) + ammo_x_offset = 3 + spread = 2 + spread_unwielded = 4 + +/obj/item/gun/energy/e_gun/advtaser + name = "hybrid taser" + desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams." + icon_state = "advtaser" + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 2 + + spread = 2 + spread_unwielded = 4 + +/obj/item/gun/energy/e_gun/advtaser/cyborg + name = "cyborg taser" + desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." + can_charge = FALSE + use_cyborg_cell = TRUE + +/obj/item/gun/energy/disabler + name = "disabler" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "disabler" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 2 + manufacturer = MANUFACTURER_SHARPLITE_NEW + spread = 2 + spread_unwielded = 4 + +/obj/item/gun/energy/disabler/cyborg + name = "cyborg disabler" + desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." + can_charge = FALSE + use_cyborg_cell = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/gunhud.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/gunhud.dm new file mode 100644 index 0000000000..da01e98bc3 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/gunhud.dm @@ -0,0 +1,340 @@ +/* +* Customizable ammo hud +* - Adapted from SR's gun hud. Most of the bad stuff was cut out and sprites were mostly replace by some azlan did years ago. +* - var names and code have been cleaned up, for the most parts. There still some weirdly named stuff like the number hud (wtf does OTH even mean?) +* - Most of this SHOULDN'T be used yet, only instance of this being used at the moment is the revolver which has completely differnt behavior +*/ + +/* +* This hud is controlled namely by the ammo_hud component. Generally speaking this is inactive much like all other hud components until it's needed. +* It does not do any calculations of it's own, you must do this externally. +* If you wish to use this hud, use the ammo_hud component or create another one which interacts with it via the below procs. +* proc/turn_off +* proc/turn_on +* proc/set_hud +* Check the gun_hud.dmi for all available icons you can use. +*/ + +// Ammo counter +#define ui_ammocounter "EAST-1:28,CENTER+1:25" + +/datum/hud + var/atom/movable/screen/ammo_counter + +/atom/movable/screen/ammo_counter + name = "ammo counter" + icon = 'icons/hud/gun_hud.dmi' + icon_state = "backing" + screen_loc = ui_ammocounter + invisibility = INVISIBILITY_ABSTRACT + + ///This is the color assigned to the OTH backing, numbers and indicator. + var/backing_color = COLOR_RED + /// The prefix used for the hud + var/prefix = "" + + //Below are the OTH numbers, these are assigned by oX, tX and hX, x being the number you wish to display(0-9) + ///OTH position X00 + var/oth_o + ///OTH position 0X0 + var/oth_t + ///OTH position 00X + var/oth_h + ///This is the custom indicator sprite that will appear in the box at the bottom of the ammo hud, use this for something like semi/auto toggle on a gun. + var/indicator + +///This proc simply resets the hud to standard and removes it from the players visible hud. +/atom/movable/screen/ammo_counter/proc/turn_off() + invisibility = INVISIBILITY_ABSTRACT + maptext = null + backing_color = COLOR_RED + oth_o = "" + oth_t = "" + oth_h = "" + indicator = "" + update_appearance() + +///This proc turns the hud on, but does not set it to anything other than the currently set values +/atom/movable/screen/ammo_counter/proc/turn_on() + invisibility = 0 + +///This is the main proc for altering the hud's appeareance, it controls the setting of the overlays. Use the OTH and below variables to set it accordingly. +/atom/movable/screen/ammo_counter/proc/set_hud(_backing_color, _oth_o, _oth_t, _oth_h, _indicator) + backing_color = _backing_color + oth_o = _oth_o + oth_t = _oth_t + oth_h = _oth_h + indicator = _indicator + + update_appearance() + +/atom/movable/screen/ammo_counter/update_overlays(list/rounds) + . = ..() + cut_overlays() + if(oth_o) + var/mutable_appearance/o_overlay = mutable_appearance(icon, oth_o) + o_overlay.color = backing_color + . += o_overlay + if(oth_t) + var/mutable_appearance/t_overlay = mutable_appearance(icon, oth_t) + t_overlay.color = backing_color + . += t_overlay + if(oth_h) + var/mutable_appearance/h_overlay = mutable_appearance(icon, oth_h) + h_overlay.color = backing_color + . += h_overlay + if(indicator) + var/mutable_appearance/indicator_overlay = mutable_appearance(icon, indicator) + indicator_overlay.color = backing_color + . += indicator_overlay + if(!rounds) + return + + for(var/image/round as anything in rounds) + add_overlay(round) + +//*////////////////////////////////////////////////////////////////////////////////////////////////////////////* + +/datum/component/ammo_hud + var/atom/movable/screen/ammo_counter/hud + /// The prefix used for the hud + var/prefix = "" + var/indicator = "" + var/backing_color = "#FFFFFF" // why was this hardcoded dlfhakhjdfj + +/datum/component/ammo_hud/Initialize() + . = ..() + if(!istype(parent, /obj/item/gun) && !istype(parent, /obj/item/weldingtool)) + return COMPONENT_INCOMPATIBLE + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(wake_up)) + +/datum/component/ammo_hud/Destroy() + turn_off() + return ..() + +/datum/component/ammo_hud/proc/wake_up(datum/source, mob/user, slot) + SIGNAL_HANDLER + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.is_holding(parent)) + if(H.hud_used) + hud = H.hud_used.ammo_counter + turn_on() + else + turn_off() + +/datum/component/ammo_hud/proc/turn_on() + SIGNAL_HANDLER + + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(turn_off)) + RegisterSignals(parent, list(COMSIG_UPDATE_AMMO_HUD, COMSIG_GUN_CHAMBER_PROCESSED), PROC_REF(update_hud)) + + hud.turn_on() + update_hud() + +/datum/component/ammo_hud/proc/turn_off() + SIGNAL_HANDLER + + UnregisterSignal(parent, list(COMSIG_ITEM_DROPPED, COMSIG_UPDATE_AMMO_HUD, COMSIG_GUN_CHAMBER_PROCESSED)) + + if(hud) + hud.turn_off() + hud = null + +/// Returns get_ammo() with the appropriate args passed to it - some guns like the revolver and bow are special cases +/datum/component/ammo_hud/proc/get_accurate_ammo_count(obj/item/gun/ballistic/the_gun) + // fucking revolvers indeed - do not count empty or chambered rounds for the display HUD + if(istype(the_gun, /obj/item/gun/ballistic/revolver)) + var/obj/item/gun/ballistic/revolver/the_revolver = the_gun + return the_revolver.get_ammo(countchambered = FALSE, countempties = FALSE) + + // bows are also weird and shouldn't count the chambered + if(istype(the_gun, /obj/item/gun/ballistic/bow)) + return the_gun.get_ammo(countchambered = FALSE) + + return the_gun.get_ammo(countchambered = TRUE) + +/datum/component/ammo_hud/proc/get_accurate_laser_count(obj/item/gun/energy/the_gun) + var/obj/item/ammo_casing/energy/current_mode = the_gun.chambered + if(!current_mode) + return FALSE + return round(the_gun.cell.charge/current_mode.e_cost) + +/datum/component/ammo_hud/proc/update_hud() + SIGNAL_HANDLER + var/obj/item/gun/ballistic/pew = parent + hud.maptext = null + hud.icon_state = "[prefix]backing" + if(!pew.magazine) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]no_mag") + return + if(!pew.get_ammo()) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]empty_flash") + return + + var/rounds = num2text(get_accurate_ammo_count(pew)) + var/oth_o + var/oth_t + var/oth_h + + switch(length(rounds)) + if(1) + oth_o = "[prefix]o[rounds[1]]" + if(2) + oth_o = "[prefix]o[rounds[2]]" + oth_t = "[prefix]t[rounds[1]]" + if(3) + oth_o = "[prefix]o[rounds[3]]" + oth_t = "[prefix]t[rounds[2]]" + oth_h = "[prefix]h[rounds[1]]" + else + oth_o = "[prefix]o9" + oth_t = "[prefix]t9" + oth_h = "[prefix]h9" + hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator) + +/datum/component/ammo_hud/laser/update_hud() + var/obj/item/gun/energy/pew = parent + hud.maptext = null + hud.icon_state = "[prefix]backing" + if(!pew.cell) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]no_mag") + return + if(!get_accurate_laser_count(pew)) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]empty_flash") + return + + var/rounds = num2text(get_accurate_laser_count(pew)) + var/oth_o + var/oth_t + var/oth_h + + switch(length(rounds)) + if(1) + oth_o = "[prefix]o[rounds[1]]" + if(2) + oth_o = "[prefix]o[rounds[2]]" + oth_t = "[prefix]t[rounds[1]]" + if(3) + oth_o = "[prefix]o[rounds[3]]" + oth_t = "[prefix]t[rounds[2]]" + oth_h = "[prefix]h[rounds[1]]" + else + oth_o = "[prefix]o9" + oth_t = "[prefix]t9" + oth_h = "[prefix]h9" + hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator) + +/datum/component/ammo_hud/laser/cybersun + prefix = "cybersun_" + +//please be aware, this only supports 6 round revolvers. It is comically easy to support more or less rounds,like in game there are 7 round and 5 round revolvers, but that requires sprites, and i'm lasy +/datum/component/ammo_hud/revolver + prefix = "revolver_" + +/// Returns get_ammo() with the appropriate args passed to it - some guns like the revolver and bow are special cases +/datum/component/ammo_hud/revolver/get_accurate_ammo_count(obj/item/gun/ballistic/revolver/the_gun) + if(istype(the_gun, /obj/item/gun/ballistic/revolver)) + var/obj/item/gun/ballistic/revolver/the_revolver = the_gun + if(the_revolver.magazine) + return the_revolver.magazine.ammo_list() + +/* //for counter-clockwise, kept here for reference + var/list/round_positions = list( + list("x" = 12,"y" = 22), + + list("x" = 20,"y" = 17), + list("x" = 20,"y" = 7 ), + list("x" = 12,"y" = 2 ), + list("x" = 4 ,"y" = 7 ), + list("x" = 4 ,"y" = 17) + ) +*/ + +/datum/component/ammo_hud/revolver/update_hud() + var/obj/item/gun/ballistic/revolver/pew = parent + hud.icon_state = "[prefix]backing" + + var/list/rounds = get_accurate_ammo_count(pew) + var/list/round_images = list() + var/list/round_positions = list( + list("x" = 12,"y" = 22), + + list("x" = 4 ,"y" = 17), + list("x" = 4 ,"y" = 7 ), + list("x" = 12,"y" = 2 ), + list("x" = 20,"y" = 7 ), + list("x" = 20,"y" = 17) + + ) + + var/bullet_count = 0 + for(var/obj/item/ammo_casing/bullet as anything in rounds) + bullet_count++ + if(!bullet) + continue + var/image/current_bullet_image = image(icon = 'icons/hud/gun_hud.dmi') + var/list/bullet_position = round_positions[bullet_count] + current_bullet_image.pixel_x = bullet_position["x"] + current_bullet_image.pixel_y = bullet_position["y"] + current_bullet_image.icon_state = "revolver_casing[bullet.BB ? "_live" : ""]" + round_images += current_bullet_image + + hud.update_overlays(round_images) + +/datum/component/ammo_hud/eoehoma + backing_color = "#cb001a" + +/datum/component/ammo_hud/eoehoma/update_hud() + var/obj/item/gun/ballistic/automatic/assault/e40/pew = parent + var/obj/item/gun/energy/laser/e40_laser_secondary/pew_secondary = pew.secondary + hud.maptext = null + hud.icon_state = "[prefix]backing" + + var/rounds = num2text(get_accurate_ammo_count(pew)) + var/oth_o + var/oth_t + var/oth_h + + var/current_firemode = pew.gun_firemodes[pew.firemode_index] + if(current_firemode == FIREMODE_FULLAUTO) + if(!pew.magazine) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]no_mag") + return + if(!pew.get_ammo()) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]empty_flash") + return + rounds = num2text(get_accurate_ammo_count(pew)) + indicator = "bullet" + else + if(!pew_secondary.cell) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]no_mag") + return + if(!get_accurate_laser_count(pew_secondary)) + hud.set_hud(backing_color, "[prefix]oe", "[prefix]te", "[prefix]he", "[prefix]empty_flash_laser") + return + rounds = num2text(get_accurate_laser_count(pew_secondary)) + indicator = "laser" + + + switch(length(rounds)) + if(1) + oth_o = "[prefix]o[rounds[1]]" + if(2) + oth_o = "[prefix]o[rounds[2]]" + oth_t = "[prefix]t[rounds[1]]" + if(3) + oth_o = "[prefix]o[rounds[3]]" + oth_t = "[prefix]t[rounds[2]]" + oth_h = "[prefix]h[rounds[1]]" + else + oth_o = "[prefix]o9" + oth_t = "[prefix]t9" + oth_h = "[prefix]h9" + hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator) + +/datum/component/ammo_hud/counter + backing_color = "#ff0000ff" + prefix = null + indicator = "bullet" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm new file mode 100644 index 0000000000..2ca35d90ca --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/ballistics.dm @@ -0,0 +1,809 @@ +#define CLIP_ATTACHMENTS list(/obj/item/attachment/silencer, /obj/item/attachment/laser_sight, /obj/item/attachment/rail_light, /obj/item/attachment/bayonet, /obj/item/attachment/ammo_counter,/obj/item/attachment/gun) +#define CLIP_ATTACHMENT_POINTS list(ATTACHMENT_SLOT_MUZZLE = 1,ATTACHMENT_SLOT_RAIL = 1,ATTACHMENT_SLOT_SCOPE=1) + + +//########### PISTOLS ###########// +/obj/item/gun/ballistic/automatic/pistol/cm23 + name = "\improper CM-23" + desc = "CLIP's standard service pistol. 10 rounds of 10mm ammunition make the CM-23 deadlier than many other service pistols, but its weight and bulk have made it unpopular as a sidearm. It has largely been phased out outside of specialized units and patrols on the fringes of CLIP space. Chambered in 10mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm23" + item_state = "clip_generic" + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/cm23 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/cm23, + ) + fire_sound = 'sound/weapons/gun/pistol/cm23.ogg' + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/slide_drop.ogg' + manufacturer = MANUFACTURER_MINUTEMAN + load_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + + default_attachments = list(/obj/item/attachment/laser_sight) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 18, + ), + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 29, + "y" = 20, + ) + ) + + recoil_unwielded = 3 + +/obj/item/gun/ballistic/automatic/pistol/cm23/no_mag + default_ammo_type = FALSE + +/obj/item/ammo_box/magazine/cm23 + name = "CM-23 pistol magazine (10x22mm)" + desc = "An 10-round magazine magazine designed for the CM-23 pistol. These rounds do moderate damage, but struggle against armor." + icon_state = "cm23_mag-1" + base_icon_state = "cm23_mag" + ammo_type = /obj/item/ammo_casing/c10mm + caliber = "10x22mm" + max_ammo = 10 + +/obj/item/ammo_box/magazine/cm23/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/cm23/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/pistol/cm70 + name = "CM-70 machine pistol" + desc = "A compact machine pistol designed to rapidly fire 3-round bursts. Popular with officers and certain special units, the CM-70 is incredibly dangerous at close range. Chambered in 9mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + icon_state = "cm70" + item_state = "clip_generic" + default_ammo_type = /obj/item/ammo_box/magazine/m9mm_cm70 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m9mm_cm70, + ) + burst_size = 3 + burst_delay = 0.1 SECONDS + fire_delay = 0.4 SECONDS + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + default_firemode = FIREMODE_SEMIAUTO + manufacturer = MANUFACTURER_MINUTEMAN + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + + fire_sound = 'sound/weapons/gun/pistol/cm70.ogg' + + spread = 8 + spread_unwielded = 20 + + wear_minor_threshold = 240 + wear_major_threshold = 720 + wear_maximum = 1200 + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 23, + "y" = 17, + ), + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 31, + "y" = 21, + ) + ) + +/obj/item/ammo_box/magazine/m9mm_cm70 + name = "CM-70 machine pistol magazine (9x18mm)" + desc = "A 18-round magazine designed for the CM-70 machine pistol. These rounds do okay damage, but struggle against armor." + icon_state = "cm70_mag_18" + base_icon_state = "cm70_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 18 + + +/obj/item/ammo_box/magazine/m9mm_cm70/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[ammo_count() == 1 ? 1 : round(ammo_count(),3)]" + +/obj/item/ammo_box/magazine/m9mm_cm70/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/pistol/cm357 + name = "\improper CM-357" + desc = "A powerful semi-automatic handgun designed for CLIP-BARD's megafauna removal unit, as standard handguns had proven ineffective as backup weapons. The heft and power of the weapon have made it a status symbol among the few CLIP officers able to requisition one. Chambered in .357." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm357" + item_state = "clip_generic" + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/cm357 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/cm357, + ) + fire_sound = 'sound/weapons/gun/pistol/deagle.ogg' + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/slide_drop.ogg' + manufacturer = MANUFACTURER_MINUTEMAN + load_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + + recoil_unwielded = 4 + recoil = 1 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 23, + "y" = 16, + ) + ) + +NO_MAG_GUN_HELPER(automatic/pistol/cm357) + +/obj/item/ammo_box/magazine/cm357 + name = "CM-357 pistol magazine (.357)" + desc = "A 7-round magazine designed for the CM-357 pistol. These rounds do good damage, but struggle against armor." + icon_state = "cm23_mag-1" + base_icon_state = "cm23_mag" + ammo_type = /obj/item/ammo_casing/a357 + caliber = ".357" + max_ammo = 7 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/cm357/empty + start_empty = TRUE + +//########### SMGS ###########// +/obj/item/gun/ballistic/automatic/smg/cm5 + name = "\improper CM-5" + desc = "CLIP's standard-issue submachine gun. Well-liked for its accuracy, stability, and ease of use compared to other submachineguns. Chambered in 9mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm5" + item_state = "cm5" + + default_ammo_type = /obj/item/ammo_box/magazine/cm5_9mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/cm5_9mm, + ) + bolt_type = BOLT_TYPE_CLIP + weapon_weight = WEAPON_LIGHT + fire_sound = 'sound/weapons/gun/smg/cm5.ogg' + manufacturer = MANUFACTURER_MINUTEMAN + + spread = 3 + spread_unwielded = 7 + + valid_attachments = CLIP_ATTACHMENTS + slot_available = CLIP_ATTACHMENT_POINTS + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 38, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 27, + "y" = 17, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 12, + "y" = 23, + ) + ) + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +NO_MAG_GUN_HELPER(automatic/smg/cm5) + +/obj/item/gun/ballistic/automatic/smg/cm5/rubber + default_ammo_type = /obj/item/ammo_box/magazine/cm5_9mm/rubber + +/obj/item/ammo_box/magazine/cm5_9mm + name = "CM-5 magazine (9x18mm)" + desc = "A 30-round magazine for the CM-5 submachine gun. These rounds do okay damage, but struggle against armor." + icon_state = "cm5_mag-1" + base_icon_state = "cm5_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/cm5_9mm/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/cm5_9mm/rubber + desc = "A 30-round magazine for the CM-5 submachine gun. These rubber rounds trade lethality for a heavy impact which can incapacitate targets. Performs even worse against armor." + caliber = "9x18mm rubber" + ammo_type = /obj/item/ammo_casing/c9mm/rubber + +/obj/item/gun/ballistic/automatic/smg/cm5/compact + name = "\improper CM-5c" + desc = "A modification of the CM-5 featuring a dramatically shortened barrel and removed stock. Designed for CLIP-GOLD covert enforcement agents to maximize portability without sacrificing firepower, though accuracy at range is abysmal at best. Chambered in 9mm." + icon_state = "cm5c" + item_state = "cm5c" + + w_class = WEIGHT_CLASS_NORMAL + spread = 10 + spread_unwielded = 20 + + fire_delay = 0.1 SECONDS + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 30, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 22, + "y" = 17, + ) + ) + + + recoil = 1 + recoil_unwielded = 2 + wield_delay = 0.2 SECONDS + wield_slowdown = 0.15 + + var/obj/item/storage/briefcase/current_case + +NO_MAG_GUN_HELPER(automatic/smg/cm5/compact) + +/obj/item/gun/ballistic/automatic/smg/cm5/compact/attackby(obj/item/attacking_item, mob/user, params) + . = ..() + if(current_case) + return + if(!istype(attacking_item, /obj/item/storage/briefcase)) + return + if(attacking_item.contents.len != 0) + return + to_chat(user, span_notice("...? You rig [src] to fire from within [attacking_item].")) + current_case = attacking_item + attacking_item.forceMove(src) + icon = attacking_item.icon + base_icon_state = attacking_item.icon_state + item_state = attacking_item.item_state + name = attacking_item.name + lefthand_file = attacking_item.lefthand_file + righthand_file = attacking_item.righthand_file + pickup_sound = attacking_item.pickup_sound + drop_sound = attacking_item.drop_sound + w_class = WEIGHT_CLASS_BULKY + +//how are you even supposed to hold it like this...? + spread += 10 + spread_unwielded +=10 + + cut_overlays() + update_appearance() + +/obj/item/gun/ballistic/automatic/smg/cm5/compact/AltClick(mob/user) + if(!current_case) + return ..() + user.put_in_hands(current_case) + icon = src::icon + base_icon_state = src::icon_state + item_state = src::item_state + name = src::name + lefthand_file = src::lefthand_file + righthand_file = src::righthand_file + pickup_sound = src::pickup_sound + drop_sound = src::drop_sound + w_class = WEIGHT_CLASS_NORMAL + + spread = src::spread + spread_unwielded = src::spread_unwielded + to_chat(user, span_notice("You remove the [current_case] from [src]")) + current_case = null + + cut_overlays() + update_appearance() + + +//########### MARKSMAN ###########// +/obj/item/gun/ballistic/automatic/marksman/f4 + name = "CM-F4" + desc = "CLIP's marksman rifle, used by both military and law enforcement units. Designed not long after the CM-24, the venerable F4 has adapted well to continued upgrades. Chambered in .308." + + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "f4" + item_state = "f4" + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + bolt_type = BOLT_TYPE_CLIP + default_ammo_type = /obj/item/ammo_box/magazine/f4_308 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/f4_308, + ) + fire_sound = 'sound/weapons/gun/rifle/f4.ogg' + burst_size = 0 + actions_types = list() + manufacturer = MANUFACTURER_MINUTEMAN + + valid_attachments = CLIP_ATTACHMENTS + slot_available = CLIP_ATTACHMENT_POINTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 17, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 35, + "y" = 16, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 18, + "y" = 22, + ) + ) + + zoom_amt = 6 + zoom_out_amt = 2 + wield_slowdown = DMR_SLOWDOWN + spread = -4 + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +/obj/item/gun/ballistic/automatic/marksman/f4/inteq + name = "\improper SsG-04" + desc = "An F4 rifle purchased from CLIP and modified to suit IRMG's needs. Chambered in .308." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "f4_inteq" + item_state = "f4_inteq" + +NO_MAG_GUN_HELPER(automatic/marksman/f4/inteq) + +/obj/item/gun/ballistic/automatic/marksman/f4/indie + name = "\improper F3" + desc = "An old model of CLIP's F4 rifle, designed very early into the history of the League. Most have been sold off as surplus by this point and tend to suffer from internal wear due to their age. Chambered in .308." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "f4_indie" + item_state = "f4_indie" + + wear_rate = 1.5 + +/obj/item/gun/ballistic/automatic/marksman/f90 + name = "CM-F90" + desc = "A powerful sniper rifle used by vanishingly rare CLIP specialists, capable of impressive range and penetrating power. Chambered in 6.5mm CLIP." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "f90" + item_state = "f90" + + fire_sound = 'sound/weapons/gun/sniper/cmf90.ogg' + + default_ammo_type = /obj/item/ammo_box/magazine/f90 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/f90, + ) + bolt_type = BOLT_TYPE_CLIP + + fire_delay = 1 SECONDS + + manufacturer = MANUFACTURER_MINUTEMAN + spread = -5 + spread_unwielded = 35 + recoil = 2 + recoil_unwielded = 10 + wield_slowdown = LIGHT_SNIPER_SLOWDOWN + wield_delay = 1.3 SECONDS + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 16, + ), + ) + + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + +/obj/item/ammo_box/magazine/f90 + name = "\improper CM-F90 Magazine (6.5x57mm CLIP)" + desc = "A large 5-round box magazine for the CM-F90 sniper rifles. These rounds deal amazing damage and bypass half of their protective equipment, though it isn't a high enough caliber to pierce armored vehicles." + base_icon_state = "f90_mag" + icon_state = "f90_mag-1" + ammo_type = /obj/item/ammo_casing/a65clip + caliber = "6.5mm CLIP" + max_ammo = 5 + +/obj/item/ammo_box/magazine/f90/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/f90/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/marksman/f90/inteq + name = "\improper SSG-08 Saluki" + desc = "A powerful sniper purchased from CLIP, lightly modified with superficial modifications and an IRMG paintjob by the Artificer Division. Chambered in 6.5mm." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + + icon_state = "saluki" + item_state = "saluki" + + manufacturer = MANUFACTURER_INTEQ + +//########### RIFLES ###########// +/obj/item/gun/ballistic/automatic/assault/cm82 + name = "\improper CM-82" + desc = "CLIP's standard assault rifle, a relatively new service weapon. Accurate, reliable, and easy to use, the CM-82 replaced the CM-24 as CLIP's assault rifle almost overnight, and has proven immensely popular since. Chambered in 5.56mm." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + fire_sound = 'sound/weapons/gun/rifle/cm82.ogg' + icon_state = "cm82" + item_state = "cm82" + show_magazine_on_sprite = TRUE + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + bolt_type = BOLT_TYPE_CLIP + default_ammo_type = /obj/item/ammo_box/magazine/p16 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/p16, + ) + spread = 2 + wield_delay = 0.5 SECONDS + + fire_delay = 0.18 SECONDS + + valid_attachments = CLIP_ATTACHMENTS + slot_available = CLIP_ATTACHMENT_POINTS + + load_sound = 'sound/weapons/gun/rifle/cm82_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/cm82_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/cm82_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/cm82_unload.ogg' + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 47, + "y" = 19, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 29, + "y" = 17, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 15, + "y" = 24, + ) + ) + +/obj/item/gun/ballistic/automatic/assault/skm/cm24 + name = "\improper CM-24" + desc = "An obsolete and very rugged assault rifle with a heavy projectile and slow action for its class. Once CLIP's standard assault rifle produced in phenomenal numbers for the First Frontiersman War, it now serves as an acceptable, if rare, battle rifle. Chambered in 7.62mm CLIP." + + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm24" + item_state = "cm24" + manufacturer = MANUFACTURER_NONE + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + +NO_MAG_GUN_HELPER(automatic/assault/skm/cm24) + +/obj/item/gun/ballistic/automatic/hmg/cm40 + name = "\improper CM-40" + desc = "A light machine gun used by CLIP heavy weapons teams, capable of withering suppressive fire. The weight and recoil make it nearly impossible to use without deploying the bipod against appropriate cover, such as a table, or bracing against solid cover. Chambered in 7.62x40mm CLIP." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm40" + item_state = "cm40" + + fire_sound = 'sound/weapons/gun/hmg/cm40.ogg' + rack_sound = 'sound/weapons/gun/hmg/cm40_cocked.ogg' + + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + + load_sound = 'sound/weapons/gun/hmg/cm40_reload.ogg' + load_empty_sound = 'sound/weapons/gun/hmg/cm40_reload.ogg' + eject_sound = 'sound/weapons/gun/hmg/cm40_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/hmg/cm40_unload.ogg' + + fire_delay = 0.1 SECONDS + + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + manufacturer = MANUFACTURER_MINUTEMAN + default_ammo_type = /obj/item/ammo_box/magazine/cm40_762_40_box + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/cm40_762_40_box, + ) + + spread = 10 + spread_unwielded = 35 + + recoil = 2 //identical to other LMGS + recoil_unwielded = 7 //same as skm + + wield_slowdown = SAW_SLOWDOWN //not as severe as other lmgs, but worse than the normal skm + wield_delay = 0.9 SECONDS //faster than normal lmgs, slower than stock skm + + has_bipod = TRUE + + //you get the rail slot back when the bipod is an attachment + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 19, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 12, + "y" = 25, + ) + ) + + deploy_recoil_bonus = -2 + deploy_spread_bonus = -6 + +/obj/item/gun/ballistic/automatic/hmg/cm40/ComponentInitialize() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.1 SECONDS) + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/ammo_box/magazine/cm40_762_40_box + name = "CM-40 box magazine (7.62x40mm CLIP)" + desc = "An 80 round box magazine for CM-40 light machine gun. These rounds do good damage with good armor penetration." + base_icon_state = "cm40_mag" + icon_state = "cm40_mag-1" + ammo_type = /obj/item/ammo_casing/a762_40 + max_ammo = 80 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/cm40_762_40_box/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/cm40_762_40_box/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/hmg/rottweiler + name = "\improper KM-05 Rottweiler" + desc = "An F4 rifle purchased from CLIP and extensively modified into a belt fed machine gun. Heavy and firing a powerful cartridge, this weapon is unwieldy without a bipod support. Uniquely, the KM-05 Rottweiler can accept F4 magazines into the magazine well." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + + icon_state = "rottweiler" + item_state = "rottweiler" + + manufacturer = MANUFACTURER_INTEQ + + show_magazine_on_sprite = TRUE + show_magazine_on_sprite_ammo = TRUE + mag_display_ammo = TRUE + + + fire_sound = 'sound/weapons/gun/hmg/hmg.ogg' + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + fire_delay = 0.2 SECONDS //chunky machine gun + + unique_mag_sprites_for_variants = TRUE + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + bolt_type = BOLT_TYPE_CLIP + tac_reloads = FALSE + + default_ammo_type = /obj/item/ammo_box/magazine/rottweiler_308_box + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/rottweiler_308_box, + /obj/item/ammo_box/magazine/f4_308 + ) + + spread = 12 + spread_unwielded = 35 + + recoil = 3 //it's firing .308 + recoil_unwielded = 8 + + has_bipod = TRUE + + deploy_recoil_bonus = -3 + deploy_spread_bonus = -10 //2 degree spread when deployed, making it VERY accurate for an lmg + + valid_attachments = CLIP_ATTACHMENTS + unique_attachments = list(/obj/item/attachment/scope) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 49, + "y" = 17, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 19, + "y" = 21, + ) + ) +/obj/item/gun/ballistic/automatic/hmg/rottweiler/ComponentInitialize() + . = ..() + AddComponent(/datum/component/automatic_fire, 0.2 SECONDS) + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/ammo_box/magazine/rottweiler_308_box + name = "Rottweiler box magazine (.308)" + desc = "A 50 round box magazine for Rottweiler machine gun. These rounds do good damage with significant armor penetration." + base_icon_state = "rottweiler_mag" + icon_state = "rottweiler_mag-1" + ammo_type = /obj/item/ammo_casing/a308 + caliber = ".308" + max_ammo = 50 + w_class = WEIGHT_CLASS_NORMAL + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/rottweiler_308_box/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/rottweiler_308_box/empty + start_empty = TRUE + +//########### MISC ###########// + +/obj/item/gun/ballistic/shotgun/cm15 + name = "\improper CM-15" + desc = "A large automatic shotgun used by CLIP. Generally employed by law enforcement and breaching specialists, and rarely by CLIP-BARD (typically with incendiary ammunition). Chambered in 12 gauge." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm15" + item_state = "cm15" + + fire_select_icon_state_prefix = "clip_" + adjust_fire_select_icon_state_on_safety = TRUE + + manufacturer = MANUFACTURER_MINUTEMAN + + weapon_weight = WEAPON_MEDIUM + default_ammo_type = /obj/item/ammo_box/magazine/cm15_12g + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/cm15_12g, + ) + + empty_indicator = FALSE + unique_mag_sprites_for_variants = FALSE + + show_magazine_on_sprite = TRUE + semi_auto = TRUE + internal_magazine = FALSE + casing_ejector = TRUE + tac_reloads = TRUE + + pickup_sound = 'sound/items/handling/rifle_pickup.ogg' + fire_sound = 'sound/weapons/gun/shotgun/bulldog.ogg' + load_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg' + + spread = 3 + spread_unwielded = 15 + recoil = 1 + recoil_unwielded = 4 + wield_slowdown = HEAVY_SHOTGUN_SLOWDOWN + wield_delay = 0.65 SECONDS + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 34, + "y" = 15, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 44, + "y" = 19, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 21, + "y" = 25, + ) + ) + +/obj/item/gun/ballistic/shotgun/cm15/no_mag + default_ammo_type = FALSE + +/obj/item/gun/ballistic/shotgun/cm15/incendiary + default_ammo_type = /obj/item/ammo_box/magazine/cm15_12g/incendiary + + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm new file mode 100644 index 0000000000..49deb9e26f --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/clip_lanchester/lasers.dm @@ -0,0 +1,68 @@ +/obj/item/gun/energy/kalix/clip + name = "ECM-6" + desc = "A modernized copy of the ECM-1, CLIP's first service weapon. Features a number of improvements to bring the aging design back into the modern age." + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm1" + item_state = "cm1" + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + wield_delay = 0.7 SECONDS + wield_slowdown = LASER_SMG_SLOWDOWN + + default_ammo_type = /obj/item/stock_parts/cell/gun/kalix + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + ammo_type = list(/obj/item/ammo_casing/energy/kalix, /obj/item/ammo_casing/energy/disabler/hitscan) + + manufacturer = MANUFACTURER_MINUTEMAN_LASER + +/obj/item/gun/energy/kalix/clip/old + name = "ECM-1" + desc = "This is either a flawless replica, or a genuine example of the colonial-era laser weaponry issued to Free Zohil forces in CLIP's founding years. Over a hundred years old, and especially difficult to source replacement parts for, but still deadly. Kept around for ceremonial use in the CLIP Minutemen, and, rarely, for influential members of all divisions." + + default_ammo_type = /obj/item/stock_parts/cell/gun + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun, + /obj/item/stock_parts/cell/gun/upgraded, + /obj/item/stock_parts/cell/gun/empty, + /obj/item/stock_parts/cell/gun/upgraded/empty, + ) + + ammo_type = list(/obj/item/ammo_casing/energy/kalix) + +/obj/item/gun/energy/laser/e50/clip + name = "ECM-50" + desc = "An extensive modification of the Eoehoma E-50 Emitter by Clover Photonics, customized for CLIP-BARD to fight Xenofauna. Sacrifices some of the E-50's raw power for vastly improved energy efficiency, while preserving its incendiary side-effects." + + icon = 'icons/obj/guns/manufacturer/clip_lanchester/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/clip_lanchester/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/clip_lanchester/onmob.dmi' + + icon_state = "cm50" + item_state = "cm50" + shaded_charge = TRUE + charge_sections = 4 + + manufacturer = MANUFACTURER_MINUTEMAN_LASER + + ammo_type = list(/obj/item/ammo_casing/energy/laser/eoehoma/e50/clip) + +/obj/item/ammo_casing/energy/laser/eoehoma/e50/clip + projectile_type = /obj/projectile/beam/emitter/hitscan/clip + fire_sound = 'sound/weapons/gun/laser/heavy_laser.ogg' + e_cost = 6250 + delay = 0.6 SECONDS + +/obj/projectile/beam/emitter/hitscan/clip + damage = 35 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/ballistics.dm new file mode 100644 index 0000000000..a096989c26 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/ballistics.dm @@ -0,0 +1,229 @@ +/obj/item/gun/ballistic/automatic/assault/e40 + name = "\improper E-40 Hybrid Rifle" + desc = "A Hybrid Assault Rifle, best known for being having a dual ballistic/laser system along with an advanced ammo counter. Once an icon for bounty hunters, age has broken most down, so these end up in collector's hands or as shoddy Frontiersmen laser SMG conversions when in their inheritted stockpiles. But if one were to find one in working condition, it would be just as formidable as back then. Chambered in .299 Eoehoma caseless, and uses energy for lasers." + icon = 'icons/obj/guns/manufacturer/eoehoma/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/eoehoma/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/eoehoma/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/eoehoma/onmob.dmi' + icon_state = "e40" + item_state = "e40" + default_ammo_type = /obj/item/ammo_box/magazine/e40 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/e40, + ) + var/obj/item/gun/energy/laser/e40_laser_secondary/secondary + fire_select_icon_state_prefix = "e40_" + + fire_delay = 0.1 SECONDS + recoil_unwielded = 3 + + gun_firenames = list(FIREMODE_FULLAUTO = "full auto ballistic", FIREMODE_OTHER = "full auto laser") + gun_firemodes = list(FIREMODE_FULLAUTO, FIREMODE_OTHER) + default_firemode = FIREMODE_OTHER + + weapon_weight = WEAPON_MEDIUM + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + show_magazine_on_sprite = TRUE + ammo_counter = TRUE + empty_indicator = TRUE + fire_sound = 'sound/weapons/gun/laser/e40_bal.ogg' + manufacturer = MANUFACTURER_EOEHOMA + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 45, + "y" = 20, + ), + ) + +/obj/item/gun/ballistic/automatic/assault/e40/Initialize() + . = ..() + secondary = new /obj/item/gun/energy/laser/e40_laser_secondary(src) + RegisterSignal(secondary, COMSIG_ATOM_UPDATE_ICON, PROC_REF(secondary_update_icon)) + SEND_SIGNAL(secondary, COMSIG_GUN_DISABLE_AUTOFIRE) + update_appearance() + +/obj/item/gun/ballistic/automatic/assault/e40/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/eoehoma) // at long last... the ammo counter on the side of the sprite is functional... + +/obj/item/gun/ballistic/automatic/assault/e40/do_autofire(datum/source, atom/target, mob/living/shooter, params) + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode != FIREMODE_OTHER) + return ..() + return secondary.do_autofire(source, target, shooter, params) + +/obj/item/gun/ballistic/automatic/assault/e40/do_autofire_shot(datum/source, atom/target, mob/living/shooter, params) + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode != FIREMODE_OTHER) + return ..() + return secondary.do_autofire_shot(source, target, shooter, params) + +/obj/item/gun/ballistic/automatic/assault/e40/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread) + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode != FIREMODE_OTHER) + if(!secondary.latch_closed && secondary.cell && prob(65)) + to_chat(user, span_warning("[src]'s cell falls out!")) + secondary.eject_cell() + return ..() + return secondary.process_fire(target, user, message, params, zone_override, bonus_spread) + +/obj/item/gun/ballistic/automatic/assault/e40/can_shoot() + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode != FIREMODE_OTHER) + return ..() + return secondary.can_shoot() + +/obj/item/gun/ballistic/automatic/assault/e40/afterattack(atom/target, mob/living/user, flag, params) + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode != FIREMODE_OTHER) + return ..() + return secondary.afterattack(target, user, flag, params) + +/obj/item/gun/ballistic/automatic/assault/e40/attackby(obj/item/attack_obj, mob/user, params) + if(istype(attack_obj, /obj/item/stock_parts/cell/gun)) + return secondary.attackby(attack_obj, user, params) + return ..() + +/obj/item/gun/ballistic/automatic/assault/e40/attack_hand(mob/user) + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode == FIREMODE_OTHER && loc == user && user.is_holding(src) && secondary.cell && !secondary.latch_closed) + secondary.eject_cell(user) + return + if(current_firemode == FIREMODE_OTHER && loc == user && user.is_holding(src) && secondary.cell && secondary.latch_closed) + to_chat(user, span_warning("The cell retainment clip is latched!")) + return + return ..() + +/obj/item/gun/ballistic/automatic/assault/e40/unique_action(mob/living/user) + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode == FIREMODE_OTHER) + if(secondary.latch_closed) + to_chat(user, span_notice("You start to unlatch the [src]'s power cell retainment clip...")) + if(do_after(user, secondary.latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE)) + to_chat(user, span_notice("You unlatch [src]'s power cell retainment clip " + span_red("OPEN") + ".")) + playsound(src, 'sound/items/taperecorder/taperecorder_play.ogg', 50, FALSE) + secondary.tac_reloads = TRUE + secondary.latch_closed = FALSE + update_appearance() + return + else + to_chat(user, span_warning("You start to latch the [src]'s power cell retainment clip...")) + if (do_after(user, secondary.latch_toggle_delay, src, IGNORE_USER_LOC_CHANGE)) + to_chat(user, span_notice("You latch [src]'s power cell retainment clip " + span_green("CLOSED") + ".")) + playsound(src, 'sound/items/taperecorder/taperecorder_close.ogg', 50, FALSE) + secondary.tac_reloads = FALSE + secondary.latch_closed = TRUE + update_appearance() + return + else + return ..() + +/obj/item/gun/ballistic/automatic/assault/e40/on_wield(obj/item/source, mob/user) + wielded = TRUE + secondary.wielded = TRUE + INVOKE_ASYNC(src, PROC_REF(do_wield), user) + +/obj/item/gun/ballistic/automatic/assault/e40/do_wield(mob/user) + . = ..() + secondary.wielded_fully = wielded_fully + +/// triggered on unwield of two handed item +/obj/item/gun/ballistic/automatic/assault/e40/on_unwield(obj/item/source, mob/user) + . = ..() + secondary.wielded_fully = FALSE + secondary.wielded = FALSE + + +/obj/item/gun/ballistic/automatic/assault/e40/proc/secondary_update_icon() + update_appearance() + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + +/obj/item/gun/ballistic/automatic/assault/e40/process_other(atom/target, mob/living/user, message = TRUE, flag, params = null, zone_override = "", bonus_spread = 0) + secondary.pre_fire(target, user, message, flag, params, zone_override, bonus_spread) + + +/obj/item/gun/ballistic/automatic/assault/e40/get_cell() + return secondary.get_cell() + +/obj/item/gun/ballistic/automatic/assault/e40/update_overlays() + . = ..() + //handle laser gunn overlays + if(!secondary) + return + var/ratio = secondary.get_charge_ratio() + if(ratio == 0) + . += "[icon_state]_chargeempty" + else + . += "[icon_state]_charge[ratio]" + if(secondary.cell) + . += "[icon_state]_cell" + if(ismob(loc)) + var/mutable_appearance/latch_overlay + latch_overlay = mutable_appearance('icons/obj/guns/cell_latch.dmi') + if(secondary.latch_closed) + if(secondary.cell) + latch_overlay.icon_state = "latch-on-full" + else + latch_overlay.icon_state = "latch-on-empty" + else + if(secondary.cell) + latch_overlay.icon_state = "latch-off-full" + else + latch_overlay.icon_state = "latch-off-empty" + . += latch_overlay + + +/obj/item/gun/ballistic/automatic/assault/e40/toggle_safety(mob/user, silent=FALSE) + . = ..() + secondary.safety = safety + +/obj/item/gun/ballistic/automatic/assault/e40/fire_select(mob/living/carbon/human/user) + . = ..() + var/current_firemode = gun_firemodes[firemode_index] + if(current_firemode == FIREMODE_OTHER) + SEND_SIGNAL(src, COMSIG_GUN_ENABLE_AUTOFIRE) + SEND_SIGNAL(src, COMSIG_GUN_SET_AUTOFIRE_SPEED, secondary.fire_delay) + else + SEND_SIGNAL(src, COMSIG_GUN_SET_AUTOFIRE_SPEED, fire_delay) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + +/obj/item/gun/ballistic/automatic/assault/e40/examine(mob/user) + . = ..() + if(!secondary.internal_magazine) + . += "The cell retainment latch is [secondary.latch_closed ? span_green("CLOSED") : span_red("OPEN")]. Use the Unique Action Key to toggle the latch while on laser mode. By default, this is space." + var/obj/item/ammo_casing/energy/shot = secondary.ammo_type[select] + if(secondary.cell) + . += "\The [name]'s cell has [secondary.cell.percent()]% charge remaining." + . += "\The [name] has [round(secondary.cell.charge/shot.e_cost)] shots remaining on [shot.select_name] mode." + else + . += span_notice("\The [name] doesn't seem to have a cell!") + +/obj/item/ammo_box/magazine/e40 + name = "E-40 magazine (.299 Eoehoma caseless)" + icon_state = "e40_mag-1" + base_icon_state = "e40_mag" + ammo_type = /obj/item/ammo_casing/caseless/c299 + caliber = ".299 caseless" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +//laser + +/obj/item/gun/energy/laser/e40_laser_secondary + name = "secondary e40 laser gun" + desc = "The laser component of a E-40 Hybrid Rifle. You probably shouldn't see this. If you can though, you should probably know lorewise, this is primary, the ballistic compontent in universe is secondary. Unfortunately, we cannot simulate this, So codewise this is secondary." + fire_sound = 'sound/weapons/gun/laser/e40_las.ogg' + w_class = WEIGHT_CLASS_NORMAL + ammo_type = list(/obj/item/ammo_casing/energy/laser/assault) + fire_delay = 0.2 SECONDS + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + latch_toggle_delay = 0.6 SECONDS + valid_attachments = list() + + spread_unwielded = 20 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/lasers.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/lasers.dm new file mode 100644 index 0000000000..f04797a392 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/eoehoma/lasers.dm @@ -0,0 +1,90 @@ +/obj/item/gun/energy/laser/e10 + name = "E-10 laser pistol" + desc = "A very old laser weapon. Despite the extreme age of some of these weapons, they are sometimes preferred to newer, mass-produced Nanotrasen laser weapons." + icon = 'icons/obj/guns/manufacturer/eoehoma/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/eoehoma/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/eoehoma/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/eoehoma/onmob.dmi' + icon_state = "e10" + item_state = "e_generickill4" + w_class = WEIGHT_CLASS_SMALL + + wield_delay = 0.2 SECONDS + wield_slowdown = LASER_PISTOL_SLOWDOWN + + spread = 6 + spread_unwielded = 10 + + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/eoehoma, /obj/item/ammo_casing/energy/lasergun/eoehoma/heavy) + manufacturer = MANUFACTURER_EOEHOMA + + +/obj/item/gun/energy/e_gun/e11 + name = "E-11 hybrid energy rifle" + desc = "A hybrid energy gun fondly remembered as one of the worst weapons ever made. It hurts, but that's only if it manages to hit its target." + icon = 'icons/obj/guns/manufacturer/eoehoma/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/eoehoma/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/eoehoma/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/eoehoma/onmob.dmi' + icon_state = "e11" + item_state = "e_generickill4" + + ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/eoehoma) + ammo_x_offset = 0 + spread = 30 + spread_unwielded = 40 + dual_wield_spread = 40 + shaded_charge = TRUE + manufacturer = MANUFACTURER_EOEHOMA + +/obj/item/gun/energy/e_gun/e11/empty_cell + spawn_no_ammo = TRUE + + +/obj/item/gun/energy/laser/e50 + name = "E-50 energy emitter" + desc = "A heavy and extremely powerful laser. Sets targets on fire and kicks ass, but it uses a massive amount of energy per shot and is generally awkward to handle." + + icon = 'icons/obj/guns/manufacturer/eoehoma/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/eoehoma/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/eoehoma/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/eoehoma/onmob.dmi' + icon_state = "e50" + item_state = "e50" + + default_ammo_type = /obj/item/stock_parts/cell/gun/large + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/large, + ) + ammo_type = list(/obj/item/ammo_casing/energy/laser/eoehoma/e50) + weapon_weight = WEAPON_HEAVY + w_class = WEIGHT_CLASS_BULKY + manufacturer = MANUFACTURER_EOEHOMA + + wield_delay = 0.7 SECONDS + wield_slowdown = LASER_SNIPER_SLOWDOWN + spread_unwielded = 20 + + shaded_charge = FALSE + ammo_x_offset = 4 + charge_sections = 2 + slot_flags = 0 + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + +/obj/item/gun/energy/disabler/e60 + name = "E-60 personal defense disabler" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." + icon = 'icons/obj/guns/manufacturer/eoehoma/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/eoehoma/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/eoehoma/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/eoehoma/onmob.dmi' + icon_state = "e60" + item_state = "e_genericdisable4" + + shaded_charge = TRUE + manufacturer = MANUFACTURER_EOEHOMA diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm new file mode 100644 index 0000000000..7875b01074 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm @@ -0,0 +1,393 @@ +/obj/item/gun/energy/kalix + name = "\improper Etherbor BG-12" + desc = "Etherbor Industries's current civilian energy weapon model. The BG-12 energy beam gun is identical to the military model, minus the removal of the full auto mode. Otherwise, it's no different from older hunting beams from Kalixcis's history." + icon_state = "kalixgun" + item_state = "kalixgun" + icon = 'icons/obj/guns/manufacturer/etherbor/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/etherbor/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/etherbor/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/etherbor/onmob.dmi' + w_class = WEIGHT_CLASS_BULKY + + modifystate = TRUE + + fire_delay = 0.16 SECONDS + + wield_delay = 0.7 SECONDS + wield_slowdown = LASER_SMG_SLOWDOWN + + default_ammo_type = /obj/item/stock_parts/cell/gun/kalix + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + ammo_type = list(/obj/item/ammo_casing/energy/kalix, /obj/item/ammo_casing/energy/disabler/hitscan) + + load_sound = 'sound/weapons/gun/gauss/pistol_reload.ogg' + + manufacturer = MANUFACTURER_PGF + +/obj/item/ammo_casing/energy/kalix + projectile_type = /obj/projectile/beam/hitscan/kalix + fire_sound = 'sound/weapons/gun/energy/kalixsmg.ogg' + e_cost = 666 //30 shots per cell + delay = 1 + +/obj/projectile/beam/hitscan/kalix + name = "concentrated energy beam" + tracer_type = /obj/effect/projectile/tracer/kalix + muzzle_type = /obj/effect/projectile/muzzle/kalix + impact_type = /obj/effect/projectile/impact/kalix + hitscan_light_color_override = LIGHT_COLOR_ELECTRIC_CYAN + muzzle_flash_color_override = LIGHT_COLOR_ELECTRIC_CYAN + impact_light_color_override = LIGHT_COLOR_ELECTRIC_CYAN + range = 12 + damage_constant = 0.8 + damage = 25 + armour_penetration = -10 + +/obj/item/gun/energy/kalix/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/projectile/beam/hitscan/kalix/nock + name = "concentrated energy beam" + damage_constant = 0.8 + damage = 15 + armour_penetration = -20 + +/obj/item/ammo_casing/energy/kalix/nock + projectile_type = /obj/projectile/beam/hitscan/kalix/nock + fire_sound = 'sound/weapons/gun/energy/kalixsmg.ogg' + e_cost = 312 + select_name = "kill" + +/obj/item/ammo_casing/energy/disabler/hitscan/kalix/nock + projectile_type = /obj/projectile/beam/hitscan/disabler + fire_sound = 'sound/weapons/gun/energy/kalixsmg.ogg' + e_cost = 312 + select_name = "disable" + +/obj/item/gun/energy/kalix/nock + name = "\improper Etherbor VG-F3" + desc = "The Etherbor Industries VG-F3 is a further refinement of the antiquated beam volleygun concept. The first of its kind to find commercial success in the modern day, the VG-F3 has found a comfortable niche with law enforcement and home defense markets galaxy-wide." + icon_state = "kalixnock" + item_state = "kalixnock" + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_SUITSTORE + + modifystate = TRUE + + gun_firemodes = list(FIREMODE_BURST) + default_firemode = FIREMODE_BURST + + randomspread = TRUE + burst_size = 4 + burst_delay = 0.1 SECONDS + fire_delay = 0.7 SECONDS + + wield_delay = 0.7 SECONDS + wield_slowdown = LASER_SMG_SLOWDOWN + + spread = 10 + spread_unwielded = 15 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + default_ammo_type = /obj/item/stock_parts/cell/gun/kalix + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + ammo_type = list(/obj/item/ammo_casing/energy/kalix/nock, /obj/item/ammo_casing/energy/disabler/hitscan/kalix/nock) + +/obj/item/gun/energy/kalix/nock/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/projectile/beam/hitscan/kalix/pgf/nock + name = "concentrated energy beam" + damage_constant = 0.8 + damage = 15 + armour_penetration = -20 + +/obj/item/ammo_casing/energy/kalix/pgf/nock + projectile_type = /obj/projectile/beam/hitscan/kalix/pgf/nock + fire_sound = 'sound/weapons/gun/energy/kalixrifle.ogg' + e_cost = 250 + select_name = "kill" + +/obj/item/ammo_casing/energy/disabler/hitscan/kalix/pgf/nock + projectile_type = /obj/projectile/beam/hitscan/disabler + fire_sound = 'sound/weapons/gun/energy/kalixrifle.ogg' + e_cost = 250 + select_name = "disable" + +/obj/item/gun/energy/kalix/pgf/nock + name = "\improper Etherbor VG-A5" + desc = "Piggybacking off the success of the VG-F3, the Etherbor Industries VG-A5 Beam Volleygun was designed specifically for contract sale to the PGFMC. With the addition of a stronger capacitor and a forward grip, the VG-A5 has found itself popular among marine raiders for its ability to take control of tight spaces." + icon_state = "pgfnock" + item_state = "pgfnock" + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_SUITSTORE + + modifystate = TRUE + + gun_firemodes = list(FIREMODE_BURST) + default_firemode = FIREMODE_BURST + + randomspread = TRUE + burst_size = 5 + burst_delay = 0.1 SECONDS + fire_delay = 0.7 SECONDS + + wield_delay = 0.5 SECONDS + wield_slowdown = LASER_SMG_SLOWDOWN + + spread = 10 + spread_unwielded = 15 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + default_ammo_type = /obj/item/stock_parts/cell/gun/pgf + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + ammo_type = list(/obj/item/ammo_casing/energy/kalix/pgf/nock, /obj/item/ammo_casing/energy/disabler/hitscan/kalix/pgf/nock) + +/obj/item/gun/energy/kalix/pgf/nock/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/item/gun/energy/kalix/pgf + name = "\improper Etherbor BG-16" + desc = "The BG-16 is the military-grade beam gun designed and manufactured by Etherbor Industries as the standard-issue close-range weapon of the PGF." + icon_state = "pgfgun" + item_state = "pgfgun" + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + wield_delay = 0.7 SECONDS + wield_slowdown = LASER_SMG_SLOWDOWN + + default_ammo_type = /obj/item/stock_parts/cell/gun/pgf + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + ammo_type = list(/obj/item/ammo_casing/energy/kalix/pgf , /obj/item/ammo_casing/energy/disabler/hitscan) + +/obj/item/gun/energy/kalix/pgf/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/projectile/beam/hitscan/kalix/pgf + name = "concentrated energy beam" + tracer_type = /obj/effect/projectile/tracer/pgf + muzzle_type = /obj/effect/projectile/muzzle/pgf + impact_type = /obj/effect/projectile/impact/pgf + hitscan_light_color_override = LIGHT_COLOR_ELECTRIC_GREEN + muzzle_flash_color_override = LIGHT_COLOR_ELECTRIC_GREEN + impact_light_color_override = LIGHT_COLOR_ELECTRIC_GREEN + +/obj/item/ammo_casing/energy/kalix/pgf + projectile_type = /obj/projectile/beam/hitscan/kalix/pgf + fire_sound = 'sound/weapons/gun/energy/kalixsmg.ogg' + e_cost = 666 //30 shots per cell + delay = 1 + +/obj/item/gun/energy/kalix/pistol //blue + name = "\improper Etherbor SG-8" + desc = "Etherbor's current and sidearm offering. While marketed for the military, it's also available for civillians as an upgrade over older and obsolete beam pistols." + icon_state = "kalixpistol" + item_state = "kalixpistol" + w_class = WEIGHT_CLASS_NORMAL + modifystate = FALSE + + wield_delay = 0.2 SECONDS + wield_slowdown = LASER_PISTOL_SLOWDOWN + + spread = 2 + spread_unwielded = 5 + + default_ammo_type = /obj/item/stock_parts/cell/gun/kalix + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + ammo_type = list(/obj/item/ammo_casing/energy/kalix/pistol) + + + load_sound = 'sound/weapons/gun/gauss/pistol_reload.ogg' + + //refused_attachments = list( + // /obj/item/attachment/gun, + // ) + +/obj/item/ammo_casing/energy/kalix/pistol + fire_sound = 'sound/weapons/gun/energy/kalixpistol.ogg' + e_cost = 1250 //10 shots per cell + delay = 0 + +/obj/item/gun/energy/kalix/pistol/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/item/gun/energy/kalix/pgf/medium + name = "\improper Etherbor BGC-10" + desc = "Etherbor's answer to the PGFMC's request for a carbine style weapon; the BGC-10 offers greater accuracy and power than the BG-16, while being less cumbersome than the DMR mode equipped HBG series rifles." + icon_state = "pgfmedium" + item_state = "pgfmedium" + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + modifystate = TRUE + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + fire_delay = 0.2 SECONDS + + wield_delay = 0.7 SECONDS + wield_slowdown = LASER_RIFLE_SLOWDOWN + + spread = 0.5 + spread_unwielded = 15 + + ammo_type = list(/obj/item/ammo_casing/energy/pgf/assault, /obj/item/ammo_casing/energy/disabler/hitscan/heavy) + +/obj/item/gun/energy/kalix/pgf/medium/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/item/gun/energy/kalix/pgf/heavy + name = "\improper Etherbor HBG-7" + desc = "The HBG-7 is the standard-issue rifle weapon of the PGF. It comes with a special DMR mode that has greater armor piercing for dealing with armored targets." + icon_state = "pgfheavy" + item_state = "pgfheavy" + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + modifystate = FALSE + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + fire_delay = 0.2 SECONDS + + wield_delay = 0.7 SECONDS + wield_slowdown = HEAVY_LASER_RIFLE_SLOWDOWN + + spread = 0 + spread_unwielded = 20 + + ammo_type = list(/obj/item/ammo_casing/energy/pgf/assault, /obj/item/ammo_casing/energy/pgf/sniper) + +/obj/item/gun/energy/kalix/pgf/heavy/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) + +/obj/item/ammo_casing/energy/pgf/assault + select_name = "AR" + projectile_type = /obj/projectile/beam/hitscan/kalix/pgf/assault + fire_sound = 'sound/weapons/gun/energy/kalixrifle.ogg' + e_cost = 1000 //20 shots per cell + delay = 1 + +/obj/projectile/beam/hitscan/kalix/pgf/assault + tracer_type = /obj/effect/projectile/tracer/pgf/rifle + muzzle_type = /obj/effect/projectile/muzzle/pgf/rifle + impact_type = /obj/effect/projectile/impact/pgf/rifle + damage = 25 //bar + armour_penetration = 20 + range = 14 + damage_constant = 0.9 + +/obj/item/ammo_casing/energy/pgf/sniper + select_name = "DMR" + projectile_type = /obj/projectile/beam/hitscan/kalix/pgf/sniper + fire_sound = 'sound/weapons/gun/laser/heavy_laser.ogg' + e_cost = 2000 //20 shots per cell + delay = 6 + +/obj/projectile/beam/hitscan/kalix/pgf/sniper + tracer_type = /obj/effect/projectile/tracer/laser/emitter + muzzle_type = /obj/effect/projectile/muzzle/laser/emitter + impact_type = /obj/effect/projectile/impact/laser/emitter + + damage = 35 + armour_penetration = 40 + range = 20 + damage_constant = 1 + +/obj/item/gun/energy/kalix/pgf/heavy/sniper + name = "\improper Etherbor HBG-7L" + desc = "HBG-7 with a longer barrel and scope. Intended to get the best use out of the DMR mode, it suffers from longer wield times and slowdown, but it's longer barrel makes it ideal for accuracy." + icon_state = "pgfheavy_sniper" + item_state = "pgfheavy_sniper" + + zoomable = TRUE + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + + spread = -5 + spread_unwielded = 40 + + wield_slowdown = LASER_SNIPER_SLOWDOWN + wield_delay = 1.3 SECONDS + +/obj/item/gun/energy/kalix/pgf/heavy/sniper/empty_cell + spawn_no_ammo = TRUE + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/kalix, + /obj/item/stock_parts/cell/gun/pgf, + /obj/item/stock_parts/cell/gun/kalix/empty, + /obj/item/stock_parts/cell/gun/pgf/empty, + ) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm new file mode 100644 index 0000000000..212d4453e1 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm @@ -0,0 +1,595 @@ +/obj/item/gun/ballistic/automatic/pistol/mauler + name = "Mauler machine pistol" + desc = "An automatic machine pistol originating from the Shoal. Impressive volume of fire with high recoil, lackluster armor penetration, and limited magazine size render it difficult to use outside of close quarters. Chambered in 9x18mm." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "mauler_mp" + item_state = "hp_generic" + default_ammo_type = /obj/item/ammo_box/magazine/m9mm_mauler/extended + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m9mm_mauler, + /obj/item/ammo_box/magazine/m9mm_mauler/extended, + ) + fire_delay = 0.06 SECONDS + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + show_magazine_on_sprite = FALSE //hard coded + + spread = 15 + spread_unwielded = 30 + recoil = 1 + recoil_unwielded = 3 + safety_multiplier = 2 //this means its twice as safe right? //oh, god no. + + fire_sound = 'sound/weapons/gun/pistol/mauler.ogg' + + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + + load_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + + wear_rate = 2 + wear_minor_threshold = 240 + wear_major_threshold = 720 + wear_maximum = 1200 + + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 44, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 20, + "y" = 19, + ) + ) + +/obj/item/gun/ballistic/automatic/pistol/mauler/update_overlays() + . = ..() + if (magazine) + . += "mauler_mag_[magazine.base_icon_state]" + +/obj/item/gun/ballistic/automatic/pistol/mauler/regular + name = "Mauler pistol" + desc = "A toned down semi-auto version of the Mauler. Still fast to fire still with better accuracy than it's auto counterpart, but it's still innaccurate compared to most modern pistols. Chambered in 9mm." + + icon_state = "mauler" + + spread = 8 + spread_unwielded = 15 + recoil = 0 + recoil_unwielded = 4 + default_ammo_type = /obj/item/ammo_box/magazine/m9mm_mauler + + fire_delay = 0.12 SECONDS + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 28, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 20, + "y" = 19, + ) + ) + + +/obj/item/ammo_box/magazine/m9mm_mauler + name = "mauler pistol magazine (9x18mm)" + desc = "A 8-round magazine designed for the Mauler pistol." + icon_state = "mauler_mag-1" + base_icon_state = "mauler_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 8 + +/obj/item/ammo_box/magazine/m9mm_mauler/extended + name = "mauler machine pistol extended magazine (9x18mm)" + desc = "A 12-round magazine designed for the Mauler machine pistol." + icon_state = "mauler_extended_mag-1" + base_icon_state = "mauler_extended_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 12 + +/obj/item/ammo_box/magazine/m9mm_mauler/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/gun/ballistic/automatic/pistol/spitter + name = "\improper Spitter" + desc = "An open-bolt submachine gun favored by the Frontiersmen. This design's origins are unclear, but its simple, robust design has been widely copied throughout the Frontier, and it is stereotypically used by pirates and various criminal groups that value low price and ease of concealment. Chambered in 9x18mm." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "spitter" + item_state = "spitter" + default_ammo_type = /obj/item/ammo_box/magazine/spitter_9mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/spitter_9mm, + ) + bolt_type = BOLT_TYPE_OPEN + weapon_weight = WEAPON_LIGHT + show_magazine_on_sprite = TRUE + manufacturer = MANUFACTURER_IMPORT + + spread = 20 + spread_unwielded = 35 + dual_wield_spread = 35 + wield_slowdown = SMG_SLOWDOWN + wield_delay = 0.2 SECONDS + fire_delay = 0.09 SECONDS + safety_multiplier = 2 + + fire_sound = 'sound/weapons/gun/smg/spitter.ogg' + rack_sound = 'sound/weapons/gun/smg/spitter_cocked.ogg' + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + load_sound = 'sound/weapons/gun/smg/spitter_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/spitter_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/spitter_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/spitter_unload.ogg' + + wear_rate = 0.8 + wear_minor_threshold = 240 + wear_major_threshold = 720 + wear_maximum = 1200 + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/foldable_stock/spitter + ) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_STOCK = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 32, + "y" = 23, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = -5, + "y" = 18, + ) + ) + + default_attachments = list(/obj/item/attachment/foldable_stock/spitter) + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + + +/obj/item/ammo_box/magazine/spitter_9mm + name = "spitter pistol magazine (9x18mm)" + desc = "A thin 30-round magazine for the Spitter submachine gun." + icon_state = "spitter_mag-1" + base_icon_state = "spitter_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 30 + +/obj/item/ammo_box/magazine/spitter_9mm/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + + +/obj/item/gun/ballistic/automatic/smg/pounder + name = "Pounder" + desc = "An unusual submachine gun of Frontiersman make. A miniscule cartridge lacking both stopping power and armor penetration is compensated for with best-in-class ammunition capacity and cycle rate. Chambered in .22 LR." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + w_class = WEIGHT_CLASS_BULKY //this gun is visually larger, so I believe this is good + + icon_state = "pounder" + item_state = "pounder" + default_ammo_type = /obj/item/ammo_box/magazine/c22lr_pounder_pan + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/c22lr_pounder_pan, + ) + burst_size = 1 + fire_delay = 0.05 SECONDS + spread = 25 + spread_unwielded = 50 + + fire_sound = 'sound/weapons/gun/smg/pounder.ogg' + rack_sound = 'sound/weapons/gun/smg/pounder_cocked.ogg' + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + load_sound = 'sound/weapons/gun/smg/pounder_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/pounder_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/pounder_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/pounder_unload.ogg' + + wear_rate = 1 + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + manufacturer = MANUFACTURER_IMPORT + wield_slowdown = SMG_SLOWDOWN + safety_multiplier = 2 + + //refused_attachments = list(/obj/item/attachment/gun) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 46, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 40, + "y" = 17, + ) + ) + + +/obj/item/ammo_box/magazine/c22lr_pounder_pan + name = "pan magazine (.22 LR)" + desc = "A 50-round pan magazine for the Pounder submachine gun." + icon_state = "firestorm_pan" + base_icon_state = "firestorm_pan" + ammo_type = /obj/item/ammo_casing/c22lr + caliber = "22lr" + max_ammo = 50 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/c22lr_pounder_pan/update_icon_state() + . = ..() + icon_state = "firestorm_pan" + +/obj/item/gun/ballistic/automatic/hmg/shredder + name = "\improper Shredder" + desc = "A vastly atypical heavy machine gun, extensively modified by the Frontiersmen. Additional grips have been added to enable firing from the hip, and it has been modified to fire belts of shotgun shells. Chambered in 12g." + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "shredder" + item_state = "shredder" + default_ammo_type = /obj/item/ammo_box/magazine/m12_shredder + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m12_shredder, + ) + spread = 15 + recoil = 2 + recoil_unwielded = 7 + fire_delay = 0.16 SECONDS + mag_display_ammo = TRUE + + bolt_type = BOLT_TYPE_STANDARD + show_magazine_on_sprite = TRUE + show_magazine_on_sprite_ammo = TRUE + tac_reloads = FALSE + fire_sound = 'sound/weapons/gun/hmg/shredder.ogg' + rack_sound = 'sound/weapons/gun/hmg/shredder_cocked_alt.ogg' + + load_sound_vary = FALSE + eject_sound_vary = FALSE + load_sound = 'sound/weapons/gun/hmg/shredder_reload.ogg' + load_empty_sound = 'sound/weapons/gun/hmg/shredder_reload.ogg' + eject_sound = 'sound/weapons/gun/hmg/shredder_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/hmg/shredder_unload.ogg' + + wear_rate = 3 // 20 to malfunction, 60 to critical + + manufacturer = MANUFACTURER_IMPORT + has_bipod = FALSE + + refused_attachments = list(/obj/item/attachment) + + slot_available = list() + +/obj/item/ammo_box/magazine/m12_shredder + name = "belt box (12g)" + desc = "A 40-round belt box for the Shredder heavy machine gun." + icon_state = "shredder_mag-1" + base_icon_state = "shredder_mag" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 40 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/m12_shredder/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/m12_shredder/slug + name = "belt box (12g slug)" + desc = "A 40-round belt box for the Shredder heavy machine gun." + icon_state = "shredder_mag_slug-1" + base_icon_state = "shredder_mag_slug" + ammo_type = /obj/item/ammo_casing/shotgun + caliber = "12ga" + max_ammo = 40 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/gun/ballistic/automatic/hmg/skm_lmg + name = "\improper SKM-24u" + desc = "What appears to be a standard SKM-24 at first glance is actually a light machine gun conversion, with an extended, heavy barrel and overhauled internals. Its weight, bulk, and robust fire rate make it difficult to handle without using the bipod in a prone position or against appropriate cover such as a table. Chambered in 7.62x40mm CLIP." + + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "skm_lmg" + item_state = "skm_lmg" + + fire_sound = 'sound/weapons/gun/rifle/skm.ogg' + rack_sound = 'sound/weapons/gun/rifle/skm_cocked.ogg' + load_sound = 'sound/weapons/gun/rifle/skm_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/skm_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/skm_unload.ogg' + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + show_magazine_on_sprite = TRUE + unique_mag_sprites_for_variants = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + manufacturer = MANUFACTURER_IMPORT + default_ammo_type = /obj/item/ammo_box/magazine/skm_762_40 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/skm_762_40, + ) + + fire_delay = 0.13 SECONDS + + spread = 7 //you can hipfire, but why? + spread_unwielded = 25 + + recoil = 1 //identical to other LMGS + recoil_unwielded = 4 //same as skm + + wield_slowdown = SAW_SLOWDOWN //not as severe as other lmgs, but worse than the normal skm + wield_delay = 0.85 SECONDS //faster than normal lmgs, slower than stock skm + + has_bipod = TRUE + +/obj/item/gun/ballistic/automatic/hmg/mower + name = "\improper Mower" + desc = "A hefty and relatively accurate HMG, the Mower is built for heavy fire support on the move. Chambered in .308." + + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + icon_state = "mower" + item_state = "mower" + + fire_sound = 'sound/weapons/gun/hmg/hmg.ogg' + rack_sound = 'sound/weapons/gun/hmg/cm40_cocked.ogg' + + rack_sound_vary = FALSE + + load_sound_vary = FALSE + eject_sound_vary = FALSE + + load_sound = 'sound/weapons/gun/hmg/cm40_reload.ogg' + load_empty_sound = 'sound/weapons/gun/hmg/cm40_reload.ogg' + eject_sound = 'sound/weapons/gun/hmg/cm40_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/hmg/cm40_unload.ogg' + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_HEAVY + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + manufacturer = MANUFACTURER_IMPORT + default_ammo_type = /obj/item/ammo_box/magazine/mower_lmg_308 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/mower_lmg_308, + ) + + fire_delay = 0.27 SECONDS //quite slow + + spread = 8 + spread_unwielded = 50 + + recoil = 0.5 + recoil_unwielded = 4 + + //low deploy bonuses due already being somewhat better than average undeployed + deploy_spread_bonus = -2 + deploy_recoil_bonus = -0.5 + + has_bipod = TRUE + +/obj/item/ammo_box/magazine/mower_lmg_308 + name = "machine gun drum (.308)" + desc = "A drum shaped, 50-round magazine for the Mower .308 machine gun. These rounds do good damage with excelent armor penetration." + icon_state = "firestorm_pan" + base_icon_state = "firestorm_pan" + ammo_type = /obj/item/ammo_casing/a308 + caliber = ".308" + max_ammo = 50 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/ammo_box/magazine/mower_lmg_308/update_icon_state() + . = ..() + icon_state = "firestorm_pan" + +/obj/item/ammo_box/magazine/mower_lmg_308/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/hmg/mower/before_firing(atom/target, mob/user, params) + . = ..() + if(chambered.BB) + chambered.BB.icon_state = "redtrac" + chambered.BB.light_system = MOVABLE_LIGHT + chambered.BB.set_light_color(COLOR_SOFT_RED) + chambered.BB.set_light_range(2) + +/obj/item/gun/ballistic/automatic/hmg/skm_lmg/ComponentInitialize() + . = ..() + AddElement(/datum/element/update_icon_updates_onmob) + +/obj/item/gun/ballistic/automatic/hmg/skm_lmg/extended //spawns with the proper extended magazine, for erts + default_ammo_type = /obj/item/ammo_box/magazine/skm_762_40/extended + +/obj/item/gun/ballistic/automatic/hmg/skm_lmg/drum_mag //spawns with a drum, maybe not for erts but admin enhanced ERTS? when things really go to shit + default_ammo_type = /obj/item/ammo_box/magazine/skm_762_40/drum + +/obj/item/gun/ballistic/rocketlauncher/oneshot + name = "\improper Hammer" + desc = "A disposable rocket-propelled grenade launcher loaded with a standard HE shell." + + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + base_icon_state = "rpg" + icon_state = "rpg" + item_state = "rpg" + + default_ammo_type = /obj/item/ammo_box/magazine/internal/rocketlauncher/oneshot + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/rocketlauncher/oneshot, + ) + fire_sound = 'sound/weapons/gun/general/rocket_launch.ogg' + load_sound = 'sound/weapons/gun/general/rocket_load.ogg' + weapon_weight = WEAPON_HEAVY + bolt_type = BOLT_TYPE_NO_BOLT + + cartridge_wording = "rocket" + empty_indicator = FALSE + sealed_magazine = TRUE + manufacturer = MANUFACTURER_IMPORT + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + has_safety = FALSE + safety = FALSE + + safety_multiplier = 0 + +/obj/item/gun/ballistic/rocketlauncher/oneshot/hedp + name = "\improper Hammer-DP" + desc = "A disposable rocket-propelled grenade launcher loaded with an HEDP shell for Direct Penetration of your target." + + default_ammo_type = /obj/item/ammo_box/magazine/internal/rocketlauncher/oneshot/hedp + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/rocketlauncher/oneshot/hedp, + ) + +/obj/item/gun/ballistic/rocketlauncher/oneshot/Initialize() + . = ..() + if(prob(1)) + name = "\improper Mallet" + +/obj/item/gun/ballistic/rocketlauncher/oneshot/examine(mob/user) + . = ..() + if(!chambered) + . += span_warning("It has been spent, and is now useless.") + +/obj/item/ammo_box/magazine/internal/rocketlauncher/oneshot + name = "oneshot rocket launcher magazine" + ammo_type = /obj/item/ammo_casing/caseless/rocket + caliber = "84mm" + max_ammo = 1 + +/obj/item/ammo_box/magazine/internal/rocketlauncher/oneshot/hedp + name = "oneshot rocket launcher magazine" + ammo_type = /obj/item/ammo_casing/caseless/rocket/hedp + caliber = "84mm" + max_ammo = 1 + +/obj/item/gun/ballistic/shotgun/automatic/slammer + name = "\improper Slammer" + desc = "An unusual, dated riot shotgun originating from Lanchester City. Often in the hands of pirates and eccentric indies, this weapon is mag-fed and pump-action." + + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + fire_sound = 'sound/weapons/gun/shotgun/brimstone.ogg' + load_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + + icon_state = "slammer" + item_state = "slammer" + + manufacturer = MANUFACTURER_IMPORT + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + semi_auto = FALSE + tac_reloads = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + show_magazine_on_sprite = TRUE + internal_magazine = FALSE + casing_ejector = TRUE + + fire_delay = 0.1 SECONDS + rack_delay = 0.1 SECONDS + + default_ammo_type = /obj/item/ammo_box/magazine/m12g_slammer + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m12g_slammer, + ) + + spread = 3 + spread_unwielded = 10 + + recoil = 2 + recoil_unwielded = 6 + + wield_slowdown = SHOTGUN_SLOWDOWN + wield_delay = 0.4 SECONDS + +/obj/item/ammo_box/magazine/m12g_slammer + name = "slammer box magazine (12g buckshot)" + desc = "A single-stack, 6-round box magazine for the Slammer shotgun and it's derivatives." + icon_state = "slammer_mag-1" + base_icon_state = "slammer_mag" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 6 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m12g_slammer/empty + start_empty = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/lasers.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/lasers.dm new file mode 100644 index 0000000000..53c8067b10 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/lasers.dm @@ -0,0 +1,44 @@ +/obj/item/gun/energy/laser/wasp + name = "Wasp" + desc = "While the E-40 was the finest laser rifle of it's day, many have failed over the years to time. This is the New Frontiersmen solution to that problem. Removing the ballistic parts while keeping the internal electronics. They also take modern NT power cells in exchange for a weaker lens." + icon_state = "wasp" + item_state = "wasp" + + icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/frontier_import/onmob.dmi' + + fire_sound = 'sound/weapons/laser4.ogg' + w_class = WEIGHT_CLASS_NORMAL + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/eoehoma/wasp) + fire_delay = 0.1 SECONDS + + spread = 12 + spread_unwielded = 25 + wield_slowdown = SMG_SLOWDOWN + manufacturer = MANUFACTURER_IMPORT + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + latch_toggle_delay = 0.8 SECONDS + valid_attachments = list( + /obj/item/attachment/foldable_stock/wasp + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_STOCK = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 33, + "y" = 18, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = 0, + "y" = 0, + ) + ) + + default_attachments = list(/obj/item/attachment/foldable_stock/wasp) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm new file mode 100644 index 0000000000..0c58a2bbb7 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm @@ -0,0 +1,1259 @@ +///Hunters Pride Weapons + +///Revolvers + +/obj/item/gun/ballistic/revolver/montagne + name = "\improper HP Montagne" + desc = "An ornate break-open revolver issued to high-ranking members of the Saint-Roumain Militia. Chambered in .44." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + icon_state = "montagne" + item_state = "hp_generic" + manufacturer = MANUFACTURER_HUNTERSPRIDE + spread_unwielded = 8 + recoil = 0 + + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder/rev44/montagne + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder/rev44/montagne, + ) + +/obj/item/gun/ballistic/revolver/montagne/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/revolver) + +EMPTY_GUN_HELPER(revolver/montagne) + +/obj/item/gun/ballistic/revolver/ashhand + name = "HP Ashhand" + desc = "A massive, long-barreled revolver often used by the Saint-Roumain Militia as protection against big game. Can only be reloaded one cartridge at a time due to its reinforced frame. Uses .45-70 ammo." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + icon_state = "ashhand" + item_state = "ashhand" + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder/rev4570 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder/rev4570, + ) + fire_sound = 'sound/weapons/gun/revolver/shot_hunting.ogg' + rack_sound = 'sound/weapons/gun/revolver/viper_prime.ogg' + manufacturer = MANUFACTURER_HUNTERSPRIDE + gate_loaded = TRUE + fire_delay = 0.6 SECONDS + wield_slowdown = HEAVY_REVOLVER_SLOWDOWN + spread_unwielded = 20 + spread = 6 + recoil = 2 + recoil_unwielded = 4 + +/obj/item/gun/ballistic/revolver/ashhand/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/revolver) + +/obj/item/gun/ballistic/revolver/firebrand + name = "\improper HP Firebrand" + desc = "An archaic precursor to revolver-type firearms, this gun was rendered completely obsolete millennia ago. While fast to fire, it is extremely inaccurate. Uses .357 ammo." + icon_state = "pepperbox" + item_state = "hp_generic_fresh" + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder/pepperbox + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder/pepperbox, + ) + spread = 20 + manufacturer = MANUFACTURER_HUNTERSPRIDE + spread_unwielded = 50 + fire_delay = 0 SECONDS + gate_offset = 4 + semi_auto = TRUE + safety_wording = "safety" + +EMPTY_GUN_HELPER(revolver/firebrand) + +/obj/item/gun/ballistic/revolver/shadow + name = "\improper HP Shadow" + desc = "A mid-size revolver. Despite the antiquated design, it is cheap, reliable, and stylish, making it a favorite among fast-drawing spacers and the officers of various militaries, as well as small-time police units. Chambered in .44." + fire_sound = 'sound/weapons/gun/revolver/cattleman.ogg' + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + icon_state = "shadow" + item_state = "shadow" + + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder/rev44 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder/rev44, + ) + manufacturer = MANUFACTURER_HUNTERSPRIDE + obj_flags = UNIQUE_RENAME + gate_loaded = TRUE + + unique_reskin = list(\ + "Shadow" = "shadow", + "Cattleman" = "shadow_cattleman", + "General" = "shadow_general", + "Sheriff" = "shadow_sheriff", + "Cobra" = "shadow_cobra", + "Hired Gun" = "shadow_hiredgun", + "Buntline" = "shadow_buntline", + "Cavalry" = "shadow_cavalry", + "Lanchester Special" = "shadow_lanchester" + ) + unique_reskin_changes_inhand = TRUE + + recoil = 0 + spread_unwielded = 8 + +/obj/item/gun/ballistic/revolver/shadow/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/revolver) + +EMPTY_GUN_HELPER(revolver/shadow) + +/obj/item/gun/ballistic/revolver/detective + name = "\improper HP Detective Special" + desc = "A small law enforcement firearm. Originally commissioned by Nanotrasen for their Private Investigation division, it has become extremely popular among independent civilians as a cheap, compact sidearm. Uses .38 Special rounds." + fire_sound = 'sound/weapons/gun/revolver/shot_light.ogg' + icon_state = "detective" + item_state = "hp_generic" + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + default_ammo_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/cylinder/rev38, + ) + obj_flags = UNIQUE_RENAME + semi_auto = TRUE //double action + safety_wording = "safety" + unique_reskin = list("Default" = "detective", + "Stainless Steel" = "detective_stainless", + "Gold Trim" = "detective_gold", + "Leopard Spots" = "detective_leopard", + "The Peacemaker" = "detective_peacemaker", + "Black Panther" = "detective_panther" + ) + w_class = WEIGHT_CLASS_SMALL + manufacturer = MANUFACTURER_HUNTERSPRIDE + + recoil = 0 + fire_delay = 0.2 SECONDS + +EMPTY_GUN_HELPER(revolver/detective) + +/obj/item/gun/ballistic/revolver/detective/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/revolver) //note that the hud at the moment only supports 6 round revolvers, 7 or 5 isn't supported rn +//...why...? +/obj/item/gun/ballistic/revolver/detective/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, burst_firing = FALSE, spread_override = 0, iteration = 0) + if(magazine.caliber != initial(magazine.caliber)) + if(prob(100 - (magazine.ammo_count() * 5))) //minimum probability of 70, maximum of 95 + playsound(user, fire_sound, fire_sound_volume, vary_fire_sound) + to_chat(user, span_userdanger("[src] blows up in your face!")) + user.take_bodypart_damage(0,20) + explosion(src, 0, 0, 1, 1) + user.dropItemToGround(src) + return 0 + ..() + +/obj/item/gun/ballistic/revolver/detective/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE + if(magazine.caliber == "38") + to_chat(user, span_notice("You begin to reinforce the barrel of [src]...")) + if(magazine.ammo_count()) + afterattack(user, user) //you know the drill + user.visible_message(span_danger("[src] goes off!"), span_userdanger("[src] goes off in your face!")) + return TRUE + if(I.use_tool(src, user, 30)) + if(magazine.ammo_count()) + to_chat(user, span_warning("You can't modify it!")) + return TRUE + magazine.caliber = ".357" + fire_sound = 'sound/weapons/gun/revolver/shot.ogg' + desc = "The barrel and chamber assembly seems to have been modified." + to_chat(user, span_notice("You reinforce the barrel of [src]. Now it will fire .357 rounds.")) + else + to_chat(user, span_notice("You begin to revert the modifications to [src]...")) + if(magazine.ammo_count()) + afterattack(user, user) //and again + user.visible_message(span_danger("[src] goes off!"), span_userdanger("[src] goes off in your face!")) + return TRUE + if(I.use_tool(src, user, 30)) + if(magazine.ammo_count()) + to_chat(user, span_warning("You can't modify it!")) + return + magazine.caliber = ".38" + fire_sound = 'sound/weapons/gun/revolver/shot.ogg' + desc = initial(desc) + to_chat(user, span_notice("You remove the modifications on [src]. Now it will fire .38 rounds.")) + return TRUE + +///pistols + +/obj/item/gun/ballistic/automatic/pistol/candor + name = "\improper Candor" + desc = "A classic semi-automatic handgun, widely popular throughout the Frontier. An engraving on the slide marks it as a product of Hunter's Pride. Chambered in .45." + icon_state = "candor" + item_state = "hp_generic" + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + default_ammo_type = /obj/item/ammo_box/magazine/m45 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m45, + ) + fire_sound = 'sound/weapons/gun/pistol/candor.ogg' + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + manufacturer = MANUFACTURER_HUNTERSPRIDE + load_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/candor_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/candor_unload.ogg' + show_magazine_on_sprite = TRUE + wear_rate = 0.66 //HP weapons are more resistant to wear + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 31, + "y" = 23, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 21, + "y" = 18, + ) + ) + + +NO_MAG_GUN_HELPER(automatic/pistol/candor) + +/obj/item/gun/ballistic/automatic/pistol/candor/factory //also give this to the srm, their candors should probably look factory fresh from how well taken care of they are + desc = "A classic semi-automatic handgun, widely popular throughout the Frontier. An engraving on the slide marks it as a product of 'Hunter's Pride Arms and Ammunition'. This example has been kept in especially good shape, and may as well be fresh out of the workshop. Chambered in .45." + item_state = "hp_generic_fresh" + wear_rate = 0.6 //factory guns are now OBJECTIVELY better. if they happen to be candors. + +NO_MAG_GUN_HELPER(automatic/pistol/candor/factory) + +/obj/item/gun/ballistic/automatic/pistol/candor/factory/update_overlays() + . = ..() + . += "[initial(icon_state)]_factory" + +/obj/item/gun/ballistic/automatic/pistol/candor/phenex + name = "\improper HP Phenex" + desc = "A uniquely modified version of the Candor, famously created by Hunter's Pride. Named after the daemonic Phoenix of legend that the Ashen Huntsman had once slain, this hell-kissed weapon is more visually intimidating than its original counterpart, but mechanically acts the same. Chambered in .45." + icon_state = "phenex" + item_state = "hp_phenex" + +/// SMG /// + +/obj/item/gun/ballistic/automatic/smg/firestorm //weapon designed by Apogee-dev + name = "HP Firestorm" + desc = "An unconventional submachinegun, rarely issued to Saint-Roumain Militia mercenary hunters for outstanding situations where normal hunting weapons fall short. Chambered in .44 Roumain." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + icon_state = "firestorm" + item_state = "firestorm" + default_ammo_type = /obj/item/ammo_box/magazine/c44_firestorm_mag + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/c44_firestorm_mag, + ) + unique_mag_sprites_for_variants = TRUE + burst_size = 1 + actions_types = list() + fire_delay = 0.22 SECONDS + bolt_type = BOLT_TYPE_OPEN + rack_sound = 'sound/weapons/gun/smg/uzi_cocked.ogg' + fire_sound = 'sound/weapons/gun/smg/firestorm.ogg' + wear_rate = 0.4 //HP weapons are more resistant to wear + + + manufacturer = MANUFACTURER_HUNTERSPRIDE + wield_slowdown = SMG_SLOWDOWN + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 47, + "y" = 17, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 34, + "y" = 13, + ) + ) + +/obj/item/gun/ballistic/automatic/smg/firestorm/pan //spawns with pan magazine, can take sticks instead of just drums, not sure where this would be used, maybe erts? + default_ammo_type = /obj/item/ammo_box/magazine/c44_firestorm_mag/pan + +///Shotguns + +///////////////////////////// +// DOUBLE BARRELED SHOTGUN // +///////////////////////////// + +/obj/item/gun/ballistic/shotgun/doublebarrel + name = "double-barreled shotgun" + desc = "A classic break action shotgun, hand-made in a Hunter's Pride workshop. Both barrels can be fired in quick succession or even simultaneously. Guns like this have been popular with hunters, sporters, and criminals for millennia. Chambered in 12g." + sawn_desc = "A break action shotgun cut down to the size of a sidearm. While the recoil is even harsher, it offers a lot of power in a very small package. Chambered in 12g." + + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + base_icon_state = "dshotgun" + + icon_state = "dshotgun" + item_state = "dshotgun" + + rack_sound = 'sound/weapons/gun/shotgun/dbshotgun_break.ogg' + bolt_drop_sound = 'sound/weapons/gun/shotgun/dbshotgun_close.ogg' + + w_class = WEIGHT_CLASS_BULKY + weapon_weight = WEAPON_MEDIUM + force = 10 + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/dual + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/dual, + ) + + obj_flags = UNIQUE_RENAME + semi_auto = TRUE + can_be_sawn_off = TRUE + bolt_type = BOLT_TYPE_NO_BOLT + pb_knockback = 3 // it's a super shotgun! + manufacturer = MANUFACTURER_HUNTERSPRIDE + bolt_wording = "barrel" + + burst_delay = 0.05 SECONDS + burst_size = 2 + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + default_firemode = FIREMODE_SEMIAUTO + unique_attachments = list(/obj/item/attachment/scope) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_SCOPE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 18, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 24, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 40, + "y" = 17, + ) + ) + +/obj/item/gun/ballistic/shotgun/doublebarrel/unique_action(mob/living/user) + if (bolt_locked == FALSE) + to_chat(user, span_notice("You snap open the [bolt_wording] of \the [src].")) + playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) + chambered = null + var/num_unloaded = 0 + for(var/obj/item/ammo_casing/casing_bullet in get_ammo_list(FALSE, TRUE)) + casing_bullet.forceMove(drop_location()) + var/angle_of_movement =(rand(-3000, 3000) / 100) + dir2angle(turn(user.dir, 180)) + casing_bullet.AddComponent(/datum/component/movable_physics, _horizontal_velocity = rand(450, 550) / 100, _vertical_velocity = rand(400, 450) / 100, _horizontal_friction = rand(20, 24) / 100, _z_gravity = PHYSICS_GRAV_STANDARD, _z_floor = 0, _angle_of_movement = angle_of_movement, _bounce_sound = casing_bullet.bounce_sfx_override) + + num_unloaded++ + SSblackbox.record_feedback("tally", "station_mess_created", 1, casing_bullet.name) + if (num_unloaded) + playsound(user, eject_sound, eject_sound_volume, eject_sound_vary) + update_appearance() + bolt_locked = TRUE + update_appearance() + return + drop_bolt(user) + +/obj/item/gun/ballistic/shotgun/doublebarrel/drop_bolt(mob/user = null) + playsound(src, bolt_drop_sound, bolt_drop_sound_volume, FALSE) + if (user) + to_chat(user, span_notice("You snap the [bolt_wording] of \the [src] closed.")) + chamber_round() + bolt_locked = FALSE + update_appearance() + +/obj/item/gun/ballistic/shotgun/doublebarrel/can_shoot() + if (bolt_locked) + return FALSE + return ..() + +/obj/item/gun/ballistic/shotgun/doublebarrel/attackby(obj/item/A, mob/user, params) + if (!bolt_locked) + if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACKBY, A, user, params) & COMPONENT_NO_AFTERATTACK) + return TRUE + to_chat(user, span_notice("The [bolt_wording] is shut closed!")) + return + return ..() + +/obj/item/gun/ballistic/shotgun/doublebarrel/update_icon_state() + . = ..() + if(current_skin) + icon_state = "[unique_reskin[current_skin]][sawn_off ? "_sawn" : ""][bolt_locked ? "_open" : ""]" + else + icon_state = "[base_icon_state || initial(icon_state)][sawn_off ? "_sawn" : ""][bolt_locked ? "_open" : ""]" + + +/obj/item/gun/ballistic/shotgun/doublebarrel/AltClick(mob/user) + . = ..() + if(unique_reskin && !current_skin && user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY) && (!bolt_locked)) + reskin_obj(user) + +/obj/item/gun/ballistic/shotgun/doublebarrel/sawoff(forced = FALSE) + . = ..() + if(.) + weapon_weight = WEAPON_MEDIUM + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.3 SECONDS //OP? maybe + + spread = 8 + spread_unwielded = 15 + recoil = 3 //or not + recoil_unwielded = 5 + item_state = "dshotgun_sawn" + mob_overlay_state = item_state + +EMPTY_GUN_HELPER(shotgun/doublebarrel) + +// sawn off beforehand +/obj/item/gun/ballistic/shotgun/doublebarrel/presawn + //init gives it the sawn_off name + name = "double-barreled shotgun" + desc = "A break action shotgun cut down to the size of a sidearm. While the recoil is even harsher, it offers a lot of power in a very small package. Chambered in 12g." + sawn_off = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_NORMAL + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + wield_slowdown = 0.15 + wield_delay = 0.3 SECONDS //OP? maybe + + spread = 8 + spread_unwielded = 15 + recoil = 3 //or not + recoil_unwielded = 5 + item_state = "dshotgun_sawn" + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/dual/lethal + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/dual/lethal, + ) + +EMPTY_GUN_HELPER(shotgun/doublebarrel/presawn) + +/obj/item/gun/ballistic/shotgun/doublebarrel/roumain + name = "HP antique double-barreled shotgun" + desc = "A special-edition shotgun hand-made by Hunter's Pride with a high-quality walnut stock inlaid with brass scrollwork. Shotguns like this are very rare outside of the Saint-Roumain Militia's ranks. Otherwise functionally identical to a common double-barreled shotgun. Chambered in 12g." + sawn_desc = "A special-edition Hunter's Pride shotgun, cut down to the size of a sidearm by some barbarian. The brass inlay on the stock and engravings on the barrel have been obliterated in the process, destroying any value beyond its use as a crude sidearm." + base_icon_state = "dshotgun_srm" + icon_state = "dshotgun_srm" + item_state = "dshotgun_srm" + unique_reskin = null + +EMPTY_GUN_HELPER(shotgun/doublebarrel/roumain) + +/obj/item/gun/ballistic/shotgun/doublebarrel/roumain/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "dshotgun_srm_sawn" + +// BRIMSTONE // + +/obj/item/gun/ballistic/shotgun/brimstone + name = "HP Brimstone" + desc = "A simple and sturdy pump-action shotgun sporting a 5-round capacity, manufactured by Hunter's Pride. Found widely throughout the Frontier in the hands of hunters, pirates, police, and countless others. Chambered in 12g." + sawn_desc = "A stockless and shortened pump-action shotgun. The worsened recoil and accuracy make it a poor sidearm anywhere beyond punching distance." + fire_sound = 'sound/weapons/gun/shotgun/brimstone.ogg' + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + icon_state = "brimstone" + item_state = "brimstone" + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/lethal + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/lethal, + ) + manufacturer = MANUFACTURER_HUNTERSPRIDE + fire_delay = 0.05 SECONDS //slamfire + rack_delay = 0.2 SECONDS + + can_be_sawn_off = TRUE + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 40, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 36, + "y" = 17, + ) + ) + + +/obj/item/gun/ballistic/shotgun/brimstone/sawoff(forced = FALSE) + . = ..() + if(.) + weapon_weight = WEAPON_MEDIUM + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.3 SECONDS //OP? maybe + + spread = 18 + spread_unwielded = 25 + recoil = 5 //your punishment for sawing off an short shotgun + recoil_unwielded = 8 + item_state = "illestren_factory_sawn" // i couldnt care about making another sprite, looks close enough + mob_overlay_state = item_state + +EMPTY_GUN_HELPER(shotgun/brimstone) + +// HELLFIRE // + +/obj/item/gun/ballistic/shotgun/hellfire + name = "HP Hellfire" + desc = "A hefty pump-action riot shotgun with an eight-round tube, manufactured by Hunter's Pride. Especially popular among the Frontier's police forces. Chambered in 12g." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + icon_state = "hellfire" + item_state = "hellfire" + + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/riot + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/riot, + ) + sawn_desc = "Come with me if you want to live." + can_be_sawn_off = TRUE + rack_sound = 'sound/weapons/gun/shotgun/rack_alt.ogg' + fire_delay = 0.1 SECONDS + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 45, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 33, + "y" = 13, + ) + ) + +/obj/item/gun/ballistic/shotgun/hellfire/sawoff(forced = FALSE) + . = ..() + if(.) + var/obj/item/ammo_box/magazine/internal/tube = magazine + tube.max_ammo = 5 //this makes it so much worse + + weapon_weight = WEAPON_MEDIUM + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.3 SECONDS //OP? maybe + + spread = 8 + spread_unwielded = 15 + recoil = 3 //or not + recoil_unwielded = 5 + item_state = "dshotgun_sawn" // ditto + mob_overlay_state = item_state + +EMPTY_GUN_HELPER(shotgun/hellfire) + +/obj/item/gun/ballistic/shotgun/flamingarrow/conflagration + name = "HP Conflagration" + base_icon_state = "conflagration" + icon_state = "conflagration" + item_state = "conflagration" + fire_sound = 'sound/weapons/gun/shotgun/shot.ogg' + desc = "A lightweight lever-action shotgun with a 5 round ammunition capacity. The lever action allows it to be cycled quickly and acurrately. In theory, you could ever operate it one-handed. Chambered in 12g." + sawn_desc = "A lever action shotgun that's been sawed down for portability. The recoil makes it mostly useless outside of point-blank range, but it hits hard for its size and, more importantly, can be flipped around stylishly." + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/winchester/conflagration + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/winchester/conflagration, + ) + door_breaching_weapon = TRUE + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 47, + "y" = 19, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 26, + "y" = 22, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 34, + "y" = 16, + ) + ) + +/obj/item/gun/ballistic/shotgun/flamingarrow/conflagration/sawoff(forced = FALSE) + . = ..() + if(.) + var/obj/item/ammo_box/magazine/internal/tube = magazine + tube.max_ammo = 5 + + item_state = "beacon_factory_sawn" + mob_overlay_state = item_state + weapon_weight = WEAPON_MEDIUM + + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.2 SECONDS + + spread = 4 + spread_unwielded = 12 + + recoil = 0 + recoil_unwielded = 3 + +EMPTY_GUN_HELPER(shotgun/flamingarrow/conflagration) + + +//Elephant Gun +/obj/item/gun/ballistic/shotgun/doublebarrel/twobore + name = "HP Huntsman" + desc = "A comically huge double-barreled rifle replete with brass inlays depicting flames and naturalistic scenes, clearly meant for the nastiest monsters the Frontier has to offer. If you want an intact trophy, don't aim for the head. Chambered in two-bore." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + inhand_x_dimension = 32 + inhand_y_dimension = 32 + base_icon_state = "huntsman" + icon_state = "huntsman" + item_state = "huntsman" + unique_reskin = null + attack_verb = list("bludgeoned", "smashed") + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/twobore + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/twobore, + ) + w_class = WEIGHT_CLASS_BULKY + force = 20 //heavy ass elephant gun, why wouldnt it be + recoil = 4 + pb_knockback = 12 + fire_sound = 'sound/weapons/gun/shotgun/quadfire.ogg' + rack_sound = 'sound/weapons/gun/shotgun/quadrack.ogg' + load_sound = 'sound/weapons/gun/shotgun/quadinsert.ogg' + + can_be_sawn_off = FALSE + fire_sound_volume = 80 + rack_sound_volume = 50 + manufacturer = MANUFACTURER_HUNTERSPRIDE + + gun_firemodes = list(FIREMODE_SEMIAUTO) //no dual burst for you + default_firemode = FIREMODE_SEMIAUTO + +/// Rifles + +/obj/item/gun/ballistic/rifle/illestren + name = "\improper HP Illestren" + desc = "A sturdy and conventional bolt-action rifle. One of Hunter's Pride's most successful firearms, the Illestren is popular among colonists, pirates, snipers, and countless others. Chambered in 8x50mmR." + icon_state = "illestren" + item_state = "illestren" + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + sawn_desc = "An Illestren rifle sawn down to a ridiculously small size. There was probably a reason it wasn't made this short to begin with, but it still packs a punch." + eject_sound = 'sound/weapons/gun/rifle/vickland_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/vickland_unload.ogg' + + internal_magazine = FALSE + default_ammo_type = /obj/item/ammo_box/magazine/illestren_a850r + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/illestren_a850r, + ) + + unique_attachments = list( + /obj/item/attachment/scope, + /obj/item/attachment/long_scope, + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 18, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 18, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 37, + "y" = 15, + ) + ) + + empty_autoeject = TRUE + eject_sound_vary = FALSE + can_be_sawn_off = TRUE + manufacturer = MANUFACTURER_HUNTERSPRIDE + +/obj/item/gun/ballistic/rifle/illestren/empty //i had to name it empty instead of no_mag because else it wouldnt work with guncases. sorry! + default_ammo_type = FALSE + +/obj/item/gun/ballistic/rifle/illestren/sawoff(forced = FALSE) + . = ..() + if(.) + spread = 19 + spread_unwielded = 30 + item_state = "illestren_sawn" + mob_overlay_state = item_state + weapon_weight = WEAPON_MEDIUM //you can fire it onehanded, makes it worse than worse than useless onehanded, but you can + +/obj/item/gun/ballistic/rifle/illestren/blow_up(mob/user) + . = FALSE + if(chambered && chambered.BB) + process_fire(user, user, FALSE) + . = TRUE + +/obj/item/gun/ballistic/rifle/illestren/factory + desc = "A sturdy and conventional bolt-action rifle. One of Hunter's Pride's most successful firearms, this example has been kept in excellent shape and may as well be fresh out of the workshop. Chambered in 8x50mmR." + icon_state = "illestren_factory" + item_state = "illestren_factory" + +EMPTY_GUN_HELPER(rifle/illestren/factory) + +/obj/item/gun/ballistic/rifle/illestren/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "illestren_factory_sawn" + mob_overlay_state = item_state + +/obj/item/gun/ballistic/rifle/illestren/sawn + desc = "An Illestren rifle sawn down to a ridiculously small size. There was probably a reason it wasn't made this short to begin with, but it still packs a punch." + sawn_off = TRUE + +//Lever-Action Rifles + +/obj/item/gun/ballistic/shotgun/flamingarrow + name = "HP Flaming Arrow" + desc = "A sturdy and lightweight lever-action rifle with hand-stamped Hunter's Pride marks on the receiver. A popular choice among Frontier homesteaders for hunting small game and rudimentary self-defense. Chambered in .38." + sawn_desc = "A lever-action rifle that has been sawed down and modified for extra portability. While surprisingly effective as a sidearm, the more important benefit is how much cooler it looks." + base_icon_state = "flamingarrow" + icon_state = "flamingarrow" + item_state = "flamingarrow" + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + inhand_x_dimension = 32 + inhand_y_dimension = 32 + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/winchester + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/winchester, + ) + fire_sound = 'sound/weapons/gun/rifle/flamingarrow.ogg' + rack_sound = 'sound/weapons/gun/rifle/skm_cocked.ogg' + bolt_wording = "lever" + cartridge_wording = "bullet" + can_be_sawn_off = TRUE + + wield_slowdown = RIFLE_SLOWDOWN + wield_delay = 0.65 SECONDS + + unique_attachments = list( + /obj/item/attachment/scope, + /obj/item/attachment/long_scope, + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + + spread = -5 + spread_unwielded = 7 + + recoil = 0 + recoil_unwielded = 2 + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 45, + "y" = 16, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 15, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 25, + "y" = 13, + ) + ) + + door_breaching_weapon = FALSE + +EMPTY_GUN_HELPER(shotgun/flamingarrow) + +/obj/item/gun/ballistic/shotgun/flamingarrow/update_icon_state() + . = ..() + if(current_skin) + icon_state = "[unique_reskin[current_skin]][sawn_off ? "_sawn" : ""]" + else + icon_state = "[base_icon_state || initial(icon_state)][sawn_off ? "_sawn" : ""]" + + +/obj/item/gun/ballistic/shotgun/flamingarrow/rack(mob/user = null) + . = ..() + if(!wielded) + SpinAnimation(7,1) + +/obj/item/gun/ballistic/shotgun/flamingarrow/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + var/fan = FALSE + if(HAS_TRAIT(user, TRAIT_GUNSLINGER) && !semi_auto && wielded_fully && loc == user && !safety) + fan = TRUE + fire_delay = 0.35 SECONDS + . = ..() + fire_delay = src::fire_delay + if(fan) + rack() + to_chat(user, span_notice("You quickly rack the [bolt_wording] of \the [src]!")) + balloon_alert_to_viewers("quickly racks!") + fire_delay = 0 SECONDS + +/obj/item/gun/ballistic/shotgun/flamingarrow/sawoff(forced = FALSE) + . = ..() + if(.) + var/obj/item/ammo_box/magazine/internal/tube = magazine + tube.max_ammo = 7 + + item_state = "flamingarrow_sawn" + mob_overlay_state = item_state + weapon_weight = WEAPON_MEDIUM + + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.2 SECONDS //THE COWBOY RIFLE + + spread = 4 + spread_unwielded = 12 + + recoil = 0 + recoil_unwielded = 3 + +/obj/item/gun/ballistic/shotgun/flamingarrow/factory + desc = "A sturdy and lightweight lever-action rifle with hand-stamped Hunter's Pride marks on the receiver. This example has been kept in excellent shape and may as well be fresh out of the workshop. Chambered in .38." + icon_state = "flamingarrow_factory" + base_icon_state = "flamingarrow_factory" + item_state = "flamingarrow_factory" + +/obj/item/gun/ballistic/shotgun/flamingarrow/factory/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "flamingarrow_factory_sawn" + mob_overlay_state = item_state + +/obj/item/gun/ballistic/shotgun/flamingarrow/bolt + name = "HP Flaming Bolt" + desc = "A sturdy, excellently-made lever-action rifle. This one appears to be a genuine antique, kept in incredibly good condition despite its advanced age. Chambered in .38." + base_icon_state = "flamingbolt" + icon_state = "flamingbolt" + item_state = "flamingbolt" + +EMPTY_GUN_HELPER(shotgun/flamingarrow/bolt) + +/obj/item/gun/ballistic/shotgun/flamingarrow/bolt/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "flamingbolt_sawn" + mob_overlay_state = item_state + +/obj/item/gun/ballistic/shotgun/flamingarrow/absolution + name = "HP Absolution" + base_icon_state = "absolution" + icon_state = "absolution" + item_state = "absolution" + fire_sound = 'sound/weapons/gun/revolver/shot.ogg' + desc = "A large lever-action rifle with hand-stamped Hunter's Pride marks on the receiver and an 8 round ammunition capacity. More powerful than the Flaming Arrow, the Absolution is a popular pick for hunting larger fauna like bears and goliaths, especially when a bolt action's slower rate of fire would be a liability. Chambered in .357." + sawn_desc = "A large lever-action rifle, sawn down for portability. It looks much cooler, but you should probably be using a revolver..." + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/winchester/absolution + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/winchester/absolution, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 19, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 18, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 33, + "y" = 15, + ) + ) + +/obj/item/gun/ballistic/shotgun/flamingarrow/absolution/sawoff(forced = FALSE) + . = ..() + if(.) + var/obj/item/ammo_box/magazine/internal/tube = magazine + tube.max_ammo = 8 + + item_state = "illestren_sawn" + mob_overlay_state = item_state + weapon_weight = WEAPON_MEDIUM + + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.2 SECONDS + + spread = 4 + spread_unwielded = 12 + + recoil = 0 + recoil_unwielded = 3 + +/obj/item/gun/ballistic/shotgun/flamingarrow/absolution/factory + desc = "A large lever-action rifle with hand-stamped Hunter's Pride marks on the receiver and an 8 round ammunition capacity. More powerful than the Flaming Arrow, the Absolution is a popular pick for hunting larger fauna like bears and goliaths, especially when a bolt action's slower rate of fire would be a liability. This example has been kept in excellent shape and may as well be fresh out of the workshop. Chambered in .357." + icon_state = "absolution_factory" + base_icon_state = "absolution_factory" + item_state = "absolution_factory" + +/obj/item/gun/ballistic/shotgun/flamingarrow/absolution/factory/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "absolution_factory_sawn" + mob_overlay_state = item_state + +//Break-Action Rifle +/obj/item/gun/ballistic/shotgun/doublebarrel/beacon + name = "HP Beacon" + desc = "A break-action rifle made by Hunter's Pride and sold to civilian hunters. Boasts excellent accuracy and stopping power. Uses .45-70 ammo." + sawn_desc= "A break-action pistol chambered in .45-70. A bit difficult to aim." + base_icon_state = "beacon" + icon_state = "beacon" + item_state = "beacon" + unique_reskin = null + inhand_x_dimension = 32 + inhand_y_dimension = 32 + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/beacon + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/beacon, + ) + fire_sound = 'sound/weapons/gun/revolver/shot_hunting.ogg' + w_class = WEIGHT_CLASS_BULKY + weapon_weight = WEAPON_MEDIUM + force = 10 + obj_flags = UNIQUE_RENAME + semi_auto = TRUE + can_be_sawn_off = TRUE + pb_knockback = 3 + wield_slowdown = HEAVY_RIFLE_SLOWDOWN + spread_unwielded = 15 + spread = 0 + recoil = 0 + recoil_unwielded = 5 + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + + unique_attachments = list( + /obj/item/attachment/alof, + /obj/item/attachment/scope, + /obj/item/attachment/long_scope) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 18, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 15, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 31, + "y" = 16, + ) + ) + +/obj/item/gun/ballistic/shotgun/doublebarrel/beacon/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "beacon_sawn" + mob_overlay_state = item_state + wield_slowdown = wield_slowdown-0.1 + wield_delay = 0.5 SECONDS + + spread_unwielded = 20 //mostly the hunting revolver stats + spread = 6 + recoil = 2 + recoil_unwielded = 4 + +EMPTY_GUN_HELPER(shotgun/doublebarrel/beacon) + +/obj/item/gun/ballistic/shotgun/doublebarrel/beacon/factory + desc = "A break-action rifle made by Hunter's Pride and sold to civilian hunters. This example has been kept in excellent shape and may as well be fresh out of the workshop. Uses .45-70 ammo." + sawn_desc= "A break-action pistol chambered in .45-70. A bit difficult to aim." + base_icon_state = "beacon_factory" + icon_state = "beacon_factory" + item_state = "beacon_factory" + +/obj/item/gun/ballistic/shotgun/doublebarrel/beacon/factory/sawoff(forced = FALSE) + . = ..() + if(.) + item_state = "beacon_factory_sawn" + mob_overlay_state = item_state + +//pre sawn off beacon +/obj/item/gun/ballistic/shotgun/doublebarrel/beacon/presawn + name = "HP Beacon" + sawn_desc= "A break-action pistol chambered in .45-70. A bit difficult to aim." + sawn_off = TRUE + w_class = WEIGHT_CLASS_NORMAL + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + weapon_weight = WEAPON_MEDIUM + + item_state = "beacon_sawn" + mob_overlay_state = "beacon_sawn" + wield_slowdown = 0.45 + wield_delay = 0.5 SECONDS + + spread_unwielded = 20 //mostly the hunting revolver stats + spread = 6 + recoil = 2 + recoil_unwielded = 4 + +/// snipers + +//well. its almost a sniper. +/obj/item/gun/ballistic/automatic/marksman/vickland //weapon designed by Apogee-dev + name = "\improper Vickland" + desc = "The pride of the Saint-Roumain Militia, the Vickland is a rare semi-automatic battle rifle produced by Hunter's Pride exclusively for SRM use. It is unusual in its class for its internal rotary magazine, which must be reloaded using stripper clips. Chambered in 8x50mmR." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + fire_sound = 'sound/weapons/gun/rifle/vickland.ogg' + icon_state = "vickland" + item_state = "vickland" + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + internal_magazine = TRUE + default_ammo_type = /obj/item/ammo_box/magazine/internal/vickland + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/vickland, + ) + fire_sound = 'sound/weapons/gun/rifle/vickland.ogg' + + manufacturer = MANUFACTURER_HUNTERSPRIDE + + rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg' + + fire_delay = 0.4 SECONDS + + spread_unwielded = 25 + recoil = 0 + recoil_unwielded = 4 + wield_slowdown = DMR_SLOWDOWN + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 17, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 17, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 38, + "y" = 14, + ) + ) + + wear_rate = 0.8 //HP weapons are more resistant to wear + +/obj/item/gun/ballistic/rifle/scout + name = "HP Scout" + desc = "A powerful bolt-action rifle usually given to mercenary hunters of the Saint-Roumain Militia, equally suited for taking down big game or two-legged game. Chambered in .300 Magnum." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + icon_state = "scout" + item_state = "scout" + + default_ammo_type = /obj/item/ammo_box/magazine/internal/boltaction/smile + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/boltaction/smile, + ) + fire_sound = 'sound/weapons/gun/rifle/scout.ogg' + + rack_sound = 'sound/weapons/gun/rifle/scout_bolt_out.ogg' + bolt_drop_sound = 'sound/weapons/gun/rifle/scout_bolt_in.ogg' + + can_be_sawn_off = FALSE + + zoomable = TRUE + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + + wield_slowdown = SNIPER_SLOWDOWN + + recoil = 3 + recoil_unwielded = 10 + + manufacturer = MANUFACTURER_HUNTERSPRIDE + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 17, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 32, + "y" = 14, + ) + ) + +/obj/item/gun/ballistic/automatic/assault/invictus + name = "HP Invictus" + desc = "An unwieldy automatic rifle fielded by the Saint-Roumain Militia, commonly sold to police forces and private buyers. This one has a smooth wood finish and is in pristine condition. Chambered in .308." + icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' + + icon_state = "invictus" + item_state = "invictus" + + manufacturer = MANUFACTURER_HUNTERSPRIDE + + default_ammo_type = /obj/item/ammo_box/magazine/invictus_308_mag + allowed_ammo_types = /obj/item/ammo_box/magazine/invictus_308_mag + + gun_firemodes = list(FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + fire_delay = 0.25 SECONDS + + spread = 3 + spread_unwielded = 20 + + recoil = 1 + recoil_unwielded = 4 + + wear_rate = 0.6 + + fire_sound = 'sound/weapons/gun/hmg/hmg.ogg' + + unique_attachments = list(/obj/item/attachment/bayonet) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 40, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 20, + "y" = 20, + ) + ) + +EMPTY_GUN_HELPER(automatic/assault/invictus) +NO_MAG_GUN_HELPER(automatic/assault/invictus) + +/obj/item/gun/ballistic/automatic/assault/invictus/old + desc = "An unwieldy automatic rifle fielded by the Saint-Roumain Militia, commonly sold to police forces and private buyers. Chambered in .308." + icon_state = "invictus_old" + item_state = "invictus_old" + + wear_rate = 1 + +/obj/item/ammo_box/magazine/invictus_308_mag + name = "Invictus magazine (.308)" + desc = "A 20 round box magazine for the Invictus automatic rifle. These rounds do good damage with significant armor penetration." + base_icon_state = "invictus_mag" + icon_state = "invictus_mag-1" + ammo_type = /obj/item/ammo_casing/a308 + caliber = ".308" + max_ammo = 20 + w_class = WEIGHT_CLASS_SMALL + +/obj/item/ammo_box/magazine/invictus_308_mag/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[!!ammo_count()]" + +/obj/item/ammo_box/magazine/invictus_308_mag/empty + start_empty = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/ballistics.dm new file mode 100644 index 0000000000..152470a641 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/ballistics.dm @@ -0,0 +1,494 @@ +/obj/item/gun/ballistic/automatic/pistol/challenger + name = "Advantage PS9 Challenger" + desc = "A lightweight semi-automatic 9mm pistol constructed largely of polymers. Low price point, forgiving recoil, and generous magazine capacity for its class. Chambered in 9x18mm." + icon_state = "challenger" + item_state = "nt_generic" + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/co9mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/co9mm, + ) + manufacturer = MANUFACTURER_VIGILITAS + fire_sound = 'sound/weapons/gun/pistol/rattlesnake.ogg' + load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 29, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 20, + "y" = 17, + ) + ) + +NO_MAG_GUN_HELPER(automatic/pistol/challenger) + +/obj/item/gun/ballistic/automatic/pistol/champion + name = "Advantage PHB Champion" + desc = "A large, burst-fire machine pistol featuring an impressive recoil compensation assembly, making it substantially more stable and accurate than most machine pistols. Chambered in 9x18mm." + icon_state = "champion" + item_state = "champion" + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/ammo_box/magazine/co9mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/co9mm, + ) + manufacturer = MANUFACTURER_VIGILITAS + fire_sound = 'sound/weapons/gun/pistol/cm23.ogg' + + load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 41, + "y" = 22, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 25, + "y" = 16, + ) + ) + + burst_size = 3 + burst_delay = 0.1 SECONDS + fire_delay = 0.4 SECONDS + + wear_minor_threshold = 240 + wear_major_threshold = 720 + + wear_maximum = 1200 + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + default_firemode = FIREMODE_SEMIAUTO + +NO_MAG_GUN_HELPER(automatic/pistol/champion) + +/obj/item/gun/ballistic/automatic/pistol/podium + name = "Advantage PH46 Podium" + desc ="A heavy pistol chambered in the high-velocity 4.6mm cartridge, designed to defeat common body armor. Despite the powerful cartridge, it is known to be surprisingly controllable, though not necessarily lightweight. Chambered in 4.6mm." + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "podium" + item_state = "podium" + + default_ammo_type = /obj/item/ammo_box/magazine/m46_30_podium + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m46_30_podium, + ) + + fire_sound = 'sound/weapons/gun/pistol/podium.ogg' + + load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + manufacturer = MANUFACTURER_VIGILITAS + show_magazine_on_sprite = TRUE + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 33, + "y" = 19, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 23, + "y" = 15, + ) + ) + +NO_MAG_GUN_HELPER(automatic/pistol/podium) + +/obj/item/gun/ballistic/automatic/pistol/podium/inteq + name = "P46 Schnauzer" + desc = "A modification of Nanotrasen's \"Podium\" line of pistols made by the IRMG. Typically issued to second line combatants due to the armor piercing potential of the 4.6mm cartridge. Just be careful around the snout. Chambered in 4.6mm." + + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "podium_inteq" + item_state = "inteq_generic" + +NO_MAG_GUN_HELPER(automatic/pistol/podium/inteq) + +/obj/item/ammo_box/magazine/m46_30_podium + name = "Podium magazine (4.6x30mm)" + desc = "A 12-round, double-stack magazine for the Podium pistol. These rounds do okay damage with average performance against armor." + icon_state = "podium_mag-12" + base_icon_state = "podium_mag" + ammo_type = /obj/item/ammo_casing/c46x30mm + caliber = "4.6x30mm" + max_ammo = 12 + +/obj/item/ammo_box/magazine/m46_30_podium/update_icon_state() + . = ..() + if(ammo_count() == 12) + icon_state = "[base_icon_state]-12" + else if(ammo_count() >= 10) + icon_state = "[base_icon_state]-10" + else if(ammo_count() >= 5) + icon_state = "[base_icon_state]-5" + else if(ammo_count() >= 1) + icon_state = "[base_icon_state]-1" + else + icon_state = "[base_icon_state]-0" + + +/obj/item/ammo_box/magazine/co9mm + name = "challenger pistol magazine (9x18mm)" + desc = "A 12-round double-stack magazine for challenger pistols. This is also compatable with the Champion machine pistol. These rounds do okay damage, but struggle against armor." + icon_state = "commander_mag-12" + base_icon_state = "commander_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 12 + multiple_sprites = AMMO_BOX_PER_BULLET + +/obj/item/ammo_box/magazine/co9mm/hp + name = "pistol magazine (9x18mm HP)" + desc= "A 12-round double-stack magazine for standard-issue 9x18mm pistols. These hollow point rounds do significant damage against soft targets, but are nearly ineffective against armored ones." + ammo_type = /obj/item/ammo_casing/c9mm/hp + +/obj/item/ammo_box/magazine/co9mm/ap + name = "pistol magazine (9x18mm AP)" + desc= "A 12-round double-stack magazine for standard-issue 9x18mm pistols. These armor-piercing rounds are okay at piercing protective equipment, but lose some stopping power." + ammo_type = /obj/item/ammo_casing/c9mm/ap + +/obj/item/ammo_box/magazine/co9mm/rubber + name = "pistol magazine (9x18mm rubber)" + desc = "A 12-round double-stack magazine for standard-issue 9x18mm pistols. These rubber rounds trade lethality for a heavy impact which can incapacitate targets. Performs even worse against armor." + ammo_type = /obj/item/ammo_casing/c9mm/rubber + +/obj/item/ammo_box/magazine/co9mm/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[ammo_count() == 1 ? 1 : round(ammo_count(),2)]" + + +/obj/item/ammo_box/magazine/co9mm/empty + start_empty = TRUE + + +/obj/item/gun/ballistic/automatic/pistol/challenger/inteq + name = "PS-03 Commissioner" + desc = "A modified version of the PS9 Challenger, issued as standard to Inteq Risk Management Group personnel. Features the same excellent handling and high magazine capacity as the original. Chambered in 9x18mm." + + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "commander_inteq" + item_state = "inteq_generic" + manufacturer = MANUFACTURER_INTEQ + +NO_MAG_GUN_HELPER(automatic/pistol/challenger/inteq) + +/obj/item/gun/ballistic/revolver/rhino + name = "Advantage Rhino" + desc = "A very famous high-powered semi-auto revolver, often used as the de-facto officer weapon of the Nanotrasen aliance. Chambered in .357." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "rhino" + item_state = "nt_generic" + manufacturer = MANUFACTURER_VIGILITAS + semi_auto = TRUE + safety_wording = "safety" + + fire_sound = 'sound/weapons/gun/revolver/viper.ogg' + rack_sound = 'sound/weapons/gun/revolver/viper_prime.ogg' + load_sound = 'sound/weapons/gun/revolver/load_bullet.ogg' + eject_sound = 'sound/weapons/gun/revolver/empty.ogg' + + dry_fire_sound = 'sound/weapons/gun/revolver/dry_fire.ogg' + + spread = 0 + spread_unwielded = 12 + recoil = 1 + recoil_unwielded = 3 + +/obj/item/gun/ballistic/revolver/rhino/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/revolver) + +/obj/item/gun/ballistic/automatic/smg/expedition + name = "\improper Advantage SGL9 Expedition" + desc = "A deceptively lightweight submachinegun. Its novel recoil compensation system almost eliminates recoil, and its compact size is well-suited for use aboard ships and stations." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "expedition" + item_state = "expedition" + default_ammo_type = /obj/item/ammo_box/magazine/m9mm_expedition + spread = 3 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m9mm_expedition, + ) //you guys remember when the autorifle was chambered in 9mm + bolt_type = BOLT_TYPE_LOCKING + show_magazine_on_sprite = TRUE + weapon_weight = WEAPON_LIGHT + fire_sound = 'sound/weapons/gun/smg/vector_fire.ogg' + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 44, + "y" = 19, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 34, + "y" = 17, + ) + ) +NO_MAG_GUN_HELPER(automatic/smg/expedition) + +/obj/item/ammo_box/magazine/m9mm_expedition + name = "expedition submachinegun magazine (9x18mm)" + desc = "A 30-round magazine for the Expedition submachine gun. These rounds do okay damage, but struggle against armor." + icon_state = "expedition_mag-30" + base_icon_state = "expedition_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m9mm_expedition/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[(ammo_count() == 1 || ammo_count() == 2) ? 1 : round(ammo_count(),6)]" + +/obj/item/ammo_box/magazine/m9mm_expedition/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m9mm_expedition/ap + name = "expedition submachinegun magazine (9x18mm AP)" + desc = "A 30-round magazine for the Expedition submachine gun. These armor-piercing rounds are okay at piercing protective equipment, but lose some stopping power." + ammo_type = /obj/item/ammo_casing/c9mm/ap + +/obj/item/ammo_box/magazine/m9mm_expedition/rubber + name = "expedition submachinegun magazine (9x18mm rubber)" + desc = "A 30-round magazine for the Expedition submachine gun. These rubber rounds trade lethality for a heavy impact which can incapacitate targets. Performs even worse against armor." + ammo_type = /obj/item/ammo_casing/c9mm/rubber + +// /obj/item/gun/ballistic/automatic/smg/resolution +/obj/item/gun/ballistic/automatic/smg/resolution + name = "\improper Advantage PD46 Resolution" + desc = "A surprisingly compact 4.6mm personal defense weapon with a very unusual design. Though somewhat awkward to reload, the PD46 has excellent shooting ergonomics and excellent accuracy for its class, combined with its armor-penetrating cartridge and low price of entry." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "resolution" + item_state = "resolution" + default_ammo_type = /obj/item/ammo_box/magazine/wt550m9 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/wt550m9, + ) + actions_types = list() + show_magazine_on_sprite = TRUE + show_magazine_on_sprite_ammo = TRUE + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_NORMAL + empty_indicator = TRUE + manufacturer = MANUFACTURER_NANOTRASEN_OLD + fire_sound = 'sound/weapons/gun/smg/resolution.ogg' + + fire_delay = 0.13 SECONDS + + spread = 8 + spread_unwielded = 10 + + recoil = 0 + recoil_unwielded = 4 + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/foldable_stock/resolution, + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + /obj/item/attachment/bayonet, + /obj/item/attachment/ammo_counter + ) + + default_attachments = list(/obj/item/attachment/foldable_stock/resolution) + + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_STOCK = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 41, + "y" = 21, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 30, + "y" = 15, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = 17, + "y" = 18, + ) + ) + + +/obj/item/gun/ballistic/automatic/smg/resolution/update_icon_state() + . = ..() + if(current_skin) + icon_state = "[unique_reskin[current_skin]][magazine ? "" : "_nomag"]" + else + icon_state = "[base_icon_state || initial(icon_state)][magazine ? "" : "_nomag"]" + +/obj/item/gun/ballistic/automatic/smg/resolution/no_mag + default_ammo_type = FALSE + +/obj/item/gun/ballistic/automatic/smg/resolution/inteq + name = "\improper BDM-50 Akita" + desc = "A seized Advantage PD46, modified to Inteq's requirements and standards. Awkward to reload, though has a powerful cartridge with good ergonomics and accuracy." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "resolution_inteq" + item_state = "resolution_inteq" + + default_attachments = list(/obj/item/attachment/foldable_stock/resolution_inteq) + + valid_attachments = list( + /obj/item/attachment/silencer, + /obj/item/attachment/foldable_stock/resolution_inteq + ) + +NO_MAG_GUN_HELPER(automatic/smg/resolution/inteq) + +/obj/item/ammo_box/magazine/wt550m9 + name = "Resolution magazine (4.6x30mm)" + desc = "A 30-round magazine for the Resolution personal defense weapon. These rounds do okay damage with average performance against armor." + icon_state = "resolution_mag-1" + base_icon_state = "resolution_mag" + ammo_type = /obj/item/ammo_casing/c46x30mm + caliber = "4.6x30mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/wt550m9/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/wt550m9/ap + name = "Resolution magazine (4.6x30mm AP)" + desc = "A compact, 30-round top-loading magazine for the WT-550 Automatic Rifle. These armor-piercing rounds are great at piercing protective equipment, but lose some stopping power." + + ammo_type = /obj/item/ammo_casing/c46x30mm/ap + +/obj/item/gun/ballistic/shotgun/automatic/negotiator + name = "Advantage AST12 Negotiator" + desc = "A pump-action shotgun with a twin-tube design that allows the user to switch between two ammo types on demand, or simply double their available ammunition." + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + icon_state = "negotiator" + item_state = "negotiator" + + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/tube + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/tube, + ) + w_class = WEIGHT_CLASS_BULKY + var/toggled = FALSE + var/obj/item/ammo_box/magazine/internal/shot/alternate_magazine + actions_types = list(/datum/action/item_action/toggle_tube) + + semi_auto = TRUE + casing_ejector = TRUE + + //refused_attachments = list(/obj/item/attachment/gun) + +/obj/item/gun/ballistic/shotgun/automatic/negotiator/secondary_action(user) + toggle_tube(user) + +/obj/item/gun/ballistic/shotgun/automatic/negotiator/examine(mob/user) + . = ..() + . += span_notice("Tube [toggled ? "B" : "A"] is currently loaded.") + . += "You can change the [src]'s tube by pressing the secondary action key. By default, this is Shift + Space" + +/obj/item/gun/ballistic/shotgun/automatic/negotiator/Initialize(mapload, spawn_empty) + . = ..() + if (!alternate_magazine) + alternate_magazine = new default_ammo_type(src, spawn_empty) + +/obj/item/gun/ballistic/shotgun/automatic/negotiator/proc/toggle_tube(mob/living/user) + var/current_mag = magazine + var/alt_mag = alternate_magazine + magazine = alt_mag + alternate_magazine = current_mag + toggled = !toggled + if(toggled) + to_chat(user, span_notice("You switch to tube B.")) + else + to_chat(user, span_notice("You switch to tube A.")) + SEND_SIGNAL(src, COMSIG_UPDATE_AMMO_HUD) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + +/datum/action/item_action/toggle_tube + name = "Toggle Tube" + +/datum/action/item_action/toggle_tube/Trigger() + if(istype(target, /obj/item/gun/ballistic/shotgun/automatic/negotiator)) + var/obj/item/gun/ballistic/shotgun/automatic/negotiator/shotty = target + shotty.toggle_tube(owner) + return + ..() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/lasers.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/lasers.dm new file mode 100644 index 0000000000..51f6d7ac76 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/nanotrasen_sharplite/lasers.dm @@ -0,0 +1,520 @@ +/obj/item/gun/energy/sharplite + name = "sharplite laser gun" + desc = "The newest from sharplite! The 'adminhelp if you see this!' Wow! A bit of a disapointment you feel, though." + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + ammo_type = list(/obj/item/ammo_casing/energy/disabler/sharplite, /obj/item/ammo_casing/energy/laser/sharplite) + + + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite + + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + /obj/item/stock_parts/cell/gun/sharplite/empty, + /obj/item/stock_parts/cell/gun/sharplite/plus/empty, + ) + + muzzleflash_iconstate = "muzzle_flash_nt" + light_color = COLOR_PALE_BLUE_GRAY + + modifystate = TRUE + ammo_x_offset = 2 + dual_wield_spread = 60 + wield_slowdown = LASER_RIFLE_SLOWDOWN + manufacturer = MANUFACTURER_SHARPLITE_NEW + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/gun/energy/sharplite/x26 + name = "SL X26 “Ohm” Variable Energy Pistol" + desc = "A compact energy pistol that can fire lethal electroplasma bolts or stamina-draining disabler bolts." + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + icon_state = "x26" + item_state = "x26" + + w_class = WEIGHT_CLASS_NORMAL + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite/mini + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite/mini, + ) + ammo_type = list(/obj/item/ammo_casing/energy/disabler/sharplite, /obj/item/ammo_casing/energy/laser/sharplite/efficent) + + shaded_charge = TRUE + modifystate = TRUE + + throwforce = 11 //This is funny, trust me. + + wield_delay = 0.2 SECONDS + wield_slowdown = LASER_PISTOL_SLOWDOWN + + spread = 4 + spread_unwielded = 8 + + muzzleflash_iconstate = "muzzle_flash_nt" + +/obj/item/gun/energy/sharplite/x01 + name = "SL X01 “Yari” Heavy Variable Pistol" + desc = "A bulky pistol that can fire devastating electroplasma bolts and disabler shots. An early Sharplite product notable for unusually strong power output." + + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + w_class = WEIGHT_CLASS_NORMAL + icon_state = "x01" + item_state = "x26" + + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite/plus + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + ) + force = 10 + ammo_type = list(/obj/item/ammo_casing/energy/disabler/sharplite/hos, /obj/item/ammo_casing/energy/laser/sharplite/hos, /obj/item/ammo_casing/energy/ion/hos) + shaded_charge = FALSE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + manufacturer = MANUFACTURER_SHARPLITE_NEW + +/obj/item/gun/energy/sharplite/x01/brazil + name = "modified antique laser gun" + desc = "It's somehow modified to have more firemodes." + icon_state = "capgun_brazil_hos" + item_state = "hoslaserkill0" + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/sharplite/x01/brazil/true + desc = "This genuine antique laser gun, modified with an experimental suite of alternative firing modes based on the X-01 MultiPhase Energy Gun, is now truly one of the finest weapons in the frontier." + icon_state = "capgun_hos" + item_state = "hoslaserkill0" + selfcharge = 1 + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/sharplite/l305 + name = "\improper L305 “Rush” Tactical Plasma Gun" + desc = "A radical development on the X26 frame, fitted with a rapid-cycle plasma chamber and designed to produce sustained bursts of low-power electroplasma bolts." + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + icon_state = "l305" + item_state = "l305" + + ammo_type = list(/obj/item/ammo_casing/energy/laser/sharplite/smg) + + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite + + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + /obj/item/stock_parts/cell/gun/sharplite/empty, + /obj/item/stock_parts/cell/gun/sharplite/plus/empty, + ) + + shaded_charge = TRUE + modifystate = FALSE + weapon_weight = WEAPON_LIGHT + w_class = WEIGHT_CLASS_BULKY + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + fire_delay = 0.13 SECONDS + wield_slowdown = LASER_SMG_SLOWDOWN + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 36, + "y" = 16, + ), + ) + +/obj/item/gun/energy/sharplite/l201 + name = "SL L201 “Surge” Marksman Plasma Rifle" + desc = "A long rifle-sized electroplasma gun. Specialized for long-range shooting, with an extended focusing assembly that produces much higher projectile velocities and more powerful bolts at the cost of power usage." + + icon_state = "l201" + item_state = "l201" + w_class = WEIGHT_CLASS_BULKY + custom_materials = list(/datum/material/iron=2000) + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/sharplite/dmr) + ammo_x_offset = 1 + shaded_charge = TRUE + modifystate = FALSE + //supports_variations = VOX_VARIATION + manufacturer = MANUFACTURER_SHARPLITE_NEW + + zoomable = TRUE //this var as true without setting anything else produces a 2x zoom + wield_slowdown = DMR_SLOWDOWN + aimed_wield_slowdown = LONG_RIFLE_AIM_SLOWDOWN + zoom_amt = DMR_ZOOM + wield_delay = 1 SECONDS + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + spread = 0 + spread_unwielded = 12 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 39, + "y" = 13, + ), + ) + +/obj/item/gun/energy/sharplite/l201/l204 + name = "SL L204 “Resistor” Plasma Rifle" + desc = "A rifle-sized, semi-automatic electroplasma gun known for its low price and surprisingly low energy usage." + icon_state = "l204" + item_state = "l204" + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/sharplite) + + zoomable = FALSE + zoom_amt = PISTOL_ZOOM + wield_slowdown = SMG_SLOWDOWN + aimed_wield_slowdown = LONG_RIFLE_AIM_SLOWDOWN + wield_delay = 0.4 SECONDS + + spread = 2 + spread_unwielded = 10 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 34, + "y" = 15, + ), + ) + +/obj/item/gun/energy/sharplite/l201/l204/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/sharplite/x26/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/sharplite/x12 + name = "SL X12 “Volt” Variable Energy Carbine" + desc = "A short, somewhat hefty carbine that can fire electroplasma or disabler bolts. Popular with security details with low-threat assignments." + + icon_state = "x12" + item_state = "x12" + w_class = WEIGHT_CLASS_BULKY + custom_materials = list(/datum/material/iron=2000) + ammo_type = list(/obj/item/ammo_casing/energy/disabler/sharplite, /obj/item/ammo_casing/energy/laser/sharplite) + ammo_x_offset = 1 + shaded_charge = TRUE + modifystate = TRUE + manufacturer = MANUFACTURER_SHARPLITE_NEW + + wield_slowdown = SMG_SLOWDOWN + aimed_wield_slowdown = LONG_RIFLE_AIM_SLOWDOWN + wield_delay = 0.4 SECONDS + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + spread = 2 + spread_unwielded = 10 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 33, + "y" = 16, + ), + ) + +/obj/item/gun/energy/sharplite/x12/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/sharplite/x12/inteq + name = "PP10 “Cadejo” Energy Carbine" + desc = "A Sharplite X12 VEC refinished in Inteq Risk Management Group colors. Like the base model, it fires lethal electroplasma or nonlethal disabler bolts. The modifications appear to be largely cosmetic." + + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + + icon_state = "x12_inteq" + item_state = "x12_inteq" + +/obj/item/gun/energy/sharplite/x12/inteq/empty_cell + spawn_no_ammo = TRUE + +/obj/item/gun/energy/sharplite/x46 + name = "\improper SL X46 “Amperage” Variable Energy Blaster" + desc = "A heavy, bulky weapon designed to fire multiple electroplasma or disabler bolts, not unlike a ballistic shotgun. Electroplasma speed tends to lessen the spread and increase effective range over conventional ballistic shotguns." + + icon_state = "x46" + item_state = "x46" + shaded_charge = TRUE + modifystate = TRUE + ammo_type = list(/obj/item/ammo_casing/energy/disabler/scatter/shotgun/sharplite, /obj/item/ammo_casing/energy/laser/shotgun/sharplite) + + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite + + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + /obj/item/stock_parts/cell/gun/sharplite/empty, + /obj/item/stock_parts/cell/gun/sharplite/plus/empty, + ) + + w_class = WEIGHT_CLASS_BULKY + fire_delay = 0.6 SECONDS + shaded_charge = TRUE + + wield_slowdown = SHOTGUN_SLOWDOWN + aimed_wield_slowdown = SHOTGUN_AIM_SLOWDOWN + wield_delay = 0.8 SECONDS + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + zoom_amt = SHOTGUN_ZOOM + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 37, + "y" = 10, + ), + ) + +/obj/item/gun/energy/sharplite/x46/zeta + name = "\improper SL X-45" + desc = "A very old looking X-46, it has no stock or much decoration, and it is from before... Hey! What's this screen next to the mode select button?" + + icon_state = "x46_zeta" + item_state = "x46_zeta" + shaded_charge = TRUE + manufacturer = MANUFACTURER_SHARPLITE + + w_class = WEIGHT_CLASS_BULKY + var/obj/item/modular_computer/integratedNTOS + var/NTOS_type = /obj/item/modular_computer/internal + +/obj/item/gun/energy/sharplite/x46/zeta/Initialize() + . = ..() + if(NTOS_type) + integratedNTOS = new NTOS_type(src) + integratedNTOS.physical = src + +/obj/item/gun/energy/sharplite/x46/zeta/attack_self(mob/user) + . = ..() + if(!integratedNTOS) + return + integratedNTOS.interact(user) + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 24, + "y" = 12, + ), + ) + +/obj/item/gun/energy/sharplite/al655 + name = "SL AL655 “Hades” Assault Plasma Rifle" + desc = "A powerful electroplasma gun with a rapid repeater assembly and many capacitors. The APR rapidly fires heavy electroplasma bolts." + icon_state = "al655" + item_state = "al655" + + ammo_type = list(/obj/item/ammo_casing/energy/laser/assault/sharplite) + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite/plus + + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + /obj/item/stock_parts/cell/gun/sharplite/empty, + /obj/item/stock_parts/cell/gun/sharplite/plus/empty, + ) + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + shaded_charge = TRUE + modifystate = FALSE + + fire_delay = 0.2 SECONDS + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + wield_delay = 0.7 SECONDS + wield_slowdown = HEAVY_LASER_RIFLE_SLOWDOWN + spread_unwielded = 20 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 36, + "y" = 11, + ), + ) + +/obj/item/gun/energy/sharplite/al655/inteq + name = "PP20 “Barghest” APR" + desc = "A Sharplite Assault Plasma Rifle refinished in Inteq Risk Management Group colors. A powerful weapon that can deliver rapid-fire, armor-penetrating electroplasma bolts." + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + + icon_state = "al655_inteq" + item_state = "al655_inteq" + + +/obj/item/gun/energy/sharplite/al607 + name = "SL AL607 “Sarissa” Plasma Accelerator" + desc = "A heavy electroplasma rifle with an extensive accelerator assembly, with an overall length almost comparable to the average Kepori height. Produces singular electroplasma bolts of impressive power and velocity that strike with enough force and precision to overwhelm most infantry defenses." + icon_state = "al607" + item_state = "al607" + + ammo_type = list(/obj/item/ammo_casing/energy/lasergun/sharplite/sniper) + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite/plus + + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + /obj/item/stock_parts/cell/gun/sharplite/empty, + /obj/item/stock_parts/cell/gun/sharplite/plus/empty, + ) + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + + gun_firemodes = list(FIREMODE_SEMIAUTO) + default_firemode = FIREMODE_SEMIAUTO + + shaded_charge = TRUE + modifystate = FALSE + + spread = -4 + spread_unwielded = 40 + + wield_slowdown = SNIPER_SLOWDOWN + wield_delay = 1.3 SECONDS + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 35, + "y" = 15, + ), + ) + +/obj/item/gun/energy/sharplite/x11 + name = "X11 Advanced Stopping Pistol" //wayland is better + desc = "An advanced energy revolver with the capacity to shoot both disabler and lethal lasers, as well as futuristic safari nets." + icon_state = "x11" + item_state = "nt_generic" + force = 7 + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite/mini + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite/mini, + ) + ammo_type = list(/obj/item/ammo_casing/energy/disabler/sharplite/hos, /obj/item/ammo_casing/energy/laser/sharplite/hos, /obj/item/ammo_casing/energy/trap) + ammo_x_offset = 1 + shaded_charge = TRUE + slot_available = list( + ATTACHMENT_SLOT_RAIL = 1, + ) + + slot_offsets = list( + ATTACHMENT_SLOT_RAIL = list( + "x" = 26, + "y" = 15, + ), + ) + +/obj/item/gun/energy/laser/retro + name ="SL L104" + desc = "An antiquated model of the L204, no longer used or sold by Sharplite. Nevertheless, the sheer popularity of this model makes it a somewhat common sight to this day." + + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + + icon_state = "laser" + item_state = "laser" + + manufacturer = MANUFACTURER_SHARPLITE + default_ammo_type = /obj/item/stock_parts/cell/gun/sharplite + + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/sharplite, + /obj/item/stock_parts/cell/gun/sharplite/plus, + /obj/item/stock_parts/cell/gun/sharplite/empty, + /obj/item/stock_parts/cell/gun/sharplite/plus/empty, + ) + +/obj/item/gun/energy/laser/captain + name = "antique laser gun" + icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/nanotrasen_sharplite/onmob.dmi' + icon_state = "caplaser" + item_state = "caplaser" + desc = "This is the SL X-00, an antique laser gun, out of production for decades and well beyond anyone's capacity to recreate. All craftsmanship is of the highest quality. It is decorated with ashdrake leather and chrome. The gun menaces with spikes of energy. On the item is an image of a space station. The station is exploding." + force = 10 + ammo_x_offset = 3 + selfcharge = TRUE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + manufacturer = MANUFACTURER_SHARPLITE + +/obj/item/gun/energy/laser/captain/brazil + icon_state = "capgun_brazil" + item_state = "caplaser" + desc = "This is the SL X-00, an antique laser gun, out of production for decades and well beyond anyone's capacity to recreate. It seems all the high quality materials it was once made of are now scratched up and torn. The nuclear power cell has been removed, and the gun will no longer automatically recharge." + selfcharge = FALSE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm new file mode 100644 index 0000000000..b74023662c --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm @@ -0,0 +1,1124 @@ +#define SCARBOROUGH_ATTACHMENTS list(/obj/item/attachment/silencer, /obj/item/attachment/laser_sight, /obj/item/attachment/rail_light, /obj/item/attachment/bayonet, /obj/item/attachment/energy_bayonet, /obj/item/attachment/ammo_counter,/obj/item/attachment/gun) +#define SCARBOROUGH_ATTACH_SLOTS list(ATTACHMENT_SLOT_MUZZLE = 1, ATTACHMENT_SLOT_SCOPE = 1, ATTACHMENT_SLOT_RAIL = 1) + +//########### PISTOLS ###########// +/obj/item/gun/ballistic/automatic/pistol/ringneck + name = "PC-76 \"Ringneck\"" + desc = "A compact handgun used by most Syndicate-affiliated groups. Small enough to conceal in most pockets, making it popular for covert elements and simply as a compact defensive weapon. Chambered in 10x22mm." + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "ringneck" + item_state = "sa_generic" + + w_class = WEIGHT_CLASS_SMALL + default_ammo_type = /obj/item/ammo_box/magazine/m10mm_ringneck + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m10mm_ringneck, + ) + + fire_sound = 'sound/weapons/gun/pistol/shot.ogg' + dry_fire_sound = 'sound/weapons/gun/pistol/dry_fire.ogg' + suppressed_sound = 'sound/weapons/gun/pistol/shot_suppressed.ogg' + + load_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release_alt.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release_alt.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + manufacturer = MANUFACTURER_SCARBOROUGH + show_magazine_on_sprite = TRUE + + valid_attachments = SCARBOROUGH_ATTACHMENTS + slot_available = SCARBOROUGH_ATTACH_SLOTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 27, + "y" = 23, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 16, + "y" = 25, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 21, + "y" = 19, + ) + ) + + + spread = 6 //becuase its compact, spread is slightly worse + spread_unwielded = 9 + recoil_unwielded = 2 + +NO_MAG_GUN_HELPER(automatic/pistol/ringneck) + +/obj/item/gun/ballistic/automatic/pistol/ringneck/indie + name = "Ringneck-76" + desc = "A service handgun popular among law enforcement, mercenaries, and independent spacers with discerning tastes. Chambered in 10x22mm." + + icon_state = "ringneck76" + item_state = "sa_indie" + + w_class = WEIGHT_CLASS_NORMAL + + spread = 5 //this one is normal sized, thus in theory its better, in theory at least + spread_unwielded = 7 + recoil_unwielded = 3 + +NO_MAG_GUN_HELPER(automatic/pistol/ringneck/indie) + + +/obj/item/ammo_box/magazine/m10mm_ringneck + name = "Ringneck pistol magazine (10x22mm)" + desc = "An 8-round magazine for the Ringneck pistol. These rounds do moderate damage, but struggle against armor." + icon_state = "ringneck_mag-1" + base_icon_state = "ringneck_mag" + ammo_type = /obj/item/ammo_casing/c10mm + caliber = "10x22mm" + max_ammo = 8 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m10mm_ringneck/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/pistol/asp + name = "BC-81 \"Asp\"" + desc = "An armor-piercing combat handgun once used by Syndicate strike teams, now primarily used by descendants of the Gorlex Marauders. Chambered in 5.7mm." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "asp" + item_state = "sa_generic" + + default_ammo_type = /obj/item/ammo_box/magazine/m57_39_asp + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m57_39_asp, + ) + + fire_sound = 'sound/weapons/gun/pistol/asp.ogg' + + load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + manufacturer = MANUFACTURER_SCARBOROUGH + show_magazine_on_sprite = TRUE + + valid_attachments = SCARBOROUGH_ATTACHMENTS + unique_attachments = list(/obj/item/attachment/scope) + slot_available = SCARBOROUGH_ATTACH_SLOTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 32, + "y" = 23, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 15, + "y" = 26, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 23, + "y" = 19, + ) + ) + +NO_MAG_GUN_HELPER(automatic/pistol/asp) + +/obj/item/ammo_box/magazine/m57_39_asp + name = "Asp magazine (5.7x39mm)" + desc = "A 12-round, double-stack magazine for the Asp pistol. These rounds do okay damage with average performance against armor." + icon_state = "asp_mag-12" + base_icon_state = "asp_mag" + ammo_type = /obj/item/ammo_casing/c57x39mm + caliber = "5.7x39mm" + max_ammo = 12 + +/obj/item/ammo_box/magazine/m57_39_asp/update_icon_state() + . = ..() + if(ammo_count() == 12) + icon_state = "[base_icon_state]-12" + else if(ammo_count() >= 10) + icon_state = "[base_icon_state]-10" + else if(ammo_count() >= 5) + icon_state = "[base_icon_state]-5" + else if(ammo_count() >= 1) + icon_state = "[base_icon_state]-1" + else + icon_state = "[base_icon_state]-0" + +/obj/item/ammo_box/magazine/m57_39_asp/empty + start_empty = TRUE + +/obj/item/gun/ballistic/revolver/viper + name = "R-23 \"Viper\"" + desc = "An imposing revolver used by officers and certain agents of Syndicate member factions during the ICW, still favored by captains and high-ranking officers of the former Syndicate. Chambered in .357 Magnum." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + icon_state = "viper" + item_state = "sa_generic" + + fire_sound = 'sound/weapons/gun/revolver/viper.ogg' + rack_sound = 'sound/weapons/gun/revolver/viper_prime.ogg' + load_sound = 'sound/weapons/gun/revolver/load_bullet.ogg' + eject_sound = 'sound/weapons/gun/revolver/empty.ogg' + + dry_fire_sound = 'sound/weapons/gun/revolver/dry_fire.ogg' + + fire_delay = 0.35 SECONDS + + spread = 3 + spread_unwielded = 8 + recoil = 1 + recoil_unwielded = 2 + + semi_auto = TRUE //double action + safety_wording = "safety" + +/obj/item/gun/ballistic/revolver/viper/no_mag + spawn_no_ammo = TRUE + +/obj/item/gun/ballistic/revolver/viper/indie + name = "Viper-23" + desc = "A powerful bull-barrel revolver. Very popular among mercenaries and the occasional well-to-do spacer or pirate for its flashy appearance and powerful cartridge. Chambered in .357 Magnum." + + icon_state = "viper23" + item_state = "viper23" + spread = 5 + spread_unwielded = 10 + +/obj/item/gun/ballistic/revolver/viper/ComponentInitialize() + . = ..() + AddComponent(/datum/component/ammo_hud/revolver) + +/obj/item/gun/ballistic/revolver/viper/indie/no_mag + spawn_no_ammo = TRUE + +/obj/item/gun/ballistic/automatic/pistol/rattlesnake + name = "MP-84 \"Rattlesnake\"" + desc = "A machine pistol, once used by Syndicate infiltrators and special forces during the ICW. Still used by specialists in former Syndicate factions. Chambered in 9x18mm." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + icon_state = "rattlesnake" + item_state = "rattlesnake" + + default_ammo_type = /obj/item/ammo_box/magazine/m9mm_rattlesnake + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m9mm_rattlesnake, + ) + + fire_sound = 'sound/weapons/gun/pistol/rattlesnake.ogg' + dry_fire_sound = 'sound/weapons/gun/pistol/dry_fire.ogg' + suppressed_sound = 'sound/weapons/gun/pistol/shot_suppressed.ogg' + + load_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release_alt.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release_alt.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + show_magazine_on_sprite = TRUE + + valid_attachments = SCARBOROUGH_ATTACHMENTS + unique_attachments = list(/obj/item/attachment/scope) + slot_available = SCARBOROUGH_ATTACH_SLOTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 40, + "y" = 26, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 14, + "y" = 29, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 22, + "y" = 21, + ) + ) + + burst_size = 3 + burst_delay = 0.1 SECONDS + fire_delay = 0.4 SECONDS + wear_minor_threshold = 240 + wear_major_threshold = 720 + wear_maximum = 1200 + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + default_firemode = FIREMODE_SEMIAUTO + +NO_MAG_GUN_HELPER(automatic/pistol/rattlesnake) + +/obj/item/gun/ballistic/automatic/pistol/rattlesnake/inteq + name = "MP-84m Kingsnake" + desc = "A machine pistol obtained from Syndicate stockpiles and lightly modified to Inteq standards. Generally issued only to specialists. Chambered in 9x18mm." + + icon_state = "rattlesnake_inteq" + item_state = "rattlesnake_inteq" + +NO_MAG_GUN_HELPER(automatic/pistol/rattlesnake/inteq) + +/obj/item/gun/ballistic/automatic/pistol/rattlesnake/cottonmouth + name = "MP-84m Cottonmouth" + desc = "A machine pistol obtained from Marauder stockpiles and heavily modified by elements of the Ramzi Clique to accept a larger calibre, with a few largely-ignored drawbacks of 2-round burst and magazine capacity. Chambered in 10x22mm." + + icon_state = "cottonmouth" + item_state = "cottonmouth" + + fire_sound = 'sound/weapons/gun/pistol/asp.ogg' + + default_ammo_type = /obj/item/ammo_box/magazine/m10mm_cottonmouth + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m10mm_cottonmouth, + ) + + wear_rate = 1.5 + + recoil = 0.5 + recoil_unwielded = 3 + burst_size = 2 + burst_delay = 0.1 SECONDS + fire_delay = 0.4 SECONDS + +/obj/item/ammo_box/magazine/m9mm_rattlesnake + name = "Rattlesnake magazine (9x18mm)" + desc = "A long, 18-round double-stack magazine designed for the Rattlesnake machine pistol. These rounds do okay damage, but struggle against armor." + icon_state = "rattlesnake_mag_18" + base_icon_state = "rattlesnake_mag" + ammo_type = /obj/item/ammo_casing/c9mm + caliber = "9x18mm" + max_ammo = 18 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m9mm_rattlesnake/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[ammo_count() == 1 ? 1 : round(ammo_count(),3)]" + +/obj/item/ammo_box/magazine/m9mm_rattlesnake/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m10mm_cottonmouth + name = "Cottonmouth magazine (10x22mm)" + desc = "A long, 14-round double-stack magazine designed for the Cottonmouth modified machine pistol. These rounds do moderate damage, but struggle against armor." + icon_state = "rattlesnake_mag_18" + base_icon_state = "rattlesnake_mag" + ammo_type = /obj/item/ammo_casing/c10mm + caliber = "10x22mm" + max_ammo = 14 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m10mm_cottonmouth/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[ammo_count() == 1 ? 1 : round(ammo_count(),3)]" + +/obj/item/ammo_box/magazine/m10mm_cottonmouth/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/pistol/himehabu + name = "PC-81 \"Himehabu\"" + desc = "An astonishingly compact machine pistol firing ultra-light projectiles, designed to be as small and concealable as possible while remaining a credible threat at very close range. Armor penetration is practically non-existent. Chambered in .22." + + icon_state = "himehabu" + item_state = "sa_generic" + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + + w_class = WEIGHT_CLASS_SMALL + default_ammo_type = /obj/item/ammo_box/magazine/m22lr_himehabu + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m22lr_himehabu, + ) + fire_sound = 'sound/weapons/gun/pistol/himehabu.ogg' + + load_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert_alt.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release_alt.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release_alt.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + show_magazine_on_sprite = TRUE + + valid_attachments = list( + /obj/item/attachment/silencer, + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_SCOPE = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 28, + "y" = 22, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 12, + "y" = 25, + ) + ) + + recoil = -2 + recoil_unwielded = -2 + spread_unwielded = 0 + wield_slowdown = 0 + +NO_MAG_GUN_HELPER(automatic/pistol/himehabu) + +/obj/item/ammo_box/magazine/m22lr_himehabu + name = "pistol magazine (.22 LR)" + desc = "A single-stack handgun magazine designed to chamber .22 LR. It's rather tiny, all things considered." + icon_state = "himehabu_mag-10" + base_icon_state = "himehabu_mag" + ammo_type = /obj/item/ammo_casing/c22lr + caliber = "22lr" + max_ammo = 10 + w_class = WEIGHT_CLASS_SMALL + multiple_sprites = AMMO_BOX_PER_BULLET + +/obj/item/ammo_box/magazine/m22lr_himehabu/empty + start_empty = TRUE + +//########### SMGS ###########// + + +/obj/item/gun/ballistic/automatic/smg/cobra + name = "C-20r \"Cobra\"" + desc = "A bullpup submachine gun with an integrated suppressor, heavily used by Syndicate strike teams during the ICW. Still sees widespread use by the descendants of the Gorlex Marauders. Chambered in .45." + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "cobra" + item_state = "cobra" + + spread = 3 + + default_ammo_type = /obj/item/ammo_box/magazine/m45_cobra + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m45_cobra, + ) + + fire_sound = 'sound/weapons/gun/smg/cobra.ogg' + + load_sound = 'sound/weapons/gun/smg/cm5_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/cm5_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/cm5_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/cm5_unload.ogg' + + show_magazine_on_sprite = TRUE + show_magazine_on_sprite_ammo = TRUE + show_ammo_capacity_on_magazine_sprite = TRUE + manufacturer = MANUFACTURER_SCARBOROUGH + + valid_attachments = SCARBOROUGH_ATTACHMENTS + default_attachments = list(/obj/item/attachment/silencer/cobra) + unique_attachments = list(/obj/item/attachment/silencer/cobra) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 27, + "y" = 23, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 28, + "y" = 16, + ) + ) + +NO_MAG_GUN_HELPER(automatic/smg/cobra) + +/obj/item/gun/ballistic/automatic/smg/cobra/indie + name = "Cobra-20" + desc = "An older model of submachine gun manufactured by Scarborough Arms and marketed to mercenaries, law enforcement, and independent militia. Only became popular after the end of the ICW. Chambered in .45." + icon_state = "cobra20" + item_state = "cobra20" + + burst_size = 4 + burst_delay = 0.8 + fire_delay = 3 + spread = 2 + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst") + default_attachments = null + valid_attachments = SCARBOROUGH_ATTACHMENTS + unique_attachments = null + + +NO_MAG_GUN_HELPER(automatic/smg/cobra/indie) + + +/obj/item/ammo_box/magazine/m45_cobra + +/obj/item/ammo_box/magazine/m45_cobra + name = "Cobra magazine (.45)" + desc = "A 24-round magazine for the Cobra submachine gun. These rounds do moderate damage, but struggle against armor." + icon_state = "cobra_mag-24" + base_icon_state = "cobra_mag" + ammo_type = /obj/item/ammo_casing/c45 + caliber = ".45" + max_ammo = 24 + +/obj/item/ammo_box/magazine/m45_cobra/update_icon_state() + . = ..() + icon_state = "[base_icon_state]-[round(ammo_count(),2)]" + +/obj/item/ammo_box/magazine/m45_cobra/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/smg/sidewinder + name = "CDW-81 \"Sidewinder\"" + desc = "An armor-piercing, compact personal defense weapon, introduced late into the Inter-Corporate War as an improvement over the C-20r when fighting armored personnel. Issued only in small numbers, and used today by specialists of former Syndicate factions. Chambered in 5.7mm." + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "sidewinder" + item_state = "sidewinder" + + default_ammo_type = /obj/item/ammo_box/magazine/m57_39_sidewinder + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m57_39_sidewinder, + ) + + fire_sound = 'sound/weapons/gun/smg/sidewinder.ogg' + + load_sound = 'sound/weapons/gun/smg/sidewinder_reload.ogg' + load_empty_sound = 'sound/weapons/gun/smg/sidewinder_reload.ogg' + eject_sound = 'sound/weapons/gun/smg/sidewinder_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/smg/sidewinder_unload.ogg' + + rack_sound = 'sound/weapons/gun/smg/sidewinder_cocked.ogg' + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_NORMAL + + show_magazine_on_sprite = TRUE + show_magazine_on_sprite_ammo = TRUE + show_ammo_capacity_on_magazine_sprite = TRUE + manufacturer = MANUFACTURER_SCARBOROUGH + + valid_attachments = SCARBOROUGH_ATTACHMENTS + unique_attachments = list( + /obj/item/attachment/foldable_stock/sidewinder + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1, + ATTACHMENT_SLOT_STOCK = 1, + ATTACHMENT_SLOT_SCOPE = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 44, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 35, + "y" = 17, + ), + ATTACHMENT_SLOT_STOCK = list( + "x" = 17, + "y" = 18, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 21, + "y" = 24, + ) + ) + + spread = 7 + spread_unwielded = 10 + + recoil = 0 + recoil_unwielded = 4 + + default_attachments = list(/obj/item/attachment/foldable_stock/sidewinder) + + +NO_MAG_GUN_HELPER(automatic/smg/sidewinder) + +/obj/item/ammo_box/magazine/m57_39_sidewinder + name = "Sidewinder magazine (5.7x39mm)" + desc = "A 30-round magazine for the Sidewinder personal defense weapon. These rounds do okay damage with average performance against armor." + icon_state = "sidewinder_mag-1" + base_icon_state = "sidewinder_mag" + ammo_type = /obj/item/ammo_casing/c57x39mm + caliber = "5.7x39mm" + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m57_39_sidewinder/empty + start_empty = TRUE + +//########### MARKSMAN ###########// +/obj/item/gun/ballistic/automatic/marksman/boomslang + name = "MSR-90 \"Boomslang\"" + desc = "A bullpup semi-automatic sniper rifle with a high-magnification scope. Compact and capable of rapid follow-up fire without sacrificing power. Used by Syndicate support units and infiltrators during the ICW. Chambered in 6.5mm CLIP." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + icon_state = "boomslang" + item_state = "boomslang" + + fire_sound = 'sound/weapons/gun/sniper/cmf90.ogg' + + default_ammo_type = /obj/item/ammo_box/magazine/boomslang + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/boomslang, + ) + w_class = WEIGHT_CLASS_BULKY + + fire_delay = 1 SECONDS + + show_magazine_on_sprite = TRUE + unique_mag_sprites_for_variants = TRUE + show_ammo_capacity_on_magazine_sprite = TRUE + manufacturer = MANUFACTURER_SCARBOROUGH + spread = -5 + spread_unwielded = 35 + recoil = 2 + recoil_unwielded = 10 + wield_slowdown = LIGHT_SNIPER_SLOWDOWN + wield_delay = 1.3 SECONDS + + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + + valid_attachments = SCARBOROUGH_ATTACHMENTS + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 19, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 28, + "y" = 10, + ) + ) + +NO_MAG_GUN_HELPER(automatic/marksman/boomslang) + +/obj/item/gun/ballistic/automatic/marksman/boomslang/indie + name = "Boomslang-90" + desc = "A modern semi-automatic hunting rifle. Its relative portability and fast follow-up potential compared to other weapons in its class have made it very popular with well-to-do hunters and the occasional law enforcement agency or mercenary. Chambered in 6.5mm CLIP." + + icon_state = "boomslang90" + item_state = "boomslang90" + + zoom_amt = 6 + zoom_out_amt = 2 + +NO_MAG_GUN_HELPER(automatic/marksman/boomslang/indie) + +/obj/item/ammo_box/magazine/boomslang + name = "\improper Boomslang Magazine (6.5mm CLIP)" + desc = "A large 10-round box magazine for Boomslang sniper rifles. These rounds deal amazing damage and can pierce protective equipment, excluding armored vehicles." + base_icon_state = "boomslang" + icon_state = "boomslang-10" + ammo_type = /obj/item/ammo_casing/a65clip + caliber = "6.5mm CLIP" + max_ammo = 10 + multiple_sprites = AMMO_BOX_PER_BULLET + +/obj/item/ammo_box/magazine/boomslang/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/boomslang/short + name = "\improper Boomslang Magazine (6.5mm CLIP)" + desc = "A 5-round box magazine for Boomslang sniper rifles. These rounds deal amazing damage and can pierce protective equipment, excluding armored vehicles." + base_icon_state = "boomslang_short" + icon_state = "boomslang_short-5" + ammo_type = /obj/item/ammo_casing/a65clip + caliber = "6.5mm CLIP" + max_ammo = 5 + multiple_sprites = AMMO_BOX_PER_BULLET + +/obj/item/ammo_box/magazine/boomslang/short/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/marksman/taipan + name = "AMR-83 \"Taipan\"" + desc = "A monstrous semi-automatic anti-materiel rifle, surprisingly short for its class. Designed to destroy mechs, light vehicles, and equipment, but more than capable of obliterating regular personnel. Chambered in .50 BMG." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + icon_state = "taipan" + item_state = "taipan" + fire_sound = 'sound/weapons/gun/sniper/shot.ogg' + fire_sound_volume = 90 + vary_fire_sound = FALSE + load_sound = 'sound/weapons/gun/sniper/mag_insert.ogg' + rack_sound = 'sound/weapons/gun/sniper/rack.ogg' + suppressed_sound = 'sound/weapons/gun/general/heavy_shot_suppressed.ogg' + weapon_weight = WEAPON_HEAVY + default_ammo_type = /obj/item/ammo_box/magazine/sniper_rounds + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/sniper_rounds, + ) + w_class = WEIGHT_CLASS_BULKY + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + actions_types = list() + show_magazine_on_sprite = TRUE + manufacturer = MANUFACTURER_SCARBOROUGH + wield_slowdown = AMR_SLOWDOWN + + show_ammo_capacity_on_magazine_sprite = TRUE + + spread = -5 + spread_unwielded = 40 + recoil = 5 + recoil_unwielded = 50 + + wield_delay = 1.3 SECONDS + + valid_attachments = list() + slot_available = list() + +NO_MAG_GUN_HELPER(automatic/marksman/taipan) + + +//########### RIFLES ###########// +/obj/item/gun/ballistic/automatic/assault/hydra + name = "SMR-80 \"Hydra\"" + desc = "Scarborough Arms' premier modular assault rifle platform. This is the basic configuration, optimized for light weight and handiness. A very well-regarded, if expensive and rare, assault rifle. Chambered in 5.56mm CLIP." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "hydra" + item_state = "hydra" + + default_ammo_type = /obj/item/ammo_box/magazine/m556_42_hydra + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m556_42_hydra, + ) + gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "burst fire", FIREMODE_FULLAUTO = "full auto") + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + //gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST, FIREMODE_OTHER) + default_firemode = FIREMODE_SEMIAUTO + show_magazine_on_sprite = FALSE //we do this to avoid making the same of every sprite, see below + + load_sound = 'sound/weapons/gun/rifle/m16_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/m16_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/m16_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/m16_unload.ogg' + + fire_sound = 'sound/weapons/gun/rifle/hydra.ogg' + manufacturer = MANUFACTURER_SCARBOROUGH + + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + burst_size = 2 + burst_delay = 0.1 SECONDS + fire_delay = 0.18 SECONDS + spread = 1 + spread_unwielded = 8 + wield_slowdown = LIGHT_RIFLE_SLOWDOWN + + valid_attachments = SCARBOROUGH_ATTACHMENTS + slot_available = SCARBOROUGH_ATTACH_SLOTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 42, + "y" = 17, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 21, + "y" = 24, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 30, + "y" = 15, + ) + ) + +NO_MAG_GUN_HELPER(automatic/assault/hydra) + +/obj/item/gun/ballistic/automatic/assault/hydra/indie + name = "Hydra-80" + desc = "A dated variant of Scarborough Arms' premier assault rifle platform. Only accepts small magazines and is locked to semi-auto. Chambered in 5.56mm CLIP." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "hydra_80" + item_state = "hydra_80" + + default_ammo_type = /obj/item/ammo_box/magazine/m556_42_hydra/small + blacklisted_ammo_types = list( + /obj/item/ammo_box/magazine/m556_42_hydra/extended, + /obj/item/ammo_box/magazine/m556_42_hydra/casket, + ) + + gun_firemodes = list(FIREMODE_SEMIAUTO) + gun_firenames = list(FIREMODE_SEMIAUTO) + + spread = 3 + spread_unwielded = 10 + +NO_MAG_GUN_HELPER(automatic/assault/hydra/indie) + +//we hard code "hydra", why? because if not, i would need to duplicate the extended/short magazine sprites like 3 fucking times for every variant with a different icon state. this eases the spriting burden +/obj/item/gun/ballistic/automatic/assault/hydra/update_overlays() + . = ..() + if (magazine) + . += "hydra_mag_[magazine.base_icon_state]" + var/capacity_number = 0 + switch(get_ammo() / magazine.max_ammo) + if(0.2 to 0.39) + capacity_number = 20 + if(0.4 to 0.59) + capacity_number = 40 + if(0.6 to 0.79) + capacity_number = 60 + if(0.8 to 0.99) + capacity_number = 80 + if(1.0 to 2.0) //to catch the chambered round + capacity_number = 100 + if (capacity_number) + . += "hydra_mag_[magazine.base_icon_state]_[capacity_number]" + + +/obj/item/gun/ballistic/automatic/assault/hydra/lmg + name = "SAW-80 \"Hydra\"" + desc = "Scarborough Arms' premier modular assault rifle platform. This example is configured as a support weapon, with heavier components for sustained firing and a large muzzle brake. Chambered in 5.56mm CLIP." + + icon_state = "hydra_lmg" + item_state = "hydra_lmg" + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_FULLAUTO + + burst_delay = 0.08 SECONDS + fire_delay = 0.08 SECONDS + spread = 7 + spread_unwielded = 25 + recoil = 2 + recoil_unwielded = 4 + wield_slowdown = SAW_SLOWDOWN + wield_delay = 0.9 SECONDS //ditto + + valid_attachments = SCARBOROUGH_ATTACHMENTS + slot_available = SCARBOROUGH_ATTACH_SLOTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 19, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 21, + "y" = 24, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 30, + "y" = 15, + ) + ) + +/obj/item/gun/ballistic/automatic/assault/hydra/lmg/extended + default_ammo_type = /obj/item/ammo_box/magazine/m556_42_hydra/extended + + +/obj/item/gun/ballistic/automatic/assault/hydra/lmg/casket_mag + default_ammo_type = /obj/item/ammo_box/magazine/m556_42_hydra/casket + +/obj/item/gun/ballistic/automatic/assault/hydra/dmr + name = "SBR-80 \"Hydra\"" + desc = "Scarborough Arms' premier modular assault rifle platform. This example is configured as a marksman rifle, with an extended barrel and medium-zoom scope. Its lightweight cartridge is compensated for with a 2-round burst action, though it is unable to fit large extended magazines. Chambered in 5.56mm CLIP." + + icon_state = "hydra_dmr" + item_state = "hydra_dmr" + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + default_firemode = FIREMODE_SEMIAUTO + + spread = 0 + spread_unwielded = 12 + wield_slowdown = DMR_SLOWDOWN //dmrrrr + wield_delay = 0.85 SECONDS //above + zoomable = TRUE + zoom_amt = 6 + zoom_out_amt = 2 + default_ammo_type = /obj/item/ammo_box/magazine/m556_42_hydra/small + blacklisted_ammo_types = list( + /obj/item/ammo_box/magazine/m556_42_hydra/extended, + /obj/item/ammo_box/magazine/m556_42_hydra/casket, + ) + +NO_MAG_GUN_HELPER(automatic/assault/hydra/dmr) + +/obj/item/gun/ballistic/automatic/assault/hydra/underbarrel_gl + name = "SMR-80 \"Hydra\"" + desc = "Scarborough Arms' premier modular assault rifle platform. This is the basic configuration, optimized for light weight and handiness. A very well-regarded, if expensive and rare, assault rifle. This one has an underslung grenade launcher attached. Chambered in 5.56x42mm CLIP." + + default_attachments = list(/obj/item/attachment/gun/ballistic/launcher) + +/obj/item/ammo_box/magazine/m556_42_hydra + name = "Hydra assault rifle magazine (5.56x42mm CLIP)" + desc = "A simple, 30-round magazine for the Hydra platform of 5.56x42mm CLIP assault rifles. These rounds do moderate damage with good armor penetration." + icon_state = "hydra_mag-30" + base_icon_state = "hydra_mag" + ammo_type = /obj/item/ammo_casing/a556_42 + caliber = "5.56x42mm" + max_ammo = 30 + +/obj/item/ammo_box/magazine/m556_42_hydra/update_icon_state() + . = ..() + if(multiple_sprites == AMMO_BOX_FULL_EMPTY) + return + icon_state = "[base_icon_state]-[ammo_count() == 1 ? 1 : round(ammo_count(),5)]" + +/obj/item/ammo_box/magazine/m556_42_hydra/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m556_42_hydra/small + name = "Short Hydra assault rifle magazine (5.56x42mm CLIP)" + desc = "A short, 20-round magazine for the Hydra platform of 5.56x42mm CLIP assault rifles; intended for the DMR variant. These rounds do moderate damage with good armor penetration." + icon_state = "hydra_small_mag-20" + base_icon_state = "hydra_small_mag" + max_ammo = 20 + +/obj/item/ammo_box/magazine/m556_42_hydra/small/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m556_42_hydra/extended + name = "extended Hydra assault rifle magazine (5.56x42mm CLIP)" + desc = "A bulkier, 60-round magazine for the Hydra platform of 5.56x42mm CLIP assault rifles. These rounds do moderate damage with good armor penetration." + icon_state = "hydra_extended_mag-1" + base_icon_state = "hydra_extended_mag" + max_ammo = 60 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m556_42_hydra/extended/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m556_42_hydra/casket + name = "casket Hydra assault rifle magazine (5.56x42mm CLIP)" + desc = "A very long and bulky 100-round magazine for the Hydra platform of 5.56x42mm CLIP assault rifles. These rounds do moderate damage with good armor penetration." + icon_state = "hydra_casket_mag-1" + base_icon_state = "hydra_casket_mag" + max_ammo = 100 + multiple_sprites = AMMO_BOX_FULL_EMPTY + w_class = WEIGHT_CLASS_NORMAL + +//########### MISC ###########// +// Bulldog shotgun // + +/obj/item/gun/ballistic/shotgun/automatic/bulldog + name = "SG-60r \"Bulldog\"" + desc = "A bullpup combat shotgun usually seen with a characteristic drum magazine. Wildly popular among Syndicate strike teams during the ICW, although it proved less useful against military-grade equipment. Still popular among former Syndicate factions, especially the Ramzi Clique pirates. Chambered in 12g." + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + icon_state = "bulldog" + item_state = "bulldog" + + weapon_weight = WEAPON_MEDIUM + default_ammo_type = /obj/item/ammo_box/magazine/m12g_bulldog + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m12g_bulldog, + ) + fire_delay = 0.4 SECONDS // this NEEDS the old delay. + fire_sound = 'sound/weapons/gun/shotgun/bulldog.ogg' + show_magazine_on_sprite = TRUE +// empty_indicator = TRUE + empty_alarm = TRUE + unique_mag_sprites_for_variants = TRUE + show_ammo_capacity_on_magazine_sprite = TRUE + internal_magazine = FALSE + casing_ejector = TRUE + tac_reloads = TRUE + pickup_sound = 'sound/items/handling/rifle_pickup.ogg' + manufacturer = MANUFACTURER_SCARBOROUGH + + load_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + load_empty_sound = 'sound/weapons/gun/rifle/ar_reload.ogg' + eject_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/rifle/ar_unload.ogg' + + rack_sound = 'sound/weapons/gun/rifle/ar_cock.ogg' + + spread = 3 + spread_unwielded = 15 + recoil = 1 + recoil_unwielded = 4 + wield_slowdown = HEAVY_SHOTGUN_SLOWDOWN + wield_delay = 0.65 SECONDS + + valid_attachments = SCARBOROUGH_ATTACHMENTS + unique_attachments = list(/obj/item/attachment/scope) + slot_available = SCARBOROUGH_ATTACH_SLOTS + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 44, + "y" = 19, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 25, + "y" = 24, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 30, + "y" = 18, + ) + ) + +/obj/item/gun/ballistic/shotgun/automatic/bulldog/drum + default_ammo_type = /obj/item/ammo_box/magazine/m12g_bulldog/drum + +NO_MAG_GUN_HELPER(shotgun/automatic/bulldog) + +/obj/item/ammo_box/magazine/m12g_bulldog + name = "shotgun box magazine (12g buckshot)" + desc = "A single-stack, 8-round box magazine for the Bulldog shotgun and it's derivatives." + icon_state = "bulldog_mag-1" + base_icon_state = "bulldog_mag" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 8 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m12g_bulldog/slug + name = "shotgun box magazine (12g Slugs)" + ammo_type = /obj/item/ammo_casing/shotgun + +/obj/item/ammo_box/magazine/m12g_bulldog/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m12g_bulldog/drum + name = "shotgun drum magazine (12g buckshot)" + desc = "A bulky 12-round drum designed for the Bulldog shotgun and it's derivatives." + icon_state = "bulldog_drum-1" + base_icon_state = "bulldog_drum" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + caliber = "12ga" + max_ammo = 12 + w_class = WEIGHT_CLASS_NORMAL + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m12g_bulldog/drum/empty + start_empty = TRUE + +/obj/item/ammo_box/magazine/m12g_bulldog/drum/stun + name = "shotgun drum magazine (12g taser slugs)" + ammo_type = /obj/item/ammo_casing/shotgun/stunslug + +/obj/item/ammo_box/magazine/m12g_bulldog/drum/slug + name = "shotgun drum magazine (12g slugs)" + ammo_type = /obj/item/ammo_casing/shotgun + +/obj/item/ammo_box/magazine/m12g_bulldog/drum/dragon + name = "shotgun drum magazine (12g dragon's breath)" + ammo_type = /obj/item/ammo_casing/shotgun/dragonsbreath + +/obj/item/ammo_box/magazine/m12g_bulldog/drum/bioterror + name = "shotgun drum magazine (12g bioterror)" + ammo_type = /obj/item/ammo_casing/shotgun/dart/bioterror + +/obj/item/ammo_box/magazine/m12g_bulldog/drum/meteor + name = "shotgun drum magazine (12g meteor slugs)" + ammo_type = /obj/item/ammo_casing/shotgun/meteorslug + +/obj/item/gun/ballistic/rocketlauncher/mako + name = "RR-86 \"Mako\"" + desc = "A large, four-tube rocket launcher, the Mako fires (relatively) small rockets filled with incendiary compound, designed to cause fires and deny enemy movement. Capable of causing significant damage to exosuits on impact, as well." + + icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/scarborough/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/scarborough/onmob.dmi' + + icon_state = "mako" + item_state = "mako" + default_ammo_type = /obj/item/ammo_box/magazine/internal/mako + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/mako, + ) + fire_sound = 'sound/weapons/gun/general/rocket_launch.ogg' + load_sound = 'sound/weapons/gun/general/rocket_load.ogg' + w_class = WEIGHT_CLASS_BULKY + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + burst_size = 1 + fire_delay = 0.4 SECONDS + casing_ejector = FALSE + weapon_weight = WEAPON_HEAVY + bolt_type = BOLT_TYPE_NO_BOLT + internal_magazine = TRUE + cartridge_wording = "rocket" + empty_indicator = TRUE + tac_reloads = FALSE + manufacturer = MANUFACTURER_SCARBOROUGH + + +/obj/item/ammo_box/magazine/internal/mako + name = "mako internal magazine" + ammo_type = /obj/item/ammo_casing/caseless/rocket/a70mm + caliber = "70mm" + max_ammo = 4 + +/obj/item/ammo_casing/caseless/rocket/a70mm + name = "\improper M-KO-9HE" + desc = "A 70mm High Explosive rocket. Fire at mech and pray." + icon_state = "srm-8" + caliber = "70mm" + projectile_type = /obj/projectile/bullet/a84mm_he + auto_rotate = FALSE + +/obj/item/ammo_casing/caseless/rocket/a70mm/hedp + name = "\improper M-KO-9HEDP" + desc = "A 70mm High Explosive Dual Purpose rocket. Pointy end toward armor." + caliber = "70mm" + icon_state = "84mm-hedp" + projectile_type = /obj/projectile/bullet/a84mm + +#undef SCARBOROUGH_ATTACHMENTS +#undef SCARBOROUGH_ATTACH_SLOTS diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm new file mode 100644 index 0000000000..0d8e3a009c --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm @@ -0,0 +1,442 @@ +#define SERENE_ATTACHMENTS list(/obj/item/attachment/rail_light, /obj/item/attachment/bayonet,/obj/item/attachment/scope, /obj/item/attachment/ammo_counter,/obj/item/attachment/gun) +#define SERENE_ATTACH_SLOTS list(ATTACHMENT_SLOT_MUZZLE = 1, ATTACHMENT_SLOT_RAIL = 1, ATTACHMENT_SLOT_SCOPE = 1) + +/* Micro Target */ + +/obj/item/gun/ballistic/automatic/pistol/m17 + name = "Model 17 \"Micro Target\"" + desc = "A lightweight and very accurate target pistol produced by Serene Outdoors. The barrel can be unscrewed for storage. Chambered in .22 LR." + + icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/serene_outdoors/onmob.dmi' + icon_state = "m17" + item_state = "so_generic" + + default_ammo_type = /obj/item/ammo_box/magazine/m17 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m17, + ) + + fire_sound = 'sound/weapons/gun/pistol/himehabu.ogg' + + manufacturer = MANUFACTURER_SERENE + show_magazine_on_sprite = TRUE + bolt_type = BOLT_TYPE_LOCKING + + w_class = WEIGHT_CLASS_SMALL + + spread = 15 + spread_unwielded = 35 + recoil = -2 + recoil_unwielded = -2 + + wield_slowdown = PISTOL_SLOWDOWN + + valid_attachments = list( + /obj/item/attachment/m17_barrel, + ) + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ) + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 30, + "y" = 23, + ), + ) + + default_attachments = list(/obj/item/attachment/m17_barrel) + +EMPTY_GUN_HELPER(automatic/pistol/m17) +NO_MAG_GUN_HELPER(automatic/pistol/m17) + +/obj/item/ammo_box/magazine/m17 + name = "Model 17 magazine (.22lr)" + desc = "A 10-round magazine for the Model 17 \"Micro Target\". These rounds do okay damage with awful performance against armor." + icon_state = "m17_mag-1" + base_icon_state = "m17_mag" + ammo_type = /obj/item/ammo_casing/c22lr + caliber = "22lr" + max_ammo = 10 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m17/empty + start_empty = TRUE + +/* Auto Elite */ + +/obj/item/gun/ballistic/automatic/pistol/m20_auto_elite + name = "Model 20 \"Auto Elite\"" + desc = "A large handgun chambered .44 Roumain. Originally developed by Serene Outdoors for the Star City Police Department when their older handguns proved underpowered, the Auto Elite proved heavy and unwieldy in practice. It has nevertheless seen modest success as a sidearm for big game hunters and among customers looking to make an impression." + + icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/serene_outdoors/onmob.dmi' + icon_state = "m20" + item_state = "so_generic" + + default_ammo_type = /obj/item/ammo_box/magazine/m20_auto_elite + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m20_auto_elite, + ) + + fire_sound = 'sound/weapons/gun/pistol/cm23.ogg' + rack_sound = 'sound/weapons/gun/pistol/candor_cocked.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/slide_lock.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/slide_drop.ogg' + manufacturer = MANUFACTURER_SERENE + load_sound = 'sound/weapons/gun/pistol/deagle_reload.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/deagle_reload.ogg' + eject_sound = 'sound/weapons/gun/pistol/deagle_unload.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/deagle_unload.ogg' + + recoil_unwielded = 3 + recoil = 0.5 + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 33, + "y" = 22, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 19, + "y" = 17, + ) + ) + +NO_MAG_GUN_HELPER(automatic/pistol/m20_auto_elite) + +/obj/item/ammo_box/magazine/m20_auto_elite + name = "Model 20 magazine (.44 Roumain)" + desc = "A nine-round magazine designed for the Model 20 pistol. These rounds do good damage, and fare better against armor." + icon_state = "cm23_mag-1" + base_icon_state = "cm23_mag" + ammo_type = /obj/item/ammo_casing/a44roum + caliber = ".44 Roumain" + max_ammo = 9 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m20_auto_elite/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/pistol/m20_auto_elite/inteq + name = "PO-20 Pinscher" + desc = "A large handgun chambered .44 Roumain and manufactured by Serene Outdoors. Modified to Inteq Risk Management Group's standards and issued as a heavy sidearm for officers." + + icon = 'icons/obj/guns/manufacturer/inteq/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/inteq/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/inteq/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/inteq/onmob.dmi' + icon_state = "m20_inteq" + item_state = "m20_inteq" + + default_ammo_type = /obj/item/ammo_box/magazine/m20_auto_elite + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m20_auto_elite, + ) + +/obj/item/ammo_box/magazine/m20_auto_elite/inteq/empty + start_empty = TRUE + +/* Sporter */ + +/obj/item/gun/ballistic/automatic/m12_sporter + name = "Model 12 \"Sporter\"" + desc = "An extremely popular target shooting rifle produced by Serene Outdoors. Inexpensive, widely available, and produced in massive numbers, the Sporter is also popular for hunting small game and ground birds. Chambered in .22 LR." + + icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/serene_outdoors/onmob.dmi' + icon_state = "m12" + item_state = "m12" + + weapon_weight = WEAPON_MEDIUM + default_ammo_type = /obj/item/ammo_box/magazine/m12_sporter + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m12_sporter, + ) + + fire_delay = 0.4 SECONDS + burst_size = 1 + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + show_magazine_on_sprite = TRUE + bolt_type = BOLT_TYPE_LOCKING + + fire_sound = 'sound/weapons/gun/gauss/claris.ogg' + + spread = 0 + spread_unwielded = 15 + recoil = 0 + recoil_unwielded = 2 + wield_slowdown = LIGHT_RIFLE_SLOWDOWN + wield_delay = 1 SECONDS + + manufacturer = MANUFACTURER_SERENE + + valid_attachments = SERENE_ATTACHMENTS + slot_available = SERENE_ATTACH_SLOTS + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 44, + "y" = 18, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 17, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 28, + "y" = 17, + ) + ) + +EMPTY_GUN_HELPER(automatic/m12_sporter) + + +/obj/item/ammo_box/magazine/m12_sporter + name = "Model 12 magazine (.22lr)" + desc = "A 25-round magazine for the Model 12 \"Sporter\". These rounds do okay damage with awful performance against armor." + icon_state = "m12_mag-1" + base_icon_state = "m12_mag" + ammo_type = /obj/item/ammo_casing/c22lr + caliber = "22lr" + max_ammo = 25 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m12_sporter/empty + start_empty = TRUE + +/obj/item/gun/ballistic/automatic/m12_sporter/mod + name = "Model 13 \"Larker\"" + desc = "A common after-market modification of the Model 12 \"Sporter\" rifle, keyed to fire a three round burst." + burst_size = 3 + burst_delay = 0.6 + + icon_state = "larker" + item_state = "larker" + + wear_minor_threshold = 240 + wear_major_threshold = 720 + wear_maximum = 1200 + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_BURST) + gun_firenames = list(FIREMODE_SEMIAUTO = "single", FIREMODE_BURST = "triptych") + default_firemode = FIREMODE_BURST + +EMPTY_GUN_HELPER(automatic/m12_sporter/mod) + +/* woodsman */ + +/obj/item/gun/ballistic/automatic/marksman/woodsman + name = "Model 23 Woodsman" + desc = "A large semi-automatic hunting rifle manufactured by Serene Outdoors. Its powerful cartridge, excellent ergonomics and ease of use make it highly popular for hunting big game Chambered in 8x50mmR." + + icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/serene_outdoors/onmob.dmi' + icon_state = "woodsman" + item_state = "woodsman" + + default_ammo_type = /obj/item/ammo_box/magazine/m23 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m23, + ) + + unique_mag_sprites_for_variants = TRUE + + fire_sound = 'sound/weapons/gun/rifle/ssg669c.ogg' + + manufacturer = MANUFACTURER_SERENE + show_magazine_on_sprite = TRUE + + bolt_type = BOLT_TYPE_LOCKING + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + w_class = WEIGHT_CLASS_BULKY + weapon_weight = WEAPON_MEDIUM + + spread = -4 + spread_unwielded = 20 + recoil = 1.25 + recoil_unwielded = 6 + fire_delay = 0.75 SECONDS + wield_delay = 1.15 SECONDS //a little longer and less wieldy than other DMRs + zoom_out_amt = 2 + + can_be_sawn_off = FALSE + + valid_attachments = SERENE_ATTACHMENTS + slot_available = list( + ATTACHMENT_SLOT_MUZZLE = 1, + ATTACHMENT_SLOT_RAIL = 1 + ) + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 48, + "y" = 20, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 32, + "y" = 18, + ) + ) + +EMPTY_GUN_HELPER(automatic/marksman/woodsman) +NO_MAG_GUN_HELPER(automatic/marksman/woodsman) + +/obj/item/ammo_box/magazine/m23 + name = "Model 23 magazine (8x50mmR)" + desc = "A 5-round magazine for the Model 23 \"Woodsman\". These rounds do high damage, with excellent armor penetration." + icon_state = "woodsman_mag-1" + base_icon_state = "woodsman_mag" + ammo_type = /obj/item/ammo_casing/a8_50r + caliber = "8x50mmR" + max_ammo = 5 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m23/empty + start_empty = TRUE + +/* super soaker */ + +/obj/item/gun/ballistic/automatic/m15 + name = "Model 15 Super Sporter" + desc = "A popular semi-automatic hunting rifle produced by Serene Outdoors. Solid all-round performance, high accuracy, and ease of access compared to military rifles makes the Super Sporter a popular choice for hunting medium game and occasionally self-defense. Chambered in 7.62x40mm CLIP." + + icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/serene_outdoors/onmob.dmi' + icon_state = "m15" + item_state = "m15" + + default_ammo_type = /obj/item/ammo_box/magazine/m15 + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/m15, + ) + + fire_sound = 'sound/weapons/gun/rifle/m16.ogg' + + manufacturer = MANUFACTURER_SERENE + show_magazine_on_sprite = TRUE + + bolt_type = BOLT_TYPE_LOCKING + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + w_class = WEIGHT_CLASS_BULKY + weapon_weight = WEAPON_MEDIUM + + spread = 0 + spread_unwielded = 20 + recoil = 0.5 + recoil_unwielded = 3 + wield_slowdown = LIGHT_RIFLE_SLOWDOWN + wield_delay = 1 SECONDS + + fire_delay = 3 + + wear_minor_threshold = 200 + wear_major_threshold = 600 + wear_maximum = 1200 + + valid_attachments = SERENE_ATTACHMENTS + slot_available = SERENE_ATTACH_SLOTS + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 47, + "y" = 21, + ), + ATTACHMENT_SLOT_SCOPE = list( + "x" = 15, + "y" = 23, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 24, + "y" = 19, + ) + ) + +EMPTY_GUN_HELPER(automatic/m15) +NO_MAG_GUN_HELPER(automatic/m15) + +/obj/item/ammo_box/magazine/m15 + name = "Model 15 magazine (7.62x40mm CLIP)" + desc = "A 20-round magazine for the Model 15 \"Super Sporter\". These rounds do good damage with good armor penetration." + icon_state = "p16_mag-1" + base_icon_state = "p16_mag" + ammo_type = /obj/item/ammo_casing/a762_40 + caliber = "7.62x40mm" + max_ammo = 20 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/m15/empty + start_empty = TRUE + +/* cuckmaster */ + +/obj/item/gun/ballistic/shotgun/automatic/m11 + name = "Model 11 \"Buckmaster\"" + desc = "A semi-automatic hunting shotgun produced by Serene Outdoors. Much lighter and handier than military combat shotguns, it offers the same fire rate and magazine capacity, making it an excellent choice for hunting birds and large game or for security forces looking to upgrade from pump action guns. Chambered in 12g." + + icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/serene_outdoors/onmob.dmi' + icon_state = "buckmaster" + item_state = "buckmaster" + + fire_delay = 0.5 SECONDS + default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/buckmaster + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/shot/buckmaster, + ) + w_class = WEIGHT_CLASS_BULKY + + bolt_type = BOLT_TYPE_LOCKING + + fire_sound = 'sound/weapons/gun/shotgun/bulldog.ogg' + + spread = 3 + spread_unwielded = 15 + recoil = 1 + recoil_unwielded = 4 + wield_slowdown = SHOTGUN_SLOWDOWN + wield_delay = 0.65 SECONDS + + casing_ejector = TRUE + + manufacturer = MANUFACTURER_SERENE + + valid_attachments = SERENE_ATTACHMENTS + slot_available = SERENE_ATTACH_SLOTS + + slot_offsets = list( + ATTACHMENT_SLOT_MUZZLE = list( + "x" = 45, + "y" = 18, + ), + ATTACHMENT_SLOT_RAIL = list( + "x" = 35, + "y" = 17, + ) + ) + +/obj/item/ammo_box/magazine/internal/shot/buckmaster + name = "Buckmaster internal magazine" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + max_ammo = 6 + +EMPTY_GUN_HELPER(shotgun/automatic/m11) + +#undef SERENE_ATTACHMENTS +#undef SERENE_ATTACH_SLOTS diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm new file mode 100644 index 0000000000..0940eddf55 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm @@ -0,0 +1,213 @@ +#define SOLAR_ATTACHMENTS list(/obj/item/attachment/laser_sight,/obj/item/attachment/rail_light,/obj/item/attachment/bayonet,/obj/item/attachment/energy_bayonet,/obj/item/attachment/scope,/obj/item/attachment/gun) +#define SOLAR_ATTACH_SLOTS list(ATTACHMENT_SLOT_MUZZLE = 1, ATTACHMENT_SLOT_SCOPE = 1, ATTACHMENT_SLOT_RAIL = 1) + +///SOLAR ARMORIES +//fuck you im not typing the full name out +//solarwaffledesuckenmydickengeschutzenweaponmanufacturinglocation + +///Pistols +/obj/item/gun/ballistic/automatic/powered/gauss/modelh + name = "Model H" + desc = "A standard-issue pistol exported from the Solarian Confederation. It fires slow flesh-rending ferromagnetic slugs at a high energy cost, however they are ineffective on any armor." + + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + icon_state = "model-h" + item_state = "model-h" + fire_sound = 'sound/weapons/gun/gauss/modelh.ogg' + load_sound = 'sound/weapons/gun/gauss/pistol_reload.ogg' + + default_ammo_type = /obj/item/ammo_box/magazine/modelh + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/modelh, + ) + default_cell_type = /obj/item/stock_parts/cell/gun/solgov + allowed_cell_types = list( + /obj/item/stock_parts/cell/gun/solgov, + ) + + slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_SUITSTORE + w_class = WEIGHT_CLASS_SMALL + fire_delay = 0.6 SECONDS //pistol, but heavy caliber. + show_magazine_on_sprite = FALSE + empty_indicator = FALSE + manufacturer = MANUFACTURER_SOLARARMORIES + recoil = 2 + recoil_unwielded = 4 + spread = 6 + spread_unwielded = 12 + fire_select_icon_state_prefix = "slug_" + + //gauss doesn't explode so there's not light. + light_range = 0 + + valid_attachments = list( + /obj/item/attachment/laser_sight, + /obj/item/attachment/rail_light, + ) + +/obj/item/gun/ballistic/automatic/powered/gauss/modelh/no_mag + default_ammo_type = FALSE + +/obj/item/gun/ballistic/automatic/powered/gauss/modelh/suns + desc = "A standard-issue pistol exported from the Solarian Confederation. It fires slow flesh-rending ferromagnetic slugs at a high energy cost, however they are ineffective on any armor. It is painted in the colors of SUNS." + default_ammo_type = /obj/item/ammo_box/magazine/modelh + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/modelh, + ) + icon_state = "model-h_suns" + item_state = "model-h_suns" + +//not gauss pistol +/obj/item/gun/ballistic/automatic/pistol/solgov + name = "\improper Pistole C" + desc = "A favorite of the Terran Regency that is despised by the Solarian bureaucracy. Shifted out of military service centuries ago, though still popular among civilians. Chambered in 5.56mm caseless." + icon_state = "pistole-c" + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + + weapon_weight = WEAPON_LIGHT + default_ammo_type = /obj/item/ammo_box/magazine/pistol556mm + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/pistol556mm, + ) + fire_sound = 'sound/weapons/gun/pistol/pistolec.ogg' + manufacturer = MANUFACTURER_SOLARARMORIES + load_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + load_empty_sound = 'sound/weapons/gun/pistol/mag_insert.ogg' + eject_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + eject_empty_sound = 'sound/weapons/gun/pistol/mag_release.ogg' + + rack_sound = 'sound/weapons/gun/pistol/rack_small.ogg' + lock_back_sound = 'sound/weapons/gun/pistol/lock_small.ogg' + bolt_drop_sound = 'sound/weapons/gun/pistol/drop_small.ogg' + + fire_select_icon_state_prefix = "caseless_" + +/obj/item/gun/ballistic/automatic/pistol/solgov/old + icon_state = "pistole-c-old" + +///Rifles + +/obj/item/gun/ballistic/automatic/powered/gauss/claris + name = "Claris" + desc = "An antiquated Solarian rifle. Chambered in ferromagnetic pellets, just as the founding Solarians intended." + default_ammo_type = /obj/item/ammo_box/magazine/internal/claris + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/claris, + ) + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + icon_state = "claris" + item_state = "claris" + fire_sound = 'sound/weapons/gun/gauss/claris.ogg' + load_sound = 'sound/weapons/gun/gauss/sniper_reload.ogg' + default_cell_type = /obj/item/stock_parts/cell/gun/solgov + allowed_cell_types = list( + /obj/item/stock_parts/cell/gun/solgov, + ) + fire_delay = 0.4 SECONDS + bolt_type = BOLT_TYPE_NO_BOLT + internal_magazine = TRUE + show_magazine_on_sprite = FALSE + empty_indicator = FALSE + manufacturer = MANUFACTURER_SOLARARMORIES + fire_select_icon_state_prefix = "pellet_" + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + valid_attachments = SOLAR_ATTACHMENTS + slot_available = SOLAR_ATTACH_SLOTS + //gauss doesn't explode so there's not light. + light_range = 0 + + doesnt_keep_bullet = TRUE + + +/obj/item/gun/ballistic/automatic/powered/gauss/claris/suns + desc = "An antiquated Solarian rifle. Chambered in ferromagnetic pellets, just as the founding Solarians intended. Evidently, SUNS' founders echo the sentiment, as it appears to be painted in their colors." + icon_state = "claris_suns" + item_state = "claris_suns" + +/obj/item/gun/ballistic/automatic/powered/gauss/gar + name = "Solar 'GAR' Carbine" + desc = "A Solarian carbine, unusually modern for its producers. Launches ferromagnetic lances at alarming speeds." + default_ammo_type = /obj/item/ammo_box/magazine/gar + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/gar, + ) + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + icon_state = "gar" + item_state = "gar" + fire_sound = 'sound/weapons/gun/gauss/gar.ogg' + load_sound = 'sound/weapons/gun/gauss/rifle_reload.ogg' + default_cell_type = /obj/item/stock_parts/cell/gun/solgov + allowed_cell_types = list( + /obj/item/stock_parts/cell/gun/solgov, + ) + burst_size = 1 + + fire_delay = 0.2 SECONDS + + actions_types = list() + empty_indicator = FALSE + manufacturer = MANUFACTURER_SOLARARMORIES + + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE + + valid_attachments = SOLAR_ATTACHMENTS + slot_available = SOLAR_ATTACH_SLOTS + + //gauss doesn't explode so there's not light. + light_range = 0 + + gun_firemodes = list(FIREMODE_SEMIAUTO, FIREMODE_FULLAUTO) + default_firemode = FIREMODE_SEMIAUTO + + wield_delay = 0.7 SECONDS + fire_select_icon_state_prefix = "lance_" + +/obj/item/gun/ballistic/automatic/powered/gauss/gar/suns + desc = "A Solarian carbine, unusually modern for its producers. It's just modern enough for SUNS, however, who have painted the weapon in their colors. Launches ferromagnetic lances at alarming speeds." + icon_state = "gar_suns" + item_state = "gar_suns" + +///Sniper +/obj/item/gun/ballistic/rifle/solgov + name = "SSG-669C" + desc = "A bolt-action sniper rifle used by Solarian troops. Beloved for its rotary design and accuracy. Chambered in 8x58mm Caseless." + default_ammo_type = /obj/item/ammo_box/magazine/internal/boltaction/solgov + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/boltaction/solgov, + ) + icon_state = "ssg669c" + item_state = "ssg669c" + icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' + lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' + righthand_file = 'icons/obj/guns/manufacturer/solararmories/righthand.dmi' + mob_overlay_icon = 'icons/obj/guns/manufacturer/solararmories/onmob.dmi' + + fire_sound = 'sound/weapons/gun/rifle/ssg669c.ogg' + can_be_sawn_off = FALSE + + zoomable = TRUE + zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. + zoom_out_amt = 5 + + manufacturer = MANUFACTURER_SOLARARMORIES + spread = -5 + spread_unwielded = 20 + recoil = 1 + recoil_unwielded = 8 + wield_slowdown = SNIPER_SLOWDOWN + wield_delay = 1.3 SECONDS + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/beam_rifle.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/beam_rifle.dm new file mode 100644 index 0000000000..466dac85b4 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -0,0 +1,543 @@ + +#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 targeted 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 = ITEM_SLOT_BACK + force = 15 + custom_materials = null + recoil = 4 + ammo_x_offset = 3 + ammo_y_offset = 3 + modifystate = FALSE + charge_sections = 1 + weapon_weight = WEAPON_HEAVY + w_class = WEIGHT_CLASS_BULKY + ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan) + internal_magazine = FALSE + default_ammo_type = /obj/item/stock_parts/cell/gun/large + allowed_ammo_types = list( + /obj/item/stock_parts/cell/gun/large, + ) + canMouseDown = TRUE + 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 = 25 + var/lastfire = 0 + + //ZOOMING + var/zoom_current_view_increase = 0 + ///The radius you want to zoom by + var/zoom_target_view_increase = 9.5 + var/zooming = FALSE + var/zoom_lock = ZOOM_LOCK_OFF + var/zooming_angle + var/current_zoom_x = 0 + var/current_zoom_y = 0 + + var/datum/action/item_action/zoom_lock_action/zoom_lock_action + var/mob/listeningTo + +/obj/item/gun/energy/beam_rifle/debug + delay = 0 + default_ammo_type = /obj/item/stock_parts/cell/infinite + allowed_ammo_types = list( + /obj/item/stock_parts/cell/infinite, + ) + aiming_time = 0 + recoil = 0 + +/obj/item/gun/energy/beam_rifle/equipped(mob/user) + set_user(user) + return ..() + +/obj/item/gun/energy/beam_rifle/pickup(mob/user) + set_user(user) + return ..() + +/obj/item/gun/energy/beam_rifle/dropped(mob/user) + set_user() + return ..() + +/obj/item/gun/energy/beam_rifle/ui_action_click(mob/user, actiontype) + if(istype(actiontype, zoom_lock_action)) + zoom_lock++ + if(zoom_lock > 3) + zoom_lock = 0 + switch(zoom_lock) + if(ZOOM_LOCK_AUTOZOOM_FREEMOVE) + to_chat(user, span_boldnotice("You switch [src]'s zooming processor to free directional.")) + if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK) + to_chat(user, span_boldnotice("You switch [src]'s zooming processor to locked directional.")) + if(ZOOM_LOCK_CENTER_VIEW) + to_chat(user, span_boldnotice("You switch [src]'s zooming processor to center mode.")) + if(ZOOM_LOCK_OFF) + to_chat(user, span_boldnotice("You disable [src]'s zooming system.")) + reset_zooming() + else + ..() + +/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.view_size.setTo(zoom_target_view_increase) + zoom_current_view_increase = zoom_target_view_increase + set_autozoom_pixel_offsets_immediate(zooming_angle) + +/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 + user.client.view_size.zoomIn() + zoom_current_view_increase = 0 + zooming_angle = 0 + current_zoom_x = 0 + current_zoom_y = 0 + +/obj/item/gun/energy/beam_rifle/unique_action(mob/living/user) + projectile_setting_pierce = !projectile_setting_pierce + to_chat(user, span_boldnotice("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() + . = ..() + fire_delay = delay + current_tracers = list() + START_PROCESSING(SSfastprocess, src) + zoom_lock_action = new(src) + +/obj/item/gun/energy/beam_rifle/Destroy() + STOP_PROCESSING(SSfastprocess, src) + set_user(null) + QDEL_LIST(current_tracers) + listeningTo = null + return ..() + +/obj/item/gun/energy/beam_rifle/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + chambered = null + recharge_newshot() + +/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE) + var/diff = abs(aiming_lastangle - lastangle) + if(!check_user()) + return + if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update) + return + aiming_lastangle = lastangle + var/obj/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) + var/mouse_modifiers = params2list(current_user.client.mouseParams) + P.preparePixelProjectile(targloc, current_user, mouse_modifiers, 0) + P.fire(lastangle) + +/obj/item/gun/energy/beam_rifle/process(seconds_per_tick) + 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) + current_user.setDir(angle2dir_cardinal(angle)) + var/difference = abs(closer_angle_difference(lastangle, angle)) + 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) + if(listeningTo) + UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED) + listeningTo = null + if(istype(current_user)) + current_user = null + if(istype(user)) + current_user = user + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_mob_move)) + listeningTo = user + +/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) + return ..() + +/obj/item/gun/energy/beam_rifle/onMouseDown(object, location, params, mob/mob) + if(istype(mob)) + set_user(mob) + if(istype(object, /atom/movable/screen) && !istype(object, /atom/movable/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, /atom/movable/screen) && !istype(object, /atom/movable/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 != BODY_ZONE_PRECISE_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/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) + var/modifiers = params2list(params) + BB.preparePixelProjectile(target, user, modifiers, spread) + BB.fire(gun? gun.lastangle : null, null) + BB = null + return TRUE + +/obj/item/ammo_casing/energy/beam_rifle/hitscan + projectile_type = /obj/projectile/beam/beam_rifle/hitscan + select_name = "beam" + e_cost = 10000 + fire_sound = 'sound/weapons/beam_sniper.ogg' + +/obj/projectile/beam/beam_rifle + name = "particle beam" + icon = null + hitsound = 'sound/effects/explosion3.ogg' + damage = 0 //Handled manually. + damage_type = BURN + flag = "energy" + range = 150 + jitter = 10 SECONDS + 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/list/pierced = list() + +/obj/projectile/beam/beam_rifle/proc/AOE(turf/epicenter) + 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, span_userdanger("\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)) + O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, "laser", FALSE) + +/obj/projectile/beam/beam_rifle/prehit_pierce(atom/A) + if(isclosedturf(A) && (wall_pierce < wall_pierce_amount)) + if(prob(wall_devastate)) + if(iswallturf(A)) + var/turf/closed/wall/W = A + W.dismantle_wall(devastated = TRUE) + else + SSexplosions.medturf += A + ++wall_pierce + return PROJECTILE_PIERCE_PHASE // yeah this gun is a snowflakey piece of garbage + if(isobj(A) && (structure_pierce < structure_pierce_amount)) + ++structure_pierce + var/obj/O = A + O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(A), BURN, "energy", FALSE) + return PROJECTILE_PIERCE_PHASE // ditto and this could be refactored to on_hit honestly + return ..() + +/obj/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/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.force_scream() + +/obj/projectile/beam/beam_rifle/proc/handle_hit(atom/target, piercing_hit = FALSE) + set waitfor = FALSE + if(nodamage) + return FALSE + playsound(src, 'sound/effects/explosion3.ogg', 100, TRUE) + if(!piercing_hit) + AOE(get_turf(target) || get_turf(src)) + if(!QDELETED(target)) + handle_impact(target) + +/obj/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE, piercing_hit = FALSE) + handle_hit(target, piercing_hit) + return ..() + +/obj/projectile/beam/beam_rifle/hitscan + icon_state = "" + hitscan = TRUE + tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle + var/constant_tracer = FALSE + +/obj/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, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) + else + for(var/datum/point/p in beam_segments) + generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) + if(cleanup) + QDEL_LIST(beam_segments) + beam_segments = null + QDEL_NULL(beam_index) + +/obj/projectile/beam/beam_rifle/hitscan/aiming_beam + tracer_type = /obj/effect/projectile/tracer/tracer/aiming + name = "aiming beam" + hitsound = null + hitsound_non_living = null + nodamage = TRUE + damage = 0 + constant_tracer = TRUE + hitscan_light_range = 0 + hitscan_light_intensity = 0 + hitscan_light_color_override = "#99ff99" + reflectable = REFLECT_FAKEPROJECTILE + +/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit_pierce(atom/target) + return PROJECTILE_DELETE_WITHOUT_HITTING + +/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit() + qdel(src) + return BULLET_ACT_BLOCK diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/blastcannon.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/blastcannon.dm new file mode 100644 index 0000000000..c6ba87e435 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/blastcannon.dm @@ -0,0 +1,162 @@ +/obj/item/gun/blastcannon + name = "pipe gun" + desc = "A pipe welded onto a gun stock, with a mechanical trigger. The pipe has an opening near the top, and there seems to be a spring loaded wheel in the hole." + icon_state = "empty_blastcannon" + item_state = "blastcannon_empty" + base_icon_state = "blastcannon" + w_class = WEIGHT_CLASS_NORMAL + force = 10 + fire_sound = 'sound/weapons/blastcannon.ogg' + item_flags = NONE + randomspread = FALSE + + var/hugbox = TRUE + var/max_power = INFINITY + var/reaction_volume_mod = 0 + var/reaction_cycles = 3 //How many times gases react() before calculation. Very finnicky value, do not mess with without good reason. + var/prereaction = TRUE + + var/bombcheck = TRUE + var/debug_power = 0 + + var/obj/item/transfer_valve/bomb + +/obj/item/gun/blastcannon/debug + debug_power = 80 + bombcheck = FALSE + +/obj/item/gun/blastcannon/Destroy() + QDEL_NULL(bomb) + return ..() + +/obj/item/gun/blastcannon/unique_action(mob/living/user) + if(bomb) + bomb.forceMove(user.loc) + user.put_in_hands(bomb) + user.visible_message(span_warning("[user] detaches [bomb] from [src].")) + bomb = null + name = initial(name) + desc = initial(desc) + update_appearance() + return ..() + +/obj/item/gun/blastcannon/update_icon_state() + icon_state = "[bomb ? "loaded" : "empty"]_[base_icon_state]" + return ..() + +/obj/item/gun/blastcannon/attackby(obj/O, mob/user) + if(istype(O, /obj/item/transfer_valve)) + var/obj/item/transfer_valve/T = O + if(!T.tank_one || !T.tank_two) + to_chat(user, span_warning("What good would an incomplete bomb do?")) + return FALSE + if(!user.transferItemToLoc(T, src)) + to_chat(user, span_warning("[T] seems to be stuck to your hand!")) + return FALSE + user.visible_message(span_warning("[user] attaches [T] to [src]!")) + bomb = T + name = "blast cannon" + desc = "A makeshift device used to concentrate a bomb's blast energy to a narrow wave." + update_appearance() + return TRUE + return ..() + +//returns the third value of a bomb blast +/obj/item/gun/blastcannon/proc/calculate_bomb() + if(!istype(bomb) || !istype(bomb.tank_one) || !istype(bomb.tank_two)) + return 0 + var/datum/gas_mixture/temp = new(max(reaction_volume_mod, 0)) + bomb.merge_gases(temp) + if(prereaction) + temp.react(src) + var/prereaction_pressure = temp.return_pressure() + if(prereaction_pressure < TANK_FRAGMENT_PRESSURE) + return 0 + for(var/i in 1 to reaction_cycles) + temp.react(src) + var/pressure = temp.return_pressure() + qdel(temp) + if(pressure < TANK_FRAGMENT_PRESSURE) + return 0 + return ((pressure - TANK_FRAGMENT_PRESSURE) / TANK_FRAGMENT_SCALE) + +/obj/item/gun/blastcannon/afterattack(atom/target, mob/user, flag, params) + if((!bomb && bombcheck) || (!target) || (get_dist(get_turf(target), get_turf(user)) <= 2)) + return ..() + var/modifiers = params2list(params) + var/power = bomb? calculate_bomb() : debug_power + power = min(power, max_power) + QDEL_NULL(bomb) + update_appearance() + var/heavy = power * 0.25 + var/medium = power * 0.5 + var/light = power + user.visible_message(span_danger("[user] opens [bomb] on [user.p_their()] [name] and fires a blast wave at [target]!"),span_danger("You open [bomb] on your [name] and fire a blast wave at [target]!")) + playsound(user, "explosion", 100, TRUE) + var/turf/starting = get_turf(user) + var/turf/targturf = get_turf(target) + message_admins("Blast wave fired from [ADMIN_VERBOSEJMP(starting)] at [ADMIN_VERBOSEJMP(targturf)] ([target.name]) by [ADMIN_LOOKUPFLW(user)] with power [heavy]/[medium]/[light].") + log_game("Blast wave fired from [AREACOORD(starting)] at [AREACOORD(targturf)] ([target.name]) by [key_name(user)] with power [heavy]/[medium]/[light].") + var/obj/projectile/blastwave/BW = new(loc, heavy, medium, light) + BW.hugbox = hugbox + BW.preparePixelProjectile(target, get_turf(src), modifiers, 0) + BW.fire() + name = initial(name) + desc = initial(desc) + +/obj/projectile/blastwave + name = "blast wave" + icon_state = "blastwave" + damage = 0 + nodamage = FALSE + movement_type = FLYING + projectile_phasing = ALL // just blows up the turfs lmao + var/heavyr = 0 + var/mediumr = 0 + var/lightr = 0 + var/hugbox = TRUE + range = 150 + +/obj/projectile/blastwave/Initialize(mapload, _h, _m, _l) + heavyr = _h + mediumr = _m + lightr = _l + return ..() + +/obj/projectile/blastwave/Range() + ..() + var/amount_destruction = EXPLODE_NONE + var/wallbreak_chance = 0 + if(heavyr) + amount_destruction = EXPLODE_DEVASTATE + wallbreak_chance = 99 + else if(mediumr) + amount_destruction = EXPLODE_HEAVY + wallbreak_chance = 66 + else if(lightr) + amount_destruction = EXPLODE_LIGHT + wallbreak_chance = 33 + if(amount_destruction) + if(hugbox) + loc.contents_explosion(EXPLODE_HEAVY, loc) + if(istype(loc, /turf/closed/wall)) + var/turf/closed/wall/W = loc + if(prob(wallbreak_chance)) + W.dismantle_wall(devastated = TRUE) + else + switch(amount_destruction) + if(EXPLODE_DEVASTATE) + SSexplosions.highturf += loc + if(EXPLODE_HEAVY) + SSexplosions.medturf += loc + if(EXPLODE_LIGHT) + SSexplosions.lowturf += loc + else + qdel(src) + + heavyr = max(heavyr - 1, 0) + mediumr = max(mediumr - 1, 0) + lightr = max(lightr - 1, 0) + +/obj/projectile/blastwave/ex_act() + return diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/bow.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/bow.dm new file mode 100644 index 0000000000..aee792acb1 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/bow.dm @@ -0,0 +1,108 @@ +/obj/item/gun/ballistic/bow + name = "longbow" + desc = "While pretty finely crafted, surely you can find something better to use in the current year." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "bow" + item_state = "pipebow" + spawn_blacklisted = TRUE + load_sound = null + fire_sound = 'sound/weapons/bowfire.ogg' + slot_flags = ITEM_SLOT_BACK + default_ammo_type = /obj/item/ammo_box/magazine/internal/bow + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/internal/bow, + ) + trigger_guard = TRIGGER_GUARD_ALLOW_ALL + force = 15 + attack_verb = list("whipped", "cracked") + weapon_weight = WEAPON_HEAVY + w_class = WEIGHT_CLASS_BULKY + internal_magazine = TRUE + bolt_type = BOLT_TYPE_NO_BOLT + var/drawn = FALSE + +/obj/item/gun/ballistic/bow/update_icon_state() + . = ..() + icon_state = chambered ? "bow_[drawn]" : "bow" + +/obj/item/gun/ballistic/bow/chamber_round(keep_bullet = FALSE, spin_cylinder, replace_new_round) + if(chambered || !magazine) + return + if(magazine.ammo_count()) + chambered = magazine.get_round(TRUE) + chambered.forceMove(src) + +/obj/item/gun/ballistic/bow/unique_action(mob/living/user) + if(chambered) + to_chat(user, span_notice("You [drawn ? "release" : "draw"] [src]'s string.")) + if(!drawn) + playsound(src, 'sound/weapons/bowdraw.ogg', 75, 0) + drawn = !drawn + update_appearance() + +/obj/item/gun/ballistic/bow/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE) + if(!chambered) + return + if(!drawn) + to_chat(user, "You can't shoot without drawing the bow.") + return + drawn = FALSE + . = ..() //fires, removing the arrow + update_appearance() + +/obj/item/gun/ballistic/bow/shoot_with_empty_chamber(mob/living/user) + return //so clicking sounds please + +/obj/item/ammo_casing/caseless/arrow/despawning/dropped() + . = ..() + addtimer(CALLBACK(src, PROC_REF(floor_vanish)), 5 SECONDS) + +/obj/item/ammo_casing/caseless/arrow/despawning/proc/floor_vanish() + if(isturf(loc)) + qdel(src) + +/obj/item/storage/bag/quiver + name = "quiver" + desc = "Holds arrows for your bow. Good, because while pocketing arrows is possible, it surely can't be pleasant." + icon_state = "quiver" + item_state = "harpoon_quiver" + var/arrow_path = /obj/item/ammo_casing/caseless/arrow + +/obj/item/storage/bag/quiver/Initialize(mapload) + . = ..() + var/datum/component/storage/storage = GetComponent(/datum/component/storage) + storage.max_w_class = WEIGHT_CLASS_TINY + storage.max_items = 40 + storage.max_combined_w_class = 100 + storage.set_holdable(list( + /obj/item/ammo_casing/caseless/arrow + )) + +/obj/item/storage/bag/quiver/PopulateContents() + . = ..() + if(arrow_path) + for(var/i in 1 to 10) + new arrow_path(src) + +/obj/item/storage/bag/quiver/despawning + arrow_path = /obj/item/ammo_casing/caseless/arrow/despawning + +/obj/item/gun/ballistic/bow/ashen + name = "Bone Bow" + desc = "Some sort of primitive projectile weapon made of bone and wrapped sinew." + icon_state = "ashenbow" + item_state = "ashenbow" + mob_overlay_icon = 'icons/mob/clothing/back.dmi' + force = 8 + +/obj/item/gun/ballistic/bow/pipe + name = "Pipe Bow" + desc = "A crude projectile weapon made from silk string, pipe and lots of bending." + icon_state = "pipebow" + mob_overlay_icon = 'icons/mob/clothing/back.dmi' + force = 7 + +/obj/item/storage/bag/quiver/empty + name = "leather quiver" + desc = "A quiver made from the hide of some animal. Used to hold arrows." + arrow_path = null diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm new file mode 100644 index 0000000000..cf194a6af9 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm @@ -0,0 +1,45 @@ +//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 + custom_materials = list(/datum/material/iron=2000) + fire_sound = 'sound/items/syringeproj.ogg' + 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, OPENCONTAINER) + +/obj/item/gun/chem/Destroy() + . = ..() + STOP_PROCESSING(SSobj, src) + +/obj/item/gun/chem/can_shoot() + return syringes_left + +/obj/item/gun/chem/process_chamber(atom/shooter) + if(chambered && !chambered.BB && syringes_left) + chambered.newshot() + +/obj/item/gun/chem/process(seconds_per_tick) + if(syringes_left >= max_syringes) + return + if(world.time < last_synth+time_per_syringe) + return + to_chat(loc, span_warning("You hear a click as [src] synthesizes a new dart.")) + syringes_left++ + if(chambered && !chambered.BB) + chambered.newshot() + last_synth = world.time diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/grenade_launcher.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/grenade_launcher.dm new file mode 100644 index 0000000000..f1daae5f8b --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/grenade_launcher.dm @@ -0,0 +1,61 @@ +/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 + var/fire_range = 10 + force = 5 + var/list/grenades = new/list() + var/max_grenades = 3 + custom_materials = list(/datum/material/iron=2000) + +/obj/item/gun/grenadelauncher/examine(mob/user) + . = ..() + . += "[grenades.len] / [max_grenades] grenades loaded." + . += span_notice("You can eject a grenade from the [src] by pressing the unique action key. By default, this is space") + +/obj/item/gun/grenadelauncher/unique_action(mob/living/user) + // there are no grenades + if(!can_shoot()) + return + var/obj/item/grenade/F = grenades[1] + grenades -= F + user.put_in_hands(F) + playsound(src,'sound/weapons/gun/shotgun/rack.ogg',100) + to_chat(user, span_notice("You unload the [F] from the [src].")) + + +/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, span_notice("You put the grenade in the grenade launcher.")) + to_chat(user, span_notice("[grenades.len] / [max_grenades] Grenades.")) + playsound(src,'sound/weapons/gun/shotgun/insert_shell.ogg',100) + else + to_chat(usr, span_warning("The grenade launcher cannot hold more grenades!")) + +/obj/item/gun/grenadelauncher/can_shoot() + return grenades.len + +/obj/item/gun/grenadelauncher/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(!can_shoot()) + return + user.visible_message(span_danger("[user] fired a grenade!"), \ + span_danger("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, fire_range, 2, user) + message_admins("[ADMIN_LOOKUPFLW(user)] fired a grenade ([F.name]) from a grenade launcher ([src]) from [AREACOORD(user)] at [target] [AREACOORD(target)].") + log_game("[key_name(user)] fired a grenade ([F.name]) with a grenade launcher ([src]) from [AREACOORD(user)] at [target] [AREACOORD(target)].") + F.active = 1 + F.icon_state = initial(F.icon_state) + "_active" + playsound(user.loc, 'sound/weapons/armbomb.ogg', 75, TRUE, -3) + addtimer(CALLBACK(F, TYPE_PROC_REF(/obj/item/grenade, prime)), 45) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm new file mode 100644 index 0000000000..bdaec3d737 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm @@ -0,0 +1,158 @@ +/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() + +/** + * Proc that always is called when we want to end the beam and makes sure things are cleaned up, see beam_died() + */ +/obj/item/gun/medbeam/proc/LoseTarget() + if(active) + qdel(current_beam) + current_beam = null + active = 0 + on_beam_release(current_target) + current_target = null + +/** + * Proc that is only called when the beam fails due to something, so not when manually ended. + * manual disconnection = LoseTarget, so it can silently end + * automatic disconnection = beam_died, so we can give a warning message first + */ +/obj/item/gun/medbeam/proc/beam_died() + active = FALSE //skip qdelling the beam again if we're doing this proc, because + if(isliving(loc)) + to_chat(loc, span_warning("You lose control of the beam!")) + LoseTarget() + +/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 = user.Beam(current_target, icon_state="medbeam", time = 10 MINUTES, maxdistance = max_range, beam_type = /obj/effect/ebeam/medical) + RegisterSignal(current_beam, COMSIG_QDELETING, PROC_REF(beam_died))//this is a WAY better rangecheck than what was done before (process check) + + SSblackbox.record_feedback("tally", "gun_fired", 1, type) + +/obj/item/gun/medbeam/process(seconds_per_tick) + + if(!mounted && !isliving(loc)) + LoseTarget() + return + + if(!current_target) + LoseTarget() + return + + if(world.time <= last_check+check_delay) + return + + last_check = world.time + + if(!los_check(loc, current_target)) + qdel(current_beam)//this will give the target lost message + 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 + var/turf/previous_step = user_turf + var/first_step = TRUE + for(var/turf/next_step as anything in (getline(user_turf, target) - user_turf)) + if(first_step) + for(var/obj/blocker in user_turf) + if(!blocker.density || !(blocker.flags_1 & ON_BORDER_1)) + continue + if(blocker.CanPass(dummy, get_dir(user_turf, next_step))) + continue + return FALSE // Could not leave the first turf. + first_step = FALSE + if(mounted && next_step == user_turf) + + continue //Mechs are dense and thus fail the check + if(next_step.density) + qdel(dummy) + return FALSE + for(var/atom/movable/movable as anything in next_step) + if(!movable.CanPass(dummy, get_dir(next_step, previous_step))) + qdel(dummy) + return FALSE + for(var/obj/effect/ebeam/medical/B in next_step)// Don't cross the str-beams! + if(B.owner.origin != current_beam.origin) + explosion(B.loc,0,3,5,8) + qdel(dummy) + return FALSE + previous_step = next_step + qdel(dummy) + return 1 + +/obj/item/gun/medbeam/proc/on_beam_hit(mob/living/target) + return + +/obj/item/gun/medbeam/proc/on_beam_tick(mob/living/target) + if(target.health != target.maxHealth) + new /obj/effect/temp_visual/heal(get_turf(target), "#80F5FF") + target.adjustBruteLoss(-4) + target.adjustFireLoss(-4) + target.adjustToxLoss(-1) + target.adjustOxyLoss(-1) + return + +/obj/item/gun/medbeam/proc/on_beam_release(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/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm new file mode 100644 index 0000000000..936cb2f601 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm @@ -0,0 +1,124 @@ +/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 + custom_materials = list(/datum/material/iron=2000) + 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/handle_atom_del(atom/A) + . = ..() + if(A in syringes) + syringes.Remove(A) + +/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(atom/shooter) + if(chambered && !chambered.BB) //we just fired + recharge_newshot() + +/obj/item/gun/syringe/examine(mob/user) + . = ..() + . += "Can hold [max_syringes] syringe\s. Has [syringes.len] syringe\s remaining." + +/obj/item/gun/syringe/unique_action(mob/living/user) + if(!syringes.len) + to_chat(user, span_warning("[src] is empty!")) + return 0 + + var/obj/item/reagent_containers/syringe/S = syringes[syringes.len] + + if(!S) + return 0 + user.put_in_hands(S) + + syringes.Remove(S) + to_chat(user, span_notice("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, span_notice("You load [A] into \the [src].")) + syringes += A + recharge_newshot() + return TRUE + else + to_chat(user, span_warning("[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 + +/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, span_warning("This injector is used up!")) + return + if(syringes.len < max_syringes) + if(!user.transferItemToLoc(D, src)) + return FALSE + to_chat(user, span_notice("You load \the [D] into \the [src].")) + syringes += D + recharge_newshot() + return TRUE + else + to_chat(user, span_warning("[src] cannot hold more syringes!")) + return FALSE + +/obj/item/gun/syringe/blowgun + name = "blowgun" + desc = "Fire syringes at a short distance." + icon_state = "blowgun" + item_state = "blowgun" + fire_sound = 'sound/items/syringeproj.ogg' + trigger_guard = TRIGGER_GUARD_ALLOW_ALL + dry_fire_sound = null //dont got a trigger, no dry fire sound WS Edit - Dry firing + dry_fire_text = "pshoo" //heehee pshoo WS Edit - Dry firing + +/obj/item/gun/syringe/blowgun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + visible_message(span_danger("[user] starts aiming with a blowgun!")) + if(do_after(user, 25, target = src)) + user.adjustStaminaLoss(20) + user.adjustOxyLoss(20) + ..() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/powered.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/powered.dm new file mode 100644 index 0000000000..1321b40ce8 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/powered.dm @@ -0,0 +1,110 @@ +/obj/item/gun/ballistic/automatic/powered + bad_type = /obj/item/gun/ballistic/automatic/powered + default_ammo_type = /obj/item/ammo_box/magazine/gauss + allowed_ammo_types = list( + /obj/item/ammo_box/magazine/gauss, + ) + var/default_cell_type = /obj/item/stock_parts/cell/gun + var/list/allowed_cell_types = list( + /obj/item/stock_parts/cell/gun, + /obj/item/stock_parts/cell/gun/upgraded, + /obj/item/stock_parts/cell/gun/empty, + /obj/item/stock_parts/cell/gun/upgraded/empty, + ) + charge_sections = 3 + +/obj/item/gun/ballistic/automatic/powered/Initialize() + . = ..() + if(default_cell_type) + cell = new default_cell_type(src) + update_appearance() + +/obj/item/gun/ballistic/automatic/powered/examine(mob/user) + . = ..() + if(cell) + . += "\The [name]'s cell has [cell.percent()]% charge remaining." + else + . += span_notice("[src] doesn't seem to have a cell!") + +/obj/item/gun/ballistic/automatic/powered/can_shoot() + if(QDELETED(cell)) + return FALSE + + var/obj/item/ammo_casing/caseless/gauss/shot = chambered + if(!shot) + return FALSE + if(cell.charge < shot.energy_cost * burst_size) + return FALSE + return ..() + +/obj/item/gun/ballistic/automatic/powered/before_firing(atom/target, mob/user) + var/obj/item/ammo_casing/caseless/gauss/shot = chambered + if(shot.energy_cost) + cell.use(shot.energy_cost) + return ..() + +/obj/item/gun/ballistic/automatic/powered/get_cell() + return cell + +//the things below were taken from energy gun code. blame whoever coded this, not me +/obj/item/gun/ballistic/automatic/powered/attackby(obj/item/A, mob/user, params) + if(..()) + return FALSE + if (!internal_cell && (A.type in allowed_cell_types)) + var/obj/item/stock_parts/cell/gun/C = A + if (!cell) + insert_cell(user, C) + +/obj/item/gun/ballistic/automatic/powered/proc/insert_cell(mob/user, obj/item/stock_parts/cell/gun/C) + if(user.transferItemToLoc(C, src)) + cell = C + to_chat(user, span_notice("You load the [C] into \the [src].")) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + update_appearance() + return TRUE + else + to_chat(user, span_warning("You cannot seem to get \the [src] out of your hands!")) + return FALSE + +/obj/item/gun/ballistic/automatic/powered/proc/eject_cell(mob/user, obj/item/stock_parts/cell/gun/tac_load = null) + playsound(src, load_sound, load_sound_volume, load_sound_vary) + cell.forceMove(drop_location()) + var/obj/item/stock_parts/cell/gun/old_cell = cell + cell = null + user.put_in_hands(old_cell) + old_cell.update_appearance() + to_chat(user, span_notice("You pull the cell out of \the [src].")) + update_appearance() + +/obj/item/gun/ballistic/automatic/powered/screwdriver_act(mob/living/user, obj/item/I) + if(cell && !internal_cell) + to_chat(user, span_notice("You begin unscrewing and pulling out the cell...")) + if(I.use_tool(src, user, unscrewing_time, volume=100)) + to_chat(user, span_notice("You remove the power cell.")) + eject_cell(user) + return ..() + +/obj/item/gun/ballistic/automatic/powered/update_overlays() + . = ..() + if(!automatic_charge_overlays) + return + var/overlay_icon_state = "[icon_state]_charge" + var/charge_ratio = get_charge_ratio() + if(cell) + . += "[icon_state]_cell" + if(charge_ratio == 0) + . += "[icon_state]_cellempty" + else + if(!shaded_charge) + var/mutable_appearance/charge_overlay = mutable_appearance(icon, overlay_icon_state) + for(var/i in 1 to charge_ratio) + charge_overlay.pixel_x = ammo_x_offset * (i - 1) + charge_overlay.pixel_y = ammo_y_offset * (i - 1) + . += new /mutable_appearance(charge_overlay) + else + . += "[icon_state]_charge[charge_ratio]" + +/obj/item/gun/ballistic/automatic/powered/proc/get_charge_ratio() + if(!cell) + return FALSE + return CEILING(clamp(cell.charge / cell.maxcharge, 0, 1) * charge_sections, 1)// Sets the ratio to 0 if the gun doesn't have enough charge to fire, or if its power cell is removed. diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile.dm new file mode 100644 index 0000000000..755a45d983 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile.dm @@ -0,0 +1,1049 @@ +/obj/projectile + name = "projectile" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bullet" + density = FALSE + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + movement_type = FLYING + generic_canpass = FALSE + plane = GAME_PLANE_FOV_HIDDEN + wound_bonus = CANT_WOUND // can't wound by default + ///armour percentage that the projectile ignores + armour_penetration = 0 + ///The sound this plays on impact. + var/hitsound = 'sound/weapons/pierce.ogg' + var/hitsound_non_living = "" + var/hitsound_glass + var/hitsound_stone + var/hitsound_metal + var/hitsound_wood + var/hitsound_snow + + var/near_miss_sound = "" + var/ricochet_sound = "" + + ///what we should call the fired bullet + var/bullet_identifier = null + + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/def_zone = "" //Aiming at + var/atom/movable/firer = null//Who shot it + // if the projectile was the result of a misfire. For logging. + var/misfire = FALSE + var/atom/fired_from = null // the atom that the projectile was fired from (gun, turret) + var/suppressed = FALSE //Attack message + var/yo = null + var/xo = null + var/atom/original = null // the original target clicked + var/turf/starting = null // the projectile's starting turf + var/p_x = 16 + var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + + //Fired processing vars + var/fired = FALSE //Have we been fired yet + var/paused = FALSE //for suspending the projectile midair + var/last_projectile_move = 0 + var/last_process = 0 + var/time_offset = 0 + var/datum/point/vector/trajectory + var/trajectory_ignore_forcemove = FALSE //instructs forceMove to NOT reset our trajectory to the new location! + /// We already impacted these things, do not impact them again. Used to make sure we can pierce things we want to pierce. Lazylist, typecache style (object = TRUE) for performance. + var/list/impacted + /// If TRUE, we can hit our firer. + var/ignore_source_check = FALSE + /// We are flagged PHASING temporarily to not stop moving when we Bump something but want to keep going anyways. + var/temporary_unstoppable_movement = FALSE + + /** PROJECTILE PIERCING + * WARNING: + * Projectile piercing MUST be done using these variables. + * Ordinary passflags will be **IGNORED**. + * The two flag variables below both use pass flags. + * In the context of LETPASStHROW, it means the projectile will ignore things that are currently "in the air" from a throw. + * + * Also, projectiles sense hits using Bump(), and then pierce them if necessary. + * They simply do not follow conventional movement rules. + * NEVER flag a projectile as PHASING movement type. + * If you so badly need to make one go through *everything*, override check_pierce() for your projectile to always return PROJECTILE_PIERCE_PHASE/HIT. + */ + /// The "usual" flags of pass_flags is used in that can_hit_target ignores these unless they're specifically targeted/clicked on. This behavior entirely bypasses process_hit if triggered, rather than phasing which uses prehit_pierce() to check. + pass_flags = PASSTABLE + /// If FALSE, allow us to hit something directly targeted/clicked/whatnot even if we're able to phase through it + var/phasing_ignore_direct_target = FALSE + /// Bitflag for things the projectile should just phase through entirely - No hitting unless direct target and [phasing_ignore_direct_target] is FALSE. Uses pass_flags flags. + var/projectile_phasing = NONE + /// Bitflag for things the projectile should hit, but pierce through without deleting itself. Defers to projectile_phasing. Uses pass_flags flags. + var/projectile_piercing = NONE + /// number of times we've pierced something. Incremented BEFORE bullet_act and on_hit proc! + var/pierces = 0 + var/ignore_concealment = FALSE + ///Amount of deciseconds it takes for projectile to travel + var/speed = 0.8 + ///plus/minus modifier to projectile speed + var/speed_mod = 0 + var/Angle = 0 + var/original_angle = 0 //Angle at firing + var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle + var/spread = 0 //amount (in degrees) of projectile spread + animate_movement = NO_STEPS //Use SLIDE_STEPS in conjunction with legacy + /// how many times we've ricochet'd so far (instance variable, not a stat) + var/ricochets = 0 + /// how many times we can ricochet max + var/ricochets_max = 0 + /// 0-100, the base chance of ricocheting, before being modified by the atom we shoot and our chance decay + var/ricochet_chance = 0 + /// 0-1 (or more, I guess) multiplier, the ricochet_chance is modified by multiplying this after each ricochet + var/ricochet_decay_chance = 0.7 + /// 0-1 (or more, I guess) multiplier, the projectile's damage is modified by multiplying this after each ricochet + var/ricochet_decay_damage = 0.7 + /// On ricochet, if nonzero, we consider all mobs within this range of our projectile at the time of ricochet to home in on like Revolver Ocelot, as governed by ricochet_auto_aim_angle + var/ricochet_auto_aim_range = 0 + /// On ricochet, if ricochet_auto_aim_range is nonzero, we'll consider any mobs within this range of the normal angle of incidence to home in on, higher = more auto aim + var/ricochet_auto_aim_angle = 30 + /// the angle of impact must be within this many degrees of the struck surface, set to 0 to allow any angle + var/ricochet_incidence_leeway = 40 + /// accuracy modifier. Used as a multiplier + var/accuracy_mod = 1 + + ///If the object being hit can pass ths damage on to something else, it should not do it for this bullet + var/force_hit = FALSE + + //Hitscan + var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored. + var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation. + var/datum/point/beam_index + var/turf/hitscan_last //last turf touched during hitscanning. + var/tracer_type + var/muzzle_type + var/impact_type + + var/turf/last_angle_set_hitscan_store //WS Edit - last turf we stored a hitscan segment while changing angles. without this you'll have potentially hundreds of segments from a homing projectile or something. + + //Fancy hitscan lighting effects! + var/hitscan_light_intensity = 1.5 + var/hitscan_light_range = 0.75 + var/hitscan_light_color_override + var/muzzle_flash_intensity = 3 + var/muzzle_flash_range = 1.5 + var/muzzle_flash_color_override + var/impact_light_intensity = 3 + var/impact_light_range = 2 + var/impact_light_color_override + + //Homing + var/homing = FALSE + var/atom/homing_target + var/homing_turn_speed = 10 //Angle per tick. + var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target. + var/homing_inaccuracy_max = 0 + var/homing_offset_x = 0 + var/homing_offset_y = 0 + + var/damage = 10 + var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE, STAMINA are the only things that should be in here + var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions + var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb + var/projectile_type = /obj/projectile + var/range = 50 //This will de-increment every step. When 0, it will deletze the projectile. + var/decayedRange //stores original range + var/reflect_range_decrease = 5 //amount of original range that falls off when reflecting, so it doesn't go forever + var/reflectable = NONE // Can it be reflected or not? + + //Effects + var/stun = 0 + var/knockdown = 0 + var/paralyze = 0 + var/immobilize = 0 + var/unconscious = 0 + var/irradiate = 0 + var/stutter = 0 + var/slur = 0 + var/eyeblur = 0 + var/drowsy = 0 + var/stamina = 0 + var/jitter = 0 + var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all. + var/impact_effect_type //what type of impact effect to show when hitting something + var/log_override = FALSE //is this type spammed enough to not log? (KAs) + + // if the projectile has the matching flags when hitting a wall, it deals it's override damage instead + var/wall_damage_flags = PROJECTILE_BONUS_DAMAGE_NONE + var/wall_damage_override = 0 + + ///If defined, on hit we create an item of this type then call hitby() on the hit target with this, mainly used for embedding items (bullets) in targets + var/shrapnel_type + ///If we have a shrapnel_type defined, these embedding stats will be passed to the spawned shrapnel type, which will roll for embedding on the target + var/list/embedding + ///If TRUE, hit mobs even if they're on the floor and not our target + var/hit_stunned_targets = FALSE + //For what kind of brute wounds we're rolling for, if we're doing such a thing. Lasers obviously don't care since they do burn instead. + var/sharpness = SHARP_NONE + ///How much we want to drop both wound_bonus and bare_wound_bonus (to a minimum of 0 for the latter) per tile, for falloff purposes + var/wound_falloff_tile + ///How much we want to drop the embed_chance value, if we can embed, per tile, for falloff purposes + var/embed_falloff_tile + /// If true directly targeted turfs can be hit + var/can_hit_turfs = FALSE + + var/static/list/projectile_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + +/obj/projectile/Initialize() + . = ..() + decayedRange = range + speed = speed + speed_mod + if(firer) + if(firer.vis_flags & SEE_MOBS) + ignore_concealment = TRUE + + if(embedding) + updateEmbedding() + + if(!bullet_identifier) + bullet_identifier = name + AddElement(/datum/element/connect_loc, projectile_connections) + +/obj/projectile/proc/Range() + range-- + if(wound_bonus != CANT_WOUND) + wound_bonus += wound_falloff_tile + bare_wound_bonus = max(0, bare_wound_bonus + wound_falloff_tile) + if(embedding) + embedding["embed_chance"] += embed_falloff_tile + if(range <= 0 && loc) + on_range() + +/obj/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range + SEND_SIGNAL(src, COMSIG_PROJECTILE_RANGE_OUT) + qdel(src) + +//to get the correct limb (if any) for the projectile hit message +/mob/living/proc/check_limb_hit(hit_zone) + if(has_limbs) + return hit_zone + +/mob/living/carbon/check_limb_hit(hit_zone) + if(get_bodypart(hit_zone)) + return hit_zone + else //when a limb is missing the damage is actually passed to the chest + return BODY_ZONE_CHEST + +/** + * Called when the projectile hits something + * + * @params + * target - thing hit + * blocked - percentage of hit blocked + * pierce_hit - are we piercing through or regular hitting + */ +/obj/projectile/proc/on_hit(atom/target, blocked = FALSE, pierce_hit) + if(fired_from) + SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle) + // i know that this is probably more with wands and gun mods in mind, but it's a bit silly that the projectile on_hit signal doesn't ping the projectile itself. + // maybe we care what the projectile thinks! See about combining these via args some time when it's not 5AM + var/obj/item/bodypart/hit_limb + if(isliving(target)) + var/mob/living/L = target + hit_limb = L.check_limb_hit(def_zone) + SEND_SIGNAL(src, COMSIG_PROJECTILE_SELF_ON_HIT, firer, target, Angle, hit_limb) + + if(QDELETED(src)) // in case one of the above signals deleted the projectile for whatever reason + return + + var/turf/target_loca = get_turf(target) + + var/hitx + var/hity + if(target == original) + hitx = target.pixel_x + p_x - 16 + hity = target.pixel_y + p_y - 16 + else + hitx = target.pixel_x + rand(-8, 8) + hity = target.pixel_y + rand(-8, 8) + + if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75)) + var/turf/closed/wall/W = target_loca + if(impact_effect_type && !hitscan) + new impact_effect_type(target_loca, hitx, hity) + + W.add_dent(WALL_DENT_SHOT, hitx, hity) + + return BULLET_ACT_HIT + + if(!isliving(target)) + if(impact_effect_type && !hitscan) + new impact_effect_type(target_loca, hitx, hity) + return BULLET_ACT_HIT + + var/mob/living/L = target + + if(blocked != 100) // not completely blocked + if(damage && L.blood_volume && damage_type == BRUTE) + var/splatter_dir = dir + if(starting) + splatter_dir = get_dir(starting, target_loca) + if(isalien(L)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_loca, splatter_dir) + var/obj/item/bodypart/B = L.get_bodypart(def_zone) + if(B && !IS_ORGANIC_LIMB(B)) // So if you hit a robotic, it sparks instead of bloodspatters + do_sparks(2, FALSE, target.loc) + else + var/splatter_color = null + if(iscarbon(L)) + var/mob/living/carbon/carbon_target = L + splatter_color = carbon_target.dna.blood_type.color + new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, splatter_color) + if(prob(33)) + L.add_splatter_floor(target_loca) + else if(impact_effect_type && !hitscan) + new impact_effect_type(target_loca, hitx, hity) + + var/organ_hit_text = "" + var/limb_hit = hit_limb + if(limb_hit) + organ_hit_text = " in \the [parse_zone(limb_hit)]" + if(suppressed==SUPPRESSED_VERY) + playsound(loc, hitsound, 5, TRUE, -1) + else if(suppressed) + playsound(loc, hitsound, 5, TRUE, -1) + to_chat(L, span_userdanger("You're shot by \a [src][organ_hit_text]!")) + else + if(hitsound) + playsound(get_turf(L), hitsound, 100, TRUE, -1) + L.visible_message(span_danger("[L] is hit by \a [src][organ_hit_text]!"), \ + span_userdanger("You're hit by \a [src][organ_hit_text]!"), null, COMBAT_MESSAGE_RANGE) + L.on_hit(src) + + var/reagent_note + if(reagents && reagents.reagent_list) + reagent_note = " REAGENTS:" + for(var/datum/reagent/R in reagents.reagent_list) + reagent_note += "[R.name] ([num2text(R.volume)])" + + if(misfire) + L.log_message("has been hit by a misfired [src] from \a [fired_from] last touched by [fired_from.fingerprintslast]", LOG_ATTACK, color = "orange") + else if(ismob(firer)) + log_combat(firer, L, "shot", src, reagent_note) + else + L.log_message("has been shot by [firer] with [src]", LOG_ATTACK, color="orange") + + return BULLET_ACT_HIT + +/obj/projectile/proc/vol_by_damage() + if(src.damage) + return clamp((src.damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then CLAMP the value between 30 and 100 + else + return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume + +/obj/projectile/proc/on_ricochet(atom/A) + if(!ricochet_auto_aim_angle || !ricochet_auto_aim_range) + return + + var/mob/living/unlucky_sob + var/best_angle = ricochet_auto_aim_angle + if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) + best_angle += NICE_SHOT_RICOCHET_BONUS + for(var/mob/living/L in range(ricochet_auto_aim_range, src.loc)) + if(L.stat == DEAD || !isInSight(src, L) || L == firer) + continue + var/our_angle = abs(closer_angle_difference(Angle, Get_Angle(src.loc, L.loc))) + if(our_angle < best_angle) + best_angle = our_angle + unlucky_sob = L + + if(unlucky_sob) + setAngle(Get_Angle(src, unlucky_sob.loc)) + +/obj/projectile/proc/store_hitscan_collision(datum/point/pcache) + beam_segments[beam_index] = pcache + beam_index = pcache + beam_segments[beam_index] = null + +/obj/projectile/Bump(atom/A) + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) + if(!can_hit_target(A, A == original, TRUE)) + return + Impact(A) + +/** + * Called when the projectile hits something + * This can either be from it bumping something, + * or it passing over a turf/being crossed and scanning that there is infact + * a valid target it needs to hit. + * This target isn't however necessarily WHAT it hits + * that is determined by process_hit and select_target. + * + * Furthermore, this proc shouldn't check can_hit_target - this should only be called if can hit target is already checked. + * Also, we select_target to find what to process_hit first. + */ +/obj/projectile/proc/Impact(atom/A) + if(!trajectory) + qdel(src) + return FALSE + if(LAZYISIN(impacted, A)) // NEVER doublehit + return FALSE + var/datum/point/pcache = trajectory.copy_to() + var/turf/T = get_turf(A) + if(ricochets < ricochets_max && check_ricochet_flag(A) && check_ricochet(A)) + ricochets++ + if(A.handle_ricochet(src)) + on_ricochet(A) + impacted = null // Shoot a x-ray laser at a pair of mirrors I dare you + ignore_source_check = TRUE // Firer is no longer immune + decayedRange = max(0, decayedRange - reflect_range_decrease) + ricochet_chance *= ricochet_decay_chance + damage *= ricochet_decay_damage + range = decayedRange + if(hitscan) + store_hitscan_collision(pcache) + if(ricochet_sound) + playsound(get_turf(src), ricochet_sound, 120, TRUE, 2) //make it loud, we want to make it known when a ricochet happens. for aesthetic reasons mostly + return TRUE + + var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. + def_zone = ran_zone(def_zone, max(80-(7*distance*accuracy_mod), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. + + return process_hit(T, select_target(T, A)) // SELECT TARGET FIRST! + +/** + * The primary workhorse proc of projectile impacts. + * This is a RECURSIVE call - process_hit is called on the first selected target, and then repeatedly called if the projectile still hasn't been deleted. + * + * Order of operations: + * 1. Checks if we are deleted, or if we're somehow trying to hit a null, in which case, bail out + * 2. Adds the thing we're hitting to impacted so we can make sure we don't doublehit + * 3. Checks piercing - stores this. + * Afterwards: + * Hit and delete, hit without deleting and pass through, pass through without hitting, or delete without hitting depending on result + * If we're going through without hitting, find something else to hit if possible and recurse, set unstoppable movement to true + * If we're deleting without hitting, delete and return + * Otherwise, send signal of COMSIG_PROJECTILE_PREHIT to target + * Then, hit, deleting ourselves if necessary. + * @params + * T - Turf we're on/supposedly hitting + * target - target we're hitting + * hit_something - only should be set by recursive calling by this proc - tracks if we hit something already + * + * Returns if we hit something. + */ +/obj/projectile/proc/process_hit(turf/T, atom/target, hit_something = FALSE) + // 1. + if(QDELETED(src) || !T || !target) + return + // 2. + LAZYSET(impacted, target, TRUE) //hash lookup > in for performance in hit-checking + // 3. + var/mode = prehit_pierce(target) + if(mode == PROJECTILE_DELETE_WITHOUT_HITTING) + qdel(src) + return hit_something + else if(mode == PROJECTILE_PIERCE_PHASE) + if(!(movement_type & PHASING)) + temporary_unstoppable_movement = TRUE + movement_type |= PHASING + return process_hit(T, select_target(T, target), hit_something) // try to hit something else + // at this point we are going to hit the thing + // in which case send signal to it + SEND_SIGNAL(target, COMSIG_PROJECTILE_PREHIT, args) + if(mode == PROJECTILE_PIERCE_HIT) + ++pierces + hit_something = TRUE + var/result = target.bullet_act(src, def_zone, mode == PROJECTILE_PIERCE_HIT) + if((result == BULLET_ACT_FORCE_PIERCE) || (mode == PROJECTILE_PIERCE_HIT)) + if(!(movement_type & PHASING)) + temporary_unstoppable_movement = TRUE + movement_type |= PHASING + return process_hit(T, select_target(T, target), TRUE) + qdel(src) + return hit_something + +/** + * Selects a target to hit from a turf + * + * @params + * T - The turf + * target - The "preferred" atom to hit, usually what we Bumped() first. + * + * Priority: + * 0. Anything that is already in impacted is ignored no matter what. Furthermore, in any bracket, if the target atom parameter is in it, that's hit first. + * Furthermore, can_hit_target is always checked. This (entire proc) is PERFORMANCE OVERHEAD!! But, it shouldn't be ""too"" bad and I frankly don't have a better *generic non snowflakey* way that I can think of right now at 3 AM. + * FURTHERMORE, mobs/objs have a density check from can_hit_target - to hit non dense objects over a turf, you must click on them, same for mobs that usually wouldn't get hit. + * 1. The thing originally aimed at/clicked on + * 2. Mobs - picks lowest buckled mob to prevent scarp piggybacking memes + * 3. Objs + * 4. Turf + * 5. Nothing + */ +/obj/projectile/proc/select_target(turf/T, atom/target) + // 1. original + if(can_hit_target(original, TRUE, FALSE)) + return original + var/list/atom/possible = list() // let's define these ONCE + var/list/atom/considering = list() + // 2. mobs + possible = typecache_filter_list(T, GLOB.typecache_living) // living only + for(var/i in possible) + if(!can_hit_target(i, i == original, TRUE)) + continue + considering += i + if(considering.len) + var/mob/living/M = pick(considering) + return M.lowest_buckled_mob() + considering.len = 0 + // 3. objs + possible = typecache_filter_list(T, GLOB.typecache_machine_or_structure) // because why are items ever dense? + for(var/i in possible) + if(!can_hit_target(i, i == original, TRUE)) + continue + considering += i + if(considering.len) + return pick(considering) + // 4. turf + if(can_hit_target(T, T == original, TRUE)) + return T + // 5. nothing + // (returns null) + +//Returns true if the target atom is on our current turf and above the right layer +//If direct target is true it's the originally clicked target. +/obj/projectile/proc/can_hit_target(atom/target, direct_target = FALSE, ignore_loc = FALSE) + if(QDELETED(target) || LAZYISIN(impacted, target)) + return FALSE + if(!ignore_loc && (loc != target.loc) && !(can_hit_turfs && direct_target && loc == target)) + return FALSE + // if pass_flags match, pass through entirely + if(target.pass_flags_self & pass_flags) // phasing + return FALSE + if(!ignore_source_check && firer) + var/mob/M = firer + if((target == firer) || ((target == firer.loc) && ismecha(firer.loc)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target))) + return FALSE + if(target.density) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc. + return TRUE + if(!isliving(target)) + if(isturf(target)) // non dense turfs + return FALSE + if(target.layer < PROJECTILE_HIT_THRESHHOLD_LAYER) + return FALSE + else if(!direct_target) // non dense objects do not get hit unless specifically clicked + return FALSE + else + var/mob/living/L = target + if(direct_target) + return TRUE + if(L.check_concealment(src)) + return FALSE + // If target not able to use items, move and stand - or if they're just dead, pass over. + if(L.stat || (!hit_stunned_targets && HAS_TRAIT(L, TRAIT_IMMOBILIZED) && HAS_TRAIT(L, TRAIT_FLOORED) && HAS_TRAIT(L, TRAIT_HANDS_BLOCKED))) + return FALSE + return TRUE + +/** + * Scan if we should hit something and hit it if we need to + * The difference between this and handling in Impact is + * In this we strictly check if we need to Impact() something in specific + * If we do, we do + * We don't even check if it got hit already - Impact() does that + * In impact there's more code for selecting WHAT to hit + * So this proc is more of checking if we should hit something at all BY having an atom cross us. + */ +/obj/projectile/proc/scan_crossed_hit(atom/movable/A) + if(can_hit_target(A, direct_target = (A == original))) + Impact(A) + +/** + * Scans if we should hit something on the turf we just moved to if we haven't already + * + * This proc is a little high in overhead but allows us to not snowflake CanPass in living and other things. + */ +/obj/projectile/proc/scan_moved_turf() + // Optimally, we scan: mobs --> objs --> turf for impact + // but, overhead is a thing and 2 for loops every time it moves is a no-go. + // realistically, since we already do select_target in impact, we can not do that + // and hope projectiles get refactored again in the future to have a less stupid impact detection system + // that hopefully won't also involve a ton of overhead + if(can_hit_target(original, TRUE, FALSE)) + Impact(original) // try to hit thing clicked on + // else, try to hit mobs + else // because if we impacted original and pierced we'll already have select target'd and hit everything else we should be hitting + for(var/mob/M in loc) // so I guess we're STILL doing a for loop of mobs because living movement would otherwise have snowflake code for projectile CanPass + // so the snowflake vs performance is pretty arguable here + if(can_hit_target(M, M == original, TRUE)) + Impact(M) + break + if(!near_miss_sound) + return FALSE + if(decayedRange <= range+2) + return FALSE + for(var/mob/misser in range(1,src)) + if(!(misser.stat <= SOFT_CRIT)) + continue + misser.playsound_local(get_turf(src), near_miss_sound, 100, FALSE) + misser.shake_animation(damage) + + +/** + * Projectile crossed: When something enters a projectile's tile, make sure the projectile hits it if it should be hitting it. + */ +/obj/projectile/proc/on_entered(datum/source, atom/movable/AM) + SIGNAL_HANDLER + scan_crossed_hit(AM) + +/** + * Projectile can pass through + * Used to not even attempt to Bump() or fail to Cross() anything we already hit. + */ +/obj/projectile/CanPassThrough(atom/blocker, movement_dir, blocker_opinion) + return LAZYISIN(impacted, blocker) ? TRUE : ..() + +/** + * Projectile moved: + * + * If not fired yet, do not do anything. Else, + * + * If temporary unstoppable movement used for piercing through things we already hit (impacted list) is set, unset it. + * Scan turf we're now in for anything we can/should hit. This is useful for hitting non dense objects the user + * directly clicks on, as well as for PHASING projectiles to be able to hit things at all as they don't ever Bump(). + */ +/obj/projectile/Moved(atom/OldLoc, Dir) + . = ..() + if(!fired) + return + if(temporary_unstoppable_movement) + temporary_unstoppable_movement = FALSE + movement_type &= ~PHASING + scan_moved_turf() //mostly used for making sure we can hit a non-dense object the user directly clicked on, and for penetrating projectiles that don't bump + +/** + * Checks if we should pierce something. + * + * NOT meant to be a pure proc, since this replaces prehit() which was used to do things. + * Return PROJECTILE_DELETE_WITHOUT_HITTING to delete projectile without hitting at all! + */ +/obj/projectile/proc/prehit_pierce(atom/A) + if(projectile_phasing & A.pass_flags_self) + return PROJECTILE_PIERCE_PHASE + if(projectile_piercing & A.pass_flags_self) + return PROJECTILE_PIERCE_HIT + if(ismovable(A)) + var/atom/movable/AM = A + if(AM.throwing) + return (projectile_phasing & LETPASSTHROW)? PROJECTILE_PIERCE_PHASE : ((projectile_piercing & LETPASSTHROW)? PROJECTILE_PIERCE_HIT : PROJECTILE_PIERCE_NONE) + return PROJECTILE_PIERCE_NONE + +/obj/projectile/proc/check_ricochet(atom/A) + var/chance = ricochet_chance * A.receive_ricochet_chance_mod + if(firer && HAS_TRAIT(firer, TRAIT_NICE_SHOT)) + chance += NICE_SHOT_RICOCHET_BONUS + if(prob(chance)) + return TRUE + return FALSE + +/obj/projectile/proc/check_ricochet_flag(atom/A) + if((flag in list("energy", "laser")) && (A.flags_ricochet & RICOCHET_SHINY)) + return TRUE + + if((flag in list("bomb", "bullet")) && (A.flags_ricochet & RICOCHET_HARD)) + return TRUE + + return FALSE + +/obj/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight. + if(!trajectory && isnull(forced_angle) && isnull(Angle)) + return FALSE + var/datum/point/vector/current = trajectory + if(!current) + var/turf/T = get_turf(src) + current = new(T.x, T.y, T.z, pixel_x, pixel_y, isnull(forced_angle)? Angle : forced_angle, SSprojectiles.global_pixel_speed) + var/datum/point/vector/v = current.return_vector_after_increments(moves * SSprojectiles.global_iterations_per_move) + return v.return_turf() + +/obj/projectile/proc/return_pathing_turfs_in_moves(moves, forced_angle) + var/turf/current = get_turf(src) + var/turf/ending = return_predicted_turf_after_moves(moves, forced_angle) + return getline(current, ending) + +/obj/projectile/Process_Spacemove(movement_dir = 0) + return TRUE //Bullets don't drift in space + +/obj/projectile/process(seconds_per_tick) + last_process = world.time + if(!loc || !fired || !trajectory) + fired = FALSE + return PROCESS_KILL + if(paused || !isturf(loc)) + last_projectile_move += world.time - last_process //Compensates for pausing, so it doesn't become a hitscan projectile when unpaused from charged up ticks. + return + var/elapsed_time_deciseconds = (world.time - last_projectile_move) + time_offset + time_offset = 0 + var/required_moves = speed > 0? FLOOR(elapsed_time_deciseconds / speed, 1) : MOVES_HITSCAN //Would be better if a 0 speed made hitscan but everyone hates those so I can't make it a universal system :< + if(required_moves == MOVES_HITSCAN) + required_moves = SSprojectiles.global_max_tick_moves + else + if(required_moves > SSprojectiles.global_max_tick_moves) + var/overrun = required_moves - SSprojectiles.global_max_tick_moves + required_moves = SSprojectiles.global_max_tick_moves + time_offset += overrun * speed + time_offset += MODULUS(elapsed_time_deciseconds, speed) + + for(var/i in 1 to required_moves) + pixel_move(1, FALSE) + +/obj/projectile/proc/fire(angle, atom/direct_target) + if(fired_from) + SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original) + //If no angle needs to resolve it from xo/yo! + if(shrapnel_type) + AddElement(/datum/element/embed, projectile_payload = shrapnel_type) + if(!log_override && firer && original) + log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]") + LAZYINITLIST(impacted) + if(direct_target && (get_dist(direct_target, get_turf(src)) <= 1)) // point blank shots + process_hit(get_turf(direct_target), direct_target) + if(QDELETED(src)) + return + if(isnum(angle)) + setAngle(angle) + if(spread) + setAngle(Angle + ((rand() - 0.5) * spread)) + var/turf/starting = get_turf(src) + if(isnull(Angle)) //Try to resolve through offsets if there's no angle set. + if(isnull(xo) || isnull(yo)) + stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!") + qdel(src) + return + var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z) + setAngle(Get_Angle(src, target)) + original_angle = Angle + if(!nondirectional_sprite) + var/matrix/M = new + M.Turn(Angle) + transform = M + trajectory_ignore_forcemove = TRUE + forceMove(starting) + trajectory_ignore_forcemove = FALSE + trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, SSprojectiles.global_pixel_speed) + last_projectile_move = world.time + name = bullet_identifier + fired = TRUE + play_fov_effect(starting, 6, "gunfire", dir = NORTH, angle = Angle) + SEND_SIGNAL(src, COMSIG_PROJECTILE_FIRE) + if(hitscan) + process_hitscan() + if(!(datum_flags & DF_ISPROCESSING)) + START_PROCESSING(SSprojectiles, src) + pixel_move(1, FALSE) //move it now! + +/obj/projectile/proc/setAngle(new_angle, hitscan_store_segment = TRUE) //wrapper for overrides. + Angle = new_angle + if(!nondirectional_sprite) + var/matrix/M = new + M.Turn(Angle) + transform = M + //WS Edit - Hitscan Emitters + if(fired && hitscan && trajectory && isloc(loc) && (loc != last_angle_set_hitscan_store)) + last_angle_set_hitscan_store = loc + var/datum/point/pcache = trajectory.copy_to() + store_hitscan_collision(pcache) + //WS End + if(trajectory) + trajectory.set_angle(new_angle) + return TRUE + +/obj/projectile/forceMove(atom/target) + if(!isloc(target) || !isloc(loc) || !z) + return ..() + var/zc = target.z != z + var/old = loc + if(zc) + before_z_change(old, target) + . = ..() + if(QDELETED(src)) // we coulda bumped something + return + if(trajectory && !trajectory_ignore_forcemove && isturf(target)) + if(hitscan) + finalize_hitscan_and_generate_tracers(FALSE) + trajectory.initialize_location(target.x, target.y, target.z, 0, 0) + if(hitscan) + record_hitscan_start(RETURN_PRECISE_POINT(src)) + if(zc) + after_z_change(old, target) + +/obj/projectile/proc/after_z_change(atom/olcloc, atom/newloc) + +/obj/projectile/proc/before_z_change(atom/oldloc, atom/newloc) + +/obj/projectile/vv_edit_var(var_name, var_value) + switch(var_name) + if(NAMEOF(src, Angle)) + setAngle(var_value) + return TRUE + else + return ..() + +/obj/projectile/proc/set_pixel_speed(new_speed) + if(trajectory) + trajectory.set_speed(new_speed) + return TRUE + return FALSE + +/obj/projectile/proc/record_hitscan_start(datum/point/pcache) + if(pcache) + beam_segments = list() + beam_index = pcache + beam_segments[beam_index] = null //record start. + +/obj/projectile/proc/process_hitscan() + var/safety = range * 10 //WS Edit - 3 to 10 - Hitscan Emitters + record_hitscan_start(RETURN_POINT_VECTOR_INCREMENT(src, Angle, MUZZLE_EFFECT_PIXEL_INCREMENT, 1)) + while(loc && !QDELETED(src)) + if(paused) + stoplag(1) + continue + if(safety-- <= 0) + if(loc) + Bump(loc) + if(!QDELETED(src)) + qdel(src) + return //Kill! + pixel_move(1, TRUE) + +/obj/projectile/proc/pixel_move(trajectory_multiplier, hitscanning = FALSE) + if(!loc || !trajectory) + return + last_projectile_move = world.time + if(!nondirectional_sprite && !hitscanning) + var/matrix/M = new + M.Turn(Angle) + transform = M + if(homing) + process_homing() + var/forcemoved = FALSE + for(var/i in 1 to SSprojectiles.global_iterations_per_move) + trajectory.increment(trajectory_multiplier) + var/turf/T = trajectory.return_turf() + if(!istype(T)) + qdel(src) + return + if(T.z != loc.z) + var/old = loc + before_z_change(loc, T) + trajectory_ignore_forcemove = TRUE + forceMove(T) + trajectory_ignore_forcemove = FALSE + after_z_change(old, loc) + if(!hitscanning) + pixel_x = trajectory.return_px() + pixel_y = trajectory.return_py() + forcemoved = TRUE + hitscan_last = loc + else if(T != loc) + step_towards(src, T) + hitscan_last = loc + if(QDELETED(src)) + return + if(!hitscanning && !forcemoved) + pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move + pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move + animate(src, pixel_x = trajectory.return_px(), pixel_y = trajectory.return_py(), time = 1, flags = ANIMATION_END_NOW) + Range() + +/obj/projectile/proc/process_homing() //may need speeding up in the future performance wise. + if(!homing_target) + return FALSE + var/datum/point/PT = RETURN_PRECISE_POINT(homing_target) + PT.x += clamp(homing_offset_x, 1, world.maxx) + PT.y += clamp(homing_offset_y, 1, world.maxy) + var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT)) + setAngle(Angle + clamp(angle, -homing_turn_speed, homing_turn_speed)) + +/obj/projectile/proc/set_homing_target(atom/A) + if(!A || (!isturf(A) && !isturf(A.loc))) + return FALSE + homing = TRUE + homing_target = A + homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max) + homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max) + if(prob(50)) + homing_offset_x = -homing_offset_x + if(prob(50)) + homing_offset_y = -homing_offset_y + +/** + * Aims the projectile at a target. + * + * Must be passed at least one of a target or a list of click parameters. + * If only passed the click modifiers the source atom must be a mob with a client. + * + * Arguments: + * - [target][/atom]: (Optional) The thing that the projectile will be aimed at. + * - [source][/atom]: The initial location of the projectile or the thing firing it. + * - [modifiers][/list]: (Optional) A list of click parameters to apply to this operation. + * - deviation: (Optional) How the trajectory should deviate from the target in degrees. + * - //Spread is FORCED! + */ +/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, list/modifiers = null, spread = 0) + if(!(isnull(modifiers) || islist(modifiers))) + stack_trace("WARNING: Projectile [type] fired with non-list modifiers, likely was passed click params.") + modifiers = null + + var/turf/source_loc = get_turf(source) + var/turf/target_loc = get_turf(target) + if(isnull(source_loc)) + stack_trace("WARNING: Projectile [type] fired from nullspace.") + qdel(src) + return FALSE + + trajectory_ignore_forcemove = TRUE + forceMove(source_loc) + trajectory_ignore_forcemove = FALSE + + starting = source_loc + pixel_x = source.pixel_x + pixel_y = source.pixel_y + original = target + if(length(modifiers)) + var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, target_loc && target, modifiers) + + p_x = calculated[2] + p_y = calculated[3] + setAngle(calculated[1] + spread) + return TRUE + + if(target_loc) + yo = target_loc.y - source_loc.y + xo = target_loc.x - source_loc.x + setAngle(get_angle(src, target_loc) + spread) + return TRUE + + stack_trace("WARNING: Projectile [type] fired without a target or mouse parameters to aim with.") + qdel(src) + return FALSE + +/proc/calculate_projectile_angle_and_pixel_offsets(atom/source, atom/target, modifiers) + var/angle = 0 + var/p_x = LAZYACCESS(modifiers, ICON_X) ? text2num(LAZYACCESS(modifiers, ICON_X)) : world.icon_size / 2 // ICON_(X|Y) are measured from the bottom left corner of the icon. + var/p_y = LAZYACCESS(modifiers, ICON_Y) ? text2num(LAZYACCESS(modifiers, ICON_Y)) : world.icon_size / 2 // This centers the target if modifiers aren't passed. + + + var/static/list/snowflake_matrix_list = list(/turf/open/floor/grass/ship, /turf/open/floor/plating/asteroid, /turf/open/floor/plating/asteroid/dry_seafloor, /turf/open/floor/plating/asteroid/snow, /turf/open/floor/plating/asteroid/icerock, /turf/open/floor/plating/asteroid/sand) //List of turfs that get translated by -19/-19 for smoothing (this breaks projectiles) + if(target) + if(is_type_in_list(target, snowflake_matrix_list)) //yes, this is stupid + if(target.smoothing_flags) + p_x -= 19 //In short, smoothing flags being active on certain turfs means they are the Big Ones and get translated by -19 x/y to mesh together + p_y -= 19 //Issue: this also moves ICON_X/Y up by 19 since it accounts for the 0,0 of the icon (moved down by 19 x/y), Giving us a wonderful projectile offset of +19 pixels. Which we remove here + var/turf/source_loc = get_turf(source) + var/turf/target_loc = get_turf(target) + var/dx = ((target_loc.x - source_loc.x) * world.icon_size) + (target.pixel_x - source.pixel_x) + (p_x - (world.icon_size / 2)) + var/dy = ((target_loc.y - source_loc.y) * world.icon_size) + (target.pixel_y - source.pixel_y) + (p_y - (world.icon_size / 2)) + + angle = ATAN2(dy, dx) + return list(angle, p_x, p_y) + + if(!ismob(source) || !LAZYACCESS(modifiers, SCREEN_LOC)) + CRASH("Can't make trajectory calculations without a target or click modifiers and a client.") + + var/mob/user = source + if(!user.client) + CRASH("Can't make trajectory calculations without a target or click modifiers and a client.") + + //Split screen-loc up into X+Pixel_X and Y+Pixel_Y + var/list/screen_loc_params = splittext(LAZYACCESS(modifiers, SCREEN_LOC), ",") + //Split X+Pixel_X up into list(X, Pixel_X) + var/list/screen_loc_X = splittext(screen_loc_params[1],":") + //Split Y+Pixel_Y up into list(Y, Pixel_Y) + var/list/screen_loc_Y = splittext(screen_loc_params[2],":") + + var/tx = (text2num(screen_loc_X[1]) - 1) * world.icon_size + text2num(screen_loc_X[2]) + var/ty = (text2num(screen_loc_Y[1]) - 1) * world.icon_size + text2num(screen_loc_Y[2]) + + //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. + var/list/screenview = getviewsize(user.client.view) + var/screenviewX = screenview[1] * world.icon_size + var/screenviewY = screenview[2] * world.icon_size + + var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x + var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y + angle = ATAN2(tx - oy, ty - ox) + return list(angle, p_x, p_y) + +/obj/projectile/Destroy() + if(hitscan) + finalize_hitscan_and_generate_tracers() + STOP_PROCESSING(SSprojectiles, src) + cleanup_beam_segments() + if(trajectory) + QDEL_NULL(trajectory) + //Empties out the list, MIGHT help with landmines hardeling but not all that confident. + LAZYCLEARLIST(impacted) + return ..() + +/obj/projectile/proc/cleanup_beam_segments() + QDEL_LIST_ASSOC(beam_segments) + beam_segments = list() + QDEL_NULL(beam_index) + +/obj/projectile/proc/finalize_hitscan_and_generate_tracers(impacting = TRUE) + if(trajectory && beam_index) + var/datum/point/pcache = trajectory.copy_to() + beam_segments[beam_index] = pcache + generate_hitscan_tracers(null, null, impacting) + +/obj/projectile/proc/generate_hitscan_tracers(cleanup = TRUE, duration = 3, impacting = TRUE) + if(!length(beam_segments)) + return + if(tracer_type) + var/tempref = REF(src) + for(var/datum/point/p in beam_segments) + generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) + if(muzzle_type && duration > 0) + var/datum/point/p = beam_segments[1] + var/atom/movable/thing = new muzzle_type + p.move_atom_to_src(thing) + var/matrix/M = new + M.Turn(original_angle) + thing.transform = M + thing.color = color + thing.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color) + QDEL_IN(thing, duration) + if(impacting && impact_type && duration > 0) + var/datum/point/p = beam_segments[beam_segments[beam_segments.len]] + var/atom/movable/thing = new impact_type + p.move_atom_to_src(thing) + var/matrix/M = new + M.Turn(Angle) + thing.transform = M + thing.color = color + thing.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color) + QDEL_IN(thing, duration) + if(cleanup) + cleanup_beam_segments() + +/obj/projectile/experience_pressure_difference() + return + +///Like [/obj/item/proc/updateEmbedding] but for projectiles instead, call this when you want to add embedding or update the stats on the embedding element +/obj/projectile/proc/updateEmbedding() + if(!shrapnel_type || !LAZYLEN(embedding)) + return + + AddElement(/datum/element/embed,\ + embed_chance = (!isnull(embedding["embed_chance"]) ? embedding["embed_chance"] : EMBED_CHANCE),\ + fall_chance = (!isnull(embedding["fall_chance"]) ? embedding["fall_chance"] : EMBEDDED_ITEM_FALLOUT),\ + pain_chance = (!isnull(embedding["pain_chance"]) ? embedding["pain_chance"] : EMBEDDED_PAIN_CHANCE),\ + pain_mult = (!isnull(embedding["pain_mult"]) ? embedding["pain_mult"] : EMBEDDED_PAIN_MULTIPLIER),\ + remove_pain_mult = (!isnull(embedding["remove_pain_mult"]) ? embedding["remove_pain_mult"] : EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER),\ + rip_time = (!isnull(embedding["rip_time"]) ? embedding["rip_time"] : EMBEDDED_UNSAFE_REMOVAL_TIME),\ + ignore_throwspeed_threshold = (!isnull(embedding["ignore_throwspeed_threshold"]) ? embedding["ignore_throwspeed_threshold"] : FALSE),\ + impact_pain_mult = (!isnull(embedding["impact_pain_mult"]) ? embedding["impact_pain_mult"] : EMBEDDED_IMPACT_PAIN_MULTIPLIER),\ + jostle_chance = (!isnull(embedding["jostle_chance"]) ? embedding["jostle_chance"] : EMBEDDED_JOSTLE_CHANCE),\ + jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\ + pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\ + projectile_payload = shrapnel_type) + return TRUE + +///Checks if the projectile can embed into someone +/obj/projectile/proc/can_embed_into(atom/hit) + return embedding && shrapnel_type && iscarbon(hit) && !HAS_TRAIT(hit, TRAIT_PIERCEIMMUNE) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/beams.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/beams.dm new file mode 100644 index 0000000000..f7cb1754a8 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/beams.dm @@ -0,0 +1,427 @@ +/obj/projectile/beam + name = "laser" + icon_state = "laser" + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + damage = 25 + armour_penetration = -5 + damage_type = BURN + wound_bonus = -20 + bare_wound_bonus = 10 + + hitsound = 'sound/weapons/gun/hit/energy_impact1.ogg' + hitsound_non_living = 'sound/weapons/effects/searwall.ogg' + hitsound_glass = 'sound/weapons/effects/searwall.ogg' + hitsound_stone = 'sound/weapons/sear.ogg' + hitsound_metal = 'sound/weapons/effects/searwall.ogg' + hitsound_wood = 'sound/weapons/sear.ogg' + hitsound_snow = 'sound/weapons/sear.ogg' + + near_miss_sound = 'sound/weapons/gun/hit/energy_miss1.ogg' + ricochet_sound = 'sound/weapons/gun/hit/energy_ricochet1.ogg' + + bullet_identifier = "laser" + + flag = "laser" + eyeblur = 2 + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_system = MOVABLE_LIGHT + light_range = 1.5 + light_power = 1 + light_color = COLOR_SOFT_RED + ricochets_max = 50 //Honk! + ricochet_chance = 90 + reflectable = REFLECT_NORMAL + +/obj/projectile/beam/throw_atom_into_space() + return + + +/obj/projectile/beam/laser + tracer_type = /obj/effect/projectile/tracer/laser + muzzle_type = /obj/effect/projectile/muzzle/laser + impact_type = /obj/effect/projectile/impact/laser + +/obj/projectile/beam/laser/light + damage = 15 + +/obj/projectile/beam/laser/sharplite + icon_state = "nt_laser" + light_color = COLOR_BLUE_LIGHT + damage = 25 + armour_penetration = -5 + + pass_flags = PASSTABLE | PASSGRILLE //does not go through glass + + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + + speed = 0.3 + +/obj/projectile/beam/weak/sharplite + icon_state = "nt_laser_light" + damage = 15 + speed = 0.3 + light_color = COLOR_BLUE_LIGHT + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + pass_flags = PASSTABLE | PASSGRILLE //does not go through glass + + +/obj/projectile/beam/laser/sharplite/dmr + icon_state = "nt_laser_stronger" + damage = 30 + armour_penetration = 20 + +/obj/projectile/beam/laser/assault/sharplite + icon_state = "nt_laser_heavy" + damage = 25 + armour_penetration = 20 + speed = 0.3 + wound_bonus = 0 + bare_wound_bonus = 20 + pass_flags = PASSTABLE | PASSGRILLE //does not go through glass + + light_color = COLOR_BLUE_LIGHT + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + +/obj/projectile/beam/laser/sharplite/sniper + icon_state = "nt_laser_sniper" + damage = 35 + armour_penetration = 30 + speed = 0.2 + wound_bonus = 0 + bare_wound_bonus = 20 + +/obj/projectile/beam/laser/light/sharplite + icon_state = "nt_laser_light" + speed = 0.4 + pass_flags = PASSTABLE | PASSGRILLE //does not go through glass + +/obj/projectile/beam/laser/eoehoma + icon_state = "heavylaser" + damage = 35 + armour_penetration = 0 + speed = 0.8 + +/obj/projectile/beam/laser/eoehoma/wasp + icon_state = "heavylaser" + damage = 30 + +/obj/projectile/beam/laser/eoehoma/heavy + icon_state = "heavylaser" + damage = 60 + knockdown = 50 + armour_penetration = 20 + speed = 1 + +/obj/projectile/beam/laser/eoehoma/heavy/on_hit(atom/target, blocked = FALSE) + ..() + explosion(get_turf(loc),0,0,0,flame_range = 3) + return BULLET_ACT_HIT + +/obj/projectile/beam/laser/assault + icon_state = "heavylaser" + damage = 25 + armour_penetration = 20 + +/obj/projectile/beam/laser/heavylaser + name = "heavy laser" + icon_state = "heavylaser" + damage = 40 + tracer_type = /obj/effect/projectile/tracer/heavy_laser + muzzle_type = /obj/effect/projectile/muzzle/heavy_laser + impact_type = /obj/effect/projectile/impact/heavy_laser + +/obj/projectile/beam/laser/heavylaser/assault + armour_penetration = 20 + +/obj/projectile/beam/laser/heavylaser/sharplite + speed = 0.4 + +/obj/projectile/beam/laser/on_hit(atom/target, blocked = FALSE) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.ignite_mob() + else if(isturf(target)) + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser/wall + +/obj/projectile/beam/weak + damage = 15 + +/obj/projectile/beam/weak/shotgun + damage = 20 + armour_penetration = -10 + var/tile_dropoff = 1 + var/ap_dropoff = 5 + var/ap_dropoff_cutoff = -35 + +/obj/projectile/beam/weak/shotgun/Range() //10% loss per tile = max range of 10, generally + ..() + if(damage > 0) + damage -= tile_dropoff + if(armour_penetration > ap_dropoff_cutoff) + armour_penetration -= ap_dropoff + if(accuracy_mod < 3) + accuracy_mod += 0.3 + if(damage < 0 && stamina < 0) + qdel(src) + +/obj/projectile/beam/weak/shotgun/sharplite + icon_state = "nt_laser_light" + light_color = COLOR_BLUE_LIGHT + + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + + speed = 0.3 + pass_flags = PASSGRILLE | PASSTABLE + +/obj/projectile/beam/weaker + damage = 10 + +/obj/projectile/beam/weak/low_range + damage = 10 + range = 9 + +/obj/projectile/beam/weak/penetrator + armour_penetration = 50 + +/obj/projectile/beam/laser/weak/negative_ap + damage = 15 + armour_penetration = -30 + range = 9 + +/obj/projectile/beam/laser/weak/negative_ap/low_range + range = 6 + +/obj/projectile/beam/practice + name = "practice laser" + damage = 0 + nodamage = TRUE + +/obj/projectile/beam/practice/sharplite + name = "practice laser" + damage = 0 + nodamage = TRUE + speed = 0.25 + +/obj/projectile/beam/laser/slug + name = "laser slug" + icon_state = "heavylaser" + damage = 20 + armour_penetration = 40 + +/obj/projectile/beam/scatter + name = "laser pellet" + icon_state = "scatterlaser" + damage = 5 + range = 7 + +/obj/projectile/beam/xray + name = "\improper X-ray beam" + icon_state = "xray" + flag = "rad" + damage = 15 + irradiate = 300 + range = 15 + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF + + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + tracer_type = /obj/effect/projectile/tracer/xray + muzzle_type = /obj/effect/projectile/muzzle/xray + impact_type = /obj/effect/projectile/impact/xray + +/obj/projectile/beam/disabler + name = "disabler beam" + icon_state = "omnilaser" + damage = 30 + armour_penetration = -20 + damage_type = STAMINA + flag = "energy" + bullet_identifier = "disabler" + hitsound = 'sound/weapons/tap.ogg' + hitsound_glass = null + hitsound_stone = null + hitsound_metal = null + hitsound_wood = null + hitsound_snow = null + eyeblur = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_BLUE + tracer_type = /obj/effect/projectile/tracer/disabler + muzzle_type = /obj/effect/projectile/muzzle/disabler + impact_type = /obj/effect/projectile/impact/disabler + +/obj/projectile/beam/disabler/sharplite + icon_state = "nt_disabler" + light_color = COLOR_PALE_ORANGE + speed = 0.3 + +/obj/projectile/beam/disabler/weak + damage = 15 + +/obj/projectile/beam/disabler/weak/sharplite + speed = 0.4 + +/obj/projectile/beam/disabler/weak/negative_ap + armour_penetration = -30 + range = 9 + +/obj/projectile/beam/disabler/weak/negative_ap/sharplite + icon_state = "nt_disabler_light" + light_color = COLOR_PALE_ORANGE + armour_penetration = -30 + range = 9 + speed = 0.3 + +/obj/projectile/beam/disabler/weak/negative_ap/low_range + range = 6 + +/obj/projectile/beam/pulse + name = "pulse" + icon_state = "u_laser" + damage = 40 + bullet_identifier = "pulse" + wall_damage_flags = PROJECTILE_BONUS_DAMAGE_MINERALS | PROJECTILE_BONUS_DAMAGE_WALLS | PROJECTILE_BONUS_DAMAGE_WALLS + wall_damage_override = 200 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_BLUE + tracer_type = /obj/effect/projectile/tracer/pulse + muzzle_type = /obj/effect/projectile/muzzle/pulse + impact_type = /obj/effect/projectile/impact/pulse + +/obj/projectile/beam/pulse/on_hit(atom/target, blocked = FALSE) + . = ..() + var/turf/targets_turf = target.loc + if(!isopenturf(targets_turf)) + return + targets_turf.ignite_turf(rand(8,22), "blue") + +/obj/projectile/beam/pulse/sharplite_turret + wall_damage_flags = null + wall_damage_override = 0 + speed = 0.4 + +/obj/projectile/beam/pulse/shotgun + damage = 40 + +/obj/projectile/beam/pulse/condor + range = 128 + +/obj/projectile/beam/pulse/heavy + name = "heavy pulse laser" + icon_state = "pulse1_bl" + var/life = 20 + +/obj/projectile/beam/pulse/heavy/on_hit(atom/target, blocked = FALSE) + life -= 10 + if(life > 0) + . = BULLET_ACT_FORCE_PIERCE + ..() + +/obj/projectile/beam/emitter + name = "emitter beam" + icon_state = "emitter" + damage = 60 //osha violation waiting to happen + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + +/obj/projectile/beam/emitter/singularity_pull() + return //don't want the emitters to miss + +/obj/projectile/beam/lasertag + name = "laser tag beam" + icon_state = "omnilaser" + hitsound = null + damage = 0 + damage_type = STAMINA + flag = "laser" + var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_BLUE + +/obj/projectile/beam/lasertag/on_hit(atom/target, blocked = FALSE) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + if(istype(M.wear_suit)) + if(M.wear_suit.type in suit_types) + M.adjustStaminaLoss(34) + +/obj/projectile/beam/lasertag/redtag + icon_state = "laser" + suit_types = list(/obj/item/clothing/suit/bluetag) + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = COLOR_SOFT_RED + tracer_type = /obj/effect/projectile/tracer/laser + muzzle_type = /obj/effect/projectile/muzzle/laser + impact_type = /obj/effect/projectile/impact/laser + +/obj/projectile/beam/lasertag/redtag/hitscan + hitscan = TRUE + +/obj/projectile/beam/lasertag/bluetag + icon_state = "bluelaser" + suit_types = list(/obj/item/clothing/suit/redtag) + tracer_type = /obj/effect/projectile/tracer/laser/blue + muzzle_type = /obj/effect/projectile/muzzle/laser/blue + impact_type = /obj/effect/projectile/impact/laser/blue + +/obj/projectile/beam/lasertag/bluetag/hitscan + hitscan = TRUE + +/obj/projectile/beam/instakill + name = "instagib laser" + icon_state = "purple_laser" + damage = 200 + damage_type = BURN + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + light_color = LIGHT_COLOR_PURPLE + +/obj/projectile/beam/instakill/blue + icon_state = "blue_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_BLUE + +/obj/projectile/beam/instakill/red + icon_state = "red_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = COLOR_SOFT_RED + +/obj/projectile/beam/instakill/on_hit(atom/target) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.visible_message(span_danger("[M] explodes into a shower of gibs!")) + M.gib() + +//a shrink ray that shrinks stuff, which grows back after a short while. +/obj/projectile/beam/shrink + name = "shrink ray" + icon_state = "blue_laser" + hitsound = 'sound/weapons/shrink_hit.ogg' + damage = 0 + damage_type = STAMINA + flag = "energy" + impact_effect_type = /obj/effect/temp_visual/impact_effect/shrink + light_color = LIGHT_COLOR_BLUE + var/shrink_time = 90 + +/obj/projectile/beam/shrink/on_hit(atom/target, blocked = FALSE) + . = ..() + if(isopenturf(target) || istype(target, /turf/closed/indestructible))//shrunk floors wouldnt do anything except look weird, i-walls shouldnt be bypassable + return + target.AddComponent(/datum/component/shrink, shrink_time) + +/obj/projectile/beam/emitter/hitscan + hitscan = TRUE + tracer_type = /obj/effect/projectile/tracer/laser/emitter + muzzle_type = /obj/effect/projectile/muzzle/laser/emitter + impact_type = /obj/effect/projectile/impact/laser/emitter + impact_effect_type = null + +/obj/projectile/beam/emitter/hitscan/on_hit(atom/target, blocked = FALSE) + . = ..() + var/turf/targets_turf = target.loc + if(!isopenturf(targets_turf)) + return + targets_turf.ignite_turf(rand(8,22), "green") diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets.dm new file mode 100644 index 0000000000..a5d8b3d88a --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets.dm @@ -0,0 +1,35 @@ +/obj/projectile/bullet + name = "bullet" + icon_state = "gauss" + damage = 60 + speed = BULLET_SPEED_RIFLE + damage_type = BRUTE + nodamage = FALSE + sharpness = SHARP_POINTY + flag = "bullet" + + hitsound = "bullet_hit" + hitsound_non_living = "bullet_impact" + hitsound_glass = "bullet_hit_glass" + hitsound_stone = "bullet_hit_stone" + hitsound_metal = "bullet_hit_metal" + hitsound_wood = "bullet_hit_wood" + hitsound_snow = "bullet_hit_snow" + + near_miss_sound = "bullet_miss" + ricochet_sound = "bullet_bounce" + + bullet_identifier = "bullet" + + impact_effect_type = /obj/effect/temp_visual/impact_effect + ricochets_max = 5 //should be enough to scare the shit out of someone + ricochet_chance = 30 + ricochet_decay_damage = 0.5 //shouldnt being reliable, but deadly enough to be careful if you accidentally hit an ally + shrapnel_type = /obj/item/shrapnel/bullet + wound_falloff_tile = -2 + embed_falloff_tile = -2 + wound_bonus = -10 + +/obj/projectile/bullet/smite + name = "divine retribution" + damage = 10 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/_incendiary.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/_incendiary.dm new file mode 100644 index 0000000000..13d08533b1 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/_incendiary.dm @@ -0,0 +1,17 @@ +/obj/projectile/bullet/incendiary + damage = 20 + var/fire_stacks = 4 + +/obj/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.ignite_mob() + +/obj/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/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dart_syringe.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dart_syringe.dm new file mode 100644 index 0000000000..6ae9d0fa2b --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dart_syringe.dm @@ -0,0 +1,46 @@ +/obj/projectile/bullet/dart + name = "dart" + icon_state = "cbbolt" + damage = 6 + var/piercing = FALSE + +/obj/projectile/bullet/dart/Initialize() + . = ..() + create_reagents(50, NO_REACT) + +/obj/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, def_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. + ..() + reagents.trans_to(M, reagents.total_volume, method = INJECT) + return BULLET_ACT_HIT + else + blocked = 100 + target.visible_message( + span_danger("\The [src] is deflected!"), \ + span_userdanger("You are protected against \the [src]!")) + + ..(target, blocked) + reagents.flags &= ~(NO_REACT) + reagents.handle_reactions() + return BULLET_ACT_HIT + +/obj/projectile/bullet/dart/metalfoam/Initialize() + . = ..() + reagents.add_reagent(/datum/reagent/aluminium, 15) + reagents.add_reagent(/datum/reagent/foaming_agent, 5) + reagents.add_reagent(/datum/reagent/toxin/acid/fluacid, 5) + +/obj/projectile/bullet/dart/tranq + name = "syringe" + icon_state = "syringeproj" + +/obj/projectile/bullet/dart/tranq/Initialize() + . = ..() + reagents.add_reagent(/datum/reagent/medicine/morphine, 7) + +/obj/projectile/bullet/dart/syringe + name = "syringe" + icon_state = "syringeproj" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dnainjector.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dnainjector.dm new file mode 100644 index 0000000000..a20b1aa879 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/dnainjector.dm @@ -0,0 +1,25 @@ +/obj/projectile/bullet/dnainjector + name = "\improper DNA injector" + icon_state = "syringeproj" + var/obj/item/dnainjector/injector + damage = 5 + hitsound_non_living = "shatter" + +/obj/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, def_zone, FALSE)) + if(injector.inject(M, firer)) + QDEL_NULL(injector) + return BULLET_ACT_HIT + else + blocked = 100 + target.visible_message( + span_danger("\The [src] is deflected!"), + span_userdanger("You are protected against \the [src]!")) + return ..() + +/obj/projectile/bullet/dnainjector/Destroy() + QDEL_NULL(injector) + return ..() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/gauss.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/gauss.dm new file mode 100644 index 0000000000..fb8e735e0f --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/gauss.dm @@ -0,0 +1,63 @@ +// Ferromagnetic Pellet (Prototype Gauss Rifle & Claris) + +/obj/projectile/bullet/gauss + name = "ferromagnetic pellet" + icon_state = "gauss-pellet" + damage = 25 + range = 35 + light_system = 2 + light_color = MOVABLE_LIGHT + light_range = 3 + +/obj/projectile/bullet/gauss/hc + name = "ferromagnetic pellet" + damage = 15 + armour_penetration = 60 + range = 50 + hitscan = TRUE + light_system = 0 + light_range = 0 + muzzle_type = /obj/effect/projectile/muzzle/gauss + tracer_type = /obj/effect/projectile/tracer/gauss + impact_type = /obj/effect/projectile/impact/gauss + +// Ferromagnetic Lance (GAR AR) + +/obj/projectile/bullet/gauss/lance + name = "ferromagnetic lance" + icon_state = "redtrac" + damage = 30 + armour_penetration = 20 + +/obj/projectile/bullet/gauss/lance/hc + name = "ferromagnetic lance" + damage = 20 + armour_penetration = 80 + range = 50 + hitscan = TRUE + light_system = 0 + light_range = 0 + muzzle_type = /obj/effect/projectile/muzzle/gauss + tracer_type = /obj/effect/projectile/tracer/gauss + impact_type = /obj/effect/projectile/impact/gauss + +// Ferromagnetic Slug (Model H) + +/obj/projectile/bullet/gauss/slug + name = "ferromagnetic slug" + icon_state = "gauss-slug" + damage = 50 + armour_penetration = -60 + speed = 0.8 + +/obj/projectile/bullet/gauss/slug/hc + name = "ferromagnetic lance" + damage = 25 + armour_penetration = 0 + range = 50 + hitscan = TRUE + light_system = 0 + light_range = 0 + muzzle_type = /obj/effect/projectile/muzzle/gauss + tracer_type = /obj/effect/projectile/tracer/gauss + impact_type = /obj/effect/projectile/impact/gauss diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/grenade.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/grenade.dm new file mode 100644 index 0000000000..254594eaaf --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/grenade.dm @@ -0,0 +1,12 @@ +// 40mm (Grenade Launcher + +/obj/projectile/bullet/a40mm + name ="40mm grenade" + desc = "USE A WEEL GUN" + icon_state= "bolter" + damage = 60 + +/obj/projectile/bullet/a40mm/on_hit(atom/target, blocked = FALSE) + ..() + explosion(target, -1, 0, 2, 1, 0, flame_range = 3) + return BULLET_ACT_HIT diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/lmg.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/lmg.dm new file mode 100644 index 0000000000..2ae19e7ca3 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/lmg.dm @@ -0,0 +1,103 @@ +// C3D (Borgs) + +/obj/projectile/bullet/c3d + damage = 20 + +/obj/projectile/bullet/ctac + damage = 40 + armour_penetration = 35 + speed = 0.4 + +/obj/projectile/bullet/csour + damage = 0 + stamina = 60 + jitter = 30 SECONDS + stutter = 10 + slur = 10 + knockdown = 5 + armour_penetration = 30 + +/obj/projectile/bullet/csweet + damage = 5 + irradiate = 250 + speed = 1.2 + +/obj/projectile/bullet/csweet/on_hit(atom/target, blocked = FALSE) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(8) + M.ignite_mob() + +// Mech LMG + +/obj/projectile/bullet/lmg + damage = 25 + armour_penetration = 40 + +// Mech FNX-99 + +/obj/projectile/bullet/incendiary/fnx99 + damage = 25 + +// Mech Railgun + +/obj/projectile/bullet/p50/penetrator/sabot + name = "Iron-tungsten rod" + icon_state = "sabot" + projectile_piercing = NONE // Piercing was requested to be disabled + projectile_phasing = NONE + var/anti_armour_damage = 50 + ricochet_chance = 0 // Superheated tungsten rod - I'd like to imagine it's impossible for it to ricochet + speed = 0.1 // Railgun, go ludicrously fast to make up for lost piercing + +/obj/projectile/bullet/p50/penetrator/sabot/on_hit(atom/target, blocked = FALSE) + ..() + if(ismecha(target)) + var/obj/mecha/M = target + M.take_damage(anti_armour_damage) + // Mechs take extra damage + return BULLET_ACT_HIT + +// Turrets + +/obj/projectile/bullet/manned_turret + damage = 30 + armour_penetration = 40 + +/obj/projectile/bullet/manned_turret/hmg + icon_state = "redtrac" + armour_penetration = 40 + +/obj/projectile/bullet/syndicate_turret + damage = 20 + armour_penetration = 20 + +// 7.12x82mm (L6 SAW) + +/obj/projectile/bullet/mm712x82 + name = "7.12x82mm bullet" + damage = 25 + armour_penetration = 40 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "large bullet" + +/obj/projectile/bullet/mm712x82/ap + name = "7.12x82mm armor-piercing bullet" + armour_penetration = 75 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/mm712x82/hp + name = "7.12x82mm hollow point bullet" + damage = 40 + armour_penetration = -20 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/mm712x82/match + name = "7.12x82mm match bullet" + armour_penetration = 50 + ricochets_max = 2 + ricochet_chance = 60 + ricochet_auto_aim_range = 4 + ricochet_incidence_leeway = 35 + speed_mod = BULLET_SPEED_HP_MOD diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/pistol.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/pistol.dm new file mode 100644 index 0000000000..b76240f611 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/pistol.dm @@ -0,0 +1,137 @@ +//.22lr (Himehabu, Micro Target, Pounder (uwu)) + +/obj/projectile/bullet/c22lr + name = ".22LR bullet" + damage = 20 + armour_penetration = -40 + ricochet_incidence_leeway = 20 + ricochet_chance = 65 + speed = BULLET_SPEED_HANDGUN + bullet_identifier = "tiny bullet" + +/obj/projectile/bullet/c22lr/hp + name = ".22LR HP bullet" + damage = 24 + armour_penetration = -50 + ricochet_chance = 0 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c22lr/ap + name = ".22LR armor piercing bullet" + damage = 18 + armour_penetration = -20 + ricochet_incidence_leeway = 20 + ricochet_chance = 30 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c22lr/rubber + name = ".22LR rubber bullet" + damage = 4 + stamina = 15 + armour_penetration = -50 + speed_mod = BULLET_SPEED_HV_MOD //do not do this for other rubber bullets. If you do I will come out of the woodwork and bludgeon you to death with this stick i found. + ricochets_max = 8 //ding ding ding ding + ricochet_incidence_leeway = 70 + ricochet_chance = 130 + ricochet_decay_damage = 0.8 + bullet_identifier = "tiny rubber bullet" + +// 9x18mm (Commander, SABR) + +/obj/projectile/bullet/c9mm + name = "9x18mm bullet" + damage = 20 + armour_penetration = -20 + embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + speed = BULLET_SPEED_HANDGUN + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c9mm/surplus + name = "9x18mm surplus bullet" + speed_mod = BULLET_SPEED_SURPLUS_MOD + +/obj/projectile/bullet/c9mm/ap + name = "9x18mm armor-piercing bullet" + damage = 18 + armour_penetration = 10 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c9mm/hp + name = "9x18mm hollow point bullet" + damage = 30 + armour_penetration = -30 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c9mm/rubber + name = "9x18mm rubber bullet" + damage = 5 + armour_penetration = -40 + stamina = 30 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" + +// 10x22mm (Ringneck) + +/obj/projectile/bullet/c10mm + name = "10x22mm bullet" + damage = 25 + armour_penetration = -20 + speed = BULLET_SPEED_HANDGUN + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c10mm/surplus + name = "10x22mm surplus bullet" + speed_mod = BULLET_SPEED_SURPLUS_MOD + +/obj/projectile/bullet/c10mm/ap + name = "10x22mm armor-piercing bullet" + damage = 23 + armour_penetration = 10 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c10mm/hp + name = "10x22mm hollow point bullet" + damage = 35 + armour_penetration = -30 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c10mm/rubber + name = "10x22mm rubber bullet" + damage = 7 + stamina = 35 + armour_penetration = -30 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" + +// .45 (Candor, C20r) + +/obj/projectile/bullet/c45 + name = ".45 bullet" + damage = 25 + armour_penetration = -20 + speed = BULLET_SPEED_HANDGUN + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c45/surplus + name = ".45 surplus bullet" + speed_mod = BULLET_SPEED_SURPLUS_MOD + +/obj/projectile/bullet/c45/ap + name = ".45 armor-piercing bullet" + damage = 22 + armour_penetration = 10 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c45/hp + name = ".45 hollow point bullet" + damage = 37 + armour_penetration = -30 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c45/rubber + name = ".45 rubber bullet" + damage = 7 + stamina = 37 + armour_penetration = -30 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/revolver.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/revolver.dm new file mode 100644 index 0000000000..06f7771c90 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/revolver.dm @@ -0,0 +1,197 @@ +// .38 (Colt Detective Special & Winchester) + +/obj/projectile/bullet/c38 + name = ".38 bullet" + damage = 20 + armour_penetration = -20 + speed = BULLET_SPEED_REVOLVER + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c38/surplus + speed_mod = BULLET_SPEED_SURPLUS_MOD + +/obj/projectile/bullet/c38/match + name = ".38 match bullet" + armour_penetration = -10 + speed_mod = BULLET_SPEED_AP_MOD + + wound_bonus = -20 + bare_wound_bonus = 10 + embedding = list(embed_chance=15, fall_chance=2, jostle_chance=2, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=3, jostle_pain_mult=5, rip_time=10) + + ricochets_max = 4 + ricochet_chance = 100 + ricochet_auto_aim_angle = 40 + ricochet_auto_aim_range = 5 + ricochet_incidence_leeway = 50 + ricochet_decay_chance = 1 + ricochet_decay_damage = 1 + +/obj/projectile/bullet/c38/bouncy + name = ".38 rubber bullet" + damage = 7 + stamina = 28 + armour_penetration = -60 + speed_mod = BULLET_SPEED_RUBBER_MOD + ricochets_max = 6 + ricochet_incidence_leeway = 70 + ricochet_chance = 130 + ricochet_decay_damage = 0.8 + bullet_identifier = "small rubber bullet" + +/obj/projectile/bullet/c38/dumdum + name = ".38 prism bullet" + damage = 20 + armour_penetration = -30 + ricochets_max = 0 + shrapnel_type = /obj/item/shrapnel/bullet/c38/dumdum + +/obj/projectile/bullet/c38/trac + name = ".38 tracker" + damage = 10 + ricochets_max = 0 + shrapnel_type = /obj/item/shrapnel/bullet/tracker/c38 + +/obj/projectile/bullet/c38/hotshot //similar to incendiary bullets, but do not leave a flaming trail + name = ".38 hearth bullet" + ricochets_max = 0 + +/obj/projectile/bullet/c38/hotshot/on_hit(atom/target, blocked = FALSE) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(3) + M.ignite_mob() + +/obj/projectile/bullet/c38/iceblox //see /obj/projectile/temp for the original code + name = ".38 chilled bullet" + var/temperature = 100 + ricochets_max = 0 + +/obj/projectile/bullet/c38/iceblox/on_hit(atom/target, blocked = FALSE) + . = ..() + if(isliving(target)) + var/mob/living/M = target + M.adjust_bodytemperature(((100-blocked)/100)*(temperature - M.bodytemperature)) + +/obj/projectile/bullet/c38/ashwine + name = ".38 hallucinogenic bullet" + ricochets_max = 0 + +/obj/projectile/bullet/c38/ashwine/on_hit(atom/target, blocked = FALSE) + . = ..() + if(isliving(target)) + var/mob/living/M = target + M.set_timed_status_effect(10 SECONDS, /datum/status_effect/jitter) + M.set_timed_status_effect(10 SECONDS, /datum/status_effect/dizziness, only_if_higher = TRUE) + M.adjust_drugginess(10) + +/obj/projectile/bullet/c38/shock + name = ".38 shock bullet" + ricochets_max = 0 + var/zap_flags = ZAP_MOB_DAMAGE + +/obj/projectile/bullet/c38/shock/on_hit(atom/target, blocked = FALSE) + . = ..() + if(isliving(target)) + var/mob/living/M = target + do_sparks(5, FALSE, M) + M.electrocute_act(5, src, siemens_coeff = 1, flags = SHOCK_NOSTUN|SHOCK_TESLA) + else + tesla_zap(target, 5, 2000, zap_flags) + +/obj/projectile/bullet/c38/force + name = ".38 force bullet" + armour_penetration = 10 + ricochets_max = 0 + +/obj/projectile/bullet/c38/force/on_hit(atom/target, blocked = FALSE) + . = ..() + if(ismovable(target) && isliving(target)) + var/atom/movable/M = target + var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) + M.safe_throw_at(throw_target, 2, 2) + +// .357 (Syndicate Revolver) + +/obj/projectile/bullet/a357 + name = ".357 bullet" + damage = 35 + + speed = BULLET_SPEED_REVOLVER + bullet_identifier = "medium bullet" + +/obj/projectile/bullet/a357/match + name = ".357 match bullet" + armour_penetration = 10 + speed_mod = BULLET_SPEED_AP_MOD + ricochets_max = 5 + ricochet_chance = 140 + ricochet_auto_aim_angle = 50 + ricochet_auto_aim_range = 6 + ricochet_incidence_leeway = 80 + ricochet_decay_chance = 1 + +/obj/projectile/bullet/a357/hp + name = ".357 hollow point bullet" + damage = 50 + armour_penetration = -20 + speed_mod = BULLET_SPEED_HP_MOD + ricochet_chance = 0 + +// .45-70 Gov't (Hunting Revolver) + +/obj/projectile/bullet/a4570 + name = ".45-70 bullet" + damage = 45 //crits in 3-4 taps depending on armor + speed = BULLET_SPEED_REVOLVER + bullet_identifier = "large bullet" + +/obj/projectile/bullet/a4570/match + name = ".45-70 match bullet" + armour_penetration = 10 + speed_mod = BULLET_SPEED_AP_MOD + ricochets_max = 5 + ricochet_chance = 140 + ricochet_auto_aim_angle = 50 + ricochet_auto_aim_range = 6 + ricochet_incidence_leeway = 80 + ricochet_decay_chance = 1 + +/obj/projectile/bullet/a4570/hp + name = ".45-70 hollow point bullet" + damage = 55 + armour_penetration = -10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/a4570/explosive //for extra oof + name = ".45-70 explosive bullet" + dismemberment = 50 //literally blow limbs off + +/obj/projectile/bullet/a4570/explosive/on_hit(atom/target, blocked = FALSE) + ..() + explosion(target, -1, 0, 1) + return BULLET_ACT_HIT + +// 44 Short (Roumain & Shadow) + +/obj/projectile/bullet/a44roum + name = ".44 roumain bullet" + damage = 30 + speed = BULLET_SPEED_REVOLVER + bullet_identifier = "small bullet" + +/obj/projectile/bullet/a44roum/rubber + name = ".44 roumain rubber bullet" + damage = 10 + stamina = 40 + armour_penetration = -10 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" + +/obj/projectile/bullet/a44roum/hp + name = ".44 roumain hollow point bullet" + damage = 45 + armour_penetration = -10 + ricochet_chance = 0 + speed_mod = BULLET_SPEED_HP_MOD diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/rifle.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/rifle.dm new file mode 100644 index 0000000000..4ed09dac99 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/rifle.dm @@ -0,0 +1,129 @@ +// 5.56x42mm CLIP (CM82, Hydra variants) + +/obj/projectile/bullet/a556_42 + name = "5.56x42mm CLIP bullet" + damage = 25 + armour_penetration = 20 + wound_bonus = -40 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "medium bullet" + +/obj/projectile/bullet/a556_42/hp + name = "5.56x42mm CLIP hollow point bullet" + damage = 35 + armour_penetration = 10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/a556_42/ap + name = "5.56x42mm CLIP AP bullet" + damage = 22 + armour_penetration = 40 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/a556_42/rubber + name = "5.56x42mm CLIP Rubber bullet" + damage = 5 + stamina = 25 + armour_penetration = 0 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "medium bullet" + +// 8x50mmR (Illestren Rifle) + +/obj/projectile/bullet/a8_50r + name = "8x50mmR bullet" + damage = 37 + armour_penetration = 30 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "large bullet" + +/obj/projectile/bullet/a8_50r/hp + name = "8x50mmR hollow point bullet" + damage = 50 + armour_penetration = 10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/a8_50r/match + name = "8x50mmR match bullet" + damage = 40 + armour_penetration = 27 + speed_mod = BULLET_SPEED_AP_MOD + ricochets_max = 4 + ricochet_chance = 80 + ricochet_auto_aim_angle = 40 + ricochet_auto_aim_range = 5 + ricochet_incidence_leeway = 50 + ricochet_decay_chance = 1 + ricochet_decay_damage = 1 + +/obj/projectile/bullet/a8_50r/trac + damage = 10 + armour_penetration = 0 + shrapnel_type = /obj/item/shrapnel/bullet/tracker/a8_50r + +//7.62x40mm CLIP (SKM Rifles) + +/obj/projectile/bullet/a762_40 + name = "7.62x40mm CLIP bullet" + damage = 30 + armour_penetration = 20 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "medium bullet" + +/obj/projectile/bullet/a762_40/hp + name = "7.62x40mm CLIP hollow point bullet" + damage = 40 + armour_penetration = 10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/a762_40/ap + name = "7.62x40mm CLIP armor piercing bullet" + damage = 27 + armour_penetration = 40 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/a762_40/rubber //"rubber" + name = "7.62x40mm CLIP rubber bullet" + damage = 15 + stamina = 40 + armour_penetration = 10 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "medium rubber bullet" + +//.308 WIN (M514 & GAL DMRs) + +/obj/projectile/bullet/a308 + name = ".308 bullet" + damage = 35 + armour_penetration = 40 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "large bullet" + +/obj/projectile/bullet/a308/hp + name = ".308 hollow point bullet" + damage = 40 + armour_penetration = 30 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/a308/ap + name = ".308 armor piercing bullet" + damage = 32 + armour_penetration = 60 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/a308/rubber //"rubber" + name = ".308 rubber bullet" + damage = 20 + stamina = 40 + armour_penetration = 20 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "large rubber bullet" + +// .299 Eoehoma Caseless (E-40) + +/obj/projectile/bullet/c299 + name = ".299 Eoehoma caseless bullet" + damage = 20 + armour_penetration = 10 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "medium bullet" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/shotgun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/shotgun.dm new file mode 100644 index 0000000000..897d1b445d --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -0,0 +1,133 @@ +/obj/projectile/bullet/slug + name = "12g shotgun slug" + damage = 40 + armour_penetration = 0 + speed = BULLET_SPEED_SHOTGUN + bullet_identifier = "large slug" + +/obj/projectile/bullet/slug/beanbag + name = "beanbag slug" + damage = 10 + stamina = 60 + armour_penetration = -45 + +/obj/projectile/bullet/incendiary/shotgun + name = "incendiary slug" + damage = 25 + armour_penetration = -10 + speed = BULLET_SPEED_SHOTGUN + +/obj/projectile/bullet/incendiary/shotgun/dragonsbreath + name = "dragonsbreath pellet" + damage = 8 + armour_penetration = -35 + +/obj/projectile/bullet/slug/stun + name = "stunslug" + damage = 5 + paralyze = 100 + stutter = 5 + jitter = 20 SECONDS + range = 7 + icon_state = "spark" + color = "#FFFF00" + +/obj/projectile/bullet/slug/meteor + name = "meteorslug" + icon = 'icons/obj/meteor.dmi' + icon_state = "dust" + damage = 30 + paralyze = 15 + knockdown = 80 + hitsound = 'sound/effects/meteorimpact.ogg' + +/obj/projectile/bullet/slug/meteor/on_hit(atom/target, blocked = FALSE) + . = ..() + if(ismovable(target)) + var/atom/movable/M = target + var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) + M.safe_throw_at(throw_target, 3, 2) + +/obj/projectile/bullet/slug/meteor/Initialize() + . = ..() + SpinAnimation() + +/obj/projectile/bullet/slug/frag12 + name = "frag12 slug" + damage = 25 + paralyze = 20 + +/obj/projectile/bullet/slug/frag12/on_hit(atom/target, blocked = FALSE) + ..() + explosion(target, -1, 0, 1) + return BULLET_ACT_HIT + +/obj/projectile/bullet/pellet + ///How much damage is subtracted per tile? + var/tile_dropoff = 1 //Standard of 10% per tile + ///How much stamina damage is subtracted per tile? + var/tile_dropoff_stamina = 1.5 //As above + + var/ap_dropoff = 5 + var/ap_dropoff_cutoff = -25 + + icon_state = "pellet" + armour_penetration = -10 + speed = BULLET_SPEED_SHOTGUN + bullet_identifier = "pellet" + +/obj/projectile/bullet/pellet/buckshot + name = "buckshot pellet" + damage = 13 + +/obj/projectile/bullet/pellet/rubbershot + name = "rubbershot pellet" + damage = 2.5 + tile_dropoff = 0.15 + stamina = 15 + armour_penetration = -35 + bullet_identifier = "rubber pellet" + +/obj/projectile/bullet/pellet/rubbershot/incapacitate + name = "incapacitating pellet" + damage = 1 + tile_dropoff = 0.1 + stamina = 6 + tile_dropoff_stamina = 0.6 + +/obj/projectile/bullet/pellet/Range() //10% loss per tile = max range of 10, generally + ..() + if(damage > 0) + damage -= tile_dropoff + if(stamina > 0) + stamina -= tile_dropoff_stamina + if(armour_penetration > ap_dropoff_cutoff) + armour_penetration -= ap_dropoff + if(accuracy_mod < 3) + accuracy_mod += 0.3 + if(damage < 0 && stamina < 0) + qdel(src) + +/obj/projectile/bullet/pellet/improvised + damage = 6 + armour_penetration = -60 + tile_dropoff = 0.6 + +// Mech Scattershot + +/obj/projectile/bullet/pellet/scattershot + damage = 24 + armour_penetration = -20 + +/obj/projectile/bullet/pellet/buckshot/twobore + name = "two-bore pellet" + damage = 30 + armour_penetration = -25 + tile_dropoff = 3 + bullet_identifier = "massive pellet" + +/obj/projectile/bullet/pellet/blank + name = "blank" + damage = 30 + range = 2 + armour_penetration = -70 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/smg.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/smg.dm new file mode 100644 index 0000000000..ebfa6bda9d --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/smg.dm @@ -0,0 +1,102 @@ +// 5.7x39mm (Asp and Sidewinder) + +/obj/projectile/bullet/c57x39mm + name = "5.7x39mm bullet" + damage = 20 + speed = BULLET_SPEED_PDW + armour_penetration = 10 + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c57x39mm/hp + name = "5.7x39mm hollow point bullet" + damage = 30 + armour_penetration = -10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c57x39mm/ap + name = "5.7x39mm armor piercing bullet" + damage = 19 + armour_penetration = 30 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c57x39mm/rubber + name = "5.7x39mm rubber bullet" + damage = 5 + stamina = 20 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" + +// 4.6x30mm (WT-550 Automatic Rifle & NT-SVG) + +/obj/projectile/bullet/c46x30mm + name = "4.6x30mm bullet" + damage = 20 + speed = BULLET_SPEED_PDW + armour_penetration = 10 + wound_bonus = -5 + bare_wound_bonus = 5 + embed_falloff_tile = -4 + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c46x30mm/recycled + damage = 15 + speed_mod = BULLET_SPEED_SURPLUS_MOD + +/obj/projectile/bullet/c46x30mm/ap + name = "4.6x30mm armor-piercing bullet" + damage = 18 + armour_penetration = 40 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c46x30mm/hp + name = "4.6x30mm HP bullet" + damage = 30 + armour_penetration = -10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c46x30mm/rubber + name = "4.6x30mm bullet" + damage = 4 + stamina = 20 + armour_penetration = -10 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" + +// 4.73x33mm caseless (Solar) + +/obj/projectile/bullet/c47x33mm + name = "4.73x33mm bullet" + damage = 25 + armour_penetration = 20 + bullet_identifier = "small bullet" + +// 5.56 HITP caseless (Solare C) + +/obj/projectile/bullet/c556mm + name = "5.56mm HITP bullet" + damage = 20 + bullet_identifier = "small bullet" + +/obj/projectile/bullet/c556mm/surplus + name = "5.56mm HITP surplus bullet" + speed_mod = BULLET_SPEED_SURPLUS_MOD + +/obj/projectile/bullet/c556mm/ap + name = "5.56mm HITP AP bullet" + damage = 19 + armour_penetration = 20 + speed_mod = BULLET_SPEED_AP_MOD + +/obj/projectile/bullet/c556mm/hp + name = "5.56mm HITP hollow point bullet" + damage = 30 + armour_penetration = -10 + speed_mod = BULLET_SPEED_HP_MOD + +/obj/projectile/bullet/c556mm/rubber + name = "5.56mm HITP rubber bullet" + damage = 5 + stamina = 30 + armour_penetration = -10 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "small rubber bullet" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/sniper.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/sniper.dm new file mode 100644 index 0000000000..5a99f8d777 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/sniper.dm @@ -0,0 +1,101 @@ +// .50 BMG (Sniper) + +/obj/projectile/bullet/p50 + name = ".50 BMG bullet" + speed = BULLET_SPEED_SNIPER + damage = 70 + knockdown = 50 + armour_penetration = 60 + var/breakthings = TRUE + bullet_identifier = "huge bullet" + +/obj/projectile/bullet/p50/on_hit(atom/target, blocked = 0) + if(isobj(target) && (blocked != 100) && breakthings) + var/obj/O = target + O.take_damage(80, BRUTE, "bullet", FALSE) + return ..() + +/obj/projectile/bullet/p50/soporific + name = ".50 BMG soporific bullet" + armour_penetration = 0 + damage = 0 + dismemberment = 0 + knockdown = 0 + breakthings = FALSE + +/obj/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/projectile/bullet/p50/penetrator + name = ".50 BMG penetrator round" + icon_state = "gauss" + damage = 60 + projectile_piercing = PASSMOB + projectile_phasing = (ALL & (~PASSMOB)) + dismemberment = 0 //It goes through you cleanly. + knockdown = 0 + breakthings = FALSE + +//6.5mm CLIP (F90, Boomslang) + +/obj/projectile/bullet/a65clip + name = "6.5mm CLIP bullet" + stamina = 10 + damage = 40 + armour_penetration = 50 + bullet_identifier = "huge bullet" + + speed = BULLET_SPEED_SNIPER + + icon_state = "redtrac" + light_system = MOVABLE_LIGHT + light_color = COLOR_SOFT_RED + light_range = 2 + +/obj/projectile/bullet/a65clip/trac + damage = 10 + armour_penetration = 0 + shrapnel_type = /obj/item/shrapnel/bullet/tracker/a65clip + +//this should only exist on the big ass turrets. don't fucking give players this. +/obj/projectile/bullet/a65clip/rubber //"rubber" + name = "6.5mm CLIP rubber bullet" + damage = 10 + stamina = 40 + speed_mod = BULLET_SPEED_RUBBER_MOD + bullet_identifier = "huge rubber bullet" + +// 8x58mm caseless (SG-669) + +/obj/projectile/bullet/a858 + name = "8x58mm caseless bullet" + damage = 45 + stamina = 10 + armour_penetration = 50 + speed = BULLET_SPEED_SNIPER + bullet_identifier = "huge bullet" + +/obj/projectile/bullet/a858/trac + name = "8x58mm tracker" + damage = 12 + armour_penetration = 0 + shrapnel_type = /obj/item/shrapnel/bullet/tracker/a858 + +// .300 Magnum + +/obj/projectile/bullet/a300 + name = ".300 Magnum bullet" + damage = 50 + stamina = 10 + armour_penetration = 40 + speed = BULLET_SPEED_RIFLE + bullet_identifier = "huge bullet" + +/obj/projectile/bullet/a300/trac + name = ".300 Tracker" + damage = 10 + armour_penetration = 0 + shrapnel_type = /obj/item/shrapnel/bullet/tracker/a308 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/turret.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/turret.dm new file mode 100644 index 0000000000..916f68af9b --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/bullets/turret.dm @@ -0,0 +1,12 @@ +//folder for turret bullets, so we're not pulling from Random Files + +/obj/projectile/bullet/turret + name = "bullet" //taking name suggestions + damage = 60 //this is on par with the old projectile + +/obj/projectile/bullet/turret/rubber + name = "rubbershot bullet" + damage = 5 //"less than lethal" + stamina = 30 + + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/_energy.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/_energy.dm new file mode 100644 index 0000000000..49144127d9 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/_energy.dm @@ -0,0 +1,7 @@ +/obj/projectile/energy + name = "energy" + icon_state = "spark" + damage = 0 + damage_type = BURN + flag = "energy" + reflectable = REFLECT_NORMAL diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/ebow.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/ebow.dm new file mode 100644 index 0000000000..c29c692b7e --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/ebow.dm @@ -0,0 +1,18 @@ +/obj/projectile/energy/bolt //ebow bolts + name = "bolt" + icon_state = "cbbolt" + damage = 15 + damage_type = TOX + nodamage = FALSE + stamina = 60 + eyeblur = 10 + knockdown = 10 + slur = 5 + +/obj/projectile/energy/bolt/halloween + name = "candy corn" + icon_state = "candy_corn" + icon = 'icons/obj/food/food.dmi' + +/obj/projectile/energy/bolt/large + damage = 20 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/misc.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/misc.dm new file mode 100644 index 0000000000..6d7a3a2efb --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/misc.dm @@ -0,0 +1,59 @@ +/obj/projectile/energy/declone + name = "radiation beam" + icon_state = "declone" + damage = 20 + damage_type = CLONE + irradiate = 100 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + +/obj/projectile/energy/declone/weak + damage = 9 + irradiate = 30 + +/obj/projectile/energy/dart //ninja throwing dart + name = "dart" + icon_state = "toxin" + damage = 5 + damage_type = TOX + paralyze = 100 + range = 7 + +/obj/projectile/energy/buster + name = "buster blast" + icon_state = "pulse1" + damage = 0 + damage_type = BURN + +/obj/projectile/energy/plasmabolt + name = "ionized plasma" + damage = 25 + armour_penetration = -15 + range = 8 + damage_type = BURN + icon_state = "blastwave" + color = "#00ff00" + hitsound = 'sound/weapons/sear.ogg' + var/heatpwr = 6 + +/obj/projectile/energy/plasmabolt/on_hit(atom/target, blocked = FALSE) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/cooked = target + cooked.adjust_bodytemperature(heatpwr) + if(prob(35)) + cooked.adjust_fire_stacks(15) + cooked.ignite_mob() + else + if(cooked.on_fire) + cooked.adjust_fire_stacks(10) + +/obj/projectile/energy/plasmabolt/shred + name = "high-energy ionized plasma" + damage = 35 + armour_penetration = -5 + range = 2 + damage_type = BURN + icon_state = "blastwave" + color = "#00ff00" + hitsound = 'sound/weapons/sear.ogg' + heatpwr = 11 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/net_snare.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/net_snare.dm new file mode 100644 index 0000000000..0e1dff3d1e --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/net_snare.dm @@ -0,0 +1,97 @@ +/obj/projectile/energy/net + name = "energy netting" + icon_state = "e_netting" + damage = 10 + damage_type = STAMINA + hitsound = 'sound/weapons/taserhit.ogg' + range = 10 + +/obj/projectile/energy/net/Initialize() + . = ..() + SpinAnimation() + +/obj/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/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/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_REF(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, channel = TELEPORT_CHANNEL_BLUESPACE)//teleport what's in the tile to the beacon + else + for(var/mob/living/L in get_turf(src)) + do_teleport(L, L, 15, channel = TELEPORT_CHANNEL_BLUESPACE) //Otherwise it just warps you off somewhere. + + qdel(src) + +/obj/effect/nettingportal/singularity_act() + return + +/obj/effect/nettingportal/singularity_pull() + return + +/obj/projectile/energy/trap + name = "energy snare" + icon_state = "e_snare" + nodamage = TRUE + hitsound = 'sound/weapons/taserhit.ogg' + range = 4 + +/obj/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.on_entered(src, target) + . = ..() + +/obj/projectile/energy/trap/on_range() + new /obj/item/restraints/legcuffs/beartrap/energy(loc) + ..() + +/obj/projectile/energy/trap/cyborg + name = "Energy Bola" + icon_state = "e_snare" + nodamage = TRUE + paralyze = 0 + hitsound = 'sound/weapons/taserhit.ogg' + range = 10 + +/obj/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.on_entered(src, target) + QDEL_IN(src, 10) + . = ..() + +/obj/projectile/energy/trap/cyborg/on_range() + do_sparks(1, TRUE, src) + qdel(src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/nuclear_particle.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/nuclear_particle.dm new file mode 100644 index 0000000000..93100df732 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/nuclear_particle.dm @@ -0,0 +1,60 @@ +//Nuclear particle projectile - a deadly side effect of fusion +/obj/projectile/energy/nuclear_particle + name = "nuclear particle" + icon_state = "nuclear_particle" + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + flag = "rad" + irradiate = 5000 + speed = 0.4 + hitsound = 'sound/weapons/emitter2.ogg' + impact_type = /obj/effect/projectile/impact/xray + var/static/list/particle_colors = list( + "red" = "#FF0000", + "blue" = "#00FF00", + "green" = "#0000FF", + "yellow" = "#FFFF00", + "cyan" = "#00FFFF", + "purple" = "#FF00FF" + ) + +/obj/projectile/energy/nuclear_particle/proc/random_color_time() + //Random color time! + var/our_color = pick(particle_colors) + add_atom_colour(particle_colors[our_color], FIXED_COLOUR_PRIORITY) + set_light(4, 3, particle_colors[our_color]) //Range of 4, brightness of 3 - Same range as a flashlight + +/obj/projectile/energy/nuclear_particle/proc/customize(custompower) + irradiate = max(3000 * 3 ** (log(10,custompower)-FUSION_RAD_MIDPOINT),10) + var/custom_color = HSVtoRGB(hsv(clamp(log(10,custompower)-12,0,5)*256,rand(191,255),rand(191,255),255)) + add_atom_colour(custom_color, FIXED_COLOUR_PRIORITY) + set_light(4, 3, custom_color) + switch (irradiate) + if(10 to 100) + name = "pathetically weak nuclear particle" + damage = 1 + if(100 to 200) + name = "very weak nuclear particle" + damage = 2 + if(200 to 500) + name = "fairly weak nuclear particle" + damage = 4 + if(500 to 1500) + name = "slightly weak nuclear particle" + damage = 7 + if(4000 to 8000) + name = "powerful nuclear particle" + damage = 15 + if(8000 to 30000) + name = "extremely strong nuclear particle" + damage = 20 + if(30000 to INFINITY) + name = "impossibly strong nuclear particle" + damage = 30 + +/atom/proc/fire_nuclear_particle(angle = rand(0,360), customize = FALSE, custompower = 1e12) //used by fusion to fire random nuclear particles. Fires one particle in a random direction. + var/obj/projectile/energy/nuclear_particle/P = new /obj/projectile/energy/nuclear_particle(src) + if(customize) + P.customize(custompower) + else + P.random_color_time() + P.fire(angle) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/stun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/stun.dm new file mode 100644 index 0000000000..b9654a0d6b --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/stun.dm @@ -0,0 +1,23 @@ +/obj/projectile/energy/electrode + name = "electrode" + icon_state = "spark" + color = "#FFFF00" + nodamage = FALSE + hitsound = 'sound/weapons/taserhit.ogg' + damage = 40 + damage_type = STAMINA + stutter = 5 + jitter = 20 SECONDS + range = 6 + tracer_type = /obj/effect/projectile/tracer/stun + muzzle_type = /obj/effect/projectile/muzzle/stun + impact_type = /obj/effect/projectile/impact/stun + +/obj/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) + +/obj/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/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/tesla.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/tesla.dm new file mode 100644 index 0000000000..8fc9e9eb04 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/energy/tesla.dm @@ -0,0 +1,50 @@ +/obj/projectile/energy/tesla + name = "tesla bolt" + icon_state = "tesla_projectile" + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + damage = 10 //A worse lasergun + var/zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE + var/zap_range = 3 + var/power = 10000 + var/constant_zaps = TRUE + +/obj/projectile/energy/tesla/on_hit(atom/target) + . = ..() + tesla_zap(src, zap_range, power, zap_flags) + qdel(src) + +/obj/projectile/energy/tesla/process(seconds_per_tick) + . = ..() + //Many coders have given their blood for this speed + if(constant_zaps) + tesla_zap(src, zap_range, power, zap_flags) + +/obj/projectile/energy/tesla/revolver + name = "energy orb" + +/obj/projectile/energy/tesla/cannon + name = "tesla orb" + power = 20000 + damage = 15 //Mech man big + +/obj/projectile/energy/tesla/explosive + constant_zaps = FALSE + power = 5000 + +/obj/projectile/energy/tesla/explosive/on_hit(atom/target) + explosion(get_turf(loc),0,0,0,flame_range = 3) + . = ..() + +/obj/projectile/energy/tesla_cannon + name = "tesla orb" + icon_state = "ice_1" + damage = 0 + speed = 1.5 + var/shock_damage = 5 + +/obj/projectile/energy/tesla_cannon/on_hit(atom/target) + . = ..() + if(isliving(target)) + var/mob/living/victim = target + victim.electrocute_act(shock_damage, src, siemens_coeff = 1, flags = SHOCK_NOSTUN|SHOCK_TESLA) + diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/_reusable.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/_reusable.dm new file mode 100644 index 0000000000..dffe94b066 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/_reusable.dm @@ -0,0 +1,20 @@ +/obj/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/projectile/bullet/reusable/on_hit(atom/target, blocked = FALSE) + . = ..() + handle_drop() + +/obj/projectile/bullet/reusable/on_range() + handle_drop() + ..() + +/obj/projectile/bullet/reusable/proc/handle_drop() + if(!dropped) + var/turf/T = get_turf(src) + new ammo_type(T) + dropped = TRUE diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/arrow.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/arrow.dm new file mode 100644 index 0000000000..de0e58d921 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/arrow.dm @@ -0,0 +1,32 @@ +/obj/projectile/bullet/reusable/arrow + name = "Arrow" + desc = "Woosh!" + damage = 50 + range = 14 + icon_state = "arrow" + ammo_type = /obj/item/ammo_casing/caseless/arrow + +/obj/projectile/bullet/reusable/arrow/wood + name = "Wooden arrow" + damage = 15 + ammo_type = /obj/item/ammo_casing/caseless/arrow/wood + +/obj/projectile/bullet/reusable/arrow/ash + name = "Ashen arrow" + desc = "Fire Hardened arrow." + damage = 25 + ammo_type = /obj/item/ammo_casing/caseless/arrow/ash + +/obj/projectile/bullet/reusable/arrow/bone //AP for ashwalkers + name = "Bone arrow" + desc = "An arrow made from bone and sinew." + damage = 25 + armour_penetration = 40 + ammo_type = /obj/item/ammo_casing/caseless/arrow/bone + +/obj/projectile/bullet/reusable/arrow/bronze + name = "Bronze arrow" + desc = "Bronze tipped arrow" + damage = 20 + armour_penetration = 10 + ammo_type = /obj/item/ammo_casing/caseless/arrow/bronze diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/foam_dart.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/foam_dart.dm new file mode 100644 index 0000000000..9ef2429dd7 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/reusable/foam_dart.dm @@ -0,0 +1,44 @@ +/obj/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 = TRUE + icon = 'icons/obj/guns/toy.dmi' + icon_state = "foamdart_proj" + base_icon_state = "foamdart_proj" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart + range = 10 + embedding = null //these should not be embedding, unlike normal bullets + var/modified = FALSE + var/obj/item/pen/pen = null + +/obj/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/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_appearance() + +/obj/projectile/bullet/reusable/foam_dart/Destroy() + pen = null + return ..() + +/obj/projectile/bullet/reusable/foam_dart/riot + name = "riot foam dart" + icon_state = "foamdart_riot_proj" + base_icon_state = "foamdart_riot_proj" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + nodamage = FALSE + stamina = 25 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/curse.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/curse.dm new file mode 100644 index 0000000000..5c928e293e --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/curse.dm @@ -0,0 +1,72 @@ +/obj/effect/ebeam/curse_arm + name = "curse arm" + layer = LARGE_MOB_LAYER + +/obj/projectile/curse_hand + name = "curse hand" + icon_state = "cursehand0" + hitsound = 'sound/effects/curse4.ogg' + layer = LARGE_MOB_LAYER + damage_type = BURN + damage = 10 + paralyze = 20 + speed = 2 + range = 16 + var/datum/beam/arm + var/handedness = 0 + +/obj/projectile/curse_hand/Initialize(mapload) + . = ..() + handedness = prob(50) + icon_state = "cursehand[handedness]" + +/obj/projectile/curse_hand/Destroy() + QDEL_NULL(arm) + return ..() + +/obj/projectile/curse_hand/update_icon_state() + icon_state = "[initial(icon_state)][handedness]" + return ..() + +/obj/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/projectile/curse_hand/prehit_pierce(atom/target) + return (target == original)? PROJECTILE_PIERCE_NONE : PROJECTILE_PIERCE_PHASE + +/obj/projectile/curse_hand/Destroy() + QDEL_NULL(arm) + return ..() + +/// The visual effect for the hand disappearing +/obj/projectile/curse_hand/proc/finale() + if(arm) + QDEL_NULL(arm) + if((movement_type & PHASING)) + playsound(src, 'sound/effects/curse3.ogg', 25, TRUE, -1) + var/turf/T = get_step(src, dir) + var/obj/effect/temp_visual/dir_setting/curse/hand/leftover = new(T, dir) + leftover.icon_state = icon_state + for(var/obj/effect/temp_visual/dir_setting/curse/grasp_portal/G in starting) + qdel(G) + if(!T) //T can be in nullspace when src is set to QDEL + return + 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, beam_type=/obj/effect/ebeam/curse_arm) + animate(D.visuals, alpha = 0, time = 32) + +/obj/projectile/curse_hand/on_range() + finale() + return ..() + +/obj/projectile/curse_hand/on_hit(atom/target, blocked, pierce_hit) + . = ..() + if (. == BULLET_ACT_HIT) + finale() + +/obj/projectile/curse_hand/phantom + name = "phantom hand" + damage = 15 + paralyze = 5 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/floral.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/floral.dm new file mode 100644 index 0000000000..5b99a20a01 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/floral.dm @@ -0,0 +1,23 @@ +/obj/projectile/energy/floramut + name = "alpha somatoray" + icon_state = "energy" + damage = 0 + damage_type = TOX + nodamage = TRUE + flag = "energy" + +/obj/projectile/energy/florayield + name = "beta somatoray" + icon_state = "energy2" + damage = 0 + damage_type = TOX + nodamage = TRUE + flag = "energy" + +/obj/projectile/energy/florarevolution + name = "gamma somatorary" + icon_state = "energy3" + damage = 0 + damage_type = TOX + nodamage = TRUE + flag = "energy" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/gravity.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/gravity.dm new file mode 100644 index 0000000000..2b56599f98 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/gravity.dm @@ -0,0 +1,102 @@ +/obj/projectile/gravityrepulse + name = "repulsion bolt" + icon = 'icons/effects/effects.dmi' + icon_state = "chronofield" + hitsound = 'sound/weapons/wave.ogg' + damage = 0 + damage_type = BRUTE + nodamage = TRUE + color = "#33CCFF" + var/turf/T + var/power = 4 + var/list/thrown_items = list() + +/obj/projectile/gravityrepulse/Initialize() + . = ..() + var/obj/item/ammo_casing/energy/gravity/repulse/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/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 + if(ismob(A)) //because (ismob(A) && A:mob_negates_gravity()) is a recipe for bugs. + var/mob/M = A + if(M.mob_negates_gravity()) + continue + var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src))) + A.safe_throw_at(throwtarget,power+1,1, force = MOVE_FORCE_EXTREMELY_STRONG) + thrown_items[A] = A + for(var/turf/F in range(T,power)) + new /obj/effect/temp_visual/gravpush(F) + +/obj/projectile/gravityattract + name = "attraction bolt" + icon = 'icons/effects/effects.dmi' + icon_state = "chronofield" + hitsound = 'sound/weapons/wave.ogg' + damage = 0 + damage_type = BRUTE + nodamage = TRUE + color = "#FF6600" + var/turf/T + var/power = 4 + var/list/thrown_items = list() + +/obj/projectile/gravityattract/Initialize() + . = ..() + var/obj/item/ammo_casing/energy/gravity/attract/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/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 + if(ismob(A)) + var/mob/M = A + if(M.mob_negates_gravity()) + continue + A.safe_throw_at(T, power+1, 1, force = MOVE_FORCE_EXTREMELY_STRONG) + thrown_items[A] = A + for(var/turf/F in range(T,power)) + new /obj/effect/temp_visual/gravpush(F) + +/obj/projectile/gravitychaos + name = "gravitational blast" + icon = 'icons/effects/effects.dmi' + icon_state = "chronofield" + hitsound = 'sound/weapons/wave.ogg' + damage = 0 + damage_type = BRUTE + nodamage = TRUE + color = "#101010" + var/turf/T + var/power = 4 + var/list/thrown_items = list() + +/obj/projectile/gravitychaos/Initialize() + . = ..() + var/obj/item/ammo_casing/energy/gravity/chaos/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/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 + if(ismob(A)) + var/mob/M = A + if(M.mob_negates_gravity()) + continue + A.safe_throw_at(get_edge_target_turf(A, pick(GLOB.cardinals)), power+1, 1, force = MOVE_FORCE_EXTREMELY_STRONG) + thrown_items[A] = A + for(var/turf/Z in range(T,power)) + new /obj/effect/temp_visual/gravpush(Z) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/hallucination.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/hallucination.dm new file mode 100644 index 0000000000..94f509bc34 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/hallucination.dm @@ -0,0 +1,230 @@ +/obj/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/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/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/projectile/hallucination/Destroy() + if(hal_target?.client) + hal_target.client.images -= fake_icon + QDEL_NULL(fake_icon) + return ..() + +/obj/projectile/hallucination/Bump(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/projectile/hallucination/proc/target_on_hit(mob/M) + if(M == hal_target) + to_chat(hal_target, span_userdanger("[M] is hit by \a [src] in the chest!")) + hal_apply_effect() + else if(M in view(hal_target)) + to_chat(hal_target, span_danger("[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/projectile/hallucination/proc/spawn_blood(mob/M, set_dir) + set waitfor = 0 + if(!hal_target.client) + return + + var/splatter_icon_state + if(ISDIAGONALDIR(set_dir)) + 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_REF(cleanup_blood)), 5) + +/obj/projectile/hallucination/proc/cleanup_blood(image/blood) + hal_target.client.images -= blood + qdel(blood) + +/obj/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/projectile/hallucination/proc/hal_apply_effect() + return + +/obj/projectile/hallucination/bullet + name = "bullet" + hal_icon_state = "bullet" + hal_fire_sound = "gunshot" + hal_hitsound = 'sound/weapons/pierce.ogg' + hal_hitsound_wall = "bullet_impact" + hal_impact_effect = "impact_bullet" + hal_impact_effect_wall = "impact_bullet" + hit_duration = 5 + hit_duration_wall = 5 + +/obj/projectile/hallucination/bullet/hal_apply_effect() + hal_target.adjustStaminaLoss(60) + +/obj/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/projectile/hallucination/laser/hal_apply_effect() + hal_target.adjustStaminaLoss(20) + hal_target.blur_eyes(2) + +/obj/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/projectile/hallucination/taser/hal_apply_effect() + hal_target.Paralyze(100) + hal_target.stuttering += 20 + if(hal_target.dna && hal_target.dna.check_mutation(HULK)) + hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk") + else if((hal_target.status_flags & CANKNOCKDOWN) && !HAS_TRAIT(hal_target, TRAIT_STUNIMMUNE)) + addtimer(CALLBACK(hal_target, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 20), 5) + +/obj/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/projectile/hallucination/disabler/hal_apply_effect() + hal_target.adjustStaminaLoss(25) + +/obj/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/projectile/hallucination/ebow/hal_apply_effect() + hal_target.Paralyze(100) + hal_target.stuttering += 5 + hal_target.adjustStaminaLoss(8) + +/obj/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/projectile/hallucination/change/hal_apply_effect() + new /datum/hallucination/self_delusion(hal_target, TRUE, wabbajack = FALSE) + +/obj/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/projectile/hallucination/death/hal_apply_effect() + new /datum/hallucination/death(hal_target, TRUE) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/ion.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/ion.dm new file mode 100644 index 0000000000..1e4ff220e8 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/ion.dm @@ -0,0 +1,17 @@ +/obj/projectile/ion + name = "ion bolt" + icon_state = "ion" + damage = 0 + damage_type = BURN + nodamage = TRUE + flag = "energy" + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + var/emp_radius = 1 + +/obj/projectile/ion/on_hit(atom/target, blocked = FALSE) + ..() + empulse(target, emp_radius, emp_radius) + return BULLET_ACT_HIT + +/obj/projectile/ion/weak + emp_radius = 0 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/meteor.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/meteor.dm new file mode 100644 index 0000000000..298b0ff3b1 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/meteor.dm @@ -0,0 +1,22 @@ +/obj/projectile/meteor + name = "meteor" + icon = 'icons/obj/meteor.dmi' + icon_state = "small1" + damage = 0 + damage_type = BRUTE + nodamage = TRUE + flag = "bullet" + +/obj/projectile/meteor/Bump(atom/A) + if(A == firer) + forceMove(A.loc) + return + if(isobj(A)) + SSexplosions.medobj += A + else if(isturf(A)) + SSexplosions.medturf += A + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, TRUE) + for(var/mob/M in urange(10, src)) + if(!M.stat) + shake_camera(M, 3, 1) + qdel(src) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/mindflayer.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/mindflayer.dm new file mode 100644 index 0000000000..d91735cb19 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/mindflayer.dm @@ -0,0 +1,9 @@ +/obj/projectile/beam/mindflayer + name = "flayer ray" + +/obj/projectile/beam/mindflayer/on_hit(atom/target, blocked = FALSE) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20) + M.hallucination += 30 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/neurotoxin.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/neurotoxin.dm new file mode 100644 index 0000000000..f66c2c2ddd --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/neurotoxin.dm @@ -0,0 +1,13 @@ +/obj/projectile/bullet/neurotoxin + name = "neurotoxin spit" + icon_state = "neurotoxin" + damage = 20 + damage_type = TOX + paralyze = 5 + flag = "bio" + +/obj/projectile/bullet/neurotoxin/on_hit(atom/target, blocked = FALSE) + if(isalien(target)) + paralyze = 0 + nodamage = TRUE + return ..() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/plasma.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/plasma.dm new file mode 100644 index 0000000000..68071bd2c5 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/plasma.dm @@ -0,0 +1,29 @@ +/obj/projectile/plasma + name = "plasma blast" + icon_state = "plasmacutter" + damage_type = BURN + damage = 15 + range = 4 + dismemberment = 10 + /// chance that the plasmablast ruins the ore + var/slag_chance = 33 + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + 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/projectile/plasma/adv + damage = 7 + range = 5 + slag_chance = 20 + +/obj/projectile/plasma/adv/mech + damage = 10 + range = 9 + +/obj/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/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/rocket.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/rocket.dm new file mode 100644 index 0000000000..d368ccdb94 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/rocket.dm @@ -0,0 +1,62 @@ +/obj/projectile/bullet/a84mm + name ="\improper HEDP rocket" + desc = "USE A WEEL GUN" + icon_state= "84mm-hedp" + damage = 80 + var/anti_armour_damage = 120 + armour_penetration = 100 + dismemberment = 30 + +/obj/projectile/bullet/a84mm/on_hit(atom/target, blocked = FALSE) + ..() + explosion(target, 0, 1, 2, 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 BULLET_ACT_HIT + +/obj/projectile/bullet/a84mm_he + name ="\improper HE missile" + desc = "Boom." + icon_state = "missile" + damage = 30 + ricochets_max = 0 //it's a MISSILE + +/obj/projectile/bullet/a84mm_he/on_hit(atom/target, blocked=0) + ..() + explosion(target, 0, 1, 2, 4) + return BULLET_ACT_HIT + +/obj/projectile/bullet/a84mm_br + name ="\improper HE missile" + desc = "Boom." + icon_state = "missile" + damage = 30 + ricochets_max = 0 //it's a MISSILE + var/sturdy = list( + /turf/closed, + /obj/mecha, + /obj/machinery/door/, + /obj/machinery/door/poddoor/shutters + ) + +/obj/item/broken_missile + name = "\improper broken missile" + desc = "A missile that did not detonate. The tail has snapped and it is in no way fit to be used again." + icon = 'icons/obj/projectiles.dmi' + icon_state = "missile_broken" + w_class = WEIGHT_CLASS_TINY + + +/obj/projectile/bullet/a84mm_br/on_hit(atom/target, blocked=0) + ..() + for(var/i in sturdy) + if(istype(target, i)) + explosion(target, 1, 1, 1, 2) + return BULLET_ACT_HIT + //if(istype(target, /turf/closed) || ismecha(target)) + new /obj/item/broken_missile(get_turf(src), 1) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/temperature.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/temperature.dm new file mode 100644 index 0000000000..65b7ad1d4d --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/temperature.dm @@ -0,0 +1,44 @@ +/obj/projectile/temp + name = "freeze beam" + icon_state = "ice_2" + damage = 0 + damage_type = BURN + nodamage = FALSE + flag = "energy" + var/temperature = -5 // reduce the body temperature by 5c + +/obj/projectile/temp/on_hit(atom/target, blocked = 0) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/hit_mob = target + var/thermal_protection = 1 // The inverse of the amount of protection + + if(temperature > 0) // The projectile is hot + thermal_protection -= hit_mob.get_heat_protection(hit_mob.bodytemperature + temperature) + else // The projectile was cold + thermal_protection -= hit_mob.get_cold_protection(hit_mob.bodytemperature + temperature) + + // The new body temperature is adjusted by 100-blocked % of the bullet's effect temperature + // Reduce the amount of the effect temperature change based on the amount of insulation the mob is wearing + hit_mob.adjust_bodytemperature(((100 - blocked) / 100) * (thermal_protection * temperature)) + + else if(isliving(target)) + var/mob/living/L = target + // the new body temperature is adjusted by 100-blocked % of the bullet's effect temperature + L.adjust_bodytemperature(((100 - blocked) / 100) * temperature) + +/obj/projectile/temp/hot + name = "heat beam" + temperature = 10 // Raise the body temp by 10c + +/obj/projectile/temp/cryo + name = "cryo beam" + range = 3 + temperature = -20 // Single slow shot reduces temp greatly + +/obj/projectile/temp/cryo/on_range() + var/turf/T = get_turf(src) + if(isopenturf(T)) + var/turf/open/O = T + O.freon_gas_act() + return ..() diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/wormhole.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/wormhole.dm new file mode 100644 index 0000000000..f354366833 --- /dev/null +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/projectile/special/wormhole.dm @@ -0,0 +1,31 @@ +/obj/projectile/beam/wormhole + name = "bluespace beam" + icon_state = "spark" + hitsound = "sparks" + damage = 0 + nodamage = TRUE + pass_flags = PASSGLASS | PASSTABLE | PASSGRILLE | PASSMOB + //Weakref to the thing that shot us + var/datum/weakref/gun + color = "#33CCFF" + tracer_type = /obj/effect/projectile/tracer/wormhole + impact_type = /obj/effect/projectile/impact/wormhole + muzzle_type = /obj/effect/projectile/muzzle/wormhole + hitscan = TRUE + +/obj/projectile/beam/wormhole/orange + name = "orange bluespace beam" + color = "#FF6600" + +/obj/projectile/beam/wormhole/Initialize(mapload, obj/item/ammo_casing/energy/wormhole/casing) + . = ..() + if(casing) + gun = casing.gun + + +/obj/projectile/beam/wormhole/on_hit(atom/target) + var/obj/item/gun/energy/wormhole_projector/projector = gun.resolve() + if(!projector) + qdel(src) + return + projector.create_portal(src, get_turf(src)) From 621e2d3a45e0c842c13d65247953ef6978afbf26 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:20:09 -0400 Subject: [PATCH 06/11] Gave some HP items to Hephaestus Industries --- .../manufacturer/hunter_pride/ballistics.dm | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm index 0c58a2bbb7..6b79cadfce 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/hunter_pride/ballistics.dm @@ -4,7 +4,7 @@ /obj/item/gun/ballistic/revolver/montagne name = "\improper HP Montagne" - desc = "An ornate break-open revolver issued to high-ranking members of the Saint-Roumain Militia. Chambered in .44." + desc = "An ornate break-open revolver made by master craftsmen. Chambered in .44." icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' @@ -29,7 +29,7 @@ EMPTY_GUN_HELPER(revolver/montagne) /obj/item/gun/ballistic/revolver/ashhand name = "HP Ashhand" - desc = "A massive, long-barreled revolver often used by the Saint-Roumain Militia as protection against big game. Can only be reloaded one cartridge at a time due to its reinforced frame. Uses .45-70 ammo." + desc = "A massive, long-barreled revolver often used as protection against big game. Can only be reloaded one cartridge at a time due to its reinforced frame. Uses .45-70 ammo." icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' @@ -261,8 +261,8 @@ NO_MAG_GUN_HELPER(automatic/pistol/candor/factory) . += "[initial(icon_state)]_factory" /obj/item/gun/ballistic/automatic/pistol/candor/phenex - name = "\improper HP Phenex" - desc = "A uniquely modified version of the Candor, famously created by Hunter's Pride. Named after the daemonic Phoenix of legend that the Ashen Huntsman had once slain, this hell-kissed weapon is more visually intimidating than its original counterpart, but mechanically acts the same. Chambered in .45." + name = "\improper HP Phoenix" + desc = "A uniquely modified version of the Candor, famously created by Hunter's Pride. Its casing was designed by hellish flames of the legendary phoenix from ancient sol mythology and uses more expensive materials, but the insides are identical. Chambered in .45." icon_state = "phenex" item_state = "hp_phenex" @@ -270,7 +270,7 @@ NO_MAG_GUN_HELPER(automatic/pistol/candor/factory) /obj/item/gun/ballistic/automatic/smg/firestorm //weapon designed by Apogee-dev name = "HP Firestorm" - desc = "An unconventional submachinegun, rarely issued to Saint-Roumain Militia mercenary hunters for outstanding situations where normal hunting weapons fall short. Chambered in .44 Roumain." + desc = "An unconventional submachinegun that some mercenaries absolutely swear by. Chambered in .44 Roumain." icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' @@ -322,8 +322,8 @@ NO_MAG_GUN_HELPER(automatic/pistol/candor/factory) /obj/item/gun/ballistic/shotgun/doublebarrel name = "double-barreled shotgun" - desc = "A classic break action shotgun, hand-made in a Hunter's Pride workshop. Both barrels can be fired in quick succession or even simultaneously. Guns like this have been popular with hunters, sporters, and criminals for millennia. Chambered in 12g." - sawn_desc = "A break action shotgun cut down to the size of a sidearm. While the recoil is even harsher, it offers a lot of power in a very small package. Chambered in 12g." + desc = "A classic break action shotgun, as seen in countless media throughout the last 700 years, risen to stardom as the staple stagecoach gun. Both barrels can be fired in quick succession or even simultaneously. Guns like this have been popular with hunters, sporters, and criminals for millennia. Chambered in 12g." + sawn_desc = "A break action shotgun cut down to the size of a sidearm. While the recoil is even harsher, it offers a lot of power in a very small package, making this modification very popular among criminals. Chambered in 12g." icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' @@ -353,7 +353,7 @@ NO_MAG_GUN_HELPER(automatic/pistol/candor/factory) can_be_sawn_off = TRUE bolt_type = BOLT_TYPE_NO_BOLT pb_knockback = 3 // it's a super shotgun! - manufacturer = MANUFACTURER_HUNTERSPRIDE + manufacturer = MANUFACTURER_HEPHAESTUS bolt_wording = "barrel" burst_delay = 0.05 SECONDS @@ -458,7 +458,7 @@ EMPTY_GUN_HELPER(shotgun/doublebarrel) /obj/item/gun/ballistic/shotgun/doublebarrel/presawn //init gives it the sawn_off name name = "double-barreled shotgun" - desc = "A break action shotgun cut down to the size of a sidearm. While the recoil is even harsher, it offers a lot of power in a very small package. Chambered in 12g." + desc = "A break action shotgun cut down to the size of a sidearm. While the recoil is even harsher, it offers a lot of power in a very small package, making this modification very popular among criminals. Chambered in 12g." sawn_off = TRUE weapon_weight = WEAPON_MEDIUM w_class = WEIGHT_CLASS_NORMAL @@ -481,13 +481,13 @@ EMPTY_GUN_HELPER(shotgun/doublebarrel/presawn) /obj/item/gun/ballistic/shotgun/doublebarrel/roumain name = "HP antique double-barreled shotgun" - desc = "A special-edition shotgun hand-made by Hunter's Pride with a high-quality walnut stock inlaid with brass scrollwork. Shotguns like this are very rare outside of the Saint-Roumain Militia's ranks. Otherwise functionally identical to a common double-barreled shotgun. Chambered in 12g." - sawn_desc = "A special-edition Hunter's Pride shotgun, cut down to the size of a sidearm by some barbarian. The brass inlay on the stock and engravings on the barrel have been obliterated in the process, destroying any value beyond its use as a crude sidearm." + desc = "A shotgun by Hunter's Pride with a high-quality walnut stock inlaid with brass scrollwork. Otherwise functionally identical to a common double-barreled shotgun. Chambered in 12g." + sawn_desc = "A Hunter's Pride shotgun, cut down to the size of a sidearm by some barbarian. The brass inlay on the stock and engravings on the barrel have been obliterated in the process, destroying any value beyond its use as a crude sidearm." base_icon_state = "dshotgun_srm" icon_state = "dshotgun_srm" item_state = "dshotgun_srm" unique_reskin = null - + manufacturer = MANUFACTURER_HUNTERSPRIDE EMPTY_GUN_HELPER(shotgun/doublebarrel/roumain) /obj/item/gun/ballistic/shotgun/doublebarrel/roumain/sawoff(forced = FALSE) @@ -498,8 +498,8 @@ EMPTY_GUN_HELPER(shotgun/doublebarrel/roumain) // BRIMSTONE // /obj/item/gun/ballistic/shotgun/brimstone - name = "HP Brimstone" - desc = "A simple and sturdy pump-action shotgun sporting a 5-round capacity, manufactured by Hunter's Pride. Found widely throughout the Frontier in the hands of hunters, pirates, police, and countless others. Chambered in 12g." + name = "Brimstone P5" + desc = "A simple and sturdy pump-action shotgun sporting a 5-round capacity, manufactured by Hephaestus Industries. Found widely throughout the Frontier in the hands of hunters, pirates, police, and countless others. Chambered in 12g." sawn_desc = "A stockless and shortened pump-action shotgun. The worsened recoil and accuracy make it a poor sidearm anywhere beyond punching distance." fire_sound = 'sound/weapons/gun/shotgun/brimstone.ogg' icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' @@ -517,7 +517,7 @@ EMPTY_GUN_HELPER(shotgun/doublebarrel/roumain) allowed_ammo_types = list( /obj/item/ammo_box/magazine/internal/shot/lethal, ) - manufacturer = MANUFACTURER_HUNTERSPRIDE + manufacturer = MANUFACTURER_HEPHAESTUS fire_delay = 0.05 SECONDS //slamfire rack_delay = 0.2 SECONDS @@ -557,14 +557,15 @@ EMPTY_GUN_HELPER(shotgun/brimstone) // HELLFIRE // /obj/item/gun/ballistic/shotgun/hellfire - name = "HP Hellfire" - desc = "A hefty pump-action riot shotgun with an eight-round tube, manufactured by Hunter's Pride. Especially popular among the Frontier's police forces. Chambered in 12g." + name = "Hellfire p7" + desc = "A hefty pump-action riot shotgun with an eight-round tube, manufactured by Hephaestus Industries. Especially popular among the Frontier's police forces. Chambered in 12g." icon = 'icons/obj/guns/manufacturer/hunterspride/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/hunterspride/lefthand.dmi' righthand_file = 'icons/obj/guns/manufacturer/hunterspride/righthand.dmi' mob_overlay_icon = 'icons/obj/guns/manufacturer/hunterspride/onmob.dmi' icon_state = "hellfire" item_state = "hellfire" + manufacturer = MANUFACTURER_HEPHAESTUS default_ammo_type = /obj/item/ammo_box/magazine/internal/shot/riot allowed_ammo_types = list( From e99a548d0932a7d981496f43ed38ce2ac330b3e5 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:27:19 -0400 Subject: [PATCH 07/11] more guns for mystery pirate manufacturer, not all so that it's clear they're not like the biggest manufacturer ever but just enough for it to be clear they've made some impact lol --- .../guns/manufacturer/frontier_import/ballistics.dm | 6 +++--- .../projectiles/guns/manufacturer/scarborough/ballistics.dm | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm index 212d4453e1..268f2b2ee6 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/frontier_import/ballistics.dm @@ -132,7 +132,7 @@ bolt_type = BOLT_TYPE_OPEN weapon_weight = WEAPON_LIGHT show_magazine_on_sprite = TRUE - manufacturer = MANUFACTURER_IMPORT + manufacturer = MANUFACTURER_LAKVAR spread = 20 spread_unwielded = 35 @@ -201,7 +201,7 @@ /obj/item/gun/ballistic/automatic/smg/pounder name = "Pounder" - desc = "An unusual submachine gun of Frontiersman make. A miniscule cartridge lacking both stopping power and armor penetration is compensated for with best-in-class ammunition capacity and cycle rate. Chambered in .22 LR." + desc = "An unusual submachine gun of unknown make. A miniscule cartridge lacking both stopping power and armor penetration is compensated for with best-in-class ammunition capacity and cycle rate. Chambered in .22 LR." icon = 'icons/obj/guns/manufacturer/frontier_import/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/frontier_import/lefthand.dmi' righthand_file = 'icons/obj/guns/manufacturer/frontier_import/righthand.dmi' @@ -235,7 +235,7 @@ gun_firemodes = list(FIREMODE_FULLAUTO) default_firemode = FIREMODE_FULLAUTO - manufacturer = MANUFACTURER_IMPORT + manufacturer = MANUFACTURER_LAKVAR wield_slowdown = SMG_SLOWDOWN safety_multiplier = 2 diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm index b74023662c..d57a102dfb 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/scarborough/ballistics.dm @@ -952,7 +952,7 @@ NO_MAG_GUN_HELPER(automatic/assault/hydra/dmr) // Bulldog shotgun // /obj/item/gun/ballistic/shotgun/automatic/bulldog - name = "SG-60r \"Bulldog\"" + name = "AS-14 \"Bulldog\"" desc = "A bullpup combat shotgun usually seen with a characteristic drum magazine. Wildly popular among Syndicate strike teams during the ICW, although it proved less useful against military-grade equipment. Still popular among former Syndicate factions, especially the Ramzi Clique pirates. Chambered in 12g." icon = 'icons/obj/guns/manufacturer/scarborough/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/scarborough/lefthand.dmi' From 5a689668fef3641db00c8a458310eaa4b2767f47 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:36:19 -0400 Subject: [PATCH 08/11] Etherbore tried to get a contract supplying NT security with weapons, but failed and armed their revolutionaries as revenge for supposedly stealing some of their designs. --- .../guns/manufacturer/etherbor/energy_gunsword.dm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm index 7875b01074..e56ea7132e 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/etherbor/energy_gunsword.dm @@ -1,6 +1,6 @@ /obj/item/gun/energy/kalix name = "\improper Etherbor BG-12" - desc = "Etherbor Industries's current civilian energy weapon model. The BG-12 energy beam gun is identical to the military model, minus the removal of the full auto mode. Otherwise, it's no different from older hunting beams from Kalixcis's history." + desc = "Etherbor Industries's current civilian energy weapon model. The BG-12 energy beam gun is identical to the military model, minus the removal of the full auto mode. Otherwise, it's no different from standard hunting beams history." icon_state = "kalixgun" item_state = "kalixgun" icon = 'icons/obj/guns/manufacturer/etherbor/48x32.dmi' @@ -140,7 +140,7 @@ /obj/item/gun/energy/kalix/pgf/nock name = "\improper Etherbor VG-A5" - desc = "Piggybacking off the success of the VG-F3, the Etherbor Industries VG-A5 Beam Volleygun was designed specifically for contract sale to the PGFMC. With the addition of a stronger capacitor and a forward grip, the VG-A5 has found itself popular among marine raiders for its ability to take control of tight spaces." + desc = "Piggybacking off the success of the VG-F3, the Etherbor Industries VG-A5 Beam Volleygun was designed specifically for contract sale to Nanotrasen in a deal that ultimately fell through. With the addition of a stronger capacitor and a forward grip, the VG-A5 has found itself popular among marine raiders for its ability to take control of tight spaces. Its popularity among the independent Epsilon Eridani movement has lead people to try connecting it to Etherbor's failed deal with Nanotrasen." icon_state = "pgfnock" item_state = "pgfnock" slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_SUITSTORE @@ -185,7 +185,7 @@ /obj/item/gun/energy/kalix/pgf name = "\improper Etherbor BG-16" - desc = "The BG-16 is the military-grade beam gun designed and manufactured by Etherbor Industries as the standard-issue close-range weapon of the PGF." + desc = "The BG-16 is the military-grade beam gun designed and manufactured by Etherbor Industries in a failed partnership with Nanotrasen." icon_state = "pgfgun" item_state = "pgfgun" @@ -274,7 +274,7 @@ /obj/item/gun/energy/kalix/pgf/medium name = "\improper Etherbor BGC-10" - desc = "Etherbor's answer to the PGFMC's request for a carbine style weapon; the BGC-10 offers greater accuracy and power than the BG-16, while being less cumbersome than the DMR mode equipped HBG series rifles." + desc = "Etherbor's proposed answer to Vigilitas' request for a carbine style weapon; the BGC-10 offers greater accuracy and power than the BG-16, while being less cumbersome than the DMR mode equipped HBG series rifles. Despite its rejection from Nanotrasen's corporate offices, it found massive success in a niche with the revolutionaries declaring independence for Epsilon Eridani, causing speculation of corporate sabotage from Etherbor." icon_state = "pgfmedium" item_state = "pgfmedium" slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE @@ -305,7 +305,7 @@ /obj/item/gun/energy/kalix/pgf/heavy name = "\improper Etherbor HBG-7" - desc = "The HBG-7 is the standard-issue rifle weapon of the PGF. It comes with a special DMR mode that has greater armor piercing for dealing with armored targets." + desc = "A fairly standard combat rifle popular in Epsilon Eridani. It comes with a special DMR mode that has greater armor piercing for dealing with armored targets." icon_state = "pgfheavy" item_state = "pgfheavy" slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_SUITSTORE From 2d5e622911ec6da6f0bcc04a8adf77e3de537e98 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:50:40 -0400 Subject: [PATCH 09/11] suns stuff goes to roseus --- .../guns/manufacturer/serene_sporting/ballistics.dm | 2 +- .../guns/manufacturer/solar_armories/ballistic.dm | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm index 0d8e3a009c..ccf140364f 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/serene_sporting/ballistics.dm @@ -234,7 +234,7 @@ EMPTY_GUN_HELPER(automatic/m12_sporter/mod) /obj/item/gun/ballistic/automatic/marksman/woodsman name = "Model 23 Woodsman" - desc = "A large semi-automatic hunting rifle manufactured by Serene Outdoors. Its powerful cartridge, excellent ergonomics and ease of use make it highly popular for hunting big game Chambered in 8x50mmR." + desc = "A large semi-automatic hunting rifle manufactured by Serene Outdoors. Its powerful cartridge, excellent ergonomics and ease of use make it highly popular for hunting big game. Chambered in 8x50mmR." icon = 'icons/obj/guns/manufacturer/serene_outdoors/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/serene_outdoors/lefthand.dmi' diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm index 0940eddf55..0b348929a9 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/manufacturer/solar_armories/ballistic.dm @@ -8,7 +8,7 @@ ///Pistols /obj/item/gun/ballistic/automatic/powered/gauss/modelh name = "Model H" - desc = "A standard-issue pistol exported from the Solarian Confederation. It fires slow flesh-rending ferromagnetic slugs at a high energy cost, however they are ineffective on any armor." + desc = "A standard-issue pistol exported from the Solar Federation. It fires slow flesh-rending ferromagnetic slugs at a high energy cost, however they are ineffective on any armor." icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' @@ -52,18 +52,19 @@ default_ammo_type = FALSE /obj/item/gun/ballistic/automatic/powered/gauss/modelh/suns - desc = "A standard-issue pistol exported from the Solarian Confederation. It fires slow flesh-rending ferromagnetic slugs at a high energy cost, however they are ineffective on any armor. It is painted in the colors of SUNS." + desc = "A standard-issue pistol exported from the Solar Federation. It fires slow flesh-rending ferromagnetic slugs at a high energy cost, however they are ineffective on any armor. This is a replica produced by Roseus Galactic." default_ammo_type = /obj/item/ammo_box/magazine/modelh allowed_ammo_types = list( /obj/item/ammo_box/magazine/modelh, ) icon_state = "model-h_suns" item_state = "model-h_suns" + manufacturer = MANUFACTURER_ROSEUS //not gauss pistol /obj/item/gun/ballistic/automatic/pistol/solgov name = "\improper Pistole C" - desc = "A favorite of the Terran Regency that is despised by the Solarian bureaucracy. Shifted out of military service centuries ago, though still popular among civilians. Chambered in 5.56mm caseless." + desc = "A favorite of the Solar Federation that is despised by the Solarian bureaucracy. Shifted out of military service centuries ago, though still popular among civilians. Chambered in 5.56mm caseless." icon_state = "pistole-c" icon = 'icons/obj/guns/manufacturer/solararmories/48x32.dmi' lefthand_file = 'icons/obj/guns/manufacturer/solararmories/lefthand.dmi' @@ -131,9 +132,10 @@ /obj/item/gun/ballistic/automatic/powered/gauss/claris/suns - desc = "An antiquated Solarian rifle. Chambered in ferromagnetic pellets, just as the founding Solarians intended. Evidently, SUNS' founders echo the sentiment, as it appears to be painted in their colors." + desc = "A replica of an antiquated Solarian rifle. Chambered in ferromagnetic pellets, just as the founding Solarians intended. Evidently, Roseus Galactic's executives agree, considering they've ripped off the design." icon_state = "claris_suns" item_state = "claris_suns" + manufacturer = MANUFACTURER_ROSEUS /obj/item/gun/ballistic/automatic/powered/gauss/gar name = "Solar 'GAR' Carbine" @@ -177,9 +179,10 @@ fire_select_icon_state_prefix = "lance_" /obj/item/gun/ballistic/automatic/powered/gauss/gar/suns - desc = "A Solarian carbine, unusually modern for its producers. It's just modern enough for SUNS, however, who have painted the weapon in their colors. Launches ferromagnetic lances at alarming speeds." + desc = "A Solarian carbine, replicated by Roseus Galactic. Launches ferromagnetic lances at alarming speeds." icon_state = "gar_suns" item_state = "gar_suns" + manufacturer = MANUFACTURER_ROSEUS ///Sniper /obj/item/gun/ballistic/rifle/solgov From 623e75fb77ac29efd87a737c52379fa33818c970 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 02:29:04 -0400 Subject: [PATCH 10/11] gives Medbeam Gun a serious description, assigns dart guns and medbeam to Interdyne products --- .../gun_lore/code/modules/projectiles/guns/misc/medbeam.dm | 2 +- .../code/modules/projectiles/guns/misc/syringe_gun.dm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm index bdaec3d737..09adc4c0ae 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/medbeam.dm @@ -1,6 +1,6 @@ /obj/item/gun/medbeam name = "Medical Beamgun" - desc = "Don't cross the streams!" + desc = "One of the most successful products by Interdyne Pharmaceuticals, the Medical Beamgun allows for a field medic to actively mend their allies while on the move. On the side it reads: WARNING! SAFETY HAZARD! DO NOT CROSS THE BEAMS!" icon = 'icons/obj/chronos.dmi' icon_state = "chronogun" item_state = "chronogun" diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm index 936cb2f601..621800e2d8 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/syringe_gun.dm @@ -68,13 +68,13 @@ /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." + desc = "An evolution of the classic syringe gun courtesy of Interdyne Pharmaceuticals, 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." + desc = "An Interdyne-designed 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 From a6a8201770ca7bc8015b812c6f4a694e41668899 Mon Sep 17 00:00:00 2001 From: lectronyx <78713019+lectronyx@users.noreply.github.com> Date: Sun, 19 Apr 2026 02:29:43 -0400 Subject: [PATCH 11/11] gives reagent gun to interdyne --- .../gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm index cf194a6af9..d6adab206b 100644 --- a/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm +++ b/modular_starfly/modules/gun_lore/code/modules/projectiles/guns/misc/chem_gun.dm @@ -2,7 +2,7 @@ //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." + desc = "An Interdyne syringe gun, modified to automatically synthesise chemical darts, and instead hold reagents." icon_state = "chemgun" item_state = "chemgun" w_class = WEIGHT_CLASS_NORMAL