# 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 <EFBFBD> 2024 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# include "CppUnitTest.h"
# include "AdventuresInLestoria.h"
# include "Tutorial.h"
# include <random>
# include <format>
# include "ItemDrop.h"
# include <ranges>
using namespace Microsoft : : VisualStudio : : CppUnitTestFramework ;
using namespace olc : : utils ;
INCLUDE_GFX
INCLUDE_ITEM_DATA
INCLUDE_INITIALIZEGAMECONFIGURATIONS
extern std : : mt19937 rng ;
namespace ItemTests
{
TEST_CLASS ( ItemTest )
{
public :
std : : unique_ptr < AiL > testGame ;
InputGroup testKeyboardInput ;
Player * player ;
HWButton * testKey ;
TEST_METHOD_INITIALIZE ( ItemInitialize ) {
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 ( ) ;
testGame - > InitializeDefaultKeybinds ( ) ;
testGame - > InitializePlayer ( ) ;
sig : : Animation : : SetupPlayerAnimations ( ) ;
Menu : : InitializeMenus ( ) ;
Tutorial : : Initialize ( ) ;
Stats : : InitializeDamageReductionTable ( ) ;
GameState : : Initialize ( ) ;
GameState : : STATE = GameState : : states . at ( States : : State : : GAME_RUN ) ;
# pragma region Setup a fake test map and test monster
game - > MAP_DATA [ " CAMPAIGN_1_1 " ] ;
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 ( ) ;
}
TEST_METHOD_CLEANUP ( CleanupTests ) {
testGame - > EndGame ( ) ;
}
TEST_METHOD ( ItemGiveTest ) {
Inventory : : AddItem ( " Health Potion " s , 3 ) ;
Assert : : AreEqual ( 3U , Inventory : : GetItemCount ( " Health Potion " s ) , L " Player has 3 Health Potions. " ) ;
}
TEST_METHOD ( ItemRemoveTest ) {
Inventory : : AddItem ( " Health Potion " s , 3 ) ;
for ( std : : weak_ptr < Item > & item : Inventory : : GetItem ( " Health Potion " s ) ) Inventory : : RemoveItem ( item , 3 ) ;
Assert : : AreEqual ( 0U , Inventory : : GetItemCount ( " Health Potion " s ) , L " Player has no Health Potions. " ) ;
}
TEST_METHOD ( ItemQuantityStackTest ) {
Inventory : : AddItem ( " Health Potion " s , 3 ) ;
Inventory : : AddItem ( " Health Potion " s , 3 ) ;
Assert : : AreEqual ( 6U , Inventory : : GetItemCount ( " Health Potion " s ) , L " Player has 6 Health Potions. " ) ;
}
TEST_METHOD ( 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 + + ;
Assert : : AreEqual ( 3 , itemCounter , L " 3 separate item entries for Ring of the Slime King. " ) ;
}
TEST_METHOD ( UsingBlankLoadoutItemDoesNothing ) {
Assert : : IsFalse ( game - > UseLoadoutItem ( 0 ) , L " Using a blank loadout item slot (0) does not produce a result. " ) ;
Assert : : IsFalse ( game - > UseLoadoutItem ( 1 ) , L " Using a blank loadout item slot (1) does not produce a result. " ) ;
Assert : : IsFalse ( game - > UseLoadoutItem ( 2 ) , L " Using a blank loadout item slot (2) does not produce a result. " ) ;
}
TEST_METHOD ( UsingLoadoutItemOfQuantityZeroDoesNothing ) {
Assert : : ExpectException < std : : exception > ( [ ] ( ) { game - > SetLoadoutItem ( 0 , " Minor Health Potion " ) ; } , L " Applying an item that doesn't exist to a loadout slot should not be allowed. " ) ;
}
TEST_METHOD ( 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 - > useItem1 , testKeyboardInput ) ;
Assert : : AreEqual ( 1 , Inventory : : loadoutItemsUsed [ 0 ] . second , L " 1 Health potion considered used in loadout inventory. " ) ;
Assert : : AreEqual ( 4U , Inventory : : GetItemCount ( " Minor Health Potion " s ) , L " 4 Health potions remain in player's inventory. " ) ;
Assert : : AreEqual ( player - > useItem1 . GetCooldownTime ( ) , player - > useItem1 . cooldown , L " Item 1 is now on cooldown. " ) ;
}
TEST_METHOD ( 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.
Assert : : ExpectException < std : : exception > ( [ & ] ( ) { player - > CheckAndPerformAbility ( player - > useItem1 , testKeyboardInput ) ; } , L " If all buffs are properly applied, then some of these stat up buffs are illegal and will catch an exception. " ) ;
}
TEST_METHOD ( FlatRestoreScriptTest ) {
player - > Hurt ( 75 , player - > OnUpperLevel ( ) , player - > GetZ ( ) ) ;
player - > mana = 24 ;
Inventory : : AddItem ( " Flat Recovery Potion " s , 5U ) ;
game - > SetLoadoutItem ( 0 , " Flat Recovery Potion " ) ;
testKey - > bHeld = true ; //Simulate key being pressed.
player - > CheckAndPerformAbility ( player - > useItem1 , testKeyboardInput ) ;
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.
Assert : : AreEqual ( 75 , player - > GetHealth ( ) , L " Player Health is 75 after using Flat Recovery Potion. " ) ;
Assert : : AreEqual ( 75 , player - > GetMana ( ) , L " Player Mana is 75 after using Flat Recovery Potion. " ) ;
}
TEST_METHOD ( PctRestoreScriptTest ) {
player - > Hurt ( 75 , player - > OnUpperLevel ( ) , player - > GetZ ( ) ) ;
player - > mana = 24 ;
Inventory : : AddItem ( " Pct Recovery Potion " s , 5U ) ;
game - > SetLoadoutItem ( 1 , " Pct Recovery Potion " ) ;
testKey - > bHeld = true ; //Simulate key being pressed.
player - > CheckAndPerformAbility ( player - > useItem2 , testKeyboardInput ) ;
game - > SetElapsedTime ( 0.05f ) ;
game - > OnUserUpdate ( 0.05f ) ; //Wait some time as the item applies a buff that heals us.
Assert : : AreEqual ( 75 , player - > GetHealth ( ) , L " Player Health is 75 after using Pct Recovery Potion. " ) ;
Assert : : AreEqual ( 75 , player - > GetMana ( ) , L " Player Mana is 75 after using Pct Recovery Potion. " ) ;
}
TEST_METHOD ( 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 - > useItem3 , testKeyboardInput ) ;
game - > SetElapsedTime ( 0.05f ) ;
game - > OnUserUpdate ( 0.05f ) ; //Wait some time as the item applies a buff that heals us.
Assert : : AreEqual ( 30 , player - > GetHealth ( ) , L " Player is immediately healed for 5 health points on Bandages use. " ) ;
game - > SetElapsedTime ( 1.f ) ;
game - > OnUserUpdate ( 1.f ) ;
for ( int seconds { 1 } ; seconds < = 6 ; seconds + + ) {
Assert : : AreEqual ( 30 + seconds * 5 , player - > GetHealth ( ) , L " Player is healed again for 5 health points. " ) ;
game - > OnUserUpdate ( 1.f ) ;
}
Assert : : AreEqual ( 60 , player - > GetHealth ( ) , L " Player should not be healed now that the Bandages effect is over. " ) ;
}
TEST_METHOD ( 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 ) ;
Assert : : AreEqual ( 2U , Inventory : : GetItemCount ( " Ring of the Slime King " s ) , L " Disassembly has removed one of the Slime King rings from our inventory. " ) ;
Assert : : IsTrue ( disassembleRingTest . expired ( ) , L " Original reference to disassembled ring should now be invalid. " ) ;
Assert : : AreEqual ( 1U , Inventory : : GetItemCount ( ITEM_DATA [ " Ring of the Slime King " ] . FragmentName ( ) ) , L " Disassembly has given us a Slime King Ring Fragment. " ) ;
}
TEST_METHOD ( DisassembleNonAccessoryTest ) {
Inventory : : AddItem ( " Ring of the Slime King " s ) ;
try {
Inventory : : Disassemble ( Inventory : : AddItem ( " Test Armor " s ) ) ;
Assert : : Fail ( L " Disassembling Test Armor succeeded! This should NOT be allowed! " ) ;
} catch ( std : : runtime_error & e ) { }
try {
Inventory : : Disassemble ( Inventory : : AddItem ( " Green Slime Remains " s ) ) ;
Assert : : Fail ( L " Disassembling Green Slime Remains succeeded! This should NOT be allowed! " ) ;
} catch ( std : : runtime_error & e ) { }
try {
Inventory : : Disassemble ( Inventory : : AddItem ( " Health Potion " s ) ) ;
Assert : : Fail ( L " Disassembling a Health Potion succeeded! This should NOT be allowed! " ) ;
} catch ( std : : runtime_error & e ) { }
}
TEST_METHOD ( RefiningTest ) {
std : : weak_ptr < Item > slimeKingRing { Inventory : : AddItem ( " Ring of the Slime King " s ) } ;
Assert : : IsFalse ( slimeKingRing . lock ( ) - > CanBeRefined ( ) , L " Ring of the Slime King should not be allowed to be refined since we have no fragments. " ) ;
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 ) ;
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. " ) ;
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 ( ) ) {
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. " ) ;
}
}
TEST_METHOD ( EnchantTestCheck ) {
std : : weak_ptr < Item > slimeKingRing { Inventory : : AddItem ( " Ring of the Slime King " s ) } ;
Assert : : IsFalse ( slimeKingRing . lock ( ) - > HasEnchant ( ) ) ;
for ( int i : std : : ranges : : iota_view ( 0 , 1000 ) ) {
slimeKingRing . lock ( ) - > EnchantItem ( ItemEnchant : : RollRandomEnchant ( ) ) ;
Assert : : IsTrue ( slimeKingRing . lock ( ) - > HasEnchant ( ) ) ;
if ( ItemEnchantInfo : : ENCHANT_LIST . at ( slimeKingRing . lock ( ) - > GetEnchant ( ) . value ( ) . Name ( ) ) . GetClass ( ) . has_value ( ) ) Assert : : AreEqual ( 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.
}
testGame - > ChangePlayerClass ( WIZARD ) ;
player = testGame - > GetPlayer ( ) ; //The player pointer has been reassigned...
for ( int i : std : : ranges : : iota_view ( 0 , 1000 ) ) {
slimeKingRing . lock ( ) - > EnchantItem ( ItemEnchant : : RollRandomEnchant ( ) ) ;
Assert : : IsTrue ( slimeKingRing . lock ( ) - > HasEnchant ( ) ) ;
if ( ItemEnchantInfo : : ENCHANT_LIST . at ( slimeKingRing . lock ( ) - > GetEnchant ( ) . value ( ) . Name ( ) ) . GetClass ( ) . has_value ( ) ) Assert : : AreEqual ( 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.
}
}
} ;
}