Cuberite
A lightweight, fast and extensible game server for Minecraft
Minecart.cpp
Go to the documentation of this file.
1 
2 // Minecart.cpp
3 
4 // Implements the cMinecart class representing a minecart in the world
5 // Handles physics when a minecart is on any type of rail (overrides simulator in Entity.cpp)
6 // Indiana Jones!
7 
8 #include "Globals.h"
9 #include "ChunkDef.h"
10 #include "Defines.h"
11 #include "Minecart.h"
12 #include "../BlockInfo.h"
13 #include "../ClientHandle.h"
14 #include "../Chunk.h"
15 #include "Player.h"
16 #include "../BoundingBox.h"
17 #include "../UI/MinecartWithChestWindow.h"
18 
19 #define NO_SPEED 0.0
20 #define MAX_SPEED 8
21 #define MAX_SPEED_NEGATIVE -MAX_SPEED
22 #define DIR_NORTH_SOUTH 270
23 #define DIR_EAST_WEST 180
24 #define DIR_NORTH_WEST 315
25 #define DIR_NORTH_EAST 225
26 #define DIR_SOUTH_WEST 135
27 #define DIR_SOUTH_EAST 45
28 
30 {
31 public:
32  cMinecartAttachCallback(cMinecart & a_Minecart, cEntity * a_Attachee) :
33  m_Minecart(a_Minecart), m_Attachee(a_Attachee)
34  {
35  }
36 
37  bool operator()(cEntity & a_Entity)
38  {
39  // Check if minecart is empty and if given entity is a mob:
40  if ((m_Attachee == nullptr) && a_Entity.IsMob())
41  {
42  // If so, attach to minecart and stop iterating:
43  a_Entity.AttachTo(m_Minecart);
44  return true;
45  }
46  return false;
47  }
48 
49 protected:
50 
53 };
54 
55 
56 
57 
58 
60 {
61 public:
62  cMinecartCollisionCallback(Vector3d a_Pos, double a_Height, double a_Width, UInt32 a_UniqueID, UInt32 a_AttacheeUniqueID) :
63  m_DoesIntersect(false),
64  m_CollidedEntityPos(0, 0, 0),
65  m_Pos(a_Pos),
66  m_Height(a_Height),
67  m_Width(a_Width),
68  m_UniqueID(a_UniqueID),
69  m_AttacheeUniqueID(a_AttacheeUniqueID)
70  {
71  }
72 
73  bool operator () (cEntity & a_Entity)
74  {
75  if (
76  (
77  !a_Entity.IsPlayer() ||
78  static_cast<cPlayer &>(a_Entity).IsGameModeSpectator() // Spectators doesn't collide with anything
79  ) &&
80  !a_Entity.IsMob() &&
81  !a_Entity.IsMinecart() &&
82  !a_Entity.IsBoat()
83  )
84  {
85  return false;
86  }
87  else if ((a_Entity.GetUniqueID() == m_UniqueID) || (a_Entity.GetUniqueID() == m_AttacheeUniqueID))
88  {
89  return false;
90  }
91 
92  cBoundingBox bbEntity(a_Entity.GetPosition(), a_Entity.GetWidth() / 2, a_Entity.GetHeight());
93  cBoundingBox bbMinecart(Vector3d(m_Pos.x, floor(m_Pos.y), m_Pos.z), m_Width / 2, m_Height);
94 
95  if (bbEntity.DoesIntersect(bbMinecart))
96  {
97  m_CollidedEntityPos = a_Entity.GetPosition();
98  m_DoesIntersect = true;
99  return true;
100  }
101  return false;
102  }
103 
104  bool FoundIntersection(void) const
105  {
106  return m_DoesIntersect;
107  }
108 
110  {
111  return m_CollidedEntityPos;
112  }
113 
114 protected:
116 
118 
120  double m_Height, m_Width;
123 };
124 
125 
126 
127 
128 
130 // cMinecart:
131 
133  Super(etMinecart, a_Pos, 0.98f, 0.7f),
134  m_Payload(a_Payload),
135  m_LastDamage(0),
136  m_DetectorRailPosition(0, 0, 0),
137  m_bIsOnDetectorRail(false)
138 {
139  SetMass(20.0f);
140  SetGravity(-16.0f);
141  SetAirDrag(0.05f);
142  SetMaxHealth(6);
143  SetHealth(6);
144 }
145 
146 
147 
148 
149 
150 void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
151 {
152  a_ClientHandle.SendSpawnEntity(*this);
153  a_ClientHandle.SendEntityMetadata(*this);
154 }
155 
156 
157 
158 
159 
160 void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
161 {
162  ASSERT(IsTicking());
163 
164  int PosY = POSY_TOINT;
165  if ((PosY <= 0) || (PosY >= cChunkDef::Height))
166  {
167  // Outside the world, just process normal falling physics
168  Super::HandlePhysics(a_Dt, a_Chunk);
170  return;
171  }
172 
173  // pos need floor, then call vec3i overload func
174  // if use default double -> int, will cast -1.xx -> -1(actually need to be -2)
175  auto relPos = cChunkDef::AbsoluteToRelative(GetPosition().Floor());
176 
177  auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(relPos);
178  if (chunk == nullptr)
179  {
180  // Inside an unloaded chunk, bail out all processing
181  return;
182  }
183 
185  {
186  // Check if the rail is still there
188  {
190  }
191 
192  m_bIsOnDetectorRail = false;
193  }
194 
195  BLOCKTYPE InsideType;
196  NIBBLETYPE InsideMeta;
197  chunk->GetBlockTypeMeta(relPos, InsideType, InsideMeta);
198 
199  if (!IsBlockRail(InsideType))
200  {
201  // When a descending minecart hits a flat rail, it goes through the ground; check for this
202  chunk->GetBlockTypeMeta(relPos.addedY(1), InsideType, InsideMeta);
203  if (IsBlockRail(InsideType))
204  {
205  // Push cart upwards
206  AddPosY(1);
207  }
208  else
209  {
210  // When a minecart gets to a descending rail, it should go down.
211  chunk->GetBlockTypeMeta(relPos.addedY(-1), InsideType, InsideMeta);
212  if (IsBlockRail(InsideType))
213  {
214  // Push cart downwards
215  AddPosY(-1);
216  }
217  }
218  }
219 
220  if (IsBlockRail(InsideType))
221  {
222  if (InsideType == E_BLOCK_RAIL)
223  {
224  SnapToRail(InsideMeta);
225  }
226  else
227  {
228  SnapToRail(InsideMeta & 0x07);
229  }
230 
231  switch (InsideType)
232  {
233  case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break;
234  case E_BLOCK_ACTIVATOR_RAIL: HandleActivatorRailPhysics(InsideMeta, a_Dt); break;
235  case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break;
237  {
239  m_bIsOnDetectorRail = true;
240  HandleDetectorRailPhysics(InsideMeta, a_Dt);
241  break;
242  }
243  default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break;
244  }
245 
246  AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
247  }
248  else
249  {
250  // Not on rail, default physics
251  SetPosY(floor(GetPosY()) + 0.35); // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail
252  Super::HandlePhysics(a_Dt, *chunk);
253  }
254 
255  // Enforce speed limit:
257 
258  // Broadcast positioning changes to client
260 }
261 
262 
263 
264 
265 
266 void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
267 {
268  /*
269  NOTE: Please bear in mind that taking away from negatives make them even more negative,
270  adding to negatives make them positive, etc.
271  */
272 
273  switch (a_RailMeta)
274  {
275  case E_META_RAIL_ZM_ZP: // NORTHSOUTH
276  {
278  SetPosY(floor(GetPosY()) + 0.55);
279  SetSpeedY(NO_SPEED); // Don't move vertically as on ground
280  SetSpeedX(NO_SPEED); // Correct diagonal movement from curved rails
281 
282  // Execute both the entity and block collision checks
283  auto BlckCol = TestBlockCollision(a_RailMeta);
284  auto EntCol = TestEntityCollision(a_RailMeta);
285  if (EntCol || BlckCol)
286  {
287  return;
288  }
289 
290  if (GetSpeedZ() != NO_SPEED) // Don't do anything if cart is stationary
291  {
292  if (GetSpeedZ() > 0)
293  {
294  // Going SOUTH, slow down
295  ApplyAcceleration({ 0.0, 0.0, 1.0 }, -0.1);
296  }
297  else
298  {
299  // Going NORTH, slow down
300  ApplyAcceleration({ 0.0, 0.0, -1.0 }, -0.1);
301  }
302  }
303 
304  return;
305  }
306  case E_META_RAIL_XM_XP: // EASTWEST
307  {
309  SetPosY(floor(GetPosY()) + 0.55);
312 
313  auto BlckCol = TestBlockCollision(a_RailMeta);
314  auto EntCol = TestEntityCollision(a_RailMeta);
315  if (EntCol || BlckCol)
316  {
317  return;
318  }
319 
320  if (GetSpeedX() != NO_SPEED)
321  {
322  if (GetSpeedX() > 0)
323  {
324  ApplyAcceleration({ 1.0, 0.0, 0.0 }, -0.1);
325  }
326  else
327  {
328  ApplyAcceleration({ -1.0, 0.0, 0.0 }, -0.1);
329  }
330  }
331 
332  return;
333  }
334  case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
335  {
338 
339  auto BlckCol = TestBlockCollision(a_RailMeta);
340  auto EntCol = TestEntityCollision(a_RailMeta);
341  if (EntCol || BlckCol)
342  {
343  return;
344  }
345 
346  if (GetSpeedZ() >= 0)
347  {
348  // SpeedZ POSITIVE, going SOUTH
349  AddSpeedZ(0.5); // Speed up
350  SetSpeedY(-GetSpeedZ()); // Downward movement is negative (0 minus positive numbers is negative)
351  }
352  else
353  {
354  // SpeedZ NEGATIVE, going NORTH
355  AddSpeedZ(1); // Slow down
356  SetSpeedY(-GetSpeedZ()); // Upward movement is positive (0 minus negative number is positive number)
357  }
358 
359  return;
360  }
361  case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
362  {
365 
366  auto BlckCol = TestBlockCollision(a_RailMeta);
367  auto EntCol = TestEntityCollision(a_RailMeta);
368  if (EntCol || BlckCol)
369  {
370  return;
371  }
372 
373  if (GetSpeedZ() > 0)
374  {
375  // SpeedZ POSITIVE, going SOUTH
376  AddSpeedZ(-1); // Slow down
377  SetSpeedY(GetSpeedZ()); // Upward movement positive
378  }
379  else
380  {
381  // SpeedZ NEGATIVE, going NORTH
382  AddSpeedZ(-0.5); // Speed up
383  SetSpeedY(GetSpeedZ()); // Downward movement negative
384  }
385 
386  return;
387  }
388  case E_META_RAIL_ASCEND_XM: // ASCEND EAST
389  {
392 
393  auto BlckCol = TestBlockCollision(a_RailMeta);
394  auto EntCol = TestEntityCollision(a_RailMeta);
395  if (EntCol || BlckCol)
396  {
397  return;
398  }
399 
400  if (GetSpeedX() >= NO_SPEED)
401  {
402  AddSpeedX(0.5);
403  SetSpeedY(-GetSpeedX());
404  }
405  else
406  {
407  AddSpeedX(1);
408  SetSpeedY(-GetSpeedX());
409  }
410 
411  return;
412  }
413  case E_META_RAIL_ASCEND_XP: // ASCEND WEST
414  {
417 
418  auto BlckCol = TestBlockCollision(a_RailMeta);
419  auto EntCol = TestEntityCollision(a_RailMeta);
420  if (EntCol || BlckCol)
421  {
422  return;
423  }
424 
425  if (GetSpeedX() > 0)
426  {
427  AddSpeedX(-1);
428  SetSpeedY(GetSpeedX());
429  }
430  else
431  {
432  AddSpeedX(-0.5);
433  SetSpeedY(GetSpeedX());
434  }
435 
436  return;
437  }
438  case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST
439  {
440  SetYaw(DIR_NORTH_WEST); // Set correct rotation server side
441  SetPosY(floor(GetPosY()) + 0.55); // Levitate dat cart
443 
444  auto BlckCol = TestBlockCollision(a_RailMeta);
445  auto EntCol = TestEntityCollision(a_RailMeta);
446  if (EntCol || BlckCol)
447  {
448  return;
449  }
450 
451  // SnapToRail handles turning
452 
453  return;
454  }
455  case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
456  {
458  SetPosY(floor(GetPosY()) + 0.55);
460 
461  auto BlckCol = TestBlockCollision(a_RailMeta);
462  auto EntCol = TestEntityCollision(a_RailMeta);
463  if (EntCol || BlckCol)
464  {
465  return;
466  }
467 
468  return;
469  }
470  case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
471  {
473  SetPosY(floor(GetPosY()) + 0.55);
475 
476  auto BlckCol = TestBlockCollision(a_RailMeta);
477  auto EntCol = TestEntityCollision(a_RailMeta);
478  if (EntCol || BlckCol)
479  {
480  return;
481  }
482 
483  return;
484  }
485  case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
486  {
488  SetPosY(floor(GetPosY()) + 0.55);
490 
491  auto BlckCol = TestBlockCollision(a_RailMeta);
492  auto EntCol = TestEntityCollision(a_RailMeta);
493  if (EntCol || BlckCol)
494  {
495  return;
496  }
497 
498  return;
499  }
500  }
501  UNREACHABLE("Unsupported rail meta type");
502 }
503 
504 
505 
506 
507 
509 {
510  // If the rail is powered set to speed up else slow down.
511  const bool IsRailPowered = ((a_RailMeta & 0x8) == 0x8);
512  const double Acceleration = IsRailPowered ? 1.0 : -2.0;
513  // PoweredRail only has 5 status in low 3bit. so we need do a logical and to get correct powered rail meta data.
514  NIBBLETYPE PoweredRailMeta = a_RailMeta & 0x7;
515  switch (PoweredRailMeta)
516  {
517  case E_META_RAIL_ZM_ZP: // NORTHSOUTH
518  {
520  SetPosY(floor(GetPosY()) + 0.55);
521  SetSpeedY(0);
522  SetSpeedX(0);
523 
524  bool BlckCol = TestBlockCollision(PoweredRailMeta), EntCol = TestEntityCollision(PoweredRailMeta);
525  if (EntCol || BlckCol)
526  {
527  return;
528  }
529 
530  if (GetSpeedZ() != NO_SPEED)
531  {
532  if (GetSpeedZ() > NO_SPEED)
533  {
534  ApplyAcceleration({ 0.0, 0.0, 1.0 }, Acceleration);
535  }
536  else
537  {
538  ApplyAcceleration({ 0.0, 0.0, -1.0 }, Acceleration);
539  }
540  }
541  // If rail is powered check for nearby blocks that could kick-start the minecart
542  else if (IsRailPowered)
543  {
544  bool IsBlockZP = IsSolidBlockAtOffset(0, 0, 1);
545  bool IsBlockZM = IsSolidBlockAtOffset(0, 0, -1);
546  // Only kick-start the minecart if a block is on one side, but not both
547  if (IsBlockZM && !IsBlockZP)
548  {
549  ApplyAcceleration({ 0.0, 0.0, 1.0 }, Acceleration);
550  }
551  else if (!IsBlockZM && IsBlockZP)
552  {
553  ApplyAcceleration({ 0.0, 0.0, -1.0 }, Acceleration);
554  }
555  }
556  break;
557  }
558  case E_META_RAIL_XM_XP: // EASTWEST
559  {
561  SetPosY(floor(GetPosY()) + 0.55);
564 
565  bool BlckCol = TestBlockCollision(PoweredRailMeta), EntCol = TestEntityCollision(PoweredRailMeta);
566  if (EntCol || BlckCol)
567  {
568  return;
569  }
570 
571  if (GetSpeedX() != NO_SPEED)
572  {
573  if (GetSpeedX() > NO_SPEED)
574  {
575  ApplyAcceleration({ 1.0, 0.0, 0.0 }, Acceleration);
576  }
577  else
578  {
579  ApplyAcceleration({ -1.0, 0.0, 0.0 }, Acceleration);
580  }
581  }
582  // If rail is powered check for nearby blocks that could kick-start the minecart
583  else if (IsRailPowered)
584  {
585  bool IsBlockXP = IsSolidBlockAtOffset(1, 0, 0);
586  bool IsBlockXM = IsSolidBlockAtOffset(-1, 0, 0);
587  // Only kick-start the minecart if a block is on one side, but not both
588  if (IsBlockXM && !IsBlockXP)
589  {
590  ApplyAcceleration({ 1.0, 0.0, 0.0 }, Acceleration);
591  }
592  else if (!IsBlockXM && IsBlockXP)
593  {
594  ApplyAcceleration({ -1.0, 0.0, 0.0 }, Acceleration);
595  }
596  }
597  break;
598  }
599  case E_META_RAIL_ASCEND_XM: // ASCEND EAST
600  {
603 
604  if (GetSpeedX() >= NO_SPEED)
605  {
606  ApplyAcceleration({ 1.0, 0.0, 0.0 }, Acceleration);
607  SetSpeedY(-GetSpeedX());
608  }
609  else
610  {
611  ApplyAcceleration({ -1.0, 0.0, 0.0 }, Acceleration);
612  SetSpeedY(-GetSpeedX());
613  }
614  break;
615  }
616  case E_META_RAIL_ASCEND_XP: // ASCEND WEST
617  {
620 
621  if (GetSpeedX() > NO_SPEED)
622  {
623  ApplyAcceleration({ 1.0, 0.0, 0.0 }, Acceleration);
624  SetSpeedY(GetSpeedX());
625  }
626  else
627  {
628  ApplyAcceleration({ -1.0, 0.0, 0.0 }, Acceleration);
629  SetSpeedY(GetSpeedX());
630  }
631  break;
632  }
633  case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
634  {
637 
638  if (GetSpeedZ() >= NO_SPEED)
639  {
640  ApplyAcceleration({ 0.0, 0.0, 1.0 }, Acceleration);
641  SetSpeedY(-GetSpeedZ());
642  }
643  else
644  {
645  ApplyAcceleration({ 0.0, 0.0, -1.0 }, Acceleration);
646  SetSpeedY(-GetSpeedZ());
647  }
648  break;
649  }
650  case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
651  {
654 
655  if (GetSpeedZ() > NO_SPEED)
656  {
657  ApplyAcceleration({ 0.0, 0.0, 1.0 }, Acceleration);
658  SetSpeedY(GetSpeedZ());
659  }
660  else
661  {
662  ApplyAcceleration({ 0.0, 0.0, -1.0 }, Acceleration);
663  SetSpeedY(GetSpeedZ());
664  }
665  break;
666  }
667  default: ASSERT(!"Unhandled powered rail metadata!"); break;
668  }
669 }
670 
671 
672 
673 
674 
675 void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
676 {
677  m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08);
678 
679  // No special handling
680  HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
681 }
682 
683 
684 
685 
686 
687 void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
688 {
689  HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
690  // TODO - shake minecart, throw entities out
691 }
692 
693 
694 
695 
696 
698 {
699  switch (a_RailMeta)
700  {
703  case E_META_RAIL_XM_XP:
704  {
706  SetPosZ(floor(GetPosZ()) + 0.5);
707  break;
708  }
711  case E_META_RAIL_ZM_ZP:
712  {
714  SetPosX(floor(GetPosX()) + 0.5);
715  break;
716  }
717  // Curved rail physics: once minecart has reached more than half of the block in the direction that it is travelling in, jerk it in the direction of curvature
719  {
720  if (GetPosZ() > floor(GetPosZ()) + 0.5)
721  {
722  if (GetSpeedZ() > NO_SPEED)
723  {
724  SetSpeedX(-GetSpeedZ() * 0.7);
725  }
726 
728  SetPosZ(floor(GetPosZ()) + 0.5);
729  }
730  else if (GetPosX() > floor(GetPosX()) + 0.5)
731  {
732  if (GetSpeedX() > 0)
733  {
734  SetSpeedZ(-GetSpeedX() * 0.7);
735  }
736 
738  SetPosX(floor(GetPosX()) + 0.5);
739  }
741  break;
742  }
744  {
745  if (GetPosZ() > floor(GetPosZ()) + 0.5)
746  {
747  if (GetSpeedZ() > NO_SPEED)
748  {
749  SetSpeedX(GetSpeedZ() * 0.7);
750  }
751 
753  SetPosZ(floor(GetPosZ()) + 0.5);
754  }
755  else if (GetPosX() < floor(GetPosX()) + 0.5)
756  {
757  if (GetSpeedX() < NO_SPEED)
758  {
759  SetSpeedZ(GetSpeedX() * 0.7);
760  }
761 
763  SetPosX(floor(GetPosX()) + 0.5);
764  }
766  break;
767  }
769  {
770  if (GetPosZ() < floor(GetPosZ()) + 0.5)
771  {
772  if (GetSpeedZ() < NO_SPEED)
773  {
774  SetSpeedX(GetSpeedZ() * 0.7);
775  }
776 
778  SetPosZ(floor(GetPosZ()) + 0.5);
779  }
780  else if (GetPosX() > floor(GetPosX()) + 0.5)
781  {
782  if (GetSpeedX() > NO_SPEED)
783  {
784  SetSpeedZ(GetSpeedX() * 0.7);
785  }
786 
788  SetPosX(floor(GetPosX()) + 0.5);
789  }
791  break;
792  }
794  {
795  if (GetPosZ() < floor(GetPosZ()) + 0.5)
796  {
797  if (GetSpeedZ() < NO_SPEED)
798  {
799  SetSpeedX(-GetSpeedZ() * 0.7);
800  }
801 
803  SetPosZ(floor(GetPosZ()) + 0.5);
804  }
805  else if (GetPosX() < floor(GetPosX()) + 0.5)
806  {
807  if (GetSpeedX() < NO_SPEED)
808  {
809  SetSpeedZ(-GetSpeedX() * 0.7);
810  }
811 
813  SetPosX(floor(GetPosX()) + 0.5);
814  }
815  SetSpeedY(0);
816  break;
817  }
818  default: break;
819  }
820 }
821 
822 
823 
824 
825 
827 {
828  BLOCKTYPE Block = m_World->GetBlock(a_Pos);
830 }
831 
832 
833 
834 
835 
836 bool cMinecart::IsSolidBlockAtOffset(int a_XOffset, int a_YOffset, int a_ZOffset)
837 {
838  return IsSolidBlockAtPosition({POSX_TOINT + a_XOffset, POSY_TOINT + a_YOffset, POSZ_TOINT + a_ZOffset});
839 }
840 
841 
842 
843 
844 
846 {
847  auto BlockPosition = GetPosition().Floor() + a_Offset;
848  if (!IsSolidBlockAtPosition(BlockPosition))
849  {
850  return false;
851  }
852 
853  auto bbBlock = cBoundingBox(
854  static_cast<Vector3d>(BlockPosition),
855  static_cast<Vector3d>(BlockPosition + Vector3i(1, 1, 1))
856  );
857 
858  return GetBoundingBox().DoesIntersect(bbBlock);
859 }
860 
861 
862 
863 
864 
866 {
867  auto SpeedX = GetSpeedX();
868  auto SpeedZ = GetSpeedZ();
869 
870  // Don't do anything if minecarts aren't moving.
871  #ifdef __clang__
872  #pragma clang diagnostic push
873  #pragma clang diagnostic ignored "-Wfloat-equal"
874  #endif
875 
876  if ((SpeedX == 0) && (SpeedZ == 0))
877  {
878  return false;
879  }
880 
881  #ifdef __clang__
882  #pragma clang diagnostic pop
883  #endif
884 
885  auto StopTheCart = true;
886  auto StopOffset = Vector3d();
887 
888  switch (a_RailMeta)
889  {
890  case E_META_RAIL_ZM_ZP:
891  {
892  if (SpeedZ > 0)
893  {
894  StopOffset = Vector3d(0, 0, 0.4);
895  StopTheCart = IsBlockCollisionAtOffset({0, 0, 1});
896  }
897  else // SpeedZ < 0
898  {
899  StopTheCart = IsBlockCollisionAtOffset({0, 0, -1});
900  StopOffset = Vector3d(0, 0, 0.65);
901  }
902  break;
903  }
904  case E_META_RAIL_XM_XP:
905  {
906  if (SpeedX > 0)
907  {
908  StopTheCart = IsBlockCollisionAtOffset({1, 0, 0});
909  StopOffset = Vector3d(0.4, 0, 0);
910  }
911  else // SpeedX < 0
912  {
913  StopTheCart = IsBlockCollisionAtOffset({-1, 0, 0});
914  StopOffset = Vector3d(0.65, 0, 0);
915  }
916  break;
917  }
918 
919  // Ascending rails check for one block on the way up, two on the way down.
921  {
922  StopOffset = Vector3d(0.5, 0, 0);
923 
924  if (SpeedX < 0)
925  {
926  StopTheCart = IsBlockCollisionAtOffset({-1, 1, 0});
927  }
928  else // SpeedX > 0
929  {
930  StopTheCart = IsBlockCollisionAtOffset({1, 0, 0}) || IsBlockCollisionAtOffset({1, 1, 0});
931  }
932  break;
933  }
935  {
936  StopOffset = Vector3d(0.5, 0, 0);
937 
938  if (SpeedX > 0)
939  {
940  StopTheCart = IsBlockCollisionAtOffset({1, 1, 0});
941  }
942  else // SpeedX < 0
943  {
944  StopTheCart = IsBlockCollisionAtOffset({-1, 0, 0}) || IsBlockCollisionAtOffset({-1, 1, 0});
945  }
946  break;
947  }
949  {
950  StopOffset = Vector3d(0, 0, 0.5);
951 
952  if (SpeedZ < 0)
953  {
954  StopTheCart = IsBlockCollisionAtOffset({0, 1, -1});
955  }
956  else // SpeedZ > 0
957  {
958  StopTheCart = IsBlockCollisionAtOffset({0, 0, 1}) || IsBlockCollisionAtOffset({0, 1, 1});
959  }
960  break;
961  }
963  {
964  StopOffset = Vector3d(0, 0, 0.5);
965 
966  if (SpeedZ > 0)
967  {
968  StopTheCart = IsBlockCollisionAtOffset({0, 1, 1});
969  }
970  else // SpeedZ < 0
971  {
972  StopTheCart = IsBlockCollisionAtOffset({0, 0, -1}) || IsBlockCollisionAtOffset({0, 1, -1});
973  }
974  break;
975  }
976 
977  // Curved rails allow movement across both the x and z axes. But when the cart is
978  // moving towards one of the rail endpoints, it will always have velocity towards
979  // the direction of that endpoint in the same axis.
981  {
982  StopOffset = Vector3d(0.5, 0, 0.5);
983 
984  if (SpeedZ > 0)
985  {
986  StopTheCart = IsBlockCollisionAtOffset({0, 0, 1});
987  break;
988  }
989  if (SpeedX > 0)
990  {
991  StopTheCart = IsBlockCollisionAtOffset({1, 0, 0});
992  break;
993  }
994 
995  break;
996  }
998  {
999  StopOffset = Vector3d(0.5, 0, 0.5);
1000 
1001  if (SpeedZ > 0)
1002  {
1003  StopTheCart = IsBlockCollisionAtOffset({0, 0, 1});
1004  break;
1005  }
1006  if (SpeedX < 0)
1007  {
1008  StopTheCart = IsBlockCollisionAtOffset({-1, 0, 0});
1009  break;
1010  }
1011 
1012  break;
1013  }
1015  {
1016  StopOffset = Vector3d(0.5, 0, 0.5);
1017 
1018  if (SpeedZ < 0)
1019  {
1020  StopTheCart = IsBlockCollisionAtOffset({0, 0, -1});
1021  break;
1022  }
1023  if (SpeedX < 0)
1024  {
1025  StopTheCart = IsBlockCollisionAtOffset({-1, 0, 0});
1026  break;
1027  }
1028 
1029  break;
1030  }
1032  {
1033  StopOffset = Vector3d(0.5, 0, 0.5);
1034 
1035  if (SpeedZ < 0)
1036  {
1037  StopTheCart = IsBlockCollisionAtOffset({0, 0, -1});
1038  break;
1039  }
1040  if (SpeedX > 0)
1041  {
1042  StopTheCart = IsBlockCollisionAtOffset({1, 0, 0});
1043  break;
1044  }
1045 
1046  break;
1047  }
1048  }
1049 
1050  if (StopTheCart)
1051  {
1052  SetSpeed(0, 0, 0);
1053 
1054  #ifdef __clang__
1055  #pragma clang diagnostic push
1056  #pragma clang diagnostic ignored "-Wfloat-equal"
1057  #endif
1058 
1059  if (StopOffset.x != 0)
1060  {
1061  SetPosX(POSX_TOINT + StopOffset.x);
1062  }
1063  if (StopOffset.z != 0)
1064  {
1065  SetPosZ(POSZ_TOINT + StopOffset.z);
1066  }
1067 
1068  #ifdef __clang__
1069  #pragma clang diagnostic pop
1070  #endif
1071 
1072  return true;
1073  }
1074 
1075  return false;
1076 }
1077 
1078 
1079 
1080 
1081 
1083 {
1084  cMinecartCollisionCallback MinecartCollisionCallback(
1087  );
1088  int ChunkX, ChunkZ;
1089  cChunkDef::BlockToChunk(POSX_TOINT, POSZ_TOINT, ChunkX, ChunkZ);
1090  m_World->ForEachEntityInChunk(ChunkX, ChunkZ, MinecartCollisionCallback);
1091 
1092  if (!MinecartCollisionCallback.FoundIntersection())
1093  {
1094  return false;
1095  }
1096 
1097  // Collision was true, create bounding box for minecart, call attach callback for all entities within that box
1098  cMinecartAttachCallback MinecartAttachCallback(*this, m_Attachee);
1099  Vector3d MinecartPosition = GetPosition();
1100  cBoundingBox bbMinecart(Vector3d(MinecartPosition.x, floor(MinecartPosition.y), MinecartPosition.z), GetWidth() / 2, GetHeight());
1101  m_World->ForEachEntityInBox(bbMinecart, MinecartAttachCallback);
1102 
1103  switch (a_RailMeta)
1104  {
1105  case E_META_RAIL_ZM_ZP:
1106  {
1107  if (MinecartCollisionCallback.GetCollidedEntityPosition().z >= GetPosZ())
1108  {
1109  if (GetSpeedZ() > 0) // True if minecart is moving into the direction of the entity
1110  {
1111  SetSpeedZ(0); // Entity handles the pushing
1112  }
1113  }
1114  else // if (MinecartCollisionCallback.GetCollidedEntityPosition().z < GetPosZ())
1115  {
1116  if (GetSpeedZ() < 0) // True if minecart is moving into the direction of the entity
1117  {
1118  SetSpeedZ(0); // Entity handles the pushing
1119  }
1120  }
1121  return true;
1122  }
1123  case E_META_RAIL_XM_XP:
1124  {
1125  if (MinecartCollisionCallback.GetCollidedEntityPosition().x >= GetPosX())
1126  {
1127  if (GetSpeedX() > 0) // True if minecart is moving into the direction of the entity
1128  {
1129  SetSpeedX(0); // Entity handles the pushing
1130  }
1131  }
1132  else // if (MinecartCollisionCallback.GetCollidedEntityPosition().x < GetPosX())
1133  {
1134  if (GetSpeedX() < 0) // True if minecart is moving into the direction of the entity
1135  {
1136  SetSpeedX(0); // Entity handles the pushing
1137  }
1138  }
1139  return true;
1140  }
1143  {
1144  Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
1145 
1146  // Prevent division by small numbers
1147  if (std::abs(Distance.z) < 0.001)
1148  {
1149  Distance.z = 0.001;
1150  }
1151 
1152  /* Check to which side the minecart is to be pushed.
1153  Let's consider a z-x-coordinate system where the minecart is the center (0, 0).
1154  The minecart moves along the line x = -z, the perpendicular line to this is x = z.
1155  In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
1156  if (
1157  ((Distance.z > 0) && ((Distance.x / Distance.z) >= 1)) ||
1158  ((Distance.z < 0) && ((Distance.x / Distance.z) <= 1))
1159  )
1160  {
1161  // Moving -X +Z
1162  if ((-GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
1163  {
1164  // ~ SpeedX >= 0 Immobile or not moving in the "right" direction. Give it a bump!
1165  AddSpeedX(-4 / sqrt(2.0));
1166  AddSpeedZ(4 / sqrt(2.0));
1167  }
1168  else
1169  {
1170  // ~ SpeedX < 0 Moving in the "right" direction. Only accelerate it a bit.
1171  SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
1172  SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
1173  }
1174  }
1175  else if ((GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
1176  {
1177  // Moving +X -Z
1178  // ~ SpeedX <= 0 Immobile or not moving in the "right" direction
1179  AddSpeedX(4 / sqrt(2.0));
1180  AddSpeedZ(-4 / sqrt(2.0));
1181  }
1182  else
1183  {
1184  // ~ SpeedX > 0 Moving in the "right" direction
1185  SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
1186  SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
1187  }
1188  break;
1189  }
1192  {
1193  Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
1194 
1195  // Prevent division by small numbers
1196  if (std::abs(Distance.z) < 0.001)
1197  {
1198  Distance.z = 0.001;
1199  }
1200 
1201  /* Check to which side the minecart is to be pushed.
1202  Let's consider a z-x-coordinate system where the minecart is the center (0, 0).
1203  The minecart moves along the line x = z, the perpendicular line to this is x = -z.
1204  In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
1205  if (
1206  ((Distance.z > 0) && ((Distance.x / Distance.z) <= -1)) ||
1207  ((Distance.z < 0) && ((Distance.x / Distance.z) >= -1))
1208  )
1209  {
1210  // Moving +X +Z
1211  if ((GetSpeedX() * 0.4) < 0.01)
1212  {
1213  // ~ SpeedX <= 0 Immobile or not moving in the "right" direction
1214  AddSpeedX(4 / sqrt(2.0));
1215  AddSpeedZ(4 / sqrt(2.0));
1216  }
1217  else
1218  {
1219  // ~ SpeedX > 0 Moving in the "right" direction
1220  SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
1221  SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
1222  }
1223  }
1224  else if ((-GetSpeedX() * 0.4) < 0.01)
1225  {
1226  // Moving -X -Z
1227  // ~ SpeedX >= 0 Immobile or not moving in the "right" direction
1228  AddSpeedX(-4 / sqrt(2.0));
1229  AddSpeedZ(-4 / sqrt(2.0));
1230  }
1231  else
1232  {
1233  // ~ SpeedX < 0 Moving in the "right" direction
1234  SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
1235  SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
1236  }
1237  break;
1238  }
1239  default: break;
1240  }
1241 
1242  return false;
1243 }
1244 
1245 
1246 
1247 
1248 
1250 {
1251  if ((TDI.Attacker != nullptr) && TDI.Attacker->IsPlayer() && static_cast<cPlayer *>(TDI.Attacker)->IsGameModeCreative())
1252  {
1253  TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative
1255  return Super::DoTakeDamage(TDI); // No drops for creative
1256  }
1257 
1258  m_LastDamage = static_cast<int>(TDI.FinalDamage);
1259  if (!Super::DoTakeDamage(TDI))
1260  {
1261  return false;
1262  }
1263 
1265 
1266  return true;
1267 }
1268 
1269 
1270 
1271 
1272 
1274 {
1275  Super::KilledBy(a_TDI);
1276 
1277  Destroy();
1278 }
1279 
1280 
1281 
1282 
1283 
1285 {
1286  if (m_bIsOnDetectorRail)
1287  {
1289  }
1290 
1291  Super::OnRemoveFromWorld(a_World);
1292 }
1293 
1294 
1295 
1296 
1297 
1298 void cMinecart::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
1299 {
1300  // limit normal minecart speed max lower than 4
1301  // once speed is higher than 4, no more add speed.
1302  if (GetSpeed().Length() > 4)
1303  {
1304  return;
1305  }
1306  Vector3d LookVector = m_Attachee->GetLookVector();
1307  Vector3d ToAddSpeed = LookVector * (a_Forward * 0.4) ;
1308  ToAddSpeed.y = 0;
1309  AddSpeed(ToAddSpeed);
1310 }
1311 
1312 
1313 
1314 
1315 
1316 void cMinecart::ApplyAcceleration(Vector3d a_ForwardDirection, double a_Acceleration)
1317 {
1318  double CurSpeed = GetSpeed().Dot(a_ForwardDirection);
1319  double NewSpeed = CurSpeed + a_Acceleration;
1320 
1321  if (NewSpeed < 0.0)
1322  {
1323  // Prevent deceleration from turning the minecart around.
1324  NewSpeed = 0.0;
1325  }
1326 
1327  auto Acceleration = a_ForwardDirection * (NewSpeed - CurSpeed);
1328 
1329  AddSpeed(Acceleration);
1330 }
1331 
1332 
1333 
1334 
1335 
1337 // cRideableMinecart:
1338 
1339 cRideableMinecart::cRideableMinecart(Vector3d a_Pos, const cItem & a_Content, int a_ContentHeight):
1340  Super(mpNone, a_Pos),
1341  m_Content(a_Content),
1342  m_ContentHeight(a_ContentHeight)
1343 {
1344 }
1345 
1346 
1347 
1348 
1349 
1350 void cRideableMinecart::GetDrops(cItems & a_Drops, cEntity * a_Killer)
1351 {
1352  a_Drops.emplace_back(E_ITEM_MINECART);
1353 }
1354 
1355 
1356 
1357 
1358 
1360 {
1361  Super::OnRightClicked(a_Player);
1362 
1363  if (m_Attachee != nullptr)
1364  {
1365  if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
1366  {
1367  // This player is already sitting in, they want out.
1368  a_Player.Detach();
1369  return;
1370  }
1371 
1372  if (m_Attachee->IsPlayer())
1373  {
1374  // Another player is already sitting in here, cannot attach
1375  return;
1376  }
1377 
1378  // Detach whatever is sitting in this minecart now:
1379  m_Attachee->Detach();
1380  }
1381 
1382  // Attach the player to this minecart:
1383  a_Player.AttachTo(*this);
1384 }
1385 
1386 
1387 
1388 
1389 
1391 // cMinecartWithChest:
1392 
1394  Super(mpChest, a_Pos),
1395  cEntityWindowOwner(this),
1396  m_Contents(ContentsWidth, ContentsHeight)
1397 {
1398  m_Contents.AddListener(*this);
1399 }
1400 
1401 
1402 
1403 
1404 
1405 void cMinecartWithChest::GetDrops(cItems & a_Drops, cEntity * a_Killer)
1406 {
1407  m_Contents.CopyToItems(a_Drops);
1408  a_Drops.emplace_back(E_ITEM_CHEST_MINECART);
1409 }
1410 
1411 
1412 
1413 
1414 
1416 {
1417  const auto Window = GetWindow();
1418  if (Window != nullptr)
1419  {
1420  Window->OwnerDestroyed();
1421  }
1422 
1423  Super::OnRemoveFromWorld(a_World);
1424 }
1425 
1426 
1427 
1428 
1429 
1431 {
1432  // If the window is not created, open it anew:
1433  cWindow * Window = GetWindow();
1434  if (Window == nullptr)
1435  {
1436  OpenNewWindow();
1437  Window = GetWindow();
1438  }
1439 
1440  // Open the window for the player:
1441  if (Window != nullptr)
1442  {
1443  if (a_Player.GetWindow() != Window)
1444  {
1445  a_Player.OpenWindow(*Window);
1446  }
1447  }
1448 }
1449 
1450 
1451 
1452 
1453 
1455 {
1457 }
1458 
1459 
1460 
1461 
1462 
1464 // cMinecartWithFurnace:
1465 
1467  Super(mpFurnace, a_Pos),
1468  m_FueledTimeLeft(-1),
1469  m_IsFueled(false)
1470 {
1471 }
1472 
1473 
1474 
1475 
1476 
1477 void cMinecartWithFurnace::GetDrops(cItems & a_Drops, cEntity * a_Killer)
1478 {
1479  a_Drops.emplace_back(E_ITEM_FURNACE_MINECART);
1480 }
1481 
1482 
1483 
1484 
1485 
1487 {
1488  if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL)
1489  {
1490  if (!a_Player.IsGameModeCreative())
1491  {
1492  a_Player.GetInventory().RemoveOneEquippedItem();
1493  }
1494  if (!m_IsFueled) // We don't want to change the direction by right clicking it.
1495  {
1496  AddSpeed(a_Player.GetLookVector().x, 0, a_Player.GetLookVector().z);
1497  }
1498  m_IsFueled = true;
1499  m_FueledTimeLeft = m_FueledTimeLeft + 600; // The minecart will be active 600 more ticks.
1501  }
1502 }
1503 
1504 
1505 
1506 
1507 
1508 void cMinecartWithFurnace::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
1509 {
1510  Super::Tick(a_Dt, a_Chunk);
1511  if (!IsTicking())
1512  {
1513  // The base class tick destroyed us
1514  return;
1515  }
1516 
1517  if (m_IsFueled)
1518  {
1519  m_FueledTimeLeft--;
1520  if (m_FueledTimeLeft < 0)
1521  {
1522  m_IsFueled = false;
1524  return;
1525  }
1526 
1527  if (GetSpeed().Length() > 6)
1528  {
1529  return;
1530  }
1531  AddSpeed(GetSpeed() / 4);
1532  }
1533 }
1534 
1535 
1536 
1537 
1538 
1540 // cMinecartWithTNT:
1541 
1543  Super(mpTNT, a_Pos)
1544 {
1545 }
1546 
1547 
1548 
1549 
1550 
1551 void cMinecartWithTNT::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
1552 {
1553  Super::HandleActivatorRailPhysics(a_RailMeta, a_Dt);
1554 
1555  if ((a_RailMeta & 0x08) && !m_isTNTFused)
1556  {
1557  m_isTNTFused = true;
1558  m_TNTFuseTicksLeft = 80;
1559  m_World->BroadcastSoundEffect("entity.tnt.primed", GetPosition(), 1.0f, 1.0f);
1561  }
1562 }
1563 
1564 
1565 
1566 
1567 
1568 void cMinecartWithTNT::GetDrops(cItems & a_Drops, cEntity * a_Killer)
1569 {
1570  a_Drops.emplace_back(E_ITEM_MINECART_WITH_TNT);
1571 }
1572 
1573 
1574 
1575 
1576 
1577 void cMinecartWithTNT::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
1578 {
1579  Super::Tick(a_Dt, a_Chunk);
1580  if (!IsTicking())
1581  {
1582  return;
1583  }
1584 
1585  if (m_isTNTFused)
1586  {
1587  if (m_TNTFuseTicksLeft > 0)
1588  {
1590  }
1591  else
1592  {
1593  Destroy();
1594  m_World->DoExplosionAt(4.0, GetPosX(), GetPosY() + GetHeight() / 2, GetPosZ(), true, esTNTMinecart, this);
1595  }
1596  }
1597 }
1598 
1599 
1600 
1601 
1602 
1604 // cMinecartWithHopper:
1605 
1607  Super(mpHopper, a_Pos)
1608 {
1609 }
1610 
1611 // TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks
1612 // AND AVARYTHING!!
1613 
1614 
1615 
1616 
1617 
1618 void cMinecartWithHopper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
1619 {
1620  a_Drops.emplace_back(E_ITEM_MINECART_WITH_HOPPER);
1621 }
bool IsBlockRail(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:67
@ E_META_RAIL_CURVED_ZP_XP
Definition: BlockType.h:787
@ E_META_RAIL_ASCEND_ZM
Definition: BlockType.h:785
@ E_META_RAIL_ZM_ZP
Definition: BlockType.h:781
@ E_META_RAIL_ASCEND_XM
Definition: BlockType.h:784
@ E_META_RAIL_CURVED_ZP_XM
Definition: BlockType.h:788
@ E_META_RAIL_ASCEND_XP
Definition: BlockType.h:783
@ E_META_RAIL_XM_XP
Definition: BlockType.h:782
@ E_META_RAIL_ASCEND_ZP
Definition: BlockType.h:786
@ E_META_RAIL_CURVED_ZM_XM
Definition: BlockType.h:789
@ E_META_RAIL_CURVED_ZM_XP
Definition: BlockType.h:790
@ E_BLOCK_ACTIVATOR_RAIL
Definition: BlockType.h:174
@ E_BLOCK_POWERED_RAIL
Definition: BlockType.h:37
@ E_BLOCK_DETECTOR_RAIL
Definition: BlockType.h:38
@ E_BLOCK_RAIL
Definition: BlockType.h:79
@ E_ITEM_COAL
Definition: BlockType.h:307
@ E_ITEM_CHEST_MINECART
Definition: BlockType.h:387
@ E_ITEM_MINECART_WITH_TNT
Definition: BlockType.h:453
@ E_ITEM_MINECART
Definition: BlockType.h:372
@ E_ITEM_MINECART_WITH_HOPPER
Definition: BlockType.h:454
@ E_ITEM_FURNACE_MINECART
Definition: BlockType.h:388
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:44
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
@ esTNTMinecart
Definition: Defines.h:317
#define POSX_TOINT
Definition: Entity.h:31
#define POSZ_TOINT
Definition: Entity.h:33
#define POSY_TOINT
Definition: Entity.h:32
#define DIR_SOUTH_EAST
Definition: Minecart.cpp:27
#define MAX_SPEED_NEGATIVE
Definition: Minecart.cpp:21
#define DIR_NORTH_WEST
Definition: Minecart.cpp:24
#define DIR_SOUTH_WEST
Definition: Minecart.cpp:26
#define DIR_EAST_WEST
Definition: Minecart.cpp:23
#define MAX_SPEED
Definition: Minecart.cpp:20
#define DIR_NORTH_EAST
Definition: Minecart.cpp:25
#define NO_SPEED
Definition: Minecart.cpp:19
#define DIR_NORTH_SOUTH
Definition: Minecart.cpp:22
#define UNREACHABLE(x)
Definition: Globals.h:288
#define VERIFY(x)
Definition: Globals.h:280
unsigned int UInt32
Definition: Globals.h:157
#define ASSERT(x)
Definition: Globals.h:276
Vector3< double > Vector3d
Definition: Vector3.h:485
Vector3< int > Vector3i
Definition: Vector3.h:487
unsigned char Distance(const BlockState Block)
static bool IsSolid(BLOCKTYPE Block)
Is this block solid (player cannot walk through)?
Definition: BlockInfo.cpp:892
Represents two sets of coords, minimum and maximum for each direction.
Definition: BoundingBox.h:24
bool DoesIntersect(const cBoundingBox &a_Other)
Returns true if the two bounding boxes intersect.
Definition: Chunk.h:36
cChunk * GetRelNeighborChunkAdjustCoords(Vector3i &a_RelPos) const
Returns the chunk into which the relatively-specified block belongs.
Definition: Chunk.cpp:1885
static void BlockToChunk(int a_X, int a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords to chunk coords:
Definition: ChunkDef.h:210
static void AbsoluteToRelative(int &a_X, int &a_Y, int &a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords into relative (chunk + block) coords:
Definition: ChunkDef.h:147
static const int Height
Definition: ChunkDef.h:125
void SendSpawnEntity(const cEntity &a_Entity)
void SendEntityMetadata(const cEntity &a_Entity)
cEntity * Attacker
Definition: Entity.h:62
float FinalDamage
Definition: Entity.h:64
Definition: Entity.h:76
const Vector3d & GetSpeed(void) const
Exported in ManualBindings.
Definition: Entity.h:300
void AddPosY(double a_AddPosY)
Definition: Entity.h:240
void AddSpeedX(double a_AddSpeedX)
Definition: Entity.cpp:2203
bool IsPlayer(void) const
Definition: Entity.h:160
cBoundingBox GetBoundingBox() const
Definition: Entity.h:211
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk)
Definition: Entity.cpp:909
cEntity * m_Attachee
The entity which is attached to this entity (rider), nullptr if none.
Definition: Entity.h:591
void SetSpeedX(double a_SpeedX)
Sets the speed in the X axis, leaving the other speed components intact.
Definition: Entity.cpp:2167
void Detach(void)
Detaches from the currently attached entity, if any.
Definition: Entity.cpp:2052
void AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ)
Definition: Entity.cpp:2194
double GetSpeedZ(void) const
Definition: Entity.h:204
void SetYaw(double a_Yaw)
Definition: Entity.cpp:2125
float GetMaxHealth(void) const
Definition: Entity.h:407
void SetHealth(float a_Health)
Sets the health of this entity; doesn't broadcast any hurt animation.
Definition: Entity.cpp:900
static const UInt32 INVALID_ID
Special ID that is considered an "invalid value", signifying no entity.
Definition: Entity.h:128
void SetSpeedY(double a_SpeedY)
Sets the speed in the Y axis, leaving the other speed components intact.
Definition: Entity.cpp:2176
bool IsTicking(void) const
Returns true if the entity is valid and ticking.
Definition: Entity.cpp:2259
void SetSpeedZ(double a_SpeedZ)
Sets the speed in the Z axis, leaving the other speed components intact.
Definition: Entity.cpp:2185
void SetGravity(float a_Gravity)
Definition: Entity.h:282
void SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
Sets the speed of the entity, measured in m / sec.
Definition: Entity.cpp:2157
double GetPosX(void) const
Definition: Entity.h:195
void SetPosY(double a_PosY)
Definition: Entity.h:216
float GetWidth(void) const
Definition: Entity.h:205
cWorld * m_World
Definition: Entity.h:624
double GetPosZ(void) const
Definition: Entity.h:197
bool IsMinecart(void) const
Definition: Entity.h:165
UInt32 GetUniqueID(void) const
Definition: Entity.h:253
void Destroy()
Destroys the entity, schedules it for memory freeing and broadcasts the DestroyEntity packet.
Definition: Entity.cpp:243
void SetInvulnerableTicks(int a_InvulnerableTicks)
Set the invulnerable ticks from the entity.
Definition: Entity.h:516
double GetPosY(void) const
Definition: Entity.h:196
Vector3d m_Speed
Measured in meters / second (m / s)
Definition: Entity.h:577
virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &a_Chunk)
Handles the physics of the entity - updates position based on speed, updates speed based on environme...
Definition: Entity.cpp:1001
void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ)
Definition: Entity.h:242
void AddSpeedZ(double a_AddSpeedZ)
Definition: Entity.cpp:2221
void SetMass(double a_Mass)
Definition: Entity.cpp:2113
bool IsBoat(void) const
Definition: Entity.h:166
float GetHeight(void) const
Definition: Entity.h:193
void SetAirDrag(float a_AirDrag)
Definition: Entity.h:286
virtual void KilledBy(TakeDamageInfo &a_TDI)
Called when the health drops below zero.
Definition: Entity.cpp:851
void SetPosZ(double a_PosZ)
Definition: Entity.h:217
virtual bool DoTakeDamage(TakeDamageInfo &a_TDI)
Makes this entity take damage specified in the a_TDI.
Definition: Entity.cpp:404
virtual void OnRemoveFromWorld(cWorld &a_World)
Called when the entity is removed from a world.
Definition: Entity.cpp:172
void SetPosX(double a_PosX)
Definition: Entity.h:215
void SetMaxHealth(float a_MaxHealth)
Sets the maximum value for the health.
Definition: Entity.cpp:1887
virtual void OnRightClicked(cPlayer &a_Player)
Called when the specified player right-clicks this entity.
Definition: Entity.h:524
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:297
Vector3d GetLookVector(void) const
Definition: Entity.cpp:2267
void AttachTo(cEntity &a_AttachTo)
Attaches to the specified entity; detaches from any previous one first.
Definition: Entity.cpp:2027
virtual void BroadcastMovementUpdate(const cClientHandle *a_Exclude=nullptr)
Updates clients of changes in the entity.
Definition: Entity.cpp:1966
double GetSpeedX(void) const
Definition: Entity.h:202
bool IsMob(void) const
Definition: Entity.h:162
bool operator()(cEntity &a_Entity)
Definition: Minecart.cpp:37
cMinecartAttachCallback(cMinecart &a_Minecart, cEntity *a_Attachee)
Definition: Minecart.cpp:32
cMinecart & m_Minecart
Definition: Minecart.cpp:51
bool operator()(cEntity &a_Entity)
Definition: Minecart.cpp:73
Vector3d GetCollidedEntityPosition(void) const
Definition: Minecart.cpp:109
cMinecartCollisionCallback(Vector3d a_Pos, double a_Height, double a_Width, UInt32 a_UniqueID, UInt32 a_AttacheeUniqueID)
Definition: Minecart.cpp:62
bool FoundIntersection(void) const
Definition: Minecart.cpp:104
bool IsSolidBlockAtOffset(int a_XOffset, int a_YOffset, int a_ZOffset)
Tests if a solid block is at a specific offset of the minecart position.
Definition: Minecart.cpp:836
void SnapToRail(NIBBLETYPE a_RailMeta)
Snaps a mincecart to a rail's axis, resetting its speed For curved rails, it changes the cart's direc...
Definition: Minecart.cpp:697
virtual void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
Handles activator rails.
Definition: Minecart.cpp:687
cMinecart(ePayload a_Payload, Vector3d a_Pos)
Definition: Minecart.cpp:132
bool TestBlockCollision(NIBBLETYPE a_RailMeta)
Tests if a solid block is in front of a cart, and stops the cart (and returns true) if so; returns fa...
Definition: Minecart.cpp:865
void ApplyAcceleration(Vector3d a_ForwardDirection, double a_Acceleration)
Applies an acceleration to the minecart parallel to a_ForwardDirection but without allowing backward ...
Definition: Minecart.cpp:1316
Vector3i m_DetectorRailPosition
Definition: Minecart.h:53
void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
Handles detector rail activation Activates detector rails when a minecart is on them.
Definition: Minecart.cpp:675
bool IsBlockCollisionAtOffset(Vector3i a_Offset)
Definition: Minecart.cpp:845
virtual bool DoTakeDamage(TakeDamageInfo &TDI) override
Makes this entity take damage specified in the a_TDI.
Definition: Minecart.cpp:1249
ePayload
Minecart payload, values correspond to packet subtype.
Definition: Minecart.h:30
bool TestEntityCollision(NIBBLETYPE a_RailMeta)
Tests if this mincecart's bounding box is intersecting another entity's bounding box (collision) and ...
Definition: Minecart.cpp:1082
bool IsSolidBlockAtPosition(Vector3i a_Offset)
Tests if there is a block at the specified position which is impassable to minecarts.
Definition: Minecart.cpp:826
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override
Definition: Minecart.cpp:1298
virtual void SpawnOn(cClientHandle &a_ClientHandle) override
Descendants override this function to send a command to the specified client to spawn the entity on t...
Definition: Minecart.cpp:150
int m_LastDamage
Definition: Minecart.h:52
virtual void KilledBy(TakeDamageInfo &a_TDI) override
Called when the health drops below zero.
Definition: Minecart.cpp:1273
void HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
Handles powered rail physics Each tick, speed up or slow down cart, depending on metadata of rail (po...
Definition: Minecart.cpp:508
bool m_bIsOnDetectorRail
Definition: Minecart.h:54
virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &a_Chunk) override
Handles the physics of the entity - updates position based on speed, updates speed based on environme...
Definition: Minecart.cpp:160
void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt)
Handles physics on normal rails For each tick, slow down on flat rails, speed up or slow down on asce...
Definition: Minecart.cpp:266
virtual void OnRemoveFromWorld(cWorld &a_World) override
Called when the entity is removed from a world.
Definition: Minecart.cpp:1284
virtual void GetDrops(cItems &a_Drops, cEntity *a_Killer=nullptr) override
Returns the list of drops for this pawn when it is killed.
Definition: Minecart.cpp:1350
virtual void OnRightClicked(cPlayer &a_Player) override
Called when the specified player right-clicks this entity.
Definition: Minecart.cpp:1359
cRideableMinecart(Vector3d a_Pos, const cItem &a_Content, int a_ContentHeight)
Definition: Minecart.cpp:1339
virtual void GetDrops(cItems &a_Drops, cEntity *a_Killer=nullptr) override
Returns the list of drops for this pawn when it is killed.
Definition: Minecart.cpp:1405
cItemGrid m_Contents
Definition: Minecart.h:151
virtual void OnRemoveFromWorld(cWorld &a_World) override
Called when the entity is removed from a world.
Definition: Minecart.cpp:1415
virtual void OnRightClicked(cPlayer &a_Player) override
Called when the specified player right-clicks this entity.
Definition: Minecart.cpp:1430
cMinecartWithChest(Vector3d a_Pos)
Definition: Minecart.cpp:1393
void OpenNewWindow(void)
Definition: Minecart.cpp:1454
virtual void OnRightClicked(cPlayer &a_Player) override
Called when the specified player right-clicks this entity.
Definition: Minecart.cpp:1486
virtual void Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk) override
Definition: Minecart.cpp:1508
virtual void GetDrops(cItems &a_Drops, cEntity *a_Killer=nullptr) override
Returns the list of drops for this pawn when it is killed.
Definition: Minecart.cpp:1477
cMinecartWithFurnace(Vector3d a_Pos)
Definition: Minecart.cpp:1466
void Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk) override
Definition: Minecart.cpp:1577
void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) override
Handles activator rails.
Definition: Minecart.cpp:1551
cMinecartWithTNT(Vector3d a_Pos)
Definition: Minecart.cpp:1542
virtual void GetDrops(cItems &a_Drops, cEntity *a_Killer=nullptr) override
Returns the list of drops for this pawn when it is killed.
Definition: Minecart.cpp:1568
int m_TNTFuseTicksLeft
Definition: Minecart.h:227
cMinecartWithHopper(Vector3d a_Pos)
Definition: Minecart.cpp:1606
virtual void GetDrops(cItems &a_Drops, cEntity *a_Killer=nullptr) override
Returns the list of drops for this pawn when it is killed.
Definition: Minecart.cpp:1618
Definition: Player.h:29
const cItem & GetEquippedItem(void) const
Definition: Player.h:162
void OpenWindow(cWindow &a_Window)
Opens the specified window; closes the current one first using CloseWindow()
Definition: Player.cpp:1123
bool IsGameModeCreative(void) const
Returns true if the player is in Creative mode, either explicitly, or by inheriting from current worl...
Definition: Player.cpp:1025
cInventory & GetInventory(void)
Definition: Player.h:156
bool IsGameModeSpectator(void) const
Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current wor...
Definition: Player.cpp:1052
cWindow * GetWindow(void)
Definition: Player.h:262
bool RemoveOneEquippedItem(void)
Removes one item out of the currently equipped item stack, returns true if successful,...
Definition: Inventory.cpp:232
Definition: Item.h:37
short m_ItemType
Definition: Item.h:163
This class bridges a vector of cItem for safe access via Lua.
Definition: Item.h:215
void AddListener(cListener &a_Listener)
Adds a callback that gets called whenever a slot changes.
Definition: ItemGrid.cpp:809
void CopyToItems(cItems &a_Items) const
Copies the contents into a cItems object; preserves the original a_Items contents.
Definition: ItemGrid.cpp:703
Represents a UI window.
Definition: Window.h:54
cWindow * GetWindow(void) const
Definition: WindowOwner.h:40
void OpenWindow(cWindow *a_Window)
Definition: WindowOwner.h:34
Window owner that is associated with an entity (chest minecart etc.)
Definition: WindowOwner.h:82
T x
Definition: Vector3.h:17
T Dot(const Vector3< T > &a_Rhs) const
Definition: Vector3.h:110
Vector3< int > Floor(void) const
Returns a new Vector3i with coords set to std::floor() of this vector's coords.
Definition: Vector3.h:177
T y
Definition: Vector3.h:17
void Clamp(T a_Min, T a_Max)
Clamps each coord into the specified range.
Definition: Vector3.h:124
T z
Definition: Vector3.h:17
Definition: World.h:53
virtual void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void *a_SourceData) override
Does an explosion with the specified strength at the specified coordinates.
Definition: World.cpp:1384
virtual void BroadcastEntityMetadata(const cEntity &a_Entity, const cClientHandle *a_Exclude=nullptr) override
virtual void BroadcastEntityAnimation(const cEntity &a_Entity, EntityAnimation a_Animation, const cClientHandle *a_Exclude=nullptr) override
BLOCKTYPE GetBlock(Vector3i a_BlockPos) const
Returns the block type at the specified position.
Definition: World.h:363
virtual bool ForEachEntityInBox(const cBoundingBox &a_Box, cEntityCallback a_Callback) override
Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
Definition: World.cpp:2445
NIBBLETYPE GetBlockMeta(Vector3i a_BlockPos) const
Returns the block meta at the specified position.
Definition: World.h:370
virtual void BroadcastSoundEffect(const AString &a_SoundName, Vector3d a_Position, float a_Volume, float a_Pitch, const cClientHandle *a_Exclude=nullptr) override
void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData)
Sets the meta for the specified block, while keeping the blocktype.
Definition: World.cpp:1752
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback a_Callback)
Calls the callback for each entity in the specified chunk; returns true if all entities processed,...
Definition: World.cpp:2436
void SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Sets the block at the specified coords to the specified value.
Definition: World.cpp:1743