Cuberite
A lightweight, fast and extensible game server for Minecraft
BlockEndPortalFrame.h
Go to the documentation of this file.
1 
2 #pragma once
3 
4 #include "BlockHandler.h"
5 
6 
7 
8 
9 
11  public cMetaRotator<cBlockHandler, 0x03,
12  E_META_END_PORTAL_FRAME_ZM,
13  E_META_END_PORTAL_FRAME_XP,
14  E_META_END_PORTAL_FRAME_ZP,
15  E_META_END_PORTAL_FRAME_XM
16  >
17 {
18 public:
25  >(a_BlockType)
26  {
27  }
28 
29 
30 
31 
32 
34  cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
35  int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
36  int a_CursorX, int a_CursorY, int a_CursorZ,
37  BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
38  ) override
39  {
40  a_BlockType = m_BlockType;
41  a_BlockMeta = YawToMetaData(a_Player.GetYaw());
42  return true;
43  }
44 
45 
46 
47 
48 
49  inline static NIBBLETYPE YawToMetaData(double a_Rotation)
50  {
51  a_Rotation += 90 + 45; // So its not aligned with axis
52  if (a_Rotation > 360)
53  {
54  a_Rotation -= 360;
55  }
56 
57  if ((a_Rotation >= 0) && (a_Rotation < 90))
58  {
60  }
61  else if ((a_Rotation >= 180) && (a_Rotation < 270))
62  {
64  }
65  else if ((a_Rotation >= 90) && (a_Rotation < 180))
66  {
68  }
69  else
70  {
72  }
73  }
74 
75 
76 
77 
78 
79  virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
80  {
81  // E_META_END_PORTAL_FRAME_EYE is the bit which signifies the eye of ender is in it.
82  // LOG("PortalPlaced, meta %d", a_BlockMeta);
83  if ((a_BlockMeta & E_META_END_PORTAL_FRAME_EYE) == E_META_END_PORTAL_FRAME_EYE)
84  {
85  // LOG("Location is %d %d %d", a_BlockX, a_BlockY, a_BlockZ);
86  // Direction is the first two bits, masked by 0x3
87  FindAndSetPortal(a_BlockPos, a_BlockMeta & 3, a_ChunkInterface, a_WorldInterface);
88  }
89  }
90 
91 
92 
93 
94 
96  bool FindAndSetPortal(Vector3i a_FirstFrame, NIBBLETYPE a_Direction, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface)
97  {
98  /*
99  PORTAL FINDING ALGORITH
100  =======================
101  - Get clicked base block
102  - Check diagonally (clockwise) for another portal block
103  - if exists, and has eye, Continue. Abort if any are facing the wrong direction.
104  - if doesn't exist, check horizontally (the block to the left of this block). Abort if there is no horizontal block.
105  - After a corner has been met, traverse the portal clockwise, ensuring valid portal frames connect the rectangle.
106  - Track the NorthWest Corner, and the dimensions.
107  - If dimensions are valid, create the portal.
108  */
109 
110  static_assert((E_META_END_PORTAL_FRAME_ZM - E_META_END_PORTAL_FRAME_XM) == 1, "Should be going clockwise");
111 
112  const int MIN_PORTAL_WIDTH = 3;
113  const int MAX_PORTAL_WIDTH = 4;
114 
115  // Directions to use for the clockwise traversal.
116  static const Vector3i Left[] =
117  {
118  { 1, 0, 0}, // 0, South, left block is East / XP
119  { 0, 0, 1}, // 1, West, left block is South / ZP
120  {-1, 0, 0}, // 2, North, left block is West / XM
121  { 0, 0, -1}, // 3, East, left block is North / ZM
122  };
123  static const Vector3i LeftForward[] =
124  {
125  { 1, 0, 1}, // 0, South, left block is SouthEast / XP ZP
126  {-1, 0, 1}, // 1, West, left block is SouthWest / XM ZP
127  {-1, 0, -1}, // 2, North, left block is NorthWest / XM ZM
128  { 1, 0, -1}, // 3, East, left block is NorthEast / XP ZM
129  };
130 
131 
132  int EdgesComplete = -1; // We start search _before_ finding the first edge
133  Vector3i NorthWestCorner;
134  int EdgeWidth[4] = { 1, 1, 1, 1 };
135  NIBBLETYPE CurrentDirection = a_Direction;
136  Vector3i CurrentPos = a_FirstFrame;
137 
138  // Scan clockwise until we have seen all 4 edges
139  while (EdgesComplete < 4)
140  {
141  // Check if we are at a corner
142  Vector3i NextPos = CurrentPos + LeftForward[CurrentDirection];
143  if (IsPortalFrame(a_ChunkInterface.GetBlock(NextPos)))
144  {
145  // We have found the corner, move clockwise to next edge
146  if (CurrentDirection == E_META_END_PORTAL_FRAME_XP)
147  {
148  // We are on the NW (XM, ZM) Corner
149  // Relative to the previous frame, the portal should appear to the right of this portal frame.
150  NorthWestCorner = NextPos - Left[CurrentDirection];
151  }
152 
153  if (EdgesComplete == -1)
154  {
155  // Reset current width, we will revisit it last
156  EdgeWidth[CurrentDirection] = 1;
157  }
158 
159  // Rotate 90 degrees clockwise
160  CurrentDirection = (CurrentDirection + 1) % 4;
161  EdgesComplete++;
162  }
163  else
164  {
165  // We are not at a corner, keep walking the edge
166  NextPos = CurrentPos + Left[CurrentDirection];
167 
168  EdgeWidth[CurrentDirection]++;
169  if (EdgeWidth[CurrentDirection] > MAX_PORTAL_WIDTH)
170  {
171  // Don't build a portal that is too long.
172  return false;
173  }
174  }
175 
176  if (!IsValidFrameAtPos(a_ChunkInterface, NextPos, CurrentDirection))
177  {
178  // Neither the edge nor the corner are valid portal blocks.
179  return false;
180  }
181 
182  CurrentPos = NextPos;
183  }
184 
185  if ((EdgeWidth[0] != EdgeWidth[2]) || (EdgeWidth[1] != EdgeWidth[3]))
186  {
187  // Mismatched Portal Dimensions.
188  return false;
189  }
190  if ((EdgeWidth[0] < MIN_PORTAL_WIDTH) || (EdgeWidth[1] < MIN_PORTAL_WIDTH))
191  {
192  // Portal too small.
193  return false;
194  }
195 
196  // LOG("NW corner (low corner) %d %d %d", Corner.x, Corner.y, Corner.z);
197  // LOG("%d by %d", Width[0], Width[1]);
198  for (int i = 0; i < EdgeWidth[0]; i++)
199  {
200  for (int j = 0; j < EdgeWidth[1]; j++)
201  {
202  a_ChunkInterface.SetBlock(NorthWestCorner.x + i, NorthWestCorner.y, NorthWestCorner.z + j, E_BLOCK_END_PORTAL, 0);
203  // TODO: Create block entity so portal doesn't become invisible on relog.
204  }
205  }
206  return true;
207  }
208 
209 
210 
211 
212 
214  bool IsValidFrameAtPos(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, NIBBLETYPE a_ShouldFace)
215  {
216  BLOCKTYPE BlockType;
217  NIBBLETYPE BlockMeta;
218 
219  return (
220  a_ChunkInterface.GetBlockTypeMeta(a_BlockPos, BlockType, BlockMeta) &&
221  (BlockType == E_BLOCK_END_PORTAL_FRAME) &&
222  (BlockMeta == (a_ShouldFace | E_META_END_PORTAL_FRAME_EYE))
223  );
224  }
225 
226 
227 
228 
230  bool IsPortalFrame(BLOCKTYPE BlockType)
231  {
232  return (BlockType == E_BLOCK_END_PORTAL_FRAME);
233  }
234 
235 
236 
237 
238 
239  virtual bool IsClickedThrough(void) override
240  {
241  // TODO: Colision
242  return true;
243  }
244 
245 
246 
247 
248 
249  virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
250  {
251  UNUSED(a_Meta);
252  return 27;
253  }
254 };
255 
256 
257 
258 
void SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Sets the block at the specified coords to the specified value.
T x
Definition: Vector3.h:17
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
virtual bool IsClickedThrough(void) override
Indicates whether the client will click through this block.
bool GetBlockTypeMeta(Vector3i a_Pos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta)
bool IsPortalFrame(BLOCKTYPE BlockType)
Return true if this block is a portal frame.
Definition: Player.h:27
BLOCKTYPE m_BlockType
Definition: BlockHandler.h:213
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
cBlockEndPortalFrameHandler(BLOCKTYPE a_BlockType)
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
Mixin for rotations and reflections following the standard pattern of "apply mask, then use a switch".
Definition: Mixins.h:84
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
Returns the base colour ID of the block, as will be represented on a map, as per documentation: https...
bool FindAndSetPortal(Vector3i a_FirstFrame, NIBBLETYPE a_Direction, cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface)
Returns false if portal cannot be made, true if portal was made.
#define UNUSED
Definition: Globals.h:152
virtual bool GetPlacementBlockTypeMeta(cChunkInterface &a_ChunkInterface, cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) override
Called before a block is placed into a world.
virtual void OnPlaced(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
Called by cWorld::SetBlock() after the block has been set.
double GetYaw(void) const
Definition: Entity.h:209
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc...
Definition: Defines.h:29
BLOCKTYPE GetBlock(Vector3i a_Pos)
Byte ColourID
Definition: Globals.h:118
bool IsValidFrameAtPos(cChunkInterface &a_ChunkInterface, Vector3i a_BlockPos, NIBBLETYPE a_ShouldFace)
Return true if this block is a portal frame, has an eye, and is facing the correct direction...
static NIBBLETYPE YawToMetaData(double a_Rotation)