the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
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}