JavaRTS/entity/solidObject.java
Pan 132fa5ebc0 Upgrade the screen resolution of considerably, it gives the user wider
view of the back ground. Also the screen resolution and the shadowmap
resolution are no longer hard coded everywhere. They are defined in
mainThread.java.
2021-08-16 09:33:33 +12:00

1655 lines
42 KiB
Java

package entity;
import core.*;
//this is the class for storing geometry information of a 3D model
public abstract class solidObject{
//reference point of the model (in world coordinate)
public vector start;
//the reference axis of the model (in world coordinate)
public vector iDirection, jDirection, kDirection;
//whether this model need to be sent to drawing pipeline
public boolean visible;
//whether this model (only apply to static structures) has been revealed by player
public boolean isRevealed;
//whether this model (only apply to static structures) has been revealed by AI
public boolean isRevealed_AI;
//whether this model is completely bounded by the screen area
public boolean withinViewScreen;
//whether this model is selected;
public boolean isSelected;
//whether this model is selectable;
public boolean isSelectable = true;
//change in center position between current frame and previous frame
public vector movement;
//distance to desination
public float distanceToDesination;
public boolean disableUnitLevelAI;
public float distanceToDesination_PreviousFrame = 9999;
public boolean closeToDestination;
public int hugWallCoolDown;
public int tightSpaceManeuverCountDown;
public int stuckCount, insideDeistinationRadiusCount;
public int bodyAngleDelta, turretAngleDelta;
public float speed;
public boolean visionInsideScreen, visible_lightspace, visible_minimap;;
public int teamNo, type, currentHP;
//this value represents the total amount of the inevitable damage that the unit will suffer, e.g damage from a incoming missile
public int incomingDamage;
public float destinationX, destinationY, secondaryDestinationX, secondaryDestinationY;
public int destinationX_, destinationY_;
public boolean newDestinationisGiven;
public solidObject attacker;
public int myDamage;
public Rect obstacle,tempObstacle;
public Rect unStableObstacle;
//immediate destination Angle
public int immediateDestinationAngle;
public int tempAngle1, tempAngle2, tempAngle3,tempAngle4;
//the index of tiles the harvester has occupied on the grid map
public int currentOccupiedTile, occupiedTile0, occupiedTile1,occupiedTile2,occupiedTile3,
previousOccupiedTile0, previousOccupiedTile1,previousOccupiedTile2,previousOccupiedTile3,
newOccupiedTile0, newOccupiedTile1, newOccupiedTile2,newOccupiedTile3,
tempTile0, tempTile1, tempTile2, tempTile3;
public solidObject[] tile;
public int xPos, yPos, xPos2, yPos2, xPos_old, yPos_old;
public float[] tempFloat;
public int[] tempInt;
public int randomNumber;
public static Rect border = new Rect(0,0,16,16);
public static Rect destinationBlock = new Rect(0,0,0,0);
public static Rect probeBlock = new Rect(0,0,0,0);
public int currentCommand;
public static final int StandBy = 0;
public static final int move = 1;
public static final int attackCautiously = 2;
public static final int attackInNumbers = 3;
public static final int follow = 4;
public static final int attackMove = 5;
public int secondaryCommand;
//movment status
public int currentMovementStatus;
public final static int freeToMove = 0;
public final static int hugLeft = 1;
public final static int hugRight = 2;
//attack status
public int attackStatus;
public final static int noTarget = 0;
public final static int isAttacking = 1;
public final static int notInRange = 2;
public float attackRange, groupAttackRange;
public static int screen_width = mainThread.screen_width;
public static int screen_height = mainThread.screen_height;
public int experience, level;
//wether this object is under attack;
public int underAttackCountDown;
//A cool down which prevents the object to change target too often
//public int changeTargetCountDown;
//a rectangle which represent the object boundary in grid map
public Rect boundary2D;
//vertices
public vector[] v;
//the 3D polygons of the object
public polygon3D[] polygons;
//the centre of the model in world coordinate
public vector centre;
//the centre of the model in camera coordinate
public vector tempCentre = new vector(0,0,0);
public vector tempVector = new vector(0,0,0);
public static int globalUniqID = 0;
public int ID;
public AssetManager theAssetManager;
public float height;
public static Rect fullSizedProbe = new Rect(0,0, 16, 16);
public int progressStatus = -1;
public drone myHealer;
public boolean isCloaked;
public int cloakCooldownCount;
public boolean isRepairing = true;
public int screenX_gui, screenY_gui;
//object that the unit is trying to attack
public solidObject targetObject;
public int groupNo = 255;
public boolean leftFactory;
//get centre of this model in camera coordinate
public vector getCentre(){
return tempCentre;
}
//return centre in world coordinate
public vector getRealCentre(){
return centre;
}
//return visibility
public boolean getVisibility(){
return visible;
}
//create a arbitrary vertex
public vector put(double i, double j, double k){
vector temp = start.myClone();
temp.add(iDirection, (float)i);
temp.add(jDirection, (float)j);
temp.add(kDirection, (float)k);
return temp;
}
//change the 3d geometry of a vertex
public void change(float i, float j, float k, vector v){
v.set(start);
v.add(iDirection, i);
v.add(jDirection, j);
v.add(kDirection, k);
}
//create color in binary format
public int createColor(int r, int g, int b){
return b + (g << 5) + (r << 10);
}
//get ID
public int getID(){
return ID;
}
//generate indexes for surrounding tile
public static int[] generateTileCheckList(float r){
int[] list = new int[(int)((r+1)*(r+1)*Math.PI)];
float[] distance = new float[list.length];
for(int i = 0; i < list.length; i++){
list[i] = Integer.MAX_VALUE;
distance[i] = 10000000;
}
int currentIndex = 0;
int R = (int)r;
if(R < r)
R++;
for(int y = 0; y < R * 2 + 1; y++){
for(int x = 0; x < R * 2 + 1; x++){
int a = Math.abs(x-R);
int b = Math.abs(y-R);
if(r >= Math.sqrt(a*a + b*b)){
list[currentIndex] = x - R + (y - R)*(32*4);
distance[currentIndex] = (float)Math.sqrt(a*a + b*b);
currentIndex++;
}
}
}
for(int i = 1; i < list.length; i++){
for(int j = 0; j <list.length - i; j++){
if(distance[j] > distance[j+1]){
float tempFloat = distance[j+1];
distance[j+1] = distance[j];
distance[j] = tempFloat;
int tempInt = list[j+1];
list[j+1] = list[j];
list[j] = tempInt;
}
}
}
return list;
}
public boolean[] createBitmapVision(int radius){
int l = radius*2+1;
boolean[] vision = new boolean[l*l];
for(int y = 0; y < l; y++){
for(int x = 0; x < l; x++){
if( (x - radius)*(x - radius) + (y - radius)*(y - radius) < ((float)radius+0.5f)*((float)radius+0.5f)){
vision[x + y*l] = true;
}
}
}
return vision;
}
//clone a group of polygons (doesn't work on smooth shaded polygons)
public polygon3D[] clonePolygons(polygon3D[] polys, boolean createNewOUV){
int l = polys.length;
polygon3D[] clone = new polygon3D[l];
for(int i = 0; i < l; i++){
if(polys[i] == null)
continue;
int length = polys[i].vertex3D.length;
v = new vector[length];
for(int j = 0; j < length; j++){
v[j] = polys[i].vertex3D[j].myClone();
}
int myType = polys[i].type;
float scaleX = polys[i].scaleX;
float scaleY = polys[i].scaleY;
texture myTexture = polys[i].myTexture;
if(createNewOUV)
clone[i] = new polygon3D(v, polys[i].origin.myClone(), polys[i].rightEnd.myClone(), polys[i].bottomEnd.myClone(), myTexture, scaleX, scaleY, myType);
else
clone[i] = new polygon3D(v, v[0], v[1], v[3], myTexture, scaleX, scaleY, myType);
clone[i].shadowBias = polys[i].shadowBias;
clone[i].diffuse_I = polys[i].diffuse_I;
clone[i].Ambient_I = polys[i].Ambient_I;
}
return clone;
}
public boolean isStable(solidObject o){
if(o != null){
if(o.currentCommand == StandBy || (o.attackStatus == isAttacking && o.getMovement().x ==0 && o.getMovement().z ==0) || o.type > 100){
return true;
}
}
return false;
}
public void removeFromGridMap(){
boundary2D.setOrigin(100000, 100000);
if(occupiedTile0 != -1){
tile = mainThread.gridMap.tiles[occupiedTile0];
for(int i = 0; i < 5; i++){
if(tile[i] == this){
tile[i] = null;
}
}
}
if(occupiedTile1 != -1){
tile = mainThread.gridMap.tiles[occupiedTile1];
for(int i = 0; i < 5; i++){
if(tile[i] == this){
tile[i] = null;
}
}
}
if(occupiedTile2 != -1){
tile = mainThread.gridMap.tiles[occupiedTile2];
for(int i = 0; i < 5; i++){
if(tile[i] == this){
tile[i] = null;
}
}
}
if(occupiedTile3 != -1){
tile = mainThread.gridMap.tiles[occupiedTile3];
for(int i = 0; i < 5; i++){
if(tile[i] == this){
tile[i] = null;
}
}
}
}
public void updateOccupiedTiles(int x, int y){
previousOccupiedTile0 = occupiedTile0;
previousOccupiedTile1 = occupiedTile1;
previousOccupiedTile2 = occupiedTile2;
previousOccupiedTile3 = occupiedTile3;
occupiedTile0= x/16 + (127 - (y-1)/16)*128;
occupiedTile1= -1;
occupiedTile2= -1;
occupiedTile3= -1;
if(movement.x == 0 && movement.z == 0){
mainThread.gridMap.currentObstacleMap[occupiedTile0] = false;
}
if(x%16 >0 && y%16 > 0){
occupiedTile1= occupiedTile0 + 1;
occupiedTile2= occupiedTile0 + 128;
occupiedTile3= occupiedTile2 + 1;
if(movement.x == 0 && movement.z == 0){
mainThread.gridMap.currentObstacleMap[occupiedTile1] = false;
mainThread.gridMap.currentObstacleMap[occupiedTile2] = false;
mainThread.gridMap.currentObstacleMap[occupiedTile3] = false;
}
}else if(x%16 > 0){
occupiedTile1= occupiedTile0 + 1;
if(movement.x == 0 && movement.z == 0){
mainThread.gridMap.currentObstacleMap[occupiedTile1] = false;
}
}else if(y%16 > 0){
occupiedTile2= occupiedTile0 + 128;
if(movement.x == 0 && movement.z == 0){
mainThread.gridMap.currentObstacleMap[occupiedTile2] = false;
}
}
//object occupies exactly the same tiles as before, then do nothing
if(previousOccupiedTile0 == occupiedTile0 && previousOccupiedTile1 == occupiedTile1 && previousOccupiedTile2 == occupiedTile2 && previousOccupiedTile3 == occupiedTile3){
return;
}
//remove the object from the old tiles
if(previousOccupiedTile0 != -1){
tile = mainThread.gridMap.tiles[previousOccupiedTile0];
for(int i = 0; i < 4; i++){
if(tile[i] == this){
tile[i] = null;
break;
}
}
}
if(previousOccupiedTile1 != -1){
tile = mainThread.gridMap.tiles[previousOccupiedTile1];
for(int i = 0; i < 4; i++){
if(tile[i] == this){
tile[i] = null;
break;
}
}
}
if(previousOccupiedTile2 != -1){
tile = mainThread.gridMap.tiles[previousOccupiedTile2];
for(int i = 0; i < 4; i++){
if(tile[i] == this){
tile[i] = null;
break;
}
}
}
if(previousOccupiedTile3 != -1){
tile = mainThread.gridMap.tiles[previousOccupiedTile3];
for(int i = 0; i < 4; i++){
if(tile[i] == this){
tile[i] = null;
break;
}
}
}
//add the object to the new tiles
if(occupiedTile0 != -1){
tile = mainThread.gridMap.tiles[occupiedTile0];
for(int i = 0; i < 4; i++){
if(tile[i] == null){
tile[i] = this;
break;
}
}
}
if(occupiedTile1 != -1){
tile = mainThread.gridMap.tiles[occupiedTile1];
for(int i = 0; i < 4; i++){
if(tile[i] == null){
tile[i] = this;
break;
}
}
}
if(occupiedTile2 != -1){
tile = mainThread.gridMap.tiles[occupiedTile2];
for(int i = 0; i < 4; i++){
if(tile[i] == null){
tile[i] = this;
break;
}
}
}
if(occupiedTile3 != -1){
tile = mainThread.gridMap.tiles[occupiedTile3];
for(int i = 0; i < 4; i++){
if(tile[i] == null){
tile[i] = this;
break;
}
}
}
}
public Rect retriveSurroundingObject(int xPos, int yPos){
tempTile0 = xPos/16 + (127 - yPos/16)*128;
tempTile1 = tempTile0 + 1;
tempTile2 = tempTile0 + 128;
tempTile3 = tempTile0 + 129;
Rect r = null;
if(tempTile0 >=0 && tempTile0 < 16384){
tile = mainThread.gridMap.tiles[tempTile0];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].ID != ID){
if(isStable(tile[i]))
return tile[i].boundary2D;
else
r = tile[i].boundary2D;
}
}
}
}
if(tempTile1 >=0 && tempTile1 < 16384){
tile = mainThread.gridMap.tiles[tempTile1];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if( tile[i].ID != ID){
if(isStable(tile[i]))
return tile[i].boundary2D;
else
r = tile[i].boundary2D;
}
}
}
}
if(tempTile2 >=0 && tempTile2 < 16384){
tile = mainThread.gridMap.tiles[tempTile2];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].ID != ID){
if(isStable(tile[i]))
return tile[i].boundary2D;
else
r = tile[i].boundary2D;
}
}
}
}
if(tempTile3 >=0 && tempTile3 < 16384){
tile = mainThread.gridMap.tiles[tempTile3];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].ID != ID){
if(isStable(tile[i]))
return tile[i].boundary2D;
else
r = tile[i].boundary2D;
}
}
}
}
return r;
}
public int validateMovement(){
xPos_old = boundary2D.x1;
yPos_old = boundary2D.y1;
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
obstacle = checkForCollision(boundary2D);
boundary2D.setOrigin(xPos_old, yPos_old);
if(obstacle == null){
return freeToMove;
}else{
destinationX_ = (int)(destinationX*64);
destinationY_ = (int)(destinationY*64);
int x = 0;
int y = 0;
if(obstacle.owner!= null){
x = obstacle.owner.boundary2D.x1;
y = obstacle.owner.boundary2D.y1;
if(x - xPos_old < -16)
x+=16;
if(y - yPos_old > 16)
y-=16;
if(x - xPos_old > 16)
x-=16;
if(y - yPos_old < -16)
y+=16;
if(obstacle.owner.teamNo != teamNo)
obstacle.owner.cloakCooldownCount = 60;
}else{
x = obstacle.x1;
y = obstacle.y1;
}
float dx = Math.abs(centre.x - destinationX);
float dy = Math.abs(centre.z - destinationY);
float dx_ = 0;
float dy_ = 0;
if(obstacle.owner != null){
dx_ = centre.x - obstacle.owner.centre.x;
dy_ = centre.z - obstacle.owner.centre.z;
}
//figure out which action should be taken next
float upDistance = 0;
float downDistance =0;
float leftDistance = 0;
float rightDistance = 0;
if(obstacle.x1 > boundary2D.x2){
if((currentCommand == attackCautiously || currentCommand == attackInNumbers) && distanceToDesination <= 1.5f){
upDistance = countOccupiedBlocksDuringAttack(x, y, 0, 16, 16, 0) * 0.25f;
downDistance = countOccupiedBlocksDuringAttack(x, y, 0, -16, 16, 0) * 0.25f;
}else{
upDistance = countOccupiedBlocks(x, y, 0, 16, 16, 0) * 0.25f;
downDistance = countOccupiedBlocks(x, y, 0, -16, 16, 0) * 0.25f;
}
if(destinationY < centre.z){
if(downDistance <= dy || dx < 0.125f){
immediateDestinationAngle = 180;
return hugLeft;
}
if(downDistance < upDistance){
immediateDestinationAngle = 180;
return hugLeft;
}else{
if(upDistance == downDistance){
if(dy_ < 0){
immediateDestinationAngle = 180;
return hugLeft;
}
}
immediateDestinationAngle = 0;
return hugRight;
}
}else{
if(upDistance <= dy || dx < 0.125f){
immediateDestinationAngle = 0;
return hugRight;
}
if(upDistance < downDistance){
immediateDestinationAngle = 0;
return hugRight;
}else{
immediateDestinationAngle = 180;
return hugLeft;
}
}
}
if(obstacle.x2 < boundary2D.x1){
if((currentCommand == attackCautiously || currentCommand == attackInNumbers) && distanceToDesination <= 1.5f){
upDistance = countOccupiedBlocksDuringAttack(x, y, 0, 16, -16, 0) * 0.25f;
downDistance = countOccupiedBlocksDuringAttack(x, y, 0, -16, -16, 0) * 0.25f;
}else{
upDistance = countOccupiedBlocks(x, y, 0, 16, -16, 0) * 0.25f;
downDistance = countOccupiedBlocks(x, y, 0, -16, -16, 0) * 0.25f;
}
if(destinationY < centre.z){
if(downDistance <= dy || dx < 0.125f){
immediateDestinationAngle = 180;
return hugRight;
}
if(downDistance < upDistance){
immediateDestinationAngle = 180;
return hugRight;
}else{
if(upDistance == downDistance){
if(dy_ < 0){
immediateDestinationAngle = 180;
return hugRight;
}
}
immediateDestinationAngle = 0;
return hugLeft;
}
}else{
if(upDistance <= dy || dx < 0.125f){
immediateDestinationAngle = 0;
return hugLeft;
}
if(upDistance < downDistance){
immediateDestinationAngle = 0;
return hugLeft;
}else{
immediateDestinationAngle = 180;
return hugRight;
}
}
}
if(obstacle.y2 > boundary2D.y1){
if((currentCommand == attackCautiously || currentCommand == attackInNumbers) && distanceToDesination <= 1.5f){
leftDistance = countOccupiedBlocksDuringAttack(x, y, -16, 0, 0, 16) * 0.25f;
rightDistance = countOccupiedBlocksDuringAttack(x, y, 16, 0, 0, 16) * 0.25f;
}else{
leftDistance = countOccupiedBlocks(x, y, -16, 0, 0, 16) * 0.25f;
rightDistance = countOccupiedBlocks(x, y, 16, 0, 0, 16) * 0.25f;
}
if(destinationX < centre.x){
if(leftDistance <= dx || dy < 0.125f){
immediateDestinationAngle = 270;
return hugRight;
}
if(leftDistance < rightDistance){
immediateDestinationAngle = 270;
return hugRight;
}else{
immediateDestinationAngle = 90;
return hugLeft;
}
}else{
if(rightDistance <= dx || dy < 0.125f){
immediateDestinationAngle = 90;
return hugLeft;
}
if(rightDistance < leftDistance){
immediateDestinationAngle = 90;
return hugLeft;
}else{
if(rightDistance == leftDistance){
if(dx_ > 0){
immediateDestinationAngle = 90;
return hugLeft;
}
}
immediateDestinationAngle = 270;
return hugRight;
}
}
}
if(obstacle.y1 < boundary2D.y2){
if((currentCommand == attackCautiously || currentCommand == attackInNumbers) && distanceToDesination <= 1.5f){
leftDistance = countOccupiedBlocksDuringAttack(x, y, -16, 0, 0, -16) * 0.25f;
rightDistance = countOccupiedBlocksDuringAttack(x, y, 16, 0, 0, -16) * 0.25f;
}else{
leftDistance = countOccupiedBlocks(x, y, -16, 0, 0, -16) * 0.25f;
rightDistance = countOccupiedBlocks(x, y, 16, 0, 0, -16) * 0.25f;
}
if(destinationX < centre.x){
if(leftDistance <= dx || dy < 0.125f){
immediateDestinationAngle = 270;
return hugLeft;
}
if(leftDistance < rightDistance){
immediateDestinationAngle = 270;
return hugLeft;
}else{
immediateDestinationAngle = 90;
return hugRight;
}
}else{
if(rightDistance <= dx || dy < 0.125f){
immediateDestinationAngle = 90;
return hugRight;
}
if(rightDistance < leftDistance){
immediateDestinationAngle = 90;
return hugRight;
}else{
if(rightDistance == leftDistance){
if(dx_ > 0){
immediateDestinationAngle = 90;
return hugRight;
}
}
immediateDestinationAngle = 270;
return hugLeft;
}
}
}
return -1;
}
}
//count the number of occupied block (up to 10 blocks) in a given direction
public int countOccupiedBlocks(int x, int y, int dx, int dy, int dx_, int dy_){
int count = 0;
for(int i = 0; i < 10; i++, x+=dx, y +=dy){
if(x > 127*16 || x < 0 || y > 127*16 || y < 0)
continue;
fullSizedProbe.setOrigin(x, y);
if(checkForCollision(fullSizedProbe) != null){
count++;
}else{
if(fullSizedProbe.contains(destinationX_, destinationY_)){
break;
}
fullSizedProbe.setOrigin(x+dx_, y+dy_);
if(checkForCollision(fullSizedProbe) != null){
fullSizedProbe.setOrigin(x+dx, y+dy);
if(checkForCollision(fullSizedProbe) != null){
count++;
}else{
break;
}
}else
break;
}
}
count-=1;
if(count < 0)
count = 0;
return count;
}
//count the number of occupied block (up to 10 blocks) in a given direction
public int countOccupiedBlocksDuringAttack(int x, int y, int dx, int dy, int dx_, int dy_){
int count = 0;
Rect tempRect;
for(int i = 0; i < 10; i++, x+=dx, y +=dy){
if(x > 127*16 || x < 0 || y > 127*16 || y < 0)
continue;
fullSizedProbe.setOrigin(x, y);
tempRect = checkForCollision(fullSizedProbe);
if(tempRect != null){
if(tempRect.owner != null && tempRect.owner == targetObject)
return 0;
count++;
}else{
if(fullSizedProbe.contains(destinationX_, destinationY_)){
break;
}
fullSizedProbe.setOrigin(x+dx_, y+dy_);
if(checkForCollision(fullSizedProbe) != null){
fullSizedProbe.setOrigin(x+dx, y+dy);
if(checkForCollision(fullSizedProbe) != null){
count++;
}else{
break;
}
}else
break;
}
}
count-=1;
if(count < 0)
count = 0;
return count;
}
public Rect checkForCollision(Rect myRect){
if(obstacle != null){
if(myRect.intersect(obstacle) && (isStable(obstacle.owner))){
return obstacle;
}
}
//check if the tank collide with the border
if(myRect.x1 < 0){
border.setOrigin(-16, myRect.y1);
return border;
}
if(myRect.x2 > 2047){
border.setOrigin(2048, myRect.y1);
return border;
}
if(myRect.y2 < 1){
border.setOrigin(myRect.x1, 0);
return border;
}
if(myRect.y1 > 2048){
border.setOrigin(myRect.x1, 2064);
return border;
}
newOccupiedTile0= myRect.x1/16 + (127 - myRect.y1/16)*128;
if(myRect.y1 == 2048)
newOccupiedTile0= myRect.x1/16 + (127 - (myRect.y1-1)/16)*128;
newOccupiedTile1 = newOccupiedTile0 + 1;
newOccupiedTile2 = newOccupiedTile0 + 128;
newOccupiedTile3 = newOccupiedTile0 + 129;
tempObstacle = null;
if(newOccupiedTile0 >= 0 && newOccupiedTile0 < 16384){
tile = mainThread.gridMap.tiles[newOccupiedTile0];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].boundary2D.intersect(myRect) && tile[i].ID != ID)
if(isStable(tile[i]) ){
return tile[i].boundary2D;
}else{
if(tempObstacle == null)
tempObstacle = tile[i].boundary2D;
}
}
}
}
if(newOccupiedTile1 >= 0 && newOccupiedTile1 < 16384){
tile = mainThread.gridMap.tiles[newOccupiedTile1];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].boundary2D.intersect(myRect) && tile[i].ID != ID)
if(isStable(tile[i]) ){
return tile[i].boundary2D;
}else{
if(tempObstacle == null)
tempObstacle = tile[i].boundary2D;
}
}
}
}
if(newOccupiedTile2 >= 0 && newOccupiedTile2 < 16384){
tile = mainThread.gridMap.tiles[newOccupiedTile2];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].boundary2D.intersect(myRect) && tile[i].ID != ID)
if(isStable(tile[i]) ){
return tile[i].boundary2D;
}else{
if(tempObstacle == null)
tempObstacle = tile[i].boundary2D;
}
}
}
}
if(newOccupiedTile3 >= 0 && newOccupiedTile3 < 16384){
tile = mainThread.gridMap.tiles[newOccupiedTile3];
for(int i = 0; i < 5; i++){
if(tile[i] != null){
if(tile[i].boundary2D.intersect(myRect) && tile[i].ID != ID)
if(isStable(tile[i]) ){
return tile[i].boundary2D;
}else{
if(tempObstacle == null)
tempObstacle = tile[i].boundary2D;
}
}
}
}
if(tempObstacle != null)
unStableObstacle = tempObstacle;
return tempObstacle;
}
public void calculateMovement(){
movement.set(destinationX - centre.x, 0, destinationY - centre.z);
movement.unit();
movement.scale(speed);
}
public void changeMovement(int angle){
if(angle == 0)
movement.set(0,0,speed);
if(angle == 90)
movement.set(speed,0,0);
if(angle == 180)
movement.set(0,0,-speed);
if(angle == 270)
movement.set(-speed,0,0);
}
public boolean checkIfTileIsOccupiedByStaticUnitProbe(float x, float y){
xPos = (int)(x*64);
yPos = (int)(y*64);
if(xPos <= 0 || yPos <= 0 || xPos >= 2048 || yPos >=2048)
return true;
probeBlock.setOrigin(xPos-6, yPos+6);
tile = mainThread.gridMap.tiles[xPos/16 + (127 - yPos/16)*128];
for(int i = 0; i < 4; i++){
if(tile[i] != null){
if(tile[i].boundary2D.intersect(probeBlock) && isStable(tile[i]) )
return true;
}
}
return false;
}
public boolean checkIfTileIsOccupiedByStaticUnitPoint(float x, float y){
xPos = (int)(x*64);
yPos = (int)(y*64);
if(xPos <= 0 || yPos <= 0 || xPos >= 2048 || yPos >=2048)
return true;
tile = mainThread.gridMap.tiles[xPos/16 + (127 - yPos/16)*128];
for(int i = 0; i < 4; i++){
if(tile[i] != null){
if(tile[i].boundary2D.contains(xPos, yPos) && isStable(tile[i]) )
return true;
}
}
return false;
}
//a maneuver that follow the edge of the obstacles until the path to its destination is clear
public void hugWalls(){
xPos_old = boundary2D.x1;
yPos_old = boundary2D.y1;
tempVector.set(movement);
calculateMovement();
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
//if there is nothing nearby then re-issue the command to move towards destination
boundary2D.setOrigin(xPos, yPos);
boolean destinationImmediatelyReachable = true;
//check if the next move towards the destination will result in collision
if(checkForCollision(boundary2D) == null){
movement.scale(4);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
//if there is no collision then check if the obstacle found in previous move is static or not
if(checkForCollision(boundary2D) == null){
if(obstacle.owner != null){
//if the obstacle is not static (e.g another moving tank) then re-issue the command to move towards destination
if(obstacle.owner.getMovement().x != 0 || obstacle.owner.getMovement().z !=0 || (Math.abs(obstacle.owner.immediateDestinationAngle - immediateDestinationAngle) < 10 && !isStable(obstacle.owner))){
newDestinationisGiven = true;
movement.reset();
currentMovementStatus = freeToMove;
hugWallCoolDown = 0;
obstacle = null;
boundary2D.setOrigin(xPos_old, yPos_old);
return;
}
}
//check more moves towards destination for potential collisions to ensure the path is really clear
movement.scale(4);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
movement.scale(2);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
//finally if no collision is found, and the destination direction is not opposite to the tank's current direction,
//then re-issue the command to move towards destination
Rect surroundingObject = retriveSurroundingObject(xPos, yPos);
boolean shouldMoveTowardsDestination = false;
if(surroundingObject != null){
if(surroundingObject.owner != null){
if(!isStable(surroundingObject.owner)){
shouldMoveTowardsDestination = true;
}
}
}
if(tempVector.dot(movement) > 0 || shouldMoveTowardsDestination){
newDestinationisGiven = true;
calculateMovement();
currentMovementStatus = freeToMove;
boundary2D.setOrigin(xPos_old, yPos_old);
hugWallCoolDown = 0;
obstacle = null;
return;
}
movement.reset();
boundary2D.setOrigin(xPos_old, yPos_old);
}
}
}
}else{
obstacle = checkForCollision(boundary2D);
if(isStable(obstacle.owner))
destinationImmediatelyReachable = false;
}
if(currentMovementStatus == hugRight){
tempAngle1 = (immediateDestinationAngle + 90)%360;
tempAngle2 = immediateDestinationAngle;
tempAngle3 = (immediateDestinationAngle - 90 + 360)%360;
tempAngle4 = (immediateDestinationAngle - 180 + 360)%360;
}else{
tempAngle1 = (immediateDestinationAngle - 90 + 360)%360;
tempAngle2 = immediateDestinationAngle;
tempAngle3 = (immediateDestinationAngle + 90)%360;
tempAngle4 = (immediateDestinationAngle + 180)%360;
}
if(hugWallCoolDown >0){
hugWallCoolDown--;
}else{
if(obstacle.owner != null){
if(obstacle.owner.type >= 100){
changeMovement(tempAngle2);
movement.scale(16);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
float x = movement.x;
float z = movement.z;
changeMovement(tempAngle1);
movement.scale(16);
if(tightSpaceManeuverCountDown ==0){
if(x * (destinationX - centre.x) + z * (destinationY - centre.z) > movement.x * (destinationX - centre.x) + movement.z * (destinationY - centre.z)){
boundary2D.setOrigin(xPos_old, yPos_old);
changeMovement(tempAngle2);
tightSpaceManeuverCountDown = 64;
return;
}
}else{
if((movement.x * (destinationX - centre.x) + movement.z * (destinationY - centre.z)) > 0){
boundary2D.setOrigin(xPos_old, yPos_old);
changeMovement(tempAngle2);
return;
}
}
}
}
}
//Check if turning is possible
if(obstacle.owner != null){
if(obstacle.owner.getMovement().x == 0 && obstacle.owner.getMovement().z ==0){
changeMovement(tempAngle1);
movement.scale(16);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
//if turning is possible but it drives the tank further away from the destination then change
if(checkForCollision(boundary2D) == null){
boolean shouldTurn = true;
movement.scale(3.3f);
float l = movement.getLength();
if(distanceToDesination >= l){
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) != null && (!checkIfTileIsOccupiedByStaticUnitPoint(destinationX, destinationY) || distanceToDesination > 1.6)){
shouldTurn = false;
}
}else{
movement.scale(0.9f);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) != null && !checkIfTileIsOccupiedByStaticUnitPoint(destinationX, destinationY) && !(distanceToDesination < 0.4f)){
shouldTurn = false;
}
}
if(shouldTurn){
if((movement.x * (destinationX - centre.x) + movement.z * (destinationY - centre.z)) < 0){
changeMovement(tempAngle3);
xPos = (int)((centre.x + movement.x*8)*64) - 8;
yPos = (int)((centre.z + movement.z*8)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
changeMovement(tempAngle4);
xPos2 = (int)((centre.x + movement.x*8)*64) - 8;
yPos2 = (int)((centre.z + movement.z*8)*64) + 8;
boundary2D.setOrigin((xPos+xPos2)/2, (yPos+yPos2)/2);
if(checkForCollision(boundary2D) != null && isStable(obstacle.owner)){
obstacle = checkForCollision(boundary2D);
boundary2D.setOrigin(xPos_old, yPos_old);
movement.reset();
immediateDestinationAngle = tempAngle3;
hugWallCoolDown = 15;
if(currentMovementStatus == hugRight)
currentMovementStatus = hugLeft;
else
currentMovementStatus = hugRight;
return;
}
}
}
boundary2D.setOrigin(xPos_old, yPos_old);
movement.reset();
immediateDestinationAngle = tempAngle1;
if(destinationImmediatelyReachable)
hugWallCoolDown = 60;
else
hugWallCoolDown = 15;
return;
}
}
}
}
}
changeMovement(tempAngle2);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
tempObstacle = checkForCollision(boundary2D);
if(tempObstacle == null){
changeMovement(tempAngle3);
movement.scale(16);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
tempObstacle = checkForCollision(boundary2D);
if(tempObstacle != null){
if(tempObstacle.owner != null)
if(isStable(tempObstacle.owner)){
if((movement.x * (destinationX - centre.x) + movement.z * (destinationY - centre.z)) > 0){
if(currentMovementStatus == hugRight)
currentMovementStatus = hugLeft;
else
currentMovementStatus = hugRight;
}
}
}
changeMovement(tempAngle2);
boundary2D.setOrigin(xPos_old, yPos_old);
return;
} else if(tempObstacle.owner != null){
if((tempObstacle.owner.getMovement().x != 0 || tempObstacle.owner.getMovement().z != 0) && !destinationImmediatelyReachable){
movement.reset();
boundary2D.setOrigin(xPos_old, yPos_old);
return;
}
}
if(obstacle.owner != null){
if(obstacle.owner.getMovement().x == 0 && obstacle.owner.getMovement().z ==0){
changeMovement(tempAngle3);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
boundary2D.setOrigin(xPos_old, yPos_old);
movement.reset();
immediateDestinationAngle = tempAngle3;
return;
}
}
if(obstacle.owner.getMovement().x == 0 && obstacle.owner.getMovement().z ==0){
changeMovement(tempAngle4);
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
boundary2D.setOrigin(xPos_old, yPos_old);
movement.reset();
immediateDestinationAngle = tempAngle4;
return;
}
}
}
boundary2D.setOrigin(xPos_old, yPos_old);
movement.reset();
}
public boolean checkIfDestinationReached(){
distanceToDesination = (float)Math.sqrt((destinationX - centre.x) * (destinationX - centre.x) + (destinationY - centre.z) * (destinationY - centre.z));
if(distanceToDesination < 1.5 && checkIfTileIsOccupiedByStaticUnitPoint(destinationX, destinationY)){
closeToDestination = true;
if(distanceToDesination > distanceToDesination_PreviousFrame){
distanceToDesination_PreviousFrame = 9999;
return true;
}
distanceToDesination_PreviousFrame = distanceToDesination;
}else{
if(closeToDestination){
closeToDestination = false;
distanceToDesination_PreviousFrame = 9999;
return true;
}
}
if(distanceToDesination > 1.5)
return false;
if(distanceToDesination < 0.1){
insideDeistinationRadiusCount = 0;
distanceToDesination_PreviousFrame = 9999;
return true;
}
if(distanceToDesination < 0.5){
insideDeistinationRadiusCount++;
}else{
insideDeistinationRadiusCount = 0;
}
if(insideDeistinationRadiusCount >= 64){
insideDeistinationRadiusCount = 0;
distanceToDesination_PreviousFrame = 9999;
return true;
}
//if the desitnation is already occupied, test if the tank is adjacent to destination tile
xPos_old = boundary2D.x1;
yPos_old = boundary2D.y1;
xPos = (int)((centre.x + movement.x)*64) - 8;
yPos = (int)((centre.z + movement.z)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(checkForCollision(boundary2D) == null){
if(distanceToDesination < 0.5 && checkIfTileIsOccupiedByStaticUnitPoint(destinationX, destinationY)){
destinationBlock.setOrigin((int)(destinationX*64)-8, (int)(destinationY*64)+8);
xPos = (int)((centre.x + (destinationX - centre.x)/32)*64) - 8;
yPos = (int)((centre.z + (destinationY - centre.z)/32)*64) + 8;
boundary2D.setOrigin(xPos, yPos);
if(boundary2D.intersect(destinationBlock)){
boundary2D.setOrigin(xPos_old, yPos_old);
distanceToDesination_PreviousFrame = 9999;
return true;
}
}
boundary2D.setOrigin(xPos_old, yPos_old);
return false;
}
if(distanceToDesination < 0.5){
if(checkIfTileIsOccupiedByStaticUnitPoint(destinationX, destinationY)){
boundary2D.setOrigin(xPos_old, yPos_old);
distanceToDesination_PreviousFrame = 9999;
return true;
}
}
if(distanceToDesination < 1.2){
if(checkIfTileIsOccupiedByStaticUnitProbe(destinationX + 0.25f, destinationY + 0.25f) &&
checkIfTileIsOccupiedByStaticUnitProbe(destinationX + 0.25f, destinationY - 0.25f) &&
checkIfTileIsOccupiedByStaticUnitProbe(destinationX - 0.25f, destinationY + 0.25f) &&
checkIfTileIsOccupiedByStaticUnitProbe(destinationX - 0.25f, destinationY - 0.25f)){
boundary2D.setOrigin(xPos_old, yPos_old);
distanceToDesination_PreviousFrame = 9999;
return true;
}
}
boundary2D.setOrigin(xPos_old, yPos_old);
return false;
}
public void avoidGettingStucked(){
//if the object can't move for some period then recalculate the path
if(movement.x == 0 && movement.z == 0 && bodyAngleDelta == 0 && attackStatus != isAttacking){
stuckCount++;
}
if(obstacle != null && attackStatus != isAttacking){
if((unStableObstacle != null || !isStable(obstacle.owner)) && (ID + randomNumber + mainThread.gameFrame)%128 ==0){
newDestinationisGiven = true;
currentMovementStatus = freeToMove;
hugWallCoolDown = 0;
stuckCount = 0;
randomNumber = gameData.getRandom();
}
}
if(stuckCount > 128){
newDestinationisGiven = true;
stuckCount = 0;
currentMovementStatus = freeToMove;
hugWallCoolDown = 0;
}
}
public String toString(){
String label = "";
if(type == 0)
label+="lightTank";
if(type == 1)
label+="rocketTank";
if(type == 101)
label+="powerPlant";
if(type == 2)
label+="harvester";
if(type==102)
label+="refinery";
if(type==3)
label+="constructionVehichle";
if(type ==4)
label+="tokenObject";
if(type== 6)
label+="stealthTank";
if(type == 7)
label+="heavyTank";
if(type==103)
label+="goldMine";
if(type==104)
label+="constructionYard";
if(type==105)
label+="factory";
if(type==106)
label+="communicationCenter";
if(type==107)
label+="techCenter";
if(type == 200)
label+="gunTurret";
if(type == 199)
label+="missileTurret";
return label + " " + centre.x + " " + centre.z;
}
public void printCurrentCommand(){
if(currentCommand == StandBy)
System.out.println("currentCommand = Standby");
if(currentCommand == move)
System.out.println("currentCommand = move");
if(currentCommand == attackCautiously)
System.out.println("currentCommand = attackCautiously");
if(currentCommand == attackInNumbers)
System.out.println("currentCommand = attackInNumbers");
if(currentCommand == attackMove)
System.out.println("currentCommand = attackMove");
}
public void printMovementStatus(){
if(currentMovementStatus == freeToMove)
System.out.println("currentMovementStatus = freeToMove");
if(currentMovementStatus == hugLeft)
System.out.println("currentMovementStatus = hugLeft");
if(currentMovementStatus == hugRight)
System.out.println("currentMovementStatus = hugRight");
}
public void printAttackStatus(){
if(attackStatus == noTarget)
System.out.println("attackStatus = noTarget");
if(attackStatus == isAttacking)
System.out.println("attackStatus = isAttacking");
if(attackStatus == notInRange)
System.out.println("attackStatus = notInRange");
}
public double getDistance(solidObject o){
return Math.sqrt((centre.x - o.centre.x)*(centre.x - o.centre.x) + (centre.z - o.centre.z)*(centre.z - o.centre.z));
}
public void attackMoveTo(float destinationX, float destinationY){
movement.reset();
turretAngleDelta = 0;
bodyAngleDelta = 0;
currentMovementStatus = freeToMove;
attackStatus = noTarget;
stuckCount = 0;
//destinationX = centre.x;
//destinationY = centre.z;
insideDeistinationRadiusCount = 0;
obstacle = null;
closeToDestination = false;
this.destinationX = destinationX;
this.destinationY = destinationY;
newDestinationisGiven = true;
secondaryDestinationX = destinationX;
secondaryDestinationY = destinationY;
}
public vector getMovement(){
return movement;
}
public void moveTo(float destinationX, float destinationY){
float distanceToDestination = (destinationX - centre.x)*(destinationX - centre.x) + (destinationY - centre.z)*(destinationY - centre.z);
if((destinationX - this.destinationX)*(destinationX - this.destinationX) + (destinationY - this.destinationY)*(destinationY - this.destinationY) > 0.05 || distanceToDestination < 0.1){
resetLogicStatus();
this.destinationX = destinationX;
this.destinationY = destinationY;
newDestinationisGiven = true;
}
}
public void attack(solidObject o){
if(targetObject != o){
targetObject = o;
resetLogicStatus();
}
}
public void resetLogicStatus(){
if(movement != null)
movement.reset();
turretAngleDelta = 0;
bodyAngleDelta = 0;
currentMovementStatus = freeToMove;
attackStatus = noTarget;
stuckCount = 0;
destinationX = centre.x;
destinationY = centre.z;
insideDeistinationRadiusCount = 0;
obstacle = null;
closeToDestination = false;
}
public boolean willDieFromIncomingAttack() {
return currentHP - incomingDamage <= 0;
}
//to be implemented in child classes
public void update(){}
public void draw(){}
public void harvest(solidObject o){}
public void returnToRefinery(solidObject o){}
public void hold(){currentCommand = StandBy;}
public int getMaxHp(){return 0;}
}