From 8653439a4ff90cbc94c1b715de914f2481b850b1 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Fri, 3 Jun 2022 12:33:17 -0400 Subject: [PATCH] v1.3.0: added a separate bindings window for controller --- .../com/watabou/input/ControllerHandler.java | 21 ++- .../java/com/watabou/input/KeyBindings.java | 48 ++++- .../main/java/com/watabou/utils/Bundle.java | 2 +- .../shatteredpixeldungeon/SPDAction.java | 170 ++++++++++++++++-- .../windows/WndKeyBindings.java | 35 +++- .../windows/WndSettings.java | 4 +- 6 files changed, 247 insertions(+), 33 deletions(-) diff --git a/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java b/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java index 9bc5202d1..a7445777d 100644 --- a/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java +++ b/SPD-classes/src/main/java/com/watabou/input/ControllerHandler.java @@ -164,10 +164,11 @@ public class ControllerHandler implements ControllerListener { if (btnCode == mapping.buttonR1) return Input.Keys.BUTTON_R1; if (btnCode == mapping.buttonR2) return Input.Keys.BUTTON_R2; - if (btnCode == mapping.buttonDpadUp) return Input.Keys.DPAD_UP; - if (btnCode == mapping.buttonDpadLeft) return Input.Keys.DPAD_LEFT; - if (btnCode == mapping.buttonDpadDown) return Input.Keys.DPAD_DOWN; - if (btnCode == mapping.buttonDpadRight) return Input.Keys.DPAD_RIGHT; + //we add 1000 here to make these keys distinct from Keys.UP, Keys.DOWN, etc.. + if (btnCode == mapping.buttonDpadUp) return Input.Keys.DPAD_UP + 1000; + if (btnCode == mapping.buttonDpadDown) return Input.Keys.DPAD_DOWN + 1000; + if (btnCode == mapping.buttonDpadLeft) return Input.Keys.DPAD_LEFT + 1000; + if (btnCode == mapping.buttonDpadRight) return Input.Keys.DPAD_RIGHT + 1000; if (btnCode == mapping.buttonLeftStick) return Input.Keys.BUTTON_THUMBL; if (btnCode == mapping.buttonRightStick)return Input.Keys.BUTTON_THUMBR; @@ -180,7 +181,7 @@ public class ControllerHandler implements ControllerListener { return true; } - else if (keyCode >= Input.Keys.DPAD_UP && keyCode <= Input.Keys.DPAD_LEFT){ + if (keyCode >= Input.Keys.DPAD_UP+1000 && keyCode <= Input.Keys.DPAD_RIGHT+1000){ return true; } @@ -200,6 +201,16 @@ public class ControllerHandler implements ControllerListener { } } + if (keyCode == Input.Keys.DPAD_UP + 1000){ + return Input.Keys.toString(Input.Keys.DPAD_UP); + } else if (keyCode == Input.Keys.DPAD_DOWN + 1000){ + return Input.Keys.toString(Input.Keys.DPAD_DOWN); + } else if (keyCode == Input.Keys.DPAD_LEFT + 1000){ + return Input.Keys.toString(Input.Keys.DPAD_LEFT); + } else if (keyCode == Input.Keys.DPAD_RIGHT + 1000){ + return Input.Keys.toString(Input.Keys.DPAD_RIGHT); + } + return null; } } diff --git a/SPD-classes/src/main/java/com/watabou/input/KeyBindings.java b/SPD-classes/src/main/java/com/watabou/input/KeyBindings.java index f568f77d6..d64803674 100644 --- a/SPD-classes/src/main/java/com/watabou/input/KeyBindings.java +++ b/SPD-classes/src/main/java/com/watabou/input/KeyBindings.java @@ -30,8 +30,12 @@ import java.util.LinkedHashMap; // should see about doing some refactoring to clean this up public class KeyBindings { + //for keyboard keys private static LinkedHashMap bindings = new LinkedHashMap<>(); + //for controller buttons + private static LinkedHashMap controllerBindings = new LinkedHashMap<>(); + public static LinkedHashMap getAllBindings(){ return new LinkedHashMap<>(bindings); } @@ -40,6 +44,14 @@ public class KeyBindings { bindings = new LinkedHashMap<>(newBindings); } + public static LinkedHashMap getAllControllerBindings(){ + return new LinkedHashMap<>(controllerBindings); + } + + public static void setAllControllerBindings(LinkedHashMap newBindings){ + controllerBindings = new LinkedHashMap<>(newBindings); + } + //these are special keybinding that are not user-configurable private static LinkedHashMap hardBindings = new LinkedHashMap<>(); @@ -53,12 +65,17 @@ public class KeyBindings { if (keyCode < 0 || (keyCode > 255 && keyCode < 1000)){ return false; } - return bindingKey || bindings.containsKey( keyCode ) || hardBindings.containsKey( keyCode ); + return bindingKey + || bindings.containsKey( keyCode ) + || controllerBindings.containsKey( keyCode ) + || hardBindings.containsKey( keyCode ); } public static GameAction getActionForKey(KeyEvent event){ - if (bindings.containsKey( event.code )){ + if (bindings.containsKey( event.code )) { return bindings.get( event.code ); + } else if (controllerBindings.containsKey( event.code )){ + return controllerBindings.get( event.code ); } else if (hardBindings.containsKey( event.code )) { return hardBindings.get( event.code ); } @@ -67,11 +84,36 @@ public class KeyBindings { public static ArrayList getBoundKeysForAction(GameAction action){ ArrayList result = new ArrayList<>(); - for( int i : bindings.keySet()){ + for( int i : bindings.keySet() ){ if (bindings.get(i) == action){ result.add(i); } } + for( int i : controllerBindings.keySet() ){ + if (controllerBindings.get(i) == action){ + result.add(i); + } + } + return result; + } + + public static ArrayList getKeyboardKeysForAction(GameAction action){ + ArrayList result = new ArrayList<>(); + for( int i : bindings.keySet() ){ + if (bindings.get(i) == action){ + result.add(i); + } + } + return result; + } + + public static ArrayList getControllerKeysForAction(GameAction action){ + ArrayList result = new ArrayList<>(); + for( int i : controllerBindings.keySet() ){ + if (controllerBindings.get(i) == action){ + result.add(i); + } + } return result; } diff --git a/SPD-classes/src/main/java/com/watabou/utils/Bundle.java b/SPD-classes/src/main/java/com/watabou/utils/Bundle.java index c7c5d0f9d..6e170c811 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/Bundle.java +++ b/SPD-classes/src/main/java/com/watabou/utils/Bundle.java @@ -88,7 +88,7 @@ public class Bundle { } public boolean contains( String key ) { - return !data.isNull( key ); + return !isNull() && !data.isNull( key ); } public boolean remove( String key ){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDAction.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDAction.java index cb226f785..461ed9cb8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDAction.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/SPDAction.java @@ -22,6 +22,7 @@ package com.shatteredpixel.shatteredpixeldungeon; import com.badlogic.gdx.Input; +import com.watabou.input.ControllerHandler; import com.watabou.input.GameAction; import com.watabou.input.KeyBindings; import com.watabou.utils.Bundle; @@ -88,12 +89,6 @@ public class SPDAction extends GameAction { static { defaultBindings.put( Input.Keys.ESCAPE, SPDAction.BACK ); defaultBindings.put( Input.Keys.BACKSPACE, SPDAction.BACK ); - defaultBindings.put( Input.Keys.BUTTON_START, SPDAction.BACK ); - - defaultBindings.put( Input.Keys.BUTTON_R2, SPDAction.LEFT_CLICK ); - defaultBindings.put( Input.Keys.BUTTON_THUMBR, SPDAction.LEFT_CLICK ); - defaultBindings.put( Input.Keys.BUTTON_L2, SPDAction.RIGHT_CLICK ); - defaultBindings.put( Input.Keys.BUTTON_SELECT, SPDAction.MIDDLE_CLICK ); defaultBindings.put( Input.Keys.W, SPDAction.N ); defaultBindings.put( Input.Keys.A, SPDAction.W ); @@ -116,19 +111,12 @@ public class SPDAction extends GameAction { defaultBindings.put( Input.Keys.NUMPAD_3, SPDAction.SE ); defaultBindings.put( Input.Keys.NUMPAD_5, SPDAction.WAIT ); - defaultBindings.put( Input.Keys.BUTTON_THUMBL, SPDAction.WAIT ); - defaultBindings.put( Input.Keys.F, SPDAction.INVENTORY ); defaultBindings.put( Input.Keys.I, SPDAction.INVENTORY ); - defaultBindings.put( Input.Keys.BUTTON_R1, SPDAction.INVENTORY ); defaultBindings.put( Input.Keys.NUM_1, SPDAction.QUICKSLOT_1 ); - defaultBindings.put( Input.Keys.BUTTON_Y, SPDAction.QUICKSLOT_1 ); defaultBindings.put( Input.Keys.NUM_2, SPDAction.QUICKSLOT_2 ); - defaultBindings.put( Input.Keys.BUTTON_B, SPDAction.QUICKSLOT_2 ); defaultBindings.put( Input.Keys.NUM_3, SPDAction.QUICKSLOT_3 ); - defaultBindings.put( Input.Keys.BUTTON_X, SPDAction.QUICKSLOT_3 ); defaultBindings.put( Input.Keys.NUM_4, SPDAction.QUICKSLOT_4 ); - defaultBindings.put( Input.Keys.BUTTON_A, SPDAction.QUICKSLOT_4 ); defaultBindings.put( Input.Keys.NUM_5, SPDAction.QUICKSLOT_5 ); defaultBindings.put( Input.Keys.NUM_6, SPDAction.QUICKSLOT_6 ); @@ -139,7 +127,6 @@ public class SPDAction extends GameAction { defaultBindings.put( Input.Keys.F5, SPDAction.BAG_5 ); defaultBindings.put( Input.Keys.E, SPDAction.EXAMINE ); - defaultBindings.put( Input.Keys.BUTTON_L1, SPDAction.EXAMINE ); defaultBindings.put( Input.Keys.Z, SPDAction.REST ); defaultBindings.put( Input.Keys.Q, SPDAction.TAG_ATTACK ); @@ -161,6 +148,38 @@ public class SPDAction extends GameAction { return new LinkedHashMap<>(defaultBindings); } + private static final LinkedHashMap defaultControllerBindings = new LinkedHashMap<>(); + static { + defaultControllerBindings.put( Input.Keys.BUTTON_START, SPDAction.BACK ); + + defaultControllerBindings.put( Input.Keys.BUTTON_R2, SPDAction.LEFT_CLICK ); + defaultControllerBindings.put( Input.Keys.BUTTON_THUMBR, SPDAction.LEFT_CLICK ); + defaultControllerBindings.put( Input.Keys.BUTTON_L2, SPDAction.RIGHT_CLICK ); + defaultControllerBindings.put( Input.Keys.BUTTON_SELECT, SPDAction.MIDDLE_CLICK ); + + defaultControllerBindings.put( Input.Keys.DPAD_UP+1000, SPDAction.N ); + defaultControllerBindings.put( Input.Keys.DPAD_LEFT+1000, SPDAction.W ); + defaultControllerBindings.put( Input.Keys.DPAD_DOWN+1000, SPDAction.S ); + defaultControllerBindings.put( Input.Keys.DPAD_RIGHT+1000, SPDAction.E ); + + defaultControllerBindings.put( Input.Keys.BUTTON_THUMBL, SPDAction.WAIT ); + + defaultControllerBindings.put( Input.Keys.BUTTON_R1, SPDAction.INVENTORY ); + + defaultControllerBindings.put( Input.Keys.BUTTON_L1, SPDAction.EXAMINE ); + + //plan for new buttons: quickslots, quick inventory, + //Probably want to repurpose dpad too: attack, loot(or combine this?), special action, resume + defaultControllerBindings.put( Input.Keys.BUTTON_Y, SPDAction.QUICKSLOT_1 ); + defaultControllerBindings.put( Input.Keys.BUTTON_B, SPDAction.QUICKSLOT_2 ); + defaultControllerBindings.put( Input.Keys.BUTTON_X, SPDAction.QUICKSLOT_3 ); + defaultControllerBindings.put( Input.Keys.BUTTON_A, SPDAction.QUICKSLOT_4 ); + } + + public static LinkedHashMap getControllerDefaults() { + return new LinkedHashMap<>(defaultControllerBindings); + } + //hard bindings for android devices static { KeyBindings.addHardBinding( Input.Keys.BACK, SPDAction.BACK ); @@ -187,7 +206,7 @@ public class SPDAction extends GameAction { LinkedHashMap merged = new LinkedHashMap<>(); for (GameAction a : allActions()) { - if (firstKeys.contains(a.name())) { + if (firstKeys.contains(a.name()) && !ControllerHandler.icControllerKey(firstKeys.getInt(a.name()))) { if (firstKeys.getInt(a.name()) == 0){ continue; //we have no keys assigned to this action, move to the next one } else { @@ -204,7 +223,7 @@ public class SPDAction extends GameAction { } } - if (secondKeys.contains(a.name())) { + if (secondKeys.contains(a.name()) && !ControllerHandler.icControllerKey(secondKeys.getInt(a.name()))) { if (secondKeys.getInt(a.name()) == 0){ continue; //we have no more keys assigned to this action, move to the next one } else { @@ -221,7 +240,7 @@ public class SPDAction extends GameAction { } } - if (thirdKeys.contains(a.name())) { + if (thirdKeys.contains(a.name()) && !ControllerHandler.icControllerKey(thirdKeys.getInt(a.name()))) { if (thirdKeys.getInt(a.name()) == 0){ continue; //we have no more keys assigned to this action, move to the next one } else { @@ -242,8 +261,72 @@ public class SPDAction extends GameAction { KeyBindings.setAllBindings(merged); + defaults = getControllerDefaults(); + merged.clear(); + + Bundle firstButtons = b.getBundle("first_keys_controller"); + Bundle secondButtons = b.getBundle("second_keys_controller"); + Bundle thirdButtons = b.getBundle("third_keys_controller"); + + for (GameAction a : allActions()) { + if (firstButtons.contains(a.name()) && ControllerHandler.icControllerKey(firstButtons.getInt(a.name()))) { + if (firstButtons.getInt(a.name()) == 0){ + continue; //we have no keys assigned to this action, move to the next one + } else { + merged.put(firstButtons.getInt(a.name()), a); + defaults.remove(firstButtons.getInt(a.name())); //prevent duplicates in other actions + } + } else { + //if we have no custom key here, find the first one from defaults and merge it + for (int i : defaults.keySet()){ + if (defaults.get(i) == a){ + merged.put(i, defaults.remove(i)); + break; + } + } + } + + if (secondButtons.contains(a.name()) && ControllerHandler.icControllerKey(secondButtons.getInt(a.name()))) { + if (secondButtons.getInt(a.name()) == 0){ + continue; //we have no more keys assigned to this action, move to the next one + } else { + merged.put(secondButtons.getInt(a.name()), a); + defaults.remove(secondButtons.getInt(a.name())); + } + } else { + //if we have no custom key here, find the next one from defaults and merge it + for (int i : defaults.keySet()){ + if (defaults.get(i) == a){ + merged.put(i, defaults.remove(i)); + break; + } + } + } + + if (thirdButtons.contains(a.name()) && ControllerHandler.icControllerKey(thirdButtons.getInt(a.name()))) { + if (thirdButtons.getInt(a.name()) == 0){ + continue; //we have no more keys assigned to this action, move to the next one + } else { + merged.put(thirdButtons.getInt(a.name()), a); + defaults.remove(thirdButtons.getInt(a.name())); + } + } else { + //if we have no custom key here, find the next one from defaults and merge it + for (int i : defaults.keySet()){ + if (defaults.get(i) == a){ + merged.put(i, defaults.remove(i)); + break; + } + } + } + + } + + KeyBindings.setAllControllerBindings(merged); + } catch (Exception e){ KeyBindings.setAllBindings(getDefaults()); + KeyBindings.setAllControllerBindings(getControllerDefaults()); } } @@ -305,6 +388,59 @@ public class SPDAction extends GameAction { b.put("second_keys", secondKeys); b.put("third_keys", thirdKeys); + Bundle firstButtons = new Bundle(); + Bundle secondButtons = new Bundle(); + Bundle thirdButtons = new Bundle(); + + for (GameAction a : allActions()){ + int firstCur = 0; + int secondCur = 0; + int thirdCur = 0; + int firstDef = 0; + int secondDef = 0; + int thirdDef = 0; + + for (int i : defaultControllerBindings.keySet()){ + if (defaultControllerBindings.get(i) == a){ + if (firstDef == 0) { + firstDef = i; + } else if (secondDef == 0) { + secondDef = i; + } else { + thirdDef = i; + } + } + } + + LinkedHashMap curBindings = KeyBindings.getAllControllerBindings(); + for (int i : curBindings.keySet()){ + if (curBindings.get(i) == a){ + if (firstCur == 0) { + firstCur = i; + } else if (secondCur == 0) { + secondCur = i; + } else { + thirdCur = i; + } + } + } + + if (firstCur != firstDef){ + firstButtons.put(a.name(), firstCur); + } + if (secondCur != secondDef){ + secondButtons.put(a.name(), secondCur); + } + if (thirdCur != thirdDef){ + thirdButtons.put(a.name(), thirdCur); + } + + } + + b.put("first_keys_controller", firstButtons); + b.put("second_keys_controller", secondButtons); + b.put("third_keys_controller", thirdButtons); + try { FileUtils.bundleToFile(BINDINGS_FILE, b); } catch (IOException e) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndKeyBindings.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndKeyBindings.java index eef4836e5..0976a444d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndKeyBindings.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndKeyBindings.java @@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton; import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock; import com.shatteredpixel.shatteredpixeldungeon.ui.ScrollPane; import com.shatteredpixel.shatteredpixeldungeon.ui.Window; +import com.watabou.input.ControllerHandler; import com.watabou.input.GameAction; import com.watabou.input.KeyBindings; import com.watabou.input.KeyEvent; @@ -57,9 +58,13 @@ public class WndKeyBindings extends Window { private LinkedHashMap changedBindings; - public WndKeyBindings() { + private static boolean controller = false; - changedBindings = KeyBindings.getAllBindings(); + public WndKeyBindings(Boolean controller) { + + this.controller = controller; + + changedBindings = controller ? KeyBindings.getAllControllerBindings() : KeyBindings.getAllBindings(); RenderedTextBlock ttlAction = PixelScene.renderTextBlock(Messages.get(this, "ttl_action"), 9); ttlAction.setPos( COL1_CENTER - ttlAction.width()/2, (BTN_HEIGHT - ttlAction.height())/2); @@ -119,6 +124,15 @@ public class WndKeyBindings extends Window { //start at 1. No bindings for NONE if (action.code() < 1) continue; + //mouse bindings are only available to controllers + if ((action == GameAction.LEFT_CLICK + || action == GameAction.RIGHT_CLICK + || action == GameAction.MIDDLE_CLICK) && !controller){ + continue; + } + + //TODO probably exclude some binding for controllers, and adjust default mappings + BindingItem item = new BindingItem(action); item.setRect(0, y, WIDTH, BindingItem.HEIGHT); bindingsList.addToBack(item); @@ -132,7 +146,7 @@ public class WndKeyBindings extends Window { RedButton btnDefaults = new RedButton(Messages.get(this, "default"), 9){ @Override protected void onClick() { - changedBindings = SPDAction.getDefaults(); + changedBindings = controller ? SPDAction.getControllerDefaults() : SPDAction.getDefaults(); for (BindingItem i : listItems){ int key1 = 0; int key2 = 0; @@ -154,7 +168,8 @@ public class WndKeyBindings extends Window { RedButton btnConfirm = new RedButton(Messages.get(this, "confirm"), 9){ @Override protected void onClick() { - KeyBindings.setAllBindings(changedBindings); + if (controller) KeyBindings.setAllControllerBindings(changedBindings); + else KeyBindings.setAllBindings(changedBindings); SPDAction.saveBindings(); hide(); } @@ -221,7 +236,12 @@ public class WndKeyBindings extends Window { actionName.setHightlighting(false); add(actionName); - ArrayList keys = KeyBindings.getBoundKeysForAction(action); + ArrayList keys; + if (controller){ + keys = KeyBindings.getControllerKeysForAction(action); + } else { + keys = KeyBindings.getKeyboardKeysForAction(action); + } origKey1 = key1 = keys.isEmpty() ? 0 : keys.remove(0); origKey2 = key2 = keys.isEmpty() ? 0 : keys.remove(0); origKey3 = key3 = keys.isEmpty() ? 0 : keys.remove(0); @@ -465,6 +485,11 @@ public class WndKeyBindings extends Window { if (btnCancel.inside(hoverPos.x, hoverPos.y)) return true; } + //ignore controller buttons on key bindings, and vice-versa + if (ControllerHandler.icControllerKey(event.code) != controller){ + return true; + } + if (event.pressed){ changedKey.text(Messages.get(this, "changed_bind", KeyBindings.getKeyName(event.code))); changedKey.setPos((WIDTH - changedKey.width())/2, changedKey.top()); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndSettings.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndSettings.java index 6b64a2d5c..5682cb928 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndSettings.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndSettings.java @@ -689,7 +689,7 @@ public class WndSettings extends WndTabbed { @Override protected void onClick() { super.onClick(); - ShatteredPixelDungeon.scene().addToFront(new WndKeyBindings()); + ShatteredPixelDungeon.scene().addToFront(new WndKeyBindings(false)); } }; @@ -701,7 +701,7 @@ public class WndSettings extends WndTabbed { @Override protected void onClick() { super.onClick(); - //TODO Controller bindings menu + ShatteredPixelDungeon.scene().addToFront(new WndKeyBindings(true)); } };