package de.fau.cs.mad.fly.game;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Preferences;
import de.fau.cs.mad.fly.Fly;
import de.fau.cs.mad.fly.features.overlay.TouchScreenOverlay;
import de.fau.cs.mad.fly.player.Player;
import de.fau.cs.mad.fly.profile.PlayerProfile;
import de.fau.cs.mad.fly.profile.PlayerProfileManager;
import de.fau.cs.mad.fly.settings.SettingManager;
/**
* Controls the flight of the player regarding to user-input
*
* @author Sebastian
*
*/
public class FlightController implements InputProcessor {
protected boolean useSensorData;
protected boolean inTouch = false;
protected int invertXFactor;
protected int invertYFactor;
protected int rotationFactor;
Set<Integer> pressedKeys = new HashSet<Integer>();
protected Player player;
protected float startRoll, startPitch;
/** */
protected float rollFactor = 0.0f;
/** */
protected float azimuthFactor = 0.0f;
/** Indicates a change to the rollFactor. 0 means that steering in rollDirection is disabled*/
protected float rollFactorChange = 1.0f;
/** Indicates a change to the azimuthFactor. 0 means that steering in azimuthDirection is disabled*/
protected float azimuthFactorChange = 1.0f;
/** The pointer to the current touchEvent*/
protected int currentEvent = -1;
protected float screenHeight = Gdx.graphics.getHeight();
protected float screenWidth = Gdx.graphics.getWidth();
// variables for Sensor input smoothing
protected int bufferSize;
protected List<Float> rollInput;
protected List<Float> pitchInput;
/** Degree of lifting and leaning the ship */
protected float roll;
/** degree of steering left and right */
protected float pitch;
/** This member defines the maximum rotation angle of the device that causes a change in steering */
protected float maxRotate = 45.f;
protected float centerX = TouchScreenOverlay.X_POS_OF_STEERING_CIRCLE + screenWidth / 2;
protected float centerY = -TouchScreenOverlay.Y_POS_OF_STEERING_CIRCLE + screenHeight / 2;
protected float radius = TouchScreenOverlay.RADIUS_OF_STEERING_CIRCLE;
private final int upKey;
private final int leftKey;
private final int downKey;
private final int rightKey;
public FlightController(Player player, PlayerProfile playerProfile) {
this.player = player;
SettingManager settings = playerProfile.getSettingManager();
this.useSensorData = !settings.getBoolean(SettingManager.USE_TOUCH) && Gdx.app.getType() != ApplicationType.Desktop;
this.invertXFactor = settings.getBoolean(SettingManager.INVERT_X) ? -1 : 1;
this.invertYFactor = settings.getBoolean(SettingManager.INVERT_Y) ? -1 : 1;
this.bufferSize = 10;
this.rotationFactor = ((Fly) Gdx.app.getApplicationListener()).orientationSwapped() ? -1 : 1;
this.upKey = settings.getInteger(SettingManager.MOVE_UP);
this.leftKey = settings.getInteger(SettingManager.MOVE_LEFT);
this.downKey = settings.getInteger(SettingManager.MOVE_DOWN);
this.rightKey = settings.getInteger(SettingManager.MOVE_RIGHT);
Gdx.app.log("orientation", "" + rotationFactor);
}
/**
* Resets steering and the buffers.
*/
public void init() {
if (useSensorData) {
resetSteering();
resetBuffers();
}
}
/**
* Getter for the factor by which the player rotate up/down
*
* @return The rollFactor
*/
public float getRollFactor() {
return rollFactor / player.getPlane().getRollingSpeed();
}
/**
* Getter for the factor by which the player should fly curves
*
* @return
*/
public float getAzimuthFactor() {
return azimuthFactor / player.getPlane().getAzimuthSpeed();
}
/**
* Resets the Steering with Sensors to the current Smartphone position
*/
public void resetSteering() {
startRoll = Gdx.input.getRoll();
startRoll *= rotationFactor;
startPitch = Gdx.input.getPitch();
}
/**
* Computes the new position to fly to regarding to the user input
*
* @param delta
* Time since last frame
*/
public void update(float delta) {
// rotating the camera according to UserInput
if (useSensorData)
interpretSensorInput();
player.getPlane().rotate(rollFactor * invertYFactor, azimuthFactor * invertXFactor, 60 * delta);
}
protected void resetBuffers() {
rollInput = new ArrayList<Float>();
pitchInput = new ArrayList<Float>();
}
/**
* Interprets the rotation of the smartphone
*/
protected void interpretSensorInput() {
pitch = Gdx.input.getPitch();
roll = Gdx.input.getRoll();
// interpret if screen on smartphone is rotated or not
roll *= rotationFactor;
pitch *= rotationFactor;
// removing oldest element in buffers
if (rollInput.size() >= bufferSize) {
rollInput.remove(0);
pitchInput.remove(0);
}
// adding newest sensor-data to buffers
rollInput.add(roll);
pitchInput.add(pitch);
roll = average(rollInput);
pitch = average(pitchInput);
float difRoll = roll - startRoll;
if (Math.abs(difRoll) > 180) {
difRoll -= Math.signum(difRoll) * 360;
}
float difPitch = pitch - startPitch;
if (Math.abs(difPitch) > 180) {
difPitch -= Math.signum(difPitch) * 360;
}
// capping the rotation to a maximum
if (Math.abs(difRoll) > maxRotate) {
difRoll = maxRotate * Math.signum(difRoll);
}
if (Math.abs(difPitch) > maxRotate) {
difPitch = maxRotate * Math.signum(difPitch);
}
rollFactor = 0.0f;
azimuthFactor = 0.0f;
// camera rotation according to smartphone rotation
setAzimuthFactor(difPitch / maxRotate);
setRollFactor(difRoll / -maxRotate);
}
/**
* Setter for the roll factor change.
*
* @param rollFactorChange
*/
public void setRollFactorChange(float rollFactorChange) {
this.rollFactorChange = rollFactorChange;
}
/**
* Setter for the azimuth factor change.
*
* @param azimuthFactorChange
*/
public void setAzimuthFactorChange(float azimuthFactorChange) {
this.azimuthFactorChange = azimuthFactorChange;
}
/**
* Resets the roll and azimuth factor changes to 1.0f.
*/
public void resetFactorChange() {
rollFactorChange = 1.0f;
azimuthFactorChange = 1.0f;
}
/**
* Setter for the {@link #azimuthDir}. Values greater than the azimuthSpeed
* of the plane are reduced to the azimuth speed of the plane.
*
* @param azimuthFactor
*/
protected void setAzimuthFactor(float azimuthFactor) {
this.azimuthFactor = azimuthFactorChange * azimuthFactor * player.getPlane().getAzimuthSpeed();
}
/**
* Setter for the {@link #rollDir}. Values greater than the rollingSpeed of
* the plane are reduced to the azimuth speed of the plane.
*
* @param rollFactor
*/
protected void setRollFactor(float rollFactor) {
this.rollFactor = rollFactorChange * rollFactor * player.getPlane().getRollingSpeed();
}
protected float average(List<Float> input) {
float result = 0.0f;
int size = input.size();
for (int i = 0; i < size; i++) {
result += input.get(i);
}
return result / (float) input.size();
}
private void evaluateKeyboardInput() {
setAzimuthFactor(0);
setRollFactor(0);
for ( Integer keycode : pressedKeys )
if ( keycode == leftKey )
setAzimuthFactor(getAzimuthFactor() + 1);
else if ( keycode == rightKey )
setAzimuthFactor(getAzimuthFactor() - 1);
else if ( keycode == upKey )
setRollFactor(getRollFactor() + 1);
else if ( keycode == downKey )
setRollFactor(getRollFactor() - 1);
}
@Override
public boolean keyDown(int keycode) {
Gdx.app.log("FlightController.keyDown", "[keycode=" + keycode + "]");
pressedKeys.add(keycode);
evaluateKeyboardInput();
return false;
}
@Override
public boolean keyUp(int keycode) {
pressedKeys.remove(keycode);
evaluateKeyboardInput();
return false;
}
@Override
public boolean keyTyped(char character) {
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
// using the touchscreen to rotate camera
if (button == Buttons.LEFT) {
float xDif = screenX - centerX;
float yDif = screenY - centerY;
float length = (float) Math.sqrt(xDif * xDif + yDif * yDif);
if (length <= radius) {
setAzimuthFactor(-xDif / radius);
setRollFactor(-yDif / radius);
inTouch = true;
} else {
inTouch = false;
}
currentEvent = pointer;
} else {
setAzimuthFactor(0);
setRollFactor(0);
}
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
// set camera rotation to 0 when finger is lifted from touchscreen
if (button == Buttons.LEFT) {
rollFactor = 0;
azimuthFactor = 0;
}
return false;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
// changing camera rotation when finger is dragged on the touchscreen
if (pointer == currentEvent) {
float xDif = centerX - screenX;
float yDif = centerY - screenY;
float length = (float) Math.sqrt(xDif * xDif + yDif * yDif);
if (length <= radius) {
setAzimuthFactor(xDif / radius);
setRollFactor(yDif / radius);
} else if (inTouch) {
setAzimuthFactor(xDif / length);
setRollFactor(yDif / length);
}
}
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
// nothing should happen here
return false;
}
@Override
public boolean scrolled(int amount) {
// nothing should happen here
return false;
}
}