001package net.minecraft.block; 002 003import cpw.mods.fml.relauncher.Side; 004import cpw.mods.fml.relauncher.SideOnly; 005import java.util.Random; 006import net.minecraft.block.material.Material; 007import net.minecraft.client.renderer.texture.IconRegister; 008import net.minecraft.entity.EntityLiving; 009import net.minecraft.item.ItemStack; 010import net.minecraft.util.Direction; 011import net.minecraft.util.Icon; 012import net.minecraft.util.MathHelper; 013import net.minecraft.world.IBlockAccess; 014import net.minecraft.world.World; 015 016public abstract class BlockRedstoneLogic extends BlockDirectional 017{ 018 /** Tells whether the repeater is powered or not */ 019 protected final boolean isRepeaterPowered; 020 021 protected BlockRedstoneLogic(int par1, boolean par2) 022 { 023 super(par1, Material.circuits); 024 this.isRepeaterPowered = par2; 025 this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); 026 } 027 028 /** 029 * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc) 030 */ 031 public boolean renderAsNormalBlock() 032 { 033 return false; 034 } 035 036 /** 037 * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z 038 */ 039 public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4) 040 { 041 return !par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) ? false : super.canPlaceBlockAt(par1World, par2, par3, par4); 042 } 043 044 /** 045 * Can this block stay at this position. Similar to canPlaceBlockAt except gets checked often with plants. 046 */ 047 public boolean canBlockStay(World par1World, int par2, int par3, int par4) 048 { 049 return !par1World.doesBlockHaveSolidTopSurface(par2, par3 - 1, par4) ? false : super.canBlockStay(par1World, par2, par3, par4); 050 } 051 052 /** 053 * Ticks the block if it's been scheduled 054 */ 055 public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random) 056 { 057 int l = par1World.getBlockMetadata(par2, par3, par4); 058 059 if (!this.func_94476_e(par1World, par2, par3, par4, l)) 060 { 061 boolean flag = this.func_94478_d(par1World, par2, par3, par4, l); 062 063 if (this.isRepeaterPowered && !flag) 064 { 065 par1World.setBlock(par2, par3, par4, this.func_94484_i().blockID, l, 2); 066 } 067 else if (!this.isRepeaterPowered) 068 { 069 par1World.setBlock(par2, par3, par4, this.func_94485_e().blockID, l, 2); 070 071 if (!flag) 072 { 073 par1World.func_82740_a(par2, par3, par4, this.func_94485_e().blockID, this.func_94486_g(l), -1); 074 } 075 } 076 } 077 } 078 079 @SideOnly(Side.CLIENT) 080 081 /** 082 * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata 083 */ 084 public Icon getIcon(int par1, int par2) 085 { 086 return par1 == 0 ? (this.isRepeaterPowered ? Block.torchRedstoneActive.getBlockTextureFromSide(par1) : Block.torchRedstoneIdle.getBlockTextureFromSide(par1)) : (par1 == 1 ? this.blockIcon : Block.stoneDoubleSlab.getBlockTextureFromSide(1)); 087 } 088 089 @SideOnly(Side.CLIENT) 090 091 /** 092 * When this method is called, your block should register all the icons it needs with the given IconRegister. This 093 * is the only chance you get to register icons. 094 */ 095 public void registerIcons(IconRegister par1IconRegister) 096 { 097 this.blockIcon = par1IconRegister.registerIcon(this.isRepeaterPowered ? "repeater_lit" : "repeater"); 098 } 099 100 @SideOnly(Side.CLIENT) 101 102 /** 103 * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given 104 * coordinates. Args: blockAccess, x, y, z, side 105 */ 106 public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 107 { 108 return par5 != 0 && par5 != 1; 109 } 110 111 /** 112 * The type of render function that is called for this block 113 */ 114 public int getRenderType() 115 { 116 return 36; 117 } 118 119 protected boolean func_96470_c(int par1) 120 { 121 return this.isRepeaterPowered; 122 } 123 124 /** 125 * Returns true if the block is emitting direct/strong redstone power on the specified side. Args: World, X, Y, Z, 126 * side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block. 127 */ 128 public int isProvidingStrongPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 129 { 130 return this.isProvidingWeakPower(par1IBlockAccess, par2, par3, par4, par5); 131 } 132 133 /** 134 * Returns true if the block is emitting indirect/weak redstone power on the specified side. If isBlockNormalCube 135 * returns true, standard redstone propagation rules will apply instead and this will not be called. Args: World, X, 136 * Y, Z, side. Note that the side is reversed - eg it is 1 (up) when checking the bottom of the block. 137 */ 138 public int isProvidingWeakPower(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 139 { 140 int i1 = par1IBlockAccess.getBlockMetadata(par2, par3, par4); 141 142 if (!this.func_96470_c(i1)) 143 { 144 return 0; 145 } 146 else 147 { 148 int j1 = getDirection(i1); 149 return j1 == 0 && par5 == 3 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : (j1 == 1 && par5 == 4 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : (j1 == 2 && par5 == 2 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : (j1 == 3 && par5 == 5 ? this.func_94480_d(par1IBlockAccess, par2, par3, par4, i1) : 0))); 150 } 151 } 152 153 /** 154 * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are 155 * their own) Args: x, y, z, neighbor blockID 156 */ 157 public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5) 158 { 159 if (!this.canBlockStay(par1World, par2, par3, par4)) 160 { 161 this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0); 162 par1World.setBlockToAir(par2, par3, par4); 163 par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID); 164 par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID); 165 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID); 166 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID); 167 par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID); 168 par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID); 169 } 170 else 171 { 172 this.func_94479_f(par1World, par2, par3, par4, par5); 173 } 174 } 175 176 protected void func_94479_f(World par1World, int par2, int par3, int par4, int par5) 177 { 178 int i1 = par1World.getBlockMetadata(par2, par3, par4); 179 180 if (!this.func_94476_e(par1World, par2, par3, par4, i1)) 181 { 182 boolean flag = this.func_94478_d(par1World, par2, par3, par4, i1); 183 184 if ((this.isRepeaterPowered && !flag || !this.isRepeaterPowered && flag) && !par1World.isBlockTickScheduled(par2, par3, par4, this.blockID)) 185 { 186 byte b0 = -1; 187 188 if (this.func_83011_d(par1World, par2, par3, par4, i1)) 189 { 190 b0 = -3; 191 } 192 else if (this.isRepeaterPowered) 193 { 194 b0 = -2; 195 } 196 197 par1World.func_82740_a(par2, par3, par4, this.blockID, this.func_94481_j_(i1), b0); 198 } 199 } 200 } 201 202 public boolean func_94476_e(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 203 { 204 return false; 205 } 206 207 protected boolean func_94478_d(World par1World, int par2, int par3, int par4, int par5) 208 { 209 return this.getInputStrength(par1World, par2, par3, par4, par5) > 0; 210 } 211 212 /** 213 * Returns the signal strength at one input of the block. Args: world, X, Y, Z, side 214 */ 215 protected int getInputStrength(World par1World, int par2, int par3, int par4, int par5) 216 { 217 int i1 = getDirection(par5); 218 int j1 = par2 + Direction.offsetX[i1]; 219 int k1 = par4 + Direction.offsetZ[i1]; 220 int l1 = par1World.getIndirectPowerLevelTo(j1, par3, k1, Direction.directionToFacing[i1]); 221 return l1 >= 15 ? l1 : Math.max(l1, par1World.getBlockId(j1, par3, k1) == Block.redstoneWire.blockID ? par1World.getBlockMetadata(j1, par3, k1) : 0); 222 } 223 224 protected int func_94482_f(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 225 { 226 int i1 = getDirection(par5); 227 228 switch (i1) 229 { 230 case 0: 231 case 2: 232 return Math.max(this.func_94488_g(par1IBlockAccess, par2 - 1, par3, par4, 4), this.func_94488_g(par1IBlockAccess, par2 + 1, par3, par4, 5)); 233 case 1: 234 case 3: 235 return Math.max(this.func_94488_g(par1IBlockAccess, par2, par3, par4 + 1, 3), this.func_94488_g(par1IBlockAccess, par2, par3, par4 - 1, 2)); 236 default: 237 return 0; 238 } 239 } 240 241 protected int func_94488_g(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 242 { 243 int i1 = par1IBlockAccess.getBlockId(par2, par3, par4); 244 return this.func_94477_d(i1) ? (i1 == Block.redstoneWire.blockID ? par1IBlockAccess.getBlockMetadata(par2, par3, par4) : par1IBlockAccess.isBlockProvidingPowerTo(par2, par3, par4, par5)) : 0; 245 } 246 247 /** 248 * Can this block provide power. Only wire currently seems to have this change based on its state. 249 */ 250 public boolean canProvidePower() 251 { 252 return true; 253 } 254 255 /** 256 * Called when the block is placed in the world. 257 */ 258 public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving, ItemStack par6ItemStack) 259 { 260 int l = ((MathHelper.floor_double((double)(par5EntityLiving.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3) + 2) % 4; 261 par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 3); 262 boolean flag = this.func_94478_d(par1World, par2, par3, par4, l); 263 264 if (flag) 265 { 266 par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, 1); 267 } 268 } 269 270 /** 271 * Called whenever the block is added into the world. Args: world, x, y, z 272 */ 273 public void onBlockAdded(World par1World, int par2, int par3, int par4) 274 { 275 this.func_94483_i_(par1World, par2, par3, par4); 276 } 277 278 protected void func_94483_i_(World par1World, int par2, int par3, int par4) 279 { 280 int l = getDirection(par1World.getBlockMetadata(par2, par3, par4)); 281 282 if (l == 1) 283 { 284 par1World.notifyBlockOfNeighborChange(par2 + 1, par3, par4, this.blockID); 285 par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID, 4); 286 } 287 288 if (l == 3) 289 { 290 par1World.notifyBlockOfNeighborChange(par2 - 1, par3, par4, this.blockID); 291 par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID, 5); 292 } 293 294 if (l == 2) 295 { 296 par1World.notifyBlockOfNeighborChange(par2, par3, par4 + 1, this.blockID); 297 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID, 2); 298 } 299 300 if (l == 0) 301 { 302 par1World.notifyBlockOfNeighborChange(par2, par3, par4 - 1, this.blockID); 303 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID, 3); 304 } 305 } 306 307 /** 308 * Called right before the block is destroyed by a player. Args: world, x, y, z, metaData 309 */ 310 public void onBlockDestroyedByPlayer(World par1World, int par2, int par3, int par4, int par5) 311 { 312 if (this.isRepeaterPowered) 313 { 314 par1World.notifyBlocksOfNeighborChange(par2 + 1, par3, par4, this.blockID); 315 par1World.notifyBlocksOfNeighborChange(par2 - 1, par3, par4, this.blockID); 316 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 + 1, this.blockID); 317 par1World.notifyBlocksOfNeighborChange(par2, par3, par4 - 1, this.blockID); 318 par1World.notifyBlocksOfNeighborChange(par2, par3 - 1, par4, this.blockID); 319 par1World.notifyBlocksOfNeighborChange(par2, par3 + 1, par4, this.blockID); 320 } 321 322 super.onBlockDestroyedByPlayer(par1World, par2, par3, par4, par5); 323 } 324 325 /** 326 * Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two 327 * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block. 328 */ 329 public boolean isOpaqueCube() 330 { 331 return false; 332 } 333 334 protected boolean func_94477_d(int par1) 335 { 336 Block block = Block.blocksList[par1]; 337 return block != null && block.canProvidePower(); 338 } 339 340 protected int func_94480_d(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) 341 { 342 return 15; 343 } 344 345 public static boolean isRedstoneRepeaterBlockID(int par0) 346 { 347 return Block.redstoneRepeaterIdle.func_94487_f(par0) || Block.redstoneComparatorIdle.func_94487_f(par0); 348 } 349 350 public boolean func_94487_f(int par1) 351 { 352 return par1 == this.func_94485_e().blockID || par1 == this.func_94484_i().blockID; 353 } 354 355 public boolean func_83011_d(World par1World, int par2, int par3, int par4, int par5) 356 { 357 int i1 = getDirection(par5); 358 359 if (isRedstoneRepeaterBlockID(par1World.getBlockId(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1]))) 360 { 361 int j1 = par1World.getBlockMetadata(par2 - Direction.offsetX[i1], par3, par4 - Direction.offsetZ[i1]); 362 int k1 = getDirection(j1); 363 return k1 != i1; 364 } 365 else 366 { 367 return false; 368 } 369 } 370 371 protected int func_94486_g(int par1) 372 { 373 return this.func_94481_j_(par1); 374 } 375 376 protected abstract int func_94481_j_(int i); 377 378 protected abstract BlockRedstoneLogic func_94485_e(); 379 380 protected abstract BlockRedstoneLogic func_94484_i(); 381 382 /** 383 * Returns true if the given block ID is equivalent to this one. Example: redstoneTorchOn matches itself and 384 * redstoneTorchOff, and vice versa. Most blocks only match themselves. 385 */ 386 public boolean isAssociatedBlockID(int par1) 387 { 388 return this.func_94487_f(par1); 389 } 390}