diff --git a/_maps/shuttles/hunter/hunter_russian.dmm b/_maps/shuttles/hunter/hunter_russian.dmm index 9a57e48a871b1..14f712621e1d9 100644 --- a/_maps/shuttles/hunter/hunter_russian.dmm +++ b/_maps/shuttles/hunter/hunter_russian.dmm @@ -329,6 +329,7 @@ }, /obj/item/gun/ballistic/shotgun/doublebarrel/improvised/sawn{ name = "\improper TOZ-1"; + reinforced = 1; pixel_y = -7 }, /obj/effect/decal/cleanable/robot_debris{ @@ -624,8 +625,13 @@ }, /obj/item/gun/ballistic/shotgun/doublebarrel/improvised/sawn{ name = "\improper TOZ-1"; + reinforced = 1; pixel_y = -4 }, +/obj/item/hacksaw{ + pixel_x = -2; + pixel_y = 8 + }, /turf/open/floor/plasteel/dark, /area/shuttle/hunter) "Ec" = ( diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index d82eac619e6ca..dabb8ba8f6337 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -213,6 +213,7 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BOLT_TYPE_NO_BOLT 3 #define BOLT_TYPE_LOCKING 4 #define BOLT_TYPE_PUMP 5 //Requires 2 hands to pump, but standard +#define BOLT_TYPE_TWO_STEP 6 //Pump, but each interaction toggles bolt between locked and unlocked // Sawn off nerfs #define SAWN_OFF_ACC_PENALTY 25 #define SAWN_OFF_RECOIL 1 diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm index 88d6d11e24018..f64bb5f29b02c 100644 --- a/code/datums/components/crafting/recipes.dm +++ b/code/datums/components/crafting/recipes.dm @@ -371,6 +371,19 @@ subcategory = CAT_AMMO dangerous_craft = TRUE +/datum/crafting_recipe/improvisedglassslug + name = "Glasspack Shotgun Shell" + result = /obj/item/ammo_casing/shotgun/improvised/glasspack + reqs = list(/obj/item/grenade/chem_grenade = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stack/cable_coil = 1, + /datum/reagent/fuel = 10) + tools = list(TOOL_SCREWDRIVER) + time = 5 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + /datum/crafting_recipe/laserslug name = "Scatter Laser Shell" result = /obj/item/ammo_casing/shotgun/laserslug @@ -383,6 +396,99 @@ subcategory = CAT_AMMO dangerous_craft = TRUE +/datum/crafting_recipe/a762improv + name = "Improvised 7.62 Cartridge" + result = /obj/item/ammo_casing/a762/improv + reqs = list(/obj/item/grenade/chem_grenade = 1, + /obj/item/stack/sheet/iron = 1, + /obj/item/stack/cable_coil = 1, + /datum/reagent/fuel = 10) + tools = list(TOOL_SCREWDRIVER) + time = 5 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + +/datum/crafting_recipe/a762hotload + name = "Hot-Loaded 7.62 Cartridge" + result = /obj/item/ammo_casing/a762/improv/hotload + reqs = list(/obj/item/grenade/chem_grenade = 1, + /obj/item/stack/sheet/iron = 1, + /obj/item/stack/cable_coil = 1, + /datum/reagent/blackpowder = 10) + tools = list(TOOL_SCREWDRIVER) + time = 5 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + +/datum/crafting_recipe/improv9mm_pack + name = "Improvised 9mm Ammo Pack" + result = /obj/item/ammo_box/pouch/c9mm/improv + reqs = list(/obj/item/grenade/chem_grenade = 2, + /obj/item/stack/rods = 3, + /obj/item/stack/cable_coil = 3, + /datum/reagent/fuel = 20, + /obj/item/paper = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 15 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + +/datum/crafting_recipe/improv10mm_pack + name = "Improvised 10mm Ammo Pack" + result = /obj/item/ammo_box/pouch/c10mm/improv + reqs = list(/obj/item/grenade/chem_grenade = 2, + /obj/item/stack/sheet/iron = 2, + /obj/item/stack/cable_coil = 2, + /datum/reagent/fuel = 20, + /obj/item/paper = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 15 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + +/datum/crafting_recipe/improv38_pack + name = "Improvised .38 Ammo Pack" + result = /obj/item/ammo_box/pouch/c38/improv + reqs = list(/obj/item/grenade/chem_grenade = 2, + /obj/item/stack/rods = 2, + /obj/item/stack/cable_coil = 2, + /datum/reagent/fuel = 20, + /obj/item/paper = 1) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 15 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + +/datum/crafting_recipe/improv357 + name = "Improvised .357 Cartridge" + result = /obj/item/ammo_casing/a357/improv + reqs = list(/obj/item/grenade/chem_grenade = 1, + /obj/item/stack/sheet/iron = 1, + /obj/item/stack/cable_coil = 2, + /datum/reagent/blackpowder = 10) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 5 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + dangerous_craft = TRUE + +/datum/crafting_recipe/pipesmg_mag + name = "Pipe Repeater Magazine" + result = /obj/item/ammo_box/magazine/pipem9mm + reqs = list(/obj/item/grenade/chem_grenade = 1, + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stack/cable_coil = 3, + /obj/item/stack/package_wrap = 3) + tools = list(TOOL_WELDER, TOOL_WIRECUTTER) + time = 50 + category = CAT_WEAPONRY + subcategory = CAT_AMMO + /datum/crafting_recipe/arrow name = "Arrow" result = /obj/item/ammo_casing/caseless/arrow/wood @@ -428,8 +534,38 @@ reqs = list(/obj/item/weaponcrafting/receiver = 1, /obj/item/pipe = 1, /obj/item/weaponcrafting/stock = 1, + /obj/item/assembly/igniter = 1, /obj/item/stack/package_wrap = 5) - tools = list(TOOL_SCREWDRIVER) + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + dangerous_craft = TRUE + +/datum/crafting_recipe/piperifle + name = "Singleshot Pipe Rifle" + result = /obj/item/gun/ballistic/rifle/pipe + reqs = list(/obj/item/weaponcrafting/receiver = 1, + /obj/item/pipe = 1, + /obj/item/weaponcrafting/stock = 1, + /obj/item/assembly/igniter = 1, + /obj/item/stack/package_wrap = 5) + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) + time = 100 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON + dangerous_craft = TRUE + +/datum/crafting_recipe/pipesmg + name = "Mag-Fed Pipe Repeater" + result = /obj/item/gun/ballistic/automatic/pipe_smg + reqs = list(/obj/item/weaponcrafting/receiver = 1, + /obj/item/pipe = 2, + /obj/item/stack/rods = 2, + /obj/item/stack/sheet/wood = 2, + /obj/item/assembly/igniter = 1, + /obj/item/stack/package_wrap = 5) + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) time = 100 category = CAT_WEAPONRY subcategory = CAT_WEAPON diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm index c068177d18a84..5ef59060bd9a1 100644 --- a/code/game/objects/items/shrapnel.dm +++ b/code/game/objects/items/shrapnel.dm @@ -18,6 +18,14 @@ icon = 'icons/obj/ammo.dmi' icon_state = "s-casing" item_flags = NONE + //Percent chance for bullets that embed to have the DROPDEL flag set on Initialize. There's an insane ammount of clutter otherwise. + var/vanish_chance = 45 //Lower = More likely to remain after falling out/failing to embed + +/obj/item/shrapnel/bullet/Initialize(mapload) + . = ..() + + if(prob(vanish_chance)) //See if a bullet will remain after falling out/failing to embed + item_flags = DROPDEL /obj/item/shrapnel/bullet/c38 // .38 round name = "\improper .38 bullet" @@ -25,6 +33,12 @@ /obj/item/shrapnel/bullet/c38/dumdum // .38 DumDum round name = "\improper .38 DumDum bullet" embedding = list(embed_chance=70, fall_chance=7, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + vanish_chance = 60 + +/obj/item/shrapnel/bullet/shotgun/glass // Improvised glasspack shell + name = "glass shard" + embedding = list(embed_chance=60, fall_chance=2, jostle_chance=10, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.3, pain_mult=2, jostle_pain_mult=3, rip_time=8) + vanish_chance = 75 /obj/projectile/bullet/shrapnel name = "flying shrapnel shard" diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm index 2cb4295ef9c29..5b32766fc59ca 100644 --- a/code/modules/projectiles/ammunition/_ammunition.dm +++ b/code/modules/projectiles/ammunition/_ammunition.dm @@ -63,7 +63,8 @@ BB = new projectile_type(src, src) /obj/item/ammo_casing/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/ammo_box)) + //Regular boxes of ammo can sweep shells up from the floor, magazines that get insert into guns do not though + if(istype(I, /obj/item/ammo_box) && !istype(I, /obj/item/ammo_box/magazine)) var/obj/item/ammo_box/box = I if(isturf(loc)) var/boolets = 0 diff --git a/code/modules/projectiles/ammunition/ballistic/pistol.dm b/code/modules/projectiles/ammunition/ballistic/pistol.dm index d3f345b02f0ca..463f2e9258b65 100644 --- a/code/modules/projectiles/ammunition/ballistic/pistol.dm +++ b/code/modules/projectiles/ammunition/ballistic/pistol.dm @@ -6,6 +6,14 @@ caliber = "10mm" projectile_type = /obj/projectile/bullet/c10mm +/obj/item/ammo_casing/c10mm/improv + name = "improvised 10mm bullet casing" + desc = "A handmade 10mm bullet casing." + caliber = "10mm" + projectile_type = /obj/projectile/bullet/c10mm/improv + randomspread = TRUE + variance = 10 //Shit ammo is inaccurate. + /obj/item/ammo_casing/c10mm/ap name = "10mm armor-piercing bullet casing" desc = "A 10mm armor-piercing bullet casing." @@ -29,6 +37,12 @@ caliber = "9mm" projectile_type = /obj/projectile/bullet/c9mm +/obj/item/ammo_casing/c9mm/improv + name = "improvised 9mm bullet casing" + desc = "A handmade 9mm bullet casing." + randomspread = TRUE + variance = 10 //Shit ammo is inaccurate. + /obj/item/ammo_casing/c9mm/ap name = "9mm armor-piercing bullet casing" desc = "A 9mm armor-piercing bullet casing." diff --git a/code/modules/projectiles/ammunition/ballistic/revolver.dm b/code/modules/projectiles/ammunition/ballistic/revolver.dm index bdae3c8334edf..09fc551674b9c 100644 --- a/code/modules/projectiles/ammunition/ballistic/revolver.dm +++ b/code/modules/projectiles/ammunition/ballistic/revolver.dm @@ -12,6 +12,12 @@ caliber = "357" projectile_type = /obj/projectile/bullet/a357/match +/obj/item/ammo_casing/a357/improv + name = "improv .357 bullet casing" + desc = "An improvised .357 bullet casing." + projectile_type = /obj/projectile/bullet/a357/improv + variance = 15 + // 7.62x38mmR (Nagant Revolver) /obj/item/ammo_casing/n762 @@ -77,6 +83,13 @@ icon_state = "sS-casing" projectile_type = /obj/projectile/bullet/c38/emp +/obj/item/ammo_casing/c38/improv + name = "improv .38 bullet casing" + desc = "An improvised .38 bullet casing." + caliber = "38" + projectile_type = /obj/projectile/bullet/c38/improv + variance = 10 + /obj/item/ammo_casing/caseless/mime name = "invisible .38 bullet casing" icon_state = null diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm index c06a06ceab05c..729bb9b2678d4 100644 --- a/code/modules/projectiles/ammunition/ballistic/rifle.dm +++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm @@ -1,4 +1,4 @@ -// 7.62 (Nagant Rifle) +// 7.62 (Nagant Rifle / Pipe Rifle) /obj/item/ammo_casing/a762 name = "7.62 bullet casing" @@ -7,6 +7,17 @@ caliber = "a762" projectile_type = /obj/projectile/bullet/a762 +/obj/item/ammo_casing/a762/improv + name = "a762 improvised cartridge" + desc = "A handmade 7.62 cartidge, made from metal and some other scraps. It reeks of welding fuel." + icon_state = "762improv" + projectile_type = /obj/projectile/bullet/a762/improv + +/obj/item/ammo_casing/a762/improv/hotload + name = "7.62 hotload cartridge" + desc = "A higher quality handmade 7.62 cartridge. It smells like charcoal." + projectile_type = /obj/projectile/bullet/a762/improv/hotload + /obj/item/ammo_casing/a762/enchanted projectile_type = /obj/projectile/bullet/a762_enchanted diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm index c8c113ce4a47b..08303ee48a03e 100644 --- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm +++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm @@ -5,6 +5,7 @@ desc = "A 12 gauge lead slug." icon_state = "blshell" caliber = "shotgun" + var/high_power = TRUE custom_materials = list(/datum/material/iron=4000) projectile_type = /obj/projectile/bullet/shotgun_slug @@ -12,6 +13,7 @@ name = "beanbag slug" desc = "A weak beanbag slug for riot control." icon_state = "bshell" + high_power = FALSE custom_materials = list(/datum/material/iron=250) projectile_type = /obj/projectile/bullet/shotgun_beanbag @@ -19,12 +21,14 @@ name = "incendiary slug" desc = "An incendiary-coated shotgun slug." icon_state = "ishell" + high_power = FALSE projectile_type = /obj/projectile/bullet/incendiary/shotgun /obj/item/ammo_casing/shotgun/dragonsbreath name = "dragonsbreath shell" desc = "A shotgun shell which fires a spread of incendiary pellets." icon_state = "ishell2" + high_power = FALSE projectile_type = /obj/projectile/bullet/incendiary/shotgun/dragonsbreath pellets = 4 variance = 35 @@ -68,6 +72,7 @@ name = "rubber shot" desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance." icon_state = "bshell" + high_power = FALSE projectile_type = /obj/projectile/bullet/pellet/shotgun_rubbershot pellets = 6 variance = 20 @@ -77,6 +82,7 @@ name = "custom incapacitating shot" desc = "A shotgun casing filled with... something. used to incapacitate targets." icon_state = "bountyshell" + high_power = FALSE projectile_type = /obj/projectile/bullet/pellet/shotgun_incapacitate pellets = 12//double the pellets, but half the stun power of each, which makes this best for just dumping right in someone's face. variance = 20 @@ -86,11 +92,20 @@ name = "improvised shell" desc = "A shotgun shell improvised from small metal shards. It won't travel as far as a regular shotgun shell, but it will still pack a punch against unarmoured opponents at close ranges." icon_state = "improvshell" + high_power = FALSE projectile_type = /obj/projectile/bullet/pellet/shotgun_improvised custom_materials = list(/datum/material/iron=250) pellets = 9 variance = 15 +/obj/item/ammo_casing/shotgun/improvised/glasspack + name = "improvised glass-packed shell" + desc = "An extremely weak shotgun shell that's been filled with shards of glass instead of metal pellets." + projectile_type = /obj/projectile/bullet/pellet/shotgun_glass + custom_materials = list(/datum/material/iron=100, /datum/material/glass=100) + pellets = 5 + variance = 15 + /obj/item/ammo_casing/shotgun/ion name = "ion shell" desc = "An advanced shotgun shell which uses a subspace ansible crystal to produce an effect similar to a standard ion rifle. \ @@ -118,6 +133,7 @@ name = "shotgun dart" desc = "A dart for use in shotguns. Can be injected with up to 30 units of any chemical." icon_state = "cshell" + high_power = FALSE projectile_type = /obj/projectile/bullet/dart var/reagent_amount = 30 @@ -131,6 +147,7 @@ /obj/item/ammo_casing/shotgun/dart/noreact name = "cryostasis shotgun dart" desc = "A dart for use in shotguns, using similar technology as cryostatis beakers to keep internal reagents from reacting. Can be injected with up to 10 units of any chemical." + high_power = FALSE icon_state = "cnrshell" reagent_amount = 10 @@ -140,6 +157,7 @@ /obj/item/ammo_casing/shotgun/dart/bioterror desc = "A shotgun dart filled with deadly toxins." + high_power = FALSE /obj/item/ammo_casing/shotgun/dart/bioterror/Initialize(mapload) . = ..() @@ -153,5 +171,6 @@ name = "breaching slug" desc = "A 12 gauge anti-material slug. Great for breaching airlocks and windows with minimal shots." icon_state = "breacher" + high_power = FALSE projectile_type = /obj/projectile/bullet/shotgun_breaching custom_materials = list(/datum/material/iron=4000) diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm index 750d8af362dca..c42820f9a48c4 100644 --- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm +++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm @@ -10,7 +10,7 @@ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' custom_materials = list(/datum/material/iron = 30000) throwforce = 2 - w_class = WEIGHT_CLASS_TINY + w_class = WEIGHT_CLASS_SMALL throw_speed = 3 throw_range = 7 var/list/stored_ammo = list() @@ -18,7 +18,7 @@ var/max_ammo = 7 var/multiple_sprites = 0 var/caliber - var/multiload = TRUE + var/multiload = FALSE //Only specific magazines have multi-load enabled. This includes all internal mags/cylinders var/start_empty = FALSE var/list/bullet_cost var/list/base_cost// override this one as well if you override bullet_cost @@ -48,7 +48,7 @@ stored_ammo.Insert(1,b) return b -/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) +/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R) // 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 @@ -57,45 +57,43 @@ 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 /obj/item/ammo_box/proc/can_load(mob/user) return TRUE -/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = FALSE, replace_spent = 0) +/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = FALSE) var/num_loaded = 0 if(!can_load(user)) return if(istype(A, /obj/item/ammo_box)) var/obj/item/ammo_box/AM = A for(var/obj/item/ammo_casing/AC in AM.stored_ammo) - var/did_load = give_round(AC, replace_spent) + //If the box you're loading from is empty, break. + if(!AM.stored_ammo) + break + if(!multiload) + if(!do_after(user, 4, src, IGNORE_USER_LOC_CHANGE)) + break + var/did_load = give_round(AC) if(did_load) AM.stored_ammo -= AC num_loaded++ - if(!did_load || !multiload) + if(!silent && !multiload) + playsound(src, 'sound/weapons/bulletinsert.ogg', 60, TRUE) + if(!did_load) break if(istype(A, /obj/item/ammo_casing)) var/obj/item/ammo_casing/AC = A - if(give_round(AC, replace_spent)) + if(give_round(AC)) user.transferItemToLoc(AC, src, TRUE) num_loaded++ if(num_loaded) if(!silent) - to_chat(user, "You load [num_loaded] shell\s into \the [src]!") - playsound(src, 'sound/weapons/bulletinsert.ogg', 60, TRUE) + to_chat(user, "You loaded [num_loaded] shell\s into \the [src]!") + if(istype(A, /obj/item/ammo_casing)) + playsound(src, 'sound/weapons/bulletinsert.ogg', 60, TRUE) A.update_icon() update_icon() return num_loaded @@ -149,3 +147,21 @@ /obj/item/ammo_box/magazine/handle_atom_del(atom/A) stored_ammo -= A update_icon() + +//Behavior for ammo pouches (disposable paper ammo box) +/obj/item/ammo_box/pouch + icon_state = "bagobullets" + bullet_cost = null + base_cost = null + +/obj/item/ammo_box/pouch/attack_self(mob/user) + //If it's out of ammo, use it in hand to return the sheet of paper and 'destroy' the ammo box + if(!stored_ammo.len) + to_chat(user, "You flatten the empty [src]!") + var/obj/item/paper/unfolded = new /obj/item/paper + unfolded.forceMove(loc) + qdel(src) + user.put_in_hands(unfolded) + return + + ..() diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index 780d44f02491e..7625da1c8d760 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -67,18 +67,45 @@ item_flags = DROPDEL ammo_type = /obj/item/ammo_casing/caseless/mime/lethal +/obj/item/ammo_box/pouch/c38 + name = "ammo pouch (.38)" + ammo_type = /obj/item/ammo_casing/c38 + max_ammo = 4 + +/obj/item/ammo_box/pouch/c38/improv + name = "ammo pouch (improv .38)" + ammo_type = /obj/item/ammo_casing/c38/improv + /obj/item/ammo_box/c9mm name = "ammo box (9mm)" icon_state = "9mmbox" ammo_type = /obj/item/ammo_casing/c9mm max_ammo = 30 +/obj/item/ammo_box/pouch/c9mm + name = "ammo pouch (9mm)" + ammo_type = /obj/item/ammo_casing/c9mm + max_ammo = 6 + +/obj/item/ammo_box/pouch/c9mm/improv + name = "ammo pouch (improv 9mm)" + ammo_type = /obj/item/ammo_casing/c9mm/improv + /obj/item/ammo_box/c10mm name = "ammo box (10mm)" icon_state = "10mmbox" ammo_type = /obj/item/ammo_casing/c10mm max_ammo = 20 +/obj/item/ammo_box/pouch/c10mm + name = "ammo pouch (10mm)" + ammo_type = /obj/item/ammo_casing/c10mm + max_ammo = 4 + +/obj/item/ammo_box/pouch/c10mm/improv + name = "ammo pouch (improv 10mm)" + ammo_type = /obj/item/ammo_casing/c10mm/improv + /obj/item/ammo_box/c45 name = "ammo box (.45)" icon_state = "45box" diff --git a/code/modules/projectiles/boxes_magazines/external/shotgun.dm b/code/modules/projectiles/boxes_magazines/external/shotgun.dm index b1aebdee69d9d..09472837b0210 100644 --- a/code/modules/projectiles/boxes_magazines/external/shotgun.dm +++ b/code/modules/projectiles/boxes_magazines/external/shotgun.dm @@ -2,14 +2,11 @@ name = "shotgun magazine (12g buckshot slugs)" desc = "A drum magazine." icon_state = "m12gb" + multiple_sprites = 2 ammo_type = /obj/item/ammo_casing/shotgun/buckshot caliber = "shotgun" max_ammo = 8 -/obj/item/ammo_box/magazine/m12g/update_icon() - ..() - icon_state = "[initial(icon_state)]-[CEILING(ammo_count(FALSE)/8, 1)*8]" - /obj/item/ammo_box/magazine/m12g/stun name = "shotgun magazine (12g taser slugs)" icon_state = "m12gs" diff --git a/code/modules/projectiles/boxes_magazines/external/smg.dm b/code/modules/projectiles/boxes_magazines/external/smg.dm index 23250e7bb52db..4a8c8407e30ab 100644 --- a/code/modules/projectiles/boxes_magazines/external/smg.dm +++ b/code/modules/projectiles/boxes_magazines/external/smg.dm @@ -82,3 +82,50 @@ ammo_type = /obj/item/ammo_casing/c45 caliber = ".45" max_ammo = 50 + +/obj/item/ammo_box/magazine/pipem9mm + name = "pipe repeater magazine (9mm)" + icon_state = "pipemag1" + start_empty = TRUE + ammo_type = /obj/item/ammo_casing/c9mm/improv + caliber = "9mm" + max_ammo = 6 + var/obj/item/stock_parts/matter_bin/installed_bin + +/obj/item/ammo_box/magazine/pipem9mm/proc/update_capacity() + max_ammo = initial(max_ammo) + (installed_bin.rating * 3) + var/I = installed_bin.rating + if(I > 4) + I = 5 + icon_state = "pipemag[I]" + update_icon() + +/obj/item/ammo_box/magazine/pipem9mm/Initialize(mapload) + . = ..() + //Initialize with a basic/T1 matter bin installed + installed_bin = new /obj/item/stock_parts/matter_bin(src) + update_capacity() + +/obj/item/ammo_box/magazine/pipem9mm/examine(mob/user) + . = ..() + . += "This one has a tier [installed_bin.rating] matter bin, and can hold [max_ammo] shells." + if(installed_bin.rating < 4) + . += "You could increase the capacity with a better matter bin..." + +/obj/item/ammo_box/magazine/pipem9mm/attackby(obj/item/A, mob/user, params, silent = FALSE) + if(istype(A, /obj/item/stock_parts/matter_bin)) + var/obj/item/stock_parts/B = A + if(B.rating <= installed_bin.rating) + to_chat(user, "\The [B] isn't better than the matter bin that's already installed!") + return + to_chat(user, "You begin to rebuild \the [src] with the [B]") + if(do_after(user, 50, target = src)) + installed_bin.forceMove(drop_location()) + user.transferItemToLoc(B, src) + installed_bin = B + update_capacity() + to_chat(user, "\The [src] can now hold [max_ammo] bullets!") + if(B.rating > 4) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), user, "Where'd you find that matter bin anyway..?"), 50) + return + ..() diff --git a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm index cc14f753e4f64..011e9dab90ba6 100644 --- a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm +++ b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm @@ -38,12 +38,9 @@ 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 + if(!bullet) //Found an empty chamber in the cylinder stored_ammo[i] = R R.forceMove(src) - - if(bullet) - bullet.forceMove(drop_location()) return TRUE return FALSE diff --git a/code/modules/projectiles/boxes_magazines/internal/_internal.dm b/code/modules/projectiles/boxes_magazines/internal/_internal.dm index c14e66af82cf3..eda5b1ac3ed19 100644 --- a/code/modules/projectiles/boxes_magazines/internal/_internal.dm +++ b/code/modules/projectiles/boxes_magazines/internal/_internal.dm @@ -2,6 +2,7 @@ desc = "Oh god, this shouldn't be here" flags_1 = CONDUCT_1 item_flags = ABSTRACT + multiload = TRUE //internals magazines are accessible, so replace spent ammo if full when trying to put a live one in /obj/item/ammo_box/magazine/internal/give_round(obj/item/ammo_casing/R) diff --git a/code/modules/projectiles/boxes_magazines/internal/rifle.dm b/code/modules/projectiles/boxes_magazines/internal/rifle.dm index a7469284a5c70..939955a3eb720 100644 --- a/code/modules/projectiles/boxes_magazines/internal/rifle.dm +++ b/code/modules/projectiles/boxes_magazines/internal/rifle.dm @@ -13,3 +13,18 @@ /obj/item/ammo_box/magazine/internal/boltaction/enchanted/arcane_barrage ammo_type = /obj/item/ammo_casing/magic/arcane_barrage +/obj/item/ammo_box/magazine/internal/leveraction + name = "lever action rifle internal magazine" + desc = "Why the fuck can you see this, this is meant to be IN the gun!" + ammo_type = /obj/item/ammo_casing/c38 + caliber = "38" + max_ammo = 8 + multiload = FALSE + +/obj/item/ammo_box/magazine/internal/piperifle + name = "pipegun internal magazine" + desc = "Me when I accidentally see items that shouldn't exist." + ammo_type = /obj/item/ammo_casing/a762/improv + caliber = "a762" + max_ammo = 1 + start_empty = TRUE diff --git a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm index 8edc86b5cbb46..6ee0aad92b22b 100644 --- a/code/modules/projectiles/boxes_magazines/internal/shotgun.dm +++ b/code/modules/projectiles/boxes_magazines/internal/shotgun.dm @@ -3,7 +3,7 @@ ammo_type = /obj/item/ammo_casing/shotgun/beanbag caliber = "shotgun" max_ammo = 4 - multiload = 0 + multiload = FALSE /obj/item/ammo_box/magazine/internal/shot/tube name = "dual feed shotgun internal tube" @@ -31,6 +31,7 @@ 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" diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index e9c9aad01cf86..bda91dd594623 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -10,6 +10,7 @@ var/load_sound_volume = 40 var/load_sound_vary = TRUE var/rack_sound = "gun_slide_lock" + var/half_rack_sound = "gun_slide_lock" //Only needs to be used on BOLT_TYPE_PUMP guns, for Ctrl-Click functionality var/rack_sound_volume = 60 var/rack_sound_vary = TRUE var/lock_back_sound = "sound/weapons/pistollock.ogg" @@ -25,23 +26,32 @@ var/empty_alarm_volume = 70 var/empty_alarm_vary = TRUE + //Info below has to deal with guns with internal magazines or actual removable magazines. var/spawnwithmagazine = TRUE - var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info + var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info //Shame I had to add caliber back anyway :^) var/mag_display = FALSE //Whether the sprite has a visible magazine or not var/mag_display_ammo = FALSE //Whether the sprite has a visible ammo display or not var/empty_indicator = FALSE //Whether the sprite has an indicator for being empty or not. var/empty_alarm = FALSE //Whether the gun alarms when empty or not. var/special_mags = FALSE //Whether the gun supports multiple special mag types var/alarmed = FALSE - //Four bolt types: + //Additional info related to the actual chambering, and to allow loading bullets directly into battery / into var/chambered + //Copies the caliber of the magazine in the gun during Initialize() | Must be explicitly set on the gun if it spawns without a magazine ;) + var/caliber = null + var/direct_loading = FALSE //A gun with this allows the internal magazine to be loaded without removing, ontop of directly chambering rounds + //Six bolt types: //BOLT_TYPE_STANDARD: Gun has a bolt, it stays closed while not cycling. The gun must be racked to have a bullet chambered when a mag is inserted. - //Example: c20, shotguns, m90 + //Example: c20, m90 //BOLT_TYPE_OPEN: Gun has a bolt, it is open when ready to fire. The gun can never have a chambered bullet with no magazine, but the bolt stays ready when a mag is removed. //Example: Some SMGs, the L6 //BOLT_TYPE_NO_BOLT: Gun has no moving bolt mechanism, it cannot be racked. Also dumps the entire contents when emptied instead of a magazine. - //Example: Break action shotguns, revolvers + //Example: Rocket launchers, Break-action shotguns, revolvers, derringer //BOLT_TYPE_LOCKING: Gun has a bolt, it locks back when empty. It can be released to chamber a round if a magazine is in. //Example: Pistols with a slide lock, some SMGs + //BOLT_TYPE_PUMP: Functions identically to BOLT_TYPE_STANDARD, but requires two hands to rack the bolt. + //Examples: Pump-action shotguns + //BOLT_TYPE_TWO_STEP: Functions identically to BOLT_TYPE_PUMP (and thus, STANDARD), but each interaction with the bolt toggles between locked (open) & unlocked (closed). + //Examples: Mosin nagant, pipe guns var/bolt_type = BOLT_TYPE_STANDARD var/bolt_locked = FALSE //Used for locking bolt and open bolt guns. Set a bit differently for the two but prevents firing when true for both. var/bolt_wording = "bolt" //bolt, slide, etc. @@ -63,7 +73,10 @@ return if (!magazine) magazine = new mag_type(src) - chamber_round() + if (!caliber) + caliber = magazine.caliber + if (bolt_type == BOLT_TYPE_NO_BOLT) + chamber_round() update_icon() /obj/item/gun/ballistic/fire_sounds() @@ -95,8 +108,9 @@ else icon_state = "[initial(icon_state)][sawn_off ? "_sawn" : ""]" cut_overlays() - if (bolt_type == BOLT_TYPE_LOCKING) - add_overlay("[icon_state]_bolt[bolt_locked ? "_locked" : ""]") + switch(bolt_type) + if(BOLT_TYPE_LOCKING, BOLT_TYPE_PUMP, BOLT_TYPE_TWO_STEP) + add_overlay("[icon_state]_bolt[bolt_locked ? "_locked" : ""]") if (bolt_type == BOLT_TYPE_OPEN && bolt_locked) add_overlay("[icon_state]_bolt") if (suppressed) @@ -105,7 +119,10 @@ add_overlay("[icon_state]_empty") if (magazine) if (special_mags) - add_overlay("[icon_state]_mag_[initial(magazine.icon_state)]") + if(magazine.multiple_sprites) + add_overlay("[icon_state]_mag_[initial(magazine.icon_state)]") + else + add_overlay("[icon_state]_mag_[magazine.icon_state]") if (!magazine.ammo_count()) add_overlay("[icon_state]_mag_empty") else @@ -161,10 +178,33 @@ to_chat(user, "\The [src]'s [bolt_wording] is already cocked!") return bolt_locked = FALSE + if(BOLT_TYPE_TWO_STEP) + if(!is_wielded && !HAS_TRAIT(user, TRAIT_NICE_SHOT)) + to_chat(user, "You require your other hand to be free to rack the [bolt_wording] of \the [src]!") + return + //If it's locked (open), drop the bolt to close and unlock it + if(bolt_locked == TRUE) + drop_bolt(user) + return + //Otherwise, we open the bolt and eject the current casing + if(!is_wielded && prob(20)) + user.visible_message("[user] racks \the [src]'s [bolt_wording] with a single hand!") + to_chat(user, "You open the [bolt_wording] of \the [src].") + playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) + process_chamber(!chambered, FALSE, FALSE) + bolt_locked = TRUE + update_icon() + return if(BOLT_TYPE_PUMP) - if(!is_wielded) + if(!is_wielded && !HAS_TRAIT(user, TRAIT_NICE_SHOT)) to_chat(user, "You require your other hand to be free to rack the [bolt_wording] of \the [src]!") return + if(!is_wielded && prob(20)) + user.visible_message("[user] racks \the [src]'s [bolt_wording] with a single hand!") + if(bolt_locked == TRUE) //If it's locked (open), drop the bolt to close and unlock it + drop_bolt(user) + return + //Otherwise, we open the bolt and eject the current casing if(user) to_chat(user, "You rack the [bolt_wording] of \the [src].") process_chamber(!chambered, FALSE) @@ -193,7 +233,7 @@ to_chat(user, "You load a new [magazine_wording] into \the [src].") playsound(src, load_empty_sound, load_sound_volume, load_sound_vary) if (bolt_type == BOLT_TYPE_OPEN && !bolt_locked) - chamber_round(TRUE) + chamber_round() update_icon() return TRUE else @@ -202,6 +242,9 @@ /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) + //Put the chambered bullet back into the magazine, because it was never really taken out in the first place. + if(chambered) + magazine.attackby(chambered, user, null, TRUE, FALSE) chambered = null if (magazine.ammo_count()) playsound(src, load_sound, load_sound_volume, load_sound_vary) @@ -224,6 +267,9 @@ update_icon() /obj/item/gun/ballistic/can_shoot() + //If it's locked open (TWO_STEP and PUMP), it can't fire. + if((bolt_type == BOLT_TYPE_TWO_STEP || bolt_type == BOLT_TYPE_PUMP) && bolt_locked) + return FALSE return chambered /obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params) @@ -241,19 +287,44 @@ to_chat(user, "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.forceMove(drop_location()) - chambered = null - var/num_loaded = magazine.attackby(A, user, params, TRUE) - if (num_loaded) - to_chat(user, "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) - chamber_round() - A.update_icon() - update_icon() + //If it has a removable magazine, and does not support direct loading, return. + if(!internal_magazine && !direct_loading) + if(magazine) + to_chat(user, "Remove \the [src]'s magazine to load it!") + return + //Most guns with internal magazines (or the ability to load a removable one) are loaded through the bolt that gets locked open. PUMP are the exception here. + if(!bolt_locked && (bolt_type == BOLT_TYPE_LOCKING || bolt_type == BOLT_TYPE_OPEN || bolt_type == BOLT_TYPE_TWO_STEP)) + to_chat(user, "The [bolt_wording] is closed!") + return + //For chambering cartridges directly, only possible with a single cartridge in hand on guns with either internal magazines or direct_loading set to true + //The additional check for bolt_locked only applies to PUMP bolt types, as they're the only ones that can load on a closed bolt. + if(!chambered && istype(A, /obj/item/ammo_casing) && bolt_locked && bolt_type != BOLT_TYPE_OPEN) + var/obj/item/ammo_casing/AC = A + //If the gun isn't chambered in the same caliber as the cartridge, don't load it. + if(src.caliber != AC.caliber) + to_chat(user, "\The [src] isn't chambered in this caliber!") + return + chambered = AC + chambered.forceMove(src) + to_chat(user, "You chamber a [cartridge_wording] directly into \the [src].") + playsound(src, load_sound, load_sound_volume, load_sound_vary) + return + //If we don't have a magazine at all, and didn't load into battery, abort loading + if(!magazine) + to_chat(user, "There's nowhere to load a [cartridge_wording] into!") return + //Otherwise, try loading into the internal magazine next. + var/num_loaded = magazine.attackby(A, user, params, TRUE) + if (num_loaded) + to_chat(user, "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) + chamber_round() + A.update_icon() + update_icon() + else + to_chat(user, "\The [src] doesn't have room for another [cartridge_wording]!") + return if(istype(A, /obj/item/suppressor)) var/obj/item/suppressor/S = A if(!can_suppress) @@ -300,6 +371,16 @@ update_icon() return +/obj/item/gun/ballistic/CtrlClick(mob/user) + if(bolt_type == BOLT_TYPE_PUMP && is_wielded && loc == user && !bolt_locked) + to_chat(user, "You lock open the [bolt_wording] of \the [src].") + playsound(src, half_rack_sound, rack_sound_volume, rack_sound_vary) + process_chamber(!chambered, FALSE, FALSE) + bolt_locked = TRUE + update_icon() + return + ..() + /obj/item/gun/ballistic/proc/prefire_empty_checks() if (!chambered && !get_ammo()) if (bolt_type == BOLT_TYPE_OPEN && !bolt_locked) @@ -335,6 +416,7 @@ if(!magazine.ammo_count()) eject_magazine(user) return + if(bolt_type == BOLT_TYPE_NO_BOLT) chambered = null var/num_unloaded = 0 @@ -346,9 +428,8 @@ to_chat(user, "You unload [num_unloaded] [cartridge_wording]\s from [src].") playsound(user, eject_sound, eject_sound_volume, eject_sound_vary) update_icon() - else - to_chat(user, "[src] is empty!") - return + return + if(bolt_type == BOLT_TYPE_LOCKING && bolt_locked) drop_bolt(user) return @@ -358,10 +439,9 @@ rack(user) return - /obj/item/gun/ballistic/examine(mob/user) . = ..() - var/count_chambered = !(bolt_type == BOLT_TYPE_NO_BOLT || bolt_type == BOLT_TYPE_OPEN) + var/count_chambered = !(bolt_type == BOLT_TYPE_NO_BOLT) . += "It has [get_ammo(count_chambered)] round\s remaining." if (!chambered) . += "It does not seem to have a round chambered." @@ -369,6 +449,8 @@ . += "The [bolt_wording] is locked back and needs to be released before firing." if (suppressed) . += "It has a suppressor attached that can be removed with alt+click." + if (bolt_type == BOLT_TYPE_PUMP) + . += "You can ctrl+click to half-pump \the [src] to directly chamber a [cartridge_wording]." /obj/item/gun/ballistic/proc/get_ammo(countchambered = TRUE) var/boolets = 0 //mature var names for mature people @@ -454,7 +536,7 @@ 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 + recoil += SAWN_OFF_RECOIL //Add the additional 1 recoil, instead of setting recoil to one (looking at you improv shotgun) can_bayonet = FALSE //you got rid of the mounting lug with the rest of the barrel, dumbass can_suppress = FALSE //ditto for the threaded barrel sawn_off = TRUE @@ -465,11 +547,11 @@ // Sawing guns related proc /obj/item/gun/ballistic/proc/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 - + if(!chambered) + return + if(chambered.BB) + process_fire(user, user, FALSE) + . = TRUE /obj/item/suppressor name = "suppressor" diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index c02da26621c3d..4fa3926dc26b7 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -307,6 +307,26 @@ automatic = 0 fire_rate = 1.5 +// Improv Pipe Sub-Machinegun // + +/obj/item/gun/ballistic/automatic/pipe_smg + name = "pipe carbine" + desc = "A much more sophisticated improvised firearm, loaded from a removable magazine and automatically cycling cartridges. It's a shame it only takes 9mm ammo." + icon_state = "pipesmg" + item_state = "arg" + mag_type = /obj/item/ammo_box/magazine/pipem9mm + special_mags = TRUE + caliber = "9mm" + tac_reloads = FALSE + bolt_type = BOLT_TYPE_OPEN + no_pin_required = TRUE + spawnwithmagazine = FALSE + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BELT + actions_types = null + fire_rate = 2 + spread = 10 + // Laser rifle (rechargeable magazine) // /obj/item/gun/ballistic/automatic/laser diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index ea19e1bea8634..ae12c1b291cbc 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -20,6 +20,7 @@ /obj/item/gun/ballistic/automatic/pistol/no_mag spawnwithmagazine = FALSE + caliber = "10mm" /obj/item/gun/ballistic/automatic/pistol/locker desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors. This one is rusted from being inside of a locker for so long." @@ -46,7 +47,7 @@ tac_reloads = FALSE fire_sound_volume = 60 spread = 18 //Innate spread of 18 degrees, unwielded spread of 48; Stechkin is unwielded 40 - spread_unwielded = 30 //Manually set unwielded spread to 30; Equivelant weight to 0.5 (Stechkin has weight 1) + weapon_weight = WEAPON_LIGHT * 0.5 //Equivelant weight to 0.5 (Stechkin has weight 1) wild_spread = TRUE wild_factor = 0.70 //Minimum spread is 70% of spread value equip_time = 0 @@ -66,6 +67,7 @@ /obj/item/gun/ballistic/automatic/pistol/m1911/no_mag spawnwithmagazine = FALSE + caliber = ".45" /obj/item/gun/ballistic/automatic/pistol/deagle name = "\improper Desert Eagle" diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 5c7a0cdc89815..733c719417d35 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -148,6 +148,7 @@ to_chat(user, "You can't modify it!") return TRUE magazine.caliber = "357" + src.caliber = magazine.caliber fire_rate = 1 //worse than a nromal .357 fire_sound = 'sound/weapons/revolver357shot.ogg' desc = "The barrel and chamber assembly seems to have been modified." @@ -163,7 +164,8 @@ to_chat(user, "You can't modify it!") return magazine.caliber = "38" - fire_rate = null + src.caliber = magazine.caliber + fire_rate = initial(fire_rate) fire_sound = 'sound/weapons/revolver38shot.ogg' desc = initial(desc) to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.") diff --git a/code/modules/projectiles/guns/ballistic/rifle.dm b/code/modules/projectiles/guns/ballistic/rifle.dm index f03259356974e..bf6e4af030f1e 100644 --- a/code/modules/projectiles/guns/ballistic/rifle.dm +++ b/code/modules/projectiles/guns/ballistic/rifle.dm @@ -5,7 +5,7 @@ icon_state = "moistnugget" mag_type = /obj/item/ammo_box/magazine/internal/boltaction bolt_wording = "bolt" - bolt_type = BOLT_TYPE_STANDARD + bolt_type = BOLT_TYPE_TWO_STEP semi_auto = FALSE internal_magazine = TRUE fire_sound = "sound/weapons/rifleshot.ogg" @@ -19,40 +19,12 @@ ..() add_overlay("[icon_state]_bolt[bolt_locked ? "_locked" : ""]") -/obj/item/gun/ballistic/rifle/rack(mob/user = null) - if(!is_wielded) - to_chat(user, "You require your other hand to be free to rack the [bolt_wording] of \the [src]!") - return - if(bolt_locked == FALSE) - to_chat(user, "You open the bolt of \the [src].") - playsound(src, rack_sound, rack_sound_volume, rack_sound_vary) - process_chamber(FALSE, FALSE, FALSE) - bolt_locked = TRUE - update_icon() - return - drop_bolt(user) - -/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 ((istype(A, /obj/item/ammo_casing/a762) || istype(A, /obj/item/ammo_box/a762)) && !bolt_locked) - to_chat(user, "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/shoot_live_shot(mob/living/user, pointblank, atom/pbtarget, message) if(sawn_off == TRUE) if(!is_wielded) recoil = 5 else - recoil = SAWN_OFF_RECOIL + recoil = initial(recoil) + SAWN_OFF_RECOIL . = ..() /////////////////////// @@ -73,6 +45,7 @@ can_bayonet = TRUE knife_x_offset = 27 knife_y_offset = 13 + recoil = 0.5 w_class = WEIGHT_CLASS_BULKY weapon_weight = WEAPON_HEAVY @@ -133,3 +106,86 @@ user.put_in_hands(gun) else user.dropItemToGround(src, TRUE) + +/////////////////////// +// .38 CAL RIFLE // +/////////////////////// + +/obj/item/gun/ballistic/rifle/leveraction + name = "lever action rifle" + desc = "Straight from the Wild West, this belongs in a museum but has found its way into your hands." + icon_state = "leverrifle" + lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' + righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' + item_state = "leveraction" + inhand_x_dimension = 64 + inhand_y_dimension = 64 + slot_flags = ITEM_SLOT_BACK + rack_sound = "sound/weapons/leveractionrack.ogg" + half_rack_sound = "sound/weapons/leveractionrack_open.ogg" + bolt_drop_sound = "sound/weapons/leveractionrack_close.ogg" + fire_sound = "sound/weapons/leveractionshot.ogg" + mag_type = /obj/item/ammo_box/magazine/internal/leveraction + w_class = WEIGHT_CLASS_BULKY + no_pin_required = TRUE //Nothing stops frontier justice + bolt_wording = "lever" + cartridge_wording = "cartridge" + recoil = 0.5 + bolt_type = BOLT_TYPE_PUMP + fire_sound_volume = 70 + +/////////////////////// +// 7.62 PIPE RIFLE // +/////////////////////// + +/obj/item/gun/ballistic/rifle/pipe + name = "pipe rifle" + desc = "It's amazing what you can do with some scrap wood and spare pipes." + can_sawoff = TRUE + sawn_name = "pipe pistol" + sawn_desc = "Why have more gun, when less gun can do!" + icon_state = "piperifle" + lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' + righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' + item_state = "shotgun_improv" + sawn_item_state = "shotgun_improv_shorty" + inhand_x_dimension = 64 + inhand_y_dimension = 64 + bolt_type = BOLT_TYPE_NO_BOLT + cartridge_wording = "cartridge" + slot_flags = null + mag_type = /obj/item/ammo_box/magazine/internal/piperifle + no_pin_required = TRUE + w_class = WEIGHT_CLASS_BULKY + force = 8 + recoil = 0.8 + var/slung = FALSE + +/obj/item/gun/ballistic/rifle/pipe/examine(mob/user) + . = ..() + if (slung) + . += "It has a shoulder sling fashioned from spare cable attached." + else + . += "You could improvise a shoulder sling from some cabling..." + +/obj/item/gun/ballistic/rifle/pipe/attackby(obj/item/A, mob/user, params) + ..() + if(istype(A, /obj/item/stack/cable_coil) && !sawn_off) + if(slung) + to_chat(user, "There is already a sling on [src]!") + return + var/obj/item/stack/cable_coil/C = A + if(C.use(10)) + slot_flags = ITEM_SLOT_BACK + to_chat(user, "You tie the lengths of cable to the [src], making a sling.") + slung = TRUE + update_icon() + else + to_chat(user, "You need at least ten lengths of cable if you want to make a sling!") + +/obj/item/gun/ballistic/rifle/pipe/sawoff(mob/user) + . = ..() + if(. && slung) //sawing off the gun removes the sling + new /obj/item/stack/cable_coil(get_turf(src), 10) + slung = FALSE + update_icon() diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index fefca0bf033f1..7f254b98d3ce7 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -11,6 +11,8 @@ vary_fire_sound = FALSE fire_sound_volume = 90 rack_sound = "sound/weapons/shotgunpump.ogg" + half_rack_sound = "sound/weapons/shotgunpump_open.ogg" + bolt_drop_sound = "sound/weapons/shotgunpump_close.ogg" load_sound = "sound/weapons/shotguninsert.ogg" w_class = WEIGHT_CLASS_BULKY force = 10 @@ -29,12 +31,6 @@ recoil = 1 pb_knockback = 2 -/obj/item/gun/ballistic/shotgun/blow_up(mob/user) - . = 0 - if(chambered?.BB) - process_fire(user, user, FALSE) - . = 1 - /obj/item/gun/ballistic/shotgun/lethal mag_type = /obj/item/ammo_box/magazine/internal/shot/lethal @@ -234,15 +230,51 @@ name = "improvised shotgun" desc = "Essentially a tube that aims shotgun shells." icon_state = "ishotgun" - item_state = "shotgun" + item_state = "shotgun_improv" + sawn_item_state = "shotgun_improv_shorty" w_class = WEIGHT_CLASS_BULKY force = 10 slot_flags = null mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised sawn_desc = "I'm just here for the gasoline." + no_pin_required = TRUE unique_reskin_icon = null - recoil = 2 + recoil = 1.5 var/slung = FALSE + var/reinforced = FALSE + var/barrel_stress = 0 + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) + if(chambered.BB && !reinforced) + var/obj/item/ammo_casing/shotgun/S = chambered + if(prob(10 + barrel_stress) && S.high_power) //Base 10% chance of misfiring. Goes up with each shot of high_power ammo + backfire(user) + return 0 + + else if (S.high_power) + barrel_stress += 5 + if (barrel_stress == 10) + to_chat(user, "[src]'s barrel is left warped from the force of the shot!") + else if (barrel_stress == 25) + to_chat(user, "[src]'s barrel cracks from the repeated strain!") + + else if (prob(5) && barrel_stress >= 30) // If the barrel is damaged enough to be cracked, flat 5% chance to detonate on low-power ammo as well. + backfire(user) + return 0 + ..() + +/obj/item/gun/ballistic/shotgun/doublebarrel/improvised/proc/backfire(mob/living/user) + playsound(user, fire_sound, fire_sound_volume, vary_fire_sound) + to_chat(user, "[src] blows up in your face!") + + user.take_bodypart_damage(0,15) //The explosion already does enough damage. + explosion(src, 0, 0, 1, 1) + + barrel_stress += 10 //Big damage to barrel, two explosions/misfires will destroy the gun entirely + qdel(chambered.BB) + chambered.BB = null //Spend the bullet when you misfire and it explodes. What's blowing up otherwise? + + user.dropItemToGround(src) /obj/item/gun/ballistic/shotgun/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) ..() @@ -274,13 +306,24 @@ /obj/item/gun/ballistic/shotgun/doublebarrel/improvised/examine(mob/user) . = ..() if (slung) - . += "It has a shoulder sling fashioned from spare wiring attached." + . += "It has a shoulder sling fashioned from spare cable attached." + else + . += "You could improvise a shoulder sling from some cabling..." + + if (reinforced) + . += "The barrel has been reinforced for use with high-power ammunition." + else if (barrel_stress < 10) + . += "The barrel is in pristine condition." + else if (barrel_stress < 20) + . += "The barrel seems to be warped mildly..." + else + . += "The barrel is warped and cracked!" /obj/item/gun/ballistic/shotgun/doublebarrel/improvised/sawn name = "sawn-off improvised shotgun" desc = "A single-shot shotgun. Better not miss." icon_state = "ishotgun" - item_state = "gun" + item_state = "shotgun_improv_shorty" w_class = WEIGHT_CLASS_LARGE sawn_off = TRUE slot_flags = ITEM_SLOT_BELT @@ -291,7 +334,6 @@ desc = "Range isn't an issue when you can bring your victim to you." icon_state = "hookshotgun" item_state = "shotgun" - load_sound = "sound/weapons/shotguninsert.ogg" mag_type = /obj/item/ammo_box/magazine/internal/shot/bounty w_class = WEIGHT_CLASS_BULKY weapon_weight = WEAPON_MEDIUM diff --git a/code/modules/projectiles/guns/ballistic/sniper.dm b/code/modules/projectiles/guns/ballistic/sniper.dm index 239f2e859b5d8..beb4695f4d75e 100644 --- a/code/modules/projectiles/guns/ballistic/sniper.dm +++ b/code/modules/projectiles/guns/ballistic/sniper.dm @@ -8,14 +8,15 @@ fire_sound = "sound/weapons/sniper_shot.ogg" fire_sound_volume = 90 load_sound = "sound/weapons/sniper_mag_insert.ogg" - rack_sound = "sound/weapons/sniper_rack.ogg" - recoil = 2 + rack_sound = "sound/weapons/sniper_rack_open.ogg" + bolt_drop_sound = 'sound/weapons/sniper_rack_close.ogg' + recoil = 1.5 weapon_weight = WEAPON_HEAVY + bolt_type = BOLT_TYPE_TWO_STEP mag_type = /obj/item/ammo_box/magazine/sniper_rounds - automatic = 0 + direct_loading = TRUE semi_auto = FALSE - fire_rate = 1.5 - burst_size = 1 + rack_delay = 4 w_class = WEIGHT_CLASS_LARGE zoomable = TRUE zoom_amt = 10 //Long range, enough to see in front of you, but no tiles behind you. diff --git a/code/modules/projectiles/projectile/bullets/pistol.dm b/code/modules/projectiles/projectile/bullets/pistol.dm index 0e4b3f4576ca2..df05999163478 100644 --- a/code/modules/projectiles/projectile/bullets/pistol.dm +++ b/code/modules/projectiles/projectile/bullets/pistol.dm @@ -20,6 +20,10 @@ name = "10mm bullet" damage = 30 +/obj/projectile/bullet/c10mm/improv + name = "10mm bullet" + damage = 27 + /obj/projectile/bullet/c10mm_ap name = "10mm armor-piercing bullet" damage = 27 diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm index 9ff042723fa56..f2701dee9d531 100644 --- a/code/modules/projectiles/projectile/bullets/revolver.dm +++ b/code/modules/projectiles/projectile/bullets/revolver.dm @@ -98,6 +98,12 @@ . = ..() empulse(target, 0, 2) +/obj/projectile/bullet/c38/improv + damage = 25 + ricochets_max = 1 + ricochet_chance = 80 + ricochet_auto_aim_range = 0 + /obj/projectile/bullet/c38/mime name = "invisible .38 bullet" icon_state = null @@ -132,6 +138,10 @@ name = ".357 bullet" damage = 60 +/obj/projectile/bullet/a357/improv + damage = 50 + armour_penetration = -10 + // admin only really, for ocelot memes /obj/projectile/bullet/a357/match name = ".357 match bullet" diff --git a/code/modules/projectiles/projectile/bullets/rifle.dm b/code/modules/projectiles/projectile/bullets/rifle.dm index 92f0ef7f65ac7..ad418f033fab3 100644 --- a/code/modules/projectiles/projectile/bullets/rifle.dm +++ b/code/modules/projectiles/projectile/bullets/rifle.dm @@ -4,7 +4,7 @@ name = "5.56mm bullet" damage = 35 -// 7.62 (Nagant Rifle) +// 7.62 (Nagant Rifle / Pipe Rifle) /obj/projectile/bullet/a762 name = "7.62 bullet" @@ -15,3 +15,19 @@ name = "enchanted 7.62 bullet" damage = 20 stamina = 80 + +/obj/projectile/bullet/a762/improv + //Possible damage range between 27 and 30 + damage = 30 + armour_penetration = 0 + +/obj/projectile/bullet/a762/improv/Initialize(mapload) + . = ..() + //Actual damage of projectile is reduced by 0 to 3 damage + damage -= (round(rand(0, 3), 1)) + +/obj/projectile/bullet/a762/improv/hotload + //Possible damage between 32 and 35 + damage = 35 + speed = 0.7 + armour_penetration = 15 diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index 6874ff76a3c37..2e23953e80cef 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -93,12 +93,25 @@ /obj/projectile/bullet/pellet/shotgun_improvised/Initialize(mapload) . = ..() - range = rand(1, 8) + range = rand(3, 8) /obj/projectile/bullet/pellet/shotgun_improvised/on_range() do_sparks(1, TRUE, src) ..() +/obj/projectile/bullet/pellet/shotgun_glass + tile_dropoff = 0.5 + damage = 6 + range = 8 + ricochets_max = 0 + shrapnel_type = /obj/item/shrapnel/bullet/shotgun/glass + +/obj/projectile/bullet/pellet/shotgun_glass/Initialize(mapload) + . = ..() + + if(prob(20)) //Each 'pellet' has a 20 percent chance to not shrapnel/attempt embedding + shrapnel_type = null + // Mech Scattershot /obj/projectile/bullet/scattershot diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index 1717192706724..303dda09e28f8 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -561,6 +561,14 @@ category = list("initial", "Medical", "Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/hacksaw + name = "Hacksaw" + id = "hacksaw" + build_type = AUTOLATHE + materials = list(/datum/material/iron=12000) + build_path = /obj/item/hacksaw + category = list("hacked", "Medical") + /datum/design/beanbag_slug name = "Beanbag Slug" id = "beanbag_slug" diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index 13a70301ddbe8..f9377f41bf5dd 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -221,6 +221,29 @@ attack_verb = list("attacked", "slashed", "sawed", "cut") sharpness = IS_SHARP +/obj/item/hacksaw + name = "hacksaw" + desc = "A hacksaw with a metal-cutting blade attached. You could use it as a regular saw in a pinch." + icon = 'icons/obj/tools.dmi' + icon_state = "hacksaw" + hitsound = 'sound/weapons/bladeslice.ogg' + throwhitsound = 'sound/weapons/pierce.ogg' + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + item_state = "wrench" + flags_1 = CONDUCT_1 + force = 7 + w_class = WEIGHT_CLASS_NORMAL + custom_materials = list(/datum/material/iron=12000) + attack_verb = list("attacked", "slashed", "sawed", "cut") + sharpness = IS_SHARP + tool_behaviour = TOOL_SAW + toolspeed = 2 + +/obj/item/hacksaw/Initialize(mapload) + . = ..() //Much worse than a circular saw for butchering. Slower, less efficient, better chance for bonus items however. + AddComponent(/datum/component/butchering, 40 * toolspeed, 80, 10, 'sound/items/hacksaw.ogg') + /obj/item/surgical_drapes name = "surgical drapes" desc = "Nanotrasen brand surgical drapes provide optimal safety and infection control." diff --git a/icons/mob/inhands/weapons/64x_guns_left.dmi b/icons/mob/inhands/weapons/64x_guns_left.dmi index da7234d29dbf8..0a8c735b5a10e 100644 Binary files a/icons/mob/inhands/weapons/64x_guns_left.dmi and b/icons/mob/inhands/weapons/64x_guns_left.dmi differ diff --git a/icons/mob/inhands/weapons/64x_guns_right.dmi b/icons/mob/inhands/weapons/64x_guns_right.dmi index c3fb95125c03b..55f7ac9df0530 100644 Binary files a/icons/mob/inhands/weapons/64x_guns_right.dmi and b/icons/mob/inhands/weapons/64x_guns_right.dmi differ diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index 6b4d11314ca6e..432f0c748ec0d 100644 Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi index b69972dd60e2d..24a8768603550 100644 Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ diff --git a/icons/obj/tools.dmi b/icons/obj/tools.dmi index bc19ae4b74d9e..0611c2226bbc2 100644 Binary files a/icons/obj/tools.dmi and b/icons/obj/tools.dmi differ diff --git a/sound/items/hacksaw.ogg b/sound/items/hacksaw.ogg new file mode 100644 index 0000000000000..43e2dab691d1b Binary files /dev/null and b/sound/items/hacksaw.ogg differ diff --git a/sound/weapons/leveractionrack_close.ogg b/sound/weapons/leveractionrack_close.ogg new file mode 100644 index 0000000000000..4909d0b2ed34b Binary files /dev/null and b/sound/weapons/leveractionrack_close.ogg differ diff --git a/sound/weapons/leveractionrack_open.ogg b/sound/weapons/leveractionrack_open.ogg new file mode 100644 index 0000000000000..99dd2cb15396b Binary files /dev/null and b/sound/weapons/leveractionrack_open.ogg differ diff --git a/sound/weapons/shotgunpump_close.ogg b/sound/weapons/shotgunpump_close.ogg new file mode 100644 index 0000000000000..1b35fba728771 Binary files /dev/null and b/sound/weapons/shotgunpump_close.ogg differ diff --git a/sound/weapons/shotgunpump_open.ogg b/sound/weapons/shotgunpump_open.ogg new file mode 100644 index 0000000000000..69e817e1376b4 Binary files /dev/null and b/sound/weapons/shotgunpump_open.ogg differ diff --git a/sound/weapons/sniper_rack_close.ogg b/sound/weapons/sniper_rack_close.ogg new file mode 100644 index 0000000000000..550cb5da7eaee Binary files /dev/null and b/sound/weapons/sniper_rack_close.ogg differ diff --git a/sound/weapons/sniper_rack_open.ogg b/sound/weapons/sniper_rack_open.ogg new file mode 100644 index 0000000000000..e58aa989eab18 Binary files /dev/null and b/sound/weapons/sniper_rack_open.ogg differ