Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ports TGMC autofire #3909

Merged
merged 34 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
b63376c
adds autofire
johndoe2013 Jul 16, 2023
95a5b2a
Merge branch 'master' into autofire
Zonespace27 Jul 16, 2023
b5dc034
make it less silent, cut out some code
johndoe2013 Jul 16, 2023
74f7b29
Merge branch 'autofire' of https://github.com/Zonespace27/cmss13 into…
johndoe2013 Jul 16, 2023
2a54946
zonenote removals
johndoe2013 Jul 17, 2023
5bfc904
smol changes
johndoe2013 Jul 17, 2023
e9eb658
more
johndoe2013 Jul 17, 2023
eaff0d9
loc
johndoe2013 Jul 17, 2023
0786894
flags
johndoe2013 Jul 17, 2023
fb871d6
more changes
johndoe2013 Jul 17, 2023
ad94e95
clean up
johndoe2013 Jul 18, 2023
62f61ac
sound
johndoe2013 Jul 18, 2023
100711c
nerf barrel charger with fixes
johndoe2013 Jul 18, 2023
84fc15c
a few bits
johndoe2013 Jul 18, 2023
1a31b25
nozzle fix
johndoe2013 Jul 18, 2023
9312b81
intent safety
johndoe2013 Jul 18, 2023
071bc99
exp. changes
johndoe2013 Jul 18, 2023
d400955
bugfix
johndoe2013 Jul 18, 2023
480be55
hahah how tf did i miss this
johndoe2013 Jul 18, 2023
2954b06
fa removal
johndoe2013 Jul 18, 2023
0056536
autoguns are now vastly more innacurate after firing for a while
johndoe2013 Jul 18, 2023
b169937
ded
johndoe2013 Jul 18, 2023
f6773ea
improves check
johndoe2013 Jul 18, 2023
35c7a6a
Merge branch 'master' into autofire
harryob Jul 18, 2023
6560a7e
a
johndoe2013 Jul 22, 2023
165f3f0
cleanup
johndoe2013 Jul 22, 2023
6e5e001
hopefully the last changes i need to make
johndoe2013 Jul 22, 2023
5cb2b47
gun
johndoe2013 Jul 23, 2023
f32787c
spam fixes
johndoe2013 Jul 23, 2023
92a045b
utterly breaks FA's knees
johndoe2013 Jul 23, 2023
02a9b2c
slightly adjust
johndoe2013 Jul 23, 2023
cc1de22
modifies a bunch of things for setters, adders, and getters
johndoe2013 Jul 24, 2023
39e67bf
rob
johndoe2013 Jul 24, 2023
689ff12
CI fix
johndoe2013 Jul 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions code/__DEFINES/autofire.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Controls how many buckets should be kept, each representing a tick. Max is ten seconds, to have better perf.
#define AUTOFIRE_BUCKET_LEN (world.fps * 10)
/// Helper for getting the correct bucket
#define AUTOFIRE_BUCKET_POS(next_fire) (((round((next_fire - SSautomatedfire.head_offset) / world.tick_lag) + 1) % AUTOFIRE_BUCKET_LEN) || AUTOFIRE_BUCKET_LEN)
harryob marked this conversation as resolved.
Show resolved Hide resolved
32 changes: 13 additions & 19 deletions code/__DEFINES/conflict.dm
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,24 @@
#define GUN_TRIGGER_SAFETY (1<<1)
#define GUN_UNUSUAL_DESIGN (1<<2)
#define GUN_SILENCED (1<<3)
#define GUN_AUTOMATIC (1<<4)
///If checking for ammo with current.mag you have to check it against numerical values, as booleans will not trigger.
#define GUN_INTERNAL_MAG (1<<5)
#define GUN_AUTO_EJECTOR (1<<6)
#define GUN_AMMO_COUNTER (1<<7)
#define GUN_BURST_ON (1<<8)
#define GUN_BURST_FIRING (1<<9)
#define GUN_FLASHLIGHT_ON (1<<10)
#define GUN_WY_RESTRICTED (1<<11)
#define GUN_SPECIALIST (1<<12)
#define GUN_WIELDED_FIRING_ONLY (1<<13)
#define GUN_HAS_FULL_AUTO (1<<14)
#define GUN_FULL_AUTO_ON (1<<15)
#define GUN_INTERNAL_MAG (1<<4)
#define GUN_AUTO_EJECTOR (1<<5)
#define GUN_AMMO_COUNTER (1<<6)
#define GUN_BURST_FIRING (1<<7)
#define GUN_FLASHLIGHT_ON (1<<8)
#define GUN_WY_RESTRICTED (1<<9)
#define GUN_SPECIALIST (1<<10)
#define GUN_WIELDED_FIRING_ONLY (1<<11)
/// removes unwielded accuracy and scatter penalties (not recoil)
#define GUN_ONE_HAND_WIELDED (1<<16)
#define GUN_ANTIQUE (1<<17)
#define GUN_ONE_HAND_WIELDED (1<<12)
#define GUN_ANTIQUE (1<<13)
/// Whether the gun has been fired by its current user (reset upon `dropped()`)
#define GUN_RECOIL_BUILDUP (1<<18)
#define GUN_RECOIL_BUILDUP (1<<14)
/// support weapon, bipod will grant IFF
#define GUN_SUPPORT_PLATFORM (1<<19)
#define GUN_BURST_ONLY (1<<20)
#define GUN_FULL_AUTO_ONLY (1<<21)
#define GUN_SUPPORT_PLATFORM (1<<15)
/// No gun description, only base desc
#define GUN_NO_DESCRIPTION (1<<22)
#define GUN_NO_DESCRIPTION (1<<16)
// NOTE: Don't add flags past 1<<23, it'll break things due to BYOND limitations. You can usually use a Component instead.

