the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 661 lines 20 kB view raw
1#include "stdafx.h" 2#include "PistonBaseTile.h" 3#include "PistonMovingPiece.h" 4#include "PistonPieceEntity.h" 5#include "PistonExtensionTile.h" 6#include "Facing.h" 7#include "net.minecraft.world.level.h" 8#include "..\Minecraft.Client\Minecraft.h" 9#include "..\Minecraft.Client\MultiPlayerLevel.h" 10#include "net.minecraft.world.h" 11#include "LevelChunk.h" 12#include "Dimension.h" 13 14const wstring PistonBaseTile::EDGE_TEX = L"piston_side"; 15const wstring PistonBaseTile::PLATFORM_TEX = L"piston_top"; 16const wstring PistonBaseTile::PLATFORM_STICKY_TEX = L"piston_top_sticky"; 17const wstring PistonBaseTile::BACK_TEX = L"piston_bottom"; 18const wstring PistonBaseTile::INSIDE_TEX = L"piston_inner_top"; 19 20const float PistonBaseTile::PLATFORM_THICKNESS = 4.0f; 21 22DWORD PistonBaseTile::tlsIdx = TlsAlloc(); 23 24// 4J - NOTE - this ignoreUpdate stuff has been removed from the java version, but I'm not currently sure how the java version does without it... there must be 25// some other mechanism that we don't have that stops the event from one piston being processed, from causing neighbours to have extra events created for them. 26// For us, that means that if we create a piston next to another one, then one of them gets two events to createPush, the second of which fails, leaving the 27// piston in a bad (simultaneously extended & not extended) state. 28// 4J - ignoreUpdate is a static in java, implementing as TLS here to make thread safe 29bool PistonBaseTile::ignoreUpdate() 30{ 31 return (TlsGetValue(tlsIdx) != NULL); 32} 33 34void PistonBaseTile::ignoreUpdate(bool set) 35{ 36 TlsSetValue(tlsIdx,(LPVOID)(set?1:0)); 37} 38 39PistonBaseTile::PistonBaseTile(int id, bool isSticky) : Tile(id, Material::piston, isSolidRender() ) 40{ 41 // 4J - added initialiser 42 ignoreUpdate(false); 43 44 this->isSticky = isSticky; 45 setSoundType(SOUND_STONE); 46 setDestroyTime(0.5f); 47 48 iconInside = NULL; 49 iconBack = NULL; 50 iconPlatform = NULL; 51} 52 53Icon *PistonBaseTile::getPlatformTexture() 54{ 55 return iconPlatform; 56} 57 58void PistonBaseTile::updateShape(float x0, float y0, float z0, float x1, float y1, float z1) 59{ 60 setShape(x0, y0, z0, x1, y1, z1); 61} 62 63Icon *PistonBaseTile::getTexture(int face, int data) 64{ 65 int facing = getFacing(data); 66 67 if (facing > 5) 68 { 69 return iconPlatform; 70 } 71 72 if (face == facing) 73 { 74 // sorry about this mess... 75 // when the piston is extended, either normally 76 // or because a piston arm animation, the top 77 // texture is the furnace bottom 78 ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); 79 if (isExtended(data) || tls->xx0 > 0 || tls->yy0 > 0 || tls->zz0 > 0 || tls->xx1 < 1 || tls->yy1 < 1 || tls->zz1 < 1) 80 { 81 return iconInside; 82 } 83 return iconPlatform; 84 } 85 if (face == Facing::OPPOSITE_FACING[facing]) 86 { 87 return iconBack; 88 } 89 90 return icon; 91} 92 93Icon *PistonBaseTile::getTexture(const wstring &name) 94{ 95 if (name.compare(EDGE_TEX) == 0) return Tile::pistonBase->icon; 96 if (name.compare(PLATFORM_TEX) == 0) return Tile::pistonBase->iconPlatform; 97 if (name.compare(PLATFORM_STICKY_TEX) == 0) return Tile::pistonStickyBase->iconPlatform; 98 if (name.compare(INSIDE_TEX) == 0) return Tile::pistonBase->iconInside; 99 100 return NULL; 101} 102 103//@Override 104void PistonBaseTile::registerIcons(IconRegister *iconRegister) 105{ 106 icon = iconRegister->registerIcon(EDGE_TEX); 107 iconPlatform = iconRegister->registerIcon(isSticky ? PLATFORM_STICKY_TEX : PLATFORM_TEX); 108 iconInside = iconRegister->registerIcon(INSIDE_TEX); 109 iconBack = iconRegister->registerIcon(BACK_TEX); 110} 111 112int PistonBaseTile::getRenderShape() 113{ 114 return SHAPE_PISTON_BASE; 115} 116 117bool PistonBaseTile::isSolidRender(bool isServerLevel) 118{ 119 return false; 120} 121 122bool PistonBaseTile::use(Level *level, int x, int y, int z, shared_ptr<Player> player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param 123{ 124 return false; 125} 126 127void PistonBaseTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr<LivingEntity> by, shared_ptr<ItemInstance> itemInstance) 128{ 129 int targetData = getNewFacing(level, x, y, z, dynamic_pointer_cast<Player>(by) ); 130 level->setData(x, y, z, targetData, Tile::UPDATE_CLIENTS); 131 if (!level->isClientSide && !ignoreUpdate()) 132 { 133 checkIfExtend(level, x, y, z); 134 } 135} 136 137void PistonBaseTile::neighborChanged(Level *level, int x, int y, int z, int type) 138{ 139 if (!level->isClientSide && !ignoreUpdate()) 140 { 141 checkIfExtend(level, x, y, z); 142 } 143} 144 145void PistonBaseTile::onPlace(Level *level, int x, int y, int z) 146{ 147 if (!level->isClientSide && level->getTileEntity(x, y, z) == NULL && !ignoreUpdate()) 148 { 149 checkIfExtend(level, x, y, z); 150 } 151} 152 153void PistonBaseTile::checkIfExtend(Level *level, int x, int y, int z) 154{ 155 int data = level->getData(x, y, z); 156 int facing = getFacing(data); 157 158 if (facing == UNDEFINED_FACING) 159 { 160 return; 161 } 162 bool extend = getNeighborSignal(level, x, y, z, facing); 163 164 if (extend && !isExtended(data)) 165 { 166 if (canPush(level, x, y, z, facing)) 167 { 168 level->tileEvent(x, y, z, id, TRIGGER_EXTEND, facing); 169 } 170 } 171 else if (!extend && isExtended(data)) 172 { 173 level->setData(x, y, z, facing, UPDATE_CLIENTS); 174 level->tileEvent(x, y, z, id, TRIGGER_CONTRACT, facing); 175 } 176} 177 178/** 179* This method checks neighbor signals for this block and the block above, 180* and directly beneath. However, it avoids checking blocks that would be 181* pushed by this block. 182* 183* @param level 184* @param x 185* @param y 186* @param z 187* @return 188*/ 189bool PistonBaseTile::getNeighborSignal(Level *level, int x, int y, int z, int facing) 190{ 191 // check adjacent neighbors, but not in push direction 192 if (facing != Facing::DOWN && level->hasSignal(x, y - 1, z, Facing::DOWN)) return true; 193 if (facing != Facing::UP && level->hasSignal(x, y + 1, z, Facing::UP)) return true; 194 if (facing != Facing::NORTH && level->hasSignal(x, y, z - 1, Facing::NORTH)) return true; 195 if (facing != Facing::SOUTH && level->hasSignal(x, y, z + 1, Facing::SOUTH)) return true; 196 if (facing != Facing::EAST && level->hasSignal(x + 1, y, z, Facing::EAST)) return true; 197 if (facing != Facing::WEST && level->hasSignal(x - 1, y, z, Facing::WEST)) return true; 198 199 // check signals above 200 if (level->hasSignal(x, y, z, 0)) return true; 201 if (level->hasSignal(x, y + 2, z, 1)) return true; 202 if (level->hasSignal(x, y + 1, z - 1, 2)) return true; 203 if (level->hasSignal(x, y + 1, z + 1, 3)) return true; 204 if (level->hasSignal(x - 1, y + 1, z, 4)) return true; 205 if (level->hasSignal(x + 1, y + 1, z, 5)) return true; 206 207 return false; 208} 209 210bool PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, int facing) 211{ 212 ignoreUpdate(true); 213 214 if (!level->isClientSide) 215 { 216 bool extend = getNeighborSignal(level, x, y, z, facing); 217 218 if (extend && param1 == TRIGGER_CONTRACT) 219 { 220 level->setData(x, y, z, facing | EXTENDED_BIT, UPDATE_CLIENTS); 221 return false; 222 } 223 else if (!extend && param1 == TRIGGER_EXTEND) 224 { 225 return false; 226 } 227 } 228 229 if (param1 == TRIGGER_EXTEND) 230 { 231 PIXBeginNamedEvent(0,"Create push\n"); 232 if (createPush(level, x, y, z, facing)) 233 { 234 // 4J - it is (currently) critical that this setData sends data to the client, so have added a bool to the method so that it sends data even if the data was already set to the same value 235 // as before, which was actually its behaviour until a change in 1.0.1 meant that setData only conditionally sent updates to listeners. If the data update Isn't sent, then what 236 // can happen is: 237 // (1) the host sends the tile event to the client 238 // (2) the client gets the tile event, and sets the tile/data value locally. 239 // (3) just before setting the tile/data locally, the client will put the old value in the vector of things to be restored should an update not be received back from the host 240 // (4) we don't get any update of the tile from the host, and so the old value gets restored on the client 241 // (5) the piston base ends up being restored to its retracted state whilst the piston arm is extended 242 // We really need to spend some time investigating a better way for pistons to work as it all seems a bit scary how the host/client interact, but forcing this to send should at least 243 // restore the behaviour of the pistons to something closer to what they were before the 1.0.1 update. By sending this data update, then (4) in the list above doesn't happen 244 // because the client does actually receive an update for this tile from the host after the event has been processed on the cient. 245 level->setData(x, y, z, facing | EXTENDED_BIT, Tile::UPDATE_CLIENTS, true); 246 level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_OUT, 0.5f, level->random->nextFloat() * 0.25f + 0.6f); 247 } 248 else 249 { 250 return false; 251 } 252 PIXEndNamedEvent(); 253 } 254 else if (param1 == TRIGGER_CONTRACT) 255 { 256 PIXBeginNamedEvent(0,"Contract phase A\n"); 257 shared_ptr<TileEntity> prevTileEntity = level->getTileEntity(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); 258 if (prevTileEntity != NULL && dynamic_pointer_cast<PistonPieceEntity>(prevTileEntity) != NULL) 259 { 260 dynamic_pointer_cast<PistonPieceEntity>(prevTileEntity)->finalTick(); 261 } 262 263 stopSharingIfServer(level, x, y, z); // 4J added 264 level->setTileAndData(x, y, z, Tile::pistonMovingPiece_Id, facing, Tile::UPDATE_ALL); 265 level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(id, facing, facing, false, true)); 266 267 PIXEndNamedEvent(); 268 269 // sticky movement 270 if (isSticky) 271 { 272 PIXBeginNamedEvent(0,"Contract sticky phase A\n"); 273 int twoX = x + Facing::STEP_X[facing] * 2; 274 int twoY = y + Facing::STEP_Y[facing] * 2; 275 int twoZ = z + Facing::STEP_Z[facing] * 2; 276 int block = level->getTile(twoX, twoY, twoZ); 277 int blockData = level->getData(twoX, twoY, twoZ); 278 bool pistonPiece = false; 279 280 PIXEndNamedEvent(); 281 282 if (block == Tile::pistonMovingPiece_Id) 283 { 284 PIXBeginNamedEvent(0,"Contract sticky phase B\n"); 285 // the block two steps away is a moving piston block piece, so replace it with the real data, 286 // since it's probably this piston which is changing too fast 287 shared_ptr<TileEntity> tileEntity = level->getTileEntity(twoX, twoY, twoZ); 288 if (tileEntity != NULL && dynamic_pointer_cast<PistonPieceEntity>(tileEntity) != NULL ) 289 { 290 shared_ptr<PistonPieceEntity> ppe = dynamic_pointer_cast<PistonPieceEntity>(tileEntity); 291 292 if (ppe->getFacing() == facing && ppe->isExtending()) 293 { 294 // force the tile to air before pushing 295 ppe->finalTick(); 296 block = ppe->getId(); 297 blockData = ppe->getData(); 298 pistonPiece = true; 299 } 300 } 301 PIXEndNamedEvent(); 302 } 303 304 PIXBeginNamedEvent(0,"Contract sticky phase C\n"); 305 if (!pistonPiece && block > 0 && (isPushable(block, level, twoX, twoY, twoZ, false)) 306 && (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_NORMAL || block == Tile::pistonBase_Id || block == Tile::pistonStickyBase_Id)) 307 { 308 stopSharingIfServer(level, twoX, twoY, twoZ); // 4J added 309 310 x += Facing::STEP_X[facing]; 311 y += Facing::STEP_Y[facing]; 312 z += Facing::STEP_Z[facing]; 313 314 level->setTileAndData(x, y, z, Tile::pistonMovingPiece_Id, blockData, Tile::UPDATE_ALL); 315 level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(block, blockData, facing, false, false)); 316 317 ignoreUpdate(false); 318 level->removeTile(twoX, twoY, twoZ); 319 ignoreUpdate(true); 320 } 321 else if (!pistonPiece) 322 { 323 stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added 324 ignoreUpdate(false); 325 level->removeTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); 326 ignoreUpdate(true); 327 } 328 PIXEndNamedEvent(); 329 } 330 else 331 { 332 stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added 333 ignoreUpdate(false); 334 level->removeTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); 335 ignoreUpdate(true); 336 } 337 338 level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_IN, 0.5f, level->random->nextFloat() * 0.15f + 0.6f); 339 } 340 341 ignoreUpdate(false); 342 343 return true; 344} 345 346void PistonBaseTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr<TileEntity> forceEntity) // 4J added forceData, forceEntity param 347{ 348 int data = (forceData == -1 ) ? level->getData(x, y, z) : forceData; 349 350 if (isExtended(data)) 351 { 352 const float thickness = PLATFORM_THICKNESS / 16.0f; 353 switch (getFacing(data)) 354 { 355 case Facing::DOWN: 356 setShape(0, thickness, 0, 1, 1, 1); 357 break; 358 case Facing::UP: 359 setShape(0, 0, 0, 1, 1 - thickness, 1); 360 break; 361 case Facing::NORTH: 362 setShape(0, 0, thickness, 1, 1, 1); 363 break; 364 case Facing::SOUTH: 365 setShape(0, 0, 0, 1, 1, 1 - thickness); 366 break; 367 case Facing::WEST: 368 setShape(thickness, 0, 0, 1, 1, 1); 369 break; 370 case Facing::EAST: 371 setShape(0, 0, 0, 1 - thickness, 1, 1); 372 break; 373 } 374 } 375 else 376 { 377 setShape(0, 0, 0, 1, 1, 1); 378 } 379} 380 381void PistonBaseTile::updateDefaultShape() 382{ 383 setShape(0, 0, 0, 1, 1, 1); 384} 385 386void PistonBaseTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr<Entity> source) 387{ 388 setShape(0, 0, 0, 1, 1, 1); 389 Tile::addAABBs(level, x, y, z, box, boxes, source); 390} 391 392AABB *PistonBaseTile::getAABB(Level *level, int x, int y, int z) 393{ 394 updateShape(level, x, y, z); 395 return Tile::getAABB(level, x, y, z); 396} 397 398bool PistonBaseTile::isCubeShaped() 399{ 400 return false; 401} 402 403int PistonBaseTile::getFacing(int data) 404{ 405 return data & 0x7; 406} 407 408bool PistonBaseTile::isExtended(int data) 409{ 410 return (data & EXTENDED_BIT) != 0; 411} 412 413int PistonBaseTile::getNewFacing(Level *level, int x, int y, int z, shared_ptr<LivingEntity> player) 414{ 415 if (Mth::abs((float) player->x - x) < 2 && Mth::abs((float) player->z - z) < 2) 416 { 417 // If the player is above the block, the slot is on the top 418 double py = player->y + 1.82 - player->heightOffset; 419 if (py - y > 2) 420 { 421 return Facing::UP; 422 } 423 // If the player is below the block, the slot is on the bottom 424 if (y - py > 0) 425 { 426 return Facing::DOWN; 427 } 428 } 429 // The slot is on the side 430 int i = Mth::floor(player->yRot * 4.0f / 360.0f + 0.5) & 0x3; 431 if (i == 0) return Facing::NORTH; 432 if (i == 1) return Facing::EAST; 433 if (i == 2) return Facing::SOUTH; 434 if (i == 3) return Facing::WEST; 435 return 0; 436} 437 438bool PistonBaseTile::isPushable(int block, Level *level, int cx, int cy, int cz, bool allowDestroyable) 439{ 440 // special case for obsidian 441 if (block == Tile::obsidian_Id) 442 { 443 return false; 444 } 445 446 if (block == Tile::pistonBase_Id || block == Tile::pistonStickyBase_Id) 447 { 448 // special case for piston bases 449 if (isExtended(level->getData(cx, cy, cz))) 450 { 451 return false; 452 } 453 } 454 else 455 { 456 if (Tile::tiles[block]->getDestroySpeed(level, cx, cy, cz) == Tile::INDESTRUCTIBLE_DESTROY_TIME) 457 { 458 return false; 459 } 460 461 if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_BLOCK) 462 { 463 return false; 464 } 465 466 if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) 467 { 468 if(!allowDestroyable) 469 { 470 return false; 471 } 472 return true; 473 } 474 } 475 476 if( Tile::tiles[block]->isEntityTile() ) // 4J - java uses instanceof EntityTile here 477 { 478 // may not push tile entities 479 return false; 480 } 481 482 return true; 483} 484 485bool PistonBaseTile::canPush(Level *level, int sx, int sy, int sz, int facing) 486{ 487 int cx = sx + Facing::STEP_X[facing]; 488 int cy = sy + Facing::STEP_Y[facing]; 489 int cz = sz + Facing::STEP_Z[facing]; 490 491 for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) 492 { 493 494 if (cy <= 0 || cy >= (Level::maxBuildHeight - 1)) 495 { 496 // out of bounds 497 return false; 498 } 499 500 // 4J - added to also check for out of bounds in x/z for our finite world 501 int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; 502 int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; 503 if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) ) 504 { 505 return false; 506 } 507 int block = level->getTile(cx, cy, cz); 508 if (block == 0) 509 { 510 break; 511 } 512 513 if (!isPushable(block, level, cx, cy, cz, true)) 514 { 515 return false; 516 } 517 518 if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) 519 { 520 break; 521 } 522 523 if (i == MAX_PUSH_DEPTH) 524 { 525 // we've reached the maximum push depth 526 // without finding air or a breakable block 527 return false; 528 } 529 530 cx += Facing::STEP_X[facing]; 531 cy += Facing::STEP_Y[facing]; 532 cz += Facing::STEP_Z[facing]; 533 } 534 535 return true; 536 537} 538 539void PistonBaseTile::stopSharingIfServer(Level *level, int x, int y, int z) 540{ 541 if( !level->isClientSide ) 542 { 543 MultiPlayerLevel *clientLevel = Minecraft::GetInstance()->getLevel(level->dimension->id); 544 if( clientLevel ) 545 { 546 LevelChunk *lc = clientLevel->getChunkAt( x, z ); 547 lc->stopSharingTilesAndData(); 548 } 549 } 550} 551 552bool PistonBaseTile::createPush(Level *level, int sx, int sy, int sz, int facing) 553{ 554 int cx = sx + Facing::STEP_X[facing]; 555 int cy = sy + Facing::STEP_Y[facing]; 556 int cz = sz + Facing::STEP_Z[facing]; 557 558 for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) 559 { 560 if (cy <= 0 || cy >= (Level::maxBuildHeight - 1)) 561 { 562 // out of bounds 563 return false; 564 } 565 566 // 4J - added to also check for out of bounds in x/z for our finite world 567 int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; 568 int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; 569 if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) ) 570 { 571 return false; 572 } 573 574 int block = level->getTile(cx, cy, cz); 575 if (block == 0) 576 { 577 break; 578 } 579 580 if (!isPushable(block, level, cx, cy, cz, true)) 581 { 582 return false; 583 } 584 585 if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) 586 { 587 // this block is destroyed when pushed 588 Tile::tiles[block]->spawnResources(level, cx, cy, cz, level->getData(cx, cy, cz), 0); 589 // setting the tile to air is actually superflous, but helps vs multiplayer problems 590 stopSharingIfServer(level, cx, cy, cz); // 4J added 591 level->removeTile(cx, cy, cz); 592 break; 593 } 594 595 if (i == MAX_PUSH_DEPTH) 596 { 597 // we've reached the maximum push depth without finding air or a breakable block 598 return false; 599 } 600 601 cx += Facing::STEP_X[facing]; 602 cy += Facing::STEP_Y[facing]; 603 cz += Facing::STEP_Z[facing]; 604 } 605 606 int ex = cx; 607 int ey = cy; 608 int ez = cz; 609 int count = 0; 610 int tiles[MAX_PUSH_DEPTH + 1]; 611 612 while (cx != sx || cy != sy || cz != sz) 613 { 614 615 int nx = cx - Facing::STEP_X[facing]; 616 int ny = cy - Facing::STEP_Y[facing]; 617 int nz = cz - Facing::STEP_Z[facing]; 618 619 int block = level->getTile(nx, ny, nz); 620 int data = level->getData(nx, ny, nz); 621 622 stopSharingIfServer(level, cx, cy, cz); // 4J added 623 624 if (block == id && nx == sx && ny == sy && nz == sz) 625 { 626 level->setTileAndData(cx, cy, cz, Tile::pistonMovingPiece_Id, facing | (isSticky ? PistonExtensionTile::STICKY_BIT : 0), Tile::UPDATE_NONE); 627 level->setTileEntity(cx, cy, cz, PistonMovingPiece::newMovingPieceEntity(Tile::pistonExtensionPiece_Id, facing | (isSticky ? PistonExtensionTile::STICKY_BIT : 0), facing, true, false)); 628 } 629 else 630 { 631 level->setTileAndData(cx, cy, cz, Tile::pistonMovingPiece_Id, data, Tile::UPDATE_NONE); 632 level->setTileEntity(cx, cy, cz, PistonMovingPiece::newMovingPieceEntity(block, data, facing, true, false)); 633 } 634 tiles[count++] = block; 635 636 cx = nx; 637 cy = ny; 638 cz = nz; 639 } 640 641 cx = ex; 642 cy = ey; 643 cz = ez; 644 count = 0; 645 646 while (cx != sx || cy != sy || cz != sz) 647 { 648 int nx = cx - Facing::STEP_X[facing]; 649 int ny = cy - Facing::STEP_Y[facing]; 650 int nz = cz - Facing::STEP_Z[facing]; 651 652 level->updateNeighborsAt(nx, ny, nz, tiles[count++]); 653 654 cx = nx; 655 cy = ny; 656 cz = nz; 657 } 658 659 return true; 660 661}