001package cpw.mods.fml.client.registry;
002
003import java.util.ArrayList;
004import java.util.EnumSet;
005import java.util.Set;
006
007import net.minecraft.client.settings.GameSettings;
008import net.minecraft.client.settings.KeyBinding;
009
010import org.lwjgl.input.Keyboard;
011import org.lwjgl.input.Mouse;
012
013import com.google.common.collect.Lists;
014import com.google.common.collect.Sets;
015
016import cpw.mods.fml.common.ITickHandler;
017import cpw.mods.fml.common.TickType;
018import cpw.mods.fml.common.registry.TickRegistry;
019import cpw.mods.fml.relauncher.Side;
020
021public 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}