#define USES_STREAKS (1<<0)
Expand Down
6 changes: 6 additions & 0 deletions code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,9 @@
#define COMSIG_MOB_STAT_SET_DEAD "mob_stat_set_dead"

#define COMSIG_GHOST_MOVED "ghost_moved"

#define COMSIG_MOB_MOUSEDOWN "mob_mousedown" //from /client/MouseDown(): (atom/object, turf/location, control, params)
#define COMSIG_MOB_MOUSEUP "mob_mouseup" //from /client/MouseUp(): (atom/object, turf/location, control, params)
#define COMSIG_MOB_MOUSEDRAG "mob_mousedrag" //from /client/MouseDrag(): (atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params)
#define COMSIG_MOB_CLICK_CANCELED (1<<0)
#define COMSIG_MOB_CLICK_HANDLED (1<<1)
16 changes: 16 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_item.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,19 @@
#define COMSIG_ITEM_ZOOM "item_zoom"
/// from /obj/item/proc/unzoom() : (mob/user)
#define COMSIG_ITEM_UNZOOM "item_unzoom"

//Signals for automatic fire at component
#define COMSIG_AUTOMATIC_SHOOTER_START_SHOOTING_AT "start_shooting_at"
#define COMSIG_AUTOMATIC_SHOOTER_STOP_SHOOTING_AT "stop_shooting_at"
#define COMSIG_AUTOMATIC_SHOOTER_SHOOT "shoot"

//Signals for gun auto fire component
#define COMSIG_GET_BURST_FIRE "get_burst_fire"
#define BURST_FIRING (1<<0)

#define COMSIG_GUN_FIRE "gun_fire"
#define COMSIG_GUN_STOP_FIRE "gun_stop_fire"
#define COMSIG_GUN_FIRE_MODE_TOGGLE "gun_fire_mode_toggle"
#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"
8 changes: 8 additions & 0 deletions code/__DEFINES/guns.dm
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@
#define REVOLVER_TIP_COLOR_INCENDIARY AMMO_BAND_COLOR_INCENDIARY
#define REVOLVER_TIP_COLOR_PENETRATING AMMO_BAND_COLOR_PENETRATING
#define REVOLVER_TIP_COLOR_TOXIN AMMO_BAND_COLOR_TOXIN

#define GUN_FIREMODE_SEMIAUTO "semi-auto fire mode"
#define GUN_FIREMODE_BURSTFIRE "burst-fire mode"
#define GUN_FIREMODE_AUTOMATIC "automatic fire mode"

//autofire component fire callback return flags
#define AUTOFIRE_CONTINUE (1<<0)
#define AUTOFIRE_SUCCESS (1<<1)
1 change: 1 addition & 0 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@

