/*
* This file is part of the Illarion project.
*
* Copyright © 2015 - Illarion e.V.
*
* Illarion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Illarion is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package illarion.client.world.movement;
import illarion.client.world.CharMovementMode;
import illarion.client.world.MapDimensions;
import illarion.client.world.World;
import illarion.common.types.Direction;
import illarion.common.types.ServerCoordinate;
import illarion.common.util.FastMath;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.illarion.engine.input.Input;
import org.illarion.engine.input.Key;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* This is the movement handler that takes care for the follow mouse movement system. As long as this is engaged moves
* following the mouse will be plotted and send to the movement handler.
*
* @author Martin Karing <nitram@illarion.org>
*/
class FollowMouseMovementHandler extends AbstractMovementHandler implements MouseMovementHandler {
/**
* This value is the relation of the distance from the character location to the location of the cursor to the
* plain x or y offset. In case the relation is smaller or equal to this the character will move straight
* horizontal or vertical on the screen. Else it will move diagonal.
*/
private static final double MOUSE_ANGLE = StrictMath.cos(Math.PI / Direction.values().length);
@Nonnull
private final Input input;
/**
* The last reported X coordinate of the mouse.
*/
private int lastMouseX;
/**
* The last reported Y coordinate of the mouse.
*/
private int lastMouseY;
private CharMovementMode currentMovementMode;
@Nullable
private Direction walkTowardsDir;
FollowMouseMovementHandler(@Nonnull Movement movement, @Nonnull Input input) {
super(movement);
this.input = input;
lastMouseX = -1;
lastMouseY = -1;
AnnotationProcessor.process(this);
}
@Nullable
@Override
public StepData getNextStep(@Nonnull ServerCoordinate currentLocation) {
calculateMove();
if (walkTowardsDir == null) {
return null;
}
return new DefaultStepData(currentMovementMode, walkTowardsDir);
}
@Override
public void handleMouse(int x, int y) {
lastMouseX = x;
lastMouseY = y;
}
private void calculateMove() {
MapDimensions mapDimensions = MapDimensions.getInstance();
int xOffset = lastMouseX - (mapDimensions.getOnScreenWidth() / 2);
int yOffset = -(lastMouseY - (mapDimensions.getOnScreenHeight() / 2));
int distance = FastMath.sqrt((xOffset * xOffset) + (yOffset * yOffset));
if (distance <= 5) {
walkTowardsDir = null;
return;
}
float relXOffset = (float) xOffset / distance;
float relYOffset = (float) yOffset / distance;
currentMovementMode = getWalkTowardsMode(distance);
//noinspection IfStatementWithTooManyBranches
if (relXOffset > MOUSE_ANGLE) {
walkTowardsDir = Direction.SouthEast;
} else if (relXOffset < -MOUSE_ANGLE) {
walkTowardsDir = Direction.NorthWest;
} else if (relYOffset > MOUSE_ANGLE) {
walkTowardsDir = Direction.NorthEast;
} else if (relYOffset < -MOUSE_ANGLE) {
walkTowardsDir = Direction.SouthWest;
} else if ((xOffset > 0) && (yOffset > 0)) {
walkTowardsDir = Direction.East;
} else if ((xOffset > 0) && (yOffset < 0)) {
walkTowardsDir = Direction.South;
} else if ((xOffset < 0) && (yOffset < 0)) {
walkTowardsDir = Direction.West;
} else if ((xOffset < 0) && (yOffset > 0)) {
walkTowardsDir = Direction.North;
}
}
@Nonnull
private CharMovementMode getWalkTowardsMode(int distance) {
if (input.isAnyKeyDown(Key.LeftShift, Key.RightShift)) {
return CharMovementMode.None;
}
if (!World.getPlayer().getCarryLoad().isRunningPossible()) {
return CharMovementMode.Walk;
}
CharMovementMode mode = CharMovementMode.Walk;
if (distance > 200) {
mode = CharMovementMode.Run;
} else if (distance < 30) {
mode = CharMovementMode.None;
}
if (getMovement().isMovementModePossible(mode)) {
return mode;
}
return CharMovementMode.Walk;
}
@Nonnull
@Override
public String toString() {
return "Follow mouse movement handler";
}
}