input: increase allowed autofire rates from 5 to 30. add autofire_on_directions option to mister.ini (defaults to 0/off) (#1120)

This commit is contained in:
tonytoon
2026-03-15 12:17:54 -05:00
committed by GitHub
parent ad191d88d6
commit 36af7f7b46
5 changed files with 26 additions and 23 deletions

View File

@@ -342,7 +342,10 @@ vrr_vesa_framerate=0
; disable autofire if for some reason it's not required and accidentally triggered
disable_autofire=0
; custom autofire rates (in hertz). up to five allowed.
; allow autofire on directional inputs. typically not recommended. default is 0 (disabled).
;autofire_on_directions=1
; custom autofire rates (in hertz). will round to nearest rate that divides evenly into 60hz.
; can also use literal bit patterns for on/off cycles, i.e. 0b00111. each bit is one frame.
;autofire_rates=10,0b00111,20,30

View File

@@ -24,8 +24,8 @@
actual_refresh / 60 * autofire_rate_hz == real_autofire_rate_hz
*/
#define MAX_AF_CODES 16
#define MAX_AF_RATES 6
#define MAX_AF_CODES 32
#define MAX_AF_RATES 32
#define AF_NAME_LEN 32
// global autofire cycle data.
@@ -51,13 +51,11 @@ static struct AutofireData autofiredata_default[MAX_AF_RATES]; // hardcoded fall
static int num_af_rates_default = 0;
static int num_af_rates = 0;
int get_autofire_rate_count()
{
return num_af_rates;
}
static void set_autofire_name(struct AutofireData *data, const char *base_name) {
float hz = data->cycle_length > 0 ? (60.0f / data->cycle_length) : 0.0f;
if (base_name && base_name[0]) {
@@ -114,7 +112,6 @@ void set_autofire_code(int player, uint32_t code, uint32_t mask, int index, bool
}
}
// step autofire rate; wrap to disabled at max.
void inc_autofire_code(int player, uint32_t code, uint32_t mask) {
if (get_autofire_locked(player, code)) return;
@@ -125,16 +122,6 @@ void inc_autofire_code(int player, uint32_t code, uint32_t mask) {
set_autofire_code(player, code, mask, index);
}
// advance all autofire patterns (run once per frame)
//void autofire_tick() {
// for (int i = 1; i < num_af_rates; i++)
// {
//autofiredata[i].bit = (autofiredata[i].cycle_mask >> autofiredata[i].frame_count) & 1u;
//if (++(autofiredata[i].frame_count) >= autofiredata[i].cycle_length)
// autofiredata[i].frame_count = 0;
// }
//}
// returns whether the buttons for this code should be held or released this frame.
// (updated every time we call autofire_tick)
bool get_autofire_bit(int player, uint32_t code, uint32_t frame_count) {
@@ -179,8 +166,11 @@ bool is_autofire_enabled(int player, uint32_t code) {
// some arcade shooters have pretty odd optimal autofire patterns to manage rank
// let's hide them here for my shmup buddies
static const struct AutofireData autofire_patterns[] = {
{ "GUNFRONTIER", 0b111100000ULL, 9 },
{ "GAREGGA", 0b1110000ULL, 7 },
{ "GUNFRONTIER", 0b000001111ULL, 9 }, // gun frontier raises rank really fast in response to autofire. this is the fastest
// you can fire without it triggering a rate increase
{ "GAREGGA", 0b0000111ULL, 7 }, // i trusted google that battle garegga likes this rate but i'm terrified of the game
// so please let me know if i'm wrong
};
// helper for formatting binary literal patterns.
@@ -212,6 +202,10 @@ static void init_autofire_entry(struct AutofireData *data, uint64_t mask, int le
if (data->cycle_length < 1) data->cycle_length = 1;
}
// this will always result in an autofire rate that divides evenly into 60hz
// could probably revisit and use a couple different algorithms to allow approximations
// of non-divisible rates but the working theory was to keep this code easy to read
// and if anybody needs something more complicated, fall back on a custom bitmask
static inline struct AutofireData mask_from_hertz(double hz_target)
{
struct AutofireData p = {{0}, 0, 0};

View File

@@ -133,8 +133,9 @@ static const ini_var_t ini_vars[] =
{ "DEBUG", (void *)(&(cfg.debug)), UINT8, 0, 1 },
{ "LOOKAHEAD", (void *)(&(cfg.lookahead)), UINT8, 0, 3 },
{ "MAIN", (void*)(&(cfg.main)), STRING, 0, sizeof(cfg.main) - 1 },
{"VFILTER_INTERLACE_DEFAULT", (void*)(&(cfg.vfilter_interlace_default)), STRING, 0, sizeof(cfg.vfilter_interlace_default) - 1 },
{ "VFILTER_INTERLACE_DEFAULT", (void*)(&(cfg.vfilter_interlace_default)), STRING, 0, sizeof(cfg.vfilter_interlace_default) - 1 },
{ "AUTOFIRE_RATES", (void *)(&(cfg.autofire_rates)), STRING, 0, sizeof(cfg.autofire_rates) - 1 },
{ "AUTOFIRE_ON_DIRECTIONS", (void *)(&(cfg.autofire_on_directions)), UINT8, 0, 1 },
};

3
cfg.h
View File

@@ -102,7 +102,8 @@ typedef struct {
uint8_t lookahead;
char main[1024];
char vfilter_interlace_default[1023];
char autofire_rates[256];
char autofire_rates[3072];
uint8_t autofire_on_directions;
} cfg_t;

View File

@@ -1915,20 +1915,25 @@ static bool handle_autofire_toggle(int num, uint32_t mask, uint32_t code, char p
}
return false;
}
const char *dir_bnames[] = { "Right", "Left", "Down", "Up" };
// we can only get here if the OSD or BTN_TGL keys were pressed
// in that event we see if lastmask/lastcode tells us we're holding a button
// and if we are, we toggle autofire for that button
if (!user_io_osd_is_visible() && press && !cfg.disable_autofire)
{
if ((lastcode[num] && lastmask[num] && (lastmask[num] & 0xF) == 0)) // don't allow enabling autofire on directions
if (lastcode[num] && lastmask[num] && (cfg.autofire_on_directions || (lastmask[num] & 0xF) == 0))
{
char *strat = str;
inc_autofire_code(num, lastcode[num], lastmask[num]);
// display autofire status for each button in the mask
FOR_EACH_SET_BIT(lastmask[num], btn) {
strat += sprintf(strat, "%s\n", joy_bnames[btn-4]);
if (btn < 4) {
strat += sprintf(strat, "%s ", dir_bnames[btn]);
} else {
strat += sprintf(strat, "%s\n", joy_bnames[btn-4]);
}
}
const char *rate = get_autofire_rate_hz_button(num, lastcode[num]);
@@ -5948,7 +5953,6 @@ int input_poll(int getchar)
static uint32_t last_frame_count = 0;
if (FRAME_TICK(last_frame_count)) {
key_update_frames_held();
//autofire_tick(); // advance all autofire patterns by 1
}
int ret = input_test(getchar);