package minechess.common;
import java.util.ArrayList;
import java.util.List;
import minechess.common.ai.ChessMove;
import minechess.common.ai.ChessPosition;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
/**
* MineChess
* @author MineMaarten
* www.minemaarten.com
* @license Lesser GNU Public License v3 (http://www.gnu.org/licenses/lgpl.html)
*/
public abstract class EntityBaseChessPiece extends EntityLiving{
private static final ResourceLocation RESOURCE_WHITE_PIECE = new ResourceLocation("chessmod:textures/entities/white.png");
private static final ResourceLocation RESOURCE_BLACK_PIECE = new ResourceLocation("chessmod:textures/entities/black.png");
public boolean isBlackTurn = false; // white's first.
public boolean firstMove = true; // the first move of the piece (used in pawns, and castling)
public boolean isCapturing = false; // this variable is only set during moving when the piece is going to capture a piece from the other color
public boolean enPassantPossibility = false;
public boolean resignConfirmed = false;
public boolean computerPiece = false;
public boolean turnToMobOnDeath = false;
public boolean solvedPuzzle = true;
private boolean offsetNeedsUpdate = false;
public int mateInTimes = -1;
public int targetX = 0;
public int targetY = 0;
public int targetZ = 0;
public int xOffset = 0;
public int zOffset = 0;
public int deathTimer = -1;// disable the timer by default.
private static final int X_OFFSET_DATAWATCHER_ID = 28;
private static final int Z_OFFSET_DATAWATCHER_ID = 29;
private static final int IS_BLACK_DATAWATCHER_ID = 30;
private EntityPlayer playerLoser = null;
private float moveSpeed;
public EntityBaseChessPiece(World par1World){
super(par1World);
moveSpeed = 0.05F;
}
@Override
protected void entityInit(){
super.entityInit();
dataWatcher.addObject(IS_BLACK_DATAWATCHER_ID, new Byte((byte)1));
dataWatcher.addObject(X_OFFSET_DATAWATCHER_ID, new Integer(xOffset));
dataWatcher.addObject(Z_OFFSET_DATAWATCHER_ID, new Integer(zOffset));
}
@Override
protected void applyEntityAttributes(){
super.applyEntityAttributes();
getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.5D);
getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(1.0D);
}
public ResourceLocation getTexture(){
if(isBlack()) return RESOURCE_BLACK_PIECE;
return RESOURCE_WHITE_PIECE;
}
protected boolean canDespawn(){
return false;
}
@Override
public void setPosition(double x, double y, double z){
super.setPosition(x, y, z);
moveSpeed = 0.1F;
offsetNeedsUpdate = true;
}
public void setPositionAndOffset(double x, double y, double z){
setPosition(x, y, z);
xOffset = MathHelper.floor_double(x);
zOffset = MathHelper.floor_double(z);
}
// overriding this method makes this entity invulnerable, unless the entity
// gets captured or when the game is over.
@Override
public boolean attackEntityFrom(DamageSource par1DamageSource, float par2){
if(MineChess.DEBUG || par1DamageSource.getDamageType() == "outOfWorld") {
return super.attackEntityFrom(par1DamageSource, par2);
}
return false;
}
@Override
public boolean canBePushed(){
return false;
}
public void setIsBlack(boolean isBlack){
dataWatcher.updateObject(IS_BLACK_DATAWATCHER_ID, new Byte((byte)(isBlack ? 1 : 0)));
}
public boolean isBlack(){
return dataWatcher.getWatchableObjectByte(IS_BLACK_DATAWATCHER_ID) == 1;
}
public int getXOffset(){
return dataWatcher.getWatchableObjectInt(X_OFFSET_DATAWATCHER_ID);
}
public int getZOffset(){
return dataWatcher.getWatchableObjectInt(Z_OFFSET_DATAWATCHER_ID);
}
@Override
public void writeEntityToNBT(NBTTagCompound compound){
super.writeEntityToNBT(compound);
compound.setBoolean("isBlackTurn", isBlackTurn);
compound.setBoolean("isBlack", isBlack());
compound.setBoolean("isCapturing", isCapturing);
compound.setBoolean("firstMove", firstMove);
compound.setBoolean("enPasse", enPassantPossibility);
compound.setBoolean("computerPiece", computerPiece);
compound.setBoolean("solvedPuzzle", solvedPuzzle);
compound.setInteger("targetX", targetX);
compound.setInteger("targetY", targetY);
compound.setInteger("targetZ", targetZ);
compound.setInteger("xOffset", xOffset);
compound.setInteger("zOffset", zOffset);
compound.setInteger("deathTimer", deathTimer);
compound.setInteger("mateInTimes", mateInTimes);
}
@Override
public void readEntityFromNBT(NBTTagCompound compound){
super.readEntityFromNBT(compound);
isBlackTurn = compound.getBoolean("isBlackTurn");
firstMove = compound.getBoolean("firstMove");
isCapturing = compound.getBoolean("isCapturing");
enPassantPossibility = compound.getBoolean("enPasse");
computerPiece = compound.getBoolean("computerPiece");
solvedPuzzle = compound.getBoolean("solvedPuzzle");
targetX = compound.getInteger("targetX");
targetY = compound.getInteger("targetY");
targetZ = compound.getInteger("targetZ");
xOffset = compound.getInteger("xOffset");
zOffset = compound.getInteger("zOffset");
setIsBlack(compound.getBoolean("isBlack"));
deathTimer = compound.getInteger("deathTimer");
mateInTimes = compound.getInteger("mateInTimes");
}
public void setTargetPosition(int x, int z){
targetX = x;
targetY = (int)posY;
targetZ = z;
}
public int[] getTargetPosition(){
int[] target = new int[2];
target[0] = targetX;
target[1] = targetZ;
return target;
}
@Override
public boolean interact(EntityPlayer player){
if(player.inventory.getCurrentItem() != null && player.inventory.getCurrentItem().getItem() == MineChess.itemPieceMover && player.inventory.getCurrentItem().getItemDamage() < 2) {
EntityBaseChessPiece entitySelected = ItemPieceMover.getEntitySelected(player.worldObj, player.getCurrentEquippedItem());
if(entitySelected != null && isBlack() ^ entitySelected.isBlack()) {
// try to move to the enemy's piece if there is an ally piece selected.
if(!worldObj.isRemote) {
if(!getEnemyPiece().computerPiece) setLosingPlayer(player, isBlack()); //only set the XP repel to not puzzle boards.
entitySelected.tryToGoTo(targetX, targetZ, player);
} else {
// ItemPieceMover.setRenderTiles(null, 0, player.getCurrentEquippedItem());
}
} else if(player.inventory.getCurrentItem().getItemDamage() == 0 && isBlack() || player.inventory.getCurrentItem().getItemDamage() == 1 && !isBlack()) {
// else, select another ally piece
if(computerPiece && !worldObj.isRemote) {
MineChessUtils.sendUnlocalizedMessage(player, "message.error.computerPiece", EnumChatFormatting.RED.toString());
return false;
}
if(!worldObj.isRemote && this instanceof EntityKing && player.isSneaking()) {
if(resignConfirmed) {
setLosingPlayer(player, true);// the surrendering player shouldnt be able to pick up the black orbs.
setLosingPlayer(player, false);// neither the white orbs
MineChessUtils.sendUnlocalizedMessage(player, "message.player.youSurrender" + (isBlack() ? "WhiteWon" : "BlackWon"), EnumChatFormatting.RED.toString());
sendChatToNearbyPlayers(player, "message.broadcast." + (isBlack() ? "blackSurrender" : "whiteSurrender"), EnumChatFormatting.DARK_GREEN.toString());
setDeathTimer(!isBlack());
} else {
MineChessUtils.sendUnlocalizedMessage(player, "message.player.confirmSurrender");
resignConfirmed = true;
}
return false;
} else {
resignConfirmed = false;
}
if(!worldObj.isRemote) MineChessUtils.sendUnlocalizedMessage(player, "message.player.selectPiece", EnumChatFormatting.DARK_AQUA.toString(), "entity." + EntityList.getEntityString(this) + ".name");
if(!worldObj.isRemote) updateClient(getValidMoves(), player, getEntityId());
return false;
} else {
if(!worldObj.isRemote) MineChessUtils.sendUnlocalizedMessage(player, "message.error.notYourPiece", EnumChatFormatting.RED.toString());
}
// ItemPieceMover.setEntitySelected(-1, player.getCurrentEquippedItem());
} else if(player.inventory.getCurrentItem() != null && player.inventory.getCurrentItem().getItem() == MineChess.itemPieceMover && player.inventory.getCurrentItem().getItemDamage() == 4) {
if(!computerPiece) {
EntityBaseChessPiece enemy = getEnemyPiece();
if(enemy != null && enemy.mateInTimes < 0) {
setMateTimes(-1);
setComputerPiece(true);
if(!worldObj.isRemote) MineChessUtils.sendUnlocalizedMessage(player, "message.player.transform" + (isBlack() ? "BlackAI" : "WhiteAI"), EnumChatFormatting.DARK_BLUE.toString());
} else {
if(!worldObj.isRemote) MineChessUtils.sendUnlocalizedMessage(player, "message.error.aiPuzzle", EnumChatFormatting.RED.toString());
}
} else {
if(!worldObj.isRemote) MineChessUtils.sendUnlocalizedMessage(player, "message.error.alreadyAI", EnumChatFormatting.RED.toString());
}
} else {
if(!worldObj.isRemote) MineChessUtils.sendUnlocalizedMessage(player, "message.error.noPieceMover", EnumChatFormatting.RED.toString());
}
return false;
}
/**
* sets the repelling XP orbs to the player given to the pieces of the given color.
* @param player
* @param isBlack
*/
public void setLosingPlayer(EntityPlayer player, boolean isBlack){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
if(pieces.get(i).isBlack() == isBlack) pieces.get(i).playerLoser = player;
}
}
/**
* Sends al the valid moves of this chesspiece to the client, which will update the rendering.
* @param validMoves
* @param player
*/
private void updateClient(List<int[]> validMoves, EntityPlayer player, int entityID){
// PacketDispatcher.sendPacketToPlayer(PacketHandler.getPieceSelectedUpdatePacket(validMoves, targetY - 1, entityID), (Player)player);
ItemPieceMover.setEntitySelected(entityID, player.getCurrentEquippedItem());
ItemPieceMover.setRenderTiles(validMoves, targetY - 1, player.getCurrentEquippedItem());
}
public void sendChatToNearbyPlayers(EntityPlayer playerToExclude, String chatMessage){
sendChatToNearbyPlayers(playerToExclude, chatMessage, EnumChatFormatting.WHITE.toString());
}
private List<EntityPlayer> getNearbyPlayers(){
AxisAlignedBB bbBox = new AxisAlignedBB(xOffset - 5, (int)posY - 5, zOffset - 5, xOffset + 13, posY + 8, zOffset + 13);
return worldObj.getEntitiesWithinAABB(EntityPlayer.class, bbBox);
}
public void sendChatToNearbyPlayers(EntityPlayer playerToExclude, String chatMessage, String... replacements){
List<EntityPlayer> players = getNearbyPlayers();
for(int i = 0; i < players.size(); i++) {
if(playerToExclude == null || players.get(i) != playerToExclude) {
MineChessUtils.sendUnlocalizedMessage(players.get(i), chatMessage, replacements);
}
}
}
// try to go to the position specified
public boolean tryToGoTo(int x, int z, EntityPlayer player){
if(isDead) {
MineChessUtils.sendUnlocalizedMessage(player, "message.error.pieceCaptured", EnumChatFormatting.RED.toString());
return false;
}
if(isPawnAwaitingPromotion()) {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.pawnAwaitingPromotion", EnumChatFormatting.RED.toString());
} else if(isBlack() == isBlackTurn && !isPieceCapturing()) {
if(canGoTo(x, z, true, player)) {
EntityBaseChessPiece pieceToBeCaptured = getPieceAt(x, z);
int oldX = targetX;// save the old targets for when the move isn't possible due to king danger.
int oldZ = targetZ;
setTargetPosition(x, z);
if(pieceToBeCaptured != null) pieceToBeCaptured.setTargetPosition(10, 10);//virtually move the piece of the board
boolean kingInDanger = isKingInDanger(isBlack(), true); //check if the king is in danger when this piece would be gone.
if(pieceToBeCaptured != null) pieceToBeCaptured.setTargetPosition(x, z);//move the piece back on the board.
if(kingInDanger) {// if the king's in danger,
setTargetPosition(oldX, oldZ);// take the old positions again.
isCapturing = false;
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.kingInCheck", EnumChatFormatting.RED.toString());
return false;
} else {
if(!handleCastling(player)) {// if the player wasn't able to castle (due to moving the king over an in chess zone)
setTargetPosition(oldX, oldZ);
return false;
}
clearEnPassant(); // none of the pawns have jumped two rows at once.
enPassantPossibility = this instanceof EntityPawn && firstMove && (targetZ == 3 || targetZ == 4);
firstMove = false; // set false if it weren't already.
if(this instanceof EntityKnight) motionY = 0.8D;
if(player != null) {
MineChessUtils.sendUnlocalizedMessage(player, "message.player.movePiece", EnumChatFormatting.DARK_GREEN.toString(), "entity." + EntityList.getEntityString(this) + ".name", getColumnName(x) + (z + 1));
AchievementHandler.giveAchievement(player, "movePiece");
}
sendChatToNearbyPlayers(player, "message.broadcast.move" + (isBlack() ? "BlackPiece" : "WhitePiece"), EnumChatFormatting.DARK_GREEN.toString(), "entity." + EntityList.getEntityString(this) + ".name", getColumnName(x) + (z + 1));
if(!isCapturing) {
handleAfterTurn(player);
}
return true;
}
}
} else {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error." + (isBlackTurn ? "blacksTurn" : "whitesTurn"), EnumChatFormatting.RED.toString());
}
return false;
}
protected void handleAfterTurn(EntityPlayer player){
if(!isPawnAwaitingPromotion()) {
List<EntityBaseChessPiece> pieces = getChessPieces(true);
EntityKing king = null;
for(EntityBaseChessPiece piece : pieces) {
if(piece instanceof EntityKing && piece.isBlack()) {//only the black king keeps track of the positions.
king = (EntityKing)piece;
break;
}
}
if(king != null) {
ChessPosition curPos = new ChessPosition(this);
king.lastPositions.add(curPos);
}
int movesAvailable = isGameOver();
if(movesAvailable == 0) {
if(isKingInDanger(!isBlack(), false)) {
if(player != null) {
MineChessUtils.sendUnlocalizedMessage(player, "message.broadcast.checkmate", EnumChatFormatting.BLUE.toString());
AchievementHandler.giveAchievement(player, "checkmate");
}
for(EntityPlayer nearbyPlayer : getNearbyPlayers())
if(nearbyPlayer != player) AchievementHandler.giveAchievement(nearbyPlayer, "lose");
sendChatToNearbyPlayers(player, "message.broadcast.checkmate", EnumChatFormatting.DARK_RED.toString());
} else {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.broadcast.stalemate", EnumChatFormatting.BLUE.toString());
for(EntityPlayer nearbyPlayer : getNearbyPlayers())
AchievementHandler.giveAchievement(nearbyPlayer, "stalemate");
sendChatToNearbyPlayers(player, "message.broadcast.stalemate", EnumChatFormatting.DARK_RED.toString());
}
setDeathTimer(isBlack());
} else if(isKingInDanger(!isBlack(), false)) {
if(player != null) {
MineChessUtils.sendUnlocalizedMessage(player, "message.broadcast.check", EnumChatFormatting.YELLOW.toString());
AchievementHandler.giveAchievement(player, "check");
}
sendChatToNearbyPlayers(player, "message.broadcast.check", EnumChatFormatting.RED.toString());
}
if(king != null) {
king.checkForDraw(true);
}
switchTurns();
handlePuzzles(player);
}
}
private EntityBaseChessPiece getPieceAt(int x, int z){
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(EntityBaseChessPiece piece : pieces) {
if(piece.targetX == x && piece.targetZ == z) {
return piece;
}
}
return null;
}
protected boolean isPieceCapturing(){
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(EntityBaseChessPiece piece : pieces) {
if(piece.isCapturing) return true;
}
return false;
}
protected boolean isPawnAwaitingPromotion(){
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(EntityBaseChessPiece piece : pieces) {
if(!piece.isDead && piece instanceof EntityPawn && piece.targetZ % 7 == 0) return true;
}
return false;
}
private void handlePuzzles(EntityPlayer player){
EntityBaseChessPiece enemy = getEnemyPiece();
if(enemy != null && enemy.computerPiece) {
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(int i = 0; i < pieces.size(); i++) {
if(enemy.isBlack() == pieces.get(i).isBlack()) {
List<int[]> moves = pieces.get(i).getValidMoves();
if(moves.size() > 0) {
if(mateInTimes == 1) {
setDeathTimer(enemy.isBlack());// enemy won.
setPuzzleFail();
MineChessUtils.onPuzzleFail(worldObj, player, this, xOffset, targetY, zOffset, rand);
}
mateInTimes--;
setMateTimes(mateInTimes);
return;
}
}
}
if(player != null && deathTimer > 0) AchievementHandler.giveAchievement(player, "puzzleWin");
}
}
public void setMateTimes(int mateInTime){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
pieces.get(i).mateInTimes = mateInTime;
}
}
public void setPuzzleFail(){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
pieces.get(i).solvedPuzzle = false;
}
}
public void setComputerPiece(boolean isComputer){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
if(pieces.get(i).isBlack() == isBlack()) pieces.get(i).computerPiece = isComputer;
}
}
public void setDeathTimer(boolean winnerIsBlack){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
pieces.get(i).deathTimer = rand.nextInt(100) /* + 280 */
+ (winnerIsBlack ^ pieces.get(i).isBlack() ? pieces.get(i) instanceof EntityKing ? 0 : 120 : 160);
}
}
private boolean handleCastling(EntityPlayer player){
if(!(this instanceof EntityKing) || !firstMove) return true; // don't influence moves that haven't to do with castling at all.
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(int i = 0; i < pieces.size(); i++) {
if(pieces.get(i) instanceof EntityRook && isBlack() == pieces.get(i).isBlack()) {
if(pieces.get(i).targetX == 0 && targetX == 1) {
targetX = 3;
if(isKingInDanger(isBlack(), false)) {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.kingInCheck", EnumChatFormatting.RED.toString());
return false;// the player can't castle if the king is in check.
}
targetX = 2;
if(isKingInDanger(isBlack(), true)) {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.kingCheckCastling", EnumChatFormatting.RED.toString());
return false;// the player can't move the King over an in check tile.
}
targetX = 1;
pieces.get(i).targetX = 2;
pieces.get(i).firstMove = false;
pieces.get(i).motionY = 0.8D;
} else if(pieces.get(i).targetX == 7 && targetX == 5) {
targetX = 3;
if(isKingInDanger(isBlack(), false)) {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.kingInCheck", EnumChatFormatting.RED.toString());
return false;// the player can't castle if the king is in check.
}
targetX = 4;
if(isKingInDanger(isBlack(), true)) {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.kingCheckCastling", EnumChatFormatting.RED.toString());
return false;// the player can't move the King over an in check tile.
}
targetX = 5;
pieces.get(i).targetX = 4;
pieces.get(i).firstMove = false;
pieces.get(i).motionY = 0.8D;
}
}
}
if(player != null && targetX == 5 || targetX == 1) AchievementHandler.giveAchievement(player, "castling");
return true;
}
public String getColumnName(int x){
switch(x){
case 0:
return "H";
case 1:
return "G";
case 2:
return "F";
case 3:
return "E";
case 4:
return "D";
case 5:
return "C";
case 6:
return "B";
default:
return "A";// which is 7
}
}
/**
* returns true if this entity can go to the position specified. If the
* boolean's set to true, the entity will be flagged to capture the piece
* potentially standing on the position specified.
* @param x
* @param z
* @param setCaptureIfneccessary
* @param player
* @return
*/
public boolean canGoTo(int x, int z, boolean setCaptureIfneccessary, EntityPlayer player){
List<int[]> possibleMoves = getPossibleMoves();
if(possibleMoves == null) return false;
for(int i = 0; i < possibleMoves.size(); i++) {
// if the desired move is in the move list, and there's no piece in
// between if the piece isn't a knight, and we are not moving to the
// space we already are
if(possibleMoves.get(i)[0] == x && possibleMoves.get(i)[1] == z && (!isPieceInBetween(x, z) || this instanceof EntityKnight) && (x != targetX || z != targetZ)) {
// Check for occupying pieces
List<EntityBaseChessPiece> chessPieces = getChessPieces(true);
for(int j = 0; j < chessPieces.size(); j++) {
if(chessPieces.get(j).getTargetPosition()[0] == x && chessPieces.get(j).getTargetPosition()[1] == z) {// when a chesspiece is occupying this space.
if(chessPieces.get(j).isBlack() != isBlack()) { //if the piece is different colored.
if(setCaptureIfneccessary) {
isCapturing = true;
}
return true;
} else {
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.ownPieceInWay", EnumChatFormatting.RED.toString());
return false;
}
} else if(this instanceof EntityPawn && chessPieces.get(j).enPassantPossibility && chessPieces.get(j).targetZ == targetZ && (chessPieces.get(j).targetX == targetX - 1 || chessPieces.get(j).targetX == targetX + 1) && chessPieces.get(j).isBlack() ^ isBlack() && targetX != x && setCaptureIfneccessary) { // passant
chessPieces.get(j).kill();
if(player != null) AchievementHandler.giveAchievement(player, "enPassant");
}
}
return true;
}
}
if(player != null) MineChessUtils.sendUnlocalizedMessage(player, "message.error.movementRuleBlocks", EnumChatFormatting.RED.toString());
return false;
}
/**
* Returns true when there is a piece between the given coordinate and the current coordinate of this entity.
* @param x
* @param z
* @return
*/
private boolean isPieceInBetween(int x, int z){
List<EntityBaseChessPiece> chessPieces = getChessPieces(true);
int checkingX = targetX;
int checkingZ = targetZ;
while(checkingX != x || checkingZ != z) {
for(int i = 0; i < chessPieces.size(); i++) {
// filter out the to be moved and the target piece.
if(chessPieces.get(i).getTargetPosition()[0] == checkingX && chessPieces.get(i).getTargetPosition()[1] == checkingZ && (checkingX != targetX || checkingZ != targetZ) && (checkingX != x || checkingZ != z)) {
return true;
}
}
if(checkingX < x) checkingX++;
if(checkingX > x) checkingX--;
if(checkingZ < z) checkingZ++;
if(checkingZ > z) checkingZ--;
}
return false;
}
/**
* Returns true if this entity can capture the king of the color given.
* @param kingIsBlack
* @return
*/
public boolean canCaptureKing(boolean kingIsBlack){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
// if the piece is the other side's king
if(pieces.get(i) instanceof EntityKing && pieces.get(i).isBlack() == kingIsBlack) {
int[] kingPos = pieces.get(i).getTargetPosition();
if(canGoTo(kingPos[0], kingPos[1], false, null)) {
return true;
}
}
}
return false;
}
/**
* Returns true if the king of the given color can be captured by the other team.
* @param isBlack
* @param impliedMove
* @return
*/
public boolean isKingInDanger(boolean isBlack, boolean impliedMove){
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(int i = 0; i < pieces.size(); i++) {
// when the piece is from the other color, and he can capture the
// king, and this entity isn't going to be killed by the implied
// move, return true.
if(pieces.get(i).isBlack() ^ isBlack && pieces.get(i).canCaptureKing(isBlack) && (pieces.get(i).getTargetPosition()[0] != targetX || pieces.get(i).getTargetPosition()[1] != targetZ || !impliedMove)) {
return true;
}
}
return false;
}
/**
* returns all valid moves of this entity.
* @return
*/
public List<int[]> getValidMoves(){
List<int[]> moves = new ArrayList<int[]>();
for(int i = 0; i < 8; i++) {
for(int j = 0; j < 8; j++) {
if(canGoTo(i, j, false, null)) {
int oldX = targetX;
int oldZ = targetZ;
EntityBaseChessPiece pieceToBeCaptured = getPieceAt(i, j);
targetX = i;
targetZ = j;
if(pieceToBeCaptured != null) pieceToBeCaptured.setTargetPosition(10, 10);
boolean kingInDanger = isKingInDanger(isBlack(), true);
if(pieceToBeCaptured != null) pieceToBeCaptured.setTargetPosition(i, j);
targetX = oldX;
targetZ = oldZ;
if(kingInDanger) continue;
int[] move = new int[2];
move[0] = i;
move[1] = j;
moves.add(move);
}
}
}
return moves;
}
public EntityBaseChessPiece getEnemyPiece(){
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(int i = 0; i < pieces.size(); i++) {
if(pieces.get(i).isBlack() ^ isBlack()) {
return pieces.get(i);
}
}
return null;
}
/**
* Returns the amount of moves the opposite team has.
* @return
*/
public int isGameOver(){
List<EntityBaseChessPiece> pieces = getChessPieces(true);
int totalValidMoves = 0;
for(int i = 0; i < pieces.size(); i++) {
if(pieces.get(i).isBlack() ^ isBlack()) {
totalValidMoves += pieces.get(i).getValidMoves().size();
}
}
return totalValidMoves;
}
public abstract List<int[]> getPossibleMoves();
public void switchTurns(){
boolean turn = !isBlackTurn;
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
pieces.get(i).isBlackTurn = turn; // notify to all the pieces that the other color is to move.
}
}
public void clearEnPassant(){
List<EntityBaseChessPiece> pieces = getChessPieces(false);
for(int i = 0; i < pieces.size(); i++) {
pieces.get(i).enPassantPossibility = false; // notify to all the pieces that they are not able to En Passant.
}
}
/**
* Returns a list of all the chess pieces on the board. This method also filters
* pieces which belong to an other board.
* @param filterToBeCapturedPieces filter out pieces that are to be captured.
* @return
*/
public List<EntityBaseChessPiece> getChessPieces(boolean filterToBeCapturedPieces){
AxisAlignedBB bbBox = new AxisAlignedBB(xOffset - 1, (int)posY - 1, zOffset - 1, xOffset + 8, posY + 2, zOffset + 8);
List<EntityBaseChessPiece> pieces = worldObj.getEntitiesWithinAABB(EntityBaseChessPiece.class, bbBox);
filterPieces(pieces, filterToBeCapturedPieces);
return pieces;
}
//TODO refactoring: This can be done non-recursively.
private void filterPieces(List<EntityBaseChessPiece> pieces, boolean filterToBeCapturedPieces){
for(int i = 0; i < pieces.size(); i++) {
if(pieces.get(i).dead || pieces.get(i).xOffset != xOffset || pieces.get(i).zOffset != zOffset) {
pieces.remove(i);
filterPieces(pieces, filterToBeCapturedPieces);
return;
}
if(pieces.get(i).isCapturing && filterToBeCapturedPieces) {
for(int j = 0; j < pieces.size(); j++) {
if(pieces.get(i).targetX == pieces.get(j).targetX && pieces.get(i).targetZ == pieces.get(j).targetZ && pieces.get(i) != pieces.get(j)) {
pieces.remove(j);
filterPieces(pieces, filterToBeCapturedPieces);
return;
}
}
}
}
}
@Override
public void onEntityUpdate(){
if(!worldObj.isRemote) {
if(offsetNeedsUpdate) {
dataWatcher.updateObject(X_OFFSET_DATAWATCHER_ID, new Integer(xOffset));
dataWatcher.updateObject(Z_OFFSET_DATAWATCHER_ID, new Integer(zOffset));
offsetNeedsUpdate = false;
}
if(deathTimer > 0) {
deathTimer--;
if(turnToMobOnDeath && rand.nextInt(40) == 0) { //indicate the piece is going to transform
int iterations = rand.nextInt(3) + 3;
for(int i = 0; i < iterations; i++) {
MineChessUtils.spawnParticle(EnumParticleTypes.FLAME, worldObj, posX, posY + rand.nextDouble() * 1.5D, posZ, rand.nextDouble() / 10 - 0.05D, rand.nextDouble() / 10 - 0.05D, rand.nextDouble() / 10 - 0.05D);
}
}
} else if(deathTimer == 0) {
if(turnToMobOnDeath) {
if(!worldObj.isRemote) {
Entity mob = getMob();
mob.setPosition(posX, posY, posZ);
worldObj.spawnEntityInWorld(mob);
setDead();
worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1009, new BlockPos(posX, posY, posZ), 0);
for(int i = 0; i < 40; i++) {
MineChessUtils.spawnParticle(EnumParticleTypes.FLAME, worldObj, posX, posY + rand.nextDouble() * 1.5D, posZ, rand.nextDouble() / 10 - 0.05D, rand.nextDouble() / 10 - 0.05D, rand.nextDouble() / 10 - 0.05D);
}
}
} else {
kill();
}
}
//Actually move the pieces to the target location
int xCoord = targetX + xOffset;
int zCoord = targetZ + zOffset;
if(xCoord + 0.5D > posX + 0.1D) {
motionX = moveSpeed;
} else if(xCoord + 0.5D < posX - 0.1D) {
motionX = -moveSpeed;
}
if(zCoord + 0.5D > posZ + 0.1D) {
motionZ = moveSpeed;
} else if(zCoord + 0.5D < posZ - 0.1D) {
motionZ = -moveSpeed;
}
if(getDistance(xCoord, targetY, zCoord) < 1.0D) {//When the piece is arrived at destination
if(isCapturing) { //and the piece is capturing
List<EntityBaseChessPiece> chessPieces = getChessPieces(false);
for(int i = 0; i < chessPieces.size(); i++) {
if(chessPieces.get(i).getTargetPosition()[0] == targetX && chessPieces.get(i).getTargetPosition()[1] == targetZ && chessPieces.get(i) != this) {
if(chessPieces.get(i) instanceof EntityKing) {
setDeathTimer(isBlack()); //remove all the pieces, as the game is over without king.
if(playerLoser != null) MineChessUtils.sendUnlocalizedMessage(playerLoser, "message.error.bugReport.capturedKing", EnumChatFormatting.RED.toString());
}
chessPieces.get(i).kill();
break;
}
}
handleAfterTurn(playerLoser);
isCapturing = false;
}
moveSpeed = 0.05F;
} else {
moveSpeed = 0.1F;
}
}
super.onEntityUpdate();
// this will make the pieces always visually stand right. Also it turns
// the black pieces 180 degrees.
rotationYaw = isBlack() ? 180F : 0F;
renderYawOffset = rotationYaw;
}
/**
* Invoked by the AI, this method executes the given move, and beforehand checks if this move is valid.
* @param move
*/
public void executeMove(ChessMove move){
if(move != null) {
EntityBaseChessPiece piece = getPieceAt(move.from % 10, move.from / 10);
if(piece != null) {
if(piece.tryToGoTo(move.to % 10, move.to / 10, null)) return;
} else {
System.err.println("[Chess AI --> board] No piece (or white piece) found at " + move.from % 10 + ", " + move.from / 10);
}
}
//catch when the AI doesn't deliver a valid move. Execute the first valid move we can find then.
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(EntityBaseChessPiece piece : pieces) {
if(piece.isBlack() == isBlack()) {
List<int[]> moves = getValidMoves();
if(moves.size() > 0) {
piece.tryToGoTo(moves.get(0)[0], moves.get(0)[1], null);
return;
}
}
}
//When we still weren't able to execute a move, remove the ai.
if(!worldObj.isRemote) sendChatToNearbyPlayers(null, "message.error.bugReport.aiWrongMove", EnumChatFormatting.RED.toString());
setComputerPiece(false);
}
/**
* Should return the entity the piece should transform into when the mob attack punish gets executed.
* @return
*/
public abstract Entity getMob();
@Override
public void setDead(){
super.setDead();
if(!worldObj.isRemote && solvedPuzzle) {
EntityPickyXPOrb xpOrb = new EntityPickyXPOrb(worldObj, posX, posY, posZ, 1, playerLoser);
worldObj.spawnEntityInWorld(xpOrb);
}
}
/**
* Overriden to make the method public
*/
@Override
public void kill(){
super.kill();
}
}