    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}