    001    package net.minecraft.src;
    002
    003    import java.util.ArrayList;
    004    import java.util.Iterator;
    005    import java.util.List;
    006    import java.util.TreeMap;
    007
    008    public class Village
    009    {
    010        private World worldObj;
    011
    012        /** list of VillageDoorInfo objects */
    013        private final List villageDoorInfoList = new ArrayList();
    014
    015        /**
    016         * This is the sum of all door coordinates and used to calculate the actual village center by dividing by the number
    017         * of doors.
    018         */
    019        private final ChunkCoordinates centerHelper = new ChunkCoordinates(0, 0, 0);
    020
    021        /** This is the actual village center. */
    022        private final ChunkCoordinates center = new ChunkCoordinates(0, 0, 0);
    023        private int villageRadius = 0;
    024        private int lastAddDoorTimestamp = 0;
    025        private int tickCounter = 0;
    026        private int numVillagers = 0;
    027        private int field_82694_i;
    028        private TreeMap field_82693_j = new TreeMap();
    029        private List villageAgressors = new ArrayList();
    030        private int numIronGolems = 0;
    031
    032        public Village() {}
    033
    034        public Village(World par1World)
    035        {
    036            this.worldObj = par1World;
    037        }
    038
    039        public void func_82691_a(World par1World)
    040        {
    041            this.worldObj = par1World;
    042        }
    043
    044        /**
    045         * Called periodically by VillageCollection
    046         */
    047        public void tick(int par1)
    048        {
    049            this.tickCounter = par1;
    050            this.removeDeadAndOutOfRangeDoors();
    051            this.removeDeadAndOldAgressors();
    052
    053            if (par1 % 20 == 0)
    054            {
    055                this.updateNumVillagers();
    056            }
    057
    058            if (par1 % 30 == 0)
    059            {
    060                this.updateNumIronGolems();
    061            }
    062
    063            int var2 = this.numVillagers / 10;
    064
    065            if (this.numIronGolems < var2 && this.villageDoorInfoList.size() > 20 && this.worldObj.rand.nextInt(7000) == 0)
    066            {
    067                Vec3 var3 = this.tryGetIronGolemSpawningLocation(MathHelper.floor_float((float)this.center.posX), MathHelper.floor_float((float)this.center.posY), MathHelper.floor_float((float)this.center.posZ), 2, 4, 2);
    068
    069                if (var3 != null)
    070                {
    071                    EntityIronGolem var4 = new EntityIronGolem(this.worldObj);
    072                    var4.setPosition(var3.xCoord, var3.yCoord, var3.zCoord);
    073                    this.worldObj.spawnEntityInWorld(var4);
    074                    ++this.numIronGolems;
    075                }
    076            }
    077        }
    078
    079        /**
    080         * Tries up to 10 times to get a valid spawning location before eventually failing and returning null.
    081         */
    082        private Vec3 tryGetIronGolemSpawningLocation(int par1, int par2, int par3, int par4, int par5, int par6)
    083        {
    084            for (int var7 = 0; var7 < 10; ++var7)
    085            {
    086                int var8 = par1 + this.worldObj.rand.nextInt(16) - 8;
    087                int var9 = par2 + this.worldObj.rand.nextInt(6) - 3;
    088                int var10 = par3 + this.worldObj.rand.nextInt(16) - 8;
    089
    090                if (this.isInRange(var8, var9, var10) && this.isValidIronGolemSpawningLocation(var8, var9, var10, par4, par5, par6))
    091                {
    092                    return this.worldObj.func_82732_R().getVecFromPool((double)var8, (double)var9, (double)var10);
    093                }
    094            }
    095
    096            return null;
    097        }
    098
    099        private boolean isValidIronGolemSpawningLocation(int par1, int par2, int par3, int par4, int par5, int par6)
    100        {
    101            if (!this.worldObj.doesBlockHaveSolidTopSurface(par1, par2 - 1, par3))
    102            {
    103                return false;
    104            }
    105            else
    106            {
    107                int var7 = par1 - par4 / 2;
    108                int var8 = par3 - par6 / 2;
    109
    110                for (int var9 = var7; var9 < var7 + par4; ++var9)
    111                {
    112                    for (int var10 = par2; var10 < par2 + par5; ++var10)
    113                    {
    114                        for (int var11 = var8; var11 < var8 + par6; ++var11)
    115                        {
    116                            if (this.worldObj.isBlockNormalCube(var9, var10, var11))
    117                            {
    118                                return false;
    119                            }
    120                        }
    121                    }
    122                }
    123
    124                return true;
    125            }
    126        }
    127
    128        private void updateNumIronGolems()
    129        {
    130            List var1 = this.worldObj.getEntitiesWithinAABB(EntityIronGolem.class, AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)(this.center.posX - this.villageRadius), (double)(this.center.posY - 4), (double)(this.center.posZ - this.villageRadius), (double)(this.center.posX + this.villageRadius), (double)(this.center.posY + 4), (double)(this.center.posZ + this.villageRadius)));
    131            this.numIronGolems = var1.size();
    132        }
    133
    134        private void updateNumVillagers()
    135        {
    136            List var1 = this.worldObj.getEntitiesWithinAABB(EntityVillager.class, AxisAlignedBB.getAABBPool().addOrModifyAABBInPool((double)(this.center.posX - this.villageRadius), (double)(this.center.posY - 4), (double)(this.center.posZ - this.villageRadius), (double)(this.center.posX + this.villageRadius), (double)(this.center.posY + 4), (double)(this.center.posZ + this.villageRadius)));
    137            this.numVillagers = var1.size();
    138
    139            if (this.numVillagers == 0)
    140            {
    141                this.field_82693_j.clear();
    142            }
    143        }
    144
    145        public ChunkCoordinates getCenter()
    146        {
    147            return this.center;
    148        }
    149
    150        public int getVillageRadius()
    151        {
    152            return this.villageRadius;
    153        }
    154
    155        /**
    156         * Actually get num village door info entries, but that boils down to number of doors. Called by
    157         * EntityAIVillagerMate and VillageSiege
    158         */
    159        public int getNumVillageDoors()
    160        {
    161            return this.villageDoorInfoList.size();
    162        }
    163
    164        public int getTicksSinceLastDoorAdding()
    165        {
    166            return this.tickCounter - this.lastAddDoorTimestamp;
    167        }
    168
    169        public int getNumVillagers()
    170        {
    171            return this.numVillagers;
    172        }
    173
    174        /**
    175         * Returns true, if the given coordinates are within the bounding box of the village.
    176         */
    177        public boolean isInRange(int par1, int par2, int par3)
    178        {
    179            return this.center.getDistanceSquared(par1, par2, par3) < (float)(this.villageRadius * this.villageRadius);
    180        }
    181
    182        /**
    183         * called only by class EntityAIMoveThroughVillage
    184         */
    185        public List getVillageDoorInfoList()
    186        {
    187            return this.villageDoorInfoList;
    188        }
    189
    190        public VillageDoorInfo findNearestDoor(int par1, int par2, int par3)
    191        {
    192            VillageDoorInfo var4 = null;
    193            int var5 = Integer.MAX_VALUE;
    194            Iterator var6 = this.villageDoorInfoList.iterator();
    195
    196            while (var6.hasNext())
    197            {
    198                VillageDoorInfo var7 = (VillageDoorInfo)var6.next();
    199                int var8 = var7.getDistanceSquared(par1, par2, par3);
    200
    201                if (var8 < var5)
    202                {
    203                    var4 = var7;
    204                    var5 = var8;
    205                }
    206            }
    207
    208            return var4;
    209        }
    210
    211        /**
    212         * Find a door suitable for shelter. If there are more doors in a distance of 16 blocks, then the least restricted
    213         * one (i.e. the one protecting the lowest number of villagers) of them is chosen, else the nearest one regardless
    214         * of restriction.
    215         */
    216        public VillageDoorInfo findNearestDoorUnrestricted(int par1, int par2, int par3)
    217        {
    218            VillageDoorInfo var4 = null;
    219            int var5 = Integer.MAX_VALUE;
    220            Iterator var6 = this.villageDoorInfoList.iterator();
    221
    222            while (var6.hasNext())
    223            {
    224                VillageDoorInfo var7 = (VillageDoorInfo)var6.next();
    225                int var8 = var7.getDistanceSquared(par1, par2, par3);
    226
    227                if (var8 > 256)
    228                {
    229                    var8 *= 1000;
    230                }
    231                else
    232                {
    233                    var8 = var7.getDoorOpeningRestrictionCounter();
    234                }
    235
    236                if (var8 < var5)
    237                {
    238                    var4 = var7;
    239                    var5 = var8;
    240                }
    241            }
    242
    243            return var4;
    244        }
    245
    246        public VillageDoorInfo getVillageDoorAt(int par1, int par2, int par3)
    247        {
    248            if (this.center.getDistanceSquared(par1, par2, par3) > (float)(this.villageRadius * this.villageRadius))
    249            {
    250                return null;
    251            }
    252            else
    253            {
    254                Iterator var4 = this.villageDoorInfoList.iterator();
    255                VillageDoorInfo var5;
    256
    257                do
    258                {
    259                    if (!var4.hasNext())
    260                    {
    261                        return null;
    262                    }
    263
    264                    var5 = (VillageDoorInfo)var4.next();
    265                }
    266                while (var5.posX != par1 || var5.posZ != par3 || Math.abs(var5.posY - par2) > 1);
    267
    268                return var5;
    269            }
    270        }
    271
    272        public void addVillageDoorInfo(VillageDoorInfo par1VillageDoorInfo)
    273        {
    274            this.villageDoorInfoList.add(par1VillageDoorInfo);
    275            this.centerHelper.posX += par1VillageDoorInfo.posX;
    276            this.centerHelper.posY += par1VillageDoorInfo.posY;
    277            this.centerHelper.posZ += par1VillageDoorInfo.posZ;
    278            this.updateVillageRadiusAndCenter();
    279            this.lastAddDoorTimestamp = par1VillageDoorInfo.lastActivityTimestamp;
    280        }
    281
    282        /**
    283         * Returns true, if there is not a single village door left. Called by VillageCollection
    284         */
    285        public boolean isAnnihilated()
    286        {
    287            return this.villageDoorInfoList.isEmpty();
    288        }
    289
    290        public void addOrRenewAgressor(EntityLiving par1EntityLiving)
    291        {
    292            Iterator var2 = this.villageAgressors.iterator();
    293            VillageAgressor var3;
    294
    295            do
    296            {
    297                if (!var2.hasNext())
    298                {
    299                    this.villageAgressors.add(new VillageAgressor(this, par1EntityLiving, this.tickCounter));
    300                    return;
    301                }
    302
    303                var3 = (VillageAgressor)var2.next();
    304            }
    305            while (var3.agressor != par1EntityLiving);
    306
    307            var3.agressionTime = this.tickCounter;
    308        }
    309
    310        public EntityLiving findNearestVillageAggressor(EntityLiving par1EntityLiving)
    311        {
    312            double var2 = Double.MAX_VALUE;
    313            VillageAgressor var4 = null;
    314            Iterator var5 = this.villageAgressors.iterator();
    315
    316            while (var5.hasNext())
    317            {
    318                VillageAgressor var6 = (VillageAgressor)var5.next();
    319                double var7 = var6.agressor.getDistanceSqToEntity(par1EntityLiving);
    320
    321                if (var7 <= var2)
    322                {
    323                    var4 = var6;
    324                    var2 = var7;
    325                }
    326            }
    327
    328            return var4 != null ? var4.agressor : null;
    329        }
    330
    331        public EntityPlayer func_82685_c(EntityLiving par1EntityLiving)
    332        {
    333            double var2 = Double.MAX_VALUE;
    334            EntityPlayer var4 = null;
    335            Iterator var5 = this.field_82693_j.keySet().iterator();
    336
    337            while (var5.hasNext())
    338            {
    339                String var6 = (String)var5.next();
    340
    341                if (this.func_82687_d(var6))
    342                {
    343                    EntityPlayer var7 = this.worldObj.getPlayerEntityByName(var6);
    344
    345                    if (var7 != null)
    346                    {
    347                        double var8 = var7.getDistanceSqToEntity(par1EntityLiving);
    348
    349                        if (var8 <= var2)
    350                        {
    351                            var4 = var7;
    352                            var2 = var8;
    353                        }
    354                    }
    355                }
    356            }
    357
    358            return var4;
    359        }
    360
    361        private void removeDeadAndOldAgressors()
    362        {
    363            Iterator var1 = this.villageAgressors.iterator();
    364
    365            while (var1.hasNext())
    366            {
    367                VillageAgressor var2 = (VillageAgressor)var1.next();
    368
    369                if (!var2.agressor.isEntityAlive() || Math.abs(this.tickCounter - var2.agressionTime) > 300)
    370                {
    371                    var1.remove();
    372                }
    373            }
    374        }
    375
    376        private void removeDeadAndOutOfRangeDoors()
    377        {
    378            boolean var1 = false;
    379            boolean var2 = this.worldObj.rand.nextInt(50) == 0;
    380            Iterator var3 = this.villageDoorInfoList.iterator();
    381
    382            while (var3.hasNext())
    383            {
    384                VillageDoorInfo var4 = (VillageDoorInfo)var3.next();
    385
    386                if (var2)
    387                {
    388                    var4.resetDoorOpeningRestrictionCounter();
    389                }
    390
    391                if (!this.isBlockDoor(var4.posX, var4.posY, var4.posZ) || Math.abs(this.tickCounter - var4.lastActivityTimestamp) > 1200)
    392                {
    393                    this.centerHelper.posX -= var4.posX;
    394                    this.centerHelper.posY -= var4.posY;
    395                    this.centerHelper.posZ -= var4.posZ;
    396                    var1 = true;
    397                    var4.isDetachedFromVillageFlag = true;
    398                    var3.remove();
    399                }
    400            }
    401
    402            if (var1)
    403            {
    404                this.updateVillageRadiusAndCenter();
    405            }
    406        }
    407
    408        private boolean isBlockDoor(int par1, int par2, int par3)
    409        {
    410            int var4 = this.worldObj.getBlockId(par1, par2, par3);
    411            return var4 <= 0 ? false : var4 == Block.doorWood.blockID;
    412        }
    413
    414        private void updateVillageRadiusAndCenter()
    415        {
    416            int var1 = this.villageDoorInfoList.size();
    417
    418            if (var1 == 0)
    419            {
    420                this.center.set(0, 0, 0);
    421                this.villageRadius = 0;
    422            }
    423            else
    424            {
    425                this.center.set(this.centerHelper.posX / var1, this.centerHelper.posY / var1, this.centerHelper.posZ / var1);
    426                int var2 = 0;
    427                VillageDoorInfo var4;
    428
    429                for (Iterator var3 = this.villageDoorInfoList.iterator(); var3.hasNext(); var2 = Math.max(var4.getDistanceSquared(this.center.posX, this.center.posY, this.center.posZ), var2))
    430                {
    431                    var4 = (VillageDoorInfo)var3.next();
    432                }
    433
    434                this.villageRadius = Math.max(32, (int)Math.sqrt((double)var2) + 1);
    435            }
    436        }
    437
    438        public int func_82684_a(String par1Str)
    439        {
    440            Integer var2 = (Integer)this.field_82693_j.get(par1Str);
    441            return var2 != null ? var2.intValue() : 0;
    442        }
    443
    444        public int func_82688_a(String par1Str, int par2)
    445        {
    446            int var3 = this.func_82684_a(par1Str);
    447            int var4 = MathHelper.clamp_int(var3 + par2, -30, 10);
    448            this.field_82693_j.put(par1Str, Integer.valueOf(var4));
    449            return var4;
    450        }
    451
    452        public boolean func_82687_d(String par1Str)
    453        {
    454            return this.func_82684_a(par1Str) <= -15;
    455        }
    456
    457        public void func_82690_a(NBTTagCompound par1NBTTagCompound)
    458        {
    459            this.numVillagers = par1NBTTagCompound.getInteger("PopSize");
    460            this.villageRadius = par1NBTTagCompound.getInteger("Radius");
    461            this.numIronGolems = par1NBTTagCompound.getInteger("Golems");
    462            this.lastAddDoorTimestamp = par1NBTTagCompound.getInteger("Stable");
    463            this.tickCounter = par1NBTTagCompound.getInteger("Tick");
    464            this.field_82694_i = par1NBTTagCompound.getInteger("MTick");
    465            this.center.posX = par1NBTTagCompound.getInteger("CX");
    466            this.center.posY = par1NBTTagCompound.getInteger("CY");
    467            this.center.posZ = par1NBTTagCompound.getInteger("CZ");
    468            this.centerHelper.posX = par1NBTTagCompound.getInteger("ACX");
    469            this.centerHelper.posY = par1NBTTagCompound.getInteger("ACY");
    470            this.centerHelper.posZ = par1NBTTagCompound.getInteger("ACZ");
    471            NBTTagList var2 = par1NBTTagCompound.getTagList("Doors");
    472
    473            for (int var3 = 0; var3 < var2.tagCount(); ++var3)
    474            {
    475                NBTTagCompound var4 = (NBTTagCompound)var2.tagAt(var3);
    476                VillageDoorInfo var5 = new VillageDoorInfo(var4.getInteger("X"), var4.getInteger("Y"), var4.getInteger("Z"), var4.getInteger("IDX"), var4.getInteger("IDZ"), var4.getInteger("TS"));
    477                this.villageDoorInfoList.add(var5);
    478            }
    479
    480            NBTTagList var6 = par1NBTTagCompound.getTagList("Players");
    481
    482            for (int var7 = 0; var7 < var6.tagCount(); ++var7)
    483            {
    484                NBTTagCompound var8 = (NBTTagCompound)var6.tagAt(var7);
    485                this.field_82693_j.put(var8.getString("Name"), Integer.valueOf(var8.getInteger("S")));
    486            }
    487        }
    488
    489        public void func_82689_b(NBTTagCompound par1NBTTagCompound)
    490        {
    491            par1NBTTagCompound.setInteger("PopSize", this.numVillagers);
    492            par1NBTTagCompound.setInteger("Radius", this.villageRadius);
    493            par1NBTTagCompound.setInteger("Golems", this.numIronGolems);
    494            par1NBTTagCompound.setInteger("Stable", this.lastAddDoorTimestamp);
    495            par1NBTTagCompound.setInteger("Tick", this.tickCounter);
    496            par1NBTTagCompound.setInteger("MTick", this.field_82694_i);
    497            par1NBTTagCompound.setInteger("CX", this.center.posX);
    498            par1NBTTagCompound.setInteger("CY", this.center.posY);
    499            par1NBTTagCompound.setInteger("CZ", this.center.posZ);
    500            par1NBTTagCompound.setInteger("ACX", this.centerHelper.posX);
    501            par1NBTTagCompound.setInteger("ACY", this.centerHelper.posY);
    502            par1NBTTagCompound.setInteger("ACZ", this.centerHelper.posZ);
    503            NBTTagList var2 = new NBTTagList("Doors");
    504            Iterator var3 = this.villageDoorInfoList.iterator();
    505
    506            while (var3.hasNext())
    507            {
    508                VillageDoorInfo var4 = (VillageDoorInfo)var3.next();
    509                NBTTagCompound var5 = new NBTTagCompound("Door");
    510                var5.setInteger("X", var4.posX);
    511                var5.setInteger("Y", var4.posY);
    512                var5.setInteger("Z", var4.posZ);
    513                var5.setInteger("IDX", var4.insideDirectionX);
    514                var5.setInteger("IDZ", var4.insideDirectionZ);
    515                var5.setInteger("TS", var4.lastActivityTimestamp);
    516                var2.appendTag(var5);
    517            }
    518
    519            par1NBTTagCompound.setTag("Doors", var2);
    520            NBTTagList var7 = new NBTTagList("Players");
    521            Iterator var8 = this.field_82693_j.keySet().iterator();
    522
    523            while (var8.hasNext())
    524            {
    525                String var9 = (String)var8.next();
    526                NBTTagCompound var6 = new NBTTagCompound(var9);
    527                var6.setString("Name", var9);
    528                var6.setInteger("S", ((Integer)this.field_82693_j.get(var9)).intValue());
    529                var7.appendTag(var6);
    530            }
    531
    532            par1NBTTagCompound.setTag("Players", var7);
    533        }
    534
    535        public void func_82692_h()
    536        {
    537            this.field_82694_i = this.tickCounter;
    538        }
    539
    540        public boolean func_82686_i()
    541        {
    542            return this.field_82694_i == 0 || this.tickCounter - this.field_82694_i >= 3600;
    543        }
    544
    545        public void func_82683_b(int par1)
    546        {
    547            Iterator var2 = this.field_82693_j.keySet().iterator();
    548
    549            while (var2.hasNext())
    550            {
    551                String var3 = (String)var2.next();
    552                this.func_82688_a(var3, par1);
    553            }
    554        }
    555    }