Skip to content

Commit

Permalink
Brings automatic weaponry to the marine and surv arsenal (#4003)
Browse files Browse the repository at this point in the history
# About the pull request
Gives full auto to the following guns:

m41a (and mk1)
m39
HPR (when bipodded)
m46c in IFF off
type71
mar40
nsg
every civvie SMG except the nailgun
laser uzi

All newly automatic guns fire 20% slower in automatic mode, to balance
for the fact that it is much easier to re-aim than burst.

This creates a dynamic between the three firemodes:

Semi:
- Best accuracy
- Lowest DPS

Burst:
- Okay accuracy
- Highest DPS

Auto:
- Okay (it depends) accuracy, but you are able to adjust for a moving
target unlike burst.
- Slightly lower DPS 


# Explain why it's good for the game
Guns not having autofire feels... pretty rough. This PR will bring CM to
a more modern time, while reducing the number of players diagnosed with
carpal tunnel yearly.

# Testing Photographs and Procedure
I went through and tested pretty much every base gun, but I cannot
guarantee that they all handle well. Lemme know if something's off.


# Changelog
:cl:
balance: The M41A, M41Amk1, m39, m46c (iff off), mar-30/40/60, type 71,
laser uzi, and every SMG now has automatic fire. To compensate for the
ability to re-aim (unlike burst), the guns fire 20% slower while
automatic. This does not apply to already-existing automatic weaponry.
balance: The HPR can now fire in full auto when bipodded.
fix: Adding/removing attachments now keeps the current firemode, if
possible.
fix: The dualtube and spec sniper now can switch tubes/toggle laser
again
fix: Fixed a long-standing exploit with bipods and scopes.
fix: Fixed guns jamming when shooting UI elements.
fix: Fixed a bunch of guns unintentionally having burst.
balance: You can transfer ammo between HPR ammo boxes by hitting one
with another.
/:cl:

---------

Co-authored-by: John Doe <[email protected]>
Co-authored-by: Benedict <[email protected]>
  • Loading branch information
3 people committed Aug 23, 2023
1 parent aca42a6 commit 295ef51
Show file tree
Hide file tree
Showing 20 changed files with 130 additions and 50 deletions.
4 changes: 3 additions & 1 deletion code/__DEFINES/conflict.dm
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
#define GUN_ANTIQUE (1<<13)
/// Whether the gun has been fired by its current user (reset upon `dropped()`)
#define GUN_RECOIL_BUILDUP (1<<14)
/// support weapon, bipod will grant IFF
/// support weapon, bipod will grant autofire
#define GUN_SUPPORT_PLATFORM (1<<15)
/// No gun description, only base desc
#define GUN_NO_DESCRIPTION (1<<16)
Expand Down Expand Up @@ -99,6 +99,8 @@
#define AMMUNITION_HANDFUL_BOX (1<<2)
#define AMMUNITION_HIDE_AMMO (1<<3)
#define AMMUNITION_CANNOT_REMOVE_BULLETS (1<<4)
/// If this magazine can transfer to other magazines of the same type by slapping one with the other
#define AMMUNITION_SLAP_TRANSFER (1<<5)
//Slowdown from various armors.

/// How much shoes slow you down by default. Negative values speed you up
Expand Down
6 changes: 6 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_item.dm
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,9 @@
#define COMSIG_GUN_AUTOFIREDELAY_MODIFIED "gun_autofiredelay_modified"
#define COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED "gun_burst_shots_to_fire_modified"
#define COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED "gun_burst_shot_delay_modified"

/// from /obj/item/weapon/gun/proc/recalculate_attachment_bonuses() : ()
#define COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES "gun_recalculate_attachment_bonuses"

/// from /obj/item/weapon/gun/proc/load_into_chamber() : ()
#define COMSIG_GUN_INTERRUPT_FIRE "gun_interrupt_fire"
1 change: 1 addition & 0 deletions code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ DEFINE_BITFIELD(flags_magazine, list(
"AMMUNITION_HANDFUL_BOX" = AMMUNITION_HANDFUL_BOX,
"AMMUNITION_HIDE_AMMO" = AMMUNITION_HIDE_AMMO,
"AMMUNITION_CANNOT_REMOVE_BULLETS" = AMMUNITION_CANNOT_REMOVE_BULLETS,
"AMMUNITION_SLAP_TRANSFER" = AMMUNITION_SLAP_TRANSFER,
))

DEFINE_BITFIELD(flags_atom, list(
Expand Down
9 changes: 7 additions & 2 deletions code/datums/components/autofire/autofire.dm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
var/have_to_reset_at_burst_end = FALSE
///If we are in a burst
var/bursting = FALSE
/// The multiplier for how much slower the parent should fire in automatic mode. 1 is normal, 1.2 is 20% slower, 2 is 100% slower, etc.
var/automatic_delay_mult = 1
///Callback to set bursting mode on the parent
var/datum/callback/callback_bursting
///Callback to ask the parent to reset its firing vars
Expand All @@ -26,7 +28,7 @@
///Callback to set parent's fa_firing
var/datum/callback/callback_set_firing

/datum/component/automatedfire/autofire/Initialize(auto_fire_shot_delay = 0.3 SECONDS, burstfire_shot_delay, burst_shots_to_fire = 3, fire_mode = GUN_FIREMODE_SEMIAUTO, datum/callback/callback_bursting, datum/callback/callback_reset_fire, datum/callback/callback_fire, datum/callback/callback_display_ammo, datum/callback/callback_set_firing)
/datum/component/automatedfire/autofire/Initialize(auto_fire_shot_delay = 0.3 SECONDS, burstfire_shot_delay, burst_shots_to_fire = 3, fire_mode = GUN_FIREMODE_SEMIAUTO, automatic_delay_mult = 1, datum/callback/callback_bursting, datum/callback/callback_reset_fire, datum/callback/callback_fire, datum/callback/callback_display_ammo, datum/callback/callback_set_firing)
. = ..()

RegisterSignal(parent, COMSIG_GUN_FIRE_MODE_TOGGLE, PROC_REF(modify_fire_mode))
Expand All @@ -35,11 +37,13 @@
RegisterSignal(parent, COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED, PROC_REF(modify_burstfire_shot_delay))
RegisterSignal(parent, COMSIG_GUN_FIRE, PROC_REF(initiate_shot))
RegisterSignal(parent, COMSIG_GUN_STOP_FIRE, PROC_REF(stop_firing))
RegisterSignal(parent, COMSIG_GUN_INTERRUPT_FIRE, PROC_REF(hard_reset))

src.auto_fire_shot_delay = auto_fire_shot_delay
src.burstfire_shot_delay = burstfire_shot_delay
src.burst_shots_to_fire = burst_shots_to_fire
src.fire_mode = fire_mode
src.automatic_delay_mult = automatic_delay_mult
src.callback_bursting = callback_bursting
src.callback_reset_fire = callback_reset_fire
src.callback_fire = callback_fire
Expand Down Expand Up @@ -96,6 +100,7 @@

///Hard reset the autofire, happens when the shooter fall/is thrown, at the end of a burst or when it runs out of ammunition
/datum/component/automatedfire/autofire/proc/hard_reset()
SIGNAL_HANDLER
callback_reset_fire.Invoke() //resets the gun
shots_fired = 0
have_to_reset_at_burst_end = FALSE
Expand Down Expand Up @@ -131,7 +136,7 @@
next_fire = world.time + burstfire_shot_delay
if(GUN_FIREMODE_AUTOMATIC)
callback_set_firing.Invoke(TRUE)
next_fire = world.time + auto_fire_shot_delay
next_fire = world.time + (auto_fire_shot_delay * automatic_delay_mult)
if(GUN_FIREMODE_SEMIAUTO)
return
schedule_shot()
10 changes: 0 additions & 10 deletions code/datums/supply_packs/ammo.dm
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,6 @@
containername = "\improper M39 AP magazines crate"
group = "Ammo"

/datum/supply_packs/ammo_smg_mag_box_ext
name = "Magazine box (M39, 10x extended mags)"
contains = list(
/obj/item/ammo_box/magazine/m39/ext,
)
cost = 30
containertype = /obj/structure/closet/crate/ammo
containername = "\improper M39 extended magazines crate"
group = "Ammo"

//------------------------For M4RA----------------

/datum/supply_packs/ammo_m4ra_mag_box
Expand Down
7 changes: 3 additions & 4 deletions code/datums/supply_packs/black_market.dm
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,9 @@ Non-USCM items, from CLF, UPP, colonies, etc. Mostly combat-related.
new /obj/item/ammo_magazine/rifle/mar40/extended(src)
new /obj/item/ammo_magazine/rifle/mar40(src)
else
new /obj/item/weapon/gun/rifle/m41aMK1/tactical(src)
new /obj/item/ammo_magazine/rifle/m41aMK1/ap(src)
new /obj/item/ammo_magazine/rifle/m41aMK1(src)
new /obj/item/ammo_magazine/rifle/m41aMK1(src)
new /obj/item/weapon/gun/rifle/mar40/lmg(src)
new /obj/item/ammo_magazine/rifle/mar40/lmg(src)
new /obj/item/ammo_magazine/rifle/mar40/lmg(src)

/* Misc. Individual Guns */

Expand Down
4 changes: 2 additions & 2 deletions code/modules/clothing/shoes/miscellaneous.dm
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@
max_heat_protection_temperature = SHOE_MAX_HEAT_PROT

/obj/item/clothing/shoes/souto
name = "\improper Souto Man's boots. Harder than the kick of Souto Red."
desc = "Souto Man boots"
name = "Souto Man boots"
desc = "\improper Souto Man's boots. Harder than the kick of Souto Red"
icon_state = "souto_man"
item_state = "souto_man"
flags_inventory = CANTSTRIP|NOSLIPPING
Expand Down
11 changes: 7 additions & 4 deletions code/modules/projectiles/ammunition.dm
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,17 @@ They're all essentially identical when it comes to getting the job done.
/obj/item/ammo_magazine/attackby(obj/item/I, mob/living/user, bypass_hold_check = 0)
if(istype(I, /obj/item/ammo_magazine))
var/obj/item/ammo_magazine/MG = I
if(MG.flags_magazine & AMMUNITION_HANDFUL) //got a handful of bullets
if((MG.flags_magazine & AMMUNITION_HANDFUL) || (MG.flags_magazine & AMMUNITION_SLAP_TRANSFER)) //got a handful of bullets
if(flags_magazine & AMMUNITION_REFILLABLE) //and a refillable magazine
var/obj/item/ammo_magazine/handful/transfer_from = I
if(src == user.get_inactive_hand() || bypass_hold_check) //It has to be held.
if(default_ammo == transfer_from.default_ammo)
transfer_ammo(transfer_from,user,transfer_from.current_rounds) // This takes care of the rest.
else to_chat(user, "Those aren't the same rounds. Better not mix them up.")
else to_chat(user, "Try holding [src] before you attempt to restock it.")
if(transfer_ammo(transfer_from,user,transfer_from.current_rounds)) // This takes care of the rest.
to_chat(user, SPAN_NOTICE("You transfer rounds to [src] from [transfer_from]."))
else
to_chat(user, SPAN_NOTICE("Those aren't the same rounds. Better not mix them up."))
else
to_chat(user, SPAN_NOTICE("Try holding [src] before you attempt to restock it."))

//Generic proc to transfer ammo between ammo mags. Can work for anything, mags, handfuls, etc.
/obj/item/ammo_magazine/proc/transfer_ammo(obj/item/ammo_magazine/source, mob/user, transfer_amount = 1)
Expand Down
23 changes: 16 additions & 7 deletions code/modules/projectiles/gun.dm
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
///How many full-auto shots to get to max scatter?
var/fa_scatter_peak = 4
///How bad does the scatter get on full auto?
var/fa_max_scatter = 8.5
var/fa_max_scatter = 6.5
///Click parameters to use when firing full-auto
var/fa_params = null

Expand Down Expand Up @@ -228,6 +228,8 @@
VAR_PROTECTED/start_semiauto = TRUE
/// If this gun should spawn with automatic fire. Protected due to it never needing to be edited.
VAR_PROTECTED/start_automatic = FALSE
/// The multiplier for how much slower this should fire in automatic mode. 1 is normal, 1.2 is 20% slower, 2 is 100% slower, etc. Protected due to it never needing to be edited.
VAR_PROTECTED/autofire_slow_mult = 1


/**
Expand Down Expand Up @@ -274,7 +276,7 @@
AddElement(/datum/element/drop_retrieval/gun, auto_retrieval_slot)
update_icon() //for things like magazine overlays
gun_firemode = gun_firemode_list[1] || GUN_FIREMODE_SEMIAUTO
AddComponent(/datum/component/automatedfire/autofire, fire_delay, burst_delay, burst_amount, gun_firemode, CALLBACK(src, PROC_REF(set_bursting)), CALLBACK(src, PROC_REF(reset_fire)), CALLBACK(src, PROC_REF(fire_wrapper)), CALLBACK(src, PROC_REF(display_ammo)), CALLBACK(src, PROC_REF(set_auto_firing))) //This should go after handle_starting_attachment() and setup_firemodes() to get the proper values set.
AddComponent(/datum/component/automatedfire/autofire, fire_delay, burst_delay, burst_amount, gun_firemode, autofire_slow_mult, CALLBACK(src, PROC_REF(set_bursting)), CALLBACK(src, PROC_REF(reset_fire)), CALLBACK(src, PROC_REF(fire_wrapper)), CALLBACK(src, PROC_REF(display_ammo)), CALLBACK(src, PROC_REF(set_auto_firing))) //This should go after handle_starting_attachment() and setup_firemodes() to get the proper values set.

/obj/item/weapon/gun/proc/set_gun_attachment_offsets()
attachable_offset = null
Expand Down Expand Up @@ -425,6 +427,10 @@
else if(M.r_hand == src)
M.update_inv_r_hand()

setup_firemodes()

SEND_SIGNAL(src, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES)

/obj/item/weapon/gun/proc/handle_random_attachments()
var/attachmentchoice

Expand Down Expand Up @@ -939,6 +945,10 @@ and you're good to go.

//Let's check on the active attachable. It loads ammo on the go, so it never chambers anything
if(active_attachable)
if(shots_fired >= 1) // This is what you'll want to remove if you want automatic underbarrel guns in the future
SEND_SIGNAL(src, COMSIG_GUN_INTERRUPT_FIRE)
return

if(active_attachable.current_rounds > 0) //If it's still got ammo and stuff.
active_attachable.current_rounds--
var/obj/item/projectile/bullet = create_bullet(active_attachable.ammo, initial(name))
Expand Down Expand Up @@ -1073,10 +1083,10 @@ and you're good to go.
This is where the grenade launcher and flame thrower function as attachments.
This is also a general check to see if the attachment can fire in the first place.
*/
var/check_for_attachment_fire = 0
var/check_for_attachment_fire = FALSE

if(active_attachable?.flags_attach_features & ATTACH_WEAPON) //Attachment activated and is a weapon.
check_for_attachment_fire = 1
check_for_attachment_fire = TRUE
if(!(active_attachable.flags_attach_features & ATTACH_PROJECTILE)) //If it's unique projectile, this is where we fire it.
if((active_attachable.current_rounds <= 0) && !(active_attachable.flags_attach_features & ATTACH_IGNORE_EMPTY))
click_empty(user) //If it's empty, let them know.
Expand Down Expand Up @@ -1202,8 +1212,7 @@ and you're good to go.

shots_fired++

else // This happens in very rare circumstances when you're moving a lot while burst firing, so I'm going to toss it up to guns jamming.
clear_jam(projectile_to_fire,user)
else
return TRUE

//>>POST PROCESSING AND CLEANUP BEGIN HERE.<<
Expand Down Expand Up @@ -1892,7 +1901,7 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
if(!target)
target = src.target
if(!user)
user = src.gun_user
user = gun_user
return Fire(target, user, params, reflex, dual_wield)

/// Setter proc for fa_firing
Expand Down
30 changes: 28 additions & 2 deletions code/modules/projectiles/gun_attachables.dm
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,28 @@ Defined in conflicts.dm of the #defines folder.
delay_scoped_nerf = FIRE_DELAY_TIER_11 //to compensate initial debuff. We want "high_fire_delay"
damage_falloff_scoped_buff = -0.4 //has to be negative

/obj/item/attachable/scope/Attach(obj/item/weapon/gun/gun)
. = ..()
RegisterSignal(gun, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES, PROC_REF(handle_attachment_recalc))

/obj/item/attachable/scope/Detach(mob/user, obj/item/weapon/gun/detaching_gub)
. = ..()
UnregisterSignal(detaching_gub, COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES)


/// Due to the bipod's interesting way of handling stat modifications, this is necessary to prevent exploits.
/obj/item/attachable/scope/proc/handle_attachment_recalc(obj/item/weapon/gun/source)
SIGNAL_HANDLER

if(!source.zoom)
return

if(using_scope)
source.accuracy_mult += accuracy_scoped_buff
source.modify_fire_delay(delay_scoped_nerf)
source.damage_falloff_mult += damage_falloff_scoped_buff


/obj/item/attachable/scope/proc/apply_scoped_buff(obj/item/weapon/gun/G, mob/living/carbon/user)
if(G.zoom)
G.accuracy_mult += accuracy_scoped_buff
Expand Down Expand Up @@ -1976,6 +1998,8 @@ Defined in conflicts.dm of the #defines folder.
G.damage_mult = 1
icon_state += "-on"

SEND_SIGNAL(G, COMSIG_GUN_INTERRUPT_FIRE)

for(var/X in G.actions)
var/datum/action/A = X
A.update_button_icon()
Expand Down Expand Up @@ -2646,13 +2670,14 @@ Defined in conflicts.dm of the #defines folder.
burst_scatter_mod = 0
delay_mod = FIRE_DELAY_TIER_12
G.recalculate_attachment_bonuses()
G.stop_fire()
var/mob/living/user
if(isliving(G.loc))
user = G.loc
UnregisterSignal(user, COMSIG_MOB_MOVE_OR_LOOK)

if(G.flags_gun_features & GUN_SUPPORT_PLATFORM)
G.remove_bullet_trait("iff")
G.remove_firemode(GUN_FIREMODE_AUTOMATIC)

if(!QDELETED(G))
playsound(user,'sound/items/m56dauto_rotate.ogg', 55, 1)
Expand Down Expand Up @@ -2683,12 +2708,13 @@ Defined in conflicts.dm of the #defines folder.
else
delay_mod = -FIRE_DELAY_TIER_12
G.recalculate_attachment_bonuses()
G.stop_fire()

initial_mob_dir = user.dir
RegisterSignal(user, COMSIG_MOB_MOVE_OR_LOOK, PROC_REF(handle_mob_move_or_look))

if(G.flags_gun_features & GUN_SUPPORT_PLATFORM)
G.add_bullet_trait(BULLET_TRAIT_ENTRY_ID("iff", /datum/element/bullet_trait_iff))
G.add_firemode(GUN_FIREMODE_AUTOMATIC)

else
to_chat(user, SPAN_NOTICE("You retract [src]."))
Expand Down
9 changes: 9 additions & 0 deletions code/modules/projectiles/gun_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,9 @@ DEFINES in setup.dm, referenced here.
CRASH("add_firemode called with a resulting gun_firemode_list length of [length(gun_firemode_list)].")

/obj/item/weapon/gun/proc/remove_firemode(removed_firemode, mob/user)
if(!(removed_firemode in gun_firemode_list))
return

if(!length(gun_firemode_list) || (length(gun_firemode_list) == 1))
CRASH("remove_firemode called with gun_firemode_list length [length(gun_firemode_list)].")

Expand All @@ -710,7 +713,9 @@ DEFINES in setup.dm, referenced here.
do_toggle_firemode(user, gun_firemode)

/obj/item/weapon/gun/proc/setup_firemodes()
var/old_firemode = gun_firemode
gun_firemode_list.len = 0

if(start_semiauto)
gun_firemode_list |= GUN_FIREMODE_SEMIAUTO

Expand All @@ -722,6 +727,10 @@ DEFINES in setup.dm, referenced here.

if(!length(gun_firemode_list))
CRASH("[src] called setup_firemodes() with an empty gun_firemode_list")

else if(old_firemode in gun_firemode_list)
gun_firemode = old_firemode

else
gun_firemode = gun_firemode_list[1]

Expand Down
2 changes: 2 additions & 0 deletions code/modules/projectiles/guns/energy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
fire_sound = 'sound/weapons/Laser4.ogg'
has_charge_meter = FALSE
charge_icon = "+laz_uzi_empty"
start_automatic = TRUE

/obj/item/weapon/gun/energy/laz_uzi/set_gun_config_values()
..()
Expand All @@ -194,6 +195,7 @@
scatter_unwielded = SCATTER_AMOUNT_TIER_6
damage_mult = BASE_BULLET_DAMAGE_MULT
recoil_unwielded = RECOIL_AMOUNT_TIER_5
fa_scatter_peak = SCATTER_AMOUNT_TIER_8

//############################ Taser ##################
// Lots of bits for it so splitting off an area
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/guns/flamer/flamer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@

/obj/item/weapon/gun/flamer/M240T/auto/set_gun_config_values()
. = ..()
set_fire_delay(FIRE_DELAY_TIER_3)
set_fire_delay(FIRE_DELAY_TIER_7)

GLOBAL_LIST_EMPTY(flamer_particles)
/particles/flamer_fire
Expand Down
Loading

0 comments on commit 295ef51

Please sign in to comment.