#define SS_PRIORITY_INPUT 1000
#define SS_PRIORITY_TIMER 700
#define SS_PRIORITY_AUTOFIRE 450
#define SS_PRIORITY_SOUND 250
#define SS_PRIORITY_TICKER 200
#define SS_PRIORITY_NIGHTMARE 180
Expand Down
6 changes: 0 additions & 6 deletions code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -109,24 +109,18 @@ DEFINE_BITFIELD(flags_gun_features, list(
"GUN_TRIGGER_SAFETY" = GUN_TRIGGER_SAFETY,
"GUN_UNUSUAL_DESIGN" = GUN_UNUSUAL_DESIGN,
"GUN_SILENCED" = GUN_SILENCED,
"GUN_AUTOMATIC" = GUN_AUTOMATIC,
"GUN_INTERNAL_MAG" = GUN_INTERNAL_MAG,
"GUN_AUTO_EJECTOR" = GUN_AUTO_EJECTOR,
"GUN_AMMO_COUNTER" = GUN_AMMO_COUNTER,
"GUN_BURST_ON" = GUN_BURST_ON,
"GUN_BURST_FIRING" = GUN_BURST_FIRING,
"GUN_FLASHLIGHT_ON" = GUN_FLASHLIGHT_ON,
"GUN_WY_RESTRICTED" = GUN_WY_RESTRICTED,
"GUN_SPECIALIST" = GUN_SPECIALIST,
"GUN_WIELDED_FIRING_ONLY" = GUN_WIELDED_FIRING_ONLY,
"GUN_HAS_FULL_AUTO" = GUN_HAS_FULL_AUTO,
"GUN_FULL_AUTO_ON" = GUN_FULL_AUTO_ON,
"GUN_ONE_HAND_WIELDED" = GUN_ONE_HAND_WIELDED,
"GUN_ANTIQUE" = GUN_ANTIQUE,
"GUN_RECOIL_BUILDUP" = GUN_RECOIL_BUILDUP,
"GUN_SUPPORT_PLATFORM" = GUN_SUPPORT_PLATFORM,
"GUN_BURST_ONLY" = GUN_BURST_ONLY,
"GUN_FULL_AUTO_ONLY" = GUN_FULL_AUTO_ONLY,
))

