/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.puppygames.applet; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.lwjgl.input.Controller; import org.lwjgl.input.Controllers; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import com.shavenpuppy.jglib.resources.Feature; /** * $Id: Binding.java,v 1.2 2010/10/14 11:59:25 foo Exp $ * Describes a binding of a key to a game command. * @author $Author: foo $ * @version $Revision: 1.2 $ */ public class Binding extends Feature { private static final long serialVersionUID = 1L; /* * Static data */ /** * Indicates a mouse button binding */ public static final String MOUSE = "mouse"; /** * Indicates a keyboard binding */ public static final String KEYBOARD = "keyboard"; /** * Indicates a controller button binding */ public static final String CONTROLLER = "controller"; /** * Indicates a controller D-Pad binding */ public static final String DPAD = "dpad"; public static final String DPAD_RIGHT = "D-Pad Right"; public static final String DPAD_UP = "D-Pad Up"; public static final String DPAD_LEFT= "D-Pad Left"; public static final String DPAD_DOWN = "D-Pad Down"; public static final int DPAD_RIGHT_INDEX = 0; public static final int DPAD_UP_INDEX = 1; public static final int DPAD_LEFT_INDEX = 2; public static final int DPAD_DOWN_INDEX = 3; /* * Standard bindings */ public static final String FOCUS_UP = "focus.up.binding"; public static final String FOCUS_DOWN = "focus.down.binding"; public static final String FOCUS_LEFT = "focus.left.binding"; public static final String FOCUS_RIGHT = "focus.right.binding"; public static final String SELECT = "select.binding"; public static final String YES = "yes.binding"; public static final String NO = "no.binding"; public static final String CANCEL = "cancel.binding"; public static final String FOCUS_UP_ALT = "focus.up.binding.alt"; public static final String FOCUS_DOWN_ALT = "focus.down.binding.alt"; public static final String FOCUS_LEFT_ALT = "focus.left.binding.alt"; public static final String FOCUS_RIGHT_ALT = "focus.right.binding.alt"; public static final String SELECT_ALT = "select.binding.alt"; public static final String YES_ALT = "yes.binding.alt"; public static final String NO_ALT = "no.binding.alt"; public static final String CANCEL_ALT = "cancel.binding.alt"; /** All bindings: a map of binding names to bindings */ private static final Map<String, Binding> BINDINGS = new HashMap<String, Binding>(); /** All bindings in order */ private static final List<Binding> ALL = new LinkedList<Binding>(); /** Whether bindings are enabled */ private static boolean enabled = true; // Enabled by default private static class SavedBinding implements Serializable { private static final long serialVersionUID = -389520805784486704L; String name; String type; int index; SavedBinding(Binding b) { name = b.getName(); type = b.type; index = b.index; } } /* * Resource data */ /** Binding type (keyboard, mouse, controller) */ private String type; /** Binding value */ private String value; /** Hold "down" */ private boolean hold; /** Description */ private String description; /** Default binding */ private String defaultType; private int defaultIndex; /* * Transient data */ /** Button index in the controller */ private transient int index; /** Is the binding down? */ private transient boolean down; /** Was the binding down last time? */ private transient boolean wasDown; /** * @param name */ public Binding(String name) { super(name); setAutoCreated(); } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#doRegister() */ @Override protected void doRegister() { BINDINGS.put(getName(), this); ALL.add(this); } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#doDeregister() */ @Override protected void doDeregister() { BINDINGS.remove(getName()); ALL.remove(this); } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#doCreate() */ @Override protected void doCreate() { super.doCreate(); if (KEYBOARD.equals(type)) { index = Keyboard.getKeyIndex(value); } else if (MOUSE.equals(type)) { index = Mouse.getButtonIndex(value); } else if (CONTROLLER.equals(type)) { index = -1; if (Controllers.getControllerCount() > 0) { Controller controller = Controllers.getController(0); for (int i = 0; i < controller.getButtonCount(); i ++) { if (controller.getButtonName(i).equals(value)) { index = i; break; } } } } else if (DPAD.equals(type)) { if (DPAD_LEFT.equals(value)) { index = DPAD_LEFT_INDEX; } else if (DPAD_RIGHT.equals(value)) { index = DPAD_RIGHT_INDEX; } else if (DPAD_UP.equals(value)) { index = DPAD_UP_INDEX; } else if (DPAD_DOWN.equals(value)) { index = DPAD_DOWN_INDEX; } } defaultType = type; defaultIndex = index; } /** * Reset this binding to its default */ private void reset() { type = defaultType; index = defaultIndex; } /** * Reset all bindings to defaults */ public static void resetToDefaults() { for (Binding b : BINDINGS.values()) { b.reset(); } validate(); } /** * Load the bindings from an input stream. After loading they are validated. * @param stream * @throws Exception */ public static void load(InputStream stream) throws Exception { ObjectInputStream ois = new ObjectInputStream(stream); List<SavedBinding> newBindings = (List<SavedBinding>) ois.readObject(); for (SavedBinding b : newBindings) { setBinding(b.name, b.type, b.index); } validate(); } /** * Save the bindings to an input stream. * @param stream * @throws Exception */ public static void save(OutputStream stream) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(stream); List<SavedBinding> output = new LinkedList<SavedBinding>(); for (Binding b : BINDINGS.values()) { output.add(new SavedBinding(b)); } oos.writeObject(output); oos.flush(); oos.reset(); } /** * Validate all the bindings. Any bindings which are invalid are cleared, * giving them a type of null. */ public static void validate() { for (Binding b : BINDINGS.values()) { if (KEYBOARD.equals(b.type) && !Keyboard.isCreated()) { b.type = null; } else if (MOUSE.equals(b.type) && !Mouse.isCreated()) { b.type = null; } else if (CONTROLLER.equals(b.type) && (!Controllers.isCreated() || Controllers.getControllerCount() == 0)) { //System.out.println("Zapped "+b); b.type = null; } else if (DPAD.equals(b.type) && (!Controllers.isCreated() || Controllers.getControllerCount() == 0)) { //System.out.println("Zapped "+b); b.type = null; } } } /** * Get all the bindings. * @return an unmodifiable List of binding names to Bindings */ public static List<Binding> getBindings() { return Collections.unmodifiableList(ALL); } /** * Is this binding "down"? Call this method after Binding.poll() * @return true if the binding is down */ public boolean isDown() { if (hold) { return down; } else if (wasDown) { return false; } else { return down; } } /** * Poll all bindings */ public static void poll() { if (!enabled) { return; } for (Iterator<Binding> i = BINDINGS.values().iterator(); i.hasNext(); ) { Binding b = i.next(); if (b.index == -1) { continue; } b.wasDown = b.down; if (KEYBOARD.equals(b.type)) { if (Keyboard.isKeyDown(b.index)) { b.down = true; } else { b.down = false; } } else if (MOUSE.equals(b.type)) { if (Mouse.getButtonCount() > b.index && Mouse.isButtonDown(b.index)) { b.down = true; } else { b.down = false; } } else if (CONTROLLER.equals(b.type)) { if (Controllers.getControllerCount() > 0 && Controllers.getController(0).getButtonCount() > b.index && Controllers.getController(0).isButtonPressed(b.index)) { b.down = true; } else { b.down = false; } } else if (DPAD.equals(b.type)) { if (Controllers.getControllerCount() == 0) { b.down = false; } else { float dx = Controllers.getController(0).getPovX(); float dy = Controllers.getController(0).getPovY(); switch (b.index) { case DPAD_LEFT_INDEX: b.down = dx < -0.5f; break; case DPAD_RIGHT_INDEX: b.down = dx > 0.5f; break; case DPAD_UP_INDEX: b.down = dy < -0.5f; break; case DPAD_DOWN_INDEX: b.down = dy > 0.5f; break; default: b.down = false; } } } } } /** * Enable all bindings. When disabled, polling has no effect * @param enable */ public static void setEnabled(boolean enabled) { Binding.enabled = enabled; for (Iterator<Binding> i = BINDINGS.values().iterator(); i.hasNext(); ) { Binding b = i.next(); b.down = false; b.wasDown = false; } } /** * Set a binding. * @param name The name of the binding * @param type The type ("keyboard" or "mouse") * @param index The button index */ public static void setBinding(String name, String type, int index) { // Look for an existing binding of that name Binding b = BINDINGS.get(name); if (b == null) { // No such binding. So, er, bugger it, do nothing. assert false : "no such binding "+name; return; } b.type = type; b.index = index; System.out.println("Set binding "+name+" to "+type+":"+index); // // See if there is an existing binding already. If there is, then clear // // the old one. // if (type != null) { // for (Iterator i = BINDINGS.values().iterator(); i.hasNext(); ) { // Binding existing = (Binding) i.next(); // if (!existing.name.equals(name)) { // if (b.type.equals(existing.type) && b.index == existing.index) { // // Clear it // existing.type = null; // existing.index = 0; // } // } // } // } validate(); // Just to be sure } /** * Is a particular named binding down? * @param name * @return boolean */ public static boolean isBindingDown(String name) { Binding b = BINDINGS.get(name); if (b == null) { return false; } else { return b.isDown(); } } /** * @return Returns the index. */ public int getIndex() { return index; } /** * @return Returns the type (null if undefined) */ public String getType() { return type; } /** * @return Returns the description. */ public String getDescription() { return description; } }