Finished implementing Accessory Refinement window. Fixed bug with accessory refining function not updating stats for accessories already equipped. Added appropriate unit test. 212/212 unit tests passing. Release Build 11482.

pull/65/head
sigonasr2 3 months ago
parent 01ec5f5390
commit 564190d2c6
  1. 1
      Adventures in Lestoria Tests/BuffTests.cpp
  2. 5
      Adventures in Lestoria Tests/EnchantTests.cpp
  3. 1
      Adventures in Lestoria Tests/EngineTests.cpp
  4. 17
      Adventures in Lestoria Tests/ItemTests.cpp
  5. 6
      Adventures in Lestoria Tests/MonsterTests.cpp
  6. 1
      Adventures in Lestoria Tests/PlayerTests.cpp
  7. 8
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  8. 6
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  9. 4
      Adventures in Lestoria/ArtificerDisassembleWindow.cpp
  10. 80
      Adventures in Lestoria/ArtificerRefineResultWindow.cpp
  11. 16
      Adventures in Lestoria/ArtificerRefineWindow.cpp
  12. 57
      Adventures in Lestoria/DynamicMenuLabel.h
  13. 3
      Adventures in Lestoria/Item.cpp
  14. 2
      Adventures in Lestoria/Item.h
  15. 1
      Adventures in Lestoria/Menu.cpp
  16. 3
      Adventures in Lestoria/Menu.h
  17. 1
      Adventures in Lestoria/MenuType.h
  18. 2
      Adventures in Lestoria/Version.h
  19. BIN
      x64/Release/Adventures in Lestoria.exe

