diff --git a/MiSTer.ini b/MiSTer.ini index ff4c453..2a854e5 100644 --- a/MiSTer.ini +++ b/MiSTer.ini @@ -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 diff --git a/autofire.cpp b/autofire.cpp index 3f2f5c9..dcb94b5 100644 --- a/autofire.cpp +++ b/autofire.cpp @@ -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}; diff --git a/cfg.cpp b/cfg.cpp index f5ff477..4214031 100644 --- a/cfg.cpp +++ b/cfg.cpp @@ -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 }, }; diff --git a/cfg.h b/cfg.h index 762ae2d..8e4b6df 100644 --- a/cfg.h +++ b/cfg.h @@ -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; diff --git a/input.cpp b/input.cpp index 1389006..4815596 100644 --- a/input.cpp +++ b/input.cpp @@ -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);