diff --git a/autofire.cpp b/autofire.cpp index d8042ca..41791f2 100644 --- a/autofire.cpp +++ b/autofire.cpp @@ -13,7 +13,8 @@ We take the desired autofire rate in hertz and convert that to a bitmask 0 == button released 1 == button pressed - We advance through a single bit of this mask every frame on the assumption that + Input tracks how many frames the button has been held for. + We use that frame count modulo the autofire cycle length to index into the bitmask. most games read their inputs every vsync (or is it vrefresh? once per frame.) We display autofire rates to the user as if the game is running at 60hz, but internally @@ -32,8 +33,6 @@ struct AutofireData { char name[AF_NAME_LEN]; // display name uint64_t cycle_mask; // bitmask representing the autofire cycle int cycle_length; // length of the cycle in frames - int bit; // current bit in the cycle - int frame_count; // current frame in the cycle }; // per-player autofire table; index 0 means disabled. @@ -103,21 +102,21 @@ void inc_autofire_code(int player, uint32_t code, uint32_t mask) { } // 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; - } -} +//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) { +bool get_autofire_bit(int player, uint32_t code, uint32_t frame_count) { int rate_idx = get_autofire_code_idx(player, code); if (rate_idx > 0) { - return autofiredata[rate_idx].bit; + return (autofiredata[rate_idx].cycle_mask >> frame_count % autofiredata[rate_idx].cycle_length) & 1u; } return false; } @@ -151,8 +150,8 @@ 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, 0, 0 }, - { "GAREGGA", 0b1110000ULL, 7, 0, 0 }, + { "GUNFRONTIER", 0b111100000ULL, 9 }, + { "GAREGGA", 0b1110000ULL, 7 }, }; // helper for formatting binary literal patterns. @@ -182,13 +181,11 @@ static void init_autofire_entry(struct AutofireData *data, uint64_t mask, int le data->cycle_mask = mask; if (data->cycle_length > 64) data->cycle_length = 64; if (data->cycle_length < 1) data->cycle_length = 1; - data->frame_count = 0; - data->bit = (data->cycle_mask & 1ULL) ? 1 : 0; } static inline struct AutofireData mask_from_hertz(double hz_target) { - struct AutofireData p = {{0}, 0, 0, 0, 0}; + struct AutofireData p = {{0}, 0, 0}; if (hz_target <= 0.0) return p; if (hz_target > 30.0) hz_target = 30.0; diff --git a/autofire.h b/autofire.h index daa27ba..713edc2 100644 --- a/autofire.h +++ b/autofire.h @@ -7,8 +7,8 @@ const char *get_autofire_rate_hz(int player, uint32_t code); bool is_autofire_enabled(int player, uint32_t code); void clear_autofire(int player); void inc_autofire_code(int player, uint32_t code, uint32_t mask); -void autofire_tick(); +//void autofire_tick(); bool parse_autofire_cfg(); -bool get_autofire_bit(int player, uint32_t code); +bool get_autofire_bit(int player, uint32_t code, uint32_t frame_count); #endif diff --git a/input.cpp b/input.cpp index 9646716..6222342 100644 --- a/input.cpp +++ b/input.cpp @@ -1902,16 +1902,38 @@ static bool handle_autofire_toggle(int num, uint32_t mask, uint32_t code, char p static uint32_t osdbtn = 0; +// tracking of key states for handling inputs +// we OR all of them together at the end of the input path to determine which +// of the 32 virtual buttons should be pressed or released for a given player. + +#define MAX_KEY_STATES 128 + +// we track a full mask per key because i wasn't sure how to get the full key->buttons mapping +// after it's already been set. struct KeyStates { - uint32_t key[256]; - uint32_t mask[256]; + uint32_t key[MAX_KEY_STATES]; + uint32_t mask[MAX_KEY_STATES]; + uint32_t frames_held[MAX_KEY_STATES]; int count; }; -#define MAX_KEY_STATES 64 - KeyStates key_states[NUMPLAYERS] = {}; +// returns the bitmask representing all button states for a given ev->code (key) +// returns 0 if the key is not found or if key has no buttons pressed. +static uint32_t get_key_state(int player, uint32_t key) +{ + for (int i = 0; i < key_states[player].count; i++) + { + if (key_states[player].key[i] == key) + { + return key_states[player].mask[i]; + } + } + return 0u; +} + +// updates the bitmask representing all button states for a given ev->code (key) static void set_key_state(int player, uint32_t key, bool press, uint32_t mask) { for (int i = 0; i < key_states[player].count; i++) @@ -1926,17 +1948,6 @@ static void set_key_state(int player, uint32_t key, bool press, uint32_t mask) else { key_states[player].mask[i] &= ~mask; - // remove an entry if no bits are set. - if (!key_states[player].mask[i]) { - int last = key_states[player].count - 1; - if (i != last) { - key_states[player].key[i] = key_states[player].key[last]; - key_states[player].mask[i] = key_states[player].mask[last]; - } - key_states[player].key[last] = 0; - key_states[player].mask[last] = 0u; - key_states[player].count--; - } return; } } @@ -1958,8 +1969,9 @@ uint32_t build_joy_mask(int player) uint32_t mask = 0u; for (int i = 0; i < key_states[player].count; i++) { + uint32_t key = key_states[player].key[i]; if (!is_autofire_enabled(player, key_states[player].key[i])) - mask |= key_states[player].mask[i]; + mask |= get_key_state(player, key); } return mask; } @@ -1969,9 +1981,10 @@ uint32_t build_autofire_mask(int player) uint32_t mask = 0u; for (int i = 0; i < key_states[player].count; i++) { - if (is_autofire_enabled(player, key_states[player].key[i])) - if (get_autofire_bit(player, key_states[player].key[i])) - mask |= key_states[player].mask[i]; + uint32_t key = key_states[player].key[i]; + uint32_t frames_held = key_states[player].frames_held[i]; + if (is_autofire_enabled(player, key) && get_autofire_bit(player, key, frames_held)) + mask |= get_key_state(player, key); } return mask; } @@ -5843,6 +5856,19 @@ int input_test(int getchar) return 0; } +void key_update_frames_held() +{ + for (int i = 0; i < NUMPLAYERS; i++) { + for (int k = 0; k < key_states[i].count; k++) { + if (key_states[i].mask[k] != 0) { + key_states[i].frames_held[k]++; + } else { + key_states[i].frames_held[k]= 0; + } + } + } +} + int input_poll(int getchar) { PROFILE_FUNCTION(); @@ -5853,7 +5879,8 @@ int input_poll(int getchar) // FRAME_TICK compares against frame_timer's counter (updated elsewhere) and fires once per frame. static uint32_t last_frame_count = 0; if (FRAME_TICK(last_frame_count)) { - autofire_tick(); // advance all autofire patterns by 1 + key_update_frames_held(); + //autofire_tick(); // advance all autofire patterns by 1 } int ret = input_test(getchar);