001 package cpw.mods.fml.client.registry; 002 003 import java.util.ArrayList; 004 import java.util.EnumSet; 005 import java.util.Set; 006 007 import net.minecraft.client.settings.GameSettings; 008 import net.minecraft.client.settings.KeyBinding; 009 010 import org.lwjgl.input.Keyboard; 011 import org.lwjgl.input.Mouse; 012 013 import com.google.common.collect.Lists; 014 import com.google.common.collect.Sets; 015 016 import cpw.mods.fml.common.ITickHandler; 017 import cpw.mods.fml.common.TickType; 018 import cpw.mods.fml.common.registry.TickRegistry; 019 import cpw.mods.fml.relauncher.Side; 020 021 public class KeyBindingRegistry 022 { 023 /** 024 * Register a KeyHandler to the game. This handler will be called on certain tick events 025 * if any of its key is inactive or has recently changed state 026 * 027 * @param handler 028 */ 029 public static void registerKeyBinding(KeyHandler handler) { 030 instance().keyHandlers.add(handler); 031 if (!handler.isDummy) 032 { 033 TickRegistry.registerTickHandler(handler, Side.CLIENT); 034 } 035 } 036 037 038 /** 039 * Extend this class to register a KeyBinding and recieve callback 040 * when the key binding is triggered 041 * 042 * @author cpw 043 * 044 */ 045 public static abstract class KeyHandler implements ITickHandler 046 { 047 protected KeyBinding[] keyBindings; 048 protected boolean[] keyDown; 049 protected boolean[] repeatings; 050 private boolean isDummy; 051 052 /** 053 * Pass an array of keybindings and a repeat flag for each one 054 * 055 * @param keyBindings 056 * @param repeatings 057 */ 058 public KeyHandler(KeyBinding[] keyBindings, boolean[] repeatings) 059 { 060 assert keyBindings.length == repeatings.length : "You need to pass two arrays of identical length"; 061 this.keyBindings = keyBindings; 062 this.repeatings = repeatings; 063 this.keyDown = new boolean[keyBindings.length]; 064 } 065 066 067 /** 068 * Register the keys into the system. You will do your own keyboard management elsewhere. No events will fire 069 * if you use this method 070 * 071 * @param keyBindings 072 */ 073 public KeyHandler(KeyBinding[] keyBindings) 074 { 075 this.keyBindings = keyBindings; 076 this.isDummy = true; 077 } 078 079 public KeyBinding[] getKeyBindings() 080 { 081 return this.keyBindings; 082 } 083 084 /** 085 * Not to be overridden - KeyBindings are tickhandlers under the covers 086 */ 087 @Override 088 public final void tickStart(EnumSet<TickType> type, Object... tickData) 089 { 090 keyTick(type, false); 091 } 092 093 /** 094 * Not to be overridden - KeyBindings are tickhandlers under the covers 095 */ 096 @Override 097 public final void tickEnd(EnumSet<TickType> type, Object... tickData) 098 { 099 keyTick(type, true); 100 } 101 102 private void keyTick(EnumSet<TickType> type, boolean tickEnd) 103 { 104 for (int i = 0; i < keyBindings.length; i++) 105 { 106 KeyBinding keyBinding = keyBindings[i]; 107 int keyCode = keyBinding.keyCode; 108 boolean state = (keyCode < 0 ? Mouse.isButtonDown(keyCode + 100) : Keyboard.isKeyDown(keyCode)); 109 if (state != keyDown[i] || (state && repeatings[i])) 110 { 111 if (state) 112 { 113 keyDown(type, keyBinding, tickEnd, state!=keyDown[i]); 114 } 115 else 116 { 117 keyUp(type, keyBinding, tickEnd); 118 } 119 if (tickEnd) 120 { 121 keyDown[i] = state; 122 } 123 } 124 125 } 126 } 127 128 /** 129 * Called when the key is first in the down position on any tick from the {@link #ticks()} 130 * set. Will be called subsequently with isRepeat set to true 131 * 132 * @see #keyUp(EnumSet, KeyBinding, boolean) 133 * 134 * @param types the type(s) of tick that fired when this key was first down 135 * @param tickEnd was it an end or start tick which fired the key 136 * @param isRepeat is it a repeat key event 137 */ 138 public abstract void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat); 139 /** 140 * Fired once when the key changes state from down to up 141 * 142 * @see #keyDown(EnumSet, KeyBinding, boolean, boolean) 143 * 144 * @param types the type(s) of tick that fired when this key was first down 145 * @param tickEnd was it an end or start tick which fired the key 146 */ 147 public abstract void keyUp(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd); 148 149 150 /** 151 * This is the list of ticks for which the key binding should trigger. The only 152 * valid ticks are client side ticks, obviously. 153 * 154 * @see cpw.mods.fml.common.ITickHandler#ticks() 155 */ 156 public abstract EnumSet<TickType> ticks(); 157 } 158 159 private static final KeyBindingRegistry INSTANCE = new KeyBindingRegistry(); 160 161 private Set<KeyHandler> keyHandlers = Sets.newLinkedHashSet(); 162 163 @Deprecated 164 public static KeyBindingRegistry instance() 165 { 166 return INSTANCE; 167 } 168 169 170 public void uploadKeyBindingsToGame(GameSettings settings) 171 { 172 ArrayList<KeyBinding> harvestedBindings = Lists.newArrayList(); 173 for (KeyHandler key : keyHandlers) 174 { 175 for (KeyBinding kb : key.keyBindings) 176 { 177 harvestedBindings.add(kb); 178 } 179 } 180 KeyBinding[] modKeyBindings = harvestedBindings.toArray(new KeyBinding[harvestedBindings.size()]); 181 KeyBinding[] allKeys = new KeyBinding[settings.keyBindings.length + modKeyBindings.length]; 182 System.arraycopy(settings.keyBindings, 0, allKeys, 0, settings.keyBindings.length); 183 System.arraycopy(modKeyBindings, 0, allKeys, settings.keyBindings.length, modKeyBindings.length); 184 settings.keyBindings = allKeys; 185 settings.loadOptions(); 186 } 187 }