diff --git a/SPD-classes/build.gradle b/SPD-classes/build.gradle index 9d30aa6ea..4aff110fa 100644 --- a/SPD-classes/build.gradle +++ b/SPD-classes/build.gradle @@ -7,5 +7,6 @@ dependencies { //TODO migrate this to implementation from api //in order to do this I have to remove 100% of libGDX API access from core api "com.badlogicgames.gdx:gdx:$gdxVersion" + api "com.badlogicgames.gdx-controllers:gdx-controllers-core:$gdxControllersVersion" implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" } diff --git a/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java b/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java new file mode 100644 index 000000000..243f769ce --- /dev/null +++ b/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java @@ -0,0 +1,137 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2022 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.watabou.input; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.controllers.Controller; +import com.badlogic.gdx.controllers.ControllerListener; +import com.badlogic.gdx.controllers.ControllerMapping; +import com.watabou.utils.DeviceCompat; +import com.watabou.utils.PointF; + +import java.lang.annotation.Target; + +public class ControllerHandler implements ControllerListener { + + public static boolean controllersSupported() { + if (DeviceCompat.isAndroid() && Gdx.app.getVersion() < 16) { + return false; + } else { + return true; + } + } + + @Override + public void connected(Controller controller) { + + } + + @Override + public void disconnected(Controller controller) { + + } + + @Override + public boolean buttonDown(Controller controller, int buttonCode) { + int keyCode = buttonToKey(controller, buttonCode); + if (keyCode != Input.Keys.UNKNOWN){ + KeyEvent.addKeyEvent(new KeyEvent(keyCode, true)); + return true; + } + return false; + } + + @Override + public boolean buttonUp(Controller controller, int buttonCode) { + int keyCode = buttonToKey(controller, buttonCode); + if (keyCode != Input.Keys.UNKNOWN){ + KeyEvent.addKeyEvent(new KeyEvent(keyCode, false)); + return true; + } + return false; + } + + public static PointF leftStickPosition = new PointF(); + public static PointF rightStickPosition = new PointF(); + + private float L2Trigger = 0f; + private float R2Trigger = 0f; + + @Override + public boolean axisMoved(Controller controller, int axisCode, float value) { + ControllerMapping mapping = controller.getMapping(); + if (mapping.axisRightX == axisCode) rightStickPosition.x = value; + else if (mapping.axisRightY == axisCode) rightStickPosition.y = value; + else if (mapping.axisLeftX == axisCode) leftStickPosition.x = value; + else if (mapping.axisLeftY == axisCode) leftStickPosition.y = value; + + //L2 and R2 triggers on Desktop + else if (axisCode == 4) { + + if (L2Trigger < 0.5f && value >= 0.5f){ + KeyEvent.addKeyEvent(new KeyEvent(Input.Keys.BUTTON_L2, true)); + } else if (L2Trigger >= 0.5f && value < 0.5f){ + KeyEvent.addKeyEvent(new KeyEvent(Input.Keys.BUTTON_L2, false)); + } + L2Trigger = value; + + } else if (axisCode == 5){ + + if (R2Trigger < 0.5f && value >= 0.5f){ + KeyEvent.addKeyEvent(new KeyEvent(Input.Keys.BUTTON_R2, true)); + } else if (R2Trigger >= 0.5f && value < 0.5f){ + KeyEvent.addKeyEvent(new KeyEvent(Input.Keys.BUTTON_R2, false)); + } + R2Trigger = value; + + } + return true; + } + + //converts controller button codes to keyEvent codes + public static int buttonToKey(Controller controller, int keyCode){ + ControllerMapping mapping = controller.getMapping(); + if (keyCode == mapping.buttonA) return Input.Keys.BUTTON_A; + if (keyCode == mapping.buttonB) return Input.Keys.BUTTON_B; + //C button? + if (keyCode == mapping.buttonX) return Input.Keys.BUTTON_X; + if (keyCode == mapping.buttonY) return Input.Keys.BUTTON_Y; + if (keyCode == mapping.buttonBack) return Input.Keys.BUTTON_SELECT; + if (keyCode == mapping.buttonStart) return Input.Keys.BUTTON_START; + + if (keyCode == mapping.buttonL1) return Input.Keys.BUTTON_L1; + if (keyCode == mapping.buttonL2) return Input.Keys.BUTTON_L2; + if (keyCode == mapping.buttonR1) return Input.Keys.BUTTON_R1; + if (keyCode == mapping.buttonR2) return Input.Keys.BUTTON_R2; + + if (keyCode == mapping.buttonDpadUp) return Input.Keys.DPAD_UP; + if (keyCode == mapping.buttonDpadLeft) return Input.Keys.DPAD_LEFT; + if (keyCode == mapping.buttonDpadDown) return Input.Keys.DPAD_DOWN; + if (keyCode == mapping.buttonDpadRight) return Input.Keys.DPAD_RIGHT; + + if (keyCode == mapping.buttonLeftStick) return Input.Keys.BUTTON_THUMBL; + if (keyCode == mapping.buttonRightStick) return Input.Keys.BUTTON_THUMBR; + + return Input.Keys.UNKNOWN; + } +} diff --git a/SPD-classes/src/main/java/com/watabou/noosa/Game.java b/SPD-classes/src/main/java/com/watabou/noosa/Game.java index 47e084887..7b54d6887 100644 --- a/SPD-classes/src/main/java/com/watabou/noosa/Game.java +++ b/SPD-classes/src/main/java/com/watabou/noosa/Game.java @@ -24,12 +24,14 @@ package com.watabou.noosa; import com.badlogic.gdx.Application; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.controllers.Controllers; import com.badlogic.gdx.graphics.glutils.GLVersion; import com.badlogic.gdx.utils.TimeUtils; import com.watabou.glscripts.Script; import com.watabou.gltextures.TextureCache; import com.watabou.glwrap.Blending; import com.watabou.glwrap.Vertexbuffer; +import com.watabou.input.ControllerHandler; import com.watabou.input.InputHandler; import com.watabou.input.PointerEvent; import com.watabou.noosa.audio.Music; @@ -100,6 +102,9 @@ public class Game implements ApplicationListener { dispWidth = Gdx.graphics.getDisplayMode().width; inputHandler = new InputHandler( Gdx.input ); + if (ControllerHandler.controllersSupported()){ + Controllers.addListener(new ControllerHandler()); + } //refreshes texture and vertex data stored on the gpu versionContextRef = Gdx.graphics.getGLVersion(); diff --git a/android/build.gradle b/android/build.gradle index 2a3be2f16..e868fe478 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -69,6 +69,7 @@ dependencies { natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a" natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86" natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64" + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-android:$gdxControllersVersion" } // called every time gradle gets executed, takes the native dependencies of diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro index 89f757217..4f48b77d8 100644 --- a/android/proguard-rules.pro +++ b/android/proguard-rules.pro @@ -22,6 +22,9 @@ -keepnames class com.badlogic.gdx.graphics.Color { *; } -keepnames class com.badlogic.gdx.scenes.scene2d.ui.TextField$TextFieldStyle { *; } +# needed for libGDX controllers +-keep class com.badlogic.gdx.controllers.android.AndroidControllers { *; } + -keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* { (com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration); } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 4dcf654bd..bfa530d87 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + + + 0){ heldDelay -= Game.elapsed; } - - if (heldAction1 != SPDAction.NONE && Dungeon.hero.ready){ + boolean leftStickActive = Math.abs(ControllerHandler.leftStickPosition.x) >= .5f + || Math.abs(ControllerHandler.leftStickPosition.y) >= 0.5f; + leftStickActive = leftStickActive && !GameScene.InterfaceBlockingHero(); + if ((heldAction1 != SPDAction.NONE || leftStickActive) && Dungeon.hero.ready){ processKeyHold(); } else if (Dungeon.hero.ready) { lastCellMoved = -1; @@ -350,7 +353,7 @@ public class CellSelector extends ScrollArea { for (GameAction action : actions) { cell += directionFromAction(action); } - + if (cell != Dungeon.hero.pos && cell != lastCellMoved){ lastCellMoved = cell; select(cell, PointerEvent.LEFT); @@ -373,9 +376,39 @@ public class CellSelector extends ScrollArea { if (action == SPDAction.NW) return -1-Dungeon.level.width(); else return 0; } - + + //TODO controller stick movement would probably be improved if it used the 50ms delay, like key movement public void processKeyHold() { - if (directionFromAction(heldAction1) + directionFromAction(heldAction2) != 0 + //prioritize moving by controller stick over moving via keys + PointF leftStick = ControllerHandler.leftStickPosition; + boolean leftStickActive = Math.abs(leftStick.x) > 0.5f || Math.abs(leftStick.y) > 0.5f; + leftStickActive = leftStickActive && !GameScene.InterfaceBlockingHero(); + if (leftStickActive) { + enabled = Dungeon.hero.ready = true; + Dungeon.observe(); + //determine which direction to move in. + if (leftStick.x > 0.5f){ + if (leftStick.y < -0.5f){ + if (moveFromActions(SPDAction.NE)) Dungeon.hero.ready = false; + } else if (leftStick.y > 0.5f){ + if (moveFromActions(SPDAction.SE)) Dungeon.hero.ready = false; + } else if (leftStick.x > 0.8f){ + if (moveFromActions(SPDAction.E)) Dungeon.hero.ready = false; + } + } else if (leftStick.x < -0.5f){ + if (leftStick.y < -0.5f){ + if (moveFromActions(SPDAction.NW)) Dungeon.hero.ready = false; + } else if (leftStick.y > 0.5f){ + if (moveFromActions(SPDAction.SW)) Dungeon.hero.ready = false; + } else if (leftStick.x < -0.8f){ + if (moveFromActions(SPDAction.W)) Dungeon.hero.ready = false; + } + } else if (leftStick.y > 0.8f){ + if (moveFromActions(SPDAction.S)) Dungeon.hero.ready = false; + } else if (leftStick.y < -0.8f){ + if (moveFromActions(SPDAction.N)) Dungeon.hero.ready = false; + } + } else if (directionFromAction(heldAction1) + directionFromAction(heldAction2) != 0 && heldDelay <= 0){ enabled = Dungeon.hero.ready = true; Dungeon.observe(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java index dc7363adf..6d726a1d5 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/PixelScene.java @@ -21,6 +21,7 @@ package com.shatteredpixel.shatteredpixeldungeon.scenes; +import com.badlogic.gdx.Gdx; import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Badges; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; @@ -32,6 +33,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.Tooltip; import com.shatteredpixel.shatteredpixeldungeon.ui.Window; import com.watabou.gltextures.TextureCache; import com.watabou.glwrap.Blending; +import com.watabou.input.ControllerHandler; import com.watabou.input.PointerEvent; import com.watabou.noosa.BitmapText; import com.watabou.noosa.BitmapText.Font; @@ -44,6 +46,7 @@ import com.watabou.noosa.Visual; import com.watabou.noosa.ui.Component; import com.watabou.noosa.ui.Cursor; import com.watabou.utils.GameMath; +import com.watabou.utils.PointF; import com.watabou.utils.Reflection; import java.util.ArrayList; @@ -155,7 +158,28 @@ public class PixelScene extends Scene { Cursor.setCustomCursor(Cursor.Type.DEFAULT, defaultZoom); } - + + private PointF fractionalMovement = new PointF(); + + @Override + public void update() { + super.update(); + if (Math.abs(ControllerHandler.rightStickPosition.x) >= 0.1f + || Math.abs(ControllerHandler.rightStickPosition.y) >= 0.1f) { + PointF curMouse = PointerEvent.currentHoverPos(); + //cursor moves 500 scaled pixels per second at full speed, 50 at minimum speed + fractionalMovement.x += defaultZoom * 500 * Game.elapsed * ControllerHandler.rightStickPosition.x; + fractionalMovement.y += defaultZoom * 500 * Game.elapsed * ControllerHandler.rightStickPosition.y; + curMouse.x += (int)fractionalMovement.x; + curMouse.y += (int)fractionalMovement.y; + Gdx.input.setCursorPosition((int) curMouse.x, (int) curMouse.y); + fractionalMovement.x -= (int)fractionalMovement.x; + fractionalMovement.y -= (int)fractionalMovement.y; + } else { + fractionalMovement.set(0); + } + } + //FIXME this system currently only works for a subset of windows private static ArrayList> savedWindows = new ArrayList<>(); private static Class savedClass = null; diff --git a/desktop/build.gradle b/desktop/build.gradle index c0d59d26b..ac7d6a68f 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -114,6 +114,7 @@ dependencies { implementation "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$gdxControllersVersion" //we use LWJGL tinyFD directly to display crash messages and (for now) single-line text input implementation "org.lwjgl:lwjgl-tinyfd:3.2.3" diff --git a/ios/build.gradle b/ios/build.gradle index f3f099854..46797485a 100644 --- a/ios/build.gradle +++ b/ios/build.gradle @@ -49,4 +49,5 @@ dependencies { implementation "com.badlogicgames.gdx:gdx-backend-robovm:$gdxVersion" implementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-ios" implementation "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-ios" + implementation "com.badlogicgames.gdx-controllers:gdx-controllers-ios:$gdxControllersVersion" } \ No newline at end of file diff --git a/ios/robovm.xml b/ios/robovm.xml index 95046370e..890a35b95 100644 --- a/ios/robovm.xml +++ b/ios/robovm.xml @@ -27,6 +27,7 @@ com.badlogic.gdx.scenes.scene2d.ui.* com.badlogic.gdx.graphics.g3d.particles.** + com.badlogic.gdx.controllers.IosControllerManager com.android.okhttp.HttpHandler com.android.okhttp.HttpsHandler com.android.org.conscrypt.**