From a0896c63d7a02d1b90572d591a16a79b4274feac Mon Sep 17 00:00:00 2001 From: Alex Sweet Date: Tue, 10 Apr 2018 23:46:11 -0700 Subject: Smelting Gives Experience (#4094) * Smelting Exp Smelting now gives experience * Furnace.txt update Exp rewards are entered in furnace.txt, Reward calculation is now done is the furnaceentity class * furnace.txt update Changed alignment tabs to spaces Included documentation of exp in recipe * Updated StringToFloat changed strtod to strtof * Explicit Float to Int * Reworked Smelting Rewards * No C casts -Adds new function to the api -Sets reward counter to 0 in furnace constructor * Style and exp lock removed -Fixed style mistakes accoring to PR notes -XP isn't locked to a single player anymore * No Smelter API -Removed SetLastSmelter and GetLastSmelter -Fixed comments -Fixed log reward amounts --- Server/Plugins/APIDump/Classes/BlockEntities.lua | 10 ++ Server/furnace.txt | 142 +++++++++++------------ src/BlockEntities/FurnaceEntity.cpp | 19 +++ src/BlockEntities/FurnaceEntity.h | 6 + src/FurnaceRecipe.cpp | 18 ++- src/FurnaceRecipe.h | 1 + src/StringUtils.cpp | 12 ++ src/StringUtils.h | 3 + src/UI/SlotArea.cpp | 7 +- 9 files changed, 142 insertions(+), 76 deletions(-) diff --git a/Server/Plugins/APIDump/Classes/BlockEntities.lua b/Server/Plugins/APIDump/Classes/BlockEntities.lua index eac70c1c8..bcdba0d0f 100644 --- a/Server/Plugins/APIDump/Classes/BlockEntities.lua +++ b/Server/Plugins/APIDump/Classes/BlockEntities.lua @@ -964,6 +964,16 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), }, Notes = "Returns true if there's time before the current fuel is depleted", }, + GetAndResetReward = + { + Returns = + { + { + Type = "number", + }, + }, + Notes = "Calculates, resets, and returns the experience reward in this furnace", + }, SetFuelSlot = { Params = diff --git a/Server/furnace.txt b/Server/furnace.txt index eb75c6148..af9168110 100644 --- a/Server/furnace.txt +++ b/Server/furnace.txt @@ -19,12 +19,13 @@ # # **** Recipe and result **** # -# Cobble @ 200 = Stone -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds) +# Cobble @ 200 = Stone $ 15 -> Produces 1 smooth stone from 1 cobblestone in 200 ticks (10 seconds). Rewards 15 experience. # # Write in full: -# Cobble : 0 , 1 @ 200 = 1 : 1 , 1 -# ItemType : Damage , Amount @ ticks = ItemType : Damage , Amount +# Cobble : 0 , 1 @ 200 = 1 : 1 , 1 $ 10 +# ItemType : Damage , Amount @ ticks = ItemType : Damage , Amount $ Experience # +# If the experience paramater isn't included, experience reward will default to zero. # # **** Fuel **** # @@ -42,74 +43,73 @@ #-------------------------- # Smelting recipes -Beef = Steak -BlackTerracotta = BlackGlazedTerracotta -BlueTerracotta = BlueGlazedTerracotta -BrownTerracotta = BrownGlazedTerracotta -Cactus = CactusGreen -ChainmailBoots = IronNugget -ChainmailChestplate = IronNugget -ChainmailHelmet = IronNugget -ChainmailLeggings = IronNugget -Chicken = CookedChicken -ChorusFruit = PoppedChorusFruit -Clay = Brick -ClayBlock = HardenedClay -CoalOre = Coal -Cobblestone = Stone -CrackedStonebrick = Stonebrick -CyanTerracotta = CyanGlazedTerracotta -DiamondOre = Diamond -EmeraldOre = Emerald -Fish = CookedFish -GoldOre = GoldIngot -GoldAxe = GoldNugget -GoldBoots = GoldNugget -GoldChestplate = GoldNugget -GoldHorseArmor = GoldNugget -GoldHelmet = GoldNugget -GoldHoe = GoldNugget -GoldPants = GoldNugget -GoldPickaxe = GoldNugget -GoldShovel = GoldNugget -GoldSword = GoldNugget -GrayTerracotta = GrayGlazedTerracotta -GreenTerracotta = GreenGlazedTerracotta -IronOre = IronIngot -IronAxe = IronNugget -IronBoots = IronNugget -IronChestplate = IronNugget -IronHorseArmor = IronNugget -IronHelmet = IronNugget -IronHoe = IronNugget -IronLeggings = IronNugget -IronPickaxe = IronNugget -IronShovel = IronNugget -IronSword = IronNugget -LapisOre = LapisLazuli -LightBlueTerracotta = LightBlueGlazedTerracotta -LightGrayTerracotta = LightGrayGlazedTerracotta -LimeTerracotta = LimeGlazedTerracotta -Log = CharCoal -Log2 = CharCoal -MagentaTerracotta = MagentaGlazedTerracotta -Mutton = CookedMutton -NetherQuartzOre = NetherQuartz -Netherrack = NetherBrick -OrangeTerracotta = OrangeGlazedTerracotta -PinkTerracotta = PinkGlazedTerracotta -Porkchop = CookedPorkchop -Potato = BakedPotato -PurpleTerracotta = PurpleGlazedTerracotta -Rabbit = CookedRabbit -RedTerracotta = RedGlazedTerracotta -RedstoneOre = Redstone -Salmon = CookedSalmon -Sand = Glass -StoneBrick = CrackedStoneBricks -WetSponge = Sponge -WhiteTerracotta = WhiteGlazedTerracotta -YellowTerracotta = YellowGlazedTerracotta +Beef = Steak $ 0.35 +BlackTerracotta = BlackGlazedTerracotta $ 0.1 +BlueTerracotta = BlueGlazedTerracotta $ 0.1 +BrownTerracotta = BrownGlazedTerracotta $ 0.10 +Cactus = CactusGreen $ 0.2 +ChainmailBoots = IronNugget $ 0.1 +ChainmailChestplate = IronNugget $ 0.1 +ChainmailHelmet = IronNugget $ 0.1 +ChainmailLeggings = IronNugget $ 0.1 +Chicken = CookedChicken $ 0.35 +ChorusFruit = PoppedChorusFruit $ 0.1 +Clay = Brick $ 0.3 +ClayBlock = HardenedClay $ 0.35 +CoalOre = Coal $ 0.1 +Cobblestone = Stone $ 0.1 +CyanTerracotta = CyanGlazedTerracotta $ 0.1 +DiamondOre = Diamond $ 1.0 +EmeraldOre = Emerald $ 1.0 +Fish = CookedFish $ 0.35 +GoldOre = GoldIngot $ 1.0 +GoldAxe = GoldNugget $ 0.1 +GoldBoots = GoldNugget $ 0.1 +GoldChestplate = GoldNugget $ 0.1 +GoldHorseArmor = GoldNugget $ 0.1 +GoldHelmet = GoldNugget $ 0.1 +GoldHoe = GoldNugget $ 0.1 +GoldPants = GoldNugget $ 0.1 +GoldPickaxe = GoldNugget $ 0.1 +GoldShovel = GoldNugget $ 0.1 +GoldSword = GoldNugget $ 0.1 +GrayTerracotta = GrayGlazedTerracotta $ 0.1 +GreenTerracotta = GreenGlazedTerracotta $ 0.1 +IronOre = IronIngot $ 0.7 +IronAxe = IronNugget $ 0.1 +IronBoots = IronNugget $ 0.1 +IronChestplate = IronNugget $ 0.1 +IronHorseArmor = IronNugget $ 0.1 +IronHelmet = IronNugget $ 0.1 +IronHoe = IronNugget $ 0.1 +IronLeggings = IronNugget $ 0.1 +IronPickaxe = IronNugget $ 0.1 +IronShovel = IronNugget $ 0.1 +IronSword = IronNugget $ 0.1 +LapisOre = LapisLazuli $ 0.2 +LightBlueTerracotta = LightBlueGlazedTerracotta $ 0.1 +LightGrayTerracotta = LightGrayGlazedTerracotta $ 0.1 +LimeTerracotta = LimeGlazedTerracotta $ 0.1 +Log = CharCoal $ 0.15 +Log2 = CharCoal $ 0.15 +MagentaTerracotta = MagentaGlazedTerracotta $ 0.1 +Mutton = CookedMutton $ 0.35 +NetherQuartzOre = NetherQuartz $ 0.2 +Netherrack = NetherBrick $ 0.1 +OrangeTerracotta = OrangeGlazedTerracotta $ 0.1 +PinkTerracotta = PinkGlazedTerracotta $ 0.1 +Porkchop = CookedPorkchop $ 0.35 +Potato = BakedPotato $ 0.35 +PurpleTerracotta = PurpleGlazedTerracotta $ 0.1 +Rabbit = CookedRabbit $ 0.35 +RedTerracotta = RedGlazedTerracotta $ 0.1 +RedstoneOre = Redstone $ 0.7 +Salmon = CookedSalmon $ 0.35 +Sand = Glass $ 0.1 +StoneBrick = CrackedStoneBricks $ 0.1 +WetSponge = Sponge $ 0.15 +WhiteTerracotta = WhiteGlazedTerracotta $ 0.1 +YellowTerracotta = YellowGlazedTerracotta $ 0.1 diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index 1f7f6e023..96a17782a 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -32,6 +32,7 @@ cFurnaceEntity::cFurnaceEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, in m_TimeCooked(0), m_FuelBurnTime(0), m_TimeBurned(0), + m_RewardCounter(0), m_IsLoading(false) { m_Contents.AddListener(*this); @@ -173,6 +174,23 @@ bool cFurnaceEntity::ContinueCooking(void) +int cFurnaceEntity::GetAndResetReward(void) +{ + int Reward = FloorC(m_RewardCounter); + float Remainder = m_RewardCounter - static_cast(Reward); + // Remainder is used as the percent chance of getting an extra xp point + if (GetRandomProvider().RandBool(Remainder)) + { + Reward++; + } + m_RewardCounter = 0.0; + return Reward; +} + + + + + void cFurnaceEntity::BroadcastProgress(short a_ProgressbarID, short a_Value) { cWindow * Window = GetWindow(); @@ -189,6 +207,7 @@ void cFurnaceEntity::BroadcastProgress(short a_ProgressbarID, short a_Value) void cFurnaceEntity::FinishOne() { m_TimeCooked = 0; + m_RewardCounter += m_CurrentRecipe->Reward; if (m_Contents.GetSlot(fsOutput).IsEmpty()) { diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index 7b189be85..b1166c3bf 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -84,6 +84,9 @@ public: /** Returns true if there's time left before the current fuel is depleted */ bool HasFuelTimeLeft(void) const { return (GetFuelBurnTimeLeft() > 0); } + /** Calculates, resets, and returns the experience reward in this furnace */ + int GetAndResetReward(void); + // tolua_end void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) @@ -130,6 +133,9 @@ protected: /** Amount of ticks that the current fuel has been burning */ int m_TimeBurned; + /** Running total of experience that can be picked up */ + float m_RewardCounter; + /** Is the block currently being loaded into the world? */ bool m_IsLoading; diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp index a2199cd33..a4647b7a0 100644 --- a/src/FurnaceRecipe.cpp +++ b/src/FurnaceRecipe.cpp @@ -161,6 +161,7 @@ void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_Li Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end()); int CookTime = 200; + float Reward = 0; std::unique_ptr InputItem = cpp14::make_unique(); std::unique_ptr OutputItem = cpp14::make_unique(); @@ -189,18 +190,27 @@ void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_Li return; } } - - if (!ParseItem(Sides[1], *OutputItem)) + const AStringVector & OutputSplit = StringSplit(Sides[1], "$"); + if (!ParseItem(OutputSplit[0], *OutputItem)) { - LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str()); + LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, OutputSplit[0].c_str()); LOGINFO("Offending line: \"%s\"", a_Line.c_str()); return; } - + if (OutputSplit.size() > 1) + { + if (!StringToFloat(OutputSplit[1], Reward)) + { + LOGWARNING("furnace.txt: line %d: Cannot parse reward \"%s\".", a_LineNum, OutputSplit[1].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } + } cRecipe Recipe; Recipe.In = InputItem.release(); Recipe.Out = OutputItem.release(); Recipe.CookTime = CookTime; + Recipe.Reward = Reward; m_pState->Recipes.push_back(Recipe); } diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h index 0779e18a4..84ee87092 100644 --- a/src/FurnaceRecipe.h +++ b/src/FurnaceRecipe.h @@ -30,6 +30,7 @@ public: cItem * In; cItem * Out; int CookTime; ///< How long this recipe takes to smelt, in ticks + float Reward; ///< Experience reward for creating 1 of this item }; /** Returns a recipe for the specified input, nullptr if no recipe found */ diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index b1f58f23c..ae0aec1ad 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -1051,3 +1051,15 @@ AString StringsConcat(const AStringVector & a_Strings, char a_Separator) } return res; } + +bool StringToFloat(const AString & a_String, float & a_Num) +{ + char *err; + a_Num = strtof(a_String.c_str(), &err); + if (*err != 0) + { + return false; + } + return true; +} + diff --git a/src/StringUtils.h b/src/StringUtils.h index a74239ec8..1c6cdf509 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -146,6 +146,9 @@ extern AStringVector MergeStringVectors(const AStringVector & a_Strings1, const /** Concatenates the specified strings into a single string, separated by the specified separator. */ extern AString StringsConcat(const AStringVector & a_Strings, char a_Separator); +/** Converts a string into a float. Returns false if the conversion fails. */ +extern bool StringToFloat(const AString & a_String, float & a_Num); + diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index d093bb337..ab1edf595 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -1909,7 +1909,6 @@ const cItem * cSlotAreaFurnace::GetSlot(int a_SlotNum, cPlayer & a_Player) const void cSlotAreaFurnace::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) { - UNUSED(a_Player); m_Furnace->SetSlot(a_SlotNum, a_Item); } @@ -1932,6 +1931,12 @@ void cSlotAreaFurnace::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) void cSlotAreaFurnace::HandleSmeltItem(const cItem & a_Result, cPlayer & a_Player) { + int Reward = m_Furnace->GetAndResetReward(); + if (Reward > 0) + { + a_Player.GetWorld()->SpawnExperienceOrb(a_Player.GetPosX(), a_Player.GetPosY(), a_Player.GetPosZ(), Reward); + } + /** TODO 2014-05-12 xdot: Figure out when to call this method. */ switch (a_Result.m_ItemType) { -- cgit v1.2.3