package org.terasology.input;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.terasology.components.LocalPlayerComponent;
import org.terasology.entitySystem.EntityRef;
import org.terasology.entitySystem.EventHandlerSystem;
import org.terasology.entitySystem.EventPriority;
import org.terasology.entitySystem.ReceiveEvent;
import org.terasology.events.input.*;
import org.terasology.events.input.binds.*;
import org.terasology.game.CoreRegistry;
import org.terasology.logic.LocalPlayer;
import org.terasology.logic.manager.Config;
import org.terasology.logic.manager.GUIManager;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* This system processes input, sending it out as events against the LocalPlayer entity.
* <p/>
* In addition to raw keyboard and mouse input, the system handles Bind Buttons and Bind Axis, which can be mapped
* to one or more inputs.
*/
public class InputSystem implements EventHandlerSystem {
private Logger logger = Logger.getLogger(getClass().getName());
private float mouseSensitivity = (float) Config.getInstance().getMouseSens();
private Map<String, BindableAxisImpl> axisLookup = Maps.newHashMap();
private Map<String, BindableButtonImpl> buttonLookup = Maps.newHashMap();
private List<BindableAxisImpl> axisBinds = Lists.newArrayList();
private List<BindableButtonImpl> buttonBinds = Lists.newArrayList();
// Links between primitive inputs and bind buttons
private Map<Integer, BindableButtonImpl> keyBinds = Maps.newHashMap();
private Map<Integer, BindableButtonImpl> mouseButtonBinds = Maps.newHashMap();
private BindableButtonImpl mouseWheelUpBind;
private BindableButtonImpl mouseWheelDownBind;
private LocalPlayer localPlayer;
private CameraTargetSystem cameraTargetSystem;
public void initialise() {
localPlayer = CoreRegistry.get(LocalPlayer.class);
cameraTargetSystem = CoreRegistry.get(CameraTargetSystem.class);
REPLACE_THIS_WITH_CONFIG();
}
@Override
public void shutdown() {
}
public BindableButton registerBindButton(String bindId, String displayName) {
return registerBindButton(bindId, displayName, new BindButtonEvent());
}
public BindableButton registerBindButton(String bindId, String displayName, BindButtonEvent event) {
BindableButtonImpl bind = new BindableButtonImpl(bindId, displayName, event);
buttonLookup.put(bindId, bind);
buttonBinds.add(bind);
return bind;
}
public BindableButton getBindButton(String bindId) {
return buttonLookup.get(bindId);
}
public void linkBindButtonToInput(InputEvent input, String bindId) {
if (input instanceof KeyEvent) {
linkBindButtonToKey(((KeyEvent) input).getKey(), bindId);
} else if (input instanceof MouseButtonEvent) {
linkBindButtonToMouse(((MouseButtonEvent) input).getButton(), bindId);
} else if (input instanceof MouseWheelEvent) {
linkBindButtonToMouseWheel(((MouseWheelEvent) input).getWheelTurns(), bindId);
}
}
public void linkBindButtonToKey(int key, String bindId) {
BindableButtonImpl bindInfo = buttonLookup.get(bindId);
keyBinds.put(key, bindInfo);
}
public void linkBindButtonToMouse(int mouseButton, String bindId) {
BindableButtonImpl bindInfo = buttonLookup.get(bindId);
mouseButtonBinds.put(mouseButton, bindInfo);
}
public void linkBindButtonToMouseWheel(int direction, String bindId) {
if (direction > 0) {
mouseWheelUpBind = buttonLookup.get(bindId);
} else if (direction < 0) {
mouseWheelDownBind = buttonLookup.get(bindId);
}
}
public BindableAxis registerBindAxis(String id, BindableButton positiveButton, BindableButton negativeButton) {
return registerBindAxis(id, new BindAxisEvent(), positiveButton, negativeButton);
}
public BindableAxis registerBindAxis(String id, BindAxisEvent event, String positiveButtonId, String negativeButtonId) {
return registerBindAxis(id, event, getBindButton(positiveButtonId), getBindButton(negativeButtonId));
}
public BindableAxis registerBindAxis(String id, BindAxisEvent event, BindableButton positiveButton, BindableButton negativeButton) {
BindableAxisImpl axis = new BindableAxisImpl(id, event, positiveButton, negativeButton);
axisBinds.add(axis);
axisLookup.put(id, axis);
return axis;
}
public void update(float delta) {
processMouseInput(delta);
processKeyboardInput(delta);
processBindRepeats(delta);
processBindAxis(delta);
}
private void processMouseInput(float delta) {
while (Mouse.next()) {
if (Mouse.getEventButton() != -1) {
int button = Mouse.getEventButton();
boolean buttonDown = Mouse.getEventButtonState();
boolean consumed = sendMouseEvent(button, buttonDown, delta);
BindableButtonImpl bind = mouseButtonBinds.get(button);
if (bind != null) {
bind.updateBindState(buttonDown, delta, cameraTargetSystem.getTarget(), localPlayer.getEntity(), consumed, GUIManager.getInstance().isConsumingInput());
}
} else if (Mouse.getEventDWheel() != 0) {
int wheelMoved = Mouse.getEventDWheel();
boolean consumed = sendMouseWheelEvent(wheelMoved / 120, delta);
BindableButtonImpl bind = (wheelMoved > 0) ? mouseWheelUpBind : mouseWheelDownBind;
if (bind != null) {
bind.updateBindState(true, delta, cameraTargetSystem.getTarget(), localPlayer.getEntity(), consumed, GUIManager.getInstance().isConsumingInput());
bind.updateBindState(false, delta, cameraTargetSystem.getTarget(), localPlayer.getEntity(), consumed, GUIManager.getInstance().isConsumingInput());
}
}
}
int deltaX = Mouse.getDX();
if (deltaX != 0 && !GUIManager.getInstance().isConsumingInput()) {
localPlayer.getEntity().send(new MouseXAxisEvent(deltaX * mouseSensitivity, delta, cameraTargetSystem.getTarget()));
}
int deltaY = Mouse.getDY();
if (deltaY != 0 && !GUIManager.getInstance().isConsumingInput()) {
localPlayer.getEntity().send(new MouseYAxisEvent(deltaY * mouseSensitivity, delta, cameraTargetSystem.getTarget()));
}
}
@ReceiveEvent(components = LocalPlayerComponent.class, priority = EventPriority.PRIORITY_HIGH)
public void sendEventToGUI(MouseButtonEvent mouseEvent, EntityRef entity) {
if (GUIManager.getInstance().isConsumingInput()) {
GUIManager.getInstance().processMouseInput(mouseEvent.getButton(), mouseEvent.getState() != ButtonState.UP, 0);
mouseEvent.consume();
}
}
@ReceiveEvent(components = LocalPlayerComponent.class, priority = EventPriority.PRIORITY_HIGH)
public void sendEventToGUI(MouseWheelEvent mouseEvent, EntityRef entity) {
if (GUIManager.getInstance().isConsumingInput()) {
GUIManager.getInstance().processMouseInput(-1, false, mouseEvent.getWheelTurns() * 120);
mouseEvent.consume();
}
}
private void processKeyboardInput(float delta) {
boolean guiConsumingInput = GUIManager.getInstance().isConsumingInput();
while (Keyboard.next()) {
int key = Keyboard.getEventKey();
ButtonState state = getButtonState(Keyboard.getEventKeyState(), Keyboard.isRepeatEvent());
boolean consumed = sendKeyEvent(key, state, delta);
// Update bind
BindableButtonImpl bind = keyBinds.get(key);
if (bind != null && !Keyboard.isRepeatEvent()) {
bind.updateBindState(Keyboard.getEventKeyState(), delta, cameraTargetSystem.getTarget(), localPlayer.getEntity(), consumed, guiConsumingInput);
}
}
}
@ReceiveEvent(components = LocalPlayerComponent.class, priority = EventPriority.PRIORITY_HIGH)
public void sendEventToGUI(KeyEvent keyEvent, EntityRef entity) {
if (GUIManager.getInstance().isConsumingInput()) {
if (keyEvent.getState() != ButtonState.UP) {
GUIManager.getInstance().processKeyboardInput(keyEvent.getKey());
}
keyEvent.consume();
}
}
private void processBindAxis(float delta) {
for (BindableAxisImpl axis : axisBinds) {
axis.update(localPlayer.getEntity(), delta, cameraTargetSystem.getTarget());
}
}
private void processBindRepeats(float delta) {
for (BindableButtonImpl button : buttonBinds) {
button.update(localPlayer.getEntity(), delta, cameraTargetSystem.getTarget());
}
}
private ButtonState getButtonState(boolean keyDown, boolean repeatEvent) {
if (repeatEvent) {
return ButtonState.REPEAT;
}
return (keyDown) ? ButtonState.DOWN : ButtonState.UP;
}
private boolean sendKeyEvent(int key, ButtonState state, float delta) {
KeyEvent event;
switch (state) {
case UP:
event = KeyUpEvent.create(key, delta, cameraTargetSystem.getTarget());
break;
case DOWN:
event = KeyDownEvent.create(key, delta, cameraTargetSystem.getTarget());
break;
case REPEAT:
event = KeyRepeatEvent.create(key, delta, cameraTargetSystem.getTarget());
break;
default:
return false;
}
localPlayer.getEntity().send(event);
return event.isConsumed();
}
private boolean sendMouseEvent(int button, boolean buttonDown, float delta) {
MouseButtonEvent event;
switch (button) {
case -1:
return false;
case 0:
event = (buttonDown) ? LeftMouseDownButtonEvent.create(delta, cameraTargetSystem.getTarget()) : LeftMouseUpButtonEvent.create(delta, cameraTargetSystem.getTarget());
break;
case 1:
event = (buttonDown) ? RightMouseDownButtonEvent.create(delta, cameraTargetSystem.getTarget()) : RightMouseUpButtonEvent.create(delta, cameraTargetSystem.getTarget());
break;
default:
event = (buttonDown) ? MouseDownButtonEvent.create(button, delta, cameraTargetSystem.getTarget()) : MouseUpButtonEvent.create(button, delta, cameraTargetSystem.getTarget());
break;
}
localPlayer.getEntity().send(event);
return event.isConsumed();
}
private boolean sendMouseWheelEvent(int wheelTurns, float delta) {
MouseWheelEvent mouseWheelEvent = new MouseWheelEvent(wheelTurns, delta, cameraTargetSystem.getTarget());
localPlayer.getEntity().send(mouseWheelEvent);
return mouseWheelEvent.isConsumed();
}
private void REPLACE_THIS_WITH_CONFIG() {
registerBindButton(InventoryButton.ID, "Inventory", new InventoryButton());
linkBindButtonToKey(Keyboard.KEY_I, InventoryButton.ID);
registerBindButton(ConsoleButton.ID, "Console", new ConsoleButton());
linkBindButtonToKey(Keyboard.KEY_TAB, ConsoleButton.ID);
registerBindButton(PauseButton.ID, "Pause", new PauseButton());
linkBindButtonToKey(Keyboard.KEY_ESCAPE, PauseButton.ID);
registerBindButton(ForwardsButton.ID, "Forwards", new ForwardsButton());
linkBindButtonToKey(Keyboard.KEY_W, ForwardsButton.ID);
registerBindButton(BackwardsButton.ID, "Backwards", new BackwardsButton());
linkBindButtonToKey(Keyboard.KEY_S, BackwardsButton.ID);
registerBindAxis(ForwardsMovementAxis.ID, new ForwardsMovementAxis(), ForwardsButton.ID, BackwardsButton.ID).setSendEventMode(BindableAxisImpl.SendEventMode.WHEN_CHANGED);
registerBindButton(LeftStrafeButton.ID, "Left", new LeftStrafeButton());
linkBindButtonToKey(Keyboard.KEY_A, LeftStrafeButton.ID);
registerBindButton(RightStrafeButton.ID, "Right", new RightStrafeButton());
linkBindButtonToKey(Keyboard.KEY_D, RightStrafeButton.ID);
registerBindAxis(StrafeMovementAxis.ID, new StrafeMovementAxis(), LeftStrafeButton.ID, RightStrafeButton.ID).setSendEventMode(BindableAxisImpl.SendEventMode.WHEN_CHANGED);
registerBindButton(JumpButton.ID, "Jump", new JumpButton());
linkBindButtonToKey(Keyboard.KEY_SPACE, JumpButton.ID);
registerBindButton(CrouchButton.ID, "Crouch", new CrouchButton());
linkBindButtonToKey(Keyboard.KEY_C, CrouchButton.ID);
registerBindAxis(VerticalMovementAxis.ID, new VerticalMovementAxis(), JumpButton.ID, CrouchButton.ID).setSendEventMode(BindableAxisImpl.SendEventMode.WHEN_CHANGED);
registerBindButton(RunButton.ID, "Run", new RunButton());
linkBindButtonToKey(Keyboard.KEY_LSHIFT, RunButton.ID);
linkBindButtonToKey(Keyboard.KEY_RSHIFT, RunButton.ID);
registerBindButton(AttackButton.ID, "Attack", new AttackButton()).setRepeating(true);
linkBindButtonToMouse(1, AttackButton.ID);
registerBindButton(UseItemButton.ID, "Use Held Item", new UseItemButton()).setRepeating(true);
linkBindButtonToMouse(0, UseItemButton.ID);
registerBindButton(FrobButton.ID, "Frob", new FrobButton());
linkBindButtonToKey(Keyboard.KEY_E, FrobButton.ID);
registerBindButton(ToolbarNextButton.ID, "Toolbar Next", new ToolbarNextButton()).setMode(BindableButtonImpl.ActivateMode.PRESS);
linkBindButtonToMouseWheel(+1, ToolbarNextButton.ID);
registerBindButton(ToolbarPrevButton.ID, "Toolbar Previous", new ToolbarPrevButton()).setMode(BindableButtonImpl.ActivateMode.PRESS);
linkBindButtonToMouseWheel(-1, ToolbarPrevButton.ID);
for (int i = 0; i < 9; ++i) {
String inventorySlotBind = "engine:toolbarSlot" + i;
registerBindButton(inventorySlotBind, "Inventory Slot " + (i + 1), new ToolbarSlotButton(i));
linkBindButtonToKey(Keyboard.KEY_1 + i, inventorySlotBind);
}
}
}