@ -105,6 +105,7 @@ namespace BuffTests
TEST_METHOD_CLEANUP(CleanupTests){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
TEST_METHOD(AddBuffMonsterCallbackExpireFunctionTest){
Monster&m{game->SpawnMonster({},MONSTER_DATA.at("TestName"))};

@ -116,6 +116,11 @@ namespace EnchantTests
DAMAGENUMBER_LIST.clear();
}
TEST_METHOD_CLEANUP(EnchantTestCleanup){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
TEST_METHOD(HealthBoostCheck){
Assert::AreEqual(100,player->GetMaxHealth(),L"Player starts with 100 health.");
std::weak_ptr<Item>nullRing{Game::GiveAndEquipEnchantedRing("Aura of the Beast")};

@ -103,6 +103,7 @@ namespace EngineTests
TEST_METHOD_CLEANUP(CleanupTests){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
TEST_METHOD(StripColorTest){
std::string noColCode{"Hello World!"};

@ -95,6 +95,11 @@ namespace ItemTests
Menu::themes.SetInitialized();
GFX.SetInitialized();
}
TEST_METHOD_CLEANUP(ItemCleanupTests){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
TEST_METHOD(ItemGiveTest){
Inventory::AddItem("Health Potion"s,3);
Assert::AreEqual(3U,Inventory::GetItemCount("Health Potion"s),L"Player has 3 Health Potions.");
@ -217,6 +222,7 @@ namespace ItemTests
std::weak_ptr<Item>testArmor{Inventory::AddItem("Test Armor"s)};
Assert::IsFalse(testArmor.lock()->CanBeRefined(),L"Test Armor should not be allowed to be refined since it's not an accessory.");
Inventory::AddItem(slimeKingRing.lock()->FragmentName(),50U);
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
Assert::IsTrue(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should now be allowed to be refined since we meet all requirements.");
player->SetMoney(0);
Assert::IsFalse(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should not be allowed to be refined since we do not have enough money.");
@ -228,6 +234,17 @@ namespace ItemTests
for(const auto&[attr,val]:slimeKingRing.lock()->RandomStats()){
Assert::AreEqual(ITEM_DATA[slimeKingRing.lock()->ActualName()].GetMaxStats().A_Read(attr),val,L"The current stats should be equal to the maximum stats when refinement is done.");
}
/*Ring of the Slime King has the following refining stats:
* StatValues = Health,Mana,Move Spd %
MinStats = 5,1,1
MaxStats = 20,4,3
*
Therefore, after this process is done the player should have 20 more health, 4 more mana, and 3% more move speed than normal.
*/
//These checks make sure that the refining call actually modifies already equipped items!
Assert::AreEqual(120,player->GetMaxHealth(),L"Player should now have 120 max health.");
Assert::AreEqual(104,player->GetMaxMana(),L"Player should now have 104 max mana.");
Assert::AreEqual(1.03f,player->GetMoveSpdMult(),L"Player should now have 103% move speed.");
}
TEST_METHOD(EnchantTestCheck){
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};

@ -108,8 +108,10 @@ namespace MonsterTests
SetupTestMonster();
SetupMockMap();
}
~MonsterTest(){
testGame.release();
TEST_METHOD_CLEANUP(MonsterTestCleanup){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
TEST_METHOD(DisplayNameCheck){
Assert::AreEqual("Test Monster",MONSTER_DATA["TestName"].GetDisplayName().c_str());

@ -103,6 +103,7 @@ namespace PlayerTests
TEST_METHOD_CLEANUP(CleanupTests){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
TEST_METHOD(PlayerAlive){
Assert::IsTrue(player->IsAlive());

@ -400,6 +400,10 @@
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="DynamicMenuLabel.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="HurtDamageInfo.h">
<SubType>
</SubType>
@ -773,6 +777,10 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="ArtificerRefineResultWindow.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="ArtificerRefineWindow.cpp">
<SubType>
</SubType>

@ -699,6 +699,9 @@
<ClInclude Include="MenuRefineLabel.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
<ClInclude Include="DynamicMenuLabel.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Player.cpp">
@ -1217,6 +1220,9 @@
<ClCompile Include="PoisonPool.cpp">
<Filter>Source Files\Effects</Filter>
</ClCompile>
<ClCompile Include="ArtificerRefineResultWindow.cpp">
<Filter>Source Files\Interface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

@ -109,7 +109,7 @@ void Menu::InitializeArtificerDisassembleWindow(){
EnableDisassemblyDisplay();
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem().lock());
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(GFX.at(item.GetItem().lock()->FragmentIcon().value()).Decal());
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(GFX.at(item.GetItem().lock()->FragmentName()).Decal());
Component<MenuLabel>(data.menu.type,"Disassembly Result Title")->SetLabel(item.GetItem().lock()->FragmentName());
Component<MenuLabel>(data.menu.type,"Fragment Total Count")->SetLabel(std::format("Currently Owned: {}",Inventory::GetItemCount(item.GetItem().lock()->FragmentName())));
return true;
@ -119,7 +119,7 @@ void Menu::InitializeArtificerDisassembleWindow(){
if(childComponent){
RowItemDisplay&item{childComponent.value().get()};
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem().lock());
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(GFX.at(item.GetItem().lock()->FragmentIcon().value()).Decal());
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(GFX.at(item.GetItem().lock()->FragmentName()).Decal());
Component<MenuLabel>(data.menu.type,"Disassembly Result Title")->SetLabel(item.GetItem().lock()->FragmentName());
Component<MenuLabel>(data.menu.type,"Fragment Total Count")->SetLabel(std::format("Currently Owned: {}",Inventory::GetItemCount(item.GetItem().lock()->FragmentName())));
EnableDisassemblyDisplay();

@ -0,0 +1,80 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "Menu.h"
#include "AdventuresInLestoria.h"
#include "MenuItemItemButton.h"
#include "DynamicMenuLabel.h"
INCLUDE_game
void Menu::InitializeArtificerRefineResultWindow(){
Menu*artificerRefineResultWindow=CreateMenu(ARTIFICER_REFINE_RESULT,CENTERED,vi2d{144,144});
auto itemIcon{artificerRefineResultWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{artificerRefineResultWindow->size.x/2.f-24.f,0.f},{48.f,48.f}},Item::BLANK,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END};
itemIcon->SetIconScale({2.f,2.f});
auto refineItemTextdisplay{artificerRefineResultWindow->ADD("Refine Item Text Display",MenuLabel)(geom2d::rect<float>{vf2d{0.f,artificerRefineResultWindow->size.y/2.f},vf2d{artificerRefineResultWindow->size.x,12.f}},"",1.f,ComponentAttr::SHADOW)END};
auto refineResultDisplay{artificerRefineResultWindow->ADD("Refine Result",DynamicMenuLabel)(geom2d::rect<float>{vf2d{0.f,artificerRefineResultWindow->size.y/2.f+artificerRefineResultWindow->size.y*0.33f},vf2d{artificerRefineResultWindow->size.x,12.f}},[](){return "";},1.f,ComponentAttr::SHADOW)END};
auto continueButton{artificerRefineResultWindow->ADD("Continue Button",MenuComponent)(geom2d::rect<float>{vf2d{artificerRefineResultWindow->size.x/4.f,artificerRefineResultWindow->size.y-6.f},{artificerRefineResultWindow->size.x/2.f,12.f}},"Continue",[](MenuFuncData data){
onClick:
Menu::CloseMenu();
return true;
})END};
artificerRefineResultWindow->SetupKeyboardNavigation(
[](MenuType type,Data&returnData){ //On Open
returnData="";
},
{ //Button Key
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}},
{game->KEY_BACK,{"Stay",[](MenuType type){
Menu::CloseMenu();
}}},
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}},
}
,{ //Button Navigation Rules
{"Sample Button",{
.up="",
.down="",
.left="",
.right="",}},
});
}

@ -43,6 +43,8 @@ All rights reserved.
#include "MenuRefineLabel.h"
#include "PlayerMoneyLabel.h"
#include "MenuDecal.h"
#include "SoundEffect.h"
#include "DynamicMenuLabel.h"
INCLUDE_game
@ -120,9 +122,19 @@ void Menu::InitializeArtificerRefineWindow(){
auto fragmentDisplayLabel{artificerRefineWindow->ADD("Fragment Label",MenuLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+80.f,152.f},{artificerRefineWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL|ComponentAttr::LEFT_ALIGN)END};
auto fragmentMoneyCostLabel{artificerRefineWindow->ADD("Fragment Money Cost Label",MenuLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+80.f,164.f},{artificerRefineWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
auto fragmentRefineButton{artificerRefineWindow->ADD("Fragment Refine Button",MenuComponent)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+96.f,180.f},{artificerRefineWindow->size.x/2-76.f,12.f}},"Refine",[](MenuFuncData data){
auto fragmentRefineButton{artificerRefineWindow->ADD("Fragment Refine Button",MenuComponent)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+96.f,180.f},{artificerRefineWindow->size.x/2-76.f,12.f}},"Refine",[EnableRefineDisplay](MenuFuncData data){
onClick:
SoundEffect::PlaySFX("Sprint",SoundEffect::CENTERED);
std::weak_ptr<Item>item{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()};
RefineResult result{item.lock()->Refine()};
Component<MenuItemItemButton>(ARTIFICER_REFINE_RESULT,"Item Icon")->SetItem(item);
Component<DynamicMenuLabel>(ARTIFICER_REFINE_RESULT,"Refine Result")->SetLabelUpdateFunction([result](){
const Pixel shimmeringColor{PixelLerp(WHITE,{220,220,220},sin((70*game->GetRunTime())/2.f+0.5f))};
return std::format("{} -> {}+{}{} #FFFF00UP!",result.first.Name(),shimmeringColor.toHTMLColorCode(),result.second,result.first.DisplayAsPercent()?"%":"");
});
Component<MenuLabel>(ARTIFICER_REFINE_RESULT,"Refine Item Text Display")->SetLabel(std::format("#FFFF00{} has been refined!",item.lock()->DisplayName()));
EnableRefineDisplay(); //Refresh the current display contents.
Menu::OpenMenu(ARTIFICER_REFINE_RESULT,true);
return true;
})END};

@ -0,0 +1,57 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#pragma once
#include "MenuLabel.h"
//A class holding a lambda function where you return an updated menu label that is declared on creation to reduce boilerplate for menu labels that just need to update text differently..
class DynamicMenuLabel:public MenuLabel{
public:
inline DynamicMenuLabel(geom2d::rect<float>rect,std::function<const std::string()>labelUpdateFunc,float scale=1,ComponentAttr attributes=ComponentAttr::NONE)
:labelUpdateFunc(labelUpdateFunc),MenuLabel(rect,"",scale,attributes){}
inline void SetLabelUpdateFunction(std::function<const std::string()>labelUpdateFunc){
this->labelUpdateFunc=labelUpdateFunc;
}
protected:
inline virtual void Update(AiL*game)override{
SetLabel(labelUpdateFunc());
MenuLabel::Update(game);
}
private:
std::function<const std::string()>labelUpdateFunc;
};

@ -1413,7 +1413,7 @@ const EquipSlot ItemInfo::StringToEquipSlot(std::string_view slotName){
return nameToEquipSlot[std::string(slotName)];
}
[[nodiscard]]const Inventory::DisassembleResult Inventory::Disassemble(std::weak_ptr<Item>itemRef){
const Inventory::DisassembleResult Inventory::Disassemble(std::weak_ptr<Item>itemRef){
if(ISBLANK(itemRef))ERR(std::format("WARNING! Trying to feed a blank item into the Disassemble function! THIS SHOULD NOT BE HAPPENING!"));
const std::shared_ptr<Item>&currentItem{itemRef.lock()};
if(!currentItem->IsAccessory())ERR(std::format("WARNING! Trying to disassemble Item {} which is not an accessory! THIS SHOULD NOT BE HAPPENING!",currentItem->ActualName()));
@ -1457,6 +1457,7 @@ RefineResult Item::Refine(){
const float oldAmt{randomizedStats.A_Read(chosenAttr)};
randomizedStats.A(chosenAttr)=std::min(maxStat,oldAmt+increaseAmt);
const float newAmt{randomizedStats.A_Read(chosenAttr)};
game->GetPlayer()->RecalculateEquipStats();
return RefineResult{chosenAttr,newAmt-oldAmt};
}

@ -310,7 +310,7 @@ public:
static void ResetLoadoutItemsUsed();
static void GivePlayerLoadoutItemsUsed();
static const bool EquipsFullyMaxedOut(int maxWeaponLevel=10,int maxArmorLevel=10);
static const Inventory::DisassembleResult Disassemble(std::weak_ptr<Item>itemRef);
[[nodiscard]]static const Inventory::DisassembleResult Disassemble(std::weak_ptr<Item>itemRef);
static bool SwapItems(ITCategory itemCategory,uint32_t slot1,uint32_t slot2);
//Makes sure this is a valid category. Will error out if it doesn't exist! Use for ERROR HANDLING!

@ -120,6 +120,7 @@ void Menu::InitializeMenus(){
InitializeCreditsWindow();
InitializeArtificerWindow();
InitializeArtificerRefineWindow();
InitializeArtificerRefineResultWindow();
InitializeArtificerDisassembleWindow();
InitializeArtificerEnchantWindow();
InitializeArtificerEnchantConfirmWindow();

@ -113,9 +113,8 @@ class Menu:public IAttributable{
static void InitializeCreditsWindow();
static void InitializeArtificerWindow();
static void InitializeArtificerRefineWindow();
static void InitializeArtificerRefineConfirmWindow();
static void InitializeArtificerRefineResultWindow();
static void InitializeArtificerDisassembleWindow();
static void InitializeArtificerDisassembleConfirmWindow();
static void InitializeArtificerEnchantWindow();
static void InitializeArtificerEnchantConfirmWindow();

@ -71,6 +71,7 @@ enum MenuType{
CREDITS, //100% Controller Compatibility
ARTIFICER, //100% Controller Compatibility
ARTIFICER_REFINE,
ARTIFICER_REFINE_RESULT,
ARTIFICER_DISASSEMBLE,
ARTIFICER_ENCHANT,
ARTIFICER_ENCHANT_CONFIRM,

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 5
#define VERSION_BUILD 11461
#define VERSION_BUILD 11483
#define stringify(a) stringify_(a)
#define stringify_(a) #a

Loading…
Cancel
Save