All mobs now drown (fixes #54)
* Implemented mob drowning * Iron Golems and squids are excluded
This commit is contained in:
parent
1112f5adc6
commit
fd7fc7e59e
@ -57,6 +57,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
|
||||
, m_Mass (0.001) // Default 1g
|
||||
, m_Width(a_Width)
|
||||
, m_Height(a_Height)
|
||||
, m_IsSubmerged(false)
|
||||
, m_IsSwimming(false)
|
||||
{
|
||||
cCSLock Lock(m_CSCount);
|
||||
m_EntityCount++;
|
||||
@ -529,7 +531,17 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
{
|
||||
TickInVoid(a_Chunk);
|
||||
}
|
||||
else { m_TicksSinceLastVoidDamage = 0; }
|
||||
else
|
||||
m_TicksSinceLastVoidDamage = 0;
|
||||
|
||||
if (IsMob() || IsPlayer())
|
||||
{
|
||||
// Set swimming state
|
||||
SetSwimState(a_Chunk);
|
||||
|
||||
// Handle drowning
|
||||
HandleAir();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -907,6 +919,87 @@ void cEntity::TickInVoid(cChunk & a_Chunk)
|
||||
|
||||
|
||||
|
||||
void cEntity::SetSwimState(cChunk & a_Chunk)
|
||||
{
|
||||
int RelY = (int)floor(m_LastPosY + 0.1);
|
||||
if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
|
||||
{
|
||||
m_IsSwimming = false;
|
||||
m_IsSubmerged = false;
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockIn;
|
||||
int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
|
||||
// Check if the player is swimming:
|
||||
// Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
|
||||
if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
|
||||
{
|
||||
// This sometimes happens on Linux machines
|
||||
// Ref.: http://forum.mc-server.org/showthread.php?tid=1244
|
||||
LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
|
||||
RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
|
||||
);
|
||||
m_IsSwimming = false;
|
||||
m_IsSubmerged = false;
|
||||
return;
|
||||
}
|
||||
m_IsSwimming = IsBlockWater(BlockIn);
|
||||
|
||||
// Check if the player is submerged:
|
||||
VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
|
||||
m_IsSubmerged = IsBlockWater(BlockIn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEntity::HandleAir(void)
|
||||
{
|
||||
// Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
|
||||
// See if the entity is /submerged/ water (block above is water)
|
||||
// Get the type of block the entity is standing in:
|
||||
|
||||
if (IsSubmerged())
|
||||
{
|
||||
SetSpeedY(1); // Float in the water
|
||||
|
||||
// Either reduce air level or damage player
|
||||
if (m_AirLevel < 1)
|
||||
{
|
||||
if (m_AirTickTimer < 1)
|
||||
{
|
||||
// Damage player
|
||||
TakeDamage(dtDrowning, NULL, 1, 1, 0);
|
||||
// Reset timer
|
||||
m_AirTickTimer = DROWNING_TICKS;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AirTickTimer -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reduce air supply
|
||||
m_AirLevel -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the air back to maximum
|
||||
m_AirLevel = MAX_AIR_LEVEL;
|
||||
m_AirTickTimer = DROWNING_TICKS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Called when the entity starts burning
|
||||
void cEntity::OnStartedBurning(void)
|
||||
{
|
||||
|
@ -110,6 +110,8 @@ public:
|
||||
BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning
|
||||
BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning
|
||||
BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
|
||||
MAX_AIR_LEVEL = 300, ///< Maximum air an entity can have
|
||||
DROWNING_TICKS = 20, ///< Number of ticks per heart of damage
|
||||
} ;
|
||||
|
||||
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
|
||||
@ -344,7 +346,14 @@ public:
|
||||
virtual bool IsRiding (void) const {return false; }
|
||||
virtual bool IsSprinting(void) const {return false; }
|
||||
virtual bool IsRclking (void) const {return false; }
|
||||
virtual bool IsInvisible(void) const {return false; }
|
||||
virtual bool IsInvisible(void) const { return false; }
|
||||
|
||||
/** Returns whether the player is swimming or not */
|
||||
virtual bool IsSwimming(void) const{ return m_IsSwimming; }
|
||||
/** Return whether the player is under water or not */
|
||||
virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
|
||||
/** Gets remaining air of a monster */
|
||||
int GetAirLevel(void) const { return m_AirLevel; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
@ -412,6 +421,18 @@ protected:
|
||||
virtual void Destroyed(void) {} // Called after the entity has been destroyed
|
||||
|
||||
void SetWorld(cWorld * a_World) { m_World = a_World; }
|
||||
|
||||
/** Called in each tick to handle air-related processing i.e. drowning */
|
||||
virtual void HandleAir();
|
||||
/** Called once per tick to set IsSwimming and IsSubmerged */
|
||||
virtual void SetSwimState(cChunk & a_Chunk);
|
||||
|
||||
/** If an entity is currently swimming in or submerged under water */
|
||||
bool m_IsSwimming, m_IsSubmerged;
|
||||
|
||||
/** Air level of a mobile */
|
||||
int m_AirLevel;
|
||||
int m_AirTickTimer;
|
||||
|
||||
private:
|
||||
// Measured in degrees, [-180, +180)
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "../UI/Window.h"
|
||||
#include "../UI/WindowOwner.h"
|
||||
#include "../World.h"
|
||||
#include "Pickup.h"
|
||||
#include "../Bindings/PluginManager.h"
|
||||
#include "../BlockEntities/BlockEntity.h"
|
||||
#include "../GroupManager.h"
|
||||
@ -36,8 +35,6 @@
|
||||
|
||||
cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
|
||||
: super(etPlayer, 0.6, 1.8)
|
||||
, m_AirLevel( MAX_AIR_LEVEL )
|
||||
, m_AirTickTimer(DROWNING_TICKS)
|
||||
, m_bVisible(true)
|
||||
, m_FoodLevel(MAX_FOOD_LEVEL)
|
||||
, m_FoodSaturationLevel(5)
|
||||
@ -108,9 +105,23 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
|
||||
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
|
||||
);
|
||||
}
|
||||
|
||||
m_LastJumpHeight = (float)(GetPosY());
|
||||
m_LastGroundHeight = (float)(GetPosY());
|
||||
m_Stance = GetPosY() + 1.62;
|
||||
|
||||
if (m_GameMode == gmNotSet)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
|
||||
if (World == NULL)
|
||||
{
|
||||
World = cRoot::Get()->GetDefaultWorld();
|
||||
}
|
||||
if (World->IsGameModeCreative())
|
||||
{
|
||||
m_CanFly = true;
|
||||
}
|
||||
}
|
||||
|
||||
cRoot::Get()->GetServer()->PlayerCreated(this);
|
||||
}
|
||||
@ -197,12 +208,6 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
|
||||
super::Tick(a_Dt, a_Chunk);
|
||||
|
||||
// Set player swimming state
|
||||
SetSwimState(a_Chunk);
|
||||
|
||||
// Handle air drowning stuff
|
||||
HandleAir();
|
||||
|
||||
// Handle charging the bow:
|
||||
if (m_IsChargingBow)
|
||||
{
|
||||
@ -1630,27 +1635,12 @@ bool cPlayer::LoadFromDisk()
|
||||
m_CurrentXp = (short) root.get("xpCurrent", 0).asInt();
|
||||
m_IsFlying = root.get("isflying", 0).asBool();
|
||||
|
||||
//SetExperience(root.get("experience", 0).asInt());
|
||||
|
||||
m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
|
||||
|
||||
if (m_GameMode == eGameMode_Creative)
|
||||
{
|
||||
m_CanFly = true;
|
||||
}
|
||||
else if (m_GameMode == eGameMode_NotSet)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
|
||||
if (World == NULL)
|
||||
{
|
||||
World = cRoot::Get()->GetDefaultWorld();
|
||||
}
|
||||
|
||||
if (World->GetGameMode() == eGameMode_Creative)
|
||||
{
|
||||
m_CanFly = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_Inventory.LoadFromJson(root["inventory"]);
|
||||
|
||||
@ -1767,85 +1757,6 @@ void cPlayer::UseEquippedItem(void)
|
||||
|
||||
|
||||
|
||||
void cPlayer::SetSwimState(cChunk & a_Chunk)
|
||||
{
|
||||
int RelY = (int)floor(m_LastPosY + 0.1);
|
||||
if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
|
||||
{
|
||||
m_IsSwimming = false;
|
||||
m_IsSubmerged = false;
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockIn;
|
||||
int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
|
||||
// Check if the player is swimming:
|
||||
// Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
|
||||
if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
|
||||
{
|
||||
// This sometimes happens on Linux machines
|
||||
// Ref.: http://forum.mc-server.org/showthread.php?tid=1244
|
||||
LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
|
||||
RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
|
||||
);
|
||||
m_IsSwimming = false;
|
||||
m_IsSubmerged = false;
|
||||
return;
|
||||
}
|
||||
m_IsSwimming = IsBlockWater(BlockIn);
|
||||
|
||||
// Check if the player is submerged:
|
||||
VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
|
||||
m_IsSubmerged = IsBlockWater(BlockIn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::HandleAir(void)
|
||||
{
|
||||
// Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
|
||||
// see if the player is /submerged/ water (block above is water)
|
||||
// Get the type of block the player's standing in:
|
||||
|
||||
if (IsSubmerged())
|
||||
{
|
||||
// either reduce air level or damage player
|
||||
if (m_AirLevel < 1)
|
||||
{
|
||||
if (m_AirTickTimer < 1)
|
||||
{
|
||||
// damage player
|
||||
TakeDamage(dtDrowning, NULL, 1, 1, 0);
|
||||
// reset timer
|
||||
m_AirTickTimer = DROWNING_TICKS;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AirTickTimer -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// reduce air supply
|
||||
m_AirLevel -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the air back to maximum
|
||||
m_AirLevel = MAX_AIR_LEVEL;
|
||||
m_AirTickTimer = DROWNING_TICKS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::HandleFood(void)
|
||||
{
|
||||
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
|
||||
|
@ -31,8 +31,6 @@ public:
|
||||
MAX_HEALTH = 20,
|
||||
MAX_FOOD_LEVEL = 20,
|
||||
EATING_TICKS = 30, ///< Number of ticks it takes to eat an item
|
||||
MAX_AIR_LEVEL = 300,
|
||||
DROWNING_TICKS = 10, //number of ticks per heart of damage
|
||||
} ;
|
||||
// tolua_end
|
||||
|
||||
@ -241,8 +239,6 @@ public:
|
||||
int GetFoodTickTimer (void) const { return m_FoodTickTimer; }
|
||||
double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
|
||||
int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; }
|
||||
|
||||
int GetAirLevel (void) const { return m_AirLevel; }
|
||||
|
||||
/// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore
|
||||
bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
|
||||
@ -353,12 +349,6 @@ public:
|
||||
/// If true the player can fly even when he's not in creative.
|
||||
void SetCanFly(bool a_CanFly);
|
||||
|
||||
/// Returns whether the player is swimming or not
|
||||
virtual bool IsSwimming(void) const{ return m_IsSwimming; }
|
||||
|
||||
/// Return whether the player is under water or not
|
||||
virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
|
||||
|
||||
/// Returns wheter the player can fly or not.
|
||||
virtual bool CanFly(void) const { return m_CanFly; }
|
||||
// tolua_end
|
||||
@ -389,12 +379,6 @@ protected:
|
||||
XP_TO_LEVEL30 = 825
|
||||
} ;
|
||||
|
||||
/// Player's air level (for swimming)
|
||||
int m_AirLevel;
|
||||
|
||||
/// used to time ticks between damage taken via drowning/suffocation
|
||||
int m_AirTickTimer;
|
||||
|
||||
bool m_bVisible;
|
||||
|
||||
// Food-related variables:
|
||||
@ -490,12 +474,6 @@ protected:
|
||||
|
||||
/// Called in each tick if the player is fishing to make sure the floater dissapears when the player doesn't have a fishing rod as equipped item.
|
||||
void HandleFloater(void);
|
||||
|
||||
/// Called in each tick to handle air-related processing i.e. drowning
|
||||
void HandleAir();
|
||||
|
||||
/// Called once per tick to set IsSwimming and IsSubmerged
|
||||
void SetSwimState(cChunk & a_Chunk);
|
||||
|
||||
/// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block)
|
||||
void ApplyFoodExhaustionFromMovement();
|
||||
|
@ -18,6 +18,10 @@ public:
|
||||
CLASS_PROTODEF(cIronGolem);
|
||||
|
||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
|
||||
|
||||
// Iron golems do not drown
|
||||
virtual void HandleAir(void) override {}
|
||||
virtual void SetSwimState(cChunk & a_Chunk) override {}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -21,6 +21,9 @@ public:
|
||||
|
||||
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
|
||||
|
||||
// Squids do not drown (or float)
|
||||
virtual void HandleAir(void) override {}
|
||||
virtual void SetSwimState(cChunk & a_Chunk) override {}
|
||||
} ;
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user