Merge branch 'pulls/1829009049/37'

This commit is contained in:
Nut.andor
2022-09-25 11:56:03 +02:00
2 changed files with 255 additions and 57 deletions

View File

@@ -1,13 +1,18 @@
package com.gpl.rpg.AndorsTrail.controller; package com.gpl.rpg.AndorsTrail.controller;
import android.content.Context;
import android.content.Intent;
import android.util.SparseIntArray;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener; import android.view.View.OnLongClickListener;
import com.gpl.rpg.AndorsTrail.activity.HeroinfoActivity;
import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.util.Coord; import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.L;
public final class InputController implements OnClickListener, OnLongClickListener{ public final class InputController implements OnClickListener, OnLongClickListener{
private final ControllerContext controllers; private final ControllerContext controllers;
@@ -19,62 +24,258 @@ public final class InputController implements OnClickListener, OnLongClickListen
private long lastTouchEventTime = 0; private long lastTouchEventTime = 0;
private boolean isDpadActive = false; private boolean isDpadActive = false;
private int keyState_dx = 0;
private int keyState_dy = 0;
private boolean keyState_attack = false;
private boolean keyState_flee = false;
private boolean keyState_endturn = false;
final private int KEY_UNHANDLED = 0; // Default, for unhandled keycodes
final private int KEY_MOVE_UP = 1;
final private int KEY_MOVE_DOWN = 2;
final private int KEY_MOVE_LEFT = 3;
final private int KEY_MOVE_RIGHT = 4;
final private int KEY_MOVE_UP_LEFT = 5;
final private int KEY_MOVE_UP_RIGHT = 6;
final private int KEY_MOVE_DOWN_LEFT = 7;
final private int KEY_MOVE_DOWN_RIGHT = 8;
final private int KEY_ATTACK = 9;
final private int KEY_FLEE = 10;
final private int KEY_END_TURN = 11;
final private int KEY_HERO_INFO = 12;
final private int KEY_TOOLBOX = 13;
private SparseIntArray keyMap = new SparseIntArray(); // Android keycode to internal key event mapping. TODO: Configure via preferences
public InputController(ControllerContext controllers, WorldContext world) { public InputController(ControllerContext controllers, WorldContext world) {
this.controllers = controllers; this.controllers = controllers;
this.world = world; this.world = world;
initializeKeyMap();
}
/* New keyboard handler. Both Key Down and Key Up events handled here to allow conditional behaviours.
On 4-way dpad controllers, cursor keys, and WASD, diagonals are generated by chording two keys.
Single-key diagonals are supported on numeric keypad and 8-way dpads (not seen/tested in the wild).
Because two-key combos initially generate a ordinal movement (one key comes in first), which can
be dangerous in tight spaces, modifiers are provided to "lock" the input until both keys are down.
TODO: Use delay timer to enable chorded diagonals on first move without locking kludge?
*/
// Map key codes to spectic internal actions
// TODO: Move mapping out of code to JSON/XML file, or maybe player prefs
private void initializeKeyMap() {
int key;
// Keys mapping to UP
key = KEY_MOVE_UP;
keyMap.put(KeyEvent.KEYCODE_DPAD_UP, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_8, key);
keyMap.put(KeyEvent.KEYCODE_8, key);
keyMap.put(KeyEvent.KEYCODE_W, key);
// Keys mapping to DOWN
key = KEY_MOVE_DOWN;
keyMap.put(KeyEvent.KEYCODE_DPAD_DOWN, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_2, key);
keyMap.put(KeyEvent.KEYCODE_2, key);
keyMap.put(KeyEvent.KEYCODE_S, key);
// Keys mapping to LEFT
key = KEY_MOVE_LEFT;
keyMap.put(KeyEvent.KEYCODE_DPAD_LEFT, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_4, key);
keyMap.put(KeyEvent.KEYCODE_4, key);
keyMap.put(KeyEvent.KEYCODE_A, key);
// Keys mapping to RIGHT
key = KEY_MOVE_RIGHT;
keyMap.put(KeyEvent.KEYCODE_DPAD_RIGHT, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_6, key);
keyMap.put(KeyEvent.KEYCODE_6, key);
keyMap.put(KeyEvent.KEYCODE_D, key);
// Keys mapping to UP_LEFT
key = KEY_MOVE_UP_LEFT;
keyMap.put(KeyEvent.KEYCODE_DPAD_UP_LEFT, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_7, key);
keyMap.put(KeyEvent.KEYCODE_7, key);
keyMap.put(KeyEvent.KEYCODE_MOVE_HOME, key);
// Keys mapping to UP_RIGHT
key = KEY_MOVE_UP_RIGHT;
keyMap.put(KeyEvent.KEYCODE_DPAD_UP_RIGHT, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_9, key);
keyMap.put(KeyEvent.KEYCODE_9, key);
keyMap.put(KeyEvent.KEYCODE_PAGE_UP, key);
// Keys mapping to DOWN_LEFT
key = KEY_MOVE_DOWN_LEFT;
keyMap.put(KeyEvent.KEYCODE_DPAD_DOWN_LEFT, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_1, key);
keyMap.put(KeyEvent.KEYCODE_1, key);
keyMap.put(KeyEvent.KEYCODE_MOVE_END, key);
// Keys mapping to DOWN_RIGHT
key = KEY_MOVE_DOWN_RIGHT;
keyMap.put(KeyEvent.KEYCODE_DPAD_DOWN_RIGHT, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_3, key);
keyMap.put(KeyEvent.KEYCODE_3, key);
keyMap.put(KeyEvent.KEYCODE_PAGE_DOWN, key);
// Keys mapping to ATTACK
key = KEY_ATTACK;
keyMap.put(KeyEvent.KEYCODE_DPAD_CENTER, key);
keyMap.put(KeyEvent.KEYCODE_BUTTON_A, key);
keyMap.put(KeyEvent.KEYCODE_SPACE, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_5, key);
// Keys mapping to FLEE
key = KEY_FLEE;
keyMap.put(KeyEvent.KEYCODE_BUTTON_X, key);
keyMap.put(KeyEvent.KEYCODE_F, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_ENTER, key);
keyMap.put(KeyEvent.KEYCODE_ENTER, key);
// Keys mapping to END_TURN
key = KEY_END_TURN;
keyMap.put(KeyEvent.KEYCODE_BUTTON_Y, key);
keyMap.put(KeyEvent.KEYCODE_E, key);
keyMap.put(KeyEvent.KEYCODE_FORWARD_DEL, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_DOT, key);
// Keys mapping to HERO_INFO
key = KEY_HERO_INFO;
//keyMap.put(KeyEvent.KEYCODE_BUTTON_SELECT, key);
keyMap.put(KeyEvent.KEYCODE_BUTTON_L1, key);
keyMap.put(KeyEvent.KEYCODE_NUM_LOCK, key);
keyMap.put(KeyEvent.KEYCODE_C, key);
// Keys mapping to TOOLBOX
key = KEY_TOOLBOX;
keyMap.put(KeyEvent.KEYCODE_BUTTON_R1, key);
keyMap.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE, key);
keyMap.put(KeyEvent.KEYCODE_B, key);
} }
public boolean onKeyboardAction(int keyCode) { // Generate game actions based on mapped keys
switch (keyCode) { public boolean onKeyboardAction(Context context, KeyEvent event, boolean acceptInput) {
case KeyEvent.KEYCODE_NUMPAD_8: //L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode());
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_W: if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions
onRelativeMovement(0, -1); boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN);
return true; boolean inihbit = (keyState_attack || keyState_flee); // used to inhibit movement if an action key is held down
case KeyEvent.KEYCODE_NUMPAD_2:
case KeyEvent.KEYCODE_DPAD_DOWN: switch (keyMap.get(event.getKeyCode())) {
case KeyEvent.KEYCODE_S:
onRelativeMovement(0, 1); // Ordinal directional keys - only modify one direction register; registers are combined when
return true; // keys used simultaneously to create synthetic diagonals
case KeyEvent.KEYCODE_NUMPAD_4: case KEY_MOVE_UP:
case KeyEvent.KEYCODE_DPAD_LEFT: keyState_dy = keydown ? -1 : 0;
case KeyEvent.KEYCODE_A: if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
onRelativeMovement(-1, 0); break;
return true; case KEY_MOVE_DOWN:
case KeyEvent.KEYCODE_NUMPAD_6: keyState_dy = keydown ? 1 : 0;
case KeyEvent.KEYCODE_DPAD_RIGHT: if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
case KeyEvent.KEYCODE_D: break;
onRelativeMovement(1, 0); case KEY_MOVE_LEFT:
return true; keyState_dx = keydown ? -1 : 0;
case KeyEvent.KEYCODE_NUMPAD_7: if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
case KeyEvent.KEYCODE_DPAD_UP_LEFT: break;
onRelativeMovement(-1, -1); case KEY_MOVE_RIGHT:
return true; keyState_dx = keydown ? 1 : 0;
case KeyEvent.KEYCODE_NUMPAD_9: if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
case KeyEvent.KEYCODE_DPAD_UP_RIGHT: break;
onRelativeMovement(1, -1);
return true; // Diagonal directional keys. Modify both direction registers, can't be combined
case KeyEvent.KEYCODE_NUMPAD_1: // TODO: store individual key position to allow combinations. May not be worth the trouble.
case KeyEvent.KEYCODE_DPAD_DOWN_LEFT: case KEY_MOVE_UP_LEFT:
onRelativeMovement(-1, 1); keyState_dx = keydown ? -1 : 0;
return true; keyState_dy = keydown ? -1 : 0;
case KeyEvent.KEYCODE_NUMPAD_3: if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT: break;
onRelativeMovement(1, 1); case KEY_MOVE_UP_RIGHT:
return true; keyState_dx = keydown ? 1 : 0;
case KeyEvent.KEYCODE_DPAD_CENTER: keyState_dy = keydown ? -1 : 0;
case KeyEvent.KEYCODE_SPACE: if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
onRelativeMovement(0, 0); break;
return true; case KEY_MOVE_DOWN_LEFT:
default: keyState_dx = keydown ? -1 : 0;
return false; keyState_dy = keydown ? 1 : 0;
if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
break;
case KEY_MOVE_DOWN_RIGHT:
keyState_dx = keydown ? 1 : 0;
keyState_dy = keydown ? 1 : 0;
if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy);
break;
// Special key handling below - some combat/movement stuff done here because it's too
// specific for logic in onRelativeMovement
// "Attack" shortcut - freeze movement to allow chorded direction when key is released.
// if in combat, executes an attack on key release
case KEY_ATTACK:
if (keydown && !keyState_attack) { // key pressed - pause any movement
if(!world.model.uiSelections.isInCombat) controllers.movementController.stopMovement();
} else if (!keydown && keyState_attack) { // key released - execute attack / move in direction
if (acceptInput) onRelativeMovement(keyState_dx, keyState_dy);
}
keyState_attack = keydown;
break;
// "Flee" shortcut. Intitiates flee when pressed. If a direction is held, moves in chosen direction when released
case KEY_FLEE:
if (world.model.uiSelections.isInCombat) {
if (keydown && !keyState_flee) { // button pressed - set flee; movement locked while pressed
if(acceptInput) controllers.combatController.startFlee();
} else if (!keydown && keyState_flee) { // button released - move flee direction, if held
// We need to do a special call because the movement key may already be down, and if the device
// doesn't generate repeat keystrokes, this handler won't get another event
if ((keyState_dx != 0 || keyState_dy != 0) && allowInputInterval()) {
if(acceptInput) controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy);
}
}
}
keyState_flee = keydown;
break;
// "End Turn" shortcut. Flag prevents repeated end turn if key is held down.
case KEY_END_TURN:
if (acceptInput && keydown && !keyState_endturn) {
if (world.model.uiSelections.isInCombat) controllers.combatController.endPlayerTurn();
}
keyState_endturn = keydown;
break;
// "Hero Info" screen shortcut. New activity takes focus, so we don't need to worry about repeats.
case KEY_HERO_INFO:
if (acceptInput && keydown) context.startActivity(new Intent(context, HeroinfoActivity.class));
break;
case KEY_TOOLBOX:
// ??? ToolboxView toolboxview = context.getApplicationContext(). findViewById(R.id.main_toolboxview);
break;
case KEY_UNHANDLED: // Unhandled keycode
return false;
default: // unhandled keymap code entry (should not happen)
L.log("onKeyboardAction(): Unhandled keyMap code constant " + keyMap.get(event.getKeyCode()) + " for keyCode " + event.getKeyCode());
return false;
} }
return true;
} }
public void onRelativeMovement(int dx, int dy) { public void onRelativeMovement(int dx, int dy) {
//L.log("onRelativeMovement(): dx=" + dx + " dy=" + dy + " combat: " + world.model.uiSelections.isInCombat);
if (world.model.uiSelections.isInCombat) { if (world.model.uiSelections.isInCombat) {
if (!allowInputInterval()) return; if (allowInputInterval()) controllers.combatController.executeMoveAttack(dx, dy);
controllers.combatController.executeMoveAttack(dx, dy); } else if (dx == 0 && dy == 0) {
controllers.movementController.stopMovement();
} else { } else {
controllers.movementController.startMovement(dx, dy, null); controllers.movementController.startMovement(dx, dy, null);
} }

View File

@@ -134,20 +134,17 @@ public final class MainView extends SurfaceView
} }
@Override @Override
public boolean onKeyDown(int keyCode, KeyEvent msg) { public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
if (!canAcceptInput()) return true; // onKeyboardAction needs context to start new activities.
// canAcceptInput() checks done in handler so it can preserve keystate even if it does not have focus.
if (inputController.onKeyboardAction(keyCode)) return true; return inputController.onKeyboardAction(getContext(), keyEvent, canAcceptInput()) || super.onKeyDown(keyCode, keyEvent);
else return super.onKeyDown(keyCode, msg);
} }
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent msg) { public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
if (!canAcceptInput()) return true; // Android provides artificial ACTION_UP events when focus changes; we process them to prevent "stuck key" effect after dialogs close
return inputController.onKeyboardAction(getContext(), keyEvent, canAcceptInput()) || super.onKeyUp(keyCode, keyEvent);
inputController.onKeyboardCancel();
return super.onKeyUp(keyCode, msg);
} }
@Override @Override