package minechess.common;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import minechess.common.ai.AIMain;
import minechess.common.ai.ChessPosition;
import net.minecraft.entity.Entity;
import net.minecraft.entity.monster.EntityCreeper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.WeightedRandomChestContent;
import net.minecraft.world.World;
import net.minecraftforge.common.ChestGenHooks;
/**
* MineChess
* @author MineMaarten
* www.minemaarten.com
* @license Lesser GNU Public License v3 (http://www.gnu.org/licenses/lgpl.html)
*/
public class EntityKing extends EntityBaseChessPiece{
private final List<EntityPlayer> playersInArea = new ArrayList<EntityPlayer>();
public final List<ChessPosition> lastPositions = new ArrayList<ChessPosition>();
private int listUpdateTicks = 0;
private AIMain ai;
public float lastPositionScore;
public EntityKing(World par1World){
super(par1World);
listUpdateTicks = rand.nextInt(60);
}
@Override
public Entity getMob(){
EntityCreeper chargedCreeper = new EntityCreeper(worldObj);
chargedCreeper.onStruckByLightning(null);
return chargedCreeper;
}
@Override
public List<int[]> getPossibleMoves(){
List<int[]> moves = new ArrayList<int[]>();
for(int i = targetX - 1; i <= targetX + 1; i++) {
for(int j = targetZ - 1; j <= targetZ + 1; j++) {
if(i >= 0 && i < 8 && j >= 0 && j < 8) {
int[] move = new int[2];
move[0] = i;
move[1] = j;
moves.add(move);
}
}
}
if(firstMove) {
List<EntityBaseChessPiece> pieces = getChessPieces(true);
for(int i = 0; i < 2; i++) {
int[] castlingMove = new int[2];
castlingMove[0] = targetX - 2 + i * 4;
castlingMove[1] = targetZ;
boolean unmovedRook = false;
boolean piecesInBetween = false;
for(int j = 0; j < pieces.size(); j++) {
if(pieces.get(j) instanceof EntityRook && pieces.get(j).firstMove && pieces.get(j).targetX == i * 7 && pieces.get(j).targetZ == targetZ) {// when a rook is on the right side and hasn't moved yet
unmovedRook = true;
}
if((pieces.get(j).targetX == i * 5 + 1 || pieces.get(j).targetX == i * 3 + 2) && pieces.get(j).targetZ == targetZ) {// when there is a piece (ally or not) next to the rook,
piecesInBetween = true; // set to true. More is not needed
}
}
if(unmovedRook && !piecesInBetween) moves.add(castlingMove); //The chess rules will be handled in the main (EntityBaseChessPiece) class.
}
}
return moves;
}
@Override
public void onEntityUpdate(){
super.onEntityUpdate();
if(!computerPiece || worldObj.isRemote) return;
if(ai != null && ai.bQuit) ai = null;//kill not needed threads
if(ticksExisted % 10 == 0 && ai == null && isBlack() == isBlackTurn && deathTimer < 0 && !isPieceCapturing()) {
ai = new AIMain();//create an AI
ai.aiCaller.go(this);//activate the AI
}
if(mateInTimes < 0) return;//only give info about the puzzle if this is a puzzle
//--------Give information to nearby players about the objective of the current puzzle.----------
listUpdateTicks++;
if(listUpdateTicks < 60) return;
listUpdateTicks = 0;
for(int i = 0; i < playersInArea.size(); i++) {
if(!getNotSoNearbyPlayers().contains(playersInArea.get(i))) playersInArea.remove(i); //Remove players in the list that aren't nearby anymore.
}
List<EntityPlayer> playersNearby = getNearbyPlayers();
for(int i = 0; i < playersNearby.size(); i++) {
EntityPlayer playerNearby = playersNearby.get(i);
if(!playersInArea.contains(playerNearby)) {
playersInArea.add(playerNearby);
AchievementHandler.giveAchievement(playerNearby, "enterArena");
MineChessUtils.sendUnlocalizedMessage(playerNearby, "message.broadcast.puzzleObjective" + (isBlack() ? "Black" : "White"), EnumChatFormatting.BLUE.toString(), mateInTimes + "");
}
}
}
public List<EntityPlayer> getNearbyPlayers(){
AxisAlignedBB bbBox = new AxisAlignedBB(xOffset - 1, (int)posY - 1, zOffset - 1, xOffset + 8, posY + 2, zOffset + 8);
return worldObj.getEntitiesWithinAABB(EntityPlayer.class, bbBox);
}
public List<EntityPlayer> getNotSoNearbyPlayers(){
AxisAlignedBB bbBox = new AxisAlignedBB(xOffset - 3, (int)posY - 2, zOffset - 3, xOffset + 10, posY + 4, zOffset + 10);
return worldObj.getEntitiesWithinAABB(EntityPlayer.class, bbBox);
}
@Override
public void setDead(){
if(solvedPuzzle && !worldObj.isRemote && computerPiece && mateInTimes >= 0) {
int chestX = xOffset + targetX;
int chestY = (int)Math.floor(posY);
int chestZ = zOffset + targetZ;
for(int i = 0; i < 40; i++)
MineChessUtils.spawnParticle(EnumParticleTypes.EXPLOSION_NORMAL, worldObj, chestX + 0.5D, chestY + 0.5D, chestZ + 0.5D, rand.nextDouble() / 5 - 0.1D, rand.nextDouble() / 5 - 0.1D, rand.nextDouble() / 5 - 0.1D);
worldObj.setBlockState(new BlockPos(chestX, chestY, chestZ), Blocks.chest.getDefaultState());
TileEntityChest chest = (TileEntityChest)worldObj.getTileEntity(new BlockPos(chestX, chestY, chestZ));
WeightedRandomChestContent.generateChestContents(rand, ChestGenHooks.getItems(ChestGenHooks.MINESHAFT_CORRIDOR, rand), chest, ChestGenHooks.getCount(ChestGenHooks.MINESHAFT_CORRIDOR, rand));
for(int i = 0; i < 50; i++) {
int slot = rand.nextInt(chest.getSizeInventory());
if(chest.getStackInSlot(slot) == null) {
chest.setInventorySlotContents(slot, new ItemStack(MineChess.itemPieceMover, 1, 4));
break;
}
}
}
super.setDead();
}
/**
* Adds the current position, and returns true if the game is a draw (3x same position, last 50 positions no active move).
* @param messagePlayer when true, nearby players will be informed about when they can ask for a draw.
* @return
*/
public boolean checkForDraw(boolean messagePlayer){
//Check for active movement (movements that can't be reproduced like pawn movement). When found, clear the movement list.
ChessPosition lastPos = null;
if(lastPositions.size() > 1) {
lastPos = lastPositions.get(lastPositions.size() - 1);
if(lastPos.hasActiveDifference(lastPositions.get(lastPositions.size() - 2))) {
lastPositions.clear();
lastPositions.add(lastPos);
}
}
if(lastPositions.size() >= 100) {
sendChatToNearbyPlayers(null, "message.broadcast.inactiveDraw", EnumChatFormatting.GOLD.toString(), "" + lastPositions.size());
return true;//50x no active move (a move being both the player and his opponent moved once).
}
if(lastPos != null) {
//Check for 3x same position.
int samePositionsFound = 0;
for(ChessPosition testPos : lastPositions) {
if(testPos.isSame(lastPos)) samePositionsFound++;
}
if(samePositionsFound >= 3) {
sendChatToNearbyPlayers(null, "message.broadcast.samePositionThrice", EnumChatFormatting.GOLD.toString(), "" + samePositionsFound);
return true;
}
}
return false;
}
@Override
public void writeEntityToNBT(NBTTagCompound tag){
super.writeEntityToNBT(tag);
tag.setFloat("lastScore", lastPositionScore);
NBTTagList tagList = new NBTTagList();
for(int i = 0; i < lastPositions.size(); i++) {
NBTTagCompound positionTag = new NBTTagCompound();
positionTag.setByte("turn", (byte)i);
lastPositions.get(i).writeToNBT(positionTag);
tagList.appendTag(positionTag);
}
tag.setTag("lastPositions", tagList);
}
@Override
public void readEntityFromNBT(NBTTagCompound tag){
super.readEntityFromNBT(tag);
lastPositionScore = tag.getFloat("lastScore");
lastPositions.clear();
NBTTagList tagList = tag.getTagList("lastPositions", 10);
lastPositions.addAll(Arrays.asList(new ChessPosition[tagList.tagCount()])); //reserve a fixed space, so we can fill in this list in sequence.
for(int i = 0; i < tagList.tagCount(); i++) {
NBTTagCompound positionTag = tagList.getCompoundTagAt(i);
int turn = positionTag.getByte("turn");
if(turn >= 0 && turn < lastPositions.size()) {
ChessPosition position = new ChessPosition();
position.readFromNBT(positionTag);
lastPositions.set(turn, position);
}
}
}
}