Cuberite
A lightweight, fast and extensible game server for Minecraft
FurnaceEntity.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
3 
4 #include "FurnaceEntity.h"
5 #include "../UI/FurnaceWindow.h"
6 #include "../Entities/Player.h"
7 #include "../Root.h"
8 #include "../Chunk.h"
9 
10 
11 
12 
13 
14 enum
15 {
19 } ;
20 
21 
22 
23 
24 
25 cFurnaceEntity::cFurnaceEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
26  super(a_BlockType, a_BlockMeta, a_Pos, ContentsWidth, ContentsHeight, a_World),
27  m_CurrentRecipe(nullptr),
28  m_IsDestroyed(false),
29  m_IsCooking(a_BlockType == E_BLOCK_LIT_FURNACE),
30  m_NeedCookTime(0),
31  m_TimeCooked(0),
32  m_FuelBurnTime(0),
33  m_TimeBurned(0),
34  m_RewardCounter(0),
35  m_IsLoading(false)
36 {
37  m_Contents.AddListener(*this);
38 }
39 
40 
41 
42 
43 
45 {
46  // Tell window its owner is destroyed
47  cWindow * Window = GetWindow();
48  if (Window != nullptr)
49  {
50  Window->OwnerDestroyed();
51  }
52 }
53 
54 
55 
56 
57 
59 {
60  m_IsDestroyed = true;
62 }
63 
64 
65 
66 
67 
69 {
70  super::CopyFrom(a_Src);
71  auto & src = static_cast<const cFurnaceEntity &>(a_Src);
72  m_Contents.CopyFrom(src.m_Contents);
73  m_CurrentRecipe = src.m_CurrentRecipe;
74  m_FuelBurnTime = src.m_FuelBurnTime;
75  m_IsCooking = src.m_IsCooking;
76  m_IsDestroyed = src.m_IsDestroyed;
77  m_IsLoading = src.m_IsLoading;
78  m_LastInput = src.m_LastInput;
79  m_NeedCookTime = src.m_NeedCookTime;
80  m_TimeBurned = src.m_TimeBurned;
81  m_TimeCooked = src.m_TimeCooked;
82 }
83 
84 
85 
86 
87 
89 {
90  // Nothing needs to be sent
91  UNUSED(a_Client);
92 }
93 
94 
95 
96 
97 
98 bool cFurnaceEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
99 {
100  UNUSED(a_Dt);
101 
102  if (m_FuelBurnTime <= 0)
103  {
104  // If a furnace is out of fuel, the progress bar reverses at twice the speed of cooking.
105  m_TimeCooked = std::max((m_TimeCooked - 2), 0);
106 
107  // Reset progressbars, block type, and bail out
111  return false;
112  }
113 
114  if (m_IsCooking)
115  {
116  m_TimeCooked++;
118  {
119  // Finished smelting one item
120  FinishOne();
121  }
122  }
123 
124  m_TimeBurned++;
126  {
127  // The current fuel has been exhausted, use another one, if possible
128  BurnNewFuel();
129  }
130 
132 
133  return true;
134 }
135 
136 
137 
138 
139 
141 {
142  cWindow * Window = GetWindow();
143  if (Window == nullptr)
144  {
145  OpenWindow(new cFurnaceWindow(this));
146  Window = GetWindow();
147  }
148 
149  if (Window != nullptr)
150  {
151  if (a_Player->GetWindow() != Window)
152  {
153  a_Player->OpenWindow(*Window);
154  }
155  }
156 
157  UpdateProgressBars(true);
158  return true;
159 }
160 
161 
162 
163 
164 
166 {
167  UpdateInput();
168  UpdateFuel();
169  return m_IsCooking;
170 }
171 
172 
173 
174 
175 
177 {
178  int Reward = FloorC(m_RewardCounter);
179  float Remainder = m_RewardCounter - static_cast<float>(Reward);
180  // Remainder is used as the percent chance of getting an extra xp point
181  if (GetRandomProvider().RandBool(Remainder))
182  {
183  Reward++;
184  }
185  m_RewardCounter = 0.0;
186  return Reward;
187 }
188 
189 
190 
191 
192 
193 void cFurnaceEntity::BroadcastProgress(short a_ProgressbarID, short a_Value)
194 {
195  cWindow * Window = GetWindow();
196  if (Window != nullptr)
197  {
198  Window->SetProperty(a_ProgressbarID, a_Value);
199  }
200 }
201 
202 
203 
204 
205 
207 {
208  m_TimeCooked = 0;
210 
212  {
214  }
215  else
216  {
218  }
220 }
221 
222 
223 
224 
225 
227 {
229  int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel));
230  if ((NewTime == 0) || !CanCookInputToOutput())
231  {
232  // The item in the fuel slot is not suitable
233  // or the input and output isn't available for cooking
234  SetBurnTimes(0, 0);
235  SetIsCooking(false);
236  return;
237  }
238 
239  // Burn one new fuel:
240  SetBurnTimes(NewTime, 0);
241  SetIsCooking(true);
243  {
245  }
246  else
247  {
249  }
250 }
251 
252 
253 
254 
255 
256 void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
257 {
258  super::OnSlotChanged(a_ItemGrid, a_SlotNum);
259 
260  if (m_IsDestroyed)
261  {
262  return;
263  }
264 
265  if (m_IsLoading)
266  {
267  return;
268  }
269 
270  ASSERT(a_ItemGrid == &m_Contents);
271  switch (a_SlotNum)
272  {
273  case fsInput: UpdateInput(); break;
274  case fsFuel: UpdateFuel(); break;
275  case fsOutput: UpdateOutput(); break;
276  default: ASSERT(!"Invalid furnace slot update!"); break;
277  }
278 }
279 
280 
281 
282 
283 
285 {
287  {
288  // The input is different from what we had before, reset the cooking time
289  if (!m_IsLoading)
290  {
291  m_TimeCooked = 0;
292  }
293  }
295 
298  if (!CanCookInputToOutput())
299  {
300  // This input cannot be cooked, reset cook counter immediately
301  SetCookTimes(0, 0);
302  SetIsCooking(false);
303  }
304  else
305  {
307 
308  // Start burning new fuel if there's no flame now:
309  if (GetFuelBurnTimeLeft() <= 0)
310  {
311  BurnNewFuel();
312  }
313  // Already burning, set cooking to ensure that cooking is occuring
314  else
315  {
316  SetIsCooking(true);
317  }
318  }
319 }
320 
321 
322 
323 
324 
326 {
328  {
329  // The current fuel is still burning, don't modify anything:
330  return;
331  }
332 
333  // The current fuel is spent, try to burn some more:
334  BurnNewFuel();
335 }
336 
337 
338 
339 
340 
342 {
343  if (!CanCookInputToOutput())
344  {
345  // Cannot cook anymore:
346  SetCookTimes(0, 0);
347  SetIsCooking(false);
348  return;
349  }
350 
351  // Can cook, start cooking if not already underway:
353 
354  // Check if fuel needs to start a burn
355  if (GetFuelBurnTimeLeft() <= 0)
356  {
357  BurnNewFuel();
358  }
359  // Already burning, set cooking to ensure that cooking is occuring
360  else
361  {
362  SetIsCooking(true);
363  }
364 }
365 
366 
367 
368 
369 
371 {
372  if (m_CurrentRecipe == nullptr)
373  {
374  // This input cannot be cooked
375  return false;
376  }
377 
378  const cItem & Slot = m_Contents.GetSlot(fsOutput);
379  if (Slot.IsEmpty())
380  {
381  // The output is empty, can cook
382  return true;
383  }
384 
385  if (!Slot.IsEqual(*m_CurrentRecipe->Out))
386  {
387  // The output slot is blocked with something that cannot be stacked with the recipe's output
388  return false;
389  }
390 
391  if (Slot.IsFullStack())
392  {
393  // Cannot add any more items to the output slot
394  return false;
395  }
396 
397  return true;
398 }
399 
400 
401 
402 
403 
404 void cFurnaceEntity::UpdateProgressBars(bool a_ForceUpdate)
405 {
406  // In order to preserve bandwidth, an update is sent only every 10th tick
407  if (!a_ForceUpdate && (m_World->GetWorldAge() % 10 != 0))
408  {
409  return;
410  }
411 
412  int CurFuel = (m_FuelBurnTime > 0) ? 200 - (200 * m_TimeBurned / m_FuelBurnTime) : 0;
413  BroadcastProgress(PROGRESSBAR_FUEL, static_cast<short>(CurFuel));
414 
415  int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0;
416  BroadcastProgress(PROGRESSBAR_SMELTING_CONFIRM, 200); // Post 1.8, Mojang requires a random packet with an ID of three and value of 200. Wat. Wat. Wat.
417  BroadcastProgress(PROGRESSBAR_SMELTING, static_cast<short>(CurCook));
418 }
419 
420 
421 
422 
423 
424 void cFurnaceEntity::SetIsCooking(bool a_IsCooking)
425 {
426  if (a_IsCooking == m_IsCooking)
427  {
428  return;
429  }
430  m_IsCooking = a_IsCooking;
431 
432  // Only light the furnace as it is extinguished only when the fuel runs out, not when cooking stops - handled in this::Tick()
433  if (m_IsCooking)
434  {
437  }
438 }
void SetCookTimes(int a_NeedCookTime, int a_TimeCooked)
float Reward
Experience reward for creating 1 of this item.
Definition: FurnaceRecipe.h:33
NIBBLETYPE m_BlockMeta
The block meta representing this particular instance in the world Mainly used for directional entitie...
Definition: BlockEntity.h:153
void UpdateInput(void)
Updates the recipe, based on the current input.
float m_RewardCounter
Running total of experience that can be picked up.
int m_NeedCookTime
Amount of ticks needed to fully cook current item.
bool ContinueCooking(void)
Restarts cooking Used after the furnace is loaded from storage to set up the internal variables so th...
bool IsFullStack(void) const
Returns true if the item is stacked up to its maximum stacking.
Definition: Item.cpp:119
virtual void Destroy() override
void CopyFrom(const cItemGrid &a_Src)
Copies all items from a_Src to this grid.
Definition: ItemGrid.cpp:83
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
MTRand & GetRandomProvider()
Returns the current thread&#39;s random number source.
Definition: FastRandom.cpp:20
cWindow * GetWindow(void)
Definition: Player.h:239
Definition: Player.h:27
virtual void CopyFrom(const cBlockEntity &a_Src) override
Copies all properties of a_Src into this entity, except for its m_World and location.
int ChangeSlotCount(int a_SlotNum, int a_AddToCount)
Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot...
Definition: ItemGrid.cpp:443
bool IsEmpty(void) const
Definition: Item.h:116
void OpenWindow(cWindow *a_Window)
Definition: WindowOwner.h:34
void OwnerDestroyed(void)
Definition: Window.cpp:352
BLOCKTYPE m_BlockType
The blocktype representing this particular instance in the world.
Definition: BlockEntity.h:149
void BurnNewFuel(void)
Starts burning a new fuel, if possible.
bool IsEqual(const cItem &a_Item) const
Definition: Item.h:123
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk &a_Chunk) override
Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking...
virtual void Destroy(void) override
virtual void OnSlotChanged(cItemGrid *a_ItemGrid, int a_SlotNum) override
Called whenever a slot changes.
Definition: Chunk.h:49
virtual ~cFurnaceEntity() override
cWorld * m_World
Definition: BlockEntity.h:155
void AddListener(cListener &a_Listener)
Adds a callback that gets called whenever a slot changes.
Definition: ItemGrid.cpp:785
cFurnaceEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld *a_World)
Constructor used for normal operation.
int GetAndResetReward(void)
Calculates, resets, and returns the experience reward in this furnace.
cWindow * GetWindow(void) const
Definition: WindowOwner.h:40
Vector3i GetRelPos() const
Definition: BlockEntity.h:109
int CookTime
How long this recipe takes to smelt, in ticks.
Definition: FurnaceRecipe.h:32
bool m_IsDestroyed
Set to true when the furnace entity has been destroyed to prevent the block being set again...
Definition: World.h:65
char m_ItemCount
Definition: Item.h:210
const cFurnaceRecipe::cRecipe * m_CurrentRecipe
The recipe for the current input slot.
void BroadcastProgress(short a_ProgressbarID, short a_Value)
Sends the specified progressbar value to all clients of the window.
virtual void SendTo(cClientHandle &a_Client) override
Sends the packet defining the block entity to the client specified.
int GetBurnTime(const cItem &a_Fuel) const
Returns the amount of time that the specified fuel burns, in ticks.
virtual Int64 GetWorldAge(void) const override
Definition: World.h:109
#define ASSERT(x)
Definition: Globals.h:335
Vector3i m_Pos
Position in absolute block coordinates.
Definition: BlockEntity.h:142
bool m_IsLoading
Is the block currently being loaded into the world?
int GetFuelBurnTimeLeft(void) const
Returns the time until the current fuel is depleted, in ticks.
Definition: FurnaceEntity.h:87
#define UNUSED
Definition: Globals.h:152
int m_TimeCooked
Amount of ticks that the current item has been cooking.
cItem m_LastInput
The item that is being smelted.
virtual void SetProperty(short a_Property, short a_Value)
Updates a numerical property associated with the window.
Definition: Window.cpp:751
void UpdateFuel(void)
Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropria...
int m_FuelBurnTime
Amount of ticks that the current fuel can burn (in total); zero if no fuel burning.
static cRoot * Get()
Definition: Root.h:51
void FinishOne()
One item finished cooking.
bool RandBool(double a_TrueProbability=0.5)
Return a random bool with the given probability of being true.
Definition: FastRandom.h:158
void FastSetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Sets the block at the specified coords to the specified value.
Definition: World.h:400
Represents a UI window.
Definition: Window.h:53
virtual void OnSlotChanged(cItemGrid *a_Grid, int a_SlotNum) override
Called whenever a slot changes.
cFurnaceRecipe * GetFurnaceRecipe(void)
Definition: Root.h:91
void SetIsCooking(bool a_IsCooking)
Sets the m_IsCooking variable, updates the furnace block type based on the value. ...
virtual void CopyFrom(const cBlockEntity &a_Src) override
Copies all properties of a_Src into this entity, except for its m_World and location.
short m_ItemType
Definition: Item.h:209
const cItem & GetSlot(int a_X, int a_Y) const
Definition: ItemGrid.cpp:96
bool m_IsCooking
Set to true if the furnace is cooking an item.
std::enable_if< std::is_arithmetic< T >::value, C >::type FloorC(T a_Value)
Floors a value, then casts it to C (an int by default)
Definition: Globals.h:362
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients=true)
Definition: Chunk.cpp:1402
int m_TimeBurned
Amount of ticks that the current fuel has been burning.
void UpdateProgressBars(bool a_ForceUpdate=false)
Broadcasts progressbar updates, if needed.
Definition: Item.h:36
void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned)
Definition: FurnaceEntity.h:97
void OpenWindow(cWindow &a_Window)
Opens the specified window; closes the current one first using CloseWindow()
Definition: Player.cpp:1349
virtual bool UsedBy(cPlayer *a_Player) override
Called when a player uses this entity; should open the UI window.
void SetSlot(int a_X, int a_Y, const cItem &a_Item)
Definition: ItemGrid.cpp:121
void UpdateOutput(void)
Called when the output slot changes.
bool CanCookInputToOutput(void) const
Returns true if the input can be cooked into output and the item counts allow for another cooking ope...
const cRecipe * GetRecipeFrom(const cItem &a_Ingredient) const
Returns a recipe for the specified input, nullptr if no recipe found.