Re-Add key binding modifiers and contexts.

This commit is contained in:
mezz 2016-04-03 01:40:38 -07:00
parent 9baabf9249
commit 21d39f6558
10 changed files with 630 additions and 2 deletions

View File

@ -474,7 +474,15 @@
}
public boolean func_70002_Q()
@@ -3192,4 +3084,10 @@
@@ -3066,6 +2958,7 @@
this.field_71456_v.func_146158_b().func_146227_a(ScreenShotHelper.func_148260_a(this.field_71412_D, this.field_71443_c, this.field_71440_d, this.field_147124_at));
}
}
+ else if (this.field_71462_r instanceof GuiControls) ((GuiControls)this.field_71462_r).field_146491_f = null;
}
}
}
@@ -3192,4 +3085,10 @@
{
return this.field_184127_aH;
}

View File

@ -0,0 +1,49 @@
--- ../src-base/minecraft/net/minecraft/client/gui/GuiControls.java
+++ ../src-work/minecraft/net/minecraft/client/gui/GuiControls.java
@@ -66,6 +66,7 @@
for (KeyBinding keybinding : this.field_146297_k.field_71474_y.field_74324_K)
{
keybinding.func_151462_b(keybinding.func_151469_h());
+ keybinding.setKeyModifier(keybinding.getKeyModifierDefault());
}
KeyBinding.func_74508_b();
@@ -81,6 +82,7 @@
{
if (this.field_146491_f != null)
{
+ this.field_146491_f.setKeyModifier(net.minecraftforge.client.settings.KeyModifier.getActiveModifier());
this.field_146497_i.func_151440_a(this.field_146491_f, -100 + p_73864_3_);
this.field_146491_f = null;
KeyBinding.func_74508_b();
@@ -105,17 +107,21 @@
{
if (p_73869_2_ == 1)
{
+ this.field_146491_f.setKeyModifier(net.minecraftforge.client.settings.KeyModifier.NONE);
this.field_146497_i.func_151440_a(this.field_146491_f, 0);
}
else if (p_73869_2_ != 0)
{
+ this.field_146491_f.setKeyModifier(net.minecraftforge.client.settings.KeyModifier.isKeyCodeModifier(p_73869_2_) ? net.minecraftforge.client.settings.KeyModifier.NONE : net.minecraftforge.client.settings.KeyModifier.getActiveModifier());
this.field_146497_i.func_151440_a(this.field_146491_f, p_73869_2_);
}
else if (p_73869_1_ > 0)
{
+ this.field_146491_f.setKeyModifier(net.minecraftforge.client.settings.KeyModifier.isKeyCodeModifier(p_73869_2_) ? net.minecraftforge.client.settings.KeyModifier.NONE : net.minecraftforge.client.settings.KeyModifier.getActiveModifier());
this.field_146497_i.func_151440_a(this.field_146491_f, p_73869_1_ + 256);
}
+ if (!net.minecraftforge.client.settings.KeyModifier.isKeyCodeModifier(p_73869_2_))
this.field_146491_f = null;
this.field_152177_g = Minecraft.func_71386_F();
KeyBinding.func_74508_b();
@@ -135,7 +141,7 @@
for (KeyBinding keybinding : this.field_146497_i.field_74324_K)
{
- if (keybinding.func_151463_i() != keybinding.func_151469_h())
+ if (keybinding.func_151463_i() != keybinding.func_151469_h() || keybinding.getKeyModifier() != keybinding.getKeyModifierDefault())
{
flag = false;
break;

View File

@ -0,0 +1,53 @@
--- ../src-base/minecraft/net/minecraft/client/gui/GuiKeyBindingList.java
+++ ../src-work/minecraft/net/minecraft/client/gui/GuiKeyBindingList.java
@@ -62,7 +62,7 @@
protected int func_148137_d()
{
- return super.func_148137_d() + 15;
+ return super.func_148137_d() + 35;
}
public int func_148139_c()
@@ -113,7 +113,7 @@
{
this.field_148282_b = p_i45029_2_;
this.field_148283_c = I18n.func_135052_a(p_i45029_2_.func_151464_g(), new Object[0]);
- this.field_148280_d = new GuiButton(0, 0, 0, 75, 20, I18n.func_135052_a(p_i45029_2_.func_151464_g(), new Object[0]));
+ this.field_148280_d = new GuiButton(0, 0, 0, 95, 20, I18n.func_135052_a(p_i45029_2_.func_151464_g(), new Object[0]));
this.field_148281_e = new GuiButton(0, 0, 0, 50, 20, I18n.func_135052_a("controls.reset", new Object[0]));
}
@@ -121,20 +121,20 @@
{
boolean flag = GuiKeyBindingList.this.field_148191_k.field_146491_f == this.field_148282_b;
GuiKeyBindingList.this.field_148189_l.field_71466_p.func_78276_b(this.field_148283_c, p_180790_2_ + 90 - GuiKeyBindingList.this.field_148188_n, p_180790_3_ + p_180790_5_ / 2 - GuiKeyBindingList.this.field_148189_l.field_71466_p.field_78288_b / 2, 16777215);
- this.field_148281_e.field_146128_h = p_180790_2_ + 190;
+ this.field_148281_e.field_146128_h = p_180790_2_ + 210;
this.field_148281_e.field_146129_i = p_180790_3_;
- this.field_148281_e.field_146124_l = this.field_148282_b.func_151463_i() != this.field_148282_b.func_151469_h();
+ this.field_148281_e.field_146124_l = this.field_148282_b.func_151463_i() != this.field_148282_b.func_151469_h() || this.field_148282_b.getKeyModifier() != this.field_148282_b.getKeyModifierDefault();
this.field_148281_e.func_146112_a(GuiKeyBindingList.this.field_148189_l, p_180790_6_, p_180790_7_);
this.field_148280_d.field_146128_h = p_180790_2_ + 105;
this.field_148280_d.field_146129_i = p_180790_3_;
- this.field_148280_d.field_146126_j = GameSettings.func_74298_c(this.field_148282_b.func_151463_i());
+ this.field_148280_d.field_146126_j = this.field_148282_b.getKeyModifier().getLocalizedComboName(GameSettings.func_74298_c(this.field_148282_b.func_151463_i()));
boolean flag1 = false;
if (this.field_148282_b.func_151463_i() != 0)
{
for (KeyBinding keybinding : GuiKeyBindingList.this.field_148189_l.field_71474_y.field_74324_K)
{
- if (keybinding != this.field_148282_b && keybinding.func_151463_i() == this.field_148282_b.func_151463_i())
+ if (keybinding != this.field_148282_b && keybinding.conflicts(this.field_148282_b))
{
flag1 = true;
break;
@@ -163,6 +163,7 @@
}
else if (this.field_148281_e.func_146116_c(GuiKeyBindingList.this.field_148189_l, p_148278_2_, p_148278_3_))
{
+ this.field_148282_b.setKeyModifier(this.field_148282_b.getKeyModifierDefault());
GuiKeyBindingList.this.field_148189_l.field_71474_y.func_151440_a(this.field_148282_b, this.field_148282_b.func_151469_h());
KeyBinding.func_74508_b();
return true;

View File

@ -1,6 +1,30 @@
--- ../src-base/minecraft/net/minecraft/client/settings/GameSettings.java
+++ ../src-work/minecraft/net/minecraft/client/settings/GameSettings.java
@@ -887,6 +887,7 @@
@@ -147,6 +147,7 @@
public GameSettings(Minecraft p_i46326_1_, File p_i46326_2_)
{
+ setForgeKeybindProperties();
this.field_74324_K = (KeyBinding[])ArrayUtils.addAll(new KeyBinding[] {this.field_74312_F, this.field_74313_G, this.field_74351_w, this.field_74370_x, this.field_74368_y, this.field_74366_z, this.field_74314_A, this.field_74311_E, this.field_151444_V, this.field_74316_C, this.field_151445_Q, this.field_74310_D, this.field_74321_H, this.field_74322_I, this.field_74323_J, this.field_151447_Z, this.field_151457_aa, this.field_151458_ab, this.field_152395_am, this.field_178883_an, this.field_186718_X}, this.field_151456_ac);
this.field_74318_M = EnumDifficulty.NORMAL;
this.field_74332_R = "";
@@ -171,6 +172,7 @@
public GameSettings()
{
+ setForgeKeybindProperties();
this.field_74324_K = (KeyBinding[])ArrayUtils.addAll(new KeyBinding[] {this.field_74312_F, this.field_74313_G, this.field_74351_w, this.field_74370_x, this.field_74368_y, this.field_74366_z, this.field_74314_A, this.field_74311_E, this.field_151444_V, this.field_74316_C, this.field_151445_Q, this.field_74310_D, this.field_74321_H, this.field_74322_I, this.field_74323_J, this.field_151447_Z, this.field_151457_aa, this.field_151458_ab, this.field_152395_am, this.field_178883_an, this.field_186718_X}, this.field_151456_ac);
this.field_74318_M = EnumDifficulty.NORMAL;
this.field_74332_R = "";
@@ -846,6 +848,7 @@
if (astring[0].equals("key_" + keybinding.func_151464_g()))
{
keybinding.func_151462_b(Integer.parseInt(astring[1]));
+ if (astring.length == 3) keybinding.setKeyModifier(net.minecraftforge.client.settings.KeyModifier.valueOf(astring[2]));
}
}
@@ -887,6 +890,7 @@
public void func_74303_b()
{
@ -8,3 +32,39 @@
try
{
PrintWriter printwriter = new PrintWriter(new FileWriter(this.field_74354_ai));
@@ -955,7 +959,8 @@
for (KeyBinding keybinding : this.field_74324_K)
{
- printwriter.println("key_" + keybinding.func_151464_g() + ":" + keybinding.func_151463_i());
+ String keyString = "key_" + keybinding.func_151464_g() + ":" + keybinding.func_151463_i();
+ printwriter.println(keybinding.getKeyModifier() != net.minecraftforge.client.settings.KeyModifier.NONE ? keyString + ":" + keybinding.getKeyModifier() : keyString);
}
for (SoundCategory soundcategory : SoundCategory.values())
@@ -1184,4 +1189,25 @@
return p_148264_1_;
}
}
+
+ /******* Forge Start ***********/
+ private void setForgeKeybindProperties() {
+ net.minecraftforge.client.settings.KeyConflictContext inGame = net.minecraftforge.client.settings.KeyConflictContext.IN_GAME;
+ field_74351_w.setKeyConflictContext(inGame);
+ field_74370_x.setKeyConflictContext(inGame);
+ field_74368_y.setKeyConflictContext(inGame);
+ field_74366_z.setKeyConflictContext(inGame);
+ field_74314_A.setKeyConflictContext(inGame);
+ field_74311_E.setKeyConflictContext(inGame);
+ field_151444_V.setKeyConflictContext(inGame);
+ field_74312_F.setKeyConflictContext(inGame);
+ field_74322_I.setKeyConflictContext(inGame);
+ field_74310_D.setKeyConflictContext(inGame);
+ field_74321_H.setKeyConflictContext(inGame);
+ field_74323_J.setKeyConflictContext(inGame);
+ field_151457_aa.setKeyConflictContext(inGame);
+ field_151458_ab.setKeyConflictContext(inGame);
+ field_186718_X.setKeyConflictContext(inGame);
+ }
+ /******* Forge End ***********/
}

View File

@ -0,0 +1,162 @@
--- ../src-base/minecraft/net/minecraft/client/settings/KeyBinding.java
+++ ../src-work/minecraft/net/minecraft/client/settings/KeyBinding.java
@@ -14,7 +14,7 @@
public class KeyBinding implements Comparable<KeyBinding>
{
private static final List<KeyBinding> field_74516_a = Lists.<KeyBinding>newArrayList();
- private static final IntHashMap<KeyBinding> field_74514_b = new IntHashMap();
+ private static final net.minecraftforge.client.settings.KeyBindingMap field_74514_b = new net.minecraftforge.client.settings.KeyBindingMap();
private static final Set<String> field_151473_c = Sets.<String>newHashSet();
private final String field_74515_c;
private final int field_151472_e;
@@ -27,7 +27,7 @@
{
if (p_74507_0_ != 0)
{
- KeyBinding keybinding = (KeyBinding)field_74514_b.func_76041_a(p_74507_0_);
+ KeyBinding keybinding = field_74514_b.lookupActive(p_74507_0_);
if (keybinding != null)
{
@@ -40,7 +40,7 @@
{
if (p_74510_0_ != 0)
{
- KeyBinding keybinding = (KeyBinding)field_74514_b.func_76041_a(p_74510_0_);
+ for (KeyBinding keybinding : field_74514_b.lookupAll(p_74510_0_))
if (keybinding != null)
{
@@ -74,11 +74,11 @@
public static void func_74508_b()
{
- field_74514_b.func_76046_c();
+ field_74514_b.clearMap();
for (KeyBinding keybinding : field_74516_a)
{
- field_74514_b.func_76038_a(keybinding.field_74512_d, keybinding);
+ field_74514_b.addKey(keybinding.field_74512_d, keybinding);
}
}
@@ -94,13 +94,13 @@
this.field_151472_e = p_i45001_2_;
this.field_151471_f = p_i45001_3_;
field_74516_a.add(this);
- field_74514_b.func_76038_a(p_i45001_2_, this);
+ field_74514_b.addKey(p_i45001_2_, this);
field_151473_c.add(p_i45001_3_);
}
public boolean func_151470_d()
{
- return this.field_74513_e;
+ return this.field_74513_e && getKeyConflictContext().isActive() && (!allowsKeyModifiers || getKeyModifier().isActive());
}
public String func_151466_e()
@@ -158,4 +158,102 @@
return i;
}
+
+ /****************** Forge Start *****************************/
+ private net.minecraftforge.client.settings.KeyModifier keyModifierDefault = net.minecraftforge.client.settings.KeyModifier.NONE;
+ private net.minecraftforge.client.settings.KeyModifier keyModifier = net.minecraftforge.client.settings.KeyModifier.NONE;
+ private net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext = net.minecraftforge.client.settings.KeyConflictContext.UNIVERSAL;
+ private boolean allowsKeyModifiers = false;
+
+ /**
+ * Convenience constructor for creating KeyBindings with keyConflictContext set.
+ */
+ public KeyBinding(String description, net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext, int keyCode, String category)
+ {
+ this(description, keyConflictContext, net.minecraftforge.client.settings.KeyModifier.NONE, keyCode, category);
+ }
+
+ /**
+ * Convenience constructor for creating KeyBindings with keyConflictContext and keyModifier set.
+ */
+ public KeyBinding(String description, net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext, net.minecraftforge.client.settings.KeyModifier keyModifier, int keyCode, String category)
+ {
+ this.field_74515_c = description;
+ this.field_74512_d = keyCode;
+ this.field_151472_e = keyCode;
+ this.field_151471_f = category;
+ this.keyConflictContext = keyConflictContext;
+ this.keyModifier = keyModifier;
+ this.keyModifierDefault = keyModifier;
+ if (keyModifier != net.minecraftforge.client.settings.KeyModifier.NONE)
+ setAllowsKeyModifiers();
+ field_74516_a.add(this);
+ field_74514_b.addKey(keyCode, this);
+ field_151473_c.add(category);
+ }
+
+ /**
+ * Call this method to let Forge know that your KeyBinding and code can handle players setting modifiers.
+ * Handling modifiers is made easy by using {@link KeyBinding#isActiveAndMatches(int)} or {@link KeyBinding#isPressed()}
+ * Vanilla code does not handle modifiers, so vanilla keyBinds do not have this set.
+ */
+ public void setAllowsKeyModifiers()
+ {
+ this.allowsKeyModifiers = true;
+ }
+
+ /**
+ * Checks that the key conflict context and modifier are active, and that the keyCode matches this binding.
+ */
+ public boolean isActiveAndMatches(int keyCode)
+ {
+ return keyCode == this.func_151463_i() && getKeyConflictContext().isActive() && (!allowsKeyModifiers || getKeyModifier().isActive());
+ }
+
+ public void setKeyConflictContext(net.minecraftforge.client.settings.IKeyConflictContext keyConflictContext)
+ {
+ this.keyConflictContext = keyConflictContext;
+ }
+
+ public net.minecraftforge.client.settings.IKeyConflictContext getKeyConflictContext()
+ {
+ return keyConflictContext;
+ }
+
+ public net.minecraftforge.client.settings.KeyModifier getKeyModifierDefault()
+ {
+ return keyModifierDefault;
+ }
+
+ public net.minecraftforge.client.settings.KeyModifier getKeyModifier()
+ {
+ if (allowsKeyModifiers)
+ {
+ return keyModifier;
+ }
+ else
+ {
+ return net.minecraftforge.client.settings.KeyModifier.NONE;
+ }
+ }
+
+ public void setKeyModifier(net.minecraftforge.client.settings.KeyModifier keyModifier)
+ {
+ field_74514_b.removeKey(this);
+ this.keyModifier = keyModifier;
+ field_74514_b.addKey(field_74512_d, this);
+ }
+
+ public boolean conflicts(KeyBinding other)
+ {
+ if (getKeyConflictContext().conflicts(other.getKeyConflictContext()) || other.getKeyConflictContext().conflicts(getKeyConflictContext()))
+ {
+ if (!allowsKeyModifiers || !other.allowsKeyModifiers || getKeyModifier() == other.getKeyModifier())
+ {
+ return func_151463_i() == other.func_151463_i();
+ }
+ }
+ return false;
+ }
+ /****************** Forge End *****************************/
}

View File

@ -0,0 +1,20 @@
package net.minecraftforge.client.settings;
import net.minecraft.client.settings.KeyBinding;
/**
* Defines the context that a {@link KeyBinding} is used.
* Key conflicts occur when a {@link KeyBinding} has the same {@link IKeyConflictContext} and has conflicting modifiers and keyCodes.
*/
public interface IKeyConflictContext {
/**
* @return true if conditions are met to activate {@link KeyBinding}s with this context
*/
boolean isActive();
/**
* @return true if the other context can have {@link KeyBinding} conflicts with this one.
* This will be called on both contexts to check for conflicts.
*/
boolean conflicts(IKeyConflictContext other);
}

View File

@ -0,0 +1,89 @@
package net.minecraftforge.client.settings;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.util.IntHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.List;
public class KeyBindingMap
{
private static final EnumMap<KeyModifier, IntHashMap<Collection<KeyBinding>>> map = new java.util.EnumMap<KeyModifier, IntHashMap<Collection<KeyBinding>>>(KeyModifier.class);
static
{
for (KeyModifier modifier : KeyModifier.values())
{
map.put(modifier, new IntHashMap<Collection<KeyBinding>>());
}
}
public KeyBinding lookupActive(int keyCode)
{
KeyModifier activeModifier = KeyModifier.isKeyCodeModifier(keyCode) ? KeyModifier.NONE : KeyModifier.getActiveModifier();
Collection<KeyBinding> bindings = map.get(activeModifier).lookup(keyCode);
if (bindings != null)
{
for (KeyBinding binding : bindings)
{
if (binding.isActiveAndMatches(keyCode))
{
return binding;
}
}
}
return null;
}
public List<KeyBinding> lookupAll(int keyCode)
{
List<KeyBinding> matchingBindings = new ArrayList<KeyBinding>();
for (IntHashMap<Collection<KeyBinding>> bindingsMap : map.values())
{
Collection<KeyBinding> bindings = bindingsMap.lookup(keyCode);
if (bindings != null)
{
matchingBindings.addAll(bindings);
}
}
return matchingBindings;
}
public void addKey(int keyCode, KeyBinding keyBinding)
{
KeyModifier keyModifier = keyBinding.getKeyModifier();
IntHashMap<Collection<KeyBinding>> bindingsMap = map.get(keyModifier);
Collection<KeyBinding> bindingsForKey = bindingsMap.lookup(keyCode);
if (bindingsForKey == null)
{
bindingsForKey = new ArrayList<KeyBinding>();
bindingsMap.addKey(keyCode, bindingsForKey);
}
bindingsForKey.add(keyBinding);
}
public void removeKey(KeyBinding keyBinding)
{
KeyModifier keyModifier = keyBinding.getKeyModifier();
int keyCode = keyBinding.getKeyCode();
IntHashMap<Collection<KeyBinding>> bindingsMap = map.get(keyModifier);
Collection<KeyBinding> bindingsForKey = bindingsMap.lookup(keyCode);
if (bindingsForKey != null)
{
bindingsForKey.remove(keyBinding);
if (bindingsForKey.isEmpty())
{
bindingsMap.removeObject(keyCode);
}
}
}
public void clearMap()
{
for (IntHashMap<Collection<KeyBinding>> bindings : map.values())
{
bindings.clearMap();
}
}
}

View File

@ -0,0 +1,59 @@
package net.minecraftforge.client.settings;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
public enum KeyConflictContext implements IKeyConflictContext
{
/**
* Universal key bindings are used in every context and will conflict with any other context.
* Key Bindings are universal by default.
*/
UNIVERSAL {
@Override
public boolean isActive()
{
return true;
}
@Override
public boolean conflicts(IKeyConflictContext other)
{
return true;
}
},
/**
* Gui key bindings are only used when a {@link GuiScreen} is open.
*/
GUI {
@Override
public boolean isActive()
{
return Minecraft.getMinecraft().currentScreen != null;
}
@Override
public boolean conflicts(IKeyConflictContext other)
{
return this == other;
}
},
/**
* In-game key bindings are only used when a {@link GuiScreen} is not open.
*/
IN_GAME {
@Override
public boolean isActive()
{
return !GUI.isActive();
}
@Override
public boolean conflicts(IKeyConflictContext other)
{
return this == other;
}
}
}

View File

@ -0,0 +1,124 @@
package net.minecraftforge.client.settings;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
public enum KeyModifier {
CONTROL {
@Override
protected boolean matches(int keyCode)
{
if (Minecraft.isRunningOnMac)
{
return keyCode == Keyboard.KEY_LMETA || keyCode == Keyboard.KEY_RMETA;
}
else
{
return keyCode == Keyboard.KEY_LCONTROL || keyCode == Keyboard.KEY_RCONTROL;
}
}
@Override
public boolean isActive()
{
return GuiScreen.isCtrlKeyDown();
}
@Override
public String getLocalizedComboName(String keyName)
{
return I18n.format("forge.controlsgui.control", keyName);
}
},
SHIFT {
@Override
protected boolean matches(int keyCode)
{
return keyCode == Keyboard.KEY_LSHIFT || keyCode == Keyboard.KEY_RSHIFT;
}
@Override
public boolean isActive()
{
return GuiScreen.isShiftKeyDown();
}
@Override
public String getLocalizedComboName(String keyName)
{
return I18n.format("forge.controlsgui.shift", keyName);
}
},
ALT {
@Override
protected boolean matches(int keyCode)
{
return keyCode == Keyboard.KEY_LMENU || keyCode == Keyboard.KEY_RMENU;
}
@Override
public boolean isActive()
{
return GuiScreen.isAltKeyDown();
}
@Override
public String getLocalizedComboName(String keyName)
{
return I18n.format("forge.controlsgui.alt", keyName);
}
},
NONE {
@Override
protected boolean matches(int keyCode)
{
return true;
}
@Override
public boolean isActive()
{
return !SHIFT.isActive() && !CONTROL.isActive() && !ALT.isActive();
}
@Override
public String getLocalizedComboName(String keyName)
{
return keyName;
}
};
public static final KeyModifier[] MODIFIER_VALUES = {SHIFT, CONTROL, ALT};
public static KeyModifier getActiveModifier()
{
for (KeyModifier keyModifier : MODIFIER_VALUES)
{
if (keyModifier.isActive())
{
return keyModifier;
}
}
return NONE;
}
public static boolean isKeyCodeModifier(int keyCode)
{
for (KeyModifier keyModifier : MODIFIER_VALUES)
{
if (keyModifier.matches(keyCode))
{
return true;
}
}
return false;
}
protected abstract boolean matches(int keyCode);
public abstract boolean isActive();
public abstract String getLocalizedComboName(String keyName);
}

View File

@ -143,6 +143,10 @@ fml.configgui.tooltip.undoChanges=Undo Changes
fml.configgui.tooltip.default=[default: %s]
fml.configgui.tooltip.defaultNumeric=[range: %s ~ %s, default: %s]
forge.controlsgui.shift=SHIFT + %s
forge.controlsgui.control=CTRL + %s
forge.controlsgui.alt=ALT + %s
fml.menu.mods=Mods
fml.menu.mods.normal=Normal
fml.menu.mods.search=Search: