the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1042 lines 22 kB view raw
1#include "stdafx.h" 2#include "net.minecraft.world.level.dimension.h" 3#include "net.minecraft.world.level.h" 4#include "net.minecraft.world.level.tile.h" 5#include "net.minecraft.world.phys.h" 6#include "net.minecraft.world.entity.h" 7#include "net.minecraft.world.entity.item.h" 8#include "net.minecraft.world.entity.player.h" 9#include "net.minecraft.world.entity.animal.h" 10#include "net.minecraft.world.item.h" 11#include "net.minecraft.world.damagesource.h" 12#include "..\Minecraft.Client\MinecraftServer.h" 13#include "..\Minecraft.Client\ServerLevel.h" 14#include "com.mojang.nbt.h" 15#include "Minecart.h" 16#include "SharedConstants.h" 17 18 19 20const int Minecart::EXITS[][2][3] = { // 21 // 22 {{+0, +0, -1}, {+0, +0, +1}}, // 0 23 {{-1, +0, +0}, {+1, +0, +0}}, // 1 24 {{-1, -1, +0}, {+1, +0, +0}}, // 2 25 {{-1, +0, +0}, {+1, -1, +0}}, // 3 26 {{+0, +0, -1}, {+0, -1, +1}}, // 4 27 {{+0, -1, -1}, {+0, +0, +1}}, // 5 28 29 {{+0, +0, +1}, {+1, +0, +0}}, // 6 30 {{+0, +0, +1}, {-1, +0, +0}}, // 7 31 {{+0, +0, -1}, {-1, +0, +0}}, // 8 32 {{+0, +0, -1}, {+1, +0, +0}}, // 9 33}; 34 35void Minecart::_init() 36{ 37 flipped = false; 38 39 lSteps = 0; 40 lx = ly = lz = lyr = lxr = 0.0; 41 lxd = lyd = lzd = 0.0; 42 43 // Java default ctor 44 blocksBuilding = true; 45 setSize(0.98f, 0.7f); 46 heightOffset = bbHeight / 2.0f; 47 soundUpdater = NULL; 48 name = L""; 49 // 50 51 // 4J Added 52 m_bHasPushedCartThisTick = false; 53} 54 55Minecart::Minecart(Level *level) : Entity( level ) 56{ 57 _init(); 58 59 //soundUpdater = level != NULL ? level->makeSoundUpdater(this) : NULL; 60} 61 62Minecart::~Minecart() 63{ 64 delete soundUpdater; 65} 66 67shared_ptr<Minecart> Minecart::createMinecart(Level *level, double x, double y, double z, int type) 68{ 69 switch (type) 70 { 71 case TYPE_CHEST: 72 return shared_ptr<MinecartChest>( new MinecartChest(level, x, y, z) ); 73 case TYPE_FURNACE: 74 return shared_ptr<MinecartFurnace>( new MinecartFurnace(level, x, y, z) ); 75 case TYPE_TNT: 76 return shared_ptr<MinecartTNT>( new MinecartTNT(level, x, y, z) ); 77 case TYPE_SPAWNER: 78 return shared_ptr<MinecartSpawner>( new MinecartSpawner(level, x, y, z) ); 79 case TYPE_HOPPER: 80 return shared_ptr<MinecartHopper>( new MinecartHopper(level, x, y, z) ); 81 default: 82 return shared_ptr<MinecartRideable>( new MinecartRideable(level, x, y, z) ); 83 } 84} 85 86bool Minecart::makeStepSound() 87{ 88 return false; 89} 90 91void Minecart::defineSynchedData() 92{ 93 entityData->define(DATA_ID_HURT, 0); 94 entityData->define(DATA_ID_HURTDIR, 1); 95 entityData->define(DATA_ID_DAMAGE, 0.0f); 96 entityData->define(DATA_ID_DISPLAY_TILE, 0); 97 entityData->define(DATA_ID_DISPLAY_OFFSET, 6); 98 entityData->define(DATA_ID_CUSTOM_DISPLAY, (byte) 0); 99} 100 101 102AABB *Minecart::getCollideAgainstBox(shared_ptr<Entity> entity) 103{ 104 if (entity->isPushable()) 105 { 106 return entity->bb; 107 } 108 return NULL; 109} 110 111AABB *Minecart::getCollideBox() 112{ 113 return NULL; 114} 115 116bool Minecart::isPushable() 117{ 118 return true; 119} 120 121Minecart::Minecart(Level *level, double x, double y, double z) : Entity( level ) 122{ 123 124 _init(); 125 setPos(x, y, z); 126 127 xd = 0; 128 yd = 0; 129 zd = 0; 130 131 xo = x; 132 yo = y; 133 zo = z; 134} 135 136double Minecart::getRideHeight() 137{ 138 return bbHeight * 0.0 - 0.3f; 139} 140 141bool Minecart::hurt(DamageSource *source, float hurtDamage) 142{ 143 if (level->isClientSide || removed) return true; 144 if (isInvulnerable()) return false; 145 146 // 4J-JEV: Fix for #88212, 147 // Untrusted players shouldn't be able to damage minecarts or boats. 148 if (dynamic_cast<EntityDamageSource *>(source) != NULL) 149 { 150 shared_ptr<Entity> attacker = source->getDirectEntity(); 151 152 if ( attacker->instanceof(eTYPE_PLAYER) && !dynamic_pointer_cast<Player>(attacker)->isAllowedToHurtEntity( shared_from_this() )) 153 { 154 return false; 155 } 156 } 157 158 setHurtDir(-getHurtDir()); 159 setHurtTime(10); 160 markHurt(); 161 setDamage(getDamage() + (hurtDamage * 10)); 162 163 // 4J Stu - If someone is riding in this, then it can tick multiple times which causes the damage to 164 // decrease too quickly. So just make the damage a bit higher to start with for similar behaviour 165 // to an unridden one. Only do this change if the riding player is attacking it. 166 if( rider.lock() != NULL && rider.lock() == source->getEntity() ) hurtDamage += 1; 167 168 bool creativePlayer = source->getEntity() != NULL && source->getEntity()->instanceof(eTYPE_PLAYER) && dynamic_pointer_cast<Player>(source->getEntity())->abilities.instabuild; 169 170 if (creativePlayer || getDamage() > 20 * 2) 171 { 172 // 4J HEG - Fixed issue with player falling through the ground on destroying a minecart while riding (issue #160607) 173 if (rider.lock() != NULL) rider.lock()->ride(nullptr); 174 175 if (!creativePlayer || hasCustomName()) 176 { 177 destroy(source); 178 } 179 else 180 { 181 remove(); 182 } 183 } 184 return true; 185} 186 187void Minecart::destroy(DamageSource *source) 188{ 189 remove(); 190 shared_ptr<ItemInstance> item = shared_ptr<ItemInstance>( new ItemInstance(Item::minecart, 1) ); 191 if (!name.empty()) item->setHoverName(name); 192 spawnAtLocation(item, 0); 193} 194 195void Minecart::animateHurt() 196{ 197 setHurtDir(-getHurtDir()); 198 setHurtTime(10); 199 setDamage(getDamage() + (getDamage() * 10)); 200} 201 202bool Minecart::isPickable() 203{ 204 return !removed; 205} 206 207void Minecart::remove() 208{ 209 Entity::remove(); 210 //if (soundUpdater != NULL) soundUpdater->tick(); 211} 212 213void Minecart::tick() 214{ 215 //if (soundUpdater != NULL) soundUpdater->tick(); 216 // 4J - make minecarts (server-side) tick twice, to put things back to how they were when we were accidently ticking them twice 217 for( int i = 0; i < 2; i++ ) 218 { 219 if (getHurtTime() > 0) setHurtTime(getHurtTime() - 1); 220 if (getDamage() > 0) setDamage(getDamage() - 1); 221 if(y < -64) 222 { 223 outOfWorld(); 224 } 225 226 if (!level->isClientSide && dynamic_cast<ServerLevel *>(level) != NULL) 227 { 228 MinecraftServer *server = ((ServerLevel *) level)->getServer(); 229 int waitTime = getPortalWaitTime(); 230 231 if (isInsidePortal) 232 { 233 if (server->isNetherEnabled()) 234 { 235 if (riding == NULL) 236 { 237 if (portalTime++ >= waitTime) 238 { 239 portalTime = waitTime; 240 changingDimensionDelay = getDimensionChangingDelay(); 241 242 int targetDimension; 243 244 if (level->dimension->id == -1) 245 { 246 targetDimension = 0; 247 } 248 else 249 { 250 targetDimension = -1; 251 } 252 253 changeDimension(targetDimension); 254 } 255 } 256 isInsidePortal = false; 257 } 258 } 259 else 260 { 261 if (portalTime > 0) portalTime -= 4; 262 if (portalTime < 0) portalTime = 0; 263 } 264 if (changingDimensionDelay > 0) changingDimensionDelay--; 265 } 266 267 // 4J Stu - Fix for #8284 - Gameplay: Collision: Minecart clips into/ through blocks at the end of the track, prevents player from riding 268 if (level->isClientSide) // && lSteps > 0) 269 { 270 if (lSteps > 0) 271 { 272 double xt = x + (lx - x) / lSteps; 273 double yt = y + (ly - y) / lSteps; 274 double zt = z + (lz - z) / lSteps; 275 276 double yrd = Mth::wrapDegrees(lyr - yRot); 277 278 yRot += (float) ( (yrd) / lSteps ); 279 xRot += (float) ( (lxr - xRot) / lSteps ); 280 281 lSteps--; 282 setPos(xt, yt, zt); 283 setRot(yRot, xRot); 284 } 285 else 286 { 287 setPos(x, y, z); 288 setRot(yRot, xRot); 289 } 290 291 return; // 4J - return here stops the client-side version of this from ticking twice 292 } 293 xo = x; 294 yo = y; 295 zo = z; 296 297 yd -= 0.04f; 298 299 int xt = Mth::floor(x); 300 int yt = Mth::floor(y); 301 int zt = Mth::floor(z); 302 if (BaseRailTile::isRail(level, xt, yt - 1, zt)) 303 { 304 yt--; 305 } 306 307 double max = 0.4; 308 309 double slideSpeed = 1 / 128.0; 310 int tile = level->getTile(xt, yt, zt); 311 if (BaseRailTile::isRail(tile)) 312 { 313 int data = level->getData(xt, yt, zt); 314 moveAlongTrack(xt, yt, zt, max, slideSpeed, tile, data); 315 316 if (tile == Tile::activatorRail_Id) 317 { 318 activateMinecart(xt, yt, zt, (data & BaseRailTile::RAIL_DATA_BIT) != 0); 319 } 320 } 321 else 322 { 323 comeOffTrack(max); 324 } 325 326 checkInsideTiles(); 327 328 xRot = 0; 329 double xDiff = xo - x; 330 double zDiff = zo - z; 331 if (xDiff * xDiff + zDiff * zDiff > 0.001) 332 { 333 yRot = (float) (atan2(zDiff, xDiff) * 180 / PI); 334 if (flipped) yRot += 180; 335 } 336 337 double rotDiff = Mth::wrapDegrees(yRot - yRotO); 338 339 if (rotDiff < -170 || rotDiff >= 170) 340 { 341 yRot += 180; 342 flipped = !flipped; 343 } 344 setRot(yRot, xRot); 345 346 vector<shared_ptr<Entity> > *entities = level->getEntities(shared_from_this(), bb->grow(0.2f, 0, 0.2f)); 347 if (entities != NULL && !entities->empty()) 348 { 349 AUTO_VAR(itEnd, entities->end()); 350 for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) 351 { 352 shared_ptr<Entity> e = (*it); //entities->at(i); 353 if (e != rider.lock() && e->isPushable() && e->instanceof(eTYPE_MINECART)) 354 { 355 shared_ptr<Minecart> cart = dynamic_pointer_cast<Minecart>(e); 356 cart->m_bHasPushedCartThisTick = false; 357 cart->push(shared_from_this()); 358 359 // 4J Added - We should only be pushed by one minecart per tick, the closest one 360 // Fix for #46937 - TU5: Gameplay: Crash/Freeze occurs when a minecart with an animal inside will be forced to despawn 361 if( cart->m_bHasPushedCartThisTick ) break; 362 } 363 } 364 } 365 366 if (rider.lock() != NULL) 367 { 368 if (rider.lock()->removed) 369 { 370 if (rider.lock()->riding == shared_from_this()) 371 { 372 rider.lock()->riding = nullptr; 373 } 374 rider = weak_ptr<Entity>(); 375 } 376 } 377 } 378} 379 380void Minecart::activateMinecart(int xt, int yt, int zt, bool state) 381{ 382} 383 384void Minecart::comeOffTrack(double maxSpeed) 385{ 386 if (xd < -maxSpeed) xd = -maxSpeed; 387 if (xd > +maxSpeed) xd = +maxSpeed; 388 if (zd < -maxSpeed) zd = -maxSpeed; 389 if (zd > +maxSpeed) zd = +maxSpeed; 390 if (onGround) 391 { 392 xd *= 0.5f; 393 yd *= 0.5f; 394 zd *= 0.5f; 395 } 396 move(xd, yd, zd); 397 398 if (!onGround) 399 { 400 xd *= 0.95f; 401 yd *= 0.95f; 402 zd *= 0.95f; 403 } 404} 405 406void Minecart::moveAlongTrack(int xt, int yt, int zt, double maxSpeed, double slideSpeed, int tile, int data) 407{ 408 fallDistance = 0; 409 410 Vec3 *oldPos = getPos(x, y, z); 411 y = yt; 412 413 bool powerTrack = false; 414 bool haltTrack = false; 415 if (tile == Tile::goldenRail_Id) 416 { 417 powerTrack = (data & BaseRailTile::RAIL_DATA_BIT) != 0; 418 haltTrack = !powerTrack; 419 } 420 if (((BaseRailTile *) Tile::tiles[tile])->isUsesDataBit()) 421 { 422 data &= BaseRailTile::RAIL_DIRECTION_MASK; 423 } 424 425 if (data >= 2 && data <= 5) 426 { 427 y = yt + 1; 428 } 429 430 if (data == 2) xd -= slideSpeed; 431 if (data == 3) xd += slideSpeed; 432 if (data == 4) zd += slideSpeed; 433 if (data == 5) zd -= slideSpeed; 434 435 int exits[2][3]; 436 memcpy( exits, EXITS[data], sizeof(int) * 2 * 3); 437 438 double xD = exits[1][0] - exits[0][0]; 439 double zD = exits[1][2] - exits[0][2]; 440 double dd = sqrt(xD * xD + zD * zD); 441 442 double flip = xd * xD + zd * zD; 443 if (flip < 0) 444 { 445 xD = -xD; 446 zD = -zD; 447 } 448 449 double pow = sqrt(xd * xd + zd * zd); 450 if (pow > 2) 451 { 452 pow = 2; 453 } 454 455 xd = pow * xD / dd; 456 zd = pow * zD / dd; 457 458 459 if ( rider.lock() != NULL && rider.lock()->instanceof(eTYPE_LIVINGENTITY) ) 460 { 461 shared_ptr<LivingEntity> living = dynamic_pointer_cast<LivingEntity>(rider.lock()); 462 463 double forward = living->yya; 464 465 if (forward > 0) 466 { 467 double riderXd = -sin(living->yRot * PI / 180); 468 double riderZd = cos(living->yRot * PI / 180); 469 470 double ownDist = xd * xd + zd * zd; 471 472 if (ownDist < 0.01) 473 { 474 xd += riderXd * 0.1; 475 zd += riderZd * 0.1; 476 477 haltTrack = false; 478 } 479 } 480 } 481 482 // on golden rails without power, stop the cart 483 if (haltTrack) 484 { 485 double speedLength = sqrt(xd * xd + zd * zd); 486 if (speedLength < .03) 487 { 488 xd *= 0; 489 yd *= 0; 490 zd *= 0; 491 } 492 else 493 { 494 xd *= 0.5f; 495 yd *= 0; 496 zd *= 0.5f; 497 } 498 } 499 500 double progress = 0; 501 double x0 = xt + 0.5 + exits[0][0] * 0.5; 502 double z0 = zt + 0.5 + exits[0][2] * 0.5; 503 double x1 = xt + 0.5 + exits[1][0] * 0.5; 504 double z1 = zt + 0.5 + exits[1][2] * 0.5; 505 506 xD = x1 - x0; 507 zD = z1 - z0; 508 509 if (xD == 0) 510 { 511 x = xt + 0.5; 512 progress = z - zt; 513 } 514 else if (zD == 0) 515 { 516 z = zt + 0.5; 517 progress = x - xt; 518 } 519 else 520 { 521 522 double xx = x - x0; 523 double zz = z - z0; 524 525 progress = (xx * xD + zz * zD) * 2; 526 } 527 528 x = x0 + xD * progress; 529 z = z0 + zD * progress; 530 531 setPos(x, y + heightOffset, z); 532 533 double xdd = xd; 534 double zdd = zd; 535 if (rider.lock() != NULL) 536 { 537 xdd *= 0.75; 538 zdd *= 0.75; 539 } 540 if (xdd < -maxSpeed) xdd = -maxSpeed; 541 if (xdd > +maxSpeed) xdd = +maxSpeed; 542 if (zdd < -maxSpeed) zdd = -maxSpeed; 543 if (zdd > +maxSpeed) zdd = +maxSpeed; 544 545 move(xdd, 0, zdd); 546 547 if (exits[0][1] != 0 && Mth::floor(x) - xt == exits[0][0] && Mth::floor(z) - zt == exits[0][2]) 548 { 549 setPos(x, y + exits[0][1], z); 550 } 551 else if (exits[1][1] != 0 && Mth::floor(x) - xt == exits[1][0] && Mth::floor(z) - zt == exits[1][2]) 552 { 553 setPos(x, y + exits[1][1], z); 554 } 555 556 applyNaturalSlowdown(); 557 558 Vec3 *newPos = getPos(x, y, z); 559 if (newPos != NULL && oldPos != NULL) 560 { 561 double speed = (oldPos->y - newPos->y) * 0.05; 562 563 pow = sqrt(xd * xd + zd * zd); 564 if (pow > 0) 565 { 566 xd = xd / pow * (pow + speed); 567 zd = zd / pow * (pow + speed); 568 } 569 setPos(x, newPos->y, z); 570 } 571 572 int xn = Mth::floor(x); 573 int zn = Mth::floor(z); 574 if (xn != xt || zn != zt) 575 { 576 pow = sqrt(xd * xd + zd * zd); 577 578 xd = pow * (xn - xt); 579 zd = pow * (zn - zt); 580 } 581 582 // if on golden rail with power, increase speed 583 if (powerTrack) 584 { 585 double speedLength = sqrt(xd * xd + zd * zd); 586 if (speedLength > .01) 587 { 588 double speed = 0.06; 589 xd += xd / speedLength * speed; 590 zd += zd / speedLength * speed; 591 } 592 else 593 { 594 // if the minecart is standing still, accelerate it away from 595 // potential walls 596 if (data == BaseRailTile::DIR_FLAT_X) 597 { 598 if (level->isSolidBlockingTile(xt - 1, yt, zt)) 599 { 600 xd = .02; 601 } 602 else if (level->isSolidBlockingTile(xt + 1, yt, zt)) 603 { 604 xd = -.02; 605 } 606 } 607 else if (data == BaseRailTile::DIR_FLAT_Z) 608 { 609 if (level->isSolidBlockingTile(xt, yt, zt - 1)) 610 { 611 zd = .02; 612 } 613 else if (level->isSolidBlockingTile(xt, yt, zt + 1)) 614 { 615 zd = -.02; 616 } 617 } 618 } 619 } 620} 621 622void Minecart::applyNaturalSlowdown() 623{ 624 if (rider.lock() != NULL) 625 { 626 xd *= 0.997f; 627 yd *= 0; 628 zd *= 0.997f; 629 } 630 else 631 { 632 xd *= 0.96f; 633 yd *= 0; 634 zd *= 0.96f; 635 } 636} 637 638Vec3 *Minecart::getPosOffs(double x, double y, double z, double offs) 639{ 640 int xt = Mth::floor(x); 641 int yt = Mth::floor(y); 642 int zt = Mth::floor(z); 643 if (BaseRailTile::isRail(level, xt, yt - 1, zt)) 644 { 645 yt--; 646 } 647 648 int tile = level->getTile(xt, yt, zt); 649 if (BaseRailTile::isRail(tile)) 650 { 651 int data = level->getData(xt, yt, zt); 652 653 if (((BaseRailTile *) Tile::tiles[tile])->isUsesDataBit()) 654 { 655 data &= BaseRailTile::RAIL_DIRECTION_MASK; 656 } 657 658 y = yt; 659 if (data >= 2 && data <= 5) 660 { 661 y = yt + 1; 662 } 663 664 // 4J TODO Is this a good way to copy the bit of the array that we need? 665 int exits[2][3]; 666 memcpy( &exits, (void *)EXITS[data], sizeof(int) * 2 * 3); 667 //int exits[2][3] = EXITS[data]; 668 669 double xD = exits[1][0] - exits[0][0]; 670 double zD = exits[1][2] - exits[0][2]; 671 double dd = sqrt(xD * xD + zD * zD); 672 xD /= dd; 673 zD /= dd; 674 675 x += xD * offs; 676 z += zD * offs; 677 678 if (exits[0][1] != 0 && Mth::floor(x) - xt == exits[0][0] && Mth::floor(z) - zt == exits[0][2]) 679 { 680 y += exits[0][1]; 681 } 682 else if (exits[1][1] != 0 && Mth::floor(x) - xt == exits[1][0] && Mth::floor(z) - zt == exits[1][2]) 683 { 684 y += exits[1][1]; 685 } 686 687 return getPos(x, y, z); 688 } 689 return NULL; 690} 691 692Vec3 *Minecart::getPos(double x, double y, double z) 693{ 694 int xt = Mth::floor(x); 695 int yt = Mth::floor(y); 696 int zt = Mth::floor(z); 697 if (BaseRailTile::isRail(level, xt, yt - 1, zt)) 698 { 699 yt--; 700 } 701 702 int tile = level->getTile(xt, yt, zt); 703 if (BaseRailTile::isRail(tile)) 704 { 705 int data = level->getData(xt, yt, zt); 706 y = yt; 707 708 if (((BaseRailTile *) Tile::tiles[tile])->isUsesDataBit()) 709 { 710 data &= BaseRailTile::RAIL_DIRECTION_MASK; 711 } 712 713 if (data >= 2 && data <= 5) 714 { 715 y = yt + 1; 716 } 717 718 // 4J TODO Is this a good way to copy the bit of the array that we need? 719 int exits[2][3]; 720 memcpy( &exits, (void *)EXITS[data], sizeof(int) * 2 * 3); 721 //int exits[2][3] = EXITS[data]; 722 723 double progress = 0; 724 double x0 = xt + 0.5 + exits[0][0] * 0.5; 725 double y0 = yt + 0.5 + exits[0][1] * 0.5; 726 double z0 = zt + 0.5 + exits[0][2] * 0.5; 727 double x1 = xt + 0.5 + exits[1][0] * 0.5; 728 double y1 = yt + 0.5 + exits[1][1] * 0.5; 729 double z1 = zt + 0.5 + exits[1][2] * 0.5; 730 731 double xD = x1 - x0; 732 double yD = (y1 - y0) * 2; 733 double zD = z1 - z0; 734 735 if (xD == 0) 736 { 737 x = xt + 0.5; 738 progress = z - zt; 739 } 740 else if (zD == 0) 741 { 742 z = zt + 0.5; 743 progress = x - xt; 744 } 745 else 746 { 747 748 double xx = x - x0; 749 double zz = z - z0; 750 751 progress = (xx * xD + zz * zD) * 2; 752 } 753 754 x = x0 + xD * progress; 755 y = y0 + yD * progress; 756 z = z0 + zD * progress; 757 if (yD < 0) y += 1; 758 if (yD > 0) y += 0.5; 759 return Vec3::newTemp(x, y, z); 760 } 761 return NULL; 762} 763 764void Minecart::readAdditionalSaveData(CompoundTag *tag) 765{ 766 if (tag->getBoolean(L"CustomDisplayTile")) 767 { 768 setDisplayTile(tag->getInt(L"DisplayTile")); 769 setDisplayData(tag->getInt(L"DisplayData")); 770 setDisplayOffset(tag->getInt(L"DisplayOffset")); 771 } 772 773 if (tag->contains(L"CustomName") && tag->getString(L"CustomName").length() > 0) name = tag->getString(L"CustomName"); 774} 775 776void Minecart::addAdditonalSaveData(CompoundTag *tag) 777{ 778 if (hasCustomDisplay()) 779 { 780 tag->putBoolean(L"CustomDisplayTile", true); 781 tag->putInt(L"DisplayTile", getDisplayTile() == NULL ? 0 : getDisplayTile()->id); 782 tag->putInt(L"DisplayData", getDisplayData()); 783 tag->putInt(L"DisplayOffset", getDisplayOffset()); 784 } 785 786 if (!name.empty()) tag->putString(L"CustomName", name); 787} 788 789float Minecart::getShadowHeightOffs() 790{ 791 return 0; 792} 793 794void Minecart::push(shared_ptr<Entity> e) 795{ 796 if (level->isClientSide) return; 797 798 if (e == rider.lock()) return; 799 if ( e->instanceof(eTYPE_LIVINGENTITY) && !e->instanceof(eTYPE_PLAYER) && !e->instanceof(eTYPE_VILLAGERGOLEM) && (getType() == TYPE_RIDEABLE) && (xd * xd + zd * zd > 0.01) ) 800 { 801 if ( (rider.lock() == NULL) && (e->riding == NULL) ) 802 { 803 e->ride( shared_from_this() ); 804 } 805 } 806 807 double xa = e->x - x; 808 double za = e->z - z; 809 810 double dd = xa * xa + za * za; 811 if (dd >= 0.0001f) 812 { 813 dd = sqrt(dd); 814 xa /= dd; 815 za /= dd; 816 double pow = 1 / dd; 817 if (pow > 1) pow = 1; 818 xa *= pow; 819 za *= pow; 820 xa *= 0.1f; 821 za *= 0.1f; 822 823 xa *= 1 - pushthrough; 824 za *= 1 - pushthrough; 825 xa *= 0.5; 826 za *= 0.5; 827 828 if (e->instanceof(eTYPE_MINECART)) 829 { 830 double xo = e->x - x; 831 double zo = e->z - z; 832 833 //4J Stu - Brought forward changes to fix minecarts pushing each other 834 // Fix for #38882 - TU5: Gameplay: Minecart with furnace is not able to move another minecart on the rail. 835 Vec3 *dir = Vec3::newTemp(xo, 0, zo)->normalize(); 836 Vec3 *facing = Vec3::newTemp(cos(yRot * PI / 180), 0, sin(yRot * PI / 180))->normalize(); 837 838 double dot = abs(dir->dot(facing)); 839 840 if (dot < 0.8f) 841 { 842 return; 843 } 844 845 double xdd = (e->xd + xd); 846 double zdd = (e->zd + zd); 847 848 shared_ptr<Minecart> cart = dynamic_pointer_cast<Minecart>(e); 849 if (cart != NULL && cart->getType() == TYPE_FURNACE && getType() != TYPE_FURNACE) 850 { 851 xd *= 0.2f; 852 zd *= 0.2f; 853 push( e->xd - xa, 0, e->zd - za); 854 e->xd *= 0.95f; 855 e->zd *= 0.95f; 856 m_bHasPushedCartThisTick = true; 857 } 858 else if (cart != NULL && cart->getType() != TYPE_FURNACE && getType() == TYPE_FURNACE) 859 { 860 e->xd *= 0.2f; 861 e->zd *= 0.2f; 862 e->push(xd + xa, 0, zd + za); 863 xd *= 0.95f; 864 zd *= 0.95f; 865 m_bHasPushedCartThisTick = true; 866 } 867 else 868 { 869 xdd /= 2; 870 zdd /= 2; 871 xd *= 0.2f; 872 zd *= 0.2f; 873 push(xdd - xa, 0, zdd - za); 874 e->xd *= 0.2f; 875 e->zd *= 0.2f; 876 e->push(xdd + xa, 0, zdd + za); 877 m_bHasPushedCartThisTick = true; 878 879 // 4J Stu - Fix for #46937 - TU5: Gameplay: Crash/Freeze occurs when a minecart with an animal inside will be forced to despawn 880 // Minecarts can end up stuck inside each other, so if they are too close then they should separate quickly 881 double modifier = 1.0; 882 if( abs(xo) < 1 && abs(zo) < 1) 883 { 884 modifier += 1-( (abs(xo) + abs(zo))/2); 885 } 886 // 4J Stu - Decelerate the cart that is pushing this one if they are too close 887 e->xd /= modifier; 888 e->zd /= modifier; 889 890 // 4J Backup fix for QNAN 891 if( !(xd==xd) ) xd = 0; 892 if( !(zd==zd) ) zd = 0; 893 if( !(e->xd == e->xd) ) e->xd = 0; 894 if( !(e->zd == e->zd) ) e->zd = 0; 895 } 896 897 } 898 else 899 { 900 push(-xa, 0, -za); 901 e->push(xa / 4, 0, za / 4); 902 } 903 } 904} 905 906void Minecart::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) 907{ 908 lx = x; 909 ly = y; 910 lz = z; 911 lyr = yRot; 912 lxr = xRot; 913 914 lSteps = steps + 2; 915 916 xd = lxd; 917 yd = lyd; 918 zd = lzd; 919} 920 921void Minecart::lerpMotion(double xd, double yd, double zd) 922{ 923 lxd = this->xd = xd; 924 lyd = this->yd = yd; 925 lzd = this->zd = zd; 926} 927 928void Minecart::setDamage(float damage) 929{ 930 entityData->set(DATA_ID_DAMAGE, damage); 931} 932 933float Minecart::getDamage() 934{ 935 return entityData->getFloat(DATA_ID_DAMAGE); 936} 937 938void Minecart::setHurtTime(int hurtTime) 939{ 940 entityData->set(DATA_ID_HURT, hurtTime); 941} 942 943int Minecart::getHurtTime() 944{ 945 return entityData->getInteger(DATA_ID_HURT); 946} 947 948void Minecart::setHurtDir(int hurtDir) 949{ 950 entityData->set(DATA_ID_HURTDIR, hurtDir); 951} 952 953int Minecart::getHurtDir() 954{ 955 return entityData->getInteger(DATA_ID_HURTDIR); 956} 957 958Tile *Minecart::getDisplayTile() 959{ 960 if (!hasCustomDisplay()) return getDefaultDisplayTile(); 961 int id = getEntityData()->getInteger(DATA_ID_DISPLAY_TILE) & 0xFFFF; 962 return id > 0 && id < Tile::TILE_NUM_COUNT ? Tile::tiles[id] : NULL; 963} 964 965Tile *Minecart::getDefaultDisplayTile() 966{ 967 return NULL; 968} 969 970int Minecart::getDisplayData() 971{ 972 if (!hasCustomDisplay()) return getDefaultDisplayData(); 973 return getEntityData()->getInteger(DATA_ID_DISPLAY_TILE) >> 16; 974} 975 976int Minecart::getDefaultDisplayData() 977{ 978 return 0; 979} 980 981int Minecart::getDisplayOffset() 982{ 983 if (!hasCustomDisplay()) return getDefaultDisplayOffset(); 984 return getEntityData()->getInteger(DATA_ID_DISPLAY_OFFSET); 985} 986 987int Minecart::getDefaultDisplayOffset() 988{ 989 return 6; 990} 991 992void Minecart::setDisplayTile(int id) 993{ 994 getEntityData()->set(DATA_ID_DISPLAY_TILE, (id & 0xFFFF) | (getDisplayData() << 16)); 995 setCustomDisplay(true); 996} 997 998void Minecart::setDisplayData(int data) 999{ 1000 Tile *tile = getDisplayTile(); 1001 int id = tile == NULL ? 0 : tile->id; 1002 1003 getEntityData()->set(DATA_ID_DISPLAY_TILE, (id & 0xFFFF) | (data << 16)); 1004 setCustomDisplay(true); 1005} 1006 1007void Minecart::setDisplayOffset(int offset) 1008{ 1009 getEntityData()->set(DATA_ID_DISPLAY_OFFSET, offset); 1010 setCustomDisplay(true); 1011} 1012 1013bool Minecart::hasCustomDisplay() 1014{ 1015 return getEntityData()->getByte(DATA_ID_CUSTOM_DISPLAY) == 1; 1016} 1017 1018void Minecart::setCustomDisplay(bool value) 1019{ 1020 getEntityData()->set(DATA_ID_CUSTOM_DISPLAY, (byte) (value ? 1 : 0)); 1021} 1022 1023void Minecart::setCustomName(const wstring &name) 1024{ 1025 this->name = name; 1026} 1027 1028wstring Minecart::getAName() 1029{ 1030 if (!name.empty()) return name; 1031 return Entity::getAName(); 1032} 1033 1034bool Minecart::hasCustomName() 1035{ 1036 return !name.empty(); 1037} 1038 1039wstring Minecart::getCustomName() 1040{ 1041 return name; 1042}