Cuberite
A lightweight, fast and extensible game server for Minecraft
EntityEffect.cpp
Go to the documentation of this file.
1 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2 
3 #include "EntityEffect.h"
4 #include "Player.h"
5 #include "../Chunk.h"
6 #include "../Mobs/Monster.h"
7 
8 
9 
10 
11 
12 int cEntityEffect::GetPotionColor(short a_ItemDamage)
13 {
14  // Lowest six bits
15  return (a_ItemDamage & 0x3f);
16 }
17 
18 
19 
20 
21 
23 {
24  // Lowest four bits
25  // Potion effect bits are different from entity effect values
26  // For reference: https://minecraft.wiki/w/Java_Edition_data_values#.22Potion_effect.22_bits
27  switch (a_ItemDamage & 0x0f)
28  {
29  case 0x01: return cEntityEffect::effRegeneration;
30  case 0x02: return cEntityEffect::effSpeed;
31  case 0x03: return cEntityEffect::effFireResistance;
32  case 0x04: return cEntityEffect::effPoison;
33  case 0x05: return cEntityEffect::effInstantHealth;
34  case 0x06: return cEntityEffect::effNightVision;
35  case 0x08: return cEntityEffect::effWeakness;
36  case 0x09: return cEntityEffect::effStrength;
37  case 0x0a: return cEntityEffect::effSlowness;
38  case 0x0b: return cEntityEffect::effJumpBoost;
39  case 0x0c: return cEntityEffect::effInstantDamage;
40  case 0x0d: return cEntityEffect::effWaterBreathing;
41  case 0x0e: return cEntityEffect::effInvisibility;
42 
43  // No effect potions
44  case 0x00:
45  case 0x07:
46  case 0x0f:
47  {
48  break;
49  }
50  }
52 }
53 
54 
55 
56 
57 
58 short cEntityEffect::GetPotionEffectIntensity(short a_ItemDamage)
59 {
60  // Level II potion if the fifth lowest bit is set
61  return ((a_ItemDamage & 0x20) != 0) ? 1 : 0;
62 }
63 
64 
65 
66 
67 
69 {
70  // Base duration in ticks
71  int base = 0;
72  double TierCoeff = 1, ExtCoeff = 1, SplashCoeff = 1;
73 
74  switch (GetPotionEffectType(a_ItemDamage))
75  {
78  {
79  base = 900;
80  break;
81  }
82 
90  {
91  base = 3600;
92  break;
93  }
94 
97  {
98  base = 1800;
99  break;
100  }
101  default: break;
102  }
103 
104  // If potion is level II, half the duration. If not, stays the same
105  TierCoeff = (GetPotionEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1;
106 
107  // If potion is extended, multiply duration by 8 / 3. If not, stays the same
108  // Extended potion if sixth lowest bit is set
109  ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1;
110 
111  // If potion is splash potion, multiply duration by 3 / 4. If not, stays the same
112  SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75;
113 
114  // Ref.:
115  // https://minecraft.wiki/w/Java_Edition_data_values#.22Tier.22_bit
116  // https://minecraft.wiki/w/Java_Edition_data_values#.22Extended_duration.22_bit
117  // https://minecraft.wiki/w/Java_Edition_data_values#.22Splash_potion.22_bit
118 
119  return static_cast<int>(base * TierCoeff * ExtCoeff * SplashCoeff);
120 }
121 
122 
123 
124 
125 
126 bool cEntityEffect::IsPotionDrinkable(short a_ItemDamage)
127 {
128  // Potions are drinkable if they are not splash potions.
129  // i.e. potions are drinkable if the 14th lowest bit is not set
130  // Ref.: https://minecraft.wiki/w/Java_Edition_data_values#.22Splash_potion.22_bit
131  return ((a_ItemDamage & 0x4000) == 0);
132 }
133 
134 
135 
136 
137 
139  m_Ticks(0),
140  m_Duration(0),
141  m_Intensity(0),
142  m_DistanceModifier(1)
143 {
144 
145 }
146 
147 
148 
149 
150 
151 cEntityEffect::cEntityEffect(int a_Duration, short a_Intensity, double a_DistanceModifier):
152  m_Ticks(0),
153  m_Duration(a_Duration),
154  m_Intensity(a_Intensity),
155  m_DistanceModifier(a_DistanceModifier)
156 {
157 
158 }
159 
160 
161 
162 
163 
165  m_Ticks(a_OtherEffect.m_Ticks),
166  m_Duration(a_OtherEffect.m_Duration),
167  m_Intensity(a_OtherEffect.m_Intensity),
168  m_DistanceModifier(a_OtherEffect.m_DistanceModifier)
169 {
170 
171 }
172 
173 
174 
175 
176 
178 {
179  std::swap(m_Ticks, a_OtherEffect.m_Ticks);
180  std::swap(m_Duration, a_OtherEffect.m_Duration);
181  std::swap(m_Intensity, a_OtherEffect.m_Intensity);
182  std::swap(m_DistanceModifier, a_OtherEffect.m_DistanceModifier);
183  return *this;
184 }
185 
186 
187 
188 
189 
190 std::unique_ptr<cEntityEffect> cEntityEffect::CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier)
191 {
192  switch (a_EffectType)
193  {
194  case cEntityEffect::effNoEffect: return std::make_unique<cEntityEffect >(a_Duration, a_Intensity, a_DistanceModifier);
195 
196  case cEntityEffect::effAbsorption: return std::make_unique<cEntityEffectAbsorption >(a_Duration, a_Intensity, a_DistanceModifier);
197  case cEntityEffect::effBlindness: return std::make_unique<cEntityEffectBlindness >(a_Duration, a_Intensity, a_DistanceModifier);
198  case cEntityEffect::effFireResistance: return std::make_unique<cEntityEffectFireResistance>(a_Duration, a_Intensity, a_DistanceModifier);
199  case cEntityEffect::effHaste: return std::make_unique<cEntityEffectHaste >(a_Duration, a_Intensity, a_DistanceModifier);
200  case cEntityEffect::effHealthBoost: return std::make_unique<cEntityEffectHealthBoost >(a_Duration, a_Intensity, a_DistanceModifier);
201  case cEntityEffect::effHunger: return std::make_unique<cEntityEffectHunger >(a_Duration, a_Intensity, a_DistanceModifier);
202  case cEntityEffect::effInstantDamage: return std::make_unique<cEntityEffectInstantDamage >(a_Duration, a_Intensity, a_DistanceModifier);
203  case cEntityEffect::effInstantHealth: return std::make_unique<cEntityEffectInstantHealth >(a_Duration, a_Intensity, a_DistanceModifier);
204  case cEntityEffect::effInvisibility: return std::make_unique<cEntityEffectInvisibility >(a_Duration, a_Intensity, a_DistanceModifier);
205  case cEntityEffect::effJumpBoost: return std::make_unique<cEntityEffectJumpBoost >(a_Duration, a_Intensity, a_DistanceModifier);
206  case cEntityEffect::effMiningFatigue: return std::make_unique<cEntityEffectMiningFatigue >(a_Duration, a_Intensity, a_DistanceModifier);
207  case cEntityEffect::effNausea: return std::make_unique<cEntityEffectNausea >(a_Duration, a_Intensity, a_DistanceModifier);
208  case cEntityEffect::effNightVision: return std::make_unique<cEntityEffectNightVision >(a_Duration, a_Intensity, a_DistanceModifier);
209  case cEntityEffect::effPoison: return std::make_unique<cEntityEffectPoison >(a_Duration, a_Intensity, a_DistanceModifier);
210  case cEntityEffect::effRegeneration: return std::make_unique<cEntityEffectRegeneration >(a_Duration, a_Intensity, a_DistanceModifier);
211  case cEntityEffect::effResistance: return std::make_unique<cEntityEffectResistance >(a_Duration, a_Intensity, a_DistanceModifier);
212  case cEntityEffect::effSaturation: return std::make_unique<cEntityEffectSaturation >(a_Duration, a_Intensity, a_DistanceModifier);
213  case cEntityEffect::effSlowness: return std::make_unique<cEntityEffectSlowness >(a_Duration, a_Intensity, a_DistanceModifier);
214  case cEntityEffect::effSpeed: return std::make_unique<cEntityEffectSpeed >(a_Duration, a_Intensity, a_DistanceModifier);
215  case cEntityEffect::effStrength: return std::make_unique<cEntityEffectStrength >(a_Duration, a_Intensity, a_DistanceModifier);
216  case cEntityEffect::effWaterBreathing: return std::make_unique<cEntityEffectWaterBreathing>(a_Duration, a_Intensity, a_DistanceModifier);
217  case cEntityEffect::effWeakness: return std::make_unique<cEntityEffectWeakness >(a_Duration, a_Intensity, a_DistanceModifier);
218  case cEntityEffect::effWither: return std::make_unique<cEntityEffectWither >(a_Duration, a_Intensity, a_DistanceModifier);
219  }
220  UNREACHABLE("Unsupported entity effect");
221 }
222 
223 
224 
225 
226 
227 void cEntityEffect::OnTick(cPawn & a_Target)
228 {
229  // Reduce the effect's duration
230  ++m_Ticks;
231 }
232 
233 
234 
235 
236 
238 // cEntityEffectSpeed:
239 
241 {
242  if (a_Target.IsMob())
243  {
244  cMonster * Mob = static_cast<cMonster*>(&a_Target);
246  }
247  else if (a_Target.IsPlayer())
248  {
249  cPlayer * Player = static_cast<cPlayer*>(&a_Target);
250  Player->SetNormalMaxSpeed(Player->GetNormalMaxSpeed() + 0.2 * m_Intensity);
251  Player->SetSprintingMaxSpeed(Player->GetSprintingMaxSpeed() + 0.26 * m_Intensity);
252  Player->SetFlyingMaxSpeed(Player->GetFlyingMaxSpeed() + 0.2 * m_Intensity);
253  }
254 }
255 
256 
257 
258 
259 
261 {
262  if (a_Target.IsMob())
263  {
264  cMonster * Mob = static_cast<cMonster*>(&a_Target);
266  }
267  else if (a_Target.IsPlayer())
268  {
269  cPlayer * Player = static_cast<cPlayer*>(&a_Target);
270  Player->SetNormalMaxSpeed(Player->GetNormalMaxSpeed() - 0.2 * m_Intensity);
271  Player->SetSprintingMaxSpeed(Player->GetSprintingMaxSpeed() - 0.26 * m_Intensity);
272  Player->SetFlyingMaxSpeed(Player->GetFlyingMaxSpeed() - 0.2 * m_Intensity);
273  }
274 }
275 
276 
277 
278 
279 
281 // cEntityEffectSlowness:
282 
284 {
285  if (a_Target.IsMob())
286  {
287  cMonster * Mob = static_cast<cMonster*>(&a_Target);
289  }
290  else if (a_Target.IsPlayer())
291  {
292  cPlayer * Player = static_cast<cPlayer*>(&a_Target);
293  Player->SetNormalMaxSpeed(Player->GetNormalMaxSpeed() - 0.15 * m_Intensity);
294  Player->SetSprintingMaxSpeed(Player->GetSprintingMaxSpeed() - 0.195 * m_Intensity);
295  Player->SetFlyingMaxSpeed(Player->GetFlyingMaxSpeed() - 0.15 * m_Intensity);
296  }
297 }
298 
299 
300 
301 
302 
304 {
305  if (a_Target.IsMob())
306  {
307  cMonster * Mob = static_cast<cMonster*>(&a_Target);
309  }
310  else if (a_Target.IsPlayer())
311  {
312  cPlayer * Player = static_cast<cPlayer*>(&a_Target);
313  Player->SetNormalMaxSpeed(Player->GetNormalMaxSpeed() + 0.15 * m_Intensity);
314  Player->SetSprintingMaxSpeed(Player->GetSprintingMaxSpeed() + 0.195 * m_Intensity);
315  Player->SetFlyingMaxSpeed(Player->GetFlyingMaxSpeed() + 0.15 * m_Intensity);
316  }
317 }
318 
319 
320 
321 
322 
324 // cEntityEffectInstantHealth:
325 
327 {
328  // Base amount = 6, doubles for every increase in intensity
329  int amount = static_cast<int>(6 * (1 << m_Intensity) * m_DistanceModifier);
330 
331  if (a_Target.IsMob() && static_cast<cMonster &>(a_Target).IsUndead())
332  {
333  a_Target.TakeDamage(dtPotionOfHarming, nullptr, amount, 0); // TODO: Store attacker in a pointer-safe way, pass to TakeDamage
334  return;
335  }
336  a_Target.Heal(amount);
337 }
338 
339 
340 
341 
342 
344 // cEntityEffectInstantDamage:
345 
347 {
348  // Base amount = 6, doubles for every increase in intensity
349  int amount = static_cast<int>(6 * (1 << m_Intensity) * m_DistanceModifier);
350 
351  if (a_Target.IsMob() && static_cast<cMonster &>(a_Target).IsUndead())
352  {
353  a_Target.Heal(amount);
354  return;
355  }
356  a_Target.TakeDamage(dtPotionOfHarming, nullptr, amount, 0); // TODO: Store attacker in a pointer-safe way, pass to TakeDamage
357 }
358 
359 
360 
361 
362 
364 // cEntityEffectRegeneration:
365 
367 {
368  Super::OnTick(a_Target);
369 
370  if (a_Target.IsMob() && static_cast<cMonster &>(a_Target).IsUndead())
371  {
372  return;
373  }
374 
375  // Regen frequency = 50 ticks, divided by potion level (Regen II = 25 ticks)
376  int frequency = std::max(1, FloorC(50.0 / static_cast<double>(m_Intensity + 1)));
377 
378  if ((m_Ticks % frequency) != 0)
379  {
380  return;
381  }
382 
383  a_Target.Heal(1);
384 }
385 
386 
387 
388 
389 
391 // cEntityEffectHunger:
392 
394 {
395  Super::OnTick(a_Target);
396 
397  if (a_Target.IsPlayer())
398  {
399  cPlayer & Target = static_cast<cPlayer &>(a_Target);
400  Target.AddFoodExhaustion(0.025 * (static_cast<double>(GetIntensity()) + 1.0)); // 0.5 per second = 0.025 per tick
401  }
402 }
403 
404 
405 
406 
407 
409 // cEntityEffectInvisibility:
410 
412 {
413  auto World = a_Target.GetWorld();
414  if (World != nullptr)
415  {
416  World->BroadcastEntityMetadata(a_Target);
417  }
418 }
419 
420 
421 
422 
423 
425 // cEntityEffectWeakness:
426 
428 {
429  Super::OnTick(a_Target);
430 
431  // Damage reduction = 0.5 damage, multiplied by potion level (Weakness II = 1 damage)
432  // double dmg_reduc = 0.5 * (a_Effect.GetIntensity() + 1);
433 
434  // TODO: Implement me!
435  // TODO: Weakened villager zombies can be turned back to villagers with the god apple
436 }
437 
438 
439 
440 
441 
443 // cEntityEffectPoison:
444 
446 {
447  Super::OnTick(a_Target);
448 
449  if (a_Target.IsMob())
450  {
451  cMonster & Target = static_cast<cMonster &>(a_Target);
452 
453  // Doesn't effect undead mobs, spiders
454  if (
455  Target.IsUndead() ||
456  (Target.GetMobType() == mtSpider) ||
457  (Target.GetMobType() == mtCaveSpider)
458  )
459  {
460  return;
461  }
462  }
463 
464  // Poison frequency = 25 ticks, divided by potion level (Poison II = 12 ticks)
465  int frequency = std::max(1, FloorC(25.0 / static_cast<double>(m_Intensity + 1)));
466 
467  if ((m_Ticks % frequency) == 0)
468  {
469  // Cannot take poison damage when health is at 1
470  if (a_Target.GetHealth() > 1)
471  {
472  a_Target.TakeDamage(dtPoisoning, nullptr, 1, 0);
473  }
474  }
475 }
476 
477 
478 
479 
480 
482 // cEntityEffectWither:
483 
485 {
486  Super::OnTick(a_Target);
487 
488  // Damage frequency = 40 ticks, divided by effect level (Wither II = 20 ticks)
489  int frequency = std::max(1, FloorC(40.0 / static_cast<double>(m_Intensity + 1)));
490 
491  if ((m_Ticks % frequency) == 0)
492  {
493  a_Target.TakeDamage(dtWither, nullptr, 1, 0);
494  }
495 }
496 
497 
498 
499 
500 
502 // cEntityEffectSaturation:
503 
505 {
506  if (a_Target.IsPlayer())
507  {
508  cPlayer & Target = static_cast<cPlayer &>(a_Target);
509  Target.SetFoodSaturationLevel(Target.GetFoodSaturationLevel() + (1 + m_Intensity)); // Increase saturation 1 per tick, adds 1 for every increase in level
510  }
511 }
@ dtPoisoning
Definition: Defines.h:256
@ dtPotionOfHarming
Definition: Defines.h:261
@ dtWither
Definition: Defines.h:286
#define UNREACHABLE(x)
Definition: Globals.h:288
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:347
@ mtCaveSpider
Definition: MonsterTypes.h:17
@ mtSpider
Definition: MonsterTypes.h:63
Utilities to allow casting a cWorld to one of its interfaces without including World....
Definition: OpaqueWorld.h:13
bool IsPlayer(void) const
Definition: Entity.h:160
void TakeDamage(cEntity &a_Attacker)
Makes this pawn take damage from an attack by a_Attacker.
Definition: Entity.cpp:272
float GetHealth(void) const
Returns the health of this entity.
Definition: Entity.h:367
virtual void Heal(int a_HitPoints)
Heals the specified amount of HPs.
Definition: Entity.cpp:890
cWorld * GetWorld(void) const
Definition: Entity.h:190
bool IsMob(void) const
Definition: Entity.h:162
static int GetPotionEffectDuration(short a_ItemDamage)
Returns the effect duration, in ticks, based on the potion's damage value.
double m_DistanceModifier
The distance modifier for affecting potency.
Definition: EntityEffect.h:124
static int GetPotionColor(short a_ItemDamage)
Returns the potion color (used by the client for visuals), based on the potion's damage value.
short GetIntensity(void) const
Returns how strong the effect will be applied.
Definition: EntityEffect.h:93
int m_Duration
How long this effect will last, in ticks.
Definition: EntityEffect.h:118
eType
All types of entity effects (numbers correspond to protocol / storage types)
Definition: EntityEffect.h:12
cEntityEffect(void)
Creates an empty entity effect.
int m_Ticks
How many ticks this effect has been active for.
Definition: EntityEffect.h:115
short m_Intensity
How strong the effect will be applied.
Definition: EntityEffect.h:121
static std::unique_ptr< cEntityEffect > CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier)
Creates a pointer to the proper entity effect from the effect type.
static bool IsPotionDrinkable(short a_ItemDamage)
Returns true if the potion with the given damage is drinkable.
virtual void OnTick(cPawn &a_Target)
Called on each tick.
cEntityEffect & operator=(cEntityEffect a_OtherEffect)
Creates an entity effect by copying another.
static short GetPotionEffectIntensity(short a_ItemDamage)
Retrieves the intensity level from the potion's damage value.
static cEntityEffect::eType GetPotionEffectType(short a_ItemDamage)
Translates the potion's damage value into the entity effect that the potion gives.
virtual void OnDeactivate(cPawn &a_Target) override
Called when the effect is removed from an entity.
virtual void OnActivate(cPawn &a_Target) override
Called when the effect is first added to an entity.
virtual void OnDeactivate(cPawn &a_Target) override
Called when the effect is removed from an entity.
virtual void OnActivate(cPawn &a_Target) override
Called when the effect is first added to an entity.
virtual void OnActivate(cPawn &a_Target) override
Called when the effect is first added to an entity.
virtual void OnActivate(cPawn &a_Target) override
Called when the effect is first added to an entity.
virtual void OnTick(cPawn &a_Target) override
Called on each tick.
static void BroadcastMetadata(cPawn &a_Target)
virtual void OnTick(cPawn &a_Target) override
Called on each tick.
virtual void OnTick(cPawn &a_Target) override
Called on each tick.
virtual void OnTick(cPawn &a_Target) override
Called on each tick.
virtual void OnTick(cPawn &a_Target) override
Called on each tick.
virtual void OnTick(cPawn &a_Target) override
Called on each tick.
Definition: Pawn.h:17
Definition: Player.h:29
double GetSprintingMaxSpeed(void) const
Gets the sprinting relative maximum speed.
Definition: Player.h:472
void SetNormalMaxSpeed(double a_Speed)
Sets the normal relative maximum speed.
Definition: Player.cpp:611
double GetFlyingMaxSpeed(void) const
Gets the flying relative maximum speed.
Definition: Player.h:475
void SetSprintingMaxSpeed(double a_Speed)
Sets the sprinting relative maximum speed.
Definition: Player.cpp:626
void SetFlyingMaxSpeed(double a_Speed)
Sets the flying relative maximum speed.
Definition: Player.cpp:641
double GetNormalMaxSpeed(void) const
Gets the normal relative maximum speed.
Definition: Player.h:469
void SetRelativeWalkSpeed(double a_WalkSpeed)
Definition: Monster.h:149
virtual bool IsUndead(void)
Returns whether this mob is undead (skeleton, zombie, etc.)
Definition: Monster.cpp:997
double GetRelativeWalkSpeed(void) const
Definition: Monster.h:148