From edd147e970ddb1501a1e51bdc12bc669245bfe68 Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Sun, 11 Apr 2021 18:54:37 -0700 Subject: [PATCH 1/8] Redo keyboard input handling to allow diagonal movement using either keypad or two-key combos, and add keyboard/dpad shortcuts for Flee, Attack, and End Turn. --- .../controller/InputController.java | 173 +++++++++++++++--- .../gpl/rpg/AndorsTrail/view/MainView.java | 139 +++++++------- 2 files changed, 215 insertions(+), 97 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index 16419f1fa..b39060712 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -8,6 +8,7 @@ import android.view.View.OnLongClickListener; import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.util.Coord; +import com.gpl.rpg.AndorsTrail.util.L; public final class InputController implements OnClickListener, OnLongClickListener{ private final ControllerContext controllers; @@ -19,36 +20,160 @@ public final class InputController implements OnClickListener, OnLongClickListen private long lastTouchEventTime = 0; 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; + public InputController(ControllerContext controllers, WorldContext world) { this.controllers = controllers; this.world = world; } - public boolean onKeyboardAction(int keyCode) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_W: - onRelativeMovement(0, -1); - return true; - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_S: - onRelativeMovement(0, 1); - return true; - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_A: - onRelativeMovement(-1, 0); - return true; - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_D: - onRelativeMovement(1, 0); - return true; - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_SPACE: - onRelativeMovement(0, 0); - return true; - default: - return false; +/* New keyboard handler. Both Key Down and Key Up events handled 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? + */ + public boolean onKeyboardAction(KeyEvent event, boolean canAcceptInput) { +// L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); + + if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions + + // Android provides artificial ACTION_UP events when focus changes; we need them to prevent "stuck key" effect after dialogs close + if (!canAcceptInput && event.getAction() == KeyEvent.ACTION_DOWN) return false; + + boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN); + boolean cancel = false; // used cancel final direction handling if already handled in switch() + + switch (event.getKeyCode()) { + // Ordinal directional keys - only modify one direction register, can be combined when + // used simultaneously to create synthetic diagonals + case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_NUMPAD_8: + case KeyEvent.KEYCODE_8: + case KeyEvent.KEYCODE_W: + keyState_dy = keydown ? -1 : 0; + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_NUMPAD_2: + case KeyEvent.KEYCODE_2: + case KeyEvent.KEYCODE_S: + keyState_dy = keydown ? 1 : 0; + break; + case KeyEvent.KEYCODE_DPAD_LEFT: + case KeyEvent.KEYCODE_NUMPAD_4: + case KeyEvent.KEYCODE_4: + case KeyEvent.KEYCODE_A: + keyState_dx = keydown ? -1 : 0; + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + case KeyEvent.KEYCODE_NUMPAD_6: + case KeyEvent.KEYCODE_6: + case KeyEvent.KEYCODE_D: + keyState_dx = keydown ? 1 : 0; + break; + + // Diagonal directional keys. Modify both direction registers, can't be combined + // TODO: store individual key position to allow combinations. May not be worth the trouble. + case KeyEvent.KEYCODE_DPAD_UP_LEFT: + case KeyEvent.KEYCODE_NUMPAD_7: + case KeyEvent.KEYCODE_7: + case KeyEvent.KEYCODE_MOVE_HOME: + keyState_dx = keydown ? -1 : 0; + keyState_dy = keydown ? -1 : 0; + break; + case KeyEvent.KEYCODE_DPAD_UP_RIGHT: + case KeyEvent.KEYCODE_NUMPAD_9: + case KeyEvent.KEYCODE_9: + case KeyEvent.KEYCODE_PAGE_UP: + keyState_dx = keydown ? 1 : 0; + keyState_dy = keydown ? -1 : 0; + break; + case KeyEvent.KEYCODE_DPAD_DOWN_LEFT: + case KeyEvent.KEYCODE_NUMPAD_1: + case KeyEvent.KEYCODE_1: + case KeyEvent.KEYCODE_MOVE_END: + keyState_dx = keydown ? -1 : 0; + keyState_dy = keydown ? 1 : 0; + break; + case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT: + case KeyEvent.KEYCODE_NUMPAD_3: + case KeyEvent.KEYCODE_3: + case KeyEvent.KEYCODE_PAGE_DOWN: + keyState_dx = keydown ? 1 : 0; + keyState_dy = keydown ? 1 : 0; + break; + + // "Attack" shortcut - freeze movement to allow chorded direction when key is released. + // if in combat, executes an attack on key release + case KeyEvent.KEYCODE_DPAD_CENTER: // Not sure if this is needed + case KeyEvent.KEYCODE_BUTTON_A: // lock movement until released for precise directional move/attack + case KeyEvent.KEYCODE_SPACE: + case KeyEvent.KEYCODE_NUMPAD_5: + if (!keydown && keyState_attack) { // key released - execute attack + if(!world.model.uiSelections.isInCombat) { + controllers.movementController.stopMovement(); + } else if (allowInputInterval()) { + controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); + } + } + keyState_attack = keydown; // prevents movement event below if pressed + cancel = false; //don't cancel, allow pending movement immediately on release via code below + break; + + // "Flee" shortcut. Intitiates flee when pressed. If a direction is held, moves in chosen direction when released + case KeyEvent.KEYCODE_BUTTON_X: + case KeyEvent.KEYCODE_F: + case KeyEvent.KEYCODE_NUMPAD_ENTER: + case KeyEvent.KEYCODE_ENTER: + if (world.model.uiSelections.isInCombat) { + if (keydown && !keyState_flee) { // button pressed + controllers.combatController.startFlee(); + } else if (!keydown && keyState_flee) { // button released - move flee direction, if chosen + // if no movement, executeMoveAttack() will just attack again, so we have to check and do it here + if ((keyState_dx != 0 || keyState_dy != 0) && allowInputInterval()) { + controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); + } + cancel = true; + } + } + keyState_flee = keydown; + break; + + // "End Turn" shortcut. Prevents repeated end turn if key is held down. + case KeyEvent.KEYCODE_BUTTON_Y: + case KeyEvent.KEYCODE_E: + case KeyEvent.KEYCODE_FORWARD_DEL: + case KeyEvent.KEYCODE_NUMPAD_DOT: + if (keydown && !keyState_endturn) { + if (world.model.uiSelections.isInCombat) controllers.combatController.endPlayerTurn(); + } + keyState_endturn = keydown; + cancel = true; + break; + default: // unhandled key + return false; } + + /* process movement if not already handled above and action modifier button is not down. + Modifiers allow input from 4-way controller or keyboard to settle (e.g., combined keys + for diagonals) before action is taken. + */ + if (!cancel && !keyState_attack && !keyState_endturn && !keyState_flee) { + if (world.model.uiSelections.isInCombat) { + if (keydown && allowInputInterval()) controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); + } else if (keyState_dx == 0 && keyState_dy == 0) { + controllers.movementController.stopMovement(); + } else { + controllers.movementController.startMovement(keyState_dx, keyState_dy, null); + } + } + return true; } public void onRelativeMovement(int dx, int dy) { diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index b7e52ee4e..e9e1ca700 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -45,7 +45,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; public final class MainView extends SurfaceView - implements SurfaceHolder.Callback, + implements SurfaceHolder.Callback, PlayerMovementListener, CombatSelectionListener, MonsterSpawnListener, @@ -63,7 +63,7 @@ public final class MainView extends SurfaceView private final Coord mapTopLeft = new Coord(); // Map coords of visible map private CoordRect mapViewArea; // Area in mapcoordinates containing the visible map. topleft == this.topleft private Rect redrawClip = new Rect(); //Area in screen coordinates containing the visible map. - + private final ModelContainer model; private final WorldContext world; private final ControllerContext controllers; @@ -74,7 +74,7 @@ public final class MainView extends SurfaceView private final Paint mPaint = new Paint(); private final CoordRect p1x1 = new CoordRect(new Coord(), new Size(1,1)); private boolean hasSurface = false; - + //DEBUG // private Coord touchedTile = null; // private static Paint touchHighlight = new Paint(); @@ -88,15 +88,15 @@ public final class MainView extends SurfaceView // redrawHighlight.setStrokeWidth(0f); // redrawHighlight.setStyle(Style.STROKE); // } - + private PredefinedMap currentMap; private LayeredTileMap currentTileMap; private TileCollection tiles; - + private final Coord playerPosition = new Coord(); private Size surfaceSize; private boolean redrawNextTick = false; - + private boolean scrolling = false; private Coord scrollVector; private long scrollStartTime; @@ -120,39 +120,32 @@ public final class MainView extends SurfaceView this.preferences = app.getPreferences(); alternateColorFilterPaint.setStyle(Style.FILL); - + holder.addCallback(this); setFocusable(true); requestFocus(); setOnClickListener(this.inputController); setOnLongClickListener(this.inputController); - - + + } @Override - public boolean onKeyDown(int keyCode, KeyEvent msg) { - if (!canAcceptInput()) return true; - - if (inputController.onKeyboardAction(keyCode)) return true; - else return super.onKeyDown(keyCode, msg); + public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { + return inputController.onKeyboardAction(keyEvent, canAcceptInput()) || super.onKeyDown(keyCode, keyEvent); } @Override - public boolean onKeyUp(int keyCode, KeyEvent msg) { - if (!canAcceptInput()) return true; - - inputController.onKeyboardCancel(); - - return super.onKeyUp(keyCode, msg); + public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { + return inputController.onKeyboardAction(keyEvent, canAcceptInput()) || super.onKeyUp(keyCode, keyEvent); } @Override public void surfaceChanged(SurfaceHolder sh, int format, int w, int h) { if (w <= 0 || h <= 0) return; - + this.scale = world.tileManager.scale; this.mPaint.setFilterBitmap(scale != 1); this.scaledTileSize = world.tileManager.viewTileSize; @@ -161,12 +154,12 @@ public final class MainView extends SurfaceView this.screenSizeTileCount = new Size( (int) Math.floor(getWidth() / scaledTileSize) ,(int) Math.floor(getHeight() / scaledTileSize) - ); - + ); + if (sh.getSurfaceFrame().right != surfaceSize.width || sh.getSurfaceFrame().bottom != surfaceSize.height) { sh.setFixedSize(surfaceSize.width, surfaceSize.height); } - + if (model.currentMaps.map != null) { onPlayerEnteredNewMap(model.currentMaps.map, model.player.position); } else { @@ -190,22 +183,22 @@ public final class MainView extends SurfaceView if (!canAcceptInput()) return true; switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - final int tile_x = (int) Math.floor(((int)event.getX() - screenOffset.x * scale) / scaledTileSize) + mapTopLeft.x; - final int tile_y = (int) Math.floor(((int)event.getY() - screenOffset.y * scale) / scaledTileSize) + mapTopLeft.y; + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + final int tile_x = (int) Math.floor(((int)event.getX() - screenOffset.x * scale) / scaledTileSize) + mapTopLeft.x; + final int tile_y = (int) Math.floor(((int)event.getY() - screenOffset.y * scale) / scaledTileSize) + mapTopLeft.y; // touchedTile = new Coord(tile_x, tile_y); - if (inputController.onTouchedTile(tile_x, tile_y)) return true; - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_OUTSIDE: - inputController.onTouchCancel(); - break; + if (inputController.onTouchedTile(tile_x, tile_y)) return true; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_OUTSIDE: + inputController.onTouchCancel(); + break; } return super.onTouchEvent(event); } - + private boolean canAcceptInput() { if (!model.uiSelections.isMainActivityVisible) return false; if (!hasSurface) return false; @@ -241,14 +234,14 @@ public final class MainView extends SurfaceView private void redrawArea_(CoordRect area, final VisualEffectAnimation effect, int tileID, int textYOffset) { if (!hasSurface) return; - + if (!currentMap.intersects(area)) return; if (!mapViewArea.intersects(area)) return; if (shouldRedrawEverything()) { area = mapViewArea; } - + calculateRedrawRect(area); redrawRect.intersect(redrawClip); Canvas c = null; @@ -264,7 +257,7 @@ public final class MainView extends SurfaceView if (area == mapViewArea) { area = adaptAreaToScrolling(area); } - + synchronized (holder) { synchronized (tiles) { int xScroll = 0; int yScroll = 0; @@ -302,7 +295,7 @@ public final class MainView extends SurfaceView if (c != null) holder.unlockCanvasAndPost(c); } } - + private boolean isRedrawRectWholeScreen(Rect redrawRect) { // if (redrawRect.width() < mapViewArea.size.width * scaledTileSize) return false; // if (redrawRect.height() < mapViewArea.size.height * scaledTileSize) return false; @@ -337,7 +330,7 @@ public final class MainView extends SurfaceView } private CoordRect adaptAreaToScrolling(final CoordRect area) { - + if (!scrolling || scrollVector == null) return area; int x, y, w, h; @@ -355,10 +348,10 @@ public final class MainView extends SurfaceView y = area.topLeft.y; h = area.size.height - scrollVector.y; } - CoordRect result = new CoordRect(new Coord(x, y), new Size(w, h)); + CoordRect result = new CoordRect(new Coord(x, y), new Size(w, h)); return result; } - + private void calculateRedrawRect(final CoordRect area) { worldCoordsToScreenCords(area, redrawRect); } @@ -368,13 +361,13 @@ public final class MainView extends SurfaceView // destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * scaledTileSize; // destScreenRect.right = destScreenRect.left + worldArea.size.width * scaledTileSize; // destScreenRect.bottom = destScreenRect.top + worldArea.size.height * scaledTileSize; - + destScreenRect.left = screenOffset.x + (worldArea.topLeft.x - mapViewArea.topLeft.x) * tileSize; destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * tileSize; destScreenRect.right = destScreenRect.left + worldArea.size.width * tileSize; destScreenRect.bottom = destScreenRect.top + worldArea.size.height * tileSize; } - + // private void worldCoordsToBitmapCoords(final CoordRect worldArea, Rect dstBitmapArea) { // dstBitmapArea.left = worldArea.topLeft.x * tileSize; // dstBitmapArea.top = worldArea.topLeft.y * tileSize; @@ -391,17 +384,17 @@ public final class MainView extends SurfaceView applyAlternateFilter(canvas, area); } } - + private void doDrawRect_Ground(Canvas canvas, CoordRect area) { drawMapLayer(canvas, area, currentTileMap.currentLayout.layerGround); tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); } - + private void doDrawRect_Objects(Canvas canvas, CoordRect area) { // if (!tryDrawMapBitmap(canvas, area, objectsBitmap)) { // tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); // } - + for (BloodSplatter splatter : currentMap.splatters) { drawFromMapPosition(canvas, area, splatter.position, splatter.iconID); } @@ -435,11 +428,11 @@ public final class MainView extends SurfaceView } } } - + private void doDrawRect_Above(Canvas canvas, CoordRect area) { tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove); tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerTop); - + if (model.uiSelections.selectedPosition != null) { if (model.uiSelections.selectedMonster != null) { drawFromMapPosition(canvas, area, model.uiSelections.selectedPosition, TileManager.iconID_attackselect); @@ -471,13 +464,13 @@ public final class MainView extends SurfaceView } } } - + private void applyAlternateFilter(Canvas canvas, CoordRect area) { canvas.drawRect(canvas.getClipBounds(), alternateColorFilterPaint); - + } - + private void drawFromMapPosition(Canvas canvas, final CoordRect area, final Coord p, final int tile) { if (!area.contains(p)) return; _drawFromMapPosition(canvas, area, p.x, p.y, tile); @@ -491,7 +484,7 @@ public final class MainView extends SurfaceView y -= mapViewArea.topLeft.y; // if ( (x >= 0 && x < mapViewArea.size.width) // && (y >= 0 && y < mapViewArea.size.height)) { - tiles.drawTile(canvas, tile, x * tileSize, y * tileSize, mPaint); + tiles.drawTile(canvas, tile, x * tileSize, y * tileSize, mPaint); // } } @@ -512,7 +505,7 @@ public final class MainView extends SurfaceView Size visibleNumberOfTiles = new Size( Math.min(screenSizeTileCount.width, currentMap.size.width) ,Math.min(screenSizeTileCount.height, currentMap.size.height) - ); + ); mapViewArea = new CoordRect(mapTopLeft, visibleNumberOfTiles); updateClip(); @@ -520,18 +513,18 @@ public final class MainView extends SurfaceView // (surfaceSize.width - scaledTileSize * visibleNumberOfTiles.width) / 2 // ,(surfaceSize.height - scaledTileSize * visibleNumberOfTiles.height) / 2 // ); - + screenOffset.set( (surfaceSize.width - tileSize * visibleNumberOfTiles.width) / 2 ,(surfaceSize.height - tileSize * visibleNumberOfTiles.height) / 2 - ); + ); useAlternateColorFilterPaint = currentTileMap.setColorFilter(this.mPaint, this.alternateColorFilterPaint, preferences.highQualityFilters); } // touchedTile = null; - + clearCanvas(); recalculateMapTopLeft(model.player.position, false); @@ -564,22 +557,22 @@ public final class MainView extends SurfaceView } } } - + private void updateClip() { worldCoordsToScreenCords(mapViewArea, redrawClip); } - + public static final class ScrollAnimationHandler extends Handler implements Runnable { private static final int FRAME_DURATION = 40; private final WeakReference view; - + public ScrollAnimationHandler(MainView view) { this.view = new WeakReference(view); } - + @Override public void run() { MainView v = view.get(); @@ -613,18 +606,18 @@ public final class MainView extends SurfaceView postDelayed(this, 0); } } - - + + public static final class SpriteMoveAnimationHandler extends Handler implements Runnable { private static final int FRAME_DURATION = 40; private final WeakReference view; private boolean stop = true; - + public SpriteMoveAnimationHandler(MainView view) { this.view = new WeakReference(view); } - + @Override public void run() { if (!stop) postDelayed(this, FRAME_DURATION); @@ -656,14 +649,14 @@ public final class MainView extends SurfaceView if (v.controllers.preferences.enableUiAnimations) postDelayed(this, 0); } } - + public void stop() { stop = true; } } - - - + + + @Override public void onPlayerMoved(PredefinedMap map, Coord newPosition, Coord previousPosition) { if (map != currentMap) return; @@ -794,19 +787,19 @@ public final class MainView extends SurfaceView movingSpritesRedrawTick.start(); } } - + @Override public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { //redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), animation); } - + @Override public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { if (animation.map != currentMap) return; movingSprites--; redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), RedrawAreaDebugReason.EffectCompleted); } - + @Override public void onAsyncAreaUpdate(CoordRect area) { redrawArea(area, RedrawAreaDebugReason.AsyncRequest); From a3c632947b9e147cd60f9ed2247cc4ca065b517c Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Sun, 11 Apr 2021 19:34:14 -0700 Subject: [PATCH 2/8] Fix whitespace changes, tweak canAcceptInput() handling. --- .../controller/InputController.java | 5 +-- .../gpl/rpg/AndorsTrail/view/MainView.java | 38 ++++++++++--------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index b39060712..055e171e8 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -39,14 +39,11 @@ public final class InputController implements OnClickListener, OnLongClickListen 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? */ - public boolean onKeyboardAction(KeyEvent event, boolean canAcceptInput) { + public boolean onKeyboardAction(KeyEvent event) { // L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions - // Android provides artificial ACTION_UP events when focus changes; we need them to prevent "stuck key" effect after dialogs close - if (!canAcceptInput && event.getAction() == KeyEvent.ACTION_DOWN) return false; - boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN); boolean cancel = false; // used cancel final direction handling if already handled in switch() diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index e9e1ca700..6420c4f8d 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -45,7 +45,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; public final class MainView extends SurfaceView - implements SurfaceHolder.Callback, + implements SurfaceHolder.Callback, PlayerMovementListener, CombatSelectionListener, MonsterSpawnListener, @@ -133,12 +133,14 @@ public final class MainView extends SurfaceView @Override public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { - return inputController.onKeyboardAction(keyEvent, canAcceptInput()) || super.onKeyDown(keyCode, keyEvent); + return !canAcceptInput() || inputController.onKeyboardAction(keyEvent) || super.onKeyDown(keyCode, keyEvent); } @Override public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { - return inputController.onKeyboardAction(keyEvent, canAcceptInput()) || super.onKeyUp(keyCode, keyEvent); + // Android provides artificial ACTION_UP events when focus changes; we process them to prevent "stuck key" effect after dialogs close + // (so don't check canAcceptInput() here) + return inputController.onKeyboardAction(keyEvent) || super.onKeyUp(keyCode, keyEvent); } @Override @@ -154,7 +156,7 @@ public final class MainView extends SurfaceView this.screenSizeTileCount = new Size( (int) Math.floor(getWidth() / scaledTileSize) ,(int) Math.floor(getHeight() / scaledTileSize) - ); + ); if (sh.getSurfaceFrame().right != surfaceSize.width || sh.getSurfaceFrame().bottom != surfaceSize.height) { sh.setFixedSize(surfaceSize.width, surfaceSize.height); @@ -183,18 +185,18 @@ public final class MainView extends SurfaceView if (!canAcceptInput()) return true; switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_MOVE: - final int tile_x = (int) Math.floor(((int)event.getX() - screenOffset.x * scale) / scaledTileSize) + mapTopLeft.x; - final int tile_y = (int) Math.floor(((int)event.getY() - screenOffset.y * scale) / scaledTileSize) + mapTopLeft.y; + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + final int tile_x = (int) Math.floor(((int)event.getX() - screenOffset.x * scale) / scaledTileSize) + mapTopLeft.x; + final int tile_y = (int) Math.floor(((int)event.getY() - screenOffset.y * scale) / scaledTileSize) + mapTopLeft.y; // touchedTile = new Coord(tile_x, tile_y); - if (inputController.onTouchedTile(tile_x, tile_y)) return true; - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_OUTSIDE: - inputController.onTouchCancel(); - break; + if (inputController.onTouchedTile(tile_x, tile_y)) return true; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_OUTSIDE: + inputController.onTouchCancel(); + break; } return super.onTouchEvent(event); } @@ -484,7 +486,7 @@ public final class MainView extends SurfaceView y -= mapViewArea.topLeft.y; // if ( (x >= 0 && x < mapViewArea.size.width) // && (y >= 0 && y < mapViewArea.size.height)) { - tiles.drawTile(canvas, tile, x * tileSize, y * tileSize, mPaint); + tiles.drawTile(canvas, tile, x * tileSize, y * tileSize, mPaint); // } } @@ -505,7 +507,7 @@ public final class MainView extends SurfaceView Size visibleNumberOfTiles = new Size( Math.min(screenSizeTileCount.width, currentMap.size.width) ,Math.min(screenSizeTileCount.height, currentMap.size.height) - ); + ); mapViewArea = new CoordRect(mapTopLeft, visibleNumberOfTiles); updateClip(); @@ -518,7 +520,7 @@ public final class MainView extends SurfaceView screenOffset.set( (surfaceSize.width - tileSize * visibleNumberOfTiles.width) / 2 ,(surfaceSize.height - tileSize * visibleNumberOfTiles.height) / 2 - ); + ); useAlternateColorFilterPaint = currentTileMap.setColorFilter(this.mPaint, this.alternateColorFilterPaint, preferences.highQualityFilters); } From c24c8d1444373cd708b28e9c23c0b89fd71b2ecb Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Sun, 11 Apr 2021 19:46:12 -0700 Subject: [PATCH 3/8] Fix to attack shortcut logic --- .../rpg/AndorsTrail/controller/InputController.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index 055e171e8..e57cc7a71 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -112,12 +112,10 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_BUTTON_A: // lock movement until released for precise directional move/attack case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_NUMPAD_5: - if (!keydown && keyState_attack) { // key released - execute attack - if(!world.model.uiSelections.isInCombat) { - controllers.movementController.stopMovement(); - } else if (allowInputInterval()) { - controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); - } + if (keydown && !keyState_attack) { // key pressed - pause movement + if(!world.model.uiSelections.isInCombat) controllers.movementController.stopMovement(); + } else if (!keydown && keyState_attack) { // key released - execute attack / move in direction + if (allowInputInterval()) controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); } keyState_attack = keydown; // prevents movement event below if pressed cancel = false; //don't cancel, allow pending movement immediately on release via code below From bd17ea8ceeaf3c7e138206197059f38e9f3e7d0d Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Sun, 11 Apr 2021 21:48:35 -0700 Subject: [PATCH 4/8] Fixed whitespace. Again. D*mn editor. --- .../gpl/rpg/AndorsTrail/view/MainView.java | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index 6420c4f8d..00b7e2dd9 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -63,7 +63,7 @@ public final class MainView extends SurfaceView private final Coord mapTopLeft = new Coord(); // Map coords of visible map private CoordRect mapViewArea; // Area in mapcoordinates containing the visible map. topleft == this.topleft private Rect redrawClip = new Rect(); //Area in screen coordinates containing the visible map. - + private final ModelContainer model; private final WorldContext world; private final ControllerContext controllers; @@ -74,7 +74,7 @@ public final class MainView extends SurfaceView private final Paint mPaint = new Paint(); private final CoordRect p1x1 = new CoordRect(new Coord(), new Size(1,1)); private boolean hasSurface = false; - + //DEBUG // private Coord touchedTile = null; // private static Paint touchHighlight = new Paint(); @@ -88,15 +88,15 @@ public final class MainView extends SurfaceView // redrawHighlight.setStrokeWidth(0f); // redrawHighlight.setStyle(Style.STROKE); // } - + private PredefinedMap currentMap; private LayeredTileMap currentTileMap; private TileCollection tiles; - + private final Coord playerPosition = new Coord(); private Size surfaceSize; private boolean redrawNextTick = false; - + private boolean scrolling = false; private Coord scrollVector; private long scrollStartTime; @@ -120,15 +120,15 @@ public final class MainView extends SurfaceView this.preferences = app.getPreferences(); alternateColorFilterPaint.setStyle(Style.FILL); - + holder.addCallback(this); setFocusable(true); requestFocus(); setOnClickListener(this.inputController); setOnLongClickListener(this.inputController); - - + + } @Override @@ -147,7 +147,7 @@ public final class MainView extends SurfaceView public void surfaceChanged(SurfaceHolder sh, int format, int w, int h) { if (w <= 0 || h <= 0) return; - + this.scale = world.tileManager.scale; this.mPaint.setFilterBitmap(scale != 1); this.scaledTileSize = world.tileManager.viewTileSize; @@ -157,11 +157,11 @@ public final class MainView extends SurfaceView (int) Math.floor(getWidth() / scaledTileSize) ,(int) Math.floor(getHeight() / scaledTileSize) ); - + if (sh.getSurfaceFrame().right != surfaceSize.width || sh.getSurfaceFrame().bottom != surfaceSize.height) { sh.setFixedSize(surfaceSize.width, surfaceSize.height); } - + if (model.currentMaps.map != null) { onPlayerEnteredNewMap(model.currentMaps.map, model.player.position); } else { @@ -200,7 +200,7 @@ public final class MainView extends SurfaceView } return super.onTouchEvent(event); } - + private boolean canAcceptInput() { if (!model.uiSelections.isMainActivityVisible) return false; if (!hasSurface) return false; @@ -236,14 +236,14 @@ public final class MainView extends SurfaceView private void redrawArea_(CoordRect area, final VisualEffectAnimation effect, int tileID, int textYOffset) { if (!hasSurface) return; - + if (!currentMap.intersects(area)) return; if (!mapViewArea.intersects(area)) return; if (shouldRedrawEverything()) { area = mapViewArea; } - + calculateRedrawRect(area); redrawRect.intersect(redrawClip); Canvas c = null; @@ -259,7 +259,7 @@ public final class MainView extends SurfaceView if (area == mapViewArea) { area = adaptAreaToScrolling(area); } - + synchronized (holder) { synchronized (tiles) { int xScroll = 0; int yScroll = 0; @@ -297,7 +297,7 @@ public final class MainView extends SurfaceView if (c != null) holder.unlockCanvasAndPost(c); } } - + private boolean isRedrawRectWholeScreen(Rect redrawRect) { // if (redrawRect.width() < mapViewArea.size.width * scaledTileSize) return false; // if (redrawRect.height() < mapViewArea.size.height * scaledTileSize) return false; @@ -332,7 +332,7 @@ public final class MainView extends SurfaceView } private CoordRect adaptAreaToScrolling(final CoordRect area) { - + if (!scrolling || scrollVector == null) return area; int x, y, w, h; @@ -350,10 +350,10 @@ public final class MainView extends SurfaceView y = area.topLeft.y; h = area.size.height - scrollVector.y; } - CoordRect result = new CoordRect(new Coord(x, y), new Size(w, h)); + CoordRect result = new CoordRect(new Coord(x, y), new Size(w, h)); return result; } - + private void calculateRedrawRect(final CoordRect area) { worldCoordsToScreenCords(area, redrawRect); } @@ -363,13 +363,13 @@ public final class MainView extends SurfaceView // destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * scaledTileSize; // destScreenRect.right = destScreenRect.left + worldArea.size.width * scaledTileSize; // destScreenRect.bottom = destScreenRect.top + worldArea.size.height * scaledTileSize; - + destScreenRect.left = screenOffset.x + (worldArea.topLeft.x - mapViewArea.topLeft.x) * tileSize; destScreenRect.top = screenOffset.y + (worldArea.topLeft.y - mapViewArea.topLeft.y) * tileSize; destScreenRect.right = destScreenRect.left + worldArea.size.width * tileSize; destScreenRect.bottom = destScreenRect.top + worldArea.size.height * tileSize; } - + // private void worldCoordsToBitmapCoords(final CoordRect worldArea, Rect dstBitmapArea) { // dstBitmapArea.left = worldArea.topLeft.x * tileSize; // dstBitmapArea.top = worldArea.topLeft.y * tileSize; @@ -386,17 +386,17 @@ public final class MainView extends SurfaceView applyAlternateFilter(canvas, area); } } - + private void doDrawRect_Ground(Canvas canvas, CoordRect area) { drawMapLayer(canvas, area, currentTileMap.currentLayout.layerGround); tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); } - + private void doDrawRect_Objects(Canvas canvas, CoordRect area) { // if (!tryDrawMapBitmap(canvas, area, objectsBitmap)) { // tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerObjects); // } - + for (BloodSplatter splatter : currentMap.splatters) { drawFromMapPosition(canvas, area, splatter.position, splatter.iconID); } @@ -430,11 +430,11 @@ public final class MainView extends SurfaceView } } } - + private void doDrawRect_Above(Canvas canvas, CoordRect area) { tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerAbove); tryDrawMapLayer(canvas, area, currentTileMap.currentLayout.layerTop); - + if (model.uiSelections.selectedPosition != null) { if (model.uiSelections.selectedMonster != null) { drawFromMapPosition(canvas, area, model.uiSelections.selectedPosition, TileManager.iconID_attackselect); @@ -466,13 +466,13 @@ public final class MainView extends SurfaceView } } } - + private void applyAlternateFilter(Canvas canvas, CoordRect area) { canvas.drawRect(canvas.getClipBounds(), alternateColorFilterPaint); - + } - + private void drawFromMapPosition(Canvas canvas, final CoordRect area, final Coord p, final int tile) { if (!area.contains(p)) return; _drawFromMapPosition(canvas, area, p.x, p.y, tile); @@ -515,7 +515,7 @@ public final class MainView extends SurfaceView // (surfaceSize.width - scaledTileSize * visibleNumberOfTiles.width) / 2 // ,(surfaceSize.height - scaledTileSize * visibleNumberOfTiles.height) / 2 // ); - + screenOffset.set( (surfaceSize.width - tileSize * visibleNumberOfTiles.width) / 2 @@ -526,7 +526,7 @@ public final class MainView extends SurfaceView } // touchedTile = null; - + clearCanvas(); recalculateMapTopLeft(model.player.position, false); @@ -559,22 +559,22 @@ public final class MainView extends SurfaceView } } } - + private void updateClip() { worldCoordsToScreenCords(mapViewArea, redrawClip); } - + public static final class ScrollAnimationHandler extends Handler implements Runnable { private static final int FRAME_DURATION = 40; private final WeakReference view; - + public ScrollAnimationHandler(MainView view) { this.view = new WeakReference(view); } - + @Override public void run() { MainView v = view.get(); @@ -608,18 +608,18 @@ public final class MainView extends SurfaceView postDelayed(this, 0); } } - - + + public static final class SpriteMoveAnimationHandler extends Handler implements Runnable { private static final int FRAME_DURATION = 40; private final WeakReference view; private boolean stop = true; - + public SpriteMoveAnimationHandler(MainView view) { this.view = new WeakReference(view); } - + @Override public void run() { if (!stop) postDelayed(this, FRAME_DURATION); @@ -651,14 +651,14 @@ public final class MainView extends SurfaceView if (v.controllers.preferences.enableUiAnimations) postDelayed(this, 0); } } - + public void stop() { stop = true; } } - - - + + + @Override public void onPlayerMoved(PredefinedMap map, Coord newPosition, Coord previousPosition) { if (map != currentMap) return; @@ -789,19 +789,19 @@ public final class MainView extends SurfaceView movingSpritesRedrawTick.start(); } } - + @Override public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { //redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), animation); } - + @Override public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { if (animation.map != currentMap) return; movingSprites--; redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), RedrawAreaDebugReason.EffectCompleted); } - + @Override public void onAsyncAreaUpdate(CoordRect area) { redrawArea(area, RedrawAreaDebugReason.AsyncRequest); From 2649471c5f7338b5737972f9396e6ce74865c500 Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Tue, 13 Apr 2021 00:08:16 -0700 Subject: [PATCH 5/8] Added shortcut to Hero Info activity, tweaked some button state logic a bit, added comments --- .../controller/InputController.java | 54 +++++++++++-------- .../gpl/rpg/AndorsTrail/view/MainView.java | 27 ++++++---- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index e57cc7a71..def9fc11c 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -1,10 +1,13 @@ package com.gpl.rpg.AndorsTrail.controller; +import android.content.Context; +import android.content.Intent; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; 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.WorldContext; import com.gpl.rpg.AndorsTrail.util.Coord; @@ -39,15 +42,17 @@ public final class InputController implements OnClickListener, OnLongClickListen 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? */ - public boolean onKeyboardAction(KeyEvent event) { + public boolean onKeyboardAction(Context context, KeyEvent event) { // L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN); - boolean cancel = false; // used cancel final direction handling if already handled in switch() + boolean inihbit = (keyState_attack || keyState_endturn || keyState_flee); // used to inhibit movement if an action key is held down switch (event.getKeyCode()) { + // Basic movement handled first + // Ordinal directional keys - only modify one direction register, can be combined when // used simultaneously to create synthetic diagonals case KeyEvent.KEYCODE_DPAD_UP: @@ -55,24 +60,28 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_8: case KeyEvent.KEYCODE_W: keyState_dy = keydown ? -1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_NUMPAD_2: case KeyEvent.KEYCODE_2: case KeyEvent.KEYCODE_S: keyState_dy = keydown ? 1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_NUMPAD_4: case KeyEvent.KEYCODE_4: case KeyEvent.KEYCODE_A: keyState_dx = keydown ? -1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_NUMPAD_6: case KeyEvent.KEYCODE_6: case KeyEvent.KEYCODE_D: keyState_dx = keydown ? 1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; // Diagonal directional keys. Modify both direction registers, can't be combined @@ -83,6 +92,7 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_MOVE_HOME: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? -1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KeyEvent.KEYCODE_DPAD_UP_RIGHT: case KeyEvent.KEYCODE_NUMPAD_9: @@ -90,6 +100,7 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_PAGE_UP: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? -1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KeyEvent.KEYCODE_DPAD_DOWN_LEFT: case KeyEvent.KEYCODE_NUMPAD_1: @@ -97,6 +108,7 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_MOVE_END: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? 1 : 0; + if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT: case KeyEvent.KEYCODE_NUMPAD_3: @@ -104,8 +116,12 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_PAGE_DOWN: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? 1 : 0; + if (!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 KeyEvent.KEYCODE_DPAD_CENTER: // Not sure if this is needed @@ -115,10 +131,9 @@ public final class InputController implements OnClickListener, OnLongClickListen if (keydown && !keyState_attack) { // key pressed - pause movement if(!world.model.uiSelections.isInCombat) controllers.movementController.stopMovement(); } else if (!keydown && keyState_attack) { // key released - execute attack / move in direction - if (allowInputInterval()) controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); + onRelativeMovement(keyState_dx, keyState_dy); } - keyState_attack = keydown; // prevents movement event below if pressed - cancel = false; //don't cancel, allow pending movement immediately on release via code below + keyState_attack = keydown; break; // "Flee" shortcut. Intitiates flee when pressed. If a direction is held, moves in chosen direction when released @@ -127,20 +142,19 @@ public final class InputController implements OnClickListener, OnLongClickListen case KeyEvent.KEYCODE_NUMPAD_ENTER: case KeyEvent.KEYCODE_ENTER: if (world.model.uiSelections.isInCombat) { - if (keydown && !keyState_flee) { // button pressed + if (keydown && !keyState_flee) { // button pressed - set flee; movement locked while pressed controllers.combatController.startFlee(); } else if (!keydown && keyState_flee) { // button released - move flee direction, if chosen - // if no movement, executeMoveAttack() will just attack again, so we have to check and do it here + // if no movement, executeMoveAttack() in will just attack again, so we have to check and do it here if ((keyState_dx != 0 || keyState_dy != 0) && allowInputInterval()) { controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); } - cancel = true; } } keyState_flee = keydown; break; - // "End Turn" shortcut. Prevents repeated end turn if key is held down. + // "End Turn" shortcut. Flag prevents repeated end turn if key is held down. case KeyEvent.KEYCODE_BUTTON_Y: case KeyEvent.KEYCODE_E: case KeyEvent.KEYCODE_FORWARD_DEL: @@ -149,25 +163,17 @@ public final class InputController implements OnClickListener, OnLongClickListen if (world.model.uiSelections.isInCombat) controllers.combatController.endPlayerTurn(); } keyState_endturn = keydown; - cancel = true; + break; + + // "Hero Info" screen shortcut. New activity takes focus, so we don't need to worry about repeats. + case KeyEvent.KEYCODE_BUTTON_SELECT: + case KeyEvent.KEYCODE_C: + if (keydown) context.startActivity(new Intent(context, HeroinfoActivity.class)); break; default: // unhandled key return false; } - /* process movement if not already handled above and action modifier button is not down. - Modifiers allow input from 4-way controller or keyboard to settle (e.g., combined keys - for diagonals) before action is taken. - */ - if (!cancel && !keyState_attack && !keyState_endturn && !keyState_flee) { - if (world.model.uiSelections.isInCombat) { - if (keydown && allowInputInterval()) controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); - } else if (keyState_dx == 0 && keyState_dy == 0) { - controllers.movementController.stopMovement(); - } else { - controllers.movementController.startMovement(keyState_dx, keyState_dy, null); - } - } return true; } @@ -175,6 +181,8 @@ public final class InputController implements OnClickListener, OnLongClickListen if (world.model.uiSelections.isInCombat) { if (!allowInputInterval()) return; controllers.combatController.executeMoveAttack(dx, dy); + } else if (keyState_dx == 0 && keyState_dy == 0) { + controllers.movementController.stopMovement(); } else { controllers.movementController.startMovement(dx, dy, null); } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index 00b7e2dd9..e214898e0 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -4,6 +4,8 @@ import java.lang.ref.WeakReference; import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; +import com.gpl.rpg.AndorsTrail.R; +import com.gpl.rpg.AndorsTrail.activity.HeroinfoActivity; import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.controller.Constants; @@ -32,17 +34,20 @@ import com.gpl.rpg.AndorsTrail.util.CoordRect; import com.gpl.rpg.AndorsTrail.util.Size; import android.content.Context; +import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; +import android.os.Build; import android.os.Handler; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.widget.ImageButton; public final class MainView extends SurfaceView implements SurfaceHolder.Callback, @@ -133,14 +138,18 @@ public final class MainView extends SurfaceView @Override public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { - return !canAcceptInput() || inputController.onKeyboardAction(keyEvent) || super.onKeyDown(keyCode, keyEvent); + if (!canAcceptInput()) return false; + + // onKeyboardAction needs context to start new activities. + return inputController.onKeyboardAction(getContext(), keyEvent) || super.onKeyDown(keyCode, keyEvent); + } @Override public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { // Android provides artificial ACTION_UP events when focus changes; we process them to prevent "stuck key" effect after dialogs close // (so don't check canAcceptInput() here) - return inputController.onKeyboardAction(keyEvent) || super.onKeyUp(keyCode, keyEvent); + return inputController.onKeyboardAction(getContext(), keyEvent) || super.onKeyUp(keyCode, keyEvent); } @Override @@ -651,14 +660,14 @@ public final class MainView extends SurfaceView if (v.controllers.preferences.enableUiAnimations) postDelayed(this, 0); } } - + public void stop() { stop = true; } } - - - + + + @Override public void onPlayerMoved(PredefinedMap map, Coord newPosition, Coord previousPosition) { if (map != currentMap) return; @@ -789,19 +798,19 @@ public final class MainView extends SurfaceView movingSpritesRedrawTick.start(); } } - + @Override public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { //redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), animation); } - + @Override public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { if (animation.map != currentMap) return; movingSprites--; redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), RedrawAreaDebugReason.EffectCompleted); } - + @Override public void onAsyncAreaUpdate(CoordRect area) { redrawArea(area, RedrawAreaDebugReason.AsyncRequest); From a4e95b1c9848b2ce8aa7b88fa7985d74434e5433 Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Tue, 13 Apr 2021 22:50:59 -0700 Subject: [PATCH 6/8] Move keycode mapping to keyMap array instead of switch case entries. --- .../controller/InputController.java | 180 +++++++++++++----- .../gpl/rpg/AndorsTrail/view/MainView.java | 19 +- 2 files changed, 137 insertions(+), 62 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index def9fc11c..9522d693b 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -28,12 +28,30 @@ public final class InputController implements OnClickListener, OnLongClickListen 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_UP = 1; + final private int KEY_DOWN = 2; + final private int KEY_LEFT = 3; + final private int KEY_RIGHT = 4; + final private int KEY_UP_LEFT = 5; + final private int KEY_UP_RIGHT = 6; + final private int KEY_DOWN_LEFT = 7; + final private int KEY_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; + + private char keyMap[]; // Android keycode to internal key event mapping. TODO: Configure via preferences public InputController(ControllerContext controllers, WorldContext world) { this.controllers = controllers; this.world = world; + initializeKeyMap(); + } - + /* New keyboard handler. Both Key Down and Key Up events handled 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). @@ -42,78 +60,148 @@ public final class InputController implements OnClickListener, OnLongClickListen 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? */ + + // Map key codes to spectic internal actions + // TODO: Move mapping out of code, to JSON or XML file, or maybe eventually user preferences + private void initializeKeyMap() { + char key; + + keyMap = new char[1024]; // Should be a long time before android exceeds 1024 keycodes + + // Keys mapping to UP + key = KEY_UP; + keyMap[KeyEvent.KEYCODE_DPAD_UP] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_8] = key; + keyMap[KeyEvent.KEYCODE_8] = key; + keyMap[KeyEvent.KEYCODE_W] = key; + + // Keys mapping to DOWN + key = KEY_DOWN; + keyMap[KeyEvent.KEYCODE_DPAD_DOWN] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_2] = key; + keyMap[KeyEvent.KEYCODE_2] = key; + keyMap[KeyEvent.KEYCODE_S] = key; + + // Keys mapping to LEFT + key = KEY_LEFT; + keyMap[KeyEvent.KEYCODE_DPAD_LEFT] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_4] = key; + keyMap[KeyEvent.KEYCODE_4] = key; + keyMap[KeyEvent.KEYCODE_A] = key; + + // Keys mapping to RIGHT + key = KEY_RIGHT; + keyMap[KeyEvent.KEYCODE_DPAD_RIGHT] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_6] = key; + keyMap[KeyEvent.KEYCODE_6] = key; + keyMap[KeyEvent.KEYCODE_D] = key; + + // Keys mapping to UP_LEFT + key = KEY_UP_LEFT; + keyMap[KeyEvent.KEYCODE_DPAD_UP_LEFT] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_7] = key; + keyMap[KeyEvent.KEYCODE_7] = key; + keyMap[KeyEvent.KEYCODE_MOVE_HOME] = key; + + // Keys mapping to UP_RIGHT + key = KEY_UP_RIGHT; + keyMap[KeyEvent.KEYCODE_DPAD_UP_RIGHT] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_9] = key; + keyMap[KeyEvent.KEYCODE_9] = key; + keyMap[KeyEvent.KEYCODE_PAGE_UP] = key; + + // Keys mapping to DOWN_LEFT + key = KEY_DOWN_LEFT; + keyMap[KeyEvent.KEYCODE_DPAD_DOWN_LEFT] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_1] = key; + keyMap[KeyEvent.KEYCODE_1] = key; + keyMap[KeyEvent.KEYCODE_MOVE_END] = key; + + // Keys mapping to DOWN_RIGHT + key = KEY_UP_LEFT; + keyMap[KeyEvent.KEYCODE_DPAD_DOWN_RIGHT] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_3] = key; + keyMap[KeyEvent.KEYCODE_3] = key; + keyMap[KeyEvent.KEYCODE_PAGE_DOWN] = key; + + // Keys mapping to ATTACK + key = KEY_ATTACK; + keyMap[KeyEvent.KEYCODE_DPAD_CENTER] = key; + keyMap[KeyEvent.KEYCODE_BUTTON_A] = key; + keyMap[KeyEvent.KEYCODE_SPACE] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_5] = key; + + // Keys mapping to FLEE + key = KEY_FLEE; + keyMap[KeyEvent.KEYCODE_BUTTON_X] = key; + keyMap[KeyEvent.KEYCODE_F] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_ENTER] = key; + keyMap[KeyEvent.KEYCODE_ENTER] = key; + + // Keys mapping to END_TURN + key = KEY_END_TURN; + keyMap[KeyEvent.KEYCODE_BUTTON_Y] = key; + keyMap[KeyEvent.KEYCODE_E] = key; + keyMap[KeyEvent.KEYCODE_FORWARD_DEL] = key; + keyMap[KeyEvent.KEYCODE_NUMPAD_DOT] = key; + + // Keys mapping to HERO_INFO + key = KEY_HERO_INFO; + keyMap[KeyEvent.KEYCODE_BUTTON_SELECT] = key; + keyMap[KeyEvent.KEYCODE_C] = key; + } + + // Generate game actions based on mapped keys public boolean onKeyboardAction(Context context, KeyEvent event) { -// L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); + L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN); boolean inihbit = (keyState_attack || keyState_endturn || keyState_flee); // used to inhibit movement if an action key is held down - switch (event.getKeyCode()) { + if (event.getKeyCode() >= keyMap.length) return false; // just in case we get an oddball keycode + + switch (keyMap[event.getKeyCode()]) { // Basic movement handled first // Ordinal directional keys - only modify one direction register, can be combined when // used simultaneously to create synthetic diagonals - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_NUMPAD_8: - case KeyEvent.KEYCODE_8: - case KeyEvent.KEYCODE_W: + case KEY_UP: keyState_dy = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_NUMPAD_2: - case KeyEvent.KEYCODE_2: - case KeyEvent.KEYCODE_S: + case KEY_DOWN: keyState_dy = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_NUMPAD_4: - case KeyEvent.KEYCODE_4: - case KeyEvent.KEYCODE_A: + case KEY_LEFT: keyState_dx = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_NUMPAD_6: - case KeyEvent.KEYCODE_6: - case KeyEvent.KEYCODE_D: + case KEY_RIGHT: keyState_dx = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; // Diagonal directional keys. Modify both direction registers, can't be combined // TODO: store individual key position to allow combinations. May not be worth the trouble. - case KeyEvent.KEYCODE_DPAD_UP_LEFT: - case KeyEvent.KEYCODE_NUMPAD_7: - case KeyEvent.KEYCODE_7: - case KeyEvent.KEYCODE_MOVE_HOME: + case KEY_UP_LEFT: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KeyEvent.KEYCODE_DPAD_UP_RIGHT: - case KeyEvent.KEYCODE_NUMPAD_9: - case KeyEvent.KEYCODE_9: - case KeyEvent.KEYCODE_PAGE_UP: + case KEY_UP_RIGHT: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KeyEvent.KEYCODE_DPAD_DOWN_LEFT: - case KeyEvent.KEYCODE_NUMPAD_1: - case KeyEvent.KEYCODE_1: - case KeyEvent.KEYCODE_MOVE_END: + case KEY_DOWN_LEFT: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT: - case KeyEvent.KEYCODE_NUMPAD_3: - case KeyEvent.KEYCODE_3: - case KeyEvent.KEYCODE_PAGE_DOWN: + case KEY_DOWN_RIGHT: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); @@ -124,10 +212,7 @@ public final class InputController implements OnClickListener, OnLongClickListen // "Attack" shortcut - freeze movement to allow chorded direction when key is released. // if in combat, executes an attack on key release - case KeyEvent.KEYCODE_DPAD_CENTER: // Not sure if this is needed - case KeyEvent.KEYCODE_BUTTON_A: // lock movement until released for precise directional move/attack - case KeyEvent.KEYCODE_SPACE: - case KeyEvent.KEYCODE_NUMPAD_5: + case KEY_ATTACK: if (keydown && !keyState_attack) { // key pressed - pause movement if(!world.model.uiSelections.isInCombat) controllers.movementController.stopMovement(); } else if (!keydown && keyState_attack) { // key released - execute attack / move in direction @@ -137,10 +222,7 @@ public final class InputController implements OnClickListener, OnLongClickListen break; // "Flee" shortcut. Intitiates flee when pressed. If a direction is held, moves in chosen direction when released - case KeyEvent.KEYCODE_BUTTON_X: - case KeyEvent.KEYCODE_F: - case KeyEvent.KEYCODE_NUMPAD_ENTER: - case KeyEvent.KEYCODE_ENTER: + case KEY_FLEE: if (world.model.uiSelections.isInCombat) { if (keydown && !keyState_flee) { // button pressed - set flee; movement locked while pressed controllers.combatController.startFlee(); @@ -155,10 +237,7 @@ public final class InputController implements OnClickListener, OnLongClickListen break; // "End Turn" shortcut. Flag prevents repeated end turn if key is held down. - case KeyEvent.KEYCODE_BUTTON_Y: - case KeyEvent.KEYCODE_E: - case KeyEvent.KEYCODE_FORWARD_DEL: - case KeyEvent.KEYCODE_NUMPAD_DOT: + case KEY_END_TURN: if (keydown && !keyState_endturn) { if (world.model.uiSelections.isInCombat) controllers.combatController.endPlayerTurn(); } @@ -166,11 +245,12 @@ public final class InputController implements OnClickListener, OnLongClickListen break; // "Hero Info" screen shortcut. New activity takes focus, so we don't need to worry about repeats. - case KeyEvent.KEYCODE_BUTTON_SELECT: - case KeyEvent.KEYCODE_C: + case KEY_HERO_INFO: if (keydown) context.startActivity(new Intent(context, HeroinfoActivity.class)); break; - default: // unhandled key + + case KEY_UNHANDLED: // Unhandled keycode + default: // unhandled keymap code entry (should not happen) return false; } diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index e214898e0..2c5c0e523 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -4,8 +4,6 @@ import java.lang.ref.WeakReference; import com.gpl.rpg.AndorsTrail.AndorsTrailApplication; import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences; -import com.gpl.rpg.AndorsTrail.R; -import com.gpl.rpg.AndorsTrail.activity.HeroinfoActivity; import com.gpl.rpg.AndorsTrail.context.ControllerContext; import com.gpl.rpg.AndorsTrail.context.WorldContext; import com.gpl.rpg.AndorsTrail.controller.Constants; @@ -34,20 +32,17 @@ import com.gpl.rpg.AndorsTrail.util.CoordRect; import com.gpl.rpg.AndorsTrail.util.Size; import android.content.Context; -import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; -import android.os.Build; import android.os.Handler; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; -import android.widget.ImageButton; public final class MainView extends SurfaceView implements SurfaceHolder.Callback, @@ -660,14 +655,14 @@ public final class MainView extends SurfaceView if (v.controllers.preferences.enableUiAnimations) postDelayed(this, 0); } } - + public void stop() { stop = true; } } - - - + + + @Override public void onPlayerMoved(PredefinedMap map, Coord newPosition, Coord previousPosition) { if (map != currentMap) return; @@ -798,19 +793,19 @@ public final class MainView extends SurfaceView movingSpritesRedrawTick.start(); } } - + @Override public void onNewSpriteMoveFrame(SpriteMoveAnimation animation) { //redrawMoveArea_(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), animation); } - + @Override public void onSpriteMoveCompleted(SpriteMoveAnimation animation) { if (animation.map != currentMap) return; movingSprites--; redrawArea(CoordRect.getBoundingRect(animation.origin, animation.destination, animation.actor.tileSize), RedrawAreaDebugReason.EffectCompleted); } - + @Override public void onAsyncAreaUpdate(CoordRect area) { redrawArea(area, RedrawAreaDebugReason.AsyncRequest); From 127cba55f5fd48a18a53248b7ea5fbc62f1f296b Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Sat, 17 Apr 2021 08:54:58 -0700 Subject: [PATCH 7/8] Change keyMap to SparseInit array. --- .../controller/InputController.java | 195 ++++++++++-------- 1 file changed, 104 insertions(+), 91 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index 9522d693b..efbd6a3f6 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -2,6 +2,7 @@ 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.View; import android.view.View.OnClickListener; @@ -30,20 +31,21 @@ public final class InputController implements OnClickListener, OnLongClickListen private boolean keyState_endturn = false; final private int KEY_UNHANDLED = 0; // Default, for unhandled keycodes - final private int KEY_UP = 1; - final private int KEY_DOWN = 2; - final private int KEY_LEFT = 3; - final private int KEY_RIGHT = 4; - final private int KEY_UP_LEFT = 5; - final private int KEY_UP_RIGHT = 6; - final private int KEY_DOWN_LEFT = 7; - final private int KEY_DOWN_RIGHT = 8; + 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 char keyMap[]; // Android keycode to internal key event mapping. TODO: Configure via preferences + private SparseIntArray keyMap = new SparseIntArray(); // Android keycode to internal key event mapping. TODO: Configure via preferences public InputController(ControllerContext controllers, WorldContext world) { this.controllers = controllers; @@ -52,103 +54,109 @@ public final class InputController implements OnClickListener, OnLongClickListen } -/* New keyboard handler. Both Key Down and Key Up events handled to allow conditional behaviours. +/* 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? + 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 or XML file, or maybe eventually user preferences + // TODO: Move mapping out of code to JSON/XML file, or maybe player prefs private void initializeKeyMap() { - char key; - - keyMap = new char[1024]; // Should be a long time before android exceeds 1024 keycodes + int key; // Keys mapping to UP - key = KEY_UP; - keyMap[KeyEvent.KEYCODE_DPAD_UP] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_8] = key; - keyMap[KeyEvent.KEYCODE_8] = key; - keyMap[KeyEvent.KEYCODE_W] = key; + 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_DOWN; - keyMap[KeyEvent.KEYCODE_DPAD_DOWN] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_2] = key; - keyMap[KeyEvent.KEYCODE_2] = key; - keyMap[KeyEvent.KEYCODE_S] = key; + 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_LEFT; - keyMap[KeyEvent.KEYCODE_DPAD_LEFT] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_4] = key; - keyMap[KeyEvent.KEYCODE_4] = key; - keyMap[KeyEvent.KEYCODE_A] = key; + 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_RIGHT; - keyMap[KeyEvent.KEYCODE_DPAD_RIGHT] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_6] = key; - keyMap[KeyEvent.KEYCODE_6] = key; - keyMap[KeyEvent.KEYCODE_D] = key; + 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_UP_LEFT; - keyMap[KeyEvent.KEYCODE_DPAD_UP_LEFT] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_7] = key; - keyMap[KeyEvent.KEYCODE_7] = key; - keyMap[KeyEvent.KEYCODE_MOVE_HOME] = key; + 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_UP_RIGHT; - keyMap[KeyEvent.KEYCODE_DPAD_UP_RIGHT] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_9] = key; - keyMap[KeyEvent.KEYCODE_9] = key; - keyMap[KeyEvent.KEYCODE_PAGE_UP] = key; + 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_DOWN_LEFT; - keyMap[KeyEvent.KEYCODE_DPAD_DOWN_LEFT] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_1] = key; - keyMap[KeyEvent.KEYCODE_1] = key; - keyMap[KeyEvent.KEYCODE_MOVE_END] = key; + 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_UP_LEFT; - keyMap[KeyEvent.KEYCODE_DPAD_DOWN_RIGHT] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_3] = key; - keyMap[KeyEvent.KEYCODE_3] = key; - keyMap[KeyEvent.KEYCODE_PAGE_DOWN] = key; + 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[KeyEvent.KEYCODE_DPAD_CENTER] = key; - keyMap[KeyEvent.KEYCODE_BUTTON_A] = key; - keyMap[KeyEvent.KEYCODE_SPACE] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_5] = key; + 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[KeyEvent.KEYCODE_BUTTON_X] = key; - keyMap[KeyEvent.KEYCODE_F] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_ENTER] = key; - keyMap[KeyEvent.KEYCODE_ENTER] = key; + 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[KeyEvent.KEYCODE_BUTTON_Y] = key; - keyMap[KeyEvent.KEYCODE_E] = key; - keyMap[KeyEvent.KEYCODE_FORWARD_DEL] = key; - keyMap[KeyEvent.KEYCODE_NUMPAD_DOT] = key; + 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[KeyEvent.KEYCODE_BUTTON_SELECT] = key; - keyMap[KeyEvent.KEYCODE_C] = key; + //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); } // Generate game actions based on mapped keys @@ -156,52 +164,48 @@ public final class InputController implements OnClickListener, OnLongClickListen L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions - boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN); - boolean inihbit = (keyState_attack || keyState_endturn || keyState_flee); // used to inhibit movement if an action key is held down + boolean inihbit = (keyState_attack || keyState_flee); // used to inhibit movement if an action key is held down - if (event.getKeyCode() >= keyMap.length) return false; // just in case we get an oddball keycode - - switch (keyMap[event.getKeyCode()]) { - // Basic movement handled first + switch (keyMap.get(event.getKeyCode())) { - // Ordinal directional keys - only modify one direction register, can be combined when - // used simultaneously to create synthetic diagonals - case KEY_UP: + // Ordinal directional keys - only modify one direction register; registers are combined when + // keys used simultaneously to create synthetic diagonals + case KEY_MOVE_UP: keyState_dy = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KEY_DOWN: + case KEY_MOVE_DOWN: keyState_dy = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KEY_LEFT: + case KEY_MOVE_LEFT: keyState_dx = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KEY_RIGHT: + case KEY_MOVE_RIGHT: keyState_dx = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; // Diagonal directional keys. Modify both direction registers, can't be combined // TODO: store individual key position to allow combinations. May not be worth the trouble. - case KEY_UP_LEFT: + case KEY_MOVE_UP_LEFT: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KEY_UP_RIGHT: + case KEY_MOVE_UP_RIGHT: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? -1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KEY_DOWN_LEFT: + case KEY_MOVE_DOWN_LEFT: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; - case KEY_DOWN_RIGHT: + case KEY_MOVE_DOWN_RIGHT: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? 1 : 0; if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); @@ -213,7 +217,7 @@ public final class InputController implements OnClickListener, OnLongClickListen // "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 movement + 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 onRelativeMovement(keyState_dx, keyState_dy); @@ -226,8 +230,9 @@ public final class InputController implements OnClickListener, OnLongClickListen if (world.model.uiSelections.isInCombat) { if (keydown && !keyState_flee) { // button pressed - set flee; movement locked while pressed controllers.combatController.startFlee(); - } else if (!keydown && keyState_flee) { // button released - move flee direction, if chosen - // if no movement, executeMoveAttack() in will just attack again, so we have to check and do it here + } 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()) { controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); } @@ -249,8 +254,16 @@ public final class InputController implements OnClickListener, OnLongClickListen if (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; } @@ -258,10 +271,10 @@ public final class InputController implements OnClickListener, OnLongClickListen } 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 (!allowInputInterval()) return; - controllers.combatController.executeMoveAttack(dx, dy); - } else if (keyState_dx == 0 && keyState_dy == 0) { + if (allowInputInterval()) controllers.combatController.executeMoveAttack(dx, dy); + } else if (dx == 0 && dy == 0) { controllers.movementController.stopMovement(); } else { controllers.movementController.startMovement(dx, dy, null); From 6d18e00535776b01d07cd116c9247b6aff07f892 Mon Sep 17 00:00:00 2001 From: guru_meditation_no42 Date: Sat, 17 Apr 2021 09:23:42 -0700 Subject: [PATCH 8/8] Move canAcceptInput() checks into handler. --- .../controller/InputController.java | 30 +++++++++---------- .../gpl/rpg/AndorsTrail/view/MainView.java | 9 +++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java index efbd6a3f6..2ce960604 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/controller/InputController.java @@ -160,8 +160,8 @@ public final class InputController implements OnClickListener, OnLongClickListen } // Generate game actions based on mapped keys - public boolean onKeyboardAction(Context context, KeyEvent event) { - L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); + public boolean onKeyboardAction(Context context, KeyEvent event, boolean acceptInput) { + //L.log("onKeyboardAction(): Processing action " + event.getAction() + " for keyCode " + event.getKeyCode()); if (event.getAction() != KeyEvent.ACTION_DOWN && event.getAction() != KeyEvent.ACTION_UP) return false; // don't handle other actions boolean keydown = (event.getAction() == KeyEvent.ACTION_DOWN); @@ -173,19 +173,19 @@ public final class InputController implements OnClickListener, OnLongClickListen // keys used simultaneously to create synthetic diagonals case KEY_MOVE_UP: keyState_dy = keydown ? -1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KEY_MOVE_DOWN: keyState_dy = keydown ? 1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KEY_MOVE_LEFT: keyState_dx = keydown ? -1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KEY_MOVE_RIGHT: keyState_dx = keydown ? 1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; // Diagonal directional keys. Modify both direction registers, can't be combined @@ -193,22 +193,22 @@ public final class InputController implements OnClickListener, OnLongClickListen case KEY_MOVE_UP_LEFT: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? -1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KEY_MOVE_UP_RIGHT: keyState_dx = keydown ? 1 : 0; keyState_dy = keydown ? -1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; case KEY_MOVE_DOWN_LEFT: keyState_dx = keydown ? -1 : 0; keyState_dy = keydown ? 1 : 0; - if (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + 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 (!inihbit) onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput && !inihbit) onRelativeMovement(keyState_dx, keyState_dy); break; // Special key handling below - some combat/movement stuff done here because it's too @@ -220,7 +220,7 @@ public final class InputController implements OnClickListener, OnLongClickListen 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 - onRelativeMovement(keyState_dx, keyState_dy); + if (acceptInput) onRelativeMovement(keyState_dx, keyState_dy); } keyState_attack = keydown; break; @@ -229,12 +229,12 @@ public final class InputController implements OnClickListener, OnLongClickListen case KEY_FLEE: if (world.model.uiSelections.isInCombat) { if (keydown && !keyState_flee) { // button pressed - set flee; movement locked while pressed - controllers.combatController.startFlee(); + 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()) { - controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); + if(acceptInput) controllers.combatController.executeMoveAttack(keyState_dx, keyState_dy); } } } @@ -243,7 +243,7 @@ public final class InputController implements OnClickListener, OnLongClickListen // "End Turn" shortcut. Flag prevents repeated end turn if key is held down. case KEY_END_TURN: - if (keydown && !keyState_endturn) { + if (acceptInput && keydown && !keyState_endturn) { if (world.model.uiSelections.isInCombat) controllers.combatController.endPlayerTurn(); } keyState_endturn = keydown; @@ -251,7 +251,7 @@ public final class InputController implements OnClickListener, OnLongClickListen // "Hero Info" screen shortcut. New activity takes focus, so we don't need to worry about repeats. case KEY_HERO_INFO: - if (keydown) context.startActivity(new Intent(context, HeroinfoActivity.class)); + if (acceptInput && keydown) context.startActivity(new Intent(context, HeroinfoActivity.class)); break; case KEY_TOOLBOX: diff --git a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java index 2c5c0e523..9db49dcde 100644 --- a/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java +++ b/AndorsTrail/src/com/gpl/rpg/AndorsTrail/view/MainView.java @@ -133,18 +133,16 @@ public final class MainView extends SurfaceView @Override public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { - if (!canAcceptInput()) return false; - // onKeyboardAction needs context to start new activities. - return inputController.onKeyboardAction(getContext(), keyEvent) || super.onKeyDown(keyCode, keyEvent); + // canAcceptInput() checks done in handler so it can preserve keystate even if it does not have focus. + return inputController.onKeyboardAction(getContext(), keyEvent, canAcceptInput()) || super.onKeyDown(keyCode, keyEvent); } @Override public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { // Android provides artificial ACTION_UP events when focus changes; we process them to prevent "stuck key" effect after dialogs close - // (so don't check canAcceptInput() here) - return inputController.onKeyboardAction(getContext(), keyEvent) || super.onKeyUp(keyCode, keyEvent); + return inputController.onKeyboardAction(getContext(), keyEvent, canAcceptInput()) || super.onKeyUp(keyCode, keyEvent); } @Override @@ -175,6 +173,7 @@ public final class MainView extends SurfaceView @Override public void surfaceCreated(SurfaceHolder sh) { + hasSurface = true; }