ItemTests migrated. Make FileTests cases reasonable (the outer folders will not have capitalization. Remove friend class errors among project. Release Build 13499.
Some checks failed
Emscripten Build / Build_and_Deploy_Web_Build (push) Failing after 4m25s
Emscripten Build / UnitTesting (push) Failing after 8m33s

This commit is contained in:
AMay 2026-05-02 00:20:18 -05:00
parent 69c6a84056
commit 3f7b45d773
15 changed files with 466 additions and 76 deletions

View File

@ -748,10 +748,6 @@
<ClInclude Include="steam\steam_api_flat.h" />
<ClInclude Include="steam\steam_api_internal.h" />
<ClInclude Include="steam\steam_gameserver.h" />
<ClInclude Include="TEST_DEFINES.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="TextEntryLabel.h">
<SubType>
</SubType>
@ -1070,6 +1066,7 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="ItemTests.cpp" />
<ClCompile Include="Key.cpp" />
<ClCompile Include="LargeStone.cpp" />
<ClCompile Include="LargeTornado.cpp">

View File

@ -672,9 +672,6 @@
<ClInclude Include="Pixel.h">
<Filter>Header Files\Engine</Filter>
</ClInclude>
<ClInclude Include="TEST_DEFINES.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Bullet.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -1415,6 +1412,9 @@
<ClCompile Include="FileTests.cpp">
<Filter>Source Files\Unit Tests</Filter>
</ClCompile>
<ClCompile Include="ItemTests.cpp">
<Filter>Source Files\Unit Tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

View File

@ -60,7 +60,8 @@ public:
static void Update();
static void UpdateLoop();
static void Play(const std::string_view sound);
[[nodiscard]]
static const size_t LoadAndPlaySFX(const std::string_view sound,const bool loop=true);
//Prepares a BGM for loading. This means we call UpdateLoop() repeatedly until the loading of the music is complete. Names are found in bgm.txt configuration file.
static void PrepareBGM(const std::string_view sound,const bool loop=true);

View File

@ -115,7 +115,7 @@ TEST(FileTests,"ImageLoadsProperRegardlessOfCaseSensitivity"){
REQUIRE(int(olc::rcode::OK)==int(testSpr.LoadFromFile("assets/title_transparent.png")));
REQUIRE_THROWS_AS(testSpr.LoadFromFile("assets/title_transparentfdjakl.png"),std::runtime_error);
REQUIRE(int(olc::rcode::OK)==int(testSpr.LoadFromFile("assets/title_transparent.png")));
REQUIRE(int(olc::rcode::OK)==int(testSpr.LoadFromFile("ASSets/TITle_transparent.PNg")));
REQUIRE(int(olc::rcode::OK)==int(testSpr.LoadFromFile("assets/TITle_transparent.PNg")));
}
TEST(FileTests,"DataFileLoadsProperRegardlessOfCaseSensitivity"){
datafile data;
@ -123,5 +123,5 @@ TEST(FileTests,"DataFileLoadsProperRegardlessOfCaseSensitivity"){
data.Reset();
REQUIRE_THROWS_AS(datafile::Read(data,"assets/config/creditsfdsfdsafd.txt"),std::runtime_error);
data.Reset();
REQUIRE(datafile::Read(data,"ASSets/Config/credITS.tXt"));
REQUIRE(datafile::Read(data,"assets/config/credITS.tXt"));
}

View File

@ -283,7 +283,6 @@ class Inventory{
friend class Item;
friend class SaveFile;
friend class InventoryCreator;
friend class ItemTests::ItemTest;
public:
enum class DisassembleResult{
@ -334,7 +333,11 @@ private:
static std::multimap<IT,std::shared_ptr<Item>>_inventory;
static std::vector<std::shared_ptr<Item>>blacksmithInventory;
static std::map<EquipSlot,std::weak_ptr<Item>>equipment;
#ifdef UNIT_TESTING
public:
#endif
static std::array<std::pair<IT,int>,3U>loadoutItemsUsed;
private:
//Only contains "1" of every item, as this is a map to index items and not the actual storage of items!
static std::map<ITCategory,std::vector<std::shared_ptr<Item>>>sortedInv;
};

View File

@ -44,8 +44,6 @@ All rights reserved.
class ItemEnchantInfo{
friend class ItemEnchant;
friend class ItemTests::ItemTest;
friend class PlayerTests::PlayerTest;
public:
enum class TextStyle{
NORMAL,
@ -108,7 +106,11 @@ private:
ItemAttributable minStatModifiers;
ItemAttributable maxStatModifiers;
std::unordered_map<std::string,float>config;
#ifdef UNIT_TESTING
public:
#endif
static std::unordered_map<std::string,ItemEnchantInfo>ENCHANT_LIST;
private:
static std::unordered_map<ItemEnchantCategory,ItemEnchantCategoryData>ENCHANT_CATEGORIES;
AbilityDescriptionModifiers modifiers;
};

View File

@ -0,0 +1,443 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy 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 "AdventuresInLestoria.h"
#include "Tutorial.h"
#include <random>
#include <format>
#include "ItemDrop.h"
#include <ranges>
#include "GameHelper.h"
#include"RowInventoryScrollableWindowComponent.h"
#include"SaveFile.h"
#include"SoundEffect.h"
using namespace olc::utils;
INCLUDE_GFX
INCLUDE_ITEM_DATA
INCLUDE_INITIALIZEGAMECONFIGURATIONS
extern std::mt19937 rng;
class ItemTests{
public:
std::unique_ptr<AiL>testGame;
InputGroup testKeyboardInput;
Player*player;
HWButton*testKey;
ItemTests(){
InitializeGameConfigurations();
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
testGame.reset(new AiL(true));
ItemAttribute::Initialize();
ItemInfo::InitializeItems();
testGame->InitializeGraphics();
testGame->InitializeClasses();
sig::Animation::InitializeAnimations();
Monster::InitializeStrategies();
MonsterData::InitializeMonsterData();
sig::Animation::InitializeAnimations();
testGame->InitializeDefaultKeybinds();
testGame->InitializePlayer();
sig::Animation::SetupPlayerAnimations();
Menu::InitializeMenus();
Tutorial::Initialize();
Stats::InitializeDamageReductionTable();
SoundEffect::Initialize();
GameState::Initialize();
GameState::STATE=GameState::states.at(States::State::GAME_RUN);
testGame->ResetLevelStates();
#pragma region Setup a fake test map and test monster
game->MAP_DATA.Unlock();
game->MAP_DATA["CAMPAIGN_1_1"];
game->MAP_DATA["HUB_TYPE_TEST_MAP"];
game->MAP_DATA["WORLD_TYPE_TEST_MAP"];
ItemDrop::ClearDrops();
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
MONSTER_DATA["TestName"]=testMonsterData;
#pragma endregion
player=testGame->GetPlayer();
//Setup key "0" as a test input
testKeyboardInput.AddKeybind(Input{InputType::KEY,0});
testKey=testGame->GetKeyboardState(0);
testGame->olc_UpdateKeyFocus(true); //Force the game to be "focused" for tests. Required if we want keyboard inputs to work.
Menu::themes.SetInitialized();
GFX.SetInitialized();
game->MAP_DATA.SetInitialized();
MapTag dungeonMapData{};
game->MAP_DATA["HUB_TYPE_TEST_MAP"].mapType=Map::MapType::HUB;
game->MAP_DATA["WORLD_TYPE_TEST_MAP"].mapType=Map::MapType::WORLD_MAP;
}
~ItemTests(){
testGame->EndGame();
testGame->OnUserUpdate(0.f);
testGame.reset();
}
};
TEST(ItemTests,"ItemGiveTest"){
Inventory::AddItem("Health Potion"s,3);
REQUIRE(3U==Inventory::GetItemCount("Health Potion"s));
}
TEST(ItemTests,"ItemAddAndRemoveAffectsStageLootTest"){
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Game::LoadLevelWithTMX("CAMPAIGN_1_1",testGame.get());
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3);
REQUIRE(size_t(1)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3,true);
REQUIRE(size_t(1)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(1)==Inventory::get("Monster Loot").size());
Game::LoadLevelWithTMX("BOSS_1",testGame.get());
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3);
REQUIRE(size_t(1)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3,true);
REQUIRE(size_t(1)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(1)==Inventory::get("Monster Loot").size());
Game::LoadFakeLevel("HUB_TYPE_TEST_MAP",testGame.get());
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3);
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3,true);
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Game::LoadFakeLevel("WORLD_TYPE_TEST_MAP",testGame.get());
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3);
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
Inventory::AddItem("Health Potion"s,3,true);
REQUIRE(size_t(0)==Inventory::get("Stage Loot").size());
REQUIRE(size_t(0)==Inventory::get("Monster Loot").size());
}
TEST(ItemTests,"ItemRemoveTest"){
Inventory::AddItem("Health Potion"s,3);
for(std::weak_ptr<Item>&item:Inventory::GetItem("Health Potion"s))Inventory::RemoveItem(item,3);
REQUIRE(0U==Inventory::GetItemCount("Health Potion"s));
}
TEST(ItemTests,"ItemQuantityStackTest"){
Inventory::AddItem("Health Potion"s,3);
Inventory::AddItem("Health Potion"s,3);
REQUIRE(6U==Inventory::GetItemCount("Health Potion"s));
}
TEST(ItemTests,"EquipmentNoStackTest"){
Inventory::AddItem("Ring of the Slime King"s);
Inventory::AddItem("Ring of the Slime King"s);
Inventory::AddItem("Ring of the Slime King"s);
int itemCounter{};
for(std::weak_ptr<Item>&item:Inventory::GetItem("Ring of the Slime King"s))itemCounter++;
REQUIRE(3==itemCounter);
}
TEST(ItemTests,"UsingBlankLoadoutItemDoesNothing"){
REQUIRE(!game->UseLoadoutItem(0));
REQUIRE(!game->UseLoadoutItem(1));
REQUIRE(!game->UseLoadoutItem(2));
}
TEST(ItemTests,"UsingLoadoutItemOfQuantityZeroDoesNothing"){
REQUIRE_THROWS_AS(game->SetLoadoutItem(0,"Minor Health Potion"),std::exception);
}
TEST(ItemTests,"UsingLoadoutItemConsumesIt"){
player->Hurt(1,player->OnUpperLevel(),player->GetZ());
Inventory::AddItem("Minor Health Potion"s,5U);
game->SetLoadoutItem(0,"Minor Health Potion");
testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput);
REQUIRE(1==Inventory::loadoutItemsUsed[0].second);
REQUIRE(4U==Inventory::GetItemCount("Minor Health Potion"s));
REQUIRE(player->GetItem1().GetCooldownTime()==player->GetItem1().cooldown);
}
TEST(ItemTests,"ItemScriptBuffTest"){
player->Hurt(1,player->OnUpperLevel(),player->GetZ());
Inventory::AddItem("Stat Up Everything Potion"s,5U);
game->SetLoadoutItem(0,"Stat Up Everything Potion");
testKey->bHeld=true; //Simulate key being pressed.
REQUIRE_THROWS_AS(player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput),std::exception);
}
TEST(ItemTests,"FlatRestoreScriptTest"){
player->Hurt(75,player->OnUpperLevel(),player->GetZ());
player->ConsumeMana(76);
Inventory::AddItem("Flat Recovery Potion"s,5U);
game->SetLoadoutItem(0,"Flat Recovery Potion");
testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput);
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(0.05f);
game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us. We're also going to gain one mana during this tick.
REQUIRE(75==player->GetHealth());
REQUIRE(75==player->GetMana());
}
TEST(ItemTests,"PctRestoreScriptTest"){
player->Hurt(75,player->OnUpperLevel(),player->GetZ());
player->ConsumeMana(76);
Inventory::AddItem("Pct Recovery Potion"s,5U);
game->SetLoadoutItem(1,"Pct Recovery Potion");
testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem2(),testKeyboardInput);
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(0.05f);
game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us.
REQUIRE(75==player->GetHealth());
REQUIRE(75==player->GetMana());
}
TEST(ItemTests,"HealOverTimeTest"){
player->Hurt(75,player->OnUpperLevel(),player->GetZ());
Inventory::AddItem("Bandages"s,5U);
game->SetLoadoutItem(2,"Bandages");
testKey->bHeld=true; //Simulate key being pressed.
player->CheckAndPerformAbility(player->GetItem3(),testKeyboardInput);
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
game->SetElapsedTime(0.05f);
game->OnUserUpdate(0.05f);//Wait some time as the item applies a buff that heals us.
REQUIRE(30==player->GetHealth());
game->SetElapsedTime(1.f);
game->OnUserUpdate(1.f);
for(int seconds{1};seconds<=6;seconds++){
REQUIRE(30+seconds*5==player->GetHealth());
game->OnUserUpdate(1.f);
}
REQUIRE(60==player->GetHealth());
}
TEST(ItemTests,"DisassembleAccessoryTest"){
Inventory::AddItem("Ring of the Slime King"s);
std::weak_ptr<Item>disassembleRingTest{Inventory::AddItem("Ring of the Slime King"s)};
Inventory::AddItem("Ring of the Slime King"s);
Inventory::Disassemble(disassembleRingTest);
REQUIRE(2U==Inventory::GetItemCount("Ring of the Slime King"s));
REQUIRE(disassembleRingTest.expired());
REQUIRE(1U==Inventory::GetItemCount(ITEM_DATA["Ring of the Slime King"].FragmentName()));
}
TEST(ItemTests,"DisassembleNonAccessoryTest"){
Inventory::AddItem("Ring of the Slime King"s);
try{
Inventory::Disassemble(Inventory::AddItem("Test Armor"s));
FAIL("Disassembling Test Armor succeeded! This should NOT be allowed!");
}catch(std::runtime_error&e){}
try{
Inventory::Disassemble(Inventory::AddItem("Green Slime Remains"s));
FAIL("Disassembling Green Slime Remains succeeded! This should NOT be allowed!");
}catch(std::runtime_error&e){}
try{
Inventory::Disassemble(Inventory::AddItem("Health Potion"s));
FAIL("Disassembling a Health Potion succeeded! This should NOT be allowed!");
}catch(std::runtime_error&e){}
}
TEST(ItemTests,"RefiningTest"){
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};
REQUIRE(!slimeKingRing.lock()->CanBeRefined());
std::weak_ptr<Item>testArmor{Inventory::AddItem("Test Armor"s)};
REQUIRE(!testArmor.lock()->CanBeRefined());
Inventory::AddItem(slimeKingRing.lock()->FragmentName(),50U);
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
REQUIRE(slimeKingRing.lock()->CanBeRefined());
player->SetMoney(0);
REQUIRE(!slimeKingRing.lock()->CanBeRefined());
player->SetMoney(10000);
while(slimeKingRing.lock()->CanBeRefined()){
RefineResult result{slimeKingRing.lock()->Refine()};
LOG(std::format("Enhanced {} by {}",result.first.Name(),result.second));
}
for(const auto&[attr,val]:slimeKingRing.lock()->RandomStats()){
REQUIRE(ITEM_DATA[slimeKingRing.lock()->ActualName()].GetMaxStats().A_Read(attr)==val);
}
/*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!
REQUIRE(120==player->GetMaxHealth());
REQUIRE(104==player->GetMaxMana());
REQUIRE(1.03f==player->GetMoveSpdMult());
}
TEST(ItemTests,"EnchantTestCheck"){
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};
REQUIRE(!slimeKingRing.lock()->HasEnchant());
bool obtainedDuplicate{false};
for(int i:std::ranges::iota_view(0,1000)){
std::optional<ItemEnchant>previousEnchant{slimeKingRing.lock()->GetEnchant()};
std::string previousEnchantName{};
if(slimeKingRing.lock()->HasEnchant())previousEnchantName=slimeKingRing.lock()->GetEnchant().value().Name();
slimeKingRing.lock()->_EnchantItem(ItemEnchant::RollRandomEnchant(previousEnchant));
REQUIRE(slimeKingRing.lock()->HasEnchant());
if(previousEnchantName==slimeKingRing.lock()->GetEnchant().value().Name()){
bool improvementExists{false};
std::string statDowngrades{};
if(previousEnchant.value().HasAttributes()){
for(const auto&[attr,val]:previousEnchant.value()){
if(slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName())>val){
improvementExists=true;
break;
}else statDowngrades+=std::format("{} - Previous:{}, New:{}\n",attr.Name(),val,slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName()));
}
}else improvementExists=true;
REQUIRE(improvementExists);
obtainedDuplicate=true;
}
if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())REQUIRE(int(player->GetClass())==int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability.
}
Game::ResetPlayerAndChangeClass(WIZARD,player,&*testGame);
for(int i:std::ranges::iota_view(0,1000)){
std::optional<ItemEnchant> previousEnchant{slimeKingRing.lock()->GetEnchant()};
std::string previousEnchantName{};
if(slimeKingRing.lock()->HasEnchant())previousEnchantName=slimeKingRing.lock()->GetEnchant().value().Name();
slimeKingRing.lock()->_EnchantItem(ItemEnchant::RollRandomEnchant(previousEnchant));
REQUIRE(slimeKingRing.lock()->HasEnchant());
if(previousEnchantName==slimeKingRing.lock()->GetEnchant().value().Name()){
bool improvementExists{false};
std::string statDowngrades{};
if(previousEnchant.value().HasAttributes()){
for(const auto&[attr,val]:previousEnchant.value()){
if(slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName())>val){
improvementExists=true;
break;
}else statDowngrades+=std::format("{} - Previous:{}, New:{}\n",attr.Name(),val,slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName()));
}
}else improvementExists=true;
REQUIRE(improvementExists);
obtainedDuplicate=true;
}
if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())REQUIRE(int(player->GetClass())==int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability.
}
REQUIRE(obtainedDuplicate);
}
TEST(ItemTests,"AccessoryAntiCompatibilityCheck"){
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
std::weak_ptr<Item>extraRing2{Inventory::AddItem("Ring of the Slime King"s)};
Inventory::EquipItem(extraRing,EquipSlot::RING2);
REQUIRE(Item::SelectedEquipIsDifferent(extraRing2,EquipSlot::RING1));
REQUIRE(!Item::SelectedEquipIsDifferent(extraRing,EquipSlot::RING1));
Inventory::UnequipItem(EquipSlot::RING2);
REQUIRE(Item::SelectedEquipIsDifferent(extraRing,EquipSlot::RING1));
}
TEST(ItemTests,"AccessoryRandomEnchantTest"){
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
std::unordered_map<ItemEnchantInfo::ItemEnchantCategory,uint32_t>enchantCounts;
for(int i:std::ranges::iota_view(0,1000)){
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
player->AddMoney("Fragment Enchant Cost"_i[1]);
const ItemEnchant&resultEnchant{extraRing.lock()->ApplyRandomEnchant()};
if(resultEnchant.GetClass().has_value())REQUIRE(int(resultEnchant.GetClass().value())==int(player->GetClass()));
enchantCounts[resultEnchant.Category()]++;
REQUIRE(extraRing.lock()->GetEnchant().has_value());
REQUIRE(resultEnchant.Name()==extraRing.lock()->GetEnchant().value().Name());
REQUIRE(!player->HasEnchant(resultEnchant.Name()));
Inventory::EquipItem(extraRing,EquipSlot::RING1);
REQUIRE(player->HasEnchant(resultEnchant.Name()));
Inventory::UnequipItem(EquipSlot::RING1);
}
Test::InRange(enchantCounts[ItemEnchantInfo::ItemEnchantCategory::GENERAL],{450U,550U});
Test::InRange(enchantCounts[ItemEnchantInfo::ItemEnchantCategory::CLASS],{350U,450U});
Test::InRange(enchantCounts[ItemEnchantInfo::ItemEnchantCategory::UNIQUE],{50U,150U});
}
TEST(ItemTests,"EnchantColorTest"){
REQUIRE("Item Enchants.General Enchants.Enchant Display Color"_Pixel.n==ItemEnchantInfo::GetEnchant("Health Boost").DisplayCol().n);
REQUIRE("Item Enchants.Class Enchants.Enchant Display Color"_Pixel.n==ItemEnchantInfo::GetEnchant("Quickdraw").DisplayCol().n);
REQUIRE("Item Enchants.Unique Enchants.Enchant Display Color"_Pixel.n==ItemEnchantInfo::GetEnchant("Magical Protection").DisplayCol().n);
}
TEST(ItemTests,"CanBeEnchantedTest"){
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
std::weak_ptr<Item>testArmor{Inventory::AddItem("Test Armor"s)};
REQUIRE(!extraRing.lock()->CanBeEnchanted());
REQUIRE(!testArmor.lock()->CanBeEnchanted());
player->SetMoney(2000U);
REQUIRE(!extraRing.lock()->CanBeEnchanted());
player->SetMoney(0U);
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
REQUIRE(!extraRing.lock()->CanBeEnchanted());
player->SetMoney(2000U);
REQUIRE(extraRing.lock()->CanBeEnchanted());
extraRing.lock()->ApplyRandomEnchant();
REQUIRE(!extraRing.lock()->CanBeEnchanted());
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
REQUIRE(extraRing.lock()->CanBeEnchanted());
REQUIRE(uint32_t(2000-"Fragment Enchant Cost"_i[1])==player->GetMoney());
}
TEST(ItemTests,"UIUpdatesWhenItemsAreReceivedTest"){
Inventory::AddItem("Health Potion"s,3);
Game::Update(0.f);
for(const auto&category:Item::GetItemCategories()|std::views::filter([](const std::string&category){return DATA["ItemCategory"][category].GetString()!="!HIDE";})){
const size_t itemCount{Component<RowInventoryScrollableWindowComponent>(MenuType::INVENTORY,std::format("Inventory Display - {}",category))->GetMainComponentCount()};
REQUIRE(Inventory::get(category).size()==itemCount);
}
}
TEST(ItemTests,"UIUpdatesWhenGameFileWithItemsIsLoadedTest"){
SaveFile::SetSaveFileID(-1);
SaveFile::LoadGame();
Game::Update(0.f);
for(const auto&category:Item::GetItemCategories()|std::views::filter([](const std::string&category){return DATA["ItemCategory"][category].GetString()!="!HIDE";})){
const size_t itemCount{Component<RowInventoryScrollableWindowComponent>(MenuType::INVENTORY,std::format("Inventory Display - {}",category))->GetMainComponentCount()};
REQUIRE(Inventory::get(category).size()==itemCount);
}
}
TEST(ItemTests,"UIDoesNotUpdateWhenSavingFileFlagIsEnabledTest"){
game->savingFile=true;
Inventory::AddItem("Health Potion"s,3);
for(const auto&category:Item::GetItemCategories()|std::views::filter([](const std::string&category){return DATA["ItemCategory"][category].GetString()!="!HIDE";})){
const size_t itemCount{Component<RowInventoryScrollableWindowComponent>(MenuType::INVENTORY,std::format("Inventory Display - {}",category))->GetMainComponentCount()};
REQUIRE(size_t(0)==itemCount);
}
}
TEST(ItemTests,"ConsumedLoadoutSlotTest"){
Inventory::AddItem("Health Potion"s,4);
Inventory::AddItem("Mana Potion"s,2);
Inventory::AddItem("Elixir of the Wind"s,1);
Inventory::AddItem("Boar Meat"s,3);
game->SetLoadoutItem(0,"Health Potion");
game->SetLoadoutItem(1,"Mana Potion");
game->SetLoadoutItem(2,"Elixir of the Wind");
const bool usedItemResult{game->UseLoadoutItem(2)};
const bool usedItemResult2{game->UseLoadoutItem(2)};
REQUIRE(usedItemResult);
REQUIRE(!usedItemResult2);
game->SetLoadoutItem(2,"Boar Meat");
}

View File

@ -64,10 +64,6 @@ namespace MoveFlag{
};
};
namespace PlayerTest{
class PlayerTests;
}
class EntityStats{
friend class Inventory;
ItemAttributable equipStats; //The stats after gear calculations are applied.

View File

@ -51,13 +51,7 @@ private:
static std::unordered_set<size_t>playingSoundEffects;
};
namespace MonsterTests{
class MonsterTest;
};
class SoundEffect{
friend class PlayerTests::PlayerTest;
friend class MonsterTests::MonsterTest;
public:
SoundEffect(const std::string_view filename,const float&vol,const float&minPitch=0.9f,const float&maxPitch=1.1f,const bool combatSound=false,const bool treatAsBGM=false);
static void PlaySFX(const std::string&eventName,const vf2d&pos);

View File

@ -1,46 +0,0 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy 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
namespace PlayerTests{
class PlayerTest;
}
namespace ItemTests{
class ItemTest;
}

View File

@ -127,10 +127,6 @@ namespace MonsterTests{
class MonsterTest;
}
namespace ItemTests{
class ItemTest;
}
namespace Game{
void LoadFakeLevel(const std::string&mapKeyname,AiL*const game);
};
@ -138,7 +134,6 @@ namespace Game{
struct Map{
friend class AiL;
friend class TMXParser;
friend class ItemTests::ItemTest;
friend void Game::LoadFakeLevel(const std::string&mapKeyname,AiL*const game);
enum class MapType{
DUNGEON,
@ -156,7 +151,11 @@ private:
std::vector<EnvironmentalAudio>environmentalAudioData;
std::vector<ItemMapData>stageLoot;
std::vector<NPCData>npcs;
#ifdef UNIT_TESTING
public:
#endif
MapType mapType{};
private:
std::string bgmSongName="";
std::string audioEvent{"Default Volume"};
std::unordered_map<Class,float>devCompletionTrialTime;

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 13488
#define VERSION_BUILD 13499
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -18003,5 +18003,6 @@ using Catch::Detail::Approx;
#define EVALUATE(a,b) PASTE(a,b)
#define TEST(cl,testCaseName) void EVALUATE(Unused##cl,__COUNTER__)()
#define REQUIRE(condition)
#define REQUIRE_THROWS_AS(condition,exception)
#define APPROX(val,err)
#endif

View File

@ -411,7 +411,6 @@ return 0;
#include "olcUTIL_Geometry2D.h"
#include "Pixel.h"
#include "util.h"
#include "TEST_DEFINES.h"
#pragma endregion
#define PGE_VER 225

View File

@ -64,6 +64,7 @@ David Barr, aka javidx9, <20>OneLoneCoder 2019, 2020, 2021, 2022
#include <numeric>
#include "Error.h"
#include<ranges>
#include"util.h"
using namespace std::literals;