summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHowaner <franzi.moos@googlemail.com>2014-09-02 19:37:03 +0200
committerHowaner <franzi.moos@googlemail.com>2014-09-02 19:37:03 +0200
commitb8769e3fb48ce05f7a76e9e55b9d864f3474d311 (patch)
treeb2842e686fe509bf65d5b7df1f311706ce0de6f3 /src
parentWebAdmin: Exported logo and background. (diff)
parentRe-added alternate spellings of darkgraywool. (diff)
downloadcuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.tar
cuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.tar.gz
cuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.tar.bz2
cuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.tar.lz
cuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.tar.xz
cuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.tar.zst
cuberite-b8769e3fb48ce05f7a76e9e55b9d864f3474d311.zip
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/ManualBindings.cpp2
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp9
-rw-r--r--src/BlockEntities/FurnaceEntity.h2
-rw-r--r--src/Blocks/BlockHandler.cpp39
-rw-r--r--src/Blocks/BlockIce.h22
-rw-r--r--src/Blocks/BlockLeaves.h2
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/ChunkDef.h16
-rw-r--r--src/Entities/ArrowEntity.cpp37
-rw-r--r--src/Entities/ArrowEntity.h5
-rw-r--r--src/Entities/Entity.cpp227
-rw-r--r--src/Entities/Pickup.cpp8
-rw-r--r--src/Entities/Player.cpp23
-rw-r--r--src/Entities/ProjectileEntity.cpp5
-rw-r--r--src/Entities/ProjectileEntity.h6
-rw-r--r--src/FurnaceRecipe.cpp243
-rw-r--r--src/FurnaceRecipe.h37
-rw-r--r--src/Items/ItemBow.h12
-rw-r--r--src/Protocol/MojangAPI.cpp3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp30
-rw-r--r--src/RankManager.cpp4
-rw-r--r--src/SetChunkData.cpp2
22 files changed, 529 insertions, 212 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index b2c57e52d..adf10a72f 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -2663,7 +2663,7 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
// Get the recipe for the input
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
- const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input);
+ const cFurnaceRecipe::cRecipe * Recipe = FR->GetRecipeFrom(*Input);
if (Recipe == NULL)
{
// There is no such furnace recipe for this input, return no value
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index dd0858378..20702a9ac 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -188,12 +188,11 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
void cCommandBlockEntity::Execute()
{
- if (m_World != NULL)
+ ASSERT(m_World != NULL); // Execute should not be called before the command block is attached to a world
+
+ if (!m_World->AreCommandBlocksEnabled())
{
- if (!m_World->AreCommandBlocksEnabled())
- {
- return;
- }
+ return;
}
class CommandBlockOutCb :
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 4f935a74b..cf1a755e0 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -105,7 +105,7 @@ protected:
NIBBLETYPE m_BlockMeta;
/// The recipe for the current input slot
- const cFurnaceRecipe::Recipe * m_CurrentRecipe;
+ const cFurnaceRecipe::cRecipe * m_CurrentRecipe;
/// The item that is being smelted
cItem m_LastInput;
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 028277e4c..6767d4de4 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -431,10 +431,45 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
else
{
// TODO: Add a proper overridable function for this
- Pickups.Add(m_BlockType, 1, Meta);
+ if (a_Digger != NULL)
+ {
+ cEnchantments Enchantments = a_Digger->GetEquippedWeapon().m_Enchantments;
+ if ((Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0) && a_Digger->IsPlayer())
+ {
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CAKE:
+ case E_BLOCK_CARROTS:
+ case E_BLOCK_COCOA_POD:
+ case E_BLOCK_DOUBLE_STONE_SLAB:
+ case E_BLOCK_DOUBLE_WOODEN_SLAB:
+ case E_BLOCK_FIRE:
+ case E_BLOCK_FARMLAND:
+ case E_BLOCK_MELON_STEM:
+ case E_BLOCK_MOB_SPAWNER:
+ case E_BLOCK_NETHER_WART:
+ case E_BLOCK_POTATOES:
+ case E_BLOCK_PUMPKIN_STEM:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_SUGARCANE:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_CROPS:
+ {
+ // Silktouch can't be used for this blocks
+ ConvertToPickups(Pickups, Meta);
+ break;
+ };
+ default: Pickups.Add(m_BlockType, 1, Meta);
+ }
+ }
+ else
+ {
+ Pickups.Add(m_BlockType, 1, Meta);
+ }
+ }
}
}
-
+
// Allow plugins to modify the pickups:
a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h
index c38630fe3..47a84e5a7 100644
--- a/src/Blocks/BlockIce.h
+++ b/src/Blocks/BlockIce.h
@@ -30,18 +30,18 @@ public:
{
return;
}
-
- BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+
+ cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
+ if (Enchantments.GetLevel(cEnchantments::enchSilkTouch) == 0)
{
- return;
+ BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
+ if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+ {
+ return;
+ }
+
+ a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
+ // This is called later than the real destroying of this ice block
}
-
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
- // This is called later than the real destroying of this ice block
}
} ;
-
-
-
-
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index 972dd6232..a8aa28a0f 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -152,7 +152,7 @@ bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
{
- for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
+ for (int y = std::max(a_BlockY - i, 0); y <= std::min(a_BlockY + i, 255); y++)
{
for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
{
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 37657ba91..2d96662a9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -256,6 +256,11 @@ set(EXECUTABLE MCServer)
if (MSVC)
get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS)
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES)
+
+ # The paths in BINDING_DEPENDENCIES are relative to the Bindings folder, convert them relative to this folder:
+ foreach (dep ${BINDING_DEPENDENCIES})
+ list (APPEND BINDINGS_DEPENDENCIES "Bindings/${dep}")
+ endforeach(dep)
ADD_CUSTOM_COMMAND(
OUTPUT ${BINDING_OUTPUTS}
@@ -268,7 +273,7 @@ if (MSVC)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here
- DEPENDS ${BINDING_DEPENDENCIES}
+ DEPENDS ${BINDINGS_DEPENDENCIES}
)
endif()
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 51075ab4a..b8b4291c7 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -197,32 +197,32 @@ public:
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
return a_HeightMap[a_X + Width * a_Z];
}
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
a_HeightMap[a_X + Width * a_Z] = a_Height;
}
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
return a_BiomeMap[a_X + Width * a_Z];
}
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
}
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 913519c4c..954e0a267 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -90,6 +90,13 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
// Broadcast arrow hit sound
m_World->BroadcastSoundEffect("random.bowhit", (double)X, (double)Y, (double)Z, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+
+ if ((m_World->GetBlock(Hit) == E_BLOCK_TNT) && IsOnFire())
+ {
+ m_World->SetBlock(X, Y, Z, E_BLOCK_AIR, 0);
+ m_World->SpawnPrimedTNT(X, Y, Z);
+ }
+
}
@@ -103,8 +110,36 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
- a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
+
+ int PowerLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPower);
+ if (PowerLevel > 0)
+ {
+ int ExtraDamage = (int)ceil(0.25 * (PowerLevel + 1));
+ Damage += ExtraDamage;
+ }
+
+ int KnockbackAmount = 1;
+ int PunchLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPunch);
+ if (PunchLevel > 0)
+ {
+ Vector3d LookVector = GetLookVector();
+ Vector3f FinalSpeed = Vector3f(0, 0, 0);
+ switch (PunchLevel)
+ {
+ case 1: FinalSpeed = LookVector * Vector3d(5, 0.3, 5); break;
+ case 2: FinalSpeed = LookVector * Vector3d(8, 0.3, 8); break;
+ default: break;
+ }
+ a_EntityHit.SetSpeed(FinalSpeed);
+ }
+
+ a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount);
+ if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
+ {
+ a_EntityHit.StartBurning(100);
+ }
+
// Broadcast successful hit sound
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h
index 4bfcb1f6d..a1e7a17e7 100644
--- a/src/Entities/ArrowEntity.h
+++ b/src/Entities/ArrowEntity.h
@@ -10,6 +10,7 @@
+
// tolua_begin
class cArrowEntity :
@@ -46,7 +47,7 @@ public:
/// 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; }
@@ -89,7 +90,7 @@ protected:
/// 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;
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 32f220897..89d1cffa1 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -13,6 +13,7 @@
#include "../Tracer.h"
#include "Player.h"
#include "Items/ItemHandler.h"
+#include "../FastRandom.h"
@@ -316,8 +317,107 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
// IsOnGround() only is false if the player is moving downwards
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
- if (!Player->IsOnGround())
+ const cEnchantments & Enchantments = Player->GetEquippedItem().m_Enchantments;
+
+ int SharpnessLevel = Enchantments.GetLevel(cEnchantments::enchSharpness);
+ int SmiteLevel = Enchantments.GetLevel(cEnchantments::enchSmite);
+ int BaneOfArthropodsLevel = Enchantments.GetLevel(cEnchantments::enchBaneOfArthropods);
+
+ if (SharpnessLevel > 0)
+ {
+ a_TDI.FinalDamage += (int)ceil(1.25 * SharpnessLevel);
+ }
+ else if (SmiteLevel > 0)
+ {
+ if (IsMob())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtSkeleton:
+ case cMonster::mtZombie:
+ case cMonster::mtWither:
+ case cMonster::mtZombiePigman:
+ {
+ a_TDI.FinalDamage += (int)ceil(2.5 * SmiteLevel);
+ break;
+ }
+ }
+ }
+ }
+ else if (BaneOfArthropodsLevel > 0)
{
+ if (IsMob())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtSpider:
+ case cMonster::mtCaveSpider:
+ case cMonster::mtSilverfish:
+ {
+ a_TDI.RawDamage += (int)ceil(2.5 * BaneOfArthropodsLevel);
+ // TODO: Add slowness effect
+
+ break;
+ };
+ default: break;
+ }
+ }
+ }
+
+ int FireAspectLevel = Enchantments.GetLevel(cEnchantments::enchFireAspect);
+ if (FireAspectLevel > 0)
+ {
+ int BurnTicks = 3;
+
+ if (FireAspectLevel > 1)
+ {
+ BurnTicks += 4 * (FireAspectLevel - 1);
+ }
+ if (!IsMob() && !IsSubmerged() && !IsSwimming())
+ {
+ StartBurning(BurnTicks * 20);
+ }
+ else if (IsMob() && !IsSubmerged() && !IsSwimming())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtGhast:
+ case cMonster::mtZombiePigman:
+ case cMonster::mtMagmaCube:
+ {
+ break;
+ };
+ default: StartBurning(BurnTicks * 20);
+ }
+ }
+ }
+
+ int ThornsLevel = 0;
+ const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
+ {
+ const cItem & Item = ArmorItems[i];
+ ThornsLevel = std::max(ThornsLevel, Item.m_Enchantments.GetLevel(cEnchantments::enchThorns));
+ }
+
+ if (ThornsLevel > 0)
+ {
+ int Chance = ThornsLevel * 15;
+
+ cFastRandom Random;
+ int RandomValue = Random.GenerateRandomInteger(0, 100);
+
+ if (RandomValue <= Chance)
+ {
+ a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.GenerateRandomInteger(1, 4), 0);
+ }
+ }
+
+ if (!Player->IsOnGround())
+ {
if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack))
{
a_TDI.FinalDamage += 2;
@@ -328,13 +428,123 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
}
+ if (IsPlayer())
+ {
+ double TotalEPF = 0.0;
+ double EPFProtection = 0.00;
+ double EPFFireProtection = 0.00;
+ double EPFBlastProtection = 0.00;
+ double EPFProjectileProtection = 0.00;
+ double EPFFeatherFalling = 0.00;
+
+ const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
+ {
+ const cItem & Item = ArmorItems[i];
+ int Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProtection);
+ if (Level > 0)
+ {
+ EPFProtection += (6 + Level * Level) * 0.75 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFireProtection);
+ if (Level > 0)
+ {
+ EPFFireProtection += (6 + Level * Level) * 1.25 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFeatherFalling);
+ if (Level > 0)
+ {
+ EPFFeatherFalling += (6 + Level * Level) * 2.5 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection);
+ if (Level > 0)
+ {
+ EPFBlastProtection += (6 + Level * Level) * 1.5 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProjectileProtection);
+ if (Level > 0)
+ {
+ EPFProjectileProtection += (6 + Level * Level) * 1.5 / 3;
+ }
+
+ }
+
+ TotalEPF = EPFProtection + EPFFireProtection + EPFFeatherFalling + EPFBlastProtection + EPFProjectileProtection;
+
+ EPFProtection = EPFProtection / TotalEPF;
+ EPFFireProtection = EPFFireProtection / TotalEPF;
+ EPFFeatherFalling = EPFFeatherFalling / TotalEPF;
+ EPFBlastProtection = EPFBlastProtection / TotalEPF;
+ EPFProjectileProtection = EPFProjectileProtection / TotalEPF;
+
+ if (TotalEPF > 25)
+ {
+ TotalEPF = 25;
+ }
+
+ cFastRandom Random;
+ float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
+
+ TotalEPF = ceil(TotalEPF * RandomValue);
+
+ if (TotalEPF > 20)
+ {
+ TotalEPF = 20;
+ }
+
+ EPFProtection = TotalEPF * EPFProtection;
+ EPFFireProtection = TotalEPF * EPFFireProtection;
+ EPFFeatherFalling = TotalEPF * EPFFeatherFalling;
+ EPFBlastProtection = TotalEPF * EPFBlastProtection;
+ EPFProjectileProtection = TotalEPF * EPFProjectileProtection;
+
+ int RemovedDamage = 0;
+
+ if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtAdmin))
+ {
+ RemovedDamage += (int)ceil(EPFProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if ((a_TDI.DamageType == dtFalling) || (a_TDI.DamageType == dtFall) || (a_TDI.DamageType == dtEnderPearl))
+ {
+ RemovedDamage += (int)ceil(EPFFeatherFalling * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtBurning)
+ {
+ RemovedDamage += (int)ceil(EPFFireProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtExplosion)
+ {
+ RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtProjectile)
+ {
+ RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.FinalDamage < RemovedDamage)
+ {
+ RemovedDamage = 0;
+ }
+
+ a_TDI.FinalDamage -= RemovedDamage;
+ }
+
m_Health -= (short)a_TDI.FinalDamage;
// TODO: Apply damage to armor
m_Health = std::max(m_Health, 0);
- if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
+ // Add knockback:
+ if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL))
{
int KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback); // More common enchantment
if (KnockbackLevel < 1)
@@ -1252,6 +1462,8 @@ void cEntity::HandleAir(void)
// See if the entity is /submerged/ water (block above is water)
// Get the type of block the entity is standing in:
+ int RespirationLevel = GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration);
+
if (IsSubmerged())
{
if (!IsPlayer()) // Players control themselves
@@ -1259,6 +1471,11 @@ void cEntity::HandleAir(void)
SetSpeedY(1); // Float in the water
}
+ if (RespirationLevel > 0)
+ {
+ ((cPawn *)this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
+ }
+
if (m_AirLevel <= 0)
{
// Runs the air tick timer to check whether the player should be damaged
@@ -1285,6 +1502,12 @@ void cEntity::HandleAir(void)
// Set the air back to maximum
m_AirLevel = MAX_AIR_LEVEL;
m_AirTickTimer = DROWNING_TICKS;
+
+ if (RespirationLevel > 0)
+ {
+ m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
+ }
+
}
}
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index aab534f41..87b5bed07 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -150,10 +150,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
}
}
- if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup
+ // Try to combine the pickup with adjacent same-item pickups:
+ if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine if already full
{
+ // By using a_Chunk's ForEachEntity() instead of cWorld's, pickups don't combine across chunk boundaries.
+ // That is a small price to pay for not having to traverse the entire world for each entity.
+ // The speedup in the tick thread is quite considerable.
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
- m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
+ a_Chunk.ForEachEntity(PickupCombiningCallback);
if (PickupCombiningCallback.FoundMatchingPickup())
{
m_World->BroadcastEntityMetadata(*this);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index b0dd40615..756410989 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -15,6 +15,7 @@
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
+#include "../FastRandom.h"
#include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h"
@@ -1795,6 +1796,28 @@ void cPlayer::UseEquippedItem(int a_Amount)
return;
}
+ // If the item has an unbreaking enchantment, give it a random chance of not breaking:
+ cItem Item = GetEquippedItem();
+ int UnbreakingLevel = Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking);
+ if (UnbreakingLevel > 0)
+ {
+ int chance;
+ if (ItemCategory::IsArmor(Item.m_ItemType))
+ {
+ chance = 60 + (40 / (UnbreakingLevel + 1));
+ }
+ else
+ {
+ chance = 100 / (UnbreakingLevel + 1);
+ }
+
+ cFastRandom Random;
+ if (Random.NextInt(101) <= chance)
+ {
+ return;
+ }
+ }
+
if (GetInventory().DamageEquippedItem(a_Amount))
{
m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 43023ec28..acc9bd674 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -222,7 +222,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
m_ProjectileKind(a_Kind),
m_CreatorData(
((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1),
- ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : "")
+ ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : ""),
+ ((a_Creator != NULL) ? a_Creator->GetEquippedWeapon().m_Enchantments : cEnchantments())
),
m_IsInGround(false)
{
@@ -235,7 +236,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
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_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : ""),
+ m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "", a_Creator->GetEquippedWeapon().m_Enchantments),
m_IsInGround(false)
{
SetSpeed(a_Speed);
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index 0ebc32f36..990136a32 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -94,14 +94,16 @@ protected:
*/
struct CreatorData
{
- CreatorData(int a_UniqueID, const AString & a_Name) :
+ CreatorData(int a_UniqueID, const AString & a_Name, const cEnchantments & a_Enchantments) :
m_UniqueID(a_UniqueID),
- m_Name(a_Name)
+ m_Name(a_Name),
+ m_Enchantments(a_Enchantments)
{
}
const int m_UniqueID;
AString m_Name;
+ cEnchantments m_Enchantments;
};
/** The type of projectile I am */
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
index ab772e97b..d200ef3d7 100644
--- a/src/FurnaceRecipe.cpp
+++ b/src/FurnaceRecipe.cpp
@@ -12,8 +12,8 @@
-typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
-typedef std::list< cFurnaceRecipe::Fuel > FuelList;
+typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
+typedef std::list<cFurnaceRecipe::cFuel> FuelList;
@@ -30,7 +30,7 @@ struct cFurnaceRecipe::sFurnaceRecipeState
cFurnaceRecipe::cFurnaceRecipe()
- : m_pState( new sFurnaceRecipeState)
+ : m_pState(new sFurnaceRecipeState)
{
ReloadRecipes();
}
@@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void)
while (std::getline(f, ParsingLine))
{
LineNum++;
- ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
if (ParsingLine.empty())
{
continue;
}
+ // Remove comments from the line:
+ size_t FirstCommentSymbol = ParsingLine.find('#');
+ if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
+ {
+ ParsingLine.erase(ParsingLine.begin() + (const long)FirstCommentSymbol, ParsingLine.end());
+ }
+
switch (ParsingLine[0])
{
case '#':
@@ -103,159 +109,132 @@ void cFurnaceRecipe::ReloadRecipes(void)
-void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, int a_LineNum)
+void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum)
{
- // Fuel
- int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
- AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang)
-
- if (
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
- !ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
- !ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value
- )
+ AString Line(a_Line);
+ Line.erase(Line.begin()); // Remove the beginning "!"
+ Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
+
+ std::auto_ptr<cItem> Item(new cItem);
+ int BurnTime;
+
+ const AStringVector & Sides = StringSplit(Line, "=");
+ if (Sides.size() != 2)
{
+ LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
- // Add to fuel list:
- Fuel F;
- F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
- F.BurnTime = IBurnTime;
- m_pState->Fuel.push_back(F);
-}
-
-
-
-
+ if (!ParseItem(Sides[0], *Item))
+ {
+ LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
+ }
-void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum)
-{
- int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
- int OItemID = 0, OItemCount = 0, OItemHealth = 0;
- AString::size_type BeginPos = 0; // Begin at start of line
-
- if (
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
- !ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
- !ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID
- !ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value
- )
+ if (!StringToInteger<int>(Sides[1], BurnTime))
{
+ LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
- // Add to recipe list
- Recipe R;
- R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
- R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth);
- R.CookTime = IBurnTime;
- m_pState->Recipes.push_back(R);
+ // Add to fuel list:
+ cFuel Fuel;
+ Fuel.In = Item.release();
+ Fuel.BurnTime = BurnTime;
+ m_pState->Fuel.push_back(Fuel);
}
-void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
+void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
{
- LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
-}
-
-
+ AString Line(a_Line);
+ Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
+ int CookTime = 200;
+ std::auto_ptr<cItem> InputItem(new cItem());
+ std::auto_ptr<cItem> OutputItem(new cItem());
+ const AStringVector & Sides = StringSplit(Line, "=");
+ if (Sides.size() != 2)
+ {
+ LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
+ }
-bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue)
-{
- // TODO: replace atoi with std::stoi
- AString::size_type End;
- if (a_IsLastValue)
+ const AStringVector & InputSplit = StringSplit(Sides[0], "@");
+ if (!ParseItem(InputSplit[0], *InputItem))
{
- End = a_Text.find_first_not_of(a_Delimiter, a_Begin);
+ LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
- else
+
+ if (InputSplit.size() > 1)
{
- End = a_Text.find_first_of(a_Delimiter, a_Begin);
- if (End == AString::npos)
+ if (!StringToInteger<int>(InputSplit[1], CookTime))
{
- PrintParseError(a_Line, a_Begin, a_Delimiter);
- return false;
+ LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
}
-
- // stoi won't throw an exception if the string is alphanumeric, we should check for this
- if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin)))
+
+ if (!ParseItem(Sides[1], *OutputItem))
{
- PrintParseError(a_Line, a_Begin, "number");
- return false;
+ LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
- a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
- a_Begin = End + 1; // Jump over delimiter
- return true;
+ cRecipe Recipe;
+ Recipe.In = InputItem.release();
+ Recipe.Out = OutputItem.release();
+ Recipe.CookTime = CookTime;
+ m_pState->Recipes.push_back(Recipe);
}
-bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue)
+bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
{
- // TODO: replace atoi with std::stoi
- AString::size_type End, Begin = a_Begin;
+ AString ItemString = a_String;
+
+ const AStringVector & SplitAmount = StringSplit(ItemString, ",");
+ ItemString = SplitAmount[0];
- End = a_Text.find_first_of(a_DelimiterOne, Begin);
- if (End != AString::npos)
- {
- if (DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
- {
- a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str());
- Begin = End + 1;
+ const AStringVector & SplitMeta = StringSplit(ItemString, ":");
+ ItemString = SplitMeta[0];
- if (a_IsLastValue)
- {
- End = a_Text.find_first_not_of(a_DelimiterTwo, Begin);
- }
- else
- {
- End = a_Text.find_first_of(a_DelimiterTwo, Begin);
- if (End == AString::npos)
- {
- PrintParseError(a_Line, Begin, a_DelimiterTwo);
- return false;
- }
- }
-
- // stoi won't throw an exception if the string is alphanumeric, we should check for this
- if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
- {
- PrintParseError(a_Line, Begin, "number");
- return false;
- }
- a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str());
+ if (!StringToItem(ItemString, a_Item))
+ {
+ return false;
+ }
- a_Begin = End + 1; // Jump over delimiter
- return true;
- }
- else
+ if (SplitAmount.size() > 1)
+ {
+ if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
{
- return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
+ return false;
}
}
-
- return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
-}
-
-
-
-
-bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String)
-{
- // TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit)
- return (a_String.find_first_not_of("0123456789") == AString::npos);
+ if (SplitMeta.size() > 1)
+ {
+ if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
+ {
+ return false;
+ }
+ }
+ return true;
}
@@ -266,19 +245,19 @@ void cFurnaceRecipe::ClearRecipes(void)
{
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
- Recipe R = *itr;
- delete R.In;
- R.In = NULL;
- delete R.Out;
- R.Out = NULL;
+ cRecipe Recipe = *itr;
+ delete Recipe.In;
+ Recipe.In = NULL;
+ delete Recipe.Out;
+ Recipe.Out = NULL;
}
m_pState->Recipes.clear();
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
- Fuel F = *itr;
- delete F.In;
- F.In = NULL;
+ cFuel Fuel = *itr;
+ delete Fuel.In;
+ Fuel.In = NULL;
}
m_pState->Fuel.clear();
}
@@ -287,21 +266,21 @@ void cFurnaceRecipe::ClearRecipes(void)
-const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
+const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
{
- const Recipe * BestRecipe = 0;
+ const cRecipe * BestRecipe = 0;
for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
- const Recipe & R = *itr;
- if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
+ const cRecipe & Recipe = *itr;
+ if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
{
- if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
+ if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
{
continue;
}
else
{
- BestRecipe = &R;
+ BestRecipe = &Recipe;
}
}
}
@@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
int BestFuel = 0;
for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
- const Fuel & F = *itr;
- if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
+ const cFuel & Fuel = *itr;
+ if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
{
- if (BestFuel > 0 && (BestFuel > F.BurnTime))
+ if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
{
continue;
}
else
{
- BestFuel = F.BurnTime;
+ BestFuel = Fuel.BurnTime;
}
}
}
diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h
index 77ed35a57..6a1650695 100644
--- a/src/FurnaceRecipe.h
+++ b/src/FurnaceRecipe.h
@@ -19,23 +19,23 @@ public:
void ReloadRecipes(void);
- struct Fuel
+ struct cFuel
{
cItem * In;
int BurnTime; ///< How long this fuel burns, in ticks
};
- struct Recipe
+ struct cRecipe
{
cItem * In;
cItem * Out;
int CookTime; ///< How long this recipe takes to smelt, in ticks
};
- /// Returns a recipe for the specified input, NULL if no recipe found
- const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const;
+ /** Returns a recipe for the specified input, NULL if no recipe found */
+ const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const;
- /// Returns the amount of time that the specified fuel burns, in ticks
+ /** Returns the amount of time that the specified fuel burns, in ticks */
int GetBurnTime(const cItem & a_Fuel) const;
private:
@@ -43,33 +43,14 @@ private:
/** Parses the fuel contained in the line, adds it to m_pState's fuels.
Logs a warning to the console on input error. */
- void AddFuelFromLine(const AString & a_Line, int a_LineNum);
+ void AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum);
/** Parses the recipe contained in the line, adds it to m_pState's recipes.
Logs a warning to the console on input error. */
- void AddRecipeFromLine(const AString & a_Line, int a_LineNum);
-
- /** Calls LOGWARN with the line, position, and error */
- static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing);
-
- /** Reads a number from a string given, starting at a given position and ending at a delimiter given
- Updates beginning position to the delimiter found + 1, and updates the value to the one read
- If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing
- Otherwise, the function will return true
- */
- static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false);
-
- /** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given
- Updates beginning position to the second delimiter found + 1, and updates the values to the ones read
- If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing
- If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber()
- True will be returned definitively for an optional value that is valid
- */
- static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false);
-
- /** Uses std::all_of to determine if a string contains only digits */
- static bool DoesStringContainOnlyNumbers(const AString & a_String);
+ void AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum);
+ /** Parses an item string in the format "<ItemType>[: <Damage>][, <Amount>]", returns true if successful. */
+ bool ParseItem(const AString & a_String, cItem & a_Item);
struct sFurnaceRecipeState;
sFurnaceRecipeState * m_pState;
diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h
index fdc24689c..f29cc5d59 100644
--- a/src/Items/ItemBow.h
+++ b/src/Items/ItemBow.h
@@ -75,7 +75,6 @@ public:
Arrow = NULL;
return;
}
-
a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force);
if (!a_Player->IsGameModeCreative())
{
@@ -83,8 +82,19 @@ public:
{
a_Player->GetInventory().RemoveItem(cItem(E_ITEM_ARROW));
}
+ else
+ {
+ Arrow->SetPickupState(cArrowEntity::psNoPickup);
+ }
+
+
a_Player->UseEquippedItem();
}
+
+ if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
+ {
+ Arrow->StartBurning(100);
+ }
}
} ;
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
index 4e5c41a8a..28da83c31 100644
--- a/src/Protocol/MojangAPI.cpp
+++ b/src/Protocol/MojangAPI.cpp
@@ -158,7 +158,8 @@ cMojangAPI::cMojangAPI(void) :
m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER),
m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS),
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
- m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS)
+ m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
+ m_RankMgr(NULL)
{
}
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index c831da251..8b395230a 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -979,9 +979,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
@@ -991,9 +1000,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
diff --git a/src/RankManager.cpp b/src/RankManager.cpp
index dc6eec9e4..e5896f8f3 100644
--- a/src/RankManager.cpp
+++ b/src/RankManager.cpp
@@ -477,8 +477,8 @@ AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
{
SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?");
stmt.bind(1, a_PlayerUUID);
- stmt.executeStep();
- if (stmt.isDone())
+ // executeStep returns false on no data
+ if (!stmt.executeStep())
{
// No data returned from the DB
return AString();
diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp
index 97903074a..bfe59fbcb 100644
--- a/src/SetChunkData.cpp
+++ b/src/SetChunkData.cpp
@@ -134,8 +134,8 @@ void cSetChunkData::RemoveInvalidBlockEntities(void)
);
cBlockEntityList::iterator itr2 = itr;
itr2++;
- m_BlockEntities.erase(itr);
delete *itr;
+ m_BlockEntities.erase(itr);
itr = itr2;
}
else