DEFINE_BITFIELD(flags_magazine, list(
Expand Down
9 changes: 9 additions & 0 deletions code/_onclick/click_hold.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
mouse_trace_history = null
LAZYADD(mouse_trace_history, A)

if(SEND_SIGNAL(mob, COMSIG_MOB_MOUSEDOWN, A, T, skin_ctl, params) & COMSIG_MOB_CLICK_CANCELED)
return

var/list/mods = params2list(params)
if(mods["left"])
SEND_SIGNAL(src, COMSIG_CLIENT_LMB_DOWN, A, mods)
Expand Down Expand Up @@ -62,6 +65,9 @@
params += ";click_catcher=1"
holding_click = FALSE

if(SEND_SIGNAL(mob, COMSIG_MOB_MOUSEUP, A, T, skin_ctl, params) & COMSIG_MOB_CLICK_CANCELED)
return

var/list/mods = params2list(params)
if(mods["left"])
SEND_SIGNAL(src, COMSIG_CLIENT_LMB_UP, A, params)
Expand All @@ -75,6 +81,9 @@
if(click_catcher_click)
params += ";click_catcher=1"

if(SEND_SIGNAL(mob, COMSIG_MOB_MOUSEDRAG, src_obj, over_obj, src_loc, over_loc, src_ctl, over_ctl, params) & COMSIG_MOB_CLICK_CANCELED)
return

var/list/mods = params2list(params)
if(mods["left"])
SEND_SIGNAL(src, COMSIG_CLIENT_LMB_DRAG, src_obj, over_obj, params)
Expand Down
85 changes: 85 additions & 0 deletions code/controllers/subsystem/autofire.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* # Autofire Subsystem
*
* Maintains a timer-like system to handle autofiring. Much of this code is modeled
* after or adapted from TGMC's runechat subsytem.
*
* Note that this has the same structure for storing and queueing shooter component as the timer subsystem does
* for handling timers: the bucket_list is a list of autofire component, each of which are the head
* of a linked list. Any given index in bucket_list could be null, representing an empty bucket.
*
* Doesn't support any event scheduled for more than 100 ticks in the future, as it has no secondary queue by design
*/
SUBSYSTEM_DEF(automatedfire)
name = "Automated fire"
flags = SS_TICKER | SS_NO_INIT
wait = 1
priority = SS_PRIORITY_AUTOFIRE

/// world.time of the first entry in the bucket list, effectively the 'start time' of the current buckets
var/head_offset = 0
/// Index of the first non-empty bucket
var/practical_offset = 1
///How many buckets for every frame of world.fps
var/bucket_resolution = 0
/// How many shooter are in the buckets
var/shooter_count = 0
/// List of buckets, each bucket holds every shooter that has to shoot this byond tick
var/list/bucket_list = list()
/// Reference to the next shooter before we clean shooter.next
var/datum/component/automatedfire/next_shooter

/datum/controller/subsystem/automatedfire/PreInit()
bucket_list.len = AUTOFIRE_BUCKET_LEN
head_offset = world.time
bucket_resolution = world.tick_lag

/datum/controller/subsystem/automatedfire/stat_entry(msg = "ActShooters: [shooter_count]")
return ..()

/datum/controller/subsystem/automatedfire/fire(resumed = FALSE)
// Check for when we need to loop the buckets, this occurs when
// the head_offset is approaching AUTOFIRE_BUCKET_LEN ticks in the past
if (practical_offset > AUTOFIRE_BUCKET_LEN)
head_offset += TICKS2DS(AUTOFIRE_BUCKET_LEN)
practical_offset = 1
resumed = FALSE

// Check for when we have to reset buckets, typically from auto-reset
if ((length(bucket_list) != AUTOFIRE_BUCKET_LEN) || (world.tick_lag != bucket_resolution))
reset_buckets()
bucket_list = src.bucket_list
resumed = FALSE

// Store a reference to the 'working' shooter so that we can resume if the MC
// has us stop mid-way through processing
var/static/datum/component/automatedfire/shooter
if (!resumed)
shooter = null

// Iterate through each bucket starting from the practical offset
while (practical_offset <= AUTOFIRE_BUCKET_LEN && head_offset + ((practical_offset - 1) * world.tick_lag) <= world.time)
if(!shooter)
shooter = bucket_list[practical_offset]
bucket_list[practical_offset] = null

while (shooter)
next_shooter = shooter.next
INVOKE_ASYNC(shooter, TYPE_PROC_REF(/datum/component/automatedfire, process_shot))

SSautomatedfire.shooter_count--
shooter = next_shooter
if (MC_TICK_CHECK)
return

// Move to the next bucket
practical_offset++

/datum/controller/subsystem/automatedfire/Recover()
bucket_list |= SSautomatedfire.bucket_list

///In the event of a change of world.tick_lag, we refresh the size of the bucket and the bucket resolution
/datum/controller/subsystem/automatedfire/proc/reset_buckets()
bucket_list.len = AUTOFIRE_BUCKET_LEN
head_offset = world.time
bucket_resolution = world.tick_lag
49 changes: 49 additions & 0 deletions code/datums/components/autofire/_automated_fire.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/datum/component/automatedfire
///The owner of this component
var/atom/shooter
/// Contains the scheduled fire time, used for scheduling EOL
var/next_fire
/// Contains the reference to the next component in the bucket, used by autofire subsystem
var/datum/component/automatedfire/next
/// Contains the reference to the previous component in the bucket, used by autofire subsystem
var/datum/component/automatedfire/prev


/// schedule the shooter into the system, inserting it into the next fire queue
/datum/component/automatedfire/proc/schedule_shot()
//We move to another bucket, so we clean the reference from the former linked list
next = null
prev = null
var/list/bucket_list = SSautomatedfire.bucket_list

// Ensure the next_fire time is properly bound to avoid missing a scheduled event
next_fire = max(CEILING(next_fire, world.tick_lag), world.time + world.tick_lag)

// Get bucket position and a local reference to the datum var, it's faster to access this way
var/bucket_pos = AUTOFIRE_BUCKET_POS(next_fire)

// Get the bucket head for that bucket, increment the bucket count
var/datum/component/automatedfire/bucket_head = bucket_list[bucket_pos]
SSautomatedfire.shooter_count++

// If there is no existing head of this bucket, we can set this shooter to be that head
if (!bucket_head)
bucket_list[bucket_pos] = src
return

// Otherwise it's a simple insertion into the double-linked list
if (bucket_head.next)
next = bucket_head.next
next.prev = src

bucket_head.next = src
prev = bucket_head

//Something went wrong, probably a lag spike or something. To prevent infinite loops, we reschedule it to a another next fire
if(prev == src)
next_fire += 1
schedule_shot()

///Handle the firing of the autofire component
/datum/component/automatedfire/proc/process_shot()
return
Loading