132 lines
3.9 KiB
C++
132 lines
3.9 KiB
C++
#include "core/jail/jinput.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
#include "core/system/director.hpp"
|
|
|
|
namespace {
|
|
|
|
// keystates és actualitzat per SDL internament. Des del joc només fem lectures.
|
|
const bool* keystates = nullptr;
|
|
|
|
// Buffer dels últims 5 caràcters tecle. Emmagatzemem caràcters ASCII
|
|
// lowercase (traduïts des de SDL_Scancode) per a poder comparar directament
|
|
// amb les cadenes dels cheats ("reviu", "alone", "obert").
|
|
Uint8 cheat[5] = {0, 0, 0, 0, 0};
|
|
|
|
bool key_pressed = false;
|
|
|
|
// Temps restant en mil·lisegons durant el qual Ji::keyPressed/Ji::anyKey
|
|
// retornen false. Utilitzat per a evitar que pulsacions fortuïtes
|
|
// saltin cinemàtiques al començament.
|
|
float wait_ms = 0.0F;
|
|
|
|
// Per a calcular el delta entre crides a Ji::update sense que els callers
|
|
// hagen de passar-lo explícitament. Es reinicia a la primera crida.
|
|
Uint64 last_update_tick = 0;
|
|
|
|
bool input_blocked = false;
|
|
|
|
Uint8 virtual_keystates[static_cast<size_t>(Ji::VirtualSource::COUNT)][SDL_SCANCODE_COUNT] = {{0}};
|
|
|
|
auto scancode_to_ascii(Uint8 scancode) -> Uint8 {
|
|
if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
|
|
return static_cast<Uint8>('a' + (scancode - SDL_SCANCODE_A));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void Ji::disableKeyboard(Uint32 time) {
|
|
wait_ms = static_cast<float>(time);
|
|
}
|
|
|
|
void Ji::setInputBlocked(bool blocked) {
|
|
input_blocked = blocked;
|
|
}
|
|
|
|
void Ji::setVirtualKey(int scancode, VirtualSource source, bool pressed) {
|
|
if (scancode < 0 || scancode >= SDL_SCANCODE_COUNT) {
|
|
return;
|
|
}
|
|
const auto src_idx = static_cast<size_t>(source);
|
|
if (src_idx >= static_cast<size_t>(VirtualSource::COUNT)) {
|
|
return;
|
|
}
|
|
virtual_keystates[src_idx][scancode] = pressed ? 1 : 0;
|
|
}
|
|
|
|
void Ji::moveCheats(Uint8 scancode) {
|
|
cheat[0] = cheat[1];
|
|
cheat[1] = cheat[2];
|
|
cheat[2] = cheat[3];
|
|
cheat[3] = cheat[4];
|
|
cheat[4] = scancode_to_ascii(scancode);
|
|
}
|
|
|
|
void Ji::update() {
|
|
// El director ha processat tots els events. Ací només refresquem
|
|
// el snapshot del teclat i consumim el flag de tecla polsada.
|
|
if (keystates == nullptr) {
|
|
keystates = SDL_GetKeyboardState(nullptr);
|
|
}
|
|
|
|
const Uint64 now = SDL_GetTicks();
|
|
if (last_update_tick == 0) {
|
|
last_update_tick = now;
|
|
}
|
|
const auto delta_ms = static_cast<float>(now - last_update_tick);
|
|
last_update_tick = now;
|
|
|
|
if (wait_ms > 0.0F) {
|
|
wait_ms -= delta_ms;
|
|
wait_ms = std::max(wait_ms, 0.0F);
|
|
}
|
|
|
|
// Consumim el flag de "alguna tecla no-GUI polsada" del director
|
|
key_pressed = Director::get()->consumeKeyPressed();
|
|
}
|
|
|
|
auto Ji::keyPressed(int key) -> bool {
|
|
if (wait_ms > 0.0F || keystates == nullptr) {
|
|
return false;
|
|
}
|
|
// Input bloquejat (p.ex. menú flotant obert)
|
|
if (input_blocked) {
|
|
return false;
|
|
}
|
|
// ESC bloquejada pel Director (primera pulsació mostra notificació)
|
|
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) {
|
|
return false;
|
|
}
|
|
if (key < 0 || key >= SDL_SCANCODE_COUNT) {
|
|
return false;
|
|
}
|
|
if (static_cast<int>(keystates[key]) != 0) {
|
|
return true;
|
|
}
|
|
return std::ranges::any_of(virtual_keystates, [key](const auto& vk) { return vk[key] != 0; });
|
|
}
|
|
|
|
auto Ji::cheatActivated(const char* cheat_code) -> bool {
|
|
const size_t len = std::strlen(cheat_code);
|
|
if (len > sizeof(cheat)) {
|
|
return false;
|
|
}
|
|
// Compara contra els últims `len` caràcters del buffer. El buffer té
|
|
// mida fixa 5 i acumula sempre el darrer tecle a la posició 4.
|
|
const size_t offset = sizeof(cheat) - len;
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (cheat[offset + i] != static_cast<Uint8>(cheat_code[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto Ji::anyKey() -> bool {
|
|
return wait_ms > 0.0F ? false : key_pressed;
|
|
}
|