1
0
Fork 0

All mobs now drown (fixes #54)

* Implemented mob drowning
* Iron Golems and squids are excluded
This commit is contained in:
Tiger Wang 2014-01-24 23:58:51 +00:00
parent 1112f5adc6
commit fd7fc7e59e
6 changed files with 137 additions and 127 deletions

View File

@ -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)
{

View File

@ -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)

View File

@ -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

View File

@ -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();

View File

@ -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 {}
} ;

View File

@ -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 {}
} ;