input: switch autofire to tracking frames per key (#1094)
This commit is contained in:
33
autofire.cpp
33
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
67
input.cpp
67
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);
|
||||
|
||||
Reference in New Issue
Block a user