I’ve been building a turn-based style RPG in Typescript based upon this ROT-JS Tutorial. For the most part it’s fairly clear and I have a handle on everything, except I’ve begun building out a graphics engine to render the game in 2D sprites rather than terminal style graphics. Inspiration is the game presentation of modern tile-based RPGs like Moonring or Caves of Qud’s graphical release.
The roadblock I’m currently having, though, involves input handling and precise control over it. The tutorial’s method of input handling is to have the engine standup some listeners on initialization and simply update the game any time an event is detected
window.addEventListener('keydown', (event) => {
this.update(event);
});
Eventually this settles on a structure where the game is broken down into screens (Main Menu, GameScreen, GameOver, etc) that has an input handler that gets invoked by the window listener, and returns an action from that input handler
window.addEventListener('keydown', (event) => {
engine.screen.update(event);
});
With each game screen being defined like this
class BaseGameScreen {
inputHandler: BaseInputHandler
constructor()
update(event) {
const action = this.inputHandler.handleKeyPress(event)
}
This works for the ASCII style presentation of most roguelikes, but felt sluggish when holding down a key, since it reverted to the polling rate after the initial press. To address this, I moved the udpate
call into the requestAnimationFrame loop that the renderer uses
class Input {
keyMap: new Map()
constructor() {
window.addEventListener("keydown", this.markPressed)
window.addEventListenred("keyup", this.markReleased)
}
markPressed(event) {
this.keyMap.set(event.key, "pressed")
}
markReleased(event) {
this.keyMap.set(event.key, "released")
}
}
const input = new Input()
function renderScreen() {
requestAnimationFrame(renderScreen)
// could probably consolidate these into one call but eh
window.engine.screen.update(input)
window.engine.screen.render()
}
renderScreen()
This greatly improved the input feel of the game, but way in the opposite way. Now even a casual tap of the keyboard causes the player to fly across the tiles, taking several turns at once. I even followed this tutorial for using the command pattern in an input handler to be able to define a list of what keys are pressed, and what actions correspond to those keys
And it works, but the game design in the tutorial is intended for a real-time multiplayer game, and includes reference a gameState object that I just don’t have or need, really. And each screen will have a different set of actions that need to be defined, and this is where I’m getting confused
My goal is to have a system where the input handlers know nothing about what KEY is pressed, but instead respond to which actions are requested, without repeating an action so fast the player can’t reasonably respond
Something like
... // inside inputHandler
handleEvent() {
if (event.wantsMovement) {
doMovementAction()
} else if (event.wantsInventory) {
doInventoryAction()
}
}
But, how do I effectively add this? Should I be doing something like const event = InputManger(this.actionMap)
where each screen defines its own actions, or should I just do it before the update call
const input = new InputManager(actionMap)
renderScreen() {
const event = input.getInputs()
window.engine.screen.update(event)
}
Anyway, I know that was a lot of questions but I hope I was clear on what I’m looking to do