diff options
Diffstat (limited to 'source/Entities')
-rw-r--r-- | source/Entities/Boat.cpp | 87 | ||||
-rw-r--r-- | source/Entities/Boat.h | 37 | ||||
-rw-r--r-- | source/Entities/Entity.cpp | 1450 | ||||
-rw-r--r-- | source/Entities/Entity.h | 446 | ||||
-rw-r--r-- | source/Entities/ExpOrb.cpp | 60 | ||||
-rw-r--r-- | source/Entities/ExpOrb.h | 29 | ||||
-rw-r--r-- | source/Entities/FallingBlock.cpp | 93 | ||||
-rw-r--r-- | source/Entities/FallingBlock.h | 43 | ||||
-rw-r--r-- | source/Entities/Minecart.cpp | 541 | ||||
-rw-r--r-- | source/Entities/Minecart.h | 169 | ||||
-rw-r--r-- | source/Entities/Pawn.cpp | 19 | ||||
-rw-r--r-- | source/Entities/Pawn.h | 28 | ||||
-rw-r--r-- | source/Entities/Pickup.cpp | 166 | ||||
-rw-r--r-- | source/Entities/Pickup.h | 64 | ||||
-rw-r--r-- | source/Entities/Player.cpp | 1765 | ||||
-rw-r--r-- | source/Entities/Player.h | 456 | ||||
-rw-r--r-- | source/Entities/ProjectileEntity.cpp | 855 | ||||
-rw-r--r-- | source/Entities/ProjectileEntity.h | 382 | ||||
-rw-r--r-- | source/Entities/TNTEntity.cpp | 62 | ||||
-rw-r--r-- | source/Entities/TNTEntity.h | 32 |
20 files changed, 0 insertions, 6784 deletions
diff --git a/source/Entities/Boat.cpp b/source/Entities/Boat.cpp deleted file mode 100644 index 56e766dd4..000000000 --- a/source/Entities/Boat.cpp +++ /dev/null @@ -1,87 +0,0 @@ - -// Boat.cpp - -// Implements the cBoat class representing a boat in the world - -#include "Globals.h" -#include "Boat.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "Player.h" - - - - - -cBoat::cBoat(double a_X, double a_Y, double a_Z) : - super(etBoat, a_X, a_Y, a_Z, 0.98, 0.7) -{ - SetMass(20.f); - SetMaxHealth(6); - SetHealth(6); -} - - - - -void cBoat::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnVehicle(*this, 1); -} - - - - - -void cBoat::DoTakeDamage(TakeDamageInfo & TDI) -{ - super::DoTakeDamage(TDI); - - if (GetHealth() == 0) - { - Destroy(true); - } -} - - - - - -void cBoat::OnRightClicked(cPlayer & a_Player) -{ - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - // This player is already sitting in, they want out. - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - // Another player is already sitting in here, cannot attach - return; - } - - // Detach whatever is sitting in this boat now: - m_Attachee->Detach(); - } - - // Attach the player to this boat - a_Player.AttachTo(this); -} - - - - - -void cBoat::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); -} - - - - diff --git a/source/Entities/Boat.h b/source/Entities/Boat.h deleted file mode 100644 index 8c51ab86c..000000000 --- a/source/Entities/Boat.h +++ /dev/null @@ -1,37 +0,0 @@ - -// Boat.h - -// Declares the cBoat class representing a boat in the world - - - - - -#pragma once - -#include "Entity.h" - - - - - -class cBoat : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cBoat); - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - - cBoat(double a_X, double a_Y, double a_Z); -} ; - - - - diff --git a/source/Entities/Entity.cpp b/source/Entities/Entity.cpp deleted file mode 100644 index 3bea7bc01..000000000 --- a/source/Entities/Entity.cpp +++ /dev/null @@ -1,1450 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Entity.h" -#include "../World.h" -#include "../Server.h" -#include "../Root.h" -#include "../Vector3d.h" -#include "../Matrix4f.h" -#include "../ReferenceManager.h" -#include "../ClientHandle.h" -#include "../Chunk.h" -#include "../Simulator/FluidSimulator.h" -#include "../PluginManager.h" -#include "../Tracer.h" -#include "Minecart.h" - - - - - -int cEntity::m_EntityCount = 0; -cCriticalSection cEntity::m_CSCount; - - - - - -cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) - : m_UniqueID(0) - , m_Health(1) - , m_MaxHealth(1) - , m_AttachedTo(NULL) - , m_Attachee(NULL) - , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS)) - , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES)) - , m_HeadYaw( 0.0 ) - , m_Rot(0.0, 0.0, 0.0) - , m_Pos(a_X, a_Y, a_Z) - , m_Mass (0.001) //Default 1g - , m_bDirtyHead(true) - , m_bDirtyOrientation(true) - , m_bDirtyPosition(true) - , m_bDirtySpeed(true) - , m_bOnGround( false ) - , m_Gravity( -9.81f ) - , m_IsInitialized(false) - , m_LastPosX( 0.0 ) - , m_LastPosY( 0.0 ) - , m_LastPosZ( 0.0 ) - , m_TimeLastTeleportPacket(0) - , m_TimeLastMoveReltPacket(0) - , m_TimeLastSpeedPacket(0) - , m_EntityType(a_EntityType) - , m_World(NULL) - , m_TicksSinceLastBurnDamage(0) - , m_TicksSinceLastLavaDamage(0) - , m_TicksSinceLastFireDamage(0) - , m_TicksSinceLastVoidDamage(0) - , m_TicksLeftBurning(0) - , m_WaterSpeed(0, 0, 0) - , m_Width(a_Width) - , m_Height(a_Height) -{ - cCSLock Lock(m_CSCount); - m_EntityCount++; - m_UniqueID = m_EntityCount; -} - - - - - -cEntity::~cEntity() -{ - ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world - - LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p", - m_UniqueID, - m_Pos.x, m_Pos.y, m_Pos.z, - (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), - this - ); - - if (m_AttachedTo != NULL) - { - Detach(); - } - if (m_Attachee != NULL) - { - m_Attachee->Detach(); - } - - if (m_IsInitialized) - { - LOGWARNING("ERROR: Entity deallocated without being destroyed"); - ASSERT(!"Entity deallocated without being destroyed or unlinked"); - } - delete m_Referencers; - delete m_References; -} - - - - - -const char * cEntity::GetClass(void) const -{ - return "cEntity"; -} - - - - - -const char * cEntity::GetClassStatic(void) -{ - return "cEntity"; -} - - - - - -const char * cEntity::GetParentClass(void) const -{ - return ""; -} - - - - - -bool cEntity::Initialize(cWorld * a_World) -{ - if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this)) - { - return false; - } - - LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}", - m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z - ); - m_IsInitialized = true; - m_World = a_World; - m_World->AddEntity(this); - - cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this); - - // Spawn the entity on the clients: - a_World->BroadcastSpawnEntity(*this); - - return true; -} - - - - - -void cEntity::WrapHeadYaw(void) -{ - while (m_HeadYaw > 180.f) m_HeadYaw -= 360.f; // Wrap it - while (m_HeadYaw < -180.f) m_HeadYaw += 360.f; -} - - - - - -void cEntity::WrapRotation(void) -{ - while (m_Rot.x > 180.f) m_Rot.x -= 360.f; // Wrap it - while (m_Rot.x < -180.f) m_Rot.x += 360.f; - while (m_Rot.y > 180.f) m_Rot.y -= 360.f; - while (m_Rot.y < -180.f) m_Rot.y += 360.f; -} - - - - -void cEntity::WrapSpeed(void) -{ - // There shoudn't be a need for flipping the flag on because this function is called - // after any update, so the flag is already turned on - if (m_Speed.x > 78.0f) m_Speed.x = 78.0f; - else if (m_Speed.x < -78.0f) m_Speed.x = -78.0f; - if (m_Speed.y > 78.0f) m_Speed.y = 78.0f; - else if (m_Speed.y < -78.0f) m_Speed.y = -78.0f; - if (m_Speed.z > 78.0f) m_Speed.z = 78.0f; - else if (m_Speed.z < -78.0f) m_Speed.z = -78.0f; -} - - - - - -void cEntity::Destroy(bool a_ShouldBroadcast) -{ - if (!m_IsInitialized) - { - return; - } - - if (a_ShouldBroadcast) - { - m_World->BroadcastDestroyEntity(*this); - } - - m_IsInitialized = false; - - Destroyed(); -} - - - - - -void cEntity::TakeDamage(cEntity & a_Attacker) -{ - int RawDamage = a_Attacker.GetRawDamageAgainst(*this); - - TakeDamage(dtAttack, &a_Attacker, RawDamage, a_Attacker.GetKnockbackAmountAgainst(*this)); -} - - - - - -void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount) -{ - int FinalDamage = a_RawDamage - GetArmorCoverAgainst(a_Attacker, a_DamageType, a_RawDamage); - cEntity::TakeDamage(a_DamageType, a_Attacker, a_RawDamage, FinalDamage, a_KnockbackAmount); -} - - - - - -void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount) -{ - TakeDamageInfo TDI; - TDI.DamageType = a_DamageType; - TDI.Attacker = a_Attacker; - TDI.RawDamage = a_RawDamage; - TDI.FinalDamage = a_FinalDamage; - Vector3d Heading; - Heading.x = sin(GetRotation()); - Heading.y = 0.4; // TODO: adjust the amount of "up" knockback when testing - Heading.z = cos(GetRotation()); - TDI.Knockback = Heading * a_KnockbackAmount; - DoTakeDamage(TDI); -} - - - - - -void cEntity::SetRotationFromSpeed(void) -{ - const double EPS = 0.0000001; - if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) - { - // atan2() may overflow or is undefined, pick any number - SetRotation(0); - return; - } - SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI); -} - - - - - -void cEntity::SetPitchFromSpeed(void) -{ - const double EPS = 0.0000001; - double xz = sqrt(m_Speed.x * m_Speed.x + m_Speed.z * m_Speed.z); // Speed XZ-plane component - if ((abs(xz) < EPS) && (abs(m_Speed.y) < EPS)) - { - // atan2() may overflow or is undefined, pick any number - SetPitch(0); - return; - } - SetPitch(atan2(m_Speed.y, xz) * 180 / PI); -} - - - - - -void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) - { - return; - } - - if (m_Health <= 0) - { - // Can't take damage if already dead - return; - } - - m_Health -= (short)a_TDI.FinalDamage; - - // TODO: Apply damage to armor - - if (m_Health < 0) - { - m_Health = 0; - } - - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); - - if (m_Health <= 0) - { - KilledBy(a_TDI.Attacker); - } -} - - - - - -int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver) -{ - // Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items - // Ref: http://www.minecraftwiki.net/wiki/Damage#Dealing_damage as of 2012_12_20 - switch (this->GetEquippedWeapon().m_ItemType) - { - case E_ITEM_WOODEN_SWORD: return 4; - case E_ITEM_GOLD_SWORD: return 4; - case E_ITEM_STONE_SWORD: return 5; - case E_ITEM_IRON_SWORD: return 6; - case E_ITEM_DIAMOND_SWORD: return 7; - - case E_ITEM_WOODEN_AXE: return 3; - case E_ITEM_GOLD_AXE: return 3; - case E_ITEM_STONE_AXE: return 4; - case E_ITEM_IRON_AXE: return 5; - case E_ITEM_DIAMOND_AXE: return 6; - - case E_ITEM_WOODEN_PICKAXE: return 2; - case E_ITEM_GOLD_PICKAXE: return 2; - case E_ITEM_STONE_PICKAXE: return 3; - case E_ITEM_IRON_PICKAXE: return 4; - case E_ITEM_DIAMOND_PICKAXE: return 5; - - case E_ITEM_WOODEN_SHOVEL: return 1; - case E_ITEM_GOLD_SHOVEL: return 1; - case E_ITEM_STONE_SHOVEL: return 2; - case E_ITEM_IRON_SHOVEL: return 3; - case E_ITEM_DIAMOND_SHOVEL: return 4; - } - // All other equipped items give a damage of 1: - return 1; -} - - - - - -int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage) -{ - // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover - - // Filter out damage types that are not protected by armor: - // Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20 - switch (a_DamageType) - { - case dtOnFire: - case dtSuffocating: - case dtDrowning: // TODO: This one could be a special case - in various MC versions (PC vs XBox) it is and isn't armor-protected - case dtStarving: - case dtInVoid: - case dtPoisoning: - case dtPotionOfHarming: - case dtFalling: - case dtLightning: - { - return 0; - } - } - - // Add up all armor points: - // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 - int ArmorValue = 0; - switch (GetEquippedHelmet().m_ItemType) - { - case E_ITEM_LEATHER_CAP: ArmorValue += 1; break; - case E_ITEM_GOLD_HELMET: ArmorValue += 2; break; - case E_ITEM_CHAIN_HELMET: ArmorValue += 2; break; - case E_ITEM_IRON_HELMET: ArmorValue += 2; break; - case E_ITEM_DIAMOND_HELMET: ArmorValue += 3; break; - } - switch (GetEquippedChestplate().m_ItemType) - { - case E_ITEM_LEATHER_TUNIC: ArmorValue += 3; break; - case E_ITEM_GOLD_CHESTPLATE: ArmorValue += 5; break; - case E_ITEM_CHAIN_CHESTPLATE: ArmorValue += 5; break; - case E_ITEM_IRON_CHESTPLATE: ArmorValue += 6; break; - case E_ITEM_DIAMOND_CHESTPLATE: ArmorValue += 8; break; - } - switch (GetEquippedLeggings().m_ItemType) - { - case E_ITEM_LEATHER_PANTS: ArmorValue += 2; break; - case E_ITEM_GOLD_LEGGINGS: ArmorValue += 3; break; - case E_ITEM_CHAIN_LEGGINGS: ArmorValue += 4; break; - case E_ITEM_IRON_LEGGINGS: ArmorValue += 5; break; - case E_ITEM_DIAMOND_LEGGINGS: ArmorValue += 6; break; - } - switch (GetEquippedBoots().m_ItemType) - { - case E_ITEM_LEATHER_BOOTS: ArmorValue += 1; break; - case E_ITEM_GOLD_BOOTS: ArmorValue += 1; break; - case E_ITEM_CHAIN_BOOTS: ArmorValue += 1; break; - case E_ITEM_IRON_BOOTS: ArmorValue += 2; break; - case E_ITEM_DIAMOND_BOOTS: ArmorValue += 3; break; - } - - // TODO: Special armor cases, such as wool, saddles, dog's collar - // Ref.: http://www.minecraftwiki.net/wiki/Armor#Mob_armor as of 2012_12_20 - - // Now ArmorValue is in [0, 20] range, which corresponds to [0, 80%] protection. Calculate the hitpoints from that: - return a_Damage * (ArmorValue * 4) / 100; -} - - - - - -double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver) -{ - // Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit - - // TODO: Enchantments - return 1; -} - - - - - -void cEntity::KilledBy(cEntity * a_Killer) -{ - m_Health = 0; - - cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer); - - if (m_Health > 0) - { - // Plugin wants to 'unkill' the pawn. Abort - return; - } - - // Drop loot: - cItems Drops; - GetDrops(Drops, a_Killer); - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); - - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); -} - - - - - -void cEntity::Heal(int a_HitPoints) -{ - m_Health += a_HitPoints; - if (m_Health > m_MaxHealth) - { - m_Health = m_MaxHealth; - } -} - - - - - -void cEntity::SetHealth(int a_Health) -{ - m_Health = std::max(0, std::min(m_MaxHealth, a_Health)); -} - - - - - -void cEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (m_AttachedTo != NULL) - { - if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5) - { - SetPosition(m_AttachedTo->GetPosition()); - } - } - else - { - if (a_Chunk.IsValid()) - { - HandlePhysics(a_Dt, a_Chunk); - } - } - if (a_Chunk.IsValid()) - { - TickBurning(a_Chunk); - } - if ((a_Chunk.IsValid()) && (GetPosY() < -46)) - { - TickInVoid(a_Chunk); - } - else { m_TicksSinceLastVoidDamage = 0; } -} - - - - - -void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - // TODO Add collision detection with entities. - a_Dt /= 1000; // Convert from msec to sec - Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ()); - Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ()); - int BlockX = (int) floor(NextPos.x); - int BlockY = (int) floor(NextPos.y); - int BlockZ = (int) floor(NextPos.z); - - if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) - { - // Outside of the world - - cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); - // See if we can commit our changes. If not, we will discard them. - if (NextChunk != NULL) - { - SetSpeed(NextSpeed); - NextPos += (NextSpeed * a_Dt); - SetPosition(NextPos); - } - return; - } - - // Make sure we got the correct chunk and a valid one. No one ever knows... - cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); - if (NextChunk != NULL) - { - int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width); - int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width); - BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ ); - BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; - if (!g_BlockIsSolid[BlockIn]) // Making sure we are not inside a solid block - { - if (m_bOnGround) // check if it's still on the ground - { - if (!g_BlockIsSolid[BlockBelow]) // Check if block below is air or water. - { - m_bOnGround = false; - } - } - } - else - { - // Push out entity. - BLOCKTYPE GotBlock; - - static const struct - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0}, - {-1, 0, 0}, - { 0, 0, 1}, - { 0, 0, -1}, - } ; - - bool IsNoAirSurrounding = true; - for (int i = 0; i < ARRAYCOUNT(gCrossCoords); i++) - { - if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock)) - { - // The pickup is too close to an unloaded chunk, bail out of any physics handling - return; - } - if (!g_BlockIsSolid[GotBlock]) - { - NextPos.x += gCrossCoords[i].x; - NextPos.z += gCrossCoords[i].z; - IsNoAirSurrounding = false; - break; - } - } // for i - gCrossCoords[] - - if (IsNoAirSurrounding) - { - NextPos.y += 0.5; - } - - m_bOnGround = true; - - LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}", - m_UniqueID, GetClass(), BlockX, BlockY, BlockZ - ); - } - - if (!m_bOnGround) - { - float fallspeed; - if (IsBlockWater(BlockIn)) - { - fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water. - } - else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts - { - fallspeed = 0; - m_bOnGround = true; - } - else if (BlockIn == E_BLOCK_COBWEB) - { - NextSpeed.y *= 0.05; // Reduce overall falling speed - fallspeed = 0; // No falling. - } - else - { - // Normal gravity - fallspeed = m_Gravity * a_Dt; - } - NextSpeed.y += fallspeed; - } - else - { - if (IsMinecart()) - { - if (!IsBlockRail(BlockBelow)) - { - // Friction if minecart is off track, otherwise, Minecart.cpp handles this - if (NextSpeed.SqrLength() > 0.0004f) - { - NextSpeed.x *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.x) < 0.05) - { - NextSpeed.x = 0; - } - NextSpeed.z *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.z) < 0.05) - { - NextSpeed.z = 0; - } - } - } - } - else - { - // Friction for non-minecarts - if (NextSpeed.SqrLength() > 0.0004f) - { - NextSpeed.x *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.x) < 0.05) - { - NextSpeed.x = 0; - } - NextSpeed.z *= 0.7f / (1 + a_Dt); - if (fabs(NextSpeed.z) < 0.05) - { - NextSpeed.z = 0; - } - } - } - } - - // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we - // might have different speed modifiers according to terrain. - if (BlockIn == E_BLOCK_COBWEB) - { - NextSpeed.x *= 0.25; - NextSpeed.z *= 0.25; - } - - //Get water direction - Direction WaterDir = m_World->GetWaterSimulator()->GetFlowingDirection(BlockX, BlockY, BlockZ); - - m_WaterSpeed *= 0.9f; //Reduce speed each tick - - switch(WaterDir) - { - case X_PLUS: - m_WaterSpeed.x = 0.2f; - m_bOnGround = false; - break; - case X_MINUS: - m_WaterSpeed.x = -0.2f; - m_bOnGround = false; - break; - case Z_PLUS: - m_WaterSpeed.z = 0.2f; - m_bOnGround = false; - break; - case Z_MINUS: - m_WaterSpeed.z = -0.2f; - m_bOnGround = false; - break; - - default: - break; - } - - if (fabs(m_WaterSpeed.x) < 0.05) - { - m_WaterSpeed.x = 0; - } - - if (fabs(m_WaterSpeed.z) < 0.05) - { - m_WaterSpeed.z = 0; - } - - NextSpeed += m_WaterSpeed; - - if( NextSpeed.SqrLength() > 0.f ) - { - cTracer Tracer( GetWorld() ); - int Ret = Tracer.Trace( NextPos, NextSpeed, 2 ); - if( Ret ) // Oh noez! we hit something - { - // Set to hit position - if( (Tracer.RealHit - NextPos).SqrLength() <= ( NextSpeed * a_Dt ).SqrLength() ) - { - if( Ret == 1 ) - { - if( Tracer.HitNormal.x != 0.f ) NextSpeed.x = 0.f; - if( Tracer.HitNormal.y != 0.f ) NextSpeed.y = 0.f; - if( Tracer.HitNormal.z != 0.f ) NextSpeed.z = 0.f; - - if( Tracer.HitNormal.y > 0 ) // means on ground - { - m_bOnGround = true; - } - } - NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z); - NextPos.x += Tracer.HitNormal.x * 0.3f; - NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot - NextPos.z += Tracer.HitNormal.z * 0.3f; - } - else - { - NextPos += (NextSpeed * a_Dt); - } - } - else - { - // We didn't hit anything, so move =] - NextPos += (NextSpeed * a_Dt); - } - } - BlockX = (int) floor(NextPos.x); - BlockZ = (int) floor(NextPos.z); - NextChunk = NextChunk->GetNeighborChunk(BlockX,BlockZ); - // See if we can commit our changes. If not, we will discard them. - if (NextChunk != NULL) - { - if (NextPos.x != GetPosX()) SetPosX(NextPos.x); - if (NextPos.y != GetPosY()) SetPosY(NextPos.y); - if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z); - if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x); - if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y); - if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z); - } - } -} - - - - - -void cEntity::TickBurning(cChunk & a_Chunk) -{ - // Remember the current burning state: - bool HasBeenBurning = (m_TicksLeftBurning > 0); - - // Do the burning damage: - if (m_TicksLeftBurning > 0) - { - m_TicksSinceLastBurnDamage++; - if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE) - { - TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0); - m_TicksSinceLastBurnDamage = 0; - } - m_TicksLeftBurning--; - } - - // Update the burning times, based on surroundings: - int MinRelX = (int)floor(GetPosX() - m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; - int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width; - int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; - int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width; - int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY()))); - int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height))); - bool HasWater = false; - bool HasLava = false; - bool HasFire = false; - - for (int x = MinRelX; x <= MaxRelX; x++) - { - for (int z = MinRelZ; z <= MaxRelZ; z++) - { - int RelX = x; - int RelZ = z; - cChunk * CurChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelX, RelZ); - if (CurChunk == NULL) - { - continue; - } - for (int y = MinY; y <= MaxY; y++) - { - switch (CurChunk->GetBlock(RelX, y, RelZ)) - { - case E_BLOCK_FIRE: - { - HasFire = true; - break; - } - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - HasLava = true; - break; - } - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_WATER: - { - HasWater = true; - break; - } - } // switch (BlockType) - } // for y - } // for z - } // for x - - if (HasWater) - { - // Extinguish the fire - m_TicksLeftBurning = 0; - } - - if (HasLava) - { - // Burn: - m_TicksLeftBurning = BURN_TICKS; - - // Periodically damage: - m_TicksSinceLastLavaDamage++; - if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE) - { - TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0); - m_TicksSinceLastLavaDamage = 0; - } - } - else - { - m_TicksSinceLastLavaDamage = 0; - } - - if (HasFire) - { - // Burn: - m_TicksLeftBurning = BURN_TICKS; - - // Periodically damage: - m_TicksSinceLastFireDamage++; - if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE) - { - TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0); - m_TicksSinceLastFireDamage = 0; - } - } - else - { - m_TicksSinceLastFireDamage = 0; - } - - // If just started / finished burning, notify descendants: - if ((m_TicksLeftBurning > 0) && !HasBeenBurning) - { - OnStartedBurning(); - } - else if ((m_TicksLeftBurning <= 0) && HasBeenBurning) - { - OnFinishedBurning(); - } -} - - - - - -void cEntity::TickInVoid(cChunk & a_Chunk) -{ - if (m_TicksSinceLastVoidDamage == 20) - { - TakeDamage(dtInVoid, NULL, 2, 0); - m_TicksSinceLastVoidDamage = 0; - } - else - { - m_TicksSinceLastVoidDamage++; - } -} - - - - - -/// Called when the entity starts burning -void cEntity::OnStartedBurning(void) -{ - // Broadcast the change: - m_World->BroadcastEntityMetadata(*this); -} - - - - - -/// Called when the entity finishes burning -void cEntity::OnFinishedBurning(void) -{ - // Broadcast the change: - m_World->BroadcastEntityMetadata(*this); -} - - - - - -/// Sets the maximum value for the health -void cEntity::SetMaxHealth(int a_MaxHealth) -{ - m_MaxHealth = a_MaxHealth; - - // Reset health, if too high: - if (m_Health > a_MaxHealth) - { - m_Health = a_MaxHealth; - } -} - - - - - -/// Puts the entity on fire for the specified amount of ticks -void cEntity::StartBurning(int a_TicksLeftBurning) -{ - if (m_TicksLeftBurning > 0) - { - // Already burning, top up the ticks left burning and bail out: - m_TicksLeftBurning = std::max(m_TicksLeftBurning, a_TicksLeftBurning); - return; - } - - m_TicksLeftBurning = a_TicksLeftBurning; - OnStartedBurning(); -} - - - - - -/// Stops the entity from burning, resets all burning timers -void cEntity::StopBurning(void) -{ - bool HasBeenBurning = (m_TicksLeftBurning > 0); - m_TicksLeftBurning = 0; - m_TicksSinceLastBurnDamage = 0; - m_TicksSinceLastFireDamage = 0; - m_TicksSinceLastLavaDamage = 0; - - // Notify if the entity has stopped burning - if (HasBeenBurning) - { - OnFinishedBurning(); - } -} - - - - - -void cEntity::TeleportToEntity(cEntity & a_Entity) -{ - TeleportToCoords(a_Entity.GetPosX(), a_Entity.GetPosY(), a_Entity.GetPosZ()); -} - - - - - -void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) -{ - SetPosition(a_PosX, a_PosY, a_PosZ); - m_World->BroadcastTeleportEntity(*this); -} - - - - - -void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) -{ - //We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks - if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2)) - { - m_World->BroadcastEntityVelocity(*this,a_Exclude); - m_bDirtySpeed = false; - m_TimeLastSpeedPacket = m_World->GetWorldAge(); - } - - //Have to process position related packets this every two ticks - if (m_World->GetWorldAge() % 2 == 0) - { - int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0)); - int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0)); - int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0)); - Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket; - // 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds - if (DiffTeleportPacket >= 400 || - ((DiffX > 127) || (DiffX < -128) || - (DiffY > 127) || (DiffY < -128) || - (DiffZ > 127) || (DiffZ < -128))) - { - // - m_World->BroadcastTeleportEntity(*this,a_Exclude); - m_TimeLastTeleportPacket = m_World->GetWorldAge(); - m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize. - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_bDirtyPosition = false; - m_bDirtyOrientation = false; - } - else - { - Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket; - //if the change is big enough. - if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition) - { - if (m_bDirtyOrientation) - { - m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); - m_bDirtyOrientation = false; - } - else - { - m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); - } - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_bDirtyPosition = false; - m_TimeLastMoveReltPacket = m_World->GetWorldAge(); - } - else - { - if (m_bDirtyOrientation) - { - m_World->BroadcastEntityLook(*this,a_Exclude); - m_bDirtyOrientation = false; - } - } - } - if (m_bDirtyHead) - { - m_World->BroadcastEntityHeadLook(*this,a_Exclude); - m_bDirtyHead = false; - } - } -} - - - - - -void cEntity::AttachTo(cEntity * a_AttachTo) -{ - if (m_AttachedTo == a_AttachTo) - { - // Already attached to that entity, nothing to do here - return; - } - - // Detach from any previous entity: - Detach(); - - // Attach to the new entity: - m_AttachedTo = a_AttachTo; - a_AttachTo->m_Attachee = this; - m_World->BroadcastAttachEntity(*this, a_AttachTo); -} - - - - - -void cEntity::Detach(void) -{ - if (m_AttachedTo == NULL) - { - // Attached to no entity, our work is done - return; - } - m_AttachedTo->m_Attachee = NULL; - m_AttachedTo = NULL; - m_World->BroadcastAttachEntity(*this, NULL); -} - - - - - -bool cEntity::IsA(const char * a_ClassName) const -{ - return (strcmp(a_ClassName, "cEntity") == 0); -} - - - - - -void cEntity::SetRot(const Vector3f & a_Rot) -{ - m_Rot = a_Rot; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetHeadYaw(double a_HeadYaw) -{ - m_HeadYaw = a_HeadYaw; - m_bDirtyHead = true; - WrapHeadYaw(); -} - - - - - -void cEntity::SetHeight(double a_Height) -{ - m_Height = a_Height; -} - - - - - -void cEntity::SetMass(double a_Mass) -{ - if (a_Mass > 0) - { - m_Mass = a_Mass; - } - else - { - // Make sure that mass is not zero. 1g is the default because we - // have to choose a number. It's perfectly legal to have a mass - // less than 1g as long as is NOT equal or less than zero. - m_Mass = 0.001; - } -} - - - - - -void cEntity::SetYaw(double a_Yaw) -{ - m_Rot.x = a_Yaw; - m_bDirtyOrientation = true; - WrapRotation(); -} - - - - - -void cEntity::SetPitch(double a_Pitch) -{ - m_Rot.y = a_Pitch; - m_bDirtyOrientation = true; - WrapRotation(); -} - - - - - -void cEntity::SetRoll(double a_Roll) -{ - m_Rot.z = a_Roll; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) -{ - m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ); - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - -void cEntity::SetSpeedX(double a_SpeedX) -{ - m_Speed.x = a_SpeedX; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - -void cEntity::SetSpeedY(double a_SpeedY) -{ - m_Speed.y = a_SpeedY; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - -void cEntity::SetSpeedZ(double a_SpeedZ) -{ - m_Speed.z = a_SpeedZ; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::SetWidth(double a_Width) -{ - m_Width = a_Width; -} - - - - - -void cEntity::AddPosX(double a_AddPosX) -{ - m_Pos.x += a_AddPosX; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddPosY(double a_AddPosY) -{ - m_Pos.y += a_AddPosY; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddPosZ(double a_AddPosZ) -{ - m_Pos.z += a_AddPosZ; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ) -{ - m_Pos.x += a_AddPosX; - m_Pos.y += a_AddPosY; - m_Pos.z += a_AddPosZ; - m_bDirtyPosition = true; -} - - - - -void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ) -{ - m_Speed.x += a_AddSpeedX; - m_Speed.y += a_AddSpeedY; - m_Speed.z += a_AddSpeedZ; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::AddSpeedX(double a_AddSpeedX) -{ - m_Speed.x += a_AddSpeedX; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::AddSpeedY(double a_AddSpeedY) -{ - m_Speed.y += a_AddSpeedY; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::AddSpeedZ(double a_AddSpeedZ) -{ - m_Speed.z += a_AddSpeedZ; - m_bDirtySpeed = true; - WrapSpeed(); -} - - - - - -void cEntity::SteerVehicle(float a_Forward, float a_Sideways) -{ - if (m_AttachedTo == NULL) - { - return; - } - if ((a_Forward != 0) || (a_Sideways != 0)) - { - Vector3d LookVector = GetLookVector(); - double AddSpeedX = LookVector.x * a_Forward + LookVector.z * a_Sideways; - double AddSpeedZ = LookVector.z * a_Forward - LookVector.x * a_Sideways; - m_AttachedTo->AddSpeed(AddSpeedX, 0, AddSpeedZ); - } -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Get look vector (this is NOT a rotation!) -Vector3d cEntity::GetLookVector(void) const -{ - Matrix4d m; - m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y); - Vector3d Look = m.Transform(Vector3d(0, 0, 1)); - return Look; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Set position -void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ) -{ - m_Pos.Set(a_PosX, a_PosY, a_PosZ); - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosX(double a_PosX) -{ - m_Pos.x = a_PosX; - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosY(double a_PosY) -{ - m_Pos.y = a_PosY; - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosZ(double a_PosZ) -{ - m_Pos.z = a_PosZ; - m_bDirtyPosition = true; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Reference stuffs -void cEntity::AddReference(cEntity * & a_EntityPtr) -{ - m_References->AddReference(a_EntityPtr); - a_EntityPtr->ReferencedBy(a_EntityPtr); -} - - - - - -void cEntity::ReferencedBy(cEntity * & a_EntityPtr) -{ - m_Referencers->AddReference(a_EntityPtr); -} - - - - - -void cEntity::Dereference(cEntity * & a_EntityPtr) -{ - m_Referencers->Dereference(a_EntityPtr); -} - - - - diff --git a/source/Entities/Entity.h b/source/Entities/Entity.h deleted file mode 100644 index de5f176ae..000000000 --- a/source/Entities/Entity.h +++ /dev/null @@ -1,446 +0,0 @@ - -#pragma once - -#include "../Item.h" -#include "../Vector3d.h" -#include "../Vector3f.h" - - - - - -// Place this macro in the public section of each cEntity descendant class and you're done :) -#define CLASS_PROTODEF(classname) \ - virtual bool IsA(const char * a_ClassName) const override\ - { \ - return ((strcmp(a_ClassName, #classname) == 0) || super::IsA(a_ClassName)); \ - } \ - virtual const char * GetClass(void) const override \ - { \ - return #classname; \ - } \ - static const char * GetClassStatic(void) \ - { \ - return #classname; \ - } \ - virtual const char * GetParentClass(void) const override \ - { \ - return super::GetClass(); \ - } - - - - - -class cWorld; -class cReferenceManager; -class cClientHandle; -class cPlayer; -class cChunk; - - - - - -// tolua_begin -struct TakeDamageInfo -{ - eDamageType DamageType; // Where does the damage come from? Being hit / on fire / contact with cactus / ... - cEntity * Attacker; // The attacking entity; valid only for dtAttack - int RawDamage; // What damage would the receiver get without any armor. Usually: attacker mob type + weapons - int FinalDamage; // What actual damage will be received. Usually: m_RawDamage minus armor - Vector3d Knockback; // The amount and direction of knockback received from the damage - // TODO: Effects - list of effects that the hit is causing. Unknown representation yet -} ; -// tolua_end - - - - - -// tolua_begin -class cEntity -{ -public: - - enum eEntityType - { - etEntity, // For all other types - etPlayer, - etPickup, - etMonster, - etFallingBlock, - etMinecart, - etBoat, - etTNT, - etProjectile, - etExpOrb, - - // Common variations - etMob = etMonster, // DEPRECATED, use etMonster instead! - } ; - - // tolua_end - - enum - { - ENTITY_STATUS_HURT = 2, - ENTITY_STATUS_DEAD = 3, - ENTITY_STATUS_WOLF_TAMING = 6, - ENTITY_STATUS_WOLF_TAMED = 7, - ENTITY_STATUS_WOLF_SHAKING = 8, - ENTITY_STATUS_EATING_ACCEPTED = 9, - ENTITY_STATUS_SHEEP_EATING = 10, - ENTITY_STATUS_GOLEM_ROSING = 11, - ENTITY_STATUS_VILLAGER_HEARTS = 12, - ENTITY_STATUS_VILLAGER_ANGRY = 13, - ENTITY_STATUS_VILLAGER_HAPPY = 14, - ENTITY_STATUS_WITCH_MAGICKING = 15, - // It seems 16 (zombie conversion) is now done with metadata - ENTITY_STATUS_FIREWORK_EXPLODE= 17, - } ; - - enum - { - FIRE_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in fire - FIRE_DAMAGE = 1, ///< How much damage to deal when standing in fire - LAVA_TICKS_PER_DAMAGE = 10, ///< How many ticks to wait between damaging an entity when it stands in lava - LAVA_DAMAGE = 5, ///< How much damage to deal when standing in lava - 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 - } ; - - cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); - virtual ~cEntity(); - - /// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed) - virtual bool Initialize(cWorld * a_World); - - // tolua_begin - - eEntityType GetEntityType(void) const { return m_EntityType; } - - bool IsPlayer (void) const { return (m_EntityType == etPlayer); } - bool IsPickup (void) const { return (m_EntityType == etPickup); } - bool IsMob (void) const { return (m_EntityType == etMonster); } - bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } - bool IsMinecart (void) const { return (m_EntityType == etMinecart); } - bool IsBoat (void) const { return (m_EntityType == etBoat); } - bool IsTNT (void) const { return (m_EntityType == etTNT); } - bool IsProjectile (void) const { return (m_EntityType == etProjectile); } - - /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) - virtual bool IsA(const char * a_ClassName) const; - - /// Returns the topmost class name for the object - virtual const char * GetClass(void) const; - - // Returns the class name of this class - static const char * GetClassStatic(void); - - /// Returns the topmost class's parent class name for the object. cEntity returns an empty string (no parent). - virtual const char * GetParentClass(void) const; - - cWorld * GetWorld(void) const { return m_World; } - - double GetHeadYaw (void) const { return m_HeadYaw; } - double GetHeight (void) const { return m_Height; } - double GetMass (void) const { return m_Mass; } - const Vector3d & GetPosition (void) const { return m_Pos; } - double GetPosX (void) const { return m_Pos.x; } - double GetPosY (void) const { return m_Pos.y; } - double GetPosZ (void) const { return m_Pos.z; } - const Vector3d & GetRot (void) const { return m_Rot; } - double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead - double GetYaw (void) const { return m_Rot.x; } - double GetPitch (void) const { return m_Rot.y; } - double GetRoll (void) const { return m_Rot.z; } - Vector3d GetLookVector(void) const; - const Vector3d & GetSpeed (void) const { return m_Speed; } - double GetSpeedX (void) const { return m_Speed.x; } - double GetSpeedY (void) const { return m_Speed.y; } - double GetSpeedZ (void) const { return m_Speed.z; } - double GetWidth (void) const { return m_Width; } - - int GetChunkX(void) const {return (int)floor(m_Pos.x / cChunkDef::Width); } - int GetChunkZ(void) const {return (int)floor(m_Pos.z / cChunkDef::Width); } - - void SetHeadYaw (double a_HeadYaw); - void SetHeight (double a_Height); - void SetMass (double a_Mass); - void SetPosX (double a_PosX); - void SetPosY (double a_PosY); - void SetPosZ (double a_PosZ); - void SetPosition(double a_PosX, double a_PosY, double a_PosZ); - void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); } - void SetRot (const Vector3f & a_Rot); - void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead - void SetYaw (double a_Yaw); - void SetPitch (double a_Pitch); - void SetRoll (double a_Roll); - void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ); - void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); } - void SetSpeedX (double a_SpeedX); - void SetSpeedY (double a_SpeedY); - void SetSpeedZ (double a_SpeedZ); - void SetWidth (double a_Width); - - void AddPosX (double a_AddPosX); - void AddPosY (double a_AddPosY); - void AddPosZ (double a_AddPosZ); - void AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ); - void AddPosition(const Vector3d & a_AddPos) { AddPosition(a_AddPos.x,a_AddPos.y,a_AddPos.z);} - void AddSpeed (double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeedZ); - void AddSpeed (const Vector3d & a_AddSpeed) { AddSpeed(a_AddSpeed.x,a_AddSpeed.y,a_AddSpeed.z);} - void AddSpeedX (double a_AddSpeedX); - void AddSpeedY (double a_AddSpeedY); - void AddSpeedZ (double a_AddSpeedZ); - - void SteerVehicle(float a_Forward, float a_Sideways); - - inline int GetUniqueID(void) const { return m_UniqueID; } - inline bool IsDestroyed(void) const { return !m_IsInitialized; } - - /// Schedules the entity for destroying; if a_ShouldBroadcast is set to true, broadcasts the DestroyEntity packet - void Destroy(bool a_ShouldBroadcast = true); - - /// Makes this pawn take damage from an attack by a_Attacker. Damage values are calculated automatically and DoTakeDamage() called - void TakeDamage(cEntity & a_Attacker); - - /// Makes this entity take the specified damage. The final damage is calculated using current armor, then DoTakeDamage() called - void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, double a_KnockbackAmount); - - /// Makes this entity take the specified damage. The values are packed into a TDI, knockback calculated, then sent through DoTakeDamage() - void TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_RawDamage, int a_FinalDamage, double a_KnockbackAmount); - - float GetGravity(void) const { return m_Gravity; } - - void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; } - - /// Sets the rotation to match the speed vector (entity goes "face-forward") - void SetRotationFromSpeed(void); - - /// Sets the pitch to match the speed vector (entity gies "face-forward") - void SetPitchFromSpeed(void); - - // tolua_end - - /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied - virtual void DoTakeDamage(TakeDamageInfo & a_TDI); - - // tolua_begin - - /// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items - virtual int GetRawDamageAgainst(const cEntity & a_Receiver); - - /// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover - virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage); - - /// Returns the knockback amount that the currently equipped items would cause to a_Receiver on a hit - virtual double GetKnockbackAmountAgainst(const cEntity & a_Receiver); - - /// Returns the curently equipped weapon; empty item if none - virtual cItem GetEquippedWeapon(void) const { return cItem(); } - - /// Returns the currently equipped helmet; empty item if none - virtual cItem GetEquippedHelmet(void) const { return cItem(); } - - /// Returns the currently equipped chestplate; empty item if none - virtual cItem GetEquippedChestplate(void) const { return cItem(); } - - /// Returns the currently equipped leggings; empty item if none - virtual cItem GetEquippedLeggings(void) const { return cItem(); } - - /// Returns the currently equipped boots; empty item if none - virtual cItem GetEquippedBoots(void) const { return cItem(); } - - /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) - virtual void KilledBy(cEntity * a_Killer); - - /// Heals the specified amount of HPs - void Heal(int a_HitPoints); - - /// Returns the health of this entity - int GetHealth(void) const { return m_Health; } - - /// Sets the health of this entity; doesn't broadcast any hurt animation - void SetHealth(int a_Health); - - // tolua_end - - virtual void Tick(float a_Dt, cChunk & a_Chunk); - - /// Handles the physics of the entity - updates position based on speed, updates speed based on environment - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); - - /// Updates the state related to this entity being on fire - virtual void TickBurning(cChunk & a_Chunk); - - /// Handles when the entity is in the void - virtual void TickInVoid(cChunk & a_Chunk); - - /// Called when the entity starts burning - virtual void OnStartedBurning(void); - - /// Called when the entity finishes burning - virtual void OnFinishedBurning(void); - - // tolua_begin - - /// Sets the maximum value for the health - void SetMaxHealth(int a_MaxHealth); - - int GetMaxHealth(void) const { return m_MaxHealth; } - - /// Puts the entity on fire for the specified amount of ticks - void StartBurning(int a_TicksLeftBurning); - - /// Stops the entity from burning, resets all burning timers - void StopBurning(void); - - // tolua_end - - /** Descendants override this function to send a command to the specified client to spawn the entity on the client. - To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity() - */ - virtual void SpawnOn(cClientHandle & a_Client) = 0; - - // tolua_begin - - /// Teleports to the entity specified - virtual void TeleportToEntity(cEntity & a_Entity); - - /// Teleports to the coordinates specified - virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ); - - // tolua_end - - /// Updates clients of changes in the entity. - virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL); - - /// Attaches to the specified entity; detaches from any previous one first - void AttachTo(cEntity * a_AttachTo); - - /// Detaches from the currently attached entity, if any - void Detach(void); - - /// Makes sure head yaw is not over the specified range. - void WrapHeadYaw(); - - /// Makes sure rotation is not over the specified range. - void WrapRotation(); - - /// Makes speed is not over 20. Max speed is 20 blocks / second - void WrapSpeed(); - - // tolua_begin - - // COMMON metadata flags; descendants may override the defaults: - virtual bool IsOnFire (void) const {return (m_TicksLeftBurning > 0); } - virtual bool IsCrouched (void) const {return false; } - 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; } - - // tolua_end - - /// Called when the specified player right-clicks this entity - virtual void OnRightClicked(cPlayer & a_Player) {}; - - /// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy(). - virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) {} - -protected: - static cCriticalSection m_CSCount; - static int m_EntityCount; - - int m_UniqueID; - - int m_Health; - int m_MaxHealth; - - /// The entity to which this entity is attached (vehicle), NULL if none - cEntity * m_AttachedTo; - - /// The entity which is attached to this entity (rider), NULL if none - cEntity * m_Attachee; - - cReferenceManager* m_Referencers; - cReferenceManager* m_References; - - // Flags that signal that we haven't updated the clients with the latest. - bool m_bDirtyHead; - bool m_bDirtyOrientation; - bool m_bDirtyPosition; - bool m_bDirtySpeed; - - bool m_bOnGround; - float m_Gravity; - - // Last Position. - double m_LastPosX, m_LastPosY, m_LastPosZ; - - // This variables keep track of the last time a packet was sent - Int64 m_TimeLastTeleportPacket,m_TimeLastMoveReltPacket,m_TimeLastSpeedPacket; // In ticks - - bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() ) - - eEntityType m_EntityType; - - cWorld * m_World; - - /// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire()) - int m_TicksSinceLastBurnDamage; - - /// Time, in ticks, since the last damage dealt by standing in lava. Reset to zero when moving out of lava. - int m_TicksSinceLastLavaDamage; - - /// Time, in ticks, since the last damage dealt by standing in fire. Reset to zero when moving out of fire. - int m_TicksSinceLastFireDamage; - - /// Time, in ticks, until the entity extinguishes its fire - int m_TicksLeftBurning; - - /// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void. - int m_TicksSinceLastVoidDamage; - - virtual void Destroyed(void) {} // Called after the entity has been destroyed - - void SetWorld(cWorld * a_World) { m_World = a_World; } - - friend class cReferenceManager; - void AddReference( cEntity*& a_EntityPtr ); - void ReferencedBy( cEntity*& a_EntityPtr ); - void Dereference( cEntity*& a_EntityPtr ); - -private: - // Measured in degrees (MAX 360°) - double m_HeadYaw; - // Measured in meter/second (m/s) - Vector3d m_Speed; - // Measured in degrees (MAX 360°) - Vector3d m_Rot; - - /// Position of the entity's XZ center and Y bottom - Vector3d m_Pos; - - // Measured in meter / second - Vector3d m_WaterSpeed; - - // Measured in Kilograms (Kg) - double m_Mass; - - /// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. - double m_Width; - - /// Height of the entity (Y axis) - double m_Height; -} ; // tolua_export - -typedef std::list<cEntity *> cEntityList; - - - - diff --git a/source/Entities/ExpOrb.cpp b/source/Entities/ExpOrb.cpp deleted file mode 100644 index 1e5ee00ce..000000000 --- a/source/Entities/ExpOrb.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Globals.h" - -#include "ExpOrb.h" -#include "Player.h" -#include "../ClientHandle.h" - - -cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) : - cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98), - m_Reward(a_Reward) -{ -} - - - - - -cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward) : - cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), - m_Reward(a_Reward) -{ -} - - - - - -void cExpOrb::SpawnOn(cClientHandle & a_Client) -{ - a_Client.SendExperienceOrb(*this); - m_bDirtyPosition = false; - m_bDirtySpeed = false; - m_bDirtyOrientation = false; - m_bDirtyHead = false; -} - - - - - -void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) -{ - cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 4)); - if (a_ClosestPlayer) - { - Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition()); - Vector3f a_Distance(a_PlayerPos - GetPosition()); - if (a_Distance.Length() < 0.1f) - { - a_ClosestPlayer->DeltaExperience(m_Reward); - a_ClosestPlayer->SendExperience(); - Destroy(true); - } - a_Distance.y = 0; - a_Distance.Normalize(); - a_Distance *= 3; - SetSpeedX( a_Distance.x ); - SetSpeedZ( a_Distance.z ); - } -}
\ No newline at end of file diff --git a/source/Entities/ExpOrb.h b/source/Entities/ExpOrb.h deleted file mode 100644 index a062eedd3..000000000 --- a/source/Entities/ExpOrb.h +++ /dev/null @@ -1,29 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - - -class cExpOrb : - public cEntity -{ - typedef cExpOrb super; - -public: - - cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward); - cExpOrb(const Vector3d & a_Pos, int a_Reward); - - // Override functions - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void SpawnOn(cClientHandle & a_Client) override; - - // cExpOrb functions - int GetReward(void) const { return m_Reward; } - -protected: - int m_Reward; -} ;
\ No newline at end of file diff --git a/source/Entities/FallingBlock.cpp b/source/Entities/FallingBlock.cpp deleted file mode 100644 index 9fcd9ac80..000000000 --- a/source/Entities/FallingBlock.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "Globals.h" - -#include "FallingBlock.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "../Simulator/SandSimulator.h" -#include "../Chunk.h" - - - - - -cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : - super(etFallingBlock, a_BlockPosition.x + 0.5f, a_BlockPosition.y + 0.5f, a_BlockPosition.z + 0.5f, 0.98, 0.98), - m_BlockType(a_BlockType), - m_BlockMeta(a_BlockMeta), - m_OriginalPosition(a_BlockPosition) -{ -} - - - - - -void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnFallingBlock(*this); -} - - - - - -void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) -{ - float MilliDt = a_Dt * 0.001f; - AddSpeedY(MilliDt * -9.8f); - AddPosY(GetSpeedY() * MilliDt); - - // GetWorld()->BroadcastTeleportEntity(*this); // Test position - - int BlockX = m_OriginalPosition.x; - int BlockY = (int)(GetPosY() - 0.5); - int BlockZ = m_OriginalPosition.z; - - if (BlockY < 0) - { - // Fallen out of this world, just continue falling until out of sight, then destroy: - if (BlockY < 100) - { - Destroy(true); - } - return; - } - - if (BlockY >= cChunkDef::Height) - { - // Above the world, just wait for it to fall back down - return; - } - - int idx = a_Chunk.MakeIndexNoCheck(BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width); - BLOCKTYPE BlockBelow = a_Chunk.GetBlock(idx); - NIBBLETYPE BelowMeta = a_Chunk.GetMeta(idx); - if (cSandSimulator::DoesBreakFallingThrough(BlockBelow, BelowMeta)) - { - // Fallen onto a block that breaks this into pickups (e. g. half-slab) - // Must finish the fall with coords one below the block: - cSandSimulator::FinishFalling(m_World, BlockX, BlockY, BlockZ, m_BlockType, m_BlockMeta); - Destroy(true); - return; - } - else if (!cSandSimulator::CanContinueFallThrough(BlockBelow)) - { - // Fallen onto a solid block - /* - LOGD( - "Sand: Checked below at {%d, %d, %d} (rel {%d, %d, %d}), it's %s, finishing the fall.", - BlockX, BlockY, BlockZ, - BlockX - a_Chunk.GetPosX() * cChunkDef::Width, BlockY, BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width, - ItemTypeToString(BlockBelow).c_str() - ); - */ - - cSandSimulator::FinishFalling(m_World, BlockX, BlockY + 1, BlockZ, m_BlockType, m_BlockMeta); - Destroy(true); - return; - } -} - - - - diff --git a/source/Entities/FallingBlock.h b/source/Entities/FallingBlock.h deleted file mode 100644 index 5ba9909bb..000000000 --- a/source/Entities/FallingBlock.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - -class cPlayer; -class cItem; - - - - - - -class cFallingBlock : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cFallingBlock); - - /// Creates a new falling block. a_BlockPosition is expected in world coords - cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - BLOCKTYPE GetBlockType(void) const { return m_BlockType; } - NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; } - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -private: - BLOCKTYPE m_BlockType; - NIBBLETYPE m_BlockMeta; - Vector3i m_OriginalPosition; // Position where the falling block has started, in world coords -} ; - - - - diff --git a/source/Entities/Minecart.cpp b/source/Entities/Minecart.cpp deleted file mode 100644 index f75e23d8b..000000000 --- a/source/Entities/Minecart.cpp +++ /dev/null @@ -1,541 +0,0 @@ - -// Minecart.cpp - -// Implements the cMinecart class representing a minecart in the world -// Indiana Jones! - -#include "Globals.h" -#include "Minecart.h" -#include "../World.h" -#include "../ClientHandle.h" -#include "../Chunk.h" -#include "Player.h" - - - - - -cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) : - super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7), - m_Payload(a_Payload), - m_LastDamage(0) -{ - SetMass(20.f); - SetMaxHealth(6); - SetHealth(6); -} - - - - -void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) -{ - char SubType = 0; - switch (m_Payload) - { - case mpNone: SubType = 0; break; - case mpChest: SubType = 1; break; - case mpFurnace: SubType = 2; break; - case mpTNT: SubType = 3; break; - case mpHopper: SubType = 5; break; - default: - { - ASSERT(!"Unknown payload, cannot spawn on client"); - return; - } - } - a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart -} - - - - - -void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - int PosY = (int)floor(GetPosY()); - if ((PosY <= 0) || (PosY >= cChunkDef::Height)) - { - // Outside the world, just process normal falling physics - super::HandlePhysics(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - return; - } - - int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - if (Chunk == NULL) - { - // Inside an unloaded chunk, bail out all processing - return; - } - BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ); - BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ); - - if (IsBlockRail(BelowType)) - { - HandleRailPhysics(a_Dt, *Chunk); - } - else - { - if (IsBlockRail(InsideType)) - { - SetPosY(PosY + 1); - HandleRailPhysics(a_Dt, *Chunk); - } - else - { - super::HandlePhysics(a_Dt, *Chunk); - BroadcastMovementUpdate(); - } - } -} - - - - - -static const double MAX_SPEED = 8; -static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED); - -void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk) -{ - - super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling - - /* - NOTE: Please bear in mind that taking away from negatives make them even more negative, - adding to negatives make them positive, etc. - */ - - // Get block meta below the cart - int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width; - NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ); - double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed - - switch (BelowMeta) - { - case E_META_RAIL_ZM_ZP: // NORTHSOUTH - { - SetRotation(270); - SpeedY = 0; // Don't move vertically as on ground - SpeedX = 0; // Correct diagonal movement from curved rails - - if (SpeedZ != 0) // Don't do anything if cart is stationary - { - if (SpeedZ > 0) - { - // Going SOUTH, slow down - SpeedZ = SpeedZ - 0.1; - } - else - { - // Going NORTH, slow down - SpeedZ = SpeedZ + 0.1; - } - } - break; - } - - case E_META_RAIL_XM_XP: // EASTWEST - { - SetRotation(180); - SpeedY = 0; - SpeedZ = 0; - - if (SpeedX != 0) - { - if (SpeedX > 0) - { - SpeedX = SpeedX - 0.1; - } - else - { - SpeedX = SpeedX + 0.1; - } - } - break; - } - - case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH - { - SetRotation(270); - SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/ - SpeedX = 0; - - if (SpeedZ >= 0) - { - // SpeedZ POSITIVE, going SOUTH - if (SpeedZ <= MAX_SPEED) // Speed limit - { - SpeedZ = SpeedZ + 0.5; // Speed up - SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative) - } - else - { - SpeedZ = MAX_SPEED; // Enforce speed limit - SpeedY = (0 - SpeedZ); - } - } - else - { - // SpeedZ NEGATIVE, going NORTH - SpeedZ = SpeedZ + 0.4; // Slow down - SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number) - } - break; - } - - case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH - { - SetRotation(270); - SetPosY(floor(GetPosY()) + 0.2); - SpeedX = 0; - - if (SpeedZ > 0) - { - // SpeedZ POSITIVE, going SOUTH - SpeedZ = SpeedZ - 0.4; // Slow down - SpeedY = SpeedZ; // Upward movement positive - } - else - { - if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit - { - // SpeedZ NEGATIVE, going NORTH - SpeedZ = SpeedZ - 0.5; // Speed up - SpeedY = SpeedZ; // Downward movement negative - } - else - { - SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit - SpeedY = SpeedZ; - } - } - break; - } - - case E_META_RAIL_ASCEND_XM: // ASCEND EAST - { - SetRotation(180); - SetPosY(floor(GetPosY()) + 0.2); - SpeedZ = 0; - - if (SpeedX >= 0) - { - if (SpeedX <= MAX_SPEED) - { - SpeedX = SpeedX + 0.5; - SpeedY = (0 - SpeedX); - } - else - { - SpeedX = MAX_SPEED; - SpeedY = (0 - SpeedX); - } - } - else - { - SpeedX = SpeedX + 0.4; - SpeedY = (0 - SpeedX); - } - break; - } - - case E_META_RAIL_ASCEND_XP: // ASCEND WEST - { - SetRotation(180); - SetPosY(floor(GetPosY()) + 0.2); - SpeedZ = 0; - - if (SpeedX > 0) - { - SpeedX = SpeedX - 0.4; - SpeedY = SpeedX; - } - else - { - if (SpeedX >= MAX_SPEED_NEGATIVE) - { - SpeedX = SpeedX - 0.5; - SpeedY = SpeedX; - } - else - { - SpeedX = MAX_SPEED_NEGATIVE; - SpeedY = SpeedX; - } - } - break; - } - - case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST - { - SetRotation(315); // Set correct rotation server side - SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart - - if (SpeedZ > 0) // Cart moving south - { - SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail) - } - else if (SpeedX > 0) // Cart moving east - { - SpeedZ = (0 - SpeedX); // Diagonally move northeast - } - break; - } - - case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST - { - SetRotation(225); - SetPosY(floor(GetPosY()) + 0.2); - - if (SpeedZ > 0) - { - SpeedX = SpeedZ; - } - else if (SpeedX < 0) - { - SpeedZ = SpeedX; - } - break; - } - - case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST - { - SetRotation(135); - SetPosY(floor(GetPosY()) + 0.2); - - if (SpeedZ < 0) - { - SpeedX = SpeedZ; - } - else if (SpeedX > 0) - { - SpeedZ = SpeedX; - } - break; - } - - case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST - { - SetRotation(45); - SetPosY(floor(GetPosY()) + 0.2); - - if (SpeedZ < 0) - { - SpeedX = (0 - SpeedZ); - } - else if (SpeedX < 0) - { - SpeedZ = (0 - SpeedX); - } - break; - } - - default: - { - ASSERT(!"Unhandled rail meta!"); // Dun dun DUN! - break; - } - } - - // Set speed to speed variables - SetSpeedX(SpeedX); - SetSpeedY(SpeedY); - SetSpeedZ(SpeedZ); - - - // Broadcast position to client - BroadcastMovementUpdate(); -} - - - - - -void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) -{ - m_LastDamage = TDI.FinalDamage; - super::DoTakeDamage(TDI); - - m_World->BroadcastEntityMetadata(*this); - - if (GetHealth() <= 0) - { - Destroy(true); - - cItems Drops; - switch (m_Payload) - { - case mpNone: - { - Drops.push_back(cItem(E_ITEM_MINECART, 1, 0)); - break; - } - case mpChest: - { - Drops.push_back(cItem(E_ITEM_CHEST_MINECART, 1, 0)); - break; - } - case mpFurnace: - { - Drops.push_back(cItem(E_ITEM_FURNACE_MINECART, 1, 0)); - break; - } - case mpTNT: - { - Drops.push_back(cItem(E_ITEM_MINECART_WITH_TNT, 1, 0)); - break; - } - case mpHopper: - { - Drops.push_back(cItem(E_ITEM_MINECART_WITH_HOPPER, 1, 0)); - break; - } - default: - { - ASSERT(!"Unhandled minecart type when spawning pickup!"); - return; - } - } - - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cEmptyMinecart: - -cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) : - super(mpNone, a_X, a_Y, a_Z) -{ -} - - - - - -void cEmptyMinecart::OnRightClicked(cPlayer & a_Player) -{ - if (m_Attachee != NULL) - { - if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID()) - { - // This player is already sitting in, they want out. - a_Player.Detach(); - return; - } - - if (m_Attachee->IsPlayer()) - { - // Another player is already sitting in here, cannot attach - return; - } - - // Detach whatever is sitting in this minecart now: - m_Attachee->Detach(); - } - - // Attach the player to this minecart - a_Player.AttachTo(this); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithChest: - -cMinecartWithChest::cMinecartWithChest(double a_X, double a_Y, double a_Z) : - super(mpChest, a_X, a_Y, a_Z) -{ -} - - - - - -void cMinecartWithChest::SetSlot(int a_Idx, const cItem & a_Item) -{ - ASSERT((a_Idx >= 0) && (a_Idx < ARRAYCOUNT(m_Items))); - - m_Items[a_Idx] = a_Item; -} - - - - - -void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) -{ - // Show the chest UI window to the player - // TODO -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithFurnace: - -cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : - super(mpFurnace, a_X, a_Y, a_Z), - m_IsFueled(false) -{ -} - - - - - -void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) -{ - if (a_Player.GetEquippedItem().m_ItemType == E_ITEM_COAL) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - - m_IsFueled = true; - m_World->BroadcastEntityMetadata(*this); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithTNT: - -cMinecartWithTNT::cMinecartWithTNT(double a_X, double a_Y, double a_Z) : - super(mpTNT, a_X, a_Y, a_Z) -{ -} - -// TODO: Make it activate when passing over activator rail - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cMinecartWithHopper: - -cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) : - super(mpHopper, a_X, a_Y, a_Z) -{ -} - -// TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks -// AND AVARYTHING!!
\ No newline at end of file diff --git a/source/Entities/Minecart.h b/source/Entities/Minecart.h deleted file mode 100644 index b1b48be4e..000000000 --- a/source/Entities/Minecart.h +++ /dev/null @@ -1,169 +0,0 @@ - -// Minecart.h - -// Declares the cMinecart class representing a minecart in the world - - - - - -#pragma once - -#include "Entity.h" - - - - - -inline bool IsBlockRail(BLOCKTYPE a_BlockType) - { - return ( - (a_BlockType == E_BLOCK_RAIL) || - (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) || - (a_BlockType == E_BLOCK_DETECTOR_RAIL) || - (a_BlockType == E_BLOCK_POWERED_RAIL) - ) ; - } - - - - - -class cMinecart : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cMinecart); - - enum ePayload - { - mpNone, // Empty minecart, ridable by player or mobs - mpChest, // Minecart-with-chest, can store a grid of 3*8 items - mpFurnace, // Minecart-with-furnace, can be powered - mpTNT, // Minecart-with-TNT, can be blown up with activator rail - mpHopper, // Minecart-with-hopper, can be hopper - // TODO: Spawner minecarts, (and possibly any block in a minecart with NBT editing) - } ; - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - - int LastDamage(void) const { return m_LastDamage; } - void HandleRailPhysics(float a_Dt, cChunk & a_Chunk); - ePayload GetPayload(void) const { return m_Payload; } - -protected: - ePayload m_Payload; - - cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z); - - int m_LastDamage; - -} ; - - - - - -class cEmptyMinecart : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cEmptyMinecart); - - cEmptyMinecart(double a_X, double a_Y, double a_Z); - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - - -class cMinecartWithChest : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithChest); - - /// Number of item slots in the chest - static const int NumSlots = 9 * 3; - - cMinecartWithChest(double a_X, double a_Y, double a_Z); - - const cItem & GetSlot(int a_Idx) const { return m_Items[a_Idx]; } - cItem & GetSlot(int a_Idx) { return m_Items[a_Idx]; } - - void SetSlot(int a_Idx, const cItem & a_Item); - -protected: - - /// The chest contents: - cItem m_Items[NumSlots]; - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; -} ; - - - - - -class cMinecartWithFurnace : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithFurnace); - - cMinecartWithFurnace(double a_X, double a_Y, double a_Z); - - // cEntity overrides: - virtual void OnRightClicked(cPlayer & a_Player) override; - bool IsFueled (void) const { return m_IsFueled; } - -private: - - bool m_IsFueled; - -} ; - - - - - -class cMinecartWithTNT : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithTNT); - - cMinecartWithTNT(double a_X, double a_Y, double a_Z); -} ; - - - - - -class cMinecartWithHopper : - public cMinecart -{ - typedef cMinecart super; - -public: - CLASS_PROTODEF(cMinecartWithHopper); - - cMinecartWithHopper(double a_X, double a_Y, double a_Z); -} ;
\ No newline at end of file diff --git a/source/Entities/Pawn.cpp b/source/Entities/Pawn.cpp deleted file mode 100644 index fffefd538..000000000 --- a/source/Entities/Pawn.cpp +++ /dev/null @@ -1,19 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Pawn.h" - - - - - -cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) - : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height) - , m_bBurnable(true) -{ -} - - - - - diff --git a/source/Entities/Pawn.h b/source/Entities/Pawn.h deleted file mode 100644 index e76337d86..000000000 --- a/source/Entities/Pawn.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - - -// tolua_begin -class cPawn : - public cEntity -{ - // tolua_end - typedef cEntity super; - -public: - CLASS_PROTODEF(cPawn); - - cPawn(eEntityType a_EntityType, double a_Width, double a_Height); - -protected: - bool m_bBurnable; -} ; // tolua_export - - - - diff --git a/source/Entities/Pickup.cpp b/source/Entities/Pickup.cpp deleted file mode 100644 index f8aae9703..000000000 --- a/source/Entities/Pickup.cpp +++ /dev/null @@ -1,166 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 -#include <cstdlib> -#endif - -#include "Pickup.h" -#include "../ClientHandle.h" -#include "../Inventory.h" -#include "../World.h" -#include "../Simulator/FluidSimulator.h" -#include "../Server.h" -#include "Player.h" -#include "../PluginManager.h" -#include "../Item.h" -#include "../Root.h" -#include "../Chunk.h" - -#include "../Vector3d.h" -#include "../Vector3f.h" - - - - - -cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) - : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2) - , m_Timer( 0.f ) - , m_Item(a_Item) - , m_bCollected( false ) - , m_bIsPlayerCreated( IsPlayerCreated ) -{ - SetGravity(-10.5f); - SetMaxHealth(5); - SetHealth(5); - SetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); -} - - - - - -void cPickup::SpawnOn(cClientHandle & a_Client) -{ - a_Client.SendPickupSpawn(*this); -} - - - - - -void cPickup::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - BroadcastMovementUpdate(); //Notify clients of position - - m_Timer += a_Dt; - - if (!m_bCollected) - { - int BlockY = (int) floor(GetPosY()); - if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world - { - int BlockX = (int) floor(GetPosX()); - int BlockZ = (int) floor(GetPosZ()); - // Position might have changed due to physics. So we have to make sure we have the correct chunk. - cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ); - if (CurrentChunk != NULL) // Make sure the chunk is loaded - { - int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width); - int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width); - - // If the pickup is on the bottommost block position, make it think the void is made of air: (#131) - BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR; - BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ); - - if ( - IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) || - IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE) - ) - { - m_bCollected = true; - m_Timer = 0; // We have to reset the timer. - m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. - if (m_Timer > 500.f) - { - Destroy(true); - return; - } - } - } - } - } - else - { - if (m_Timer > 500.f) // 0.5 second - { - Destroy(true); - return; - } - } - - if (m_Timer > 1000 * 60 * 5) // 5 minutes - { - Destroy(true); - return; - } - - if (GetPosY() < -8) // Out of this world and no more visible! - { - Destroy(true); - return; - } -} - - - - - -bool cPickup::CollectedBy(cPlayer * a_Dest) -{ - ASSERT(a_Dest != NULL); - - if (m_bCollected) - { - // LOG("Pickup %d cannot be collected by \"%s\", because it has already been collected.", m_UniqueID, a_Dest->GetName().c_str()); - return false; // It's already collected! - } - - // Two seconds if player created the pickup (vomiting), half a second if anything else - if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f)) - { - // LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str()); - return false; // Not old enough - } - - if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this)) - { - // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str()); - return false; - } - - int NumAdded = a_Dest->GetInventory().AddItem(m_Item); - if (NumAdded > 0) - { - m_Item.m_ItemCount -= NumAdded; - m_World->BroadcastCollectPickup(*this, *a_Dest); - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - if (m_Item.m_ItemCount == 0) - { - // All of the pickup has been collected, schedule the pickup for destroying - m_bCollected = true; - } - m_Timer = 0; - return true; - } - - // LOG("Pickup %d cannot be collected by \"%s\", because there's no space in the inventory.", a_Dest->GetName().c_str(), m_UniqueID); - return false; -} - - - - diff --git a/source/Entities/Pickup.h b/source/Entities/Pickup.h deleted file mode 100644 index d39eda298..000000000 --- a/source/Entities/Pickup.h +++ /dev/null @@ -1,64 +0,0 @@ - -#pragma once - -#include "Entity.h" -#include "../Item.h" - - - - - -class cPlayer; - - - - - -// tolua_begin -class cPickup : - public cEntity -{ - // tolua_end - typedef cEntity super; - -public: - CLASS_PROTODEF(cPickup); - - cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export - - cItem & GetItem(void) {return m_Item; } // tolua_export - const cItem & GetItem(void) const {return m_Item; } - - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - - bool CollectedBy(cPlayer * a_Dest); // tolua_export - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - /// Returns the number of ticks that this entity has existed - int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export - - /// Returns true if the pickup has already been collected - bool IsCollected(void) const { return m_bCollected; } // tolua_export - - /// Returns true if created by player (i.e. vomiting), used for determining picking-up delay time - bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export - -private: - Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) - - Vector3d m_WaterSpeed; - - /// The number of ticks that the entity has existed / timer between collect and destroy; in msec - float m_Timer; - - cItem m_Item; - - bool m_bCollected; - - bool m_bIsPlayerCreated; -}; // tolua_export - - - - diff --git a/source/Entities/Player.cpp b/source/Entities/Player.cpp deleted file mode 100644 index f37a23f22..000000000 --- a/source/Entities/Player.cpp +++ /dev/null @@ -1,1765 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Player.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../UI/Window.h" -#include "../UI/WindowOwner.h" -#include "../World.h" -#include "Pickup.h" -#include "../PluginManager.h" -#include "../BlockEntities/BlockEntity.h" -#include "../GroupManager.h" -#include "../Group.h" -#include "../ChatColor.h" -#include "../Item.h" -#include "../Tracer.h" -#include "../Root.h" -#include "../OSSupport/Timer.h" -#include "../MersenneTwister.h" -#include "../Chunk.h" -#include "../Items/ItemHandler.h" - -#include "../Vector3d.h" -#include "../Vector3f.h" - -#include "../../iniFile/iniFile.h" -#include <json/json.h> - -#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) - - - - - - -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) - : super(etPlayer, 0.6, 1.8) - , m_GameMode(eGameMode_NotSet) - , m_IP("") - , m_LastBlockActionTime( 0 ) - , m_LastBlockActionCnt( 0 ) - , m_AirLevel( MAX_AIR_LEVEL ) - , m_AirTickTimer( DROWNING_TICKS ) - , m_bVisible( true ) - , m_LastGroundHeight( 0 ) - , m_bTouchGround( false ) - , m_Stance( 0.0 ) - , m_Inventory(*this) - , m_CurrentWindow(NULL) - , m_InventoryWindow(NULL) - , m_TimeLastPickupCheck( 0.f ) - , m_Color('-') - , m_ClientHandle( a_Client ) - , m_FoodLevel(MAX_FOOD_LEVEL) - , m_FoodSaturationLevel(5) - , m_FoodTickTimer(0) - , m_FoodExhaustionLevel(0) - , m_FoodPoisonedTicksRemaining(0) - , m_NormalMaxSpeed(0.1) - , m_SprintingMaxSpeed(0.13) - , m_IsCrouched(false) - , m_IsSprinting(false) - , m_IsSwimming(false) - , m_IsSubmerged(false) - , m_EatingFinishTick(-1) - , m_IsChargingBow(false) - , m_BowCharge(0) - , m_CurrentXp(0) - , m_LifetimeTotalXp(0) - , m_bDirtyExperience(false) -{ - LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", - a_PlayerName.c_str(), a_Client->GetIPString().c_str(), - this, GetUniqueID() - ); - - m_InventoryWindow = new cInventoryWindow(*this); - m_CurrentWindow = m_InventoryWindow; - m_InventoryWindow->OpenedByPlayer(*this); - - SetMaxHealth(MAX_HEALTH); - m_Health = MAX_HEALTH; - - cTimer t1; - m_LastPlayerListTime = t1.GetNowTime(); - - m_TimeLastTeleportPacket = 0; - m_TimeLastPickupCheck = 0; - - m_PlayerName = a_PlayerName; - m_bDirtyPosition = true; // So chunks are streamed to player at spawn - - if (!LoadFromDisk()) - { - m_Inventory.Clear(); - SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX()); - SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY()); - SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ()); - - LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", - a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ() - ); - } - m_LastJumpHeight = (float)(GetPosY()); - m_LastGroundHeight = (float)(GetPosY()); - m_Stance = GetPosY() + 1.62; - - cRoot::Get()->GetServer()->PlayerCreated(this); -} - - - - - -cPlayer::~cPlayer(void) -{ - LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); - - // Notify the server that the player is being destroyed - cRoot::Get()->GetServer()->PlayerDestroying(this); - - SaveToDisk(); - - m_World->RemovePlayer( this ); - - m_ClientHandle = NULL; - - delete m_InventoryWindow; - - LOGD("Player %p deleted", this); -} - - - - - -bool cPlayer::Initialize(cWorld * a_World) -{ - ASSERT(a_World != NULL); - - if (super::Initialize(a_World)) - { - // Remove the client handle from the server, it will be ticked from this object from now on - if (m_ClientHandle != NULL) - { - cRoot::Get()->GetServer()->ClientMovedToWorld(m_ClientHandle); - } - - GetWorld()->AddPlayer(this); - return true; - } - return false; -} - - - - - -void cPlayer::Destroyed() -{ - CloseWindow(false); - - m_ClientHandle = NULL; -} - - - - - -void cPlayer::SpawnOn(cClientHandle & a_Client) -{ - if (!m_bVisible || (m_ClientHandle == (&a_Client))) - { - return; - } - a_Client.SendPlayerSpawn(*this); - a_Client.SendEntityHeadLook(*this); - a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() ); - a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() ); - a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() ); - a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() ); - a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() ); -} - - - - - -void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) -{ - if (m_ClientHandle != NULL) - { - if (m_ClientHandle->IsDestroyed()) - { - // This should not happen, because destroying a client will remove it from the world, but just in case - m_ClientHandle = NULL; - return; - } - - if (!m_ClientHandle->IsPlaying()) - { - // We're not yet in the game, ignore everything - return; - } - } - - if (!a_Chunk.IsValid()) - { - // This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83) - return; - } - - 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) - { - m_BowCharge += 1; - } - - //handle updating experience - if (m_bDirtyExperience) - { - SendExperience(); - } - - if (m_bDirtyPosition) - { - // Apply food exhaustion from movement: - ApplyFoodExhaustionFromMovement(); - - cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); - BroadcastMovementUpdate(m_ClientHandle); - m_ClientHandle->StreamChunks(); - } - else - { - BroadcastMovementUpdate(m_ClientHandle); - } - - if (m_Health > 0) // make sure player is alive - { - m_World->CollectPickupsByPlayer(this); - - if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) - { - FinishEating(); - } - - HandleFood(); - } - - // Send Player List (Once per m_LastPlayerListTime/1000 ms) - cTimer t1; - if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) - { - m_World->SendPlayerList(this); - m_LastPlayerListTime = t1.GetNowTime(); - } -} - - - - - -short cPlayer::CalcLevelFromXp(short a_XpTotal) -{ - //level 0 to 15 - if(a_XpTotal <= XP_TO_LEVEL15) - { - return a_XpTotal / XP_PER_LEVEL_TO15; - } - - //level 30+ - if(a_XpTotal > XP_TO_LEVEL30) - { - return (short) (151.5 + sqrt( 22952.25 - (14 * (2220 - a_XpTotal)))) / 7; - } - - //level 16 to 30 - return (short) ( 29.5 + sqrt( 870.25 - (6 * ( 360 - a_XpTotal )))) / 3; -} - - - - - -short cPlayer::XpForLevel(short a_Level) -{ - //level 0 to 15 - if(a_Level <= 15) - { - return a_Level * XP_PER_LEVEL_TO15; - } - - //level 30+ - if(a_Level >= 31) - { - return (short) ( (3.5 * a_Level * a_Level) - (151.5 * a_Level) + 2220 ); - } - - //level 16 to 30 - return (short) ( (1.5 * a_Level * a_Level) - (29.5 * a_Level) + 360 ); -} - - - - - -short cPlayer::GetXpLevel() -{ - return CalcLevelFromXp(m_CurrentXp); -} - - - - - -float cPlayer::GetXpPercentage() -{ - short int currentLevel = CalcLevelFromXp(m_CurrentXp); - short int currentLevel_XpBase = XpForLevel(currentLevel); - - return (float)(m_CurrentXp - currentLevel_XpBase) / - (float)(XpForLevel(1+currentLevel) - currentLevel_XpBase); -} - - - - - -bool cPlayer::SetCurrentExperience(short int a_CurrentXp) -{ - if(!(a_CurrentXp >= 0) || (a_CurrentXp > (SHRT_MAX - m_LifetimeTotalXp))) - { - LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); - return false; //oops, they gave us a dodgey number - } - - m_CurrentXp = a_CurrentXp; - - // Set experience to be updated - m_bDirtyExperience = true; - - return true; -} - - - - - -short cPlayer::DeltaExperience(short a_Xp_delta) -{ - if (a_Xp_delta > (SHRT_MAX - m_CurrentXp)) - { - // Value was bad, abort and report - LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", - a_Xp_delta); - return -1; // Should we instead just return the current Xp? - } - - m_CurrentXp += a_Xp_delta; - - // Make sure they didn't subtract too much - if (m_CurrentXp < 0) - { - m_CurrentXp = 0; - } - - // Update total for score calculation - if (a_Xp_delta > 0) - { - m_LifetimeTotalXp += a_Xp_delta; - } - - LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", - m_PlayerName.c_str(), a_Xp_delta, m_CurrentXp); - - // Set experience to be updated - m_bDirtyExperience = true; - - return m_CurrentXp; -} - - - - - -void cPlayer::StartChargingBow(void) -{ - LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str()); - m_IsChargingBow = true; - m_BowCharge = 0; -} - - - - - -int cPlayer::FinishChargingBow(void) -{ - LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); - int res = m_BowCharge; - m_IsChargingBow = false; - m_BowCharge = 0; - return res; -} - - - - - -void cPlayer::CancelChargingBow(void) -{ - LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge); - m_IsChargingBow = false; - m_BowCharge = 0; -} - - - - - -void cPlayer::SetTouchGround(bool a_bTouchGround) -{ - m_bTouchGround = a_bTouchGround; - - if (!m_bTouchGround) - { - if (GetPosY() > m_LastJumpHeight) - { - m_LastJumpHeight = (float)GetPosY(); - } - cWorld * World = GetWorld(); - if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height)) - { - BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ())); - if (BlockType != E_BLOCK_AIR) - { - m_bTouchGround = true; - } - if ( - (BlockType == E_BLOCK_WATER) || - (BlockType == E_BLOCK_STATIONARY_WATER) || - (BlockType == E_BLOCK_LADDER) || - (BlockType == E_BLOCK_VINES) - ) - { - m_LastGroundHeight = (float)GetPosY(); - } - } - } - else - { - float Dist = (float)(m_LastGroundHeight - floor(GetPosY())); - int Damage = (int)(Dist - 3.f); - if (m_LastJumpHeight > m_LastGroundHeight) Damage++; - m_LastJumpHeight = (float)GetPosY(); - - if ((Damage > 0) && (!IsGameModeCreative())) - { - TakeDamage(dtFalling, NULL, Damage, Damage, 0); - } - - m_LastGroundHeight = (float)GetPosY(); - } -} - - - - - -void cPlayer::Heal(int a_Health) -{ - super::Heal(a_Health); - SendHealth(); -} - - - - - -void cPlayer::SetFoodLevel(int a_FoodLevel) -{ - m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); - SendHealth(); -} - - - - - -void cPlayer::SetFoodSaturationLevel(double a_FoodSaturationLevel) -{ - m_FoodSaturationLevel = std::max(0.0, std::min(a_FoodSaturationLevel, (double)m_FoodLevel)); -} - - - - - -void cPlayer::SetFoodTickTimer(int a_FoodTickTimer) -{ - m_FoodTickTimer = a_FoodTickTimer; -} - - - - - -void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel) -{ - m_FoodExhaustionLevel = std::max(0.0, std::min(a_FoodExhaustionLevel, 4.0)); -} - - - - - -void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) -{ - m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; -} - - - - - -bool cPlayer::Feed(int a_Food, double a_Saturation) -{ - if (m_FoodLevel >= MAX_FOOD_LEVEL) - { - return false; - } - - m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); - m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); - - SendHealth(); - return true; -} - - - - - -void cPlayer::FoodPoison(int a_NumTicks) -{ - bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); - m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); - if (!HasBeenFoodPoisoned) - { - // TODO: Send the poisoning indication to the client - how? - SendHealth(); - } -} - - - - - -void cPlayer::StartEating(void) -{ - // Set the timer: - m_EatingFinishTick = m_World->GetWorldAge() + EATING_TICKS; - - // Send the packets: - m_World->BroadcastPlayerAnimation(*this, 5); - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::FinishEating(void) -{ - // Reset the timer: - m_EatingFinishTick = -1; - - // Send the packets: - m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED); - m_World->BroadcastPlayerAnimation(*this, 0); - m_World->BroadcastEntityMetadata(*this); - - // consume the item: - cItem Item(GetEquippedItem()); - Item.m_ItemCount = 1; - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Item.m_ItemType); - if (!ItemHandler->EatItem(this, &Item)) - { - return; - } - ItemHandler->OnFoodEaten(m_World, this, &Item); - - GetInventory().RemoveOneEquippedItem(); - - //if the food is mushroom soup, return a bowl to the inventory - if( Item.m_ItemType == E_ITEM_MUSHROOM_SOUP ) { - cItem emptyBowl(E_ITEM_BOWL, 1, 0, ""); - GetInventory().AddItem(emptyBowl, true, true); - } -} - - - - - -void cPlayer::AbortEating(void) -{ - m_EatingFinishTick = -1; - m_World->BroadcastPlayerAnimation(*this, 0); - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::SendHealth(void) -{ - if (m_ClientHandle != NULL) - { - m_ClientHandle->SendHealth(); - } -} - - - - - -void cPlayer::SendExperience(void) -{ - if (m_ClientHandle != NULL) - { - m_ClientHandle->SendExperience(); - m_bDirtyExperience = false; - } -} - - - - - -void cPlayer::ClearInventoryPaintSlots(void) -{ - // Clear the list of slots that are being inventory-painted. Used by cWindow only - m_InventoryPaintSlots.clear(); -} - - - - - -void cPlayer::AddInventoryPaintSlot(int a_SlotNum) -{ - // Add a slot to the list for inventory painting. Used by cWindow only - m_InventoryPaintSlots.push_back(a_SlotNum); -} - - - - - -const cSlotNums & cPlayer::GetInventoryPaintSlots(void) const -{ - // Return the list of slots currently stored for inventory painting. Used by cWindow only - return m_InventoryPaintSlots; -} - - - - - -double cPlayer::GetMaxSpeed(void) const -{ - return m_IsSprinting ? m_SprintingMaxSpeed : m_NormalMaxSpeed; -} - - - - - -void cPlayer::SetNormalMaxSpeed(double a_Speed) -{ - m_NormalMaxSpeed = a_Speed; - if (!m_IsSprinting) - { - m_ClientHandle->SendPlayerMaxSpeed(); - } -} - - - - - -void cPlayer::SetSprintingMaxSpeed(double a_Speed) -{ - m_SprintingMaxSpeed = a_Speed; - if (m_IsSprinting) - { - m_ClientHandle->SendPlayerMaxSpeed(); - } -} - - - - - -void cPlayer::SetCrouch(bool a_IsCrouched) -{ - // Set the crouch status, broadcast to all visible players - - if (a_IsCrouched == m_IsCrouched) - { - // No change - return; - } - m_IsCrouched = a_IsCrouched; - m_World->BroadcastEntityMetadata(*this); -} - - - - - -void cPlayer::SetSprint(bool a_IsSprinting) -{ - if (a_IsSprinting == m_IsSprinting) - { - // No change - return; - } - - m_IsSprinting = a_IsSprinting; - m_ClientHandle->SendPlayerMaxSpeed(); -} - - - - - -void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - if (a_TDI.DamageType != dtInVoid) - { - if (IsGameModeCreative()) - { - // No damage / health in creative mode - return; - } - } - - super::DoTakeDamage(a_TDI); - - // Any kind of damage adds food exhaustion - AddFoodExhaustion(0.3f); - - SendHealth(); -} - - - - - -void cPlayer::KilledBy(cEntity * a_Killer) -{ - super::KilledBy(a_Killer); - - if (m_Health > 0) - { - return; // not dead yet =] - } - - m_bVisible = false; // So new clients don't see the player - - // Puke out all the items - cItems Pickups; - m_Inventory.CopyToItems(Pickups); - m_Inventory.Clear(); - m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); - SaveToDisk(); // Save it, yeah the world is a tough place ! -} - - - - - -void cPlayer::Respawn(void) -{ - m_Health = GetMaxHealth(); - - // Reset food level: - m_FoodLevel = MAX_FOOD_LEVEL; - m_FoodSaturationLevel = 5; - - // Reset Experience - m_CurrentXp = 0; - m_LifetimeTotalXp = 0; - // ToDo: send score to client? How? - - m_ClientHandle->SendRespawn(); - - // Extinguish the fire: - StopBurning(); - - TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); - - SetVisible(true); -} - - - - - -double cPlayer::GetEyeHeight(void) const -{ - return m_Stance; -} - - - - -Vector3d cPlayer::GetEyePosition(void) const -{ - return Vector3d( GetPosX(), m_Stance, GetPosZ() ); -} - - - - - -bool cPlayer::IsGameModeCreative(void) const -{ - return (m_GameMode == gmCreative) || // Either the player is explicitly in Creative - ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Creative -} - - - - - -bool cPlayer::IsGameModeSurvival(void) const -{ - return (m_GameMode == gmSurvival) || // Either the player is explicitly in Survival - ((m_GameMode == gmNotSet) && m_World->IsGameModeSurvival()); // or they inherit from the world and the world is Survival -} - - - - - -bool cPlayer::IsGameModeAdventure(void) const -{ - return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure - ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure -} - - - - - -void cPlayer::OpenWindow(cWindow * a_Window) -{ - if (a_Window != m_CurrentWindow) - { - CloseWindow(false); - } - a_Window->OpenedByPlayer(*this); - m_CurrentWindow = a_Window; - a_Window->SendWholeWindow(*GetClientHandle()); -} - - - - - -void cPlayer::CloseWindow(bool a_CanRefuse) -{ - if (m_CurrentWindow == NULL) - { - m_CurrentWindow = m_InventoryWindow; - return; - } - - if (m_CurrentWindow->ClosedByPlayer(*this, a_CanRefuse) || !a_CanRefuse) - { - // Close accepted, go back to inventory window (the default): - m_CurrentWindow = m_InventoryWindow; - } - else - { - // Re-open the window - m_CurrentWindow->OpenedByPlayer(*this); - m_CurrentWindow->SendWholeWindow(*GetClientHandle()); - } -} - - - - - -void cPlayer::CloseWindowIfID(char a_WindowID, bool a_CanRefuse) -{ - if ((m_CurrentWindow == NULL) || (m_CurrentWindow->GetWindowID() != a_WindowID)) - { - return; - } - CloseWindow(); -} - - - - - -void cPlayer::SetLastBlockActionTime() -{ - if (m_World != NULL) - { - m_LastBlockActionTime = m_World->GetWorldAge() / 20.0f; - } -} - - - - - -void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) -{ - m_LastBlockActionCnt = a_LastBlockActionCnt; -} - - - - - -void cPlayer::SetGameMode(eGameMode a_GameMode) -{ - if ((a_GameMode < gmMin) || (a_GameMode >= gmMax)) - { - LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); - return; - } - - if (m_GameMode == a_GameMode) - { - // Gamemode already set - return; - } - - m_GameMode = a_GameMode; - m_ClientHandle->SendGameMode(a_GameMode); -} - - - - - -void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) -{ - m_GameMode = a_GameMode; -} - - - - - -void cPlayer::SetIP(const AString & a_IP) -{ - m_IP = a_IP; -} - - - - - -void cPlayer::SendMessage(const AString & a_Message) -{ - m_ClientHandle->SendChat(a_Message); -} - - - - - -void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) -{ - SetPosition( a_PosX, a_PosY, a_PosZ ); - m_LastGroundHeight = (float)a_PosY; - - m_World->BroadcastTeleportEntity(*this, GetClientHandle()); - m_ClientHandle->SendPlayerMoveLook(); -} - - - - - -Vector3d cPlayer::GetThrowStartPos(void) const -{ - Vector3d res = GetEyePosition(); - - // Adjust the position to be just outside the player's bounding box: - res.x += 0.16 * cos(GetPitch()); - res.y += -0.1; - res.z += 0.16 * sin(GetPitch()); - - return res; -} - - - - - -Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const -{ - Vector3d res = GetLookVector(); - res.Normalize(); - - // TODO: Add a slight random change (+-0.0075 in each direction) - - return res * a_SpeedCoeff; -} - - - - - -void cPlayer::MoveTo( const Vector3d & a_NewPos ) -{ - if ((a_NewPos.y < -990) && (GetPosY() > -100)) - { - // When attached to an entity, the client sends position packets with weird coords: - // Y = -999 and X, Z = attempting to create speed, usually up to 0.03 - // We cannot test m_AttachedTo, because when deattaching, the server thinks the client is already deattached while - // the client may still send more of these nonsensical packets. - if (m_AttachedTo != NULL) - { - Vector3d AddSpeed(a_NewPos); - AddSpeed.y = 0; - m_AttachedTo->AddSpeed(AddSpeed); - } - return; - } - - // TODO: should do some checks to see if player is not moving through terrain - // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - - SetPosition( a_NewPos ); - SetStance(a_NewPos.y + 1.62); -} - - - - - -void cPlayer::SetVisible(bool a_bVisible) -{ - if (a_bVisible && !m_bVisible) // Make visible - { - m_bVisible = true; - m_World->BroadcastSpawnEntity(*this); - } - if (!a_bVisible && m_bVisible) - { - m_bVisible = false; - m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients - } -} - - - - - -void cPlayer::AddToGroup( const AString & a_GroupName ) -{ - cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); - m_Groups.push_back( Group ); - LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); - ResolveGroups(); - ResolvePermissions(); -} - - - - - -void cPlayer::RemoveFromGroup( const AString & a_GroupName ) -{ - bool bRemoved = false; - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->GetName().compare(a_GroupName ) == 0 ) - { - m_Groups.erase( itr ); - bRemoved = true; - break; - } - } - - if( bRemoved ) - { - LOGD("Removed %s from group %s", m_PlayerName.c_str(), a_GroupName.c_str() ); - ResolveGroups(); - ResolvePermissions(); - } - else - { - LOGWARN("Tried to remove %s from group %s but was not in that group", m_PlayerName.c_str(), a_GroupName.c_str() ); - } -} - - - - - -bool cPlayer::CanUseCommand( const AString & a_Command ) -{ - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - - - - - -bool cPlayer::HasPermission(const AString & a_Permission) -{ - if (a_Permission.empty()) - { - // Empty permission request is always granted - return true; - } - - AStringVector Split = StringSplit( a_Permission, "." ); - PermissionMap Possibilities = m_ResolvedPermissions; - // Now search the namespaces - while( Possibilities.begin() != Possibilities.end() ) - { - PermissionMap::iterator itr = Possibilities.begin(); - if( itr->second ) - { - AStringVector OtherSplit = StringSplit( itr->first, "." ); - if( OtherSplit.size() <= Split.size() ) - { - unsigned int i; - for( i = 0; i < OtherSplit.size(); ++i ) - { - if( OtherSplit[i].compare( Split[i] ) != 0 ) - { - if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! - break; - } - } - if( i == Split.size() ) return true; - } - } - Possibilities.erase( itr ); - } - - // Nothing that matched :( - return false; -} - - - - - -bool cPlayer::IsInGroup( const AString & a_Group ) -{ - for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) - { - if( a_Group.compare( (*itr)->GetName().c_str() ) == 0 ) - return true; - } - return false; -} - - - - - -void cPlayer::ResolvePermissions() -{ - m_ResolvedPermissions.clear(); // Start with an empty map yo~ - - // Copy all player specific permissions into the resolved permissions map - for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - - for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) - { - const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); - for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - } -} - - - - - -void cPlayer::ResolveGroups() -{ - // Clear resolved groups first - m_ResolvedGroups.clear(); - - // Get a complete resolved list of all groups the player is in - std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates - GroupList ToIterate; - for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) - { - ToIterate.push_back( *GroupItr ); - } - while( ToIterate.begin() != ToIterate.end() ) - { - cGroup* CurrentGroup = *ToIterate.begin(); - if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) - { - LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", - m_PlayerName.c_str(), CurrentGroup->GetName().c_str() - ); - } - else - { - AllGroups[ CurrentGroup ] = true; - m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list - const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); - for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) - { - if( AllGroups.find( *itr ) != AllGroups.end() ) - { - LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); - continue; - } - ToIterate.push_back( *itr ); - } - } - ToIterate.erase( ToIterate.begin() ); - } -} - - - - - -AString cPlayer::GetColor(void) const -{ - if ( m_Color != '-' ) - { - return cChatColor::MakeColor( m_Color ); - } - - if ( m_Groups.size() < 1 ) - { - return cChatColor::White; - } - - return (*m_Groups.begin())->GetColor(); -} - - - - - -void cPlayer::TossItem( - bool a_bDraggingItem, - char a_Amount /* = 1 */, - short a_CreateType /* = 0 */, - short a_CreateHealth /* = 0 */ -) -{ - cItems Drops; - if (a_CreateType != 0) - { - // Just create item without touching the inventory (used in creative mode) - Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); - } - else - { - // Drop an item from the inventory: - if (a_bDraggingItem) - { - cItem & Item = GetDraggingItem(); - if (!Item.IsEmpty()) - { - char OriginalItemAmount = Item.m_ItemCount; - Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount); - Drops.push_back(Item); - if (OriginalItemAmount > a_Amount) - { - Item.m_ItemCount = OriginalItemAmount - (char)a_Amount; - } - else - { - Item.Empty(); - } - } - } - else - { - // Else drop equipped item - cItem DroppedItem(GetInventory().GetEquippedItem()); - if (!DroppedItem.IsEmpty()) - { - if (GetInventory().RemoveOneEquippedItem()) - { - DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again - Drops.push_back(DroppedItem); - } - } - } - } - double vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player -} - - - - - -bool cPlayer::MoveToWorld(const char * a_WorldName) -{ - cWorld * World = cRoot::Get()->GetWorld(a_WorldName); - if (World == NULL) - { - LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName); - return false; - } - - eDimension OldDimension = m_World->GetDimension(); - - // Remove all links to the old world - m_World->RemovePlayer(this); - m_ClientHandle->RemoveFromAllChunks(); - m_World->RemoveEntity(this); - - // If the dimension is different, we can send the respawn packet - // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 - m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension())); - - // Add player to all the necessary parts of the new world - SetWorld(World); - World->AddEntity(this); - World->AddPlayer(this); - - return true; -} - - - - - -void cPlayer::LoadPermissionsFromDisk() -{ - m_Groups.clear(); - m_Permissions.clear(); - - cIniFile IniFile; - if (IniFile.ReadFile("users.ini")) - { - std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); - if (!Groups.empty()) - { - AStringVector Split = StringSplit( Groups, "," ); - for( unsigned int i = 0; i < Split.size(); i++ ) - { - AddToGroup( Split[i].c_str() ); - } - } - else - { - AddToGroup("Default"); - } - - m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; - } - else - { - LOGWARN("Failed to read the users.ini file. The player will be added only to the Default group."); - AddToGroup("Default"); - } - ResolvePermissions(); -} - - - - -bool cPlayer::LoadFromDisk() -{ - LoadPermissionsFromDisk(); - - // Log player permissions, cause it's what the cool kids do - LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); - for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) LOGINFO("%s", itr->first.c_str() ); - } - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) - { - // This is a new player whom we haven't seen yet, bail out, let them have the defaults - return false; - } - - AString buffer; - if (f.ReadRestOfFile(buffer) != f.GetSize()) - { - LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); - return false; - } - f.Close(); //cool kids play nice - - Json::Value root; - Json::Reader reader; - if (!reader.parse(buffer, root, false)) - { - LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); - } - - Json::Value & JSON_PlayerPosition = root["position"]; - if (JSON_PlayerPosition.size() == 3) - { - SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); - SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); - SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_LastFoodPos = GetPosition(); - } - - Json::Value & JSON_PlayerRotation = root["rotation"]; - if (JSON_PlayerRotation.size() == 3) - { - SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); - SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); - SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); - } - - m_Health = root.get("health", 0).asInt(); - m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt(); - m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); - m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); - m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); - m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - m_LifetimeTotalXp = (short) root.get("xpTotal", 0).asInt(); - m_CurrentXp = (short) root.get("xpCurrent", 0).asInt(); - - //SetExperience(root.get("experience", 0).asInt()); - - m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); - - m_Inventory.LoadFromJson(root["inventory"]); - - m_LoadedWorldName = root.get("world", "world").asString(); - - LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", - m_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() - ); - - return true; -} - - - - - -bool cPlayer::SaveToDisk() -{ - cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); - - // create the JSON data - Json::Value JSON_PlayerPosition; - JSON_PlayerPosition.append(Json::Value(GetPosX())); - JSON_PlayerPosition.append(Json::Value(GetPosY())); - JSON_PlayerPosition.append(Json::Value(GetPosZ())); - - Json::Value JSON_PlayerRotation; - JSON_PlayerRotation.append(Json::Value(GetRotation())); - JSON_PlayerRotation.append(Json::Value(GetPitch())); - JSON_PlayerRotation.append(Json::Value(GetRoll())); - - Json::Value JSON_Inventory; - m_Inventory.SaveToJson(JSON_Inventory); - - Json::Value root; - root["position"] = JSON_PlayerPosition; - root["rotation"] = JSON_PlayerRotation; - root["inventory"] = JSON_Inventory; - root["health"] = m_Health; - root["xpTotal"] = m_LifetimeTotalXp; - root["xpCurrent"] = m_CurrentXp; - root["air"] = m_AirLevel; - root["food"] = m_FoodLevel; - root["foodSaturation"] = m_FoodSaturationLevel; - root["foodTickTimer"] = m_FoodTickTimer; - root["foodExhaustion"] = m_FoodExhaustionLevel; - root["world"] = GetWorld()->GetName(); - - if (m_GameMode == GetWorld()->GetGameMode()) - { - root["gamemode"] = (int) eGameMode_NotSet; - } - else - { - root["gamemode"] = (int) m_GameMode; - } - - Json::StyledWriter writer; - std::string JsonData = writer.write(root); - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmWrite)) - { - LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); - return false; - } - if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) - { - LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); - return false; - } - return true; -} - - - - - -cPlayer::StringList cPlayer::GetResolvedPermissions() -{ - StringList Permissions; - - const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; - for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) Permissions.push_back( itr->first ); - } - - return Permissions; -} - - - - - -void cPlayer::UseEquippedItem(void) -{ - if (IsGameModeCreative()) // No damage in creative - { - return; - } - - GetInventory().DamageEquippedItem(); -} - - - - - -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 - - // Remember the food level before processing, for later comparison - int LastFoodLevel = m_FoodLevel; - - // Heal or damage, based on the food level, using the m_FoodTickTimer: - if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) - { - m_FoodTickTimer++; - if (m_FoodTickTimer >= 80) - { - m_FoodTickTimer = 0; - - if (m_FoodLevel >= 17) - { - // Regenerate health from food, incur 3 pts of food exhaustion: - Heal(1); - m_FoodExhaustionLevel += 3; - } - else if (m_FoodLevel <= 0) - { - // Damage from starving - TakeDamage(dtStarving, NULL, 1, 1, 0); - } - } - } - - // Apply food poisoning food exhaustion: - if (m_FoodPoisonedTicksRemaining > 0) - { - m_FoodPoisonedTicksRemaining--; - m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick - } - - // Apply food exhaustion that has accumulated: - if (m_FoodExhaustionLevel >= 4) - { - m_FoodExhaustionLevel -= 4; - - if (m_FoodSaturationLevel >= 1) - { - m_FoodSaturationLevel -= 1; - } - else - { - m_FoodLevel = std::max(m_FoodLevel - 1, 0); - } - } - - if (m_FoodLevel != LastFoodLevel) - { - SendHealth(); - } -} - - - - - -void cPlayer::ApplyFoodExhaustionFromMovement() -{ - if (IsGameModeCreative()) - { - return; - } - - // Calculate the distance travelled, update the last pos: - Vector3d Movement(GetPosition() - m_LastFoodPos); - Movement.y = 0; // Only take XZ movement into account - m_LastFoodPos = GetPosition(); - - // If riding anything, apply no food exhaustion - if (m_AttachedTo != NULL) - { - return; - } - - // Apply the exhaustion based on distance travelled: - double BaseExhaustion = Movement.Length(); - if (IsSprinting()) - { - // 0.1 pt per meter sprinted - BaseExhaustion = BaseExhaustion * 0.1; - } - else if (IsSwimming()) - { - // 0.015 pt per meter swum - BaseExhaustion = BaseExhaustion * 0.015; - } - else - { - // 0.01 pt per meter walked / sneaked - BaseExhaustion = BaseExhaustion * 0.01; - } - m_FoodExhaustionLevel += BaseExhaustion; -} - - - - diff --git a/source/Entities/Player.h b/source/Entities/Player.h deleted file mode 100644 index 44cab7d74..000000000 --- a/source/Entities/Player.h +++ /dev/null @@ -1,456 +0,0 @@ - -#pragma once - -#include "Pawn.h" -#include "../Inventory.h" -#include "../Defines.h" -#include "../World.h" - - - - - -class cGroup; -class cWindow; -class cClientHandle; - - - - - -// tolua_begin -class cPlayer : - public cPawn -{ - typedef cPawn super; - -public: - enum - { - 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 - - CLASS_PROTODEF(cPlayer) - - - cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); - virtual ~cPlayer(); - - virtual bool Initialize(cWorld * a_World) override; - - virtual void SpawnOn(cClientHandle & a_Client) override; - - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { }; - - /// Returns the curently equipped weapon; empty item if none - virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } - - /// Returns the currently equipped helmet; empty item if nonte - virtual cItem GetEquippedHelmet(void) const override { return m_Inventory.GetEquippedHelmet(); } - - /// Returns the currently equipped chestplate; empty item if none - virtual cItem GetEquippedChestplate(void) const override { return m_Inventory.GetEquippedChestplate(); } - - /// Returns the currently equipped leggings; empty item if none - virtual cItem GetEquippedLeggings(void) const override { return m_Inventory.GetEquippedLeggings(); } - - /// Returns the currently equipped boots; empty item if none - virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); } - - - // tolua_begin - - /** Sets the experience total - Returns true on success - "should" really only be called at init or player death, plugins excepted - */ - bool SetCurrentExperience(short a_XpTotal); - - /* changes Xp by Xp_delta, you "shouldn't" inc more than MAX_EXPERIENCE_ORB_SIZE - Wont't allow xp to go negative - Returns the new current experience, -1 on error - */ - short DeltaExperience(short a_Xp_delta); - - /// Gets the experience total - XpTotal for score on death - inline short GetXpLifetimeTotal(void) { return m_LifetimeTotalXp; } - - /// Gets the currrent experience - inline short GetCurrentXp(void) { return m_CurrentXp; } - - /// Gets the current level - XpLevel - short GetXpLevel(void); - - /// Gets the experience bar percentage - XpP - float GetXpPercentage(void); - - /// Caculates the amount of XP needed for a given level, ref: http://minecraft.gamepedia.com/XP - static short XpForLevel(short int a_Level); - - /// inverse of XpForLevel, ref: http://minecraft.gamepedia.com/XP values are as per this with pre-calculations - static short CalcLevelFromXp(short int a_CurrentXp); - - // tolua_end - - /// Starts charging the equipped bow - void StartChargingBow(void); - - /// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged - int FinishChargingBow(void); - - /// Cancels the current bow charging - void CancelChargingBow(void); - - /// Returns true if the player is currently charging the bow - bool IsChargingBow(void) const { return m_IsChargingBow; } - - void SetTouchGround( bool a_bTouchGround ); - inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; } - double GetEyeHeight(void) const; // tolua_export - Vector3d GetEyePosition(void) const; // tolua_export - inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export - inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. - inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export - inline const cInventory & GetInventory(void) const { return m_Inventory; } - - inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export - - virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override; - - // tolua_begin - - /// Returns the position where projectiles thrown by this player should start, player eye position + adjustment - Vector3d GetThrowStartPos(void) const; - - /// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff. - Vector3d GetThrowSpeed(double a_SpeedCoeff) const; - - /// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable - eGameMode GetGameMode(void) const { return m_GameMode; } - - /// Returns the current effective gamemode (inherited gamemode is resolved before returning) - eGameMode GetEffectiveGameMode(void) const { return (m_GameMode == gmNotSet) ? m_World->GetGameMode() : m_GameMode; } - - /** Sets the gamemode for the player. - The gamemode may be gmNotSet, in that case the player inherits the world's gamemode. - Updates the gamemode on the client (sends the packet) - */ - void SetGameMode(eGameMode a_GameMode); - - /// Returns true if the player is in Creative mode, either explicitly, or by inheriting from current world - bool IsGameModeCreative(void) const; - - /// Returns true if the player is in Survival mode, either explicitly, or by inheriting from current world - bool IsGameModeSurvival(void) const; - - /// Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world - bool IsGameModeAdventure(void) const; - - AString GetIP(void) const { return m_IP; } // tolua_export - - // tolua_end - - void SetIP(const AString & a_IP); - - float GetLastBlockActionTime() { return m_LastBlockActionTime; } - int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } - void SetLastBlockActionCnt( int ); - void SetLastBlockActionTime(); - - // Sets the current gamemode, doesn't check validity, doesn't send update packets to client - void LoginSetGameMode(eGameMode a_GameMode); - - /// Tries to move to a new position, with attachment-related checks (y == -999) - void MoveTo(const Vector3d & a_NewPos); // tolua_export - - cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export - const cWindow * GetWindow(void) const { return m_CurrentWindow; } - - /// Opens the specified window; closes the current one first using CloseWindow() - void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - - // tolua_begin - - /// Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true - void CloseWindow(bool a_CanRefuse = true); - - /// Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow - void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true); - - cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } - - void SendMessage(const AString & a_Message); - - const AString & GetName(void) const { return m_PlayerName; } - void SetName(const AString & a_Name) { m_PlayerName = a_Name; } - - // tolua_end - - typedef std::list< cGroup* > GroupList; - typedef std::list< std::string > StringList; - - /// Adds a player to existing group or creates a new group when it doesn't exist - void AddToGroup( const AString & a_GroupName ); // tolua_export - - /// Removes a player from the group, resolves permissions and group inheritance (case sensitive) - void RemoveFromGroup( const AString & a_GroupName ); // tolua_export - - bool CanUseCommand( const AString & a_Command ); // tolua_export - bool HasPermission( const AString & a_Permission ); // tolua_export - const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << - StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << - bool IsInGroup( const AString & a_Group ); // tolua_export - - // tolua_begin - - /// Returns the full color code to use for this player, based on their primary group or set in m_Color - AString GetColor(void) const; - - void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); - - /// Heals the player by the specified amount of HPs (positive only); sends health update - void Heal(int a_Health); - - int GetFoodLevel (void) const { return m_FoodLevel; } - double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } - 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); } - - void SetFoodLevel (int a_FoodLevel); - void SetFoodSaturationLevel (double a_FoodSaturationLevel); - void SetFoodTickTimer (int a_FoodTickTimer); - void SetFoodExhaustionLevel (double a_FoodExhaustionLevel); - void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining); - - /// Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" - bool Feed(int a_Food, double a_Saturation); - - /// Adds the specified exhaustion to m_FoodExhaustion. Expects only positive values. - void AddFoodExhaustion(double a_Exhaustion) - { - m_FoodExhaustionLevel += a_Exhaustion; - } - - /// Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two - void FoodPoison(int a_NumTicks); - - /// Returns true if the player is currently in the process of eating the currently equipped item - bool IsEating(void) const { return (m_EatingFinishTick >= 0); } - - // tolua_end - - /// Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet - void StartEating(void); - - /// Finishes eating the currently equipped item. Consumes the item, updates health and broadcasts the packets - void FinishEating(void); - - /// Aborts the current eating operation - void AbortEating(void); - - virtual void KilledBy(cEntity * a_Killer) override; - - void Respawn(void); // tolua_export - - void SetVisible( bool a_bVisible ); // tolua_export - bool IsVisible(void) const { return m_bVisible; } // tolua_export - - bool MoveToWorld(const char * a_WorldName); // tolua_export - - bool SaveToDisk(void); - bool LoadFromDisk(void); - void LoadPermissionsFromDisk(void); // tolua_export - - const AString & GetLoadedWorldName() { return m_LoadedWorldName; } - - void UseEquippedItem(void); - - void SendHealth(void); - - void SendExperience(void); - - // In UI windows, the item that the player is dragging: - bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); } - cItem & GetDraggingItem(void) {return m_DraggingItem; } - - // In UI windows, when inventory-painting: - /// Clears the list of slots that are being inventory-painted. To be used by cWindow only - void ClearInventoryPaintSlots(void); - - /// Adds a slot to the list for inventory painting. To be used by cWindow only - void AddInventoryPaintSlot(int a_SlotNum); - - /// Returns the list of slots currently stored for inventory painting. To be used by cWindow only - const cSlotNums & GetInventoryPaintSlots(void) const; - - // tolua_begin - - /// Returns the current maximum speed, as reported in the 1.6.1+ protocol (takes current sprinting state into account) - double GetMaxSpeed(void) const; - - /// Gets the normal maximum speed, as reported in the 1.6.1+ protocol, in the protocol units - double GetNormalMaxSpeed(void) const { return m_NormalMaxSpeed; } - - /// Gets the sprinting maximum speed, as reported in the 1.6.1+ protocol, in the protocol units - double GetSprintingMaxSpeed(void) const { return m_SprintingMaxSpeed; } - - /// Sets the normal maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. - void SetNormalMaxSpeed(double a_Speed); - - /// Sets the sprinting maximum speed, as reported in the 1.6.1+ protocol. Sends the update to player, if needed. - void SetSprintingMaxSpeed(double a_Speed); - - /// Sets the crouch status, broadcasts to all visible players - void SetCrouch(bool a_IsCrouched); - - /// Starts or stops sprinting, sends the max speed update to the client, if needed - void SetSprint(bool a_IsSprinting); - - /// 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; } - - // tolua_end - - // cEntity overrides: - virtual bool IsCrouched (void) const { return m_IsCrouched; } - virtual bool IsSprinting(void) const { return m_IsSprinting; } - virtual bool IsRclking (void) const { return IsEating(); } - -protected: - typedef std::map< std::string, bool > PermissionMap; - PermissionMap m_ResolvedPermissions; - PermissionMap m_Permissions; - - GroupList m_ResolvedGroups; - GroupList m_Groups; - - std::string m_PlayerName; - std::string m_LoadedWorldName; - - /// Xp Level stuff - enum - { - XP_TO_LEVEL15 = 255, - XP_PER_LEVEL_TO15 = 17, - 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: - /// Represents the food bar, one point equals half a "drumstick" - int m_FoodLevel; - - /// "Overcharge" for the m_FoodLevel; is depleted before m_FoodLevel - double m_FoodSaturationLevel; - - /// Count-up to the healing or damaging action, based on m_FoodLevel - int m_FoodTickTimer; - - /// A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little - double m_FoodExhaustionLevel; - - /// Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned - int m_FoodPoisonedTicksRemaining; - - /// Last position that has been recorded for food-related processing: - Vector3d m_LastFoodPos; - - float m_LastJumpHeight; - float m_LastGroundHeight; - bool m_bTouchGround; - double m_Stance; - cInventory m_Inventory; - cWindow * m_CurrentWindow; - cWindow * m_InventoryWindow; - - float m_TimeLastPickupCheck; - - void ResolvePermissions(); - - void ResolveGroups(); - char m_Color; - - float m_LastBlockActionTime; - int m_LastBlockActionCnt; - eGameMode m_GameMode; - std::string m_IP; - - cItem m_DraggingItem; - - long long m_LastPlayerListTime; - static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second - - cClientHandle * m_ClientHandle; - - cSlotNums m_InventoryPaintSlots; - - /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is walking. 0.1 by default - double m_NormalMaxSpeed; - - /// Max speed, in ENTITY_PROPERTIES packet's units, when the player is sprinting. 0.13 by default - double m_SprintingMaxSpeed; - - bool m_IsCrouched; - bool m_IsSprinting; - - bool m_IsSwimming; - bool m_IsSubmerged; - - /// The world tick in which eating will be finished. -1 if not eating - Int64 m_EatingFinishTick; - - /// Player Xp level - short int m_LifetimeTotalXp; - short int m_CurrentXp; - - // flag saying we need to send a xp update to client - bool m_bDirtyExperience; - - bool m_IsChargingBow; - int m_BowCharge; - - - virtual void Destroyed(void); - - /// Filters out damage for creative mode - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; - - /// Called in each tick to handle food-related processing - void HandleFood(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(); -} ; // tolua_export - - - - diff --git a/source/Entities/ProjectileEntity.cpp b/source/Entities/ProjectileEntity.cpp deleted file mode 100644 index fb25aea35..000000000 --- a/source/Entities/ProjectileEntity.cpp +++ /dev/null @@ -1,855 +0,0 @@ - -// ProjectileEntity.cpp - -// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types - -#include "Globals.h" -#include "ProjectileEntity.h" -#include "../ClientHandle.h" -#include "Player.h" -#include "../LineBlockTracer.h" -#include "../BoundingBox.h" -#include "../ChunkMap.h" -#include "../Chunk.h" - - - - - -/// Converts an angle in radians into a byte representation used by the network protocol -#define ANGLE_TO_PROTO(X) (Byte)(X * 255 / 360) - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProjectileTracerCallback: - -class cProjectileTracerCallback : - public cBlockTracer::cCallbacks -{ -public: - cProjectileTracerCallback(cProjectileEntity * a_Projectile) : - m_Projectile(a_Projectile), - m_SlowdownCoeff(0.99) // Default slowdown when not in water - { - } - - double GetSlowdownCoeff(void) const { return m_SlowdownCoeff; } - -protected: - cProjectileEntity * m_Projectile; - double m_SlowdownCoeff; - - // cCallbacks overrides: - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override - { - /* - // DEBUG: - LOGD("Hit block %d:%d at {%d, %d, %d} face %d, %s (%s)", - a_BlockType, a_BlockMeta, - a_BlockX, a_BlockY, a_BlockZ, a_EntryFace, - g_BlockIsSolid[a_BlockType] ? "solid" : "non-solid", - ItemToString(cItem(a_BlockType, 1, a_BlockMeta)).c_str() - ); - */ - - if (g_BlockIsSolid[a_BlockType]) - { - // The projectile hit a solid block - // Calculate the exact hit coords: - cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); - Vector3d Line1 = m_Projectile->GetPosition(); - Vector3d Line2 = Line1 + m_Projectile->GetSpeed(); - double LineCoeff = 0; - char Face; - if (bb.CalcLineIntersection(Line1, Line2, LineCoeff, Face)) - { - Vector3d Intersection = Line1 + m_Projectile->GetSpeed() * LineCoeff; - m_Projectile->OnHitSolidBlock(Intersection, Face); - return true; - } - else - { - LOGD("WEIRD! block tracer reports a hit, but BBox tracer doesn't. Ignoring the hit."); - } - } - - // Convey some special effects from special blocks: - switch (a_BlockType) - { - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - { - m_Projectile->StartBurning(30); - m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.9); // Slow down to 0.9* the speed each tick when moving through lava - break; - } - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - { - m_Projectile->StopBurning(); - m_SlowdownCoeff = std::min(m_SlowdownCoeff, 0.8); // Slow down to 0.8* the speed each tick when moving through water - break; - } - } // switch (a_BlockType) - - // Continue tracing - return false; - } -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProjectileEntityCollisionCallback: - -class cProjectileEntityCollisionCallback : - public cEntityCallback -{ -public: - cProjectileEntityCollisionCallback(cProjectileEntity * a_Projectile, const Vector3d & a_Pos, const Vector3d & a_NextPos) : - m_Projectile(a_Projectile), - m_Pos(a_Pos), - m_NextPos(a_NextPos), - m_MinCoeff(1), - m_HitEntity(NULL) - { - } - - - virtual bool Item(cEntity * a_Entity) override - { - if ( - (a_Entity == m_Projectile) || // Do not check collisions with self - (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile - ) - { - // TODO: Don't check creator only for the first 5 ticks - // so that arrows stuck in ground and dug up can hurt the player - return false; - } - - cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); - - // Instead of colliding the bounding box with another bounding box in motion, we collide an enlarged bounding box with a hairline. - // The results should be good enough for our purposes - double LineCoeff; - char Face; - EntBox.Expand(m_Projectile->GetWidth() / 2, m_Projectile->GetHeight() / 2, m_Projectile->GetWidth() / 2); - if (!EntBox.CalcLineIntersection(m_Pos, m_NextPos, LineCoeff, Face)) - { - // No intersection whatsoever - return false; - } - - // TODO: Some entities don't interact with the projectiles (pickups, falling blocks) - // TODO: Allow plugins to interfere about which entities can be hit - - if (LineCoeff < m_MinCoeff) - { - // The entity is closer than anything we've stored so far, replace it as the potential victim - m_MinCoeff = LineCoeff; - m_HitEntity = a_Entity; - } - - // Don't break the enumeration, we want all the entities - return false; - } - - /// Returns the nearest entity that was hit, after the enumeration has been completed - cEntity * GetHitEntity(void) const { return m_HitEntity; } - - /// Returns the line coeff where the hit was encountered, after the enumeration has been completed - double GetMinCoeff(void) const { return m_MinCoeff; } - - /// Returns true if the callback has encountered a true hit - bool HasHit(void) const { return (m_MinCoeff < 1); } - -protected: - cProjectileEntity * m_Projectile; - const Vector3d & m_Pos; - const Vector3d & m_NextPos; - double m_MinCoeff; // The coefficient of the nearest hit on the Pos line - - // Although it's bad(tm) to store entity ptrs from a callback, we can afford it here, because the entire callback - // is processed inside the tick thread, so the entities won't be removed in between the calls and the final processing - cEntity * m_HitEntity; // The nearest hit entity -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cProjectileEntity: - -cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) : - super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height), - m_ProjectileKind(a_Kind), - m_Creator(a_Creator), - m_IsInGround(false) -{ -} - - - - - -cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) : - super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height), - m_ProjectileKind(a_Kind), - m_Creator(a_Creator), - m_IsInGround(false) -{ - SetSpeed(a_Speed); - SetRotationFromSpeed(); - SetPitchFromSpeed(); -} - - - - - -cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed) -{ - Vector3d Speed; - if (a_Speed != NULL) - { - Speed = *a_Speed; - } - - switch (a_Kind) - { - case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed); - case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed); - case pkFirework: return new cFireworkEntity (a_Creator, a_X, a_Y, a_Z ); - // TODO: the rest - } - - LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind); - return NULL; -} - - - - - -void cProjectileEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // Set the position based on what face was hit: - SetPosition(a_HitPos); - SetSpeed(0, 0, 0); - - // DEBUG: - LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d", - m_UniqueID, - a_HitPos.x, a_HitPos.y, a_HitPos.z, - a_HitFace - ); - - m_IsInGround = true; -} - - - - - -AString cProjectileEntity::GetMCAClassName(void) const -{ - switch (m_ProjectileKind) - { - case pkArrow: return "Arrow"; - case pkSnowball: return "Snowball"; - case pkEgg: return "Egg"; - case pkGhastFireball: return "Fireball"; - case pkFireCharge: return "SmallFireball"; - case pkEnderPearl: return "ThrownEnderPearl"; - case pkExpBottle: return "ThrownExpBottle"; - case pkSplashPotion: return "ThrownPotion"; - case pkWitherSkull: return "WitherSkull"; - case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? - } - ASSERT(!"Unhandled projectile entity kind!"); - return ""; -} - - - - - -void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - if (GetProjectileKind() != pkArrow) // See cArrow::Tick - { - BroadcastMovementUpdate(); - } -} - - - - - -void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - if (m_IsInGround) - { - // Already-grounded projectiles don't move at all - return; - } - - Vector3d PerTickSpeed = GetSpeed() / 20; - Vector3d Pos = GetPosition(); - - // Trace the tick's worth of movement as a line: - Vector3d NextPos = Pos + PerTickSpeed; - cProjectileTracerCallback TracerCallback(this); - if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) - { - // Something has been hit, abort all other processing - return; - } - // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff - - // Test for entity collisions: - cProjectileEntityCollisionCallback EntityCollisionCallback(this, Pos, NextPos); - a_Chunk.ForEachEntity(EntityCollisionCallback); - if (EntityCollisionCallback.HasHit()) - { - // An entity was hit: - Vector3d HitPos = Pos + (NextPos - Pos) * EntityCollisionCallback.GetMinCoeff(); - - // DEBUG: - LOGD("Projectile %d has hit an entity %d (%s) at {%.02f, %.02f, %.02f} (coeff %.03f)", - m_UniqueID, - EntityCollisionCallback.GetHitEntity()->GetUniqueID(), - EntityCollisionCallback.GetHitEntity()->GetClass(), - HitPos.x, HitPos.y, HitPos.z, - EntityCollisionCallback.GetMinCoeff() - ); - - OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); - } - // TODO: Test the entities in the neighboring chunks, too - - // Update the position: - SetPosition(NextPos); - - // Add slowdown and gravity effect to the speed: - Vector3d NewSpeed(GetSpeed()); - NewSpeed.y += m_Gravity / 20; - NewSpeed *= TracerCallback.GetSlowdownCoeff(); - SetSpeed(NewSpeed); - SetRotationFromSpeed(); - SetPitchFromSpeed(); - - // DEBUG: - LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}", - m_UniqueID, - GetPosX(), GetPosY(), GetPosZ(), - GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetRotation(), GetPitch() - ); -} - - - - - -void cProjectileEntity::SpawnOn(cClientHandle & a_Client) -{ - // Default spawning - use the projectile kind to spawn an object: - a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch())); - a_Client.SendEntityMetadata(*this); -} - - - - - -void cProjectileEntity::CollectedBy(cPlayer * a_Dest) -{ - // Overriden in arrow - UNUSED(a_Dest); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cArrowEntity: - -cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5), - m_PickupState(psNoPickup), - m_DamageCoeff(2), - m_IsCritical(false), - m_Timer(0), - m_bIsCollected(false), - m_HitBlockPos(Vector3i(0, 0, 0)), - m_HitGroundTimer(0) -{ - SetSpeed(a_Speed); - SetMass(0.1); - SetRotationFromSpeed(); - SetPitchFromSpeed(); - LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", - m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetRotation(), GetPitch() - ); -} - - - - - -cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : - super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5), - m_PickupState(psInSurvivalOrCreative), - m_DamageCoeff(2), - m_IsCritical((a_Force >= 1)), - m_Timer(0), - m_bIsCollected(false), - m_HitBlockPos(0, 0, 0), - m_HitGroundTimer(0) -{ -} - - - - - -bool cArrowEntity::CanPickup(const cPlayer & a_Player) const -{ - switch (m_PickupState) - { - case psNoPickup: return false; - case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative()); - case psInCreative: return a_Player.IsGameModeCreative(); - } - ASSERT(!"Unhandled pickup state"); - return false; -} - - - - - -void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - if (a_HitFace == BLOCK_FACE_NONE) { return; } - - super::OnHitSolidBlock(a_HitPos, a_HitFace); - int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z; - - switch (a_HitFace) - { - case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed - case BLOCK_FACE_YM: - { - break; - } - default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true); - } - - m_HitBlockPos = Vector3i(a_X, a_Y, a_Z); - - // Broadcast arrow hit sound - m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); -} - - - - - -void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat()) - { - // Not an entity that interacts with an arrow - return; - } - - int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); - if (m_IsCritical) - { - Damage += m_World->GetTickRandomNumber(Damage / 2 + 2); - } - a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); - - // Broadcast successful hit sound - m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - - Destroy(); -} - - - - - -void cArrowEntity::CollectedBy(cPlayer * a_Dest) -{ - if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) - { - int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); - if (NumAdded > 0) // Only play effects if there was space in inventory - { - m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - m_bIsCollected = true; - } - } -} - - - - - -void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - m_Timer += a_Dt; - - if (m_bIsCollected) - { - if (m_Timer > 500.f) // 0.5 seconds - { - Destroy(); - return; - } - } - else if (m_Timer > 1000 * 60 * 5) // 5 minutes - { - Destroy(); - return; - } - - if (m_IsInGround) - { - // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL - // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing) - // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync - // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position - - if (m_HitGroundTimer != -1) // Sent a teleport already, don't do again - { - if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case - { - m_World->BroadcastTeleportEntity(*this); - m_HitGroundTimer = -1; - } - else - { - m_HitGroundTimer += a_Dt; - } - } - - int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - - if (Chunk == NULL) - { - // Inside an unloaded chunk, abort - return; - } - - if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? - { - m_IsInGround = false; // Yes, begin simulating physics again - } - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownEggEntity: - -cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - if (m_World->GetTickRandomNumber(7) == 1) - { - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - } - else if (m_World->GetTickRandomNumber(32) == 1) - { - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - } - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownEnderPearlEntity : - -cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // Teleport the creator here, make them take 5 damage: - if (m_Creator != NULL) - { - // TODO: The coords might need some tweaking based on the block face - m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5); - m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); - } - - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownSnowballEntity : - -cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs - - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBottleOEnchantingEntity : - -cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : -super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - // TODO: Spawn experience orbs - - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFireworkEntity : - -cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z) : -super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ -} - - - - - -void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - if ((a_HitFace != BLOCK_FACE_BOTTOM) && (a_HitFace != BLOCK_FACE_NONE)) - { - return; - } - - SetSpeed(0, 0, 0); - SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ()); - - std::cout << a_HitPos.x << " " << a_HitPos.y << " " << a_HitPos.z << std::endl; - - m_IsInGround = true; - - BroadcastMovementUpdate(); -} - - - - - -void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - if (m_IsInGround) - { - if (a_Chunk.GetBlock((int)GetPosX(), (int)GetPosY() + 1, (int)GetPosZ()) == E_BLOCK_AIR) - { - m_IsInGround = false; - } - else - { - return; - } - } - - Vector3d PerTickSpeed = GetSpeed() / 20; - Vector3d Pos = GetPosition(); - - // Trace the tick's worth of movement as a line: - Vector3d NextPos = Pos + PerTickSpeed; - cProjectileTracerCallback TracerCallback(this); - if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) - { - // Something has been hit, abort all other processing - return; - } - // The tracer also checks the blocks for slowdown blocks - water and lava - and stores it for later in its SlowdownCoeff - - // Update the position: - SetPosition(NextPos); - - // Add slowdown and gravity effect to the speed: - Vector3d NewSpeed(GetSpeed()); - NewSpeed.y += 2; - NewSpeed *= TracerCallback.GetSlowdownCoeff(); - SetSpeed(NewSpeed); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cGhastFireballEntity : - -cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1) -{ - SetSpeed(a_Speed); - SetGravity(0); -} - - - - - -void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this); -} - - - - - -void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFireChargeEntity : - -cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125) -{ - SetSpeed(a_Speed); - SetGravity(0); -} - - - - - -void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) - { - m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1); - } -} - - - - - -void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); - - // TODO: Some entities are immune to hits - a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning -} - - - - diff --git a/source/Entities/ProjectileEntity.h b/source/Entities/ProjectileEntity.h deleted file mode 100644 index 959e81ae5..000000000 --- a/source/Entities/ProjectileEntity.h +++ /dev/null @@ -1,382 +0,0 @@ - -// ProjectileEntity.h - -// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types - - - - - -#pragma once - -#include "Entity.h" - - - - - -// tolua_begin - -class cProjectileEntity : - public cEntity -{ - typedef cEntity super; - -public: - /// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet. - enum eKind - { - pkArrow = 60, - pkSnowball = 61, - pkEgg = 62, - pkGhastFireball = 63, - pkFireCharge = 64, - pkEnderPearl = 65, - pkExpBottle = 75, - pkSplashPotion = 73, - pkFirework = 76, - pkWitherSkull = 66, - pkFishingFloat = 90, - } ; - - // tolua_end - - CLASS_PROTODEF(cProjectileEntity); - - cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); - cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - - static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL); - - /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace); - - /// Called by the physics blocktracer when the entity hits another entity - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {} - - /// Called by Chunk when the projectile is eligible for player collection - virtual void CollectedBy(cPlayer * a_Dest); - - // tolua_begin - - /// Returns the kind of the projectile (fast class identification) - eKind GetProjectileKind(void) const { return m_ProjectileKind; } - - /// Returns the entity who created this projectile; may be NULL - cEntity * GetCreator(void) { return m_Creator; } - - /// Returns the string that is used as the entity type (class name) in MCA files - AString GetMCAClassName(void) const; - - /// Returns true if the projectile has hit the ground and is stuck there - bool IsInGround(void) const { return m_IsInGround; } - - // tolua_end - - /// Sets the internal InGround flag. To be used by MCA loader only! - void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } - -protected: - eKind m_ProjectileKind; - - /// The entity who has created this projectile; may be NULL (e. g. for dispensers) - cEntity * m_Creator; - - /// True if the projectile has hit the ground and is stuck there - bool m_IsInGround; - - // cEntity overrides: - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void SpawnOn(cClientHandle & a_Client) override; - - // tolua_begin -} ; - - - - - -class cArrowEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field - enum ePickupState - { - psNoPickup = 0, - psInSurvivalOrCreative = 1, - psInCreative = 2, - } ; - - // tolua_end - - CLASS_PROTODEF(cArrowEntity); - - /// Creates a new arrow with psNoPickup state and default damage modifier coeff - cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - - /// Creates a new arrow as shot by a player, initializes it from the player object - cArrowEntity(cPlayer & a_Player, double a_Force); - - // tolua_begin - - /// Returns whether the arrow can be picked up by players - ePickupState GetPickupState(void) const { return m_PickupState; } - - /// Sets a new pickup state - void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; } - - /// Returns the damage modifier coeff. - double GetDamageCoeff(void) const { return m_DamageCoeff; } - - /// Sets the damage modifier coeff - void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } - - /// Returns true if the specified player can pick the arrow up - bool CanPickup(const cPlayer & a_Player) const; - - /// Returns true if the arrow is set as critical - bool IsCritical(void) const { return m_IsCritical; } - - /// Sets the IsCritical flag - void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } - - // tolua_end - -protected: - - /// Determines when the arrow can be picked up by players - ePickupState m_PickupState; - - /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow - double m_DamageCoeff; - - /// If true, the arrow deals more damage - bool m_IsCritical; - - /// Timer for pickup collection animation or five minute timeout - float m_Timer; - - /// Timer for client arrow position confirmation via TeleportEntity - float m_HitGroundTimer; - - /// If true, the arrow is in the process of being collected - don't go to anyone else - bool m_bIsCollected; - - /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air - Vector3i m_HitBlockPos; - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void CollectedBy(cPlayer * a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - // tolua_begin -} ; - - - - - -class cThrownEggEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownEggEntity); - - cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // tolua_end - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -} ; - - - - - -class cThrownEnderPearlEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownEnderPearlEntity); - - cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // tolua_end - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -} ; - - - - - -class cThrownSnowballEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownSnowballEntity); - - cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -} ; - - - - - -class cExpBottleEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cExpBottleEntity); - - cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - - // tolua_begin - -}; - - - - - -class cFireworkEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cFireworkEntity); - - cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z); - -protected: - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - - // tolua_begin - -}; - - - - - -class cGhastFireballEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cGhastFireballEntity); - - cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // TODO: Deflecting the fireballs by arrow- or sword- hits - - // tolua_begin - -} ; - - - - - -class cFireChargeEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cFireChargeEntity); - - cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // tolua_begin - -} ; - - - - -// tolua_end - - - diff --git a/source/Entities/TNTEntity.cpp b/source/Entities/TNTEntity.cpp deleted file mode 100644 index 339107b2e..000000000 --- a/source/Entities/TNTEntity.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "Globals.h" - -#include "TNTEntity.h" -#include "../World.h" -#include "../ClientHandle.h" - - - - - -cTNTEntity::cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec) : - super(etTNT, a_X, a_Y, a_Z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) -{ -} - - - - - -cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) : - super(etTNT, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98), - m_Counter(0), - m_MaxFuseTime(a_FuseTimeInSec) -{ -} - - - - -void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) -{ - a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT - m_bDirtyPosition = false; - m_bDirtySpeed = false; - m_bDirtyOrientation = false; - m_bDirtyHead = false; -} - - - - - -void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - BroadcastMovementUpdate(); - float delta_time = a_Dt / 1000; // Convert miliseconds to seconds - m_Counter += delta_time; - if (m_Counter > m_MaxFuseTime) // Check if we go KABOOOM - { - Destroy(true); - LOGD("BOOM at {%f,%f,%f}", GetPosX(), GetPosY(), GetPosZ()); - m_World->DoExplosionAt(4.0, GetPosX() + 0.49, GetPosY() + 0.49, GetPosZ() + 0.49, true, esPrimedTNT, this); - return; - } -} - - - - diff --git a/source/Entities/TNTEntity.h b/source/Entities/TNTEntity.h deleted file mode 100644 index eb5040e8a..000000000 --- a/source/Entities/TNTEntity.h +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "Entity.h" - - - - - -class cTNTEntity : - public cEntity -{ - typedef cEntity super; - -public: - CLASS_PROTODEF(cTNTEntity); - - cTNTEntity(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec); - cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec); - - // cEntity overrides: - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -protected: - double m_Counter; ///< How much time has elapsed since the object was created, in seconds - double m_MaxFuseTime; ///< How long the fuse is, in seconds -}; - - - - |