package TrackRider;
import engine.GameObject;
import engine.Map;
import java.awt.Point;
import java.util.ArrayDeque;
import org.lwjgl.util.vector.Vector2f;
/**
* Baseclass for all gameobjects running on tracks.
*
* @author Jari Saaranen <rasaari@gmail.com>
* @author simokr
*/
public abstract class TrackRider extends GameObject {
private static int MAX_KM_H = 80;
// constants for intersection controlling
public static final int LEFT = -1;
public static final int RIGHT = 1;
private float powerLevel; // From 0.0f to 1.0f, power to push train
private float velocity; // Actual velocity of the train
// object's weight in kilograms
private float weight;
// available directions to continue from current train's position
protected int[] freeDirections;
protected Map map;
// -1 or LEFT indicates to turn left, 1 or RIGHT to right
private int intersectionControl;
private boolean mayTurn;
private float tileProgress, oldAngle, newAngle;
private TurnPoint nextTurnPoint;
private ArrayDeque<TurnPoint> nextTurns;
protected Coach coach;
// amount of money the rider carries
private float cash;
// map methods
public void setMap(Map map) { this.map = map; }
// power methods
public void setPowerLevel(float powerLevel) {
this.powerLevel = (float) Math.max(0.0, Math.min(1.0, powerLevel));
}
public float getPowerLevel() { return this.powerLevel; }
// velocity methods
public void setVelocity(float velocity) { this.velocity = velocity; }
public float getVelocity() { return this.velocity; }
public float getBalancedVelocity() { return getVelocity()*5f; }
// weight methods
public void setWeight(float weight) { this.weight = weight; }
public float getWeight() { return this.weight; }
public float getCash() {
return cash;
}
public float withdrawCash() {
float ret = this.cash;
this.cash = 0;
return ret;
}
public float withdrawCash(float amount) {
if(this.cash >= amount) {
this.cash -= amount;
return amount;
}
return 0.0f;
}
public void addCash(float amount) {
cash += amount;
}
// intersection controlling methods
public void requestTurn(int dir) {
this.intersectionControl = dir;
}
public int getInterSectionDirection() {
return this.intersectionControl;
}
private TurnPoint getNextTurnPoint(){
return this.nextTurnPoint;
}
public TrackRider() {
this.coach = null;
this.freeDirections = new int[8];
this.intersectionControl = 0;
mayTurn = false;
tileProgress = 0.0f;
oldAngle=0f;
newAngle=0f;
nextTurnPoint = null;
cash = 0;
nextTurns = new ArrayDeque<>();
}
protected void updateModel() {
// update model location and rotation
Vector2f newLocation = new Vector2f(this.getLocation());
if(oldAngle == 6 && newAngle == 0)
newAngle = 8;
else if(oldAngle == 0 && newAngle == 6){
oldAngle = 8;
}
float change = (newAngle-oldAngle)*(this.tileProgress);
double rad = (-(oldAngle-2+change))*(2.0*Math.PI/8.0);
float angle = (float) Math.toDegrees(rad);
/*
if(oldAngle < newAngle){
Point loc = this.getGridLocation();
double pointRad = (-(newAngle))*(2.0*Math.PI/8.0);
newLocation.x = loc.x+((0.5f)*(float)Math.cos(pointRad)+(0.5f)*(float)Math.sin(pointRad));
newLocation.y = loc.y+((0.5f)*(float)Math.cos(pointRad)-(0.5f)*(float)Math.sin(pointRad));
double rotateRad = (-(newAngle+change))*(2.0*Math.PI/8.0);
newLocation.x += (float)Math.cos(rotateRad)*-0.5f;
newLocation.y += (float)Math.sin(rotateRad)*0.5f;
}
*/
/*
else if(newAngle < oldAngle){
Point loc = this.getGridLocation2();
double pointRad = (-(oldAngle))*(2.0*Math.PI/8.0);
newLocation.x = loc.x+((-0.5f)*(float)Math.cos(pointRad)+(-0.5f)*(float)Math.sin(pointRad));
newLocation.y = loc.y+((0.5f)*(float)Math.cos(pointRad)-(0.5f)*(float)Math.sin(pointRad));
System.out.println("smooth left");
double rotateRad = (-(oldAngle+change))*(2.0*Math.PI/8.0);
newLocation.x += (float)Math.cos(rotateRad)*0.5f;
newLocation.y += (float)Math.sin(rotateRad)*-0.5f;
}
*/
/*
if(oldAngle == 0 && newAngle == 2){
newLocation.x += Math.sin(this.tileProgress*Math.PI/2.0)*0.5;
newLocation.y -= Math.cos(this.tileProgress*Math.PI/2.0)*0.5;
}
else if(oldAngle == 2 && newAngle == 4){
newLocation.x += Math.cos(this.tileProgress*Math.PI/2.0)*0.5;
newLocation.y += Math.sin(this.tileProgress*Math.PI/2.0)*0.5;
}
else if(oldAngle == 4 && newAngle == 6){
newLocation.x -= Math.sin(this.tileProgress*Math.PI/2.0)*0.5;
newLocation.y += Math.cos(this.tileProgress*Math.PI/2.0)*0.5;
}
else if(oldAngle == 6 && newAngle == 8){
newLocation.x -= Math.cos(this.tileProgress*Math.PI/2.0)*0.5;
newLocation.y -= Math.sin(this.tileProgress*Math.PI/2.0)*0.5;
}
*/
this.model.setRotation(
0,
angle,
0);
this.model.setPosition(
newLocation.x,
0,
newLocation.y);
}
protected void calculateVelocity() {
// TODO: make some serious calculation here
this.velocity = this.powerLevel*0.3f;
}
protected void move(float movement, Point oldTile, TrackRider prev) {
if(map == null){
System.out.println("No map");
return;
}
/* Get how much to move without turning */
double yVel = Math.sin(Math.toRadians(getDirection()*45))*movement;
double xVel = Math.cos(Math.toRadians(getDirection()*45))*movement;
Vector2f newLocation = new Vector2f(this.getLocation());
newLocation.x += xVel;
newLocation.y += yVel;
Point newTile = new Point(Math.round(newLocation.x), Math.round(newLocation.y));
/* Get how much we have moved in this tile (0.0 - 1.0) */
float tileProgress = 0f;
if(getDirection() == 0){
tileProgress = newLocation.x - (float)oldTile.x + 0.5f;
}
else if(getDirection() == 2){
tileProgress = newLocation.y - (float)oldTile.y + 0.5f;
}
else if(getDirection() == 4){
tileProgress = (float)oldTile.x - newLocation.x + 0.5f;
}
else if(getDirection() == 6){
tileProgress = (float)oldTile.y - newLocation.y + 0.5f;
}
this.tileProgress = tileProgress;
if(prev == null){
/* Currently the train is active */
if(!oldTile.equals(newTile)){
/* We have moved in to a new tile, so calculate next turn */
this.mayTurn = true;
int nextDirection = this.getDirection();
boolean freeDirs[] = map.scanRideable(newTile.x, newTile.y);
int right = (getDirection()+2)%8;
int left = (getDirection()-2)%8;
if(left < 0)
left += 8;
if((!freeDirs[getDirection()] && freeDirs[right] && !freeDirs[left])
|| (!freeDirs[getDirection()] && freeDirs[right] && freeDirs[left] && this.getInterSectionDirection() == 0)){
/* Can only turn right */
nextDirection = this.getTurnDirection(2);
System.out.println("Forced right turn");
}
else if(!freeDirs[getDirection()] && !freeDirs[right] && freeDirs[left]){
/* Can only turn left */
nextDirection = this.getTurnDirection(-2);
System.out.println("Forced left turn");
}
else if((!freeDirs[getDirection()] && !freeDirs[right] && !freeDirs[left])){
/* Can only reverse */
nextDirection = this.getTurnDirection(-4);
System.out.println("Reversing");
}
else{
if(this.getInterSectionDirection() == LEFT && freeDirs[left]){
/* Can't go forward but can go right & left, left chosen */
nextDirection = this.getTurnDirection(-2);
this.intersectionControl = 0;
System.out.println("Left turn");
}
else if(this.getInterSectionDirection() == RIGHT && freeDirs[right]){
/* Can't go forward but can go right & left, right chosen */
nextDirection = this.getTurnDirection(2);
this.intersectionControl = 0;
System.out.println("Right turn");
}
}
/* Update model angles to have a smooth turn */
oldAngle = this.getDirection();
newAngle = nextDirection;
/* We are starting a turn, so set lastTurn to this tile */
if(this.getDirection() != nextDirection)
this.nextTurnPoint = new TurnPoint(newTile, nextDirection);
/* Adjust tileProgress, since we moved in to a new tile */
this.tileProgress = tileProgress-1f;
}
}
else{
/* Currently a coach is active */
if(prev.getNextTurnPoint() != null && (this.nextTurns.isEmpty() || !this.nextTurns.peekLast().getTile().equals(prev.getNextTurnPoint().getTile()))){
/* Query previous TrackRider's latest turn and compare it to the last one in this queue.
* Add it to the end of queue, if it isn't already there.
*/
this.nextTurns.add(prev.getNextTurnPoint());
}
if(!oldTile.equals(newTile)){
if(!this.nextTurns.isEmpty()){
/* There are turns queued */
if(newTile.equals(this.nextTurns.peekFirst().getTile())){
/* Current tile matches the tile at the front of turn queue, so execute a turn. */
this.mayTurn = true;
TurnPoint nextTurn = this.nextTurns.pollFirst();
this.nextTurnPoint = new TurnPoint(nextTurn.getTile(), nextTurn.getDirection());
}
}
/* Update model angles to have a smooth turn */
oldAngle = this.getDirection();
newAngle = (this.nextTurnPoint != null)?this.nextTurnPoint.getDirection():this.getDirection();
/* Adjust tileProgress, since we moved in to a new tile */
this.tileProgress = tileProgress-1f;
}
}
if(this.mayTurn && this.tileProgress > 0.5f){
/* Turn flag has been set and the TrackRider is over half way the current tile,
* so try to turn to the next direction.
*/
if(this.nextTurnPoint != null && this.getDirection() != this.nextTurnPoint.getDirection()){
/* Current direction is different from next turn direction, so turn */
this.setDirection(this.nextTurnPoint.getDirection());
/* Set position to the center of current tile and add how much is
* still needed to adjust position with the new direction in mind.
*/
double overhead = tileProgress-0.5f;
//System.out.println("Turn "+((prev==null)?"Train":"Coach")+" "+overhead);
yVel = Math.sin(Math.toRadians(getDirection()*45))*overhead;
xVel = Math.cos(Math.toRadians(getDirection()*45))*overhead;
newLocation.set((float)(newTile.x+xVel), (float)(newTile.y+yVel));
/* TrackRider has turned. Unset the turn point. */
this.nextTurnPoint = null;
}
/* TrackRider can attempt a turn only once per tile */
this.mayTurn = false;
}
/* Set the new position */
this.setLocation(newLocation);
}
abstract public void update();
abstract public void render();
abstract public void free();
/**
* Callback function performed after
* object has turned to left or to right.
*/
abstract protected void onTurn();
public void setDirections(int[] data) {
this.freeDirections = data;
}
private class TurnPoint{
private Point tile;
private int direction;
public TurnPoint(Point tile, int direction){
this.tile = new Point(tile);
this.direction = direction;
}
public void set(Point tile, int direction){
this.tile = new Point(tile);
this.direction = direction;
}
public void set(TurnPoint point){
this.tile = new Point(point.tile);
this.direction = point.direction;
}
public Point getTile(){
return this.tile;
}
public int getDirection(){
return this.direction;
}
}
}