diff options
Diffstat (limited to '')
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 2 | ||||
-rw-r--r-- | src/BlockEntities/CommandBlockEntity.cpp | 9 | ||||
-rw-r--r-- | src/BlockEntities/FurnaceEntity.h | 2 | ||||
-rw-r--r-- | src/Blocks/BlockHandler.cpp | 39 | ||||
-rw-r--r-- | src/Blocks/BlockIce.h | 22 | ||||
-rw-r--r-- | src/CMakeLists.txt | 7 | ||||
-rw-r--r-- | src/Entities/ArrowEntity.cpp | 37 | ||||
-rw-r--r-- | src/Entities/ArrowEntity.h | 5 | ||||
-rw-r--r-- | src/Entities/Entity.cpp | 227 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 23 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.cpp | 5 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.h | 6 | ||||
-rw-r--r-- | src/FurnaceRecipe.cpp | 243 | ||||
-rw-r--r-- | src/FurnaceRecipe.h | 37 | ||||
-rw-r--r-- | src/Items/ItemBow.h | 12 | ||||
-rw-r--r-- | src/Protocol/MojangAPI.cpp | 3 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.cpp | 30 | ||||
-rw-r--r-- | src/RankManager.cpp | 4 | ||||
-rw-r--r-- | src/SetChunkData.cpp | 2 |
19 files changed, 514 insertions, 201 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/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/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/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 |