package robombs.game.ai;
import java.util.ArrayList;
import java.util.List;
import com.threed.jpct.*;
import robombs.game.Globals;
import robombs.game.model.*;
import robombs.game.view.*;
import robombs.game.util.*;
import robombs.game.*;
public class Bot extends LocalPlayerObject {
public final static int NO_NEW_TARGET=0;
public final static int NEW_TARGET_CAUSE_BOMB=1;
public final static int NEW_TARGET_CAUSE_COLLISION=2;
public final static int NEW_TARGET_CAUSE_STUCK=3;
private AIGridPosition target=null;
private GridPosition[] path=null;
private int pathIndex=0;
private TargetFinder finder=new TargetFinder();
private int waitCounter=0;
private int newTargetLimit=0;
private int targetScheduled=NO_NEW_TARGET;
private long bombTimer=0;
private GridPosition lastTarget=null;
private GridPosition globalTarget=null;
private MapMask decisionMask=null;
private int gridCnt=0;
private GridPosition lastGrid=new GridPosition(-1,-1);
public void setDead() {
super.setDead();
target=null;
path=null;
pathIndex=0;
targetScheduled=NO_NEW_TARGET;
setSpeed(SimpleVector.ORIGIN);
}
public GridPosition getLastTarget() {
return lastTarget;
}
/*
private boolean shouldTryToEscapeFromBomb(LocalBombManager lbm, GridPosition next) {
boolean should=lbm.getBombMask().isInBombRange(next.getX(), next.getZ(), 4);
if (should) {
if (Ticker.getTime()-bombTimer<LocalBombManager.LIFE_TIME) {
return false;
}
}
return should;
}
*/
public void clearLastTarget() {
lastTarget=null;
}
public long getBombTimer() {
return bombTimer;
}
public boolean nextStep(BotClient client, World world, long ticks, Level level, ClientObjectManager com, LocalBombManager lbm, List<SimpleVector> playerPos, List<SimpleVector> playerPosTeam, PlayerDummy dummy) {
List<SimpleVector> playerPos2=new ArrayList<SimpleVector>(playerPos);
playerPos2.addAll(com.getPlayerPositions(true));
playerPos2.remove(getPosition()); // A bot should not block himself!
List<SimpleVector> playerPosOtherTeams=new ArrayList<SimpleVector>(playerPosTeam);
playerPosOtherTeams.addAll(com.getPlayerPositions(true, this.getView()));
playerPosOtherTeams.remove(getPosition());
PlayerPowers pp=getPlayerPowers();
int sick=pp.isSick();
float mul=1;
if (sick==PlayerPowers.SLOWER) {
mul=0.7f;
}
if (sick==PlayerPowers.FASTER) {
mul=1.3f;
}
MapMask mask=level.getMask();
MapMask bombs=lbm.getBombMask();
int width=mask.getWidth();
int height=mask.getHeight();
dummy.setObjectID(getObjectID()); // Make the dummy carry this bot's id for collision detection.
int crashCnt=0;
if (decisionMask==null || decisionMask.getWidth()!=width || decisionMask.getHeight()!=height) {
decisionMask=new MapMask(width, height);
}
// Either the speed will be set correctly later...or not. If not, 0,0,0 is correct. because it
// hasn't moved then...
setSpeed(SimpleVector.ORIGIN);
SimpleVector botPos=new SimpleVector(getPosition());
SimpleVector tmp=new SimpleVector(botPos);
GridPosition pos=mask.getGrid(botPos.x, botPos.z);
if (!isDead() && this.getAnimation()!=Animations.DIE && this.getAnimation()!=Animations.DEAD) {
if (pos.equals(lastGrid)) {
gridCnt++;
} else {
lastGrid.set(pos.getX(), pos.getZ());
gridCnt=0;
}
if (gridCnt>120) {
targetScheduled=NEW_TARGET_CAUSE_STUCK;
path=null;
gridCnt=0;
}
if (Math.random()<0.002) {
// Randomly drop a bomb...very seldom, but it happens
client.placeBomb(this);
path=null;
}
if (path==null || pathIndex==path.length || targetScheduled>NO_NEW_TARGET) {
// Determine a new target...
// Build a mask to base the decision on:
decisionMask.clear();
for (int x=0; x<width; x++) {
for (int y=0; y<height; y++) {
int b=bombs.getMaskAt(x, y);
int l=mask.getMaskAt(x, y);
int c=decisionMask.getMaskAt(x, y);
if (l==MapMask.UNKNOWN && c==0) {
decisionMask.setMaskAt(x, y, -99);
}
if (l==MapMask.CRATE && c==0) {
decisionMask.setMaskAt(x, y, 0);
}
if (l==MapMask.FLOOR && c==0) {
decisionMask.setMaskAt(x, y, 10);
}
if (MapMask.isItem(l) && c==0) {
decisionMask.setMaskAt(x, y, 15);
}
if (b==MapMask.BOMB) {
decisionMask.setMaskAt(x, y, -2);
boolean left=true;
boolean right=true;
boolean up=true;
boolean down=true;
for (int p=0; p<5; p++) {
if (p!=0) {
int tr=mask.getMaskAt(x+p, y);
int tl=mask.getMaskAt(x-p, y);
int tu=mask.getMaskAt(x, y-p);
int td=mask.getMaskAt(x, y+p);
if (tr==MapMask.UNKNOWN) {
right=false;
}
if (tl==MapMask.UNKNOWN) {
left=false;
}
if (tu==MapMask.UNKNOWN) {
up=false;
}
if (td==MapMask.UNKNOWN) {
down=false;
}
if (right) {
decisionMask.setMaskAt(x+p, y, -1);
}
if (left) {
decisionMask.setMaskAt(x-p, y, -1);
}
if (up) {
decisionMask.setMaskAt(x, y-p, -1);
}
if (down) {
decisionMask.setMaskAt(x, y+p, -1);
}
if (!right) {
if (tr==MapMask.FLOOR && decisionMask.getMaskAt(x+p, y)>=0) {
decisionMask.setMaskAt(x+p, y, 5);
}
}
if (!left) {
if (tl==MapMask.FLOOR && decisionMask.getMaskAt(x-p, y)>=0) {
decisionMask.setMaskAt(x-p, y, 5);
}
}
if (!up) {
if (tu==MapMask.FLOOR && decisionMask.getMaskAt(x, y-p)>=0) {
decisionMask.setMaskAt(x, y-p, 5);
}
}
if (!down) {
if (td==MapMask.FLOOR && decisionMask.getMaskAt(x, y+p)>=0) {
decisionMask.setMaskAt(x, y+p, 5);
}
}
}
}
}
}
}
//System.out.println(decisionMask.toString());
AStar as=new AStar(mask, false);
GridPosition newTarget=finder.findTarget(this, new AStar(mask, true), decisionMask, level, lbm, playerPosOtherTeams, targetScheduled);
if (newTarget!=null) {
globalTarget=newTarget;
as.clear();
as.setDecisionMask(decisionMask);
target=as.getPathToTarget(pos, newTarget);
}
if (target!=null) {
path=as.getPath(target);
pathIndex=0;
targetScheduled=NO_NEW_TARGET;
newTargetLimit=2;
/*
for (int i=0; i<path.length; i++) {
System.out.println("--> "+path[i]+"/"+target);
}
*/
} else {
path=null;
newTargetLimit=10;
}
}
if (target!=null) {
// Normal run...
SimpleVector next3D=path[pathIndex].convertTo3D();
next3D.y=tmp.y;
/*
if (pos.equals(path[pathIndex])) {
pathIndex++;
}
*/
if (next3D.calcSub(tmp).length()<0.5f) {
pathIndex++;
}
if (pathIndex<path.length) {
GridPosition next=path[pathIndex];
/*
if (bombs.getMaskAt(pos)!=MapMask.BOMB && newTargetLimit<=0) {
// Avoid a bomb's blow...
for (int i=1; i<3; i++) {
if (bombs.getMaskAt(next.getX()+i,next.getZ())==MapMask.BOMB ||
bombs.getMaskAt(next.getX()-i,next.getZ())==MapMask.BOMB ||
bombs.getMaskAt(next.getX(),next.getZ()+i)==MapMask.BOMB ||
bombs.getMaskAt(next.getX(),next.getZ()-i)==MapMask.BOMB) {
setSpeed(SimpleVector.ORIGIN);
newTargetLimit=20;
}
}
}
*/
boolean corner=false;
// Checks if the bot move diagonally and places a bomb if needed in that case.
if (pos.getDistanceTo(next)>1) {
GridPosition gp1=new GridPosition(pos.getX(), next.getZ());
GridPosition gp2=new GridPosition(next.getX(), pos.getZ());
corner=level.getCrateManager().hasCrateAt(gp1, mask)|level.getCrateManager().hasCrateAt(gp2, mask);
}
if ((corner || level.getCrateManager().hasCrateAt(next, mask)) && bombs.getMaskAt(pos)!=MapMask.BOMB /*&& newTargetLimit<=0*/) {
targetScheduled=NEW_TARGET_CAUSE_BOMB; // Then force a new target!
client.placeBomb(this);
bombTimer=Ticker.getTime();
lastTarget=globalTarget;
}
if (bombs.getMaskAt(next)==MapMask.BOMB && pathIndex>2) { // Don't walk into the bomb...
targetScheduled=NEW_TARGET_CAUSE_BOMB;
newTargetLimit=20;
}
SimpleVector newPos=next.convertTo3D();
newPos.y=tmp.y;
if (newTargetLimit!=0) {
newTargetLimit-=ticks;
}
float mind=10000000;
for (SimpleVector p:playerPosOtherTeams) {
float d=getPosition().calcSub(p).length();
if (d<mind) {
mind=d;
}
}
if (mind<30 && Math.random()>0.3f) {
// Enemy in range?
client.placeBomb(this);
}
if (waitCounter<=0 && isBlockedByPlayer(newPos, com, playerPos2)) {
// Blocked by a player? Then wait some time. Maybe the situation
// is resolved then...
waitCounter=(int)(15f*Math.random());
} else {
if (waitCounter>0) {
// Still waiting?
waitCounter-=ticks;
if (waitCounter<=0) {
// Finished waiting?
if (isBlockedByPlayer(newPos, com, playerPos2)) {
// ...but still blocked? Choose another target!
targetScheduled=NEW_TARGET_CAUSE_COLLISION;
newTargetLimit=10;
} else {
if (Math.random()<0.2f) {
// Not blocked, no enemy in range? Drop a bomb with 20% prob.
targetScheduled=NEW_TARGET_CAUSE_BOMB; // Then force a new target!
client.placeBomb(this);
newTargetLimit=20;
}
}
waitCounter=0;
}
} else {
if (newTargetLimit<=0) {
newPos=newPos.calcSub(botPos);
Matrix m=newPos.getRotationMatrix();
m.interpolate(getRotation(), m, 0.75f);
setRotation(m);
dummy.setBackRotationMatrix(m); // Store it in the dummy...collision detection needs it
SimpleVector ellipsoid=getEllipsoid();
if (botPos != null && botPos.y>Globals.skyLimit) {
botPos.y=-10.01f;
botPos.add(new SimpleVector(0, ellipsoid.y, 0));
SimpleVector netSpeed=new SimpleVector();
newPos=newPos.normalize();
newPos.scalarMul(mul*moveSpeed*(float) ticks);
dummy.getTranslationMatrix().setIdentity();
dummy.translate(botPos);
SimpleVector nP=new SimpleVector(newPos);
// Disable self and some of its own bombs...
getView().setVisibility(false);
lbm.setVisibilityOfBombs(getObjectID(), Globals.botsCanPassOwnBombsForMS, false);
newPos = dummy.checkForCollisionEllipsoid(newPos, ellipsoid, 5);
// Enable self and some of its own bombs...
getView().setVisibility(true);
lbm.setVisibilityOfBombs(getObjectID(), Globals.botsCanPassOwnBombsForMS, true);
if (!newPos.equals(nP)) {
crashCnt++;
if (crashCnt>10 || (!level.wasCollisionParticipant() && Math.random()>0.9f)) {
targetScheduled=NEW_TARGET_CAUSE_COLLISION;
newTargetLimit=10;
setSpeed(SimpleVector.ORIGIN);
newPos=SimpleVector.ORIGIN;
}
} else {
crashCnt=0;
}
botPos.add(newPos);
//netSpeed.scalarMul(1f/(float)ticks);
netSpeed.add(newPos);
netSpeed.y=0; // Ensure one level...
setSpeed(netSpeed);
botPos.add(new SimpleVector(0, -ellipsoid.y, 0));
botPos.y=-10.01f;
setPosition(botPos);
}
}
}
}
}
}
}
return !tmp.equals(getPosition());
}
private boolean isBlockedByPlayer(SimpleVector pos, ClientObjectManager com, List<SimpleVector> playerPos) {
return isBlockedGeneric(pos, playerPos);
}
private boolean isBlockedGeneric(SimpleVector pos, List<SimpleVector> poss) {
for (SimpleVector pos2:poss) {
float delta=pos.calcSub(pos2).length();
if (delta<10f) {
return true;
}
}
return false;
}
}