Compare commits
517 Commits
master
...
removeExpo
@ -0,0 +1,2 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<module classpath="CMake" type="CPP_MODULE" version="4" /> |
@ -0,0 +1,126 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup Label="ProjectConfigurations"> |
||||
<ProjectConfiguration Include="Unit Testing|Win32"> |
||||
<Configuration>Unit Testing</Configuration> |
||||
<Platform>Win32</Platform> |
||||
</ProjectConfiguration> |
||||
<ProjectConfiguration Include="Unit Testing|x64"> |
||||
<Configuration>Unit Testing</Configuration> |
||||
<Platform>x64</Platform> |
||||
</ProjectConfiguration> |
||||
</ItemGroup> |
||||
<PropertyGroup Label="Globals"> |
||||
<VCProjectVersion>17.0</VCProjectVersion> |
||||
<ProjectGuid>{11969B7B-3D50-4825-9584-AF01D15B88E0}</ProjectGuid> |
||||
<Keyword>Win32Proj</Keyword> |
||||
<RootNamespace>AdventuresinLestoriaTests</RootNamespace> |
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> |
||||
<ProjectSubType>NativeUnitTestProject</ProjectSubType> |
||||
</PropertyGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|Win32'" Label="Configuration"> |
||||
<ConfigurationType>DynamicLibrary</ConfigurationType> |
||||
<UseDebugLibraries>true</UseDebugLibraries> |
||||
<PlatformToolset>v143</PlatformToolset> |
||||
<CharacterSet>Unicode</CharacterSet> |
||||
<UseOfMfc>false</UseOfMfc> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'" Label="Configuration"> |
||||
<ConfigurationType>DynamicLibrary</ConfigurationType> |
||||
<UseDebugLibraries>true</UseDebugLibraries> |
||||
<PlatformToolset>v143</PlatformToolset> |
||||
<CharacterSet>Unicode</CharacterSet> |
||||
<UseOfMfc>false</UseOfMfc> |
||||
</PropertyGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
||||
<ImportGroup Label="ExtensionSettings"> |
||||
</ImportGroup> |
||||
<ImportGroup Label="Shared"> |
||||
</ImportGroup> |
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|Win32'" Label="PropertySheets"> |
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||
</ImportGroup> |
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'" Label="PropertySheets"> |
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
||||
</ImportGroup> |
||||
<PropertyGroup Label="UserMacros" /> |
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'"> |
||||
<LinkIncremental>true</LinkIncremental> |
||||
<IncludePath>C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria;$(IncludePath)</IncludePath> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|Win32'"> |
||||
<LinkIncremental>true</LinkIncremental> |
||||
</PropertyGroup> |
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'"> |
||||
<ClCompile> |
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader> |
||||
<WarningLevel>Level3</WarningLevel> |
||||
<SDLCheck>true</SDLCheck> |
||||
<AdditionalIncludeDirectories>C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\OneDrive\Documents\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<UseFullPaths>true</UseFullPaths> |
||||
<PrecompiledHeaderFile> |
||||
</PrecompiledHeaderFile> |
||||
<LanguageStandard>stdcpp20</LanguageStandard> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Windows</SubSystem> |
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> |
||||
</Link> |
||||
<PreBuildEvent> |
||||
<Command>powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File ../unit-testing-prebuild.ps1</Command> |
||||
</PreBuildEvent> |
||||
</ItemDefinitionGroup> |
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|Win32'"> |
||||
<ClCompile> |
||||
<PrecompiledHeader>Use</PrecompiledHeader> |
||||
<WarningLevel>Level3</WarningLevel> |
||||
<SDLCheck>true</SDLCheck> |
||||
<AdditionalIncludeDirectories>$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
||||
<PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
<UseFullPaths>true</UseFullPaths> |
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> |
||||
</ClCompile> |
||||
<Link> |
||||
<SubSystem>Windows</SubSystem> |
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> |
||||
</Link> |
||||
</ItemDefinitionGroup> |
||||
<ItemGroup> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\achievement_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\activity_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\application_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\core.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\image_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\lobby_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\network_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\overlay_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\relationship_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\storage_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\store_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\user_manager.cpp" /> |
||||
<ClCompile Include="..\Adventures in Lestoria\discord-files\voice_manager.cpp" /> |
||||
<ClCompile Include="GeometryTests.cpp" /> |
||||
<ClCompile Include="ItemTests.cpp"> |
||||
<SubType> |
||||
</SubType> |
||||
</ClCompile> |
||||
<ClCompile Include="MonsterTests.cpp"> |
||||
<SubType> |
||||
</SubType> |
||||
</ClCompile> |
||||
<ClCompile Include="PlayerTests.cpp"> |
||||
<SubType> |
||||
</SubType> |
||||
</ClCompile> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Adventures in Lestoria\Adventures in Lestoria.vcxproj"> |
||||
<Project>{8e3067af-cfe7-4b11-bc6b-b867c32753d7}</Project> |
||||
</ProjectReference> |
||||
</ItemGroup> |
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
||||
<ImportGroup Label="ExtensionTargets"> |
||||
</ImportGroup> |
||||
</Project> |
@ -0,0 +1,55 @@ |
||||
#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 "CppUnitTest.h" |
||||
#include "olcUTIL_Geometry2D.h" |
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework; |
||||
using namespace olc::utils; |
||||
|
||||
namespace GeometryTests |
||||
{ |
||||
TEST_CLASS(GeometryTest) |
||||
{ |
||||
public: |
||||
|
||||
TEST_METHOD(CircleOverlapTest) |
||||
{ |
||||
Assert::IsTrue(geom2d::overlaps(geom2d::circle<float>{vf2d{},10},vf2d{5,5})); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,230 @@ |
||||
#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 "CppUnitTest.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "Tutorial.h" |
||||
#include <random> |
||||
#include <format> |
||||
#include "ItemDrop.h" |
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework; |
||||
using namespace olc::utils; |
||||
|
||||
INCLUDE_GFX |
||||
INCLUDE_ITEM_DATA |
||||
|
||||
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){ |
||||
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::drops.clear(); |
||||
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->pKeyboardState[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."); |
||||
} |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,460 @@ |
||||
#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 "CppUnitTest.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "config.h" |
||||
#include "ItemDrop.h" |
||||
#include "Tutorial.h" |
||||
#include "DamageNumber.h" |
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework; |
||||
using namespace olc::utils; |
||||
|
||||
INCLUDE_MONSTER_DATA |
||||
INCLUDE_game |
||||
INCLUDE_GFX |
||||
INCLUDE_DAMAGENUMBER_LIST |
||||
INCLUDE_MONSTER_LIST |
||||
|
||||
TEST_MODULE_INITIALIZE(AiLTestSuite) |
||||
{ |
||||
debugLogger.open("debug.log"); |
||||
} |
||||
|
||||
namespace MonsterTests |
||||
{ |
||||
TEST_CLASS(MonsterTest) |
||||
{ |
||||
public: |
||||
std::unique_ptr<AiL>testGame; |
||||
#pragma region Setup Functions |
||||
//Makes MONSTER_DATA["TestName"] available.
|
||||
void SetupTestMonster(){ |
||||
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 |
||||
game->MAP_DATA["CAMPAIGN_1_1"]; |
||||
game->MAP_DATA["CAMPAIGN_1_1"].ZoneData["UpperZone"]; |
||||
game->MAP_DATA["CAMPAIGN_1_1"].ZoneData["LowerZone"]; |
||||
game->currentLevel="CAMPAIGN_1_1"; |
||||
ItemDrop::drops.clear(); |
||||
#pragma endregion |
||||
|
||||
MonsterData testMonsterData{"TestName","Test Monster",30,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f}; |
||||
MONSTER_DATA["TestName"]=testMonsterData; |
||||
|
||||
Menu::themes.SetInitialized(); |
||||
GFX.SetInitialized(); |
||||
DAMAGENUMBER_LIST.clear(); |
||||
} |
||||
void SetupMockMap(){ |
||||
game->MAP_DATA["CAMPAIGN_1_1"]; |
||||
ItemDrop::drops.clear(); |
||||
} |
||||
#pragma endregion |
||||
|
||||
TEST_METHOD_INITIALIZE(MonsterInitialize){ |
||||
SetupTestMonster(); |
||||
SetupMockMap(); |
||||
} |
||||
~MonsterTest(){ |
||||
testGame.release(); |
||||
} |
||||
TEST_METHOD(DisplayNameCheck){ |
||||
Assert::AreEqual("Test Monster",MONSTER_DATA["TestName"].GetDisplayName().c_str()); |
||||
} |
||||
TEST_METHOD(InternalNameCheck){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
Assert::AreEqual("TestName",testMonster.GetName().c_str()); |
||||
} |
||||
TEST_METHOD(HealthCheck){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
Assert::AreEqual(testMonster.GetHealth(),testMonster.GetMaxHealth()); |
||||
Assert::AreEqual(testMonster.GetHealth(),30); |
||||
} |
||||
TEST_METHOD(Deal5Damage) |
||||
{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.Hurt(5,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
Assert::AreEqual(testMonster.GetHealth(),testMonster.GetMaxHealth()-5); |
||||
} |
||||
TEST_METHOD(IFrameShouldResultInNoDamage){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.ApplyIframes(0.5f); |
||||
testMonster.Hurt(5,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
Assert::AreEqual(testMonster.GetHealth(),testMonster.GetMaxHealth()); |
||||
} |
||||
TEST_METHOD(BeingInTheAirShouldAvoidAttacksFromTheGround){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.SetZ(2.f); |
||||
testMonster.Hurt(5,testMonster.OnUpperLevel(),0.f); |
||||
Assert::AreEqual(testMonster.GetHealth(),testMonster.GetMaxHealth()); |
||||
} |
||||
TEST_METHOD(MonstersDeal10Damage_NoDamageReduction) |
||||
{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
Monster testMonster2{{},MONSTER_DATA["TestName"]}; |
||||
game->GetPlayer()->Hurt(testMonster.GetAttack(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
testMonster2.Hurt(testMonster.GetAttack(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
Assert::AreEqual(game->GetPlayer()->GetMaxHealth()-10,game->GetPlayer()->GetHealth()); |
||||
Assert::AreEqual(testMonster2.GetMaxHealth()-10,testMonster2.GetHealth()); |
||||
} |
||||
TEST_METHOD(DoubleAttackPctModifierWorks){ |
||||
Monster buffMonster{{},MONSTER_DATA["TestName"]}; |
||||
buffMonster.AddBuff(BuffType::STAT_UP,5,100._Pct,{ItemAttribute::Get("Attack %")}); |
||||
|
||||
Monster testMonster2{{},MONSTER_DATA["TestName"]}; |
||||
game->GetPlayer()->Hurt(buffMonster.GetAttack(),buffMonster.OnUpperLevel(),buffMonster.GetZ()); |
||||
testMonster2.Hurt(buffMonster.GetAttack(),buffMonster.OnUpperLevel(),buffMonster.GetZ()); |
||||
Assert::AreEqual(game->GetPlayer()->GetMaxHealth()-20,game->GetPlayer()->GetHealth()); |
||||
Assert::AreEqual(testMonster2.GetMaxHealth()-20,testMonster2.GetHealth()); |
||||
} |
||||
TEST_METHOD(AttackUpModifierWorks){ |
||||
Monster buffMonster{{},MONSTER_DATA["TestName"]}; |
||||
buffMonster.AddBuff(BuffType::STAT_UP,5,5.f,{"Attack"}); |
||||
|
||||
Monster testMonster2{{},MONSTER_DATA["TestName"]}; |
||||
game->GetPlayer()->Hurt(buffMonster.GetAttack(),buffMonster.OnUpperLevel(),buffMonster.GetZ()); |
||||
testMonster2.Hurt(buffMonster.GetAttack(),buffMonster.OnUpperLevel(),buffMonster.GetZ()); |
||||
Assert::AreEqual(game->GetPlayer()->GetMaxHealth()-15,game->GetPlayer()->GetHealth(),L"Player Health is now 85."); |
||||
Assert::AreEqual(testMonster2.GetMaxHealth()-15,testMonster2.GetHealth(),L"Monster Health is now 15."); |
||||
} |
||||
TEST_METHOD(HealthUpModifierWorks){ |
||||
Monster buffMonster{{},MONSTER_DATA["TestName"]}; |
||||
buffMonster.AddBuff(BuffType::STAT_UP,5,5.f,{"Health"}); |
||||
|
||||
Assert::AreEqual(35,buffMonster.GetMaxHealth(),L"Monster Max Health is now 35."); |
||||
Assert::AreEqual(30,buffMonster.GetHealth(),L"Monster Current Health is still 30."); |
||||
} |
||||
TEST_METHOD(AttackUpPctModifierWorks){ |
||||
Monster buffMonster{{},MONSTER_DATA["TestName"]}; |
||||
buffMonster.AddBuff(BuffType::STAT_UP,5,100.0_Pct,{"Attack %"}); |
||||
|
||||
Monster testMonster2{{},MONSTER_DATA["TestName"]}; |
||||
game->GetPlayer()->Hurt(buffMonster.GetAttack(),buffMonster.OnUpperLevel(),buffMonster.GetZ()); |
||||
testMonster2.Hurt(buffMonster.GetAttack(),buffMonster.OnUpperLevel(),buffMonster.GetZ()); |
||||
Assert::AreEqual(game->GetPlayer()->GetMaxHealth()-20,game->GetPlayer()->GetHealth(),L"Player Health is now 80."); |
||||
Assert::AreEqual(testMonster2.GetMaxHealth()-20,testMonster2.GetHealth(),L"Monster Health is now 10."); |
||||
} |
||||
TEST_METHOD(MonsterIsConsideredDeadAt0Health){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.Hurt(testMonster.GetMaxHealth(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
Assert::AreEqual(true,testMonster.IsDead()); |
||||
} |
||||
TEST_METHOD(ItemDropSpawnsWhenMonsterIsKilled){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.Hurt(testMonster.GetMaxHealth(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
Assert::AreEqual(size_t(1),ItemDrop::GetDrops().size()); |
||||
} |
||||
TEST_METHOD(MoveSpdSetCorrectly){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
Assert::AreEqual(2.f,testMonster.GetMoveSpdMult()); |
||||
} |
||||
TEST_METHOD(SlowdownBuffTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::SLOWDOWN,5.f,0.5f); |
||||
Assert::AreEqual(1.f,testMonster.GetMoveSpdMult()); |
||||
} |
||||
TEST_METHOD(SelfInflictedSlowdownTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::SELF_INFLICTED_SLOWDOWN,5.f,0.5f); |
||||
Assert::AreEqual(1.f,testMonster.GetMoveSpdMult()); |
||||
} |
||||
TEST_METHOD(SpeedBoostTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::SPEEDBOOST,5.f,0.5f); |
||||
Assert::AreEqual(3.f,testMonster.GetMoveSpdMult()); |
||||
} |
||||
TEST_METHOD(LockOnSpeedBoostTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::LOCKON_SPEEDBOOST,5.f,0.5f); |
||||
Assert::AreEqual(3.f,testMonster.GetMoveSpdMult()); |
||||
} |
||||
TEST_METHOD(AdditiveMoveSpdBuffsTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::SLOWDOWN,5.f,0.5f); |
||||
testMonster.AddBuff(BuffType::SPEEDBOOST,5.f,0.75f); |
||||
Assert::AreEqual(2.5f,testMonster.GetMoveSpdMult()); |
||||
} |
||||
TEST_METHOD(DamageReductionBuffTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::DAMAGE_REDUCTION,5.f,0.25f); |
||||
|
||||
testMonster.Hurt(testMonster.GetAttack(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//25% damage reduction should result in 7.5 health taken, When ceiling'd results in 8 health taken.
|
||||
Assert::AreEqual(22,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(BarrierBuffTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::BARRIER_DAMAGE_REDUCTION,5.f,0.5f); |
||||
|
||||
testMonster.Hurt(testMonster.GetAttack(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//50% damage reduction should result in 5 health taken
|
||||
Assert::AreEqual(25,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(AdditiveDamageReductionTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::DAMAGE_REDUCTION,5.f,0.25f); |
||||
testMonster.AddBuff(BuffType::BARRIER_DAMAGE_REDUCTION,5.f,0.5f); |
||||
|
||||
testMonster.Hurt(testMonster.GetAttack(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//25+50% damage reduction should result in 2.5 health taken. When ceiling'd, results in 3 health taken.
|
||||
Assert::AreEqual(27,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(MaximumDamageReductionTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::DAMAGE_REDUCTION,5.f,INFINITY); |
||||
|
||||
testMonster.Hurt(testMonster.GetAttack(),testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//At 100% or more damage reduction, the monster should take 0 damage.
|
||||
Assert::AreEqual(30,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(TrueDamageTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
|
||||
testMonster._DealTrueDamage(testMonster.GetAttack()*3); |
||||
Assert::AreEqual(0,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(TrueDamageTestWith100PctDamageReduction){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::DAMAGE_REDUCTION,5.f,INFINITY); |
||||
|
||||
testMonster._DealTrueDamage(testMonster.GetAttack()*3); |
||||
//Damage reduction should not affect true damage at all.
|
||||
Assert::AreEqual(0,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(CriticalRateTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
|
||||
game->GetPlayer()->SetBaseStat(ItemAttribute::Get("Crit Rate"),100.f); |
||||
|
||||
testMonster.Hurt(5,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//If crit rate works 100% of the time, getting hurt should deal crit dmg % more damage (which defaults to 50%). Ceiling 7.5 damage to 8.
|
||||
Assert::AreEqual(22,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(CriticalDamageTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
|
||||
game->GetPlayer()->SetBaseStat(ItemAttribute::Get("Crit Rate"),100.f); |
||||
game->GetPlayer()->SetBaseStat(ItemAttribute::Get("Crit Dmg"),150.f); |
||||
|
||||
testMonster.Hurt(5,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//If crit rate works 100% of the time, getting hurt will deal 150% more damage. Ceiling 12.5 damage to 13.
|
||||
Assert::AreEqual(17,testMonster.GetHealth()); |
||||
} |
||||
TEST_METHOD(ShouldNotDieNormallyTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.diesNormally=false; |
||||
|
||||
testMonster.Hurt(1000,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
testMonster.Hurt(1000,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
testMonster.Hurt(1000,testMonster.OnUpperLevel(),testMonster.GetZ()); |
||||
//Health should continue to remain at 1 and the monster should remain alive if the dies normally flag is false.
|
||||
Assert::AreEqual(1,testMonster.GetHealth()); |
||||
Assert::IsTrue(testMonster.IsAlive()); |
||||
} |
||||
TEST_METHOD(IllegalDefenseStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Defense"}); |
||||
Assert::Fail(L"Adding a Defense buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalMoveSpdStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Move Spd %"}); |
||||
Assert::Fail(L"Adding a Move Spd % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalDefensePctStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Defense %"}); |
||||
Assert::Fail(L"Adding a Defense % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalCDRStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"CDR"}); |
||||
Assert::Fail(L"Adding a CDR buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalCritRateStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Crit Rate"}); |
||||
Assert::Fail(L"Adding a Crit Rate buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalCritDmgStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Crit Damage"}); |
||||
Assert::Fail(L"Adding a Crit Damage buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalHPRecoveryPctStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"HP Recovery %"}); |
||||
Assert::Fail(L"Adding a HP Recovery % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalHP6RecoveryPctStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"HP6 Recovery %"}); |
||||
Assert::Fail(L"Adding a HP6 Recovery % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalHP4RecoveryPctStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"HP4 Recovery %"}); |
||||
Assert::Fail(L"Adding a HP4 Recovery % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalDamageReductionStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Damage Reduction"}); |
||||
Assert::Fail(L"Adding a Damage Reduction buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalAttackSpdStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Attack Spd"}); |
||||
Assert::Fail(L"Adding a Attack Spd buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){}; |
||||
} |
||||
TEST_METHOD(IllegalManaStatUpBuffCheck){ |
||||
try{ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::STAT_UP,5.f,5.f,{"Mana"}); |
||||
Assert::Fail(L"Adding a Mana buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(DOTTest){ |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
testMonster.AddBuff(BuffType::DAMAGE_REDUCTION,10.f,100._Pct); |
||||
testMonster.Hurt(10,testMonster.OnUpperLevel(),testMonster.GetZ(),HurtFlag::DOT); |
||||
Assert::AreEqual(20,testMonster.GetHealth(),L"A DOT should go through all sources of damage reduction."); |
||||
testMonster.Update(1.f); //Let time pass so two DOT numbers don't stack up.
|
||||
testMonster.ApplyIframes(0.5f); |
||||
testMonster.Hurt(10,testMonster.OnUpperLevel(),testMonster.GetZ(),HurtFlag::DOT); |
||||
Assert::AreEqual(10,testMonster.GetHealth(),L"A DOT should go through all sources of iframes."); |
||||
Assert::AreEqual(2,std::accumulate(DAMAGENUMBER_LIST.begin(),DAMAGENUMBER_LIST.end(),0,[](int count,const std::shared_ptr<DamageNumber>&damageNumber){ |
||||
if(damageNumber->type==DamageNumberType::DOT)return count+1; |
||||
else return count; |
||||
}) |
||||
,L"There should be 2 damage numbers of type DOT."); |
||||
} |
||||
TEST_METHOD(TrapperMarkTest){ |
||||
MonsterData testMonsterData{"TestName","Test Monster",30,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f}; |
||||
game->SpawnMonster({},testMonsterData); |
||||
game->SetElapsedTime(0.1f); |
||||
game->OnUserUpdate(0.1f); //A monster that is spawned needs to be added to the monster list in the next tick.
|
||||
std::weak_ptr<Monster>testMonster{MONSTER_LIST.front()}; |
||||
|
||||
Assert::AreEqual(uint8_t(0),testMonster.lock()->GetMarkStacks(),L"Monster has 0 marks initially."); |
||||
|
||||
testMonster.lock()->ApplyMark(7.f,5U); |
||||
|
||||
game->SetElapsedTime(0.3f); |
||||
game->OnUserUpdate(0.3f); //A monster that had a mark applied needs to be added as a lock on target in the next tick.
|
||||
|
||||
Assert::AreEqual(uint8_t(5),testMonster.lock()->GetMarkStacks(),L"Monster has 5 marks after receiving a buff."); |
||||
|
||||
testMonster.lock()->Hurt(1,testMonster.lock()->OnUpperLevel(),testMonster.lock()->GetZ()); |
||||
|
||||
Assert::AreEqual(uint8_t(5),testMonster.lock()->GetMarkStacks(),L"Monster should still have 5 marks after taking damage from a normal attack."); |
||||
|
||||
testMonster.lock()->Hurt(1,testMonster.lock()->OnUpperLevel(),testMonster.lock()->GetZ(),HurtFlag::PLAYER_ABILITY); |
||||
|
||||
Assert::AreEqual(uint8_t(4),testMonster.lock()->GetMarkStacks(),L"Monster should have 4 marks remaining."); |
||||
Assert::AreEqual(22,testMonster.lock()->GetHealth(),L"Mark deals 60% of the player's attack. And 2 damage already taken from earlier."); |
||||
|
||||
testMonster.lock()->Hurt(1,testMonster.lock()->OnUpperLevel(),testMonster.lock()->GetZ(),HurtFlag::DOT); |
||||
|
||||
Assert::AreEqual(uint8_t(4),testMonster.lock()->GetMarkStacks(),L"Monster should still have 4 marks remaining (DOTs don't remove a mark)."); |
||||
|
||||
testMonster.lock()->_DealTrueDamage(1); |
||||
|
||||
Assert::AreEqual(uint8_t(4),testMonster.lock()->GetMarkStacks(),L"Monster should still have 4 marks remaining after taking true damage from something not marked as a player ability."); |
||||
|
||||
testMonster.lock()->Heal(testMonster.lock()->GetMaxHealth()); //Heal the monster so it doesn't die.
|
||||
|
||||
testMonster.lock()->_DealTrueDamage(1,HurtFlag::PLAYER_ABILITY); |
||||
|
||||
Assert::AreEqual(uint8_t(3),testMonster.lock()->GetMarkStacks(),L"Monster should have 3 marks remaining after taking true damage."); |
||||
|
||||
testMonster.lock()->_DealTrueDamage(10,HurtFlag::DOT|HurtFlag::PLAYER_ABILITY); |
||||
|
||||
Assert::AreEqual(uint8_t(2),testMonster.lock()->GetMarkStacks(),L"Monster should have 2 marks remaining after taking true damage, even though it's classified as a DOT. This is an edge case, but it's really meaningless here..."); |
||||
|
||||
testMonster.lock()->Heal(testMonster.lock()->GetMaxHealth()); |
||||
|
||||
testMonster.lock()->TriggerMark(); |
||||
|
||||
Assert::AreEqual(uint8_t(1),testMonster.lock()->GetMarkStacks(),L"Monster should have 1 mark remaining after using TriggerMark function"); |
||||
Assert::AreEqual(24,testMonster.lock()->GetHealth(),L"Monster should not take damage from the TriggerMark function (Mark deals 6 damage though)."); |
||||
|
||||
game->SetElapsedTime(10.f); |
||||
testMonster.lock()->Update(10.f); |
||||
|
||||
Assert::AreEqual(uint8_t(0),testMonster.lock()->GetMarkStacks(),L"The marks should have expired after 10 seconds."); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,609 @@ |
||||
#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 "CppUnitTest.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "Tutorial.h" |
||||
#include <random> |
||||
#include <format> |
||||
#include "ItemDrop.h" |
||||
#include "DamageNumber.h" |
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework; |
||||
using namespace olc::utils; |
||||
|
||||
INCLUDE_GFX |
||||
INCLUDE_ITEM_DATA |
||||
INCLUDE_DAMAGENUMBER_LIST |
||||
|
||||
extern std::mt19937 rng; |
||||
|
||||
namespace PlayerTests |
||||
{ |
||||
TEST_CLASS(PlayerTest) |
||||
{ |
||||
public: |
||||
std::unique_ptr<AiL>testGame; |
||||
InputGroup testKeyboardInput; |
||||
Player*player; |
||||
HWButton*testKey; |
||||
TEST_METHOD_INITIALIZE(PlayerInitialize){ |
||||
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::drops.clear(); |
||||
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->pKeyboardState[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(); |
||||
|
||||
DAMAGENUMBER_LIST.clear(); |
||||
} |
||||
TEST_METHOD_CLEANUP(CleanupTests){ |
||||
testGame->EndGame(); |
||||
} |
||||
TEST_METHOD(PlayerAlive){ |
||||
Assert::IsTrue(player->IsAlive()); |
||||
} |
||||
TEST_METHOD(PlayerTakesDamage){ |
||||
player->Hurt(30,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(70,player->GetHealth()); |
||||
} |
||||
TEST_METHOD(PlayerBlockingTakesNoDamage){ |
||||
player->SetState(State::BLOCK); |
||||
player->Hurt(30,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(100,player->GetHealth()); |
||||
} |
||||
TEST_METHOD(PlayerIframesPreventsDamage){ |
||||
player->ApplyIframes(0.5f); |
||||
player->Hurt(30,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(100,player->GetHealth()); |
||||
} |
||||
TEST_METHOD(PlayerConsumesManaOnRightClickAbilityUse){ |
||||
int abilityManaCost{player->GetRightClickAbility().manaCost}; |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); |
||||
} |
||||
TEST_METHOD(PlayerConsumesManaOnAbility1Use){ |
||||
int abilityManaCost{player->GetAbility1().manaCost}; |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility1(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); |
||||
} |
||||
TEST_METHOD(PlayerConsumesManaOnAbility2Use){ |
||||
int abilityManaCost{player->GetAbility2().manaCost}; |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); |
||||
} |
||||
TEST_METHOD(PlayerConsumesManaOnAbility3Use){ |
||||
int abilityManaCost{player->GetAbility3().manaCost}; |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); |
||||
} |
||||
TEST_METHOD(PlayerConsumesManaOnAbility4Use){ |
||||
int abilityManaCost{player->GetAbility4().manaCost}; |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility4(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana()-abilityManaCost,player->GetMana()); |
||||
} |
||||
TEST_METHOD(WarriorBlockActivatesBlock){ |
||||
player->GetRightClickAbility().action(player,player->GetPos()); |
||||
Assert::AreEqual(int(State::BLOCK),int(player->GetState())); |
||||
} |
||||
TEST_METHOD(WizardCastDoesNotConsumeManaImmediately){ |
||||
testGame->ChangePlayerClass(WIZARD); |
||||
player=testGame->GetPlayer(); //The player pointer has been reassigned...
|
||||
//Ability 3 is Meteor, which is a cast. Mana should not be consumed for a spell that begins as a cast.
|
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana(),player->GetMana()); |
||||
} |
||||
TEST_METHOD(RangerCastDoesNotConsumeManaImmediately){ |
||||
testGame->ChangePlayerClass(RANGER); |
||||
player=testGame->GetPlayer(); //The player pointer has been reassigned...
|
||||
//Ability 2 is Charged Shot, which is a cast. Mana should not be consumed for a spell that begins as a cast.
|
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); |
||||
Assert::AreEqual(player->GetMaxMana(),player->GetMana()); |
||||
} |
||||
TEST_METHOD(PlayerTakesDamageWithDamageReductionApplied){ |
||||
std::weak_ptr<Item>testArmor{Inventory::AddItem("Bone Armor"s)}; |
||||
testArmor.lock()->enhancementLevel="Item.Item Max Enhancement Level"_I; //Force enhance the item to the max enhancement level.
|
||||
Inventory::EquipItem(testArmor,EquipSlot::ARMOR); |
||||
|
||||
//If any of these values change, the armor piece may return a different value and these need to be updated for
|
||||
//BONE ARMOR +10!
|
||||
Assert::AreEqual(72.f,testArmor.lock()->GetStats().A_Read("Defense"),L"Defense increase is not 72"); |
||||
Assert::AreEqual(14.f,testArmor.lock()->GetStats().A_Read("Health"),L"Health increase is not 14"); |
||||
Assert::AreEqual(7.f,testArmor.lock()->GetStats().A_Read("Attack"),L"Attack increase is not 7"); |
||||
|
||||
player->Hurt(30,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(74,player->GetHealth()); //Even though we are supposed to take 30 damage, damage reduction reduces it to 26 instead. (Random variance is controlled during testing. If this number changes at some point, either the damage formula or the item's stats have changed!)
|
||||
} |
||||
TEST_METHOD(PlayerSetEffectsAcknowledged){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor"s)}; |
||||
setArmor.lock()->enhancementLevel="Item.Item Max Enhancement Level"_I; //Force enhance the item to the max enhancement level.
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
|
||||
//This gear is supposed to be provide an additional 100 defense, 1000 health, and 100 attack.
|
||||
Assert::AreEqual(100.f+player->GetBaseStat("Defense"),player->GetEquipStat("Defense"),L"Defense stat was not increased by 100."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Attack"),player->GetEquipStat("Attack"),L"Attack stat was not increased by 100."); |
||||
Assert::AreEqual(1000.f+player->GetBaseStat("Health"),player->GetEquipStat("Health"),L"Health Points was not increased by 1000."); |
||||
} |
||||
TEST_METHOD(PlayerSetEffectsAcknowledged2){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
setArmor.lock()->enhancementLevel="Item.Item Max Enhancement Level"_I; //Force enhance the item to the max enhancement level.
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
|
||||
//This gear is supposed to be provide an additional 100% defense, 100% Attack, 100% Move Spd, 100% CDR, 100% Crit Rate, 100% Crit Dmg, 100% Health %, 100% HP/6 Recovery
|
||||
Assert::AreEqual(100.f+player->GetBaseStat("Defense %"),player->GetEquipStat("Defense %"),L"Defense % stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Attack %"),player->GetEquipStat("Attack %"),L"Attack % stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Move Spd %"),player->GetEquipStat("Move Spd %"),L"Move Spd % stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("CDR"),player->GetEquipStat("CDR"),L"CDR stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Crit Rate % stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Crit Dmg"),player->GetEquipStat("Crit Dmg"),L"Critical Damage stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Health %"),player->GetEquipStat("Health %"),L"Health Points was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("HP6 Recovery %"),player->GetEquipStat("HP6 Recovery %"),L"HP/6s Recovery stat was not increased by 100%."); |
||||
} |
||||
TEST_METHOD(PlayerSetEffectsAcknowledged3){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)}; |
||||
setArmor.lock()->enhancementLevel="Item.Item Max Enhancement Level"_I; //Force enhance the item to the max enhancement level.
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
|
||||
//This gear is supposed to be provide an additional 100% HP/4 Recovery, 100% Damage Reduction
|
||||
Assert::AreEqual(100.f+player->GetBaseStat("HP4 Recovery %"),player->GetEquipStat("HP4 Recovery %"),L"HP/4s Recovery stat was not increased by 100%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Damage Reduction"),player->GetEquipStat("Damage Reduction"),L"Damage Reduction stat was not increased by 100%."); |
||||
} |
||||
TEST_METHOD(PlayerSetEffectsAcknowledged4){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor4"s)}; |
||||
setArmor.lock()->enhancementLevel="Item.Item Max Enhancement Level"_I; //Force enhance the item to the max enhancement level.
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
|
||||
//This gear is supposed to be provide an additional 100% HP/s Recovery, 50% Attack Spd, and 100 More Base Mana.
|
||||
Assert::AreEqual(100.f+player->GetBaseStat("HP Recovery %"),player->GetEquipStat("HP Recovery %"),L"HP Recovery % stat was not increased by 100%."); |
||||
Assert::AreEqual(50.f+player->GetBaseStat("Attack Spd"),player->GetEquipStat("Attack Spd"),L"Attack Speed stat was not increased by 50%."); |
||||
Assert::AreEqual(100.f+player->GetBaseStat("Mana"),player->GetEquipStat("Mana"),L"Mana stat was not increased by 100."); |
||||
} |
||||
TEST_METHOD(MultiPieceSetEffectsWork){ |
||||
std::weak_ptr<Item>testArmor{Inventory::AddItem("Bone Armor"s)}; |
||||
std::weak_ptr<Item>testHeadpiece{Inventory::AddItem("Bone Helmet"s)}; |
||||
std::weak_ptr<Item>testLeggings{Inventory::AddItem("Bone Pants"s)}; |
||||
std::weak_ptr<Item>testGloves{Inventory::AddItem("Bone Gloves"s)}; |
||||
|
||||
Assert::AreEqual(player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Base Crit Rate does not match initial Crit Rate"); |
||||
Assert::AreEqual(player->GetBaseStat("Crit Dmg"),player->GetEquipStat("Crit Dmg"),L"Base Crit Dmg does not match initial Crit Dmg"); |
||||
|
||||
Inventory::EquipItem(testArmor,EquipSlot::ARMOR); |
||||
Inventory::EquipItem(testHeadpiece,EquipSlot::HELMET); |
||||
|
||||
Assert::AreEqual(5+player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Crit Rate does not increase by 5% with 2 Bone Equips (Set Bonus)"); |
||||
Assert::AreEqual(player->GetBaseStat("Crit Dmg"),player->GetEquipStat("Crit Dmg"),L"Crit Dmg does not remain the same with 2 Bone Equips (Set Bonus)"); |
||||
|
||||
Inventory::UnequipItem(EquipSlot::ARMOR); |
||||
Assert::AreEqual(player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Removing an armor piece does not remove the crit rate set effect"); |
||||
Assert::AreEqual(player->GetBaseStat("Crit Dmg"),player->GetEquipStat("Crit Dmg"),L"Removing an armor piece affects crit dmg incorrectly"); |
||||
|
||||
Inventory::EquipItem(testArmor,EquipSlot::ARMOR); |
||||
Inventory::EquipItem(testLeggings,EquipSlot::PANTS); |
||||
Inventory::EquipItem(testGloves,EquipSlot::GLOVES); |
||||
Assert::AreEqual(5+player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Crit Rate does not increase by 5% with 4 Bone Equips (Set Bonus)"); |
||||
Assert::AreEqual(7+player->GetBaseStat("Crit Dmg"),player->GetEquipStat("Crit Dmg"),L"Crit Dmg does not increase by 5% with 4 Bone Equips (Set Bonus)"); |
||||
} |
||||
TEST_METHOD(AccessoriesWork){ |
||||
std::weak_ptr<Item>testRing{Inventory::AddItem("Bird's Treasure"s)}; |
||||
std::weak_ptr<Item>testRing2{Inventory::AddItem("Bird's Treasure"s)}; |
||||
|
||||
Assert::AreEqual(3.f,testRing.lock()->RandomStats().A_Read("Move Spd %"),L"Generated Ring 1 Move Spd % is incorrect."); |
||||
Assert::AreEqual(2.f,testRing.lock()->RandomStats().A_Read("Crit Rate"),L"Generated Ring 1 Crit Rate is incorrect."); |
||||
Assert::AreEqual(5.f,testRing.lock()->RandomStats().A_Read("Mana"),L"Generated Ring 1 Mana is incorrect."); |
||||
|
||||
Assert::AreEqual(2.f,testRing2.lock()->RandomStats().A_Read("Move Spd %"),L"Generated Ring 2 Move Spd % is incorrect."); |
||||
Assert::AreEqual(2.f,testRing2.lock()->RandomStats().A_Read("Crit Rate"),L"Generated Ring 2 Crit Rate is incorrect."); |
||||
Assert::AreEqual(2.f,testRing2.lock()->RandomStats().A_Read("Mana"),L"Generated Ring 2 Mana is incorrect."); |
||||
|
||||
Assert::IsTrue(testRing.lock()->RandomStats().A_Read("Move Spd %")>=ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Move Spd %")&& |
||||
testRing.lock()->RandomStats().A_Read("Move Spd %")<=ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Move Spd %"),std::format(L"Generated Move Spd % {} is out of bounds: Min:{} Max:{}",testRing.lock()->RandomStats().A_Read("Move Spd %"),ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Move Spd %"),ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Move Spd %")).c_str()); |
||||
Assert::IsTrue(testRing.lock()->RandomStats().A_Read("Crit Rate")>=ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Crit Rate")&& |
||||
testRing.lock()->RandomStats().A_Read("Crit Rate")<=ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Crit Rate"),std::format(L"Generated Crit Rate {} is out of bounds: Min:{} Max:{}",testRing.lock()->RandomStats().A_Read("Crit Rate"),ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Move Spd %"),ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Crit Rate")).c_str()); |
||||
Assert::IsTrue(testRing.lock()->RandomStats().A_Read("Mana")>=ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Mana")&& |
||||
testRing.lock()->RandomStats().A_Read("Mana")<=ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Mana"),std::format(L"Generated Mana {} is out of bounds: Min:{} Max:{}",testRing.lock()->RandomStats().A_Read("Mana"),ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Mana"),ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Mana")).c_str()); |
||||
|
||||
Assert::IsTrue(testRing2.lock()->RandomStats().A_Read("Move Spd %")>=ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Move Spd %")&& |
||||
testRing2.lock()->RandomStats().A_Read("Move Spd %")<=ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Move Spd %"),std::format(L"Generated Move Spd % {} is out of bounds: Min:{} Max:{}",testRing2.lock()->RandomStats().A_Read("Move Spd %"),ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Move Spd %"),ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Move Spd %")).c_str()); |
||||
Assert::IsTrue(testRing2.lock()->RandomStats().A_Read("Crit Rate")>=ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Crit Rate")&& |
||||
testRing2.lock()->RandomStats().A_Read("Crit Rate")<=ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Crit Rate"),std::format(L"Generated Crit Rate {} is out of bounds: Min:{} Max:{}",testRing2.lock()->RandomStats().A_Read("Crit Rate"),ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Move Spd %"),ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Crit Rate")).c_str()); |
||||
Assert::IsTrue(testRing2.lock()->RandomStats().A_Read("Mana")>=ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Mana")&& |
||||
testRing2.lock()->RandomStats().A_Read("Mana")<=ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Mana"),std::format(L"Generated Mana {} is out of bounds: Min:{} Max:{}",testRing2.lock()->RandomStats().A_Read("Mana"),ITEM_DATA.at("Bird's Treasure"s).GetMinStats().A_Read("Mana"),ITEM_DATA.at("Bird's Treasure"s).GetMaxStats().A_Read("Mana")).c_str()); |
||||
|
||||
//Ensure stats are default.
|
||||
Assert::AreEqual(player->GetBaseStat("Move Spd %"),player->GetEquipStat("Move Spd %"),L"Base Move Spd %does not match initial Move Spd %"); |
||||
Assert::AreEqual(player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Base Crit Rate does not match initial Crit Rate"); |
||||
Assert::AreEqual(player->GetBaseStat("Mana"),player->GetEquipStat("Mana"),L"Base Mana does not match initial Mana"); |
||||
|
||||
Inventory::EquipItem(testRing,EquipSlot::RING1); |
||||
Assert::AreEqual(3+player->GetBaseStat("Move Spd %"),player->GetEquipStat("Move Spd %"),L"Move Spd % increased incorrectly when ring 1 equipped!"); |
||||
Assert::AreEqual(2+player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Crit Rate increased incorrectly when ring 1 equipped!"); |
||||
Assert::AreEqual(5+player->GetBaseStat("Mana"),player->GetEquipStat("Mana"),L"Mana increased incorrectly when ring 1 equipped!"); |
||||
|
||||
//Equipping to same slot should undo the previous stats and apply the new ring's stats.
|
||||
Inventory::EquipItem(testRing2,EquipSlot::RING1); |
||||
Assert::AreEqual(2+player->GetBaseStat("Move Spd %"),player->GetEquipStat("Move Spd %"),L"Move Spd % increased incorrectly when ring 1 equipped!"); |
||||
Assert::AreEqual(2+player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Crit Rate increased incorrectly when ring 1 equipped!"); |
||||
Assert::AreEqual(2+player->GetBaseStat("Mana"),player->GetEquipStat("Mana"),L"Mana increased incorrectly when ring 1 equipped!"); |
||||
|
||||
Inventory::UnequipItem(EquipSlot::RING1); |
||||
//Ensure stats are default again.
|
||||
Assert::AreEqual(player->GetBaseStat("Move Spd %"),player->GetEquipStat("Move Spd %"),L"Base Move Spd %does not match initial Move Spd % after unequipping the ring."); |
||||
Assert::AreEqual(player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Base Crit Rate does not match initial Crit Rate after unequipping the ring."); |
||||
Assert::AreEqual(player->GetBaseStat("Mana"),player->GetEquipStat("Mana"),L"Base Mana does not match initial Mana after unequipping the ring."); |
||||
|
||||
Inventory::EquipItem(testRing,EquipSlot::RING1); |
||||
Inventory::EquipItem(testRing2,EquipSlot::RING2); |
||||
Assert::AreEqual(5+player->GetBaseStat("Move Spd %"),player->GetEquipStat("Move Spd %"),L"Move Spd % increased incorrectly when both rings equipped!"); |
||||
Assert::AreEqual(4+player->GetBaseStat("Crit Rate"),player->GetEquipStat("Crit Rate"),L"Crit Rate increased incorrectly when both rings equipped!"); |
||||
Assert::AreEqual(7+player->GetBaseStat("Mana"),player->GetEquipStat("Mana"),L"Mana increased incorrectly when both rings equipped!"); |
||||
} |
||||
TEST_METHOD(FlatDefenseStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor"s)}; |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
player->Hurt(100,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(17,player->GetHealth(),L"100 Defense prevents 17 damage, resulting in 83 damage points dealt."); |
||||
} |
||||
TEST_METHOD(HealthStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor"s)}; |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100."); |
||||
} |
||||
TEST_METHOD(HealthStatUpBuffCheck){ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.f,{"Health"}); |
||||
Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100."); |
||||
} |
||||
TEST_METHOD(HealthPctStatUpBuffCheck){ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Health %"}); |
||||
Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100."); |
||||
} |
||||
TEST_METHOD(IllegalCritRateStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Crit Rate"}); |
||||
Assert::Fail(L"Adding a Crit Rate buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalCritDmgStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Crit Damage"}); |
||||
Assert::Fail(L"Adding a Crit Damage buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalHPRecoveryPctStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"HP Recovery %"}); |
||||
Assert::Fail(L"Adding a HP Recovery % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalHP6RecoveryPctStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"HP6 Recovery %"}); |
||||
Assert::Fail(L"Adding a HP6 Recovery % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalHP4RecoveryPctStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"HP4 Recovery %"}); |
||||
Assert::Fail(L"Adding a HP4 Recovery % buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalDamageReductionStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Damage Reduction"}); |
||||
Assert::Fail(L"Adding a Damage Reduction buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(IllegalAttackSpdStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Attack Spd"}); |
||||
Assert::Fail(L"Adding a Attack Spd buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){}; |
||||
} |
||||
TEST_METHOD(IllegalManaStatUpBuffCheck){ |
||||
try{ |
||||
player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Mana"}); |
||||
Assert::Fail(L"Adding a Mana buff succeeded! This should NOT be allowed!"); |
||||
}catch(std::exception&e){} |
||||
} |
||||
TEST_METHOD(AttackStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor"s)}; |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100+player->GetBaseStat("Attack"),float(player->GetAttack()),L"Attack stat should be increased from 10 to 110."); |
||||
} |
||||
TEST_METHOD(DefensePctStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(player->GetBaseStat("Defense"),float(player->GetDefense()),L"100% Defense % causes a 0 increase in Defense when it's default zero."); |
||||
|
||||
std::weak_ptr<Item>bonePants{Inventory::AddItem("Bone Pants"s)}; |
||||
bonePants.lock()->enhancementLevel="Item.Item Max Enhancement Level"_I; //Force enhance the item to the max enhancement level.
|
||||
Inventory::EquipItem(bonePants,EquipSlot::PANTS); |
||||
Assert::AreEqual(122.f+player->GetBaseStat("Defense"),float(player->GetDefense()),L"100% Defense % causes double increase in Defense. 61 -> 122"); |
||||
|
||||
Inventory::UnequipItem(EquipSlot::ARMOR); |
||||
player->Hurt(100,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(13,player->GetHealth(),L"Took 87 damage with 61 defense."); |
||||
player->Heal(87); //Back to 100 Health.
|
||||
LOG(std::format("Player Health is now {}",player->GetHealth())); |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
player->Hurt(100,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(36,player->GetHealth(),L"Took 54 damage with 122 defense."); |
||||
} |
||||
TEST_METHOD(AttackPctStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
Monster testMonster{{},MONSTER_DATA["TestName"]}; |
||||
|
||||
testMonster.Hurt(player->GetAttack(),player->OnUpperLevel(),player->GetZ()); |
||||
const int damageDealt{testMonster.GetMaxHealth()-testMonster.GetHealth()}; |
||||
|
||||
testMonster.Heal(testMonster.GetMaxHealth()); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(player->GetBaseStat("Attack")*2,float(player->GetAttack()),L"100% Attack should double the attack of the player."); |
||||
|
||||
testMonster.Hurt(player->GetAttack(),player->OnUpperLevel(),player->GetZ()); |
||||
const int enhancedDamageDealt{testMonster.GetMaxHealth()-testMonster.GetHealth()}; |
||||
Assert::AreEqual(enhancedDamageDealt,damageDealt*5,L"Original damage dealt should be half of the new attack. Factor in 100% crit rate and a bonus 100% crit dmg (making it 250%) 200%*2.5=500% damage."); |
||||
} |
||||
TEST_METHOD(MoveSpdPctStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
|
||||
Assert::AreEqual(1.f,player->GetMoveSpdMult(),L"Move Spd is 100%"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(2.f,player->GetMoveSpdMult(),L"100% Move Spd should double the move speed from 100% to 200%"); |
||||
} |
||||
TEST_METHOD(CDRStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
|
||||
Assert::AreEqual(0.f,player->GetCooldownReductionPct(),L"CDR is 0%"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(1.f,player->GetCooldownReductionPct(),L"Player should have 100% CDR"); |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); |
||||
player->CheckAndPerformAbility(player->GetAbility1(),testKeyboardInput); |
||||
player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput); |
||||
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); |
||||
player->CheckAndPerformAbility(player->GetAbility4(),testKeyboardInput); |
||||
game->SetElapsedTime(0.01f); //Force elapsed time value.
|
||||
game->OnUserUpdate(0.01f); //Let 0.01 seconds go by. All abilities should be off cooldown.
|
||||
Assert::AreEqual(0.f,player->GetRightClickAbility().cooldown,L"Using an ability with 100% CDR should result in a 0 second cooldown."); |
||||
Assert::AreEqual(0.f,player->GetAbility1().cooldown,L"Using an ability with 100% CDR should result in a 0 second cooldown."); |
||||
Assert::AreEqual(0.f,player->GetAbility2().cooldown,L"Using an ability with 100% CDR should result in a 0 second cooldown."); |
||||
Assert::AreEqual(0.f,player->GetAbility3().cooldown,L"Using an ability with 100% CDR should result in a 0 second cooldown."); |
||||
Assert::AreEqual(0.f,player->GetAbility4().cooldown,L"Using an ability with 100% CDR should result in a 0 second cooldown."); |
||||
} |
||||
TEST_METHOD(CritRateStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
|
||||
Assert::AreEqual(0.f,player->GetCritRatePct(),L"Crit Rate is 0%"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(1.f,player->GetCritRatePct(),L"Player should have 100% Crit Rate"); |
||||
} |
||||
TEST_METHOD(CritDmgStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
|
||||
Assert::AreEqual(0.5f,player->GetCritDmgPct(),L"Crit Damage is 50%"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(1.5f,player->GetCritDmgPct(),L"Player should have 150% Crit Damage"); |
||||
} |
||||
TEST_METHOD(HealthPctStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
|
||||
Assert::AreEqual(100,player->GetHealth(),L"Current Health is 100"); |
||||
Assert::AreEqual(100,player->GetMaxHealth(),L"Max Health is 100"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100,player->GetHealth(),L"Current Health is still 100"); |
||||
Assert::AreEqual(200,player->GetMaxHealth(),L"Max Health is now 200"); |
||||
} |
||||
TEST_METHOD(HP6RecoveryStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
|
||||
Assert::AreEqual(0.0_Pct,player->GetHP6RecoveryPct(),L"HP6 Recovery % is 0"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetHP6RecoveryPct(),L"HP6 Recovery % is 100%"); |
||||
} |
||||
TEST_METHOD(HP4RecoveryStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)}; |
||||
|
||||
Assert::AreEqual(0.0_Pct,player->GetHP4RecoveryPct(),L"HP4 Recovery % is 0"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetHP4RecoveryPct(),L"HP4 Recovery % is 100%"); |
||||
} |
||||
TEST_METHOD(DamageReductionStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)}; |
||||
Assert::AreEqual(0.0_Pct,player->GetDamageReductionPct(),L"Damage Reduction is 0%"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetDamageReductionPct(),L"Damage Reduction is 100%"); |
||||
} |
||||
TEST_METHOD(HPRecoveryStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor4"s)}; |
||||
|
||||
Assert::AreEqual(0.0_Pct,player->GetHPRecoveryPct(),L"HP Recovery % is 0"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetHPRecoveryPct(),L"HP Recovery % is 100%"); |
||||
} |
||||
TEST_METHOD(AttackSpeedStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor4"s)}; |
||||
|
||||
Assert::AreEqual(0.f,player->GetAttackRecoveryRateReduction(),L"Attack Speed Reduction is 0 seconds"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(50.f,player->GetAttackRecoveryRateReduction(),L"Attack Speed Reduction is 50 seconds"); |
||||
} |
||||
TEST_METHOD(ManaStatEquipCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor4"s)}; |
||||
|
||||
Assert::AreEqual(100,player->GetMana(),L"Mana is 100"); |
||||
Assert::AreEqual(100,player->GetMaxMana(),L"Max Mana is 100"); |
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100,player->GetMana(),L"Mana is still 100"); |
||||
Assert::AreEqual(200,player->GetMaxMana(),L"Max Mana is now 200"); |
||||
} |
||||
TEST_METHOD(HP6RecoveryTest){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)}; |
||||
game->SetElapsedTime(0.1f); |
||||
game->OnUserUpdate(0.1f); //Advance the game by 0.1s so that all healing ticks occur at least once.
|
||||
//Hurt the player for most of their health.
|
||||
player->Hurt(player->GetMaxHealth()-1,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(1,player->GetHealth(),L"Player is at 1 Health."); |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetHP6RecoveryPct(),L"HP6 Recovery % is 100%"); |
||||
|
||||
game->SetElapsedTime(3.f); |
||||
game->OnUserUpdate(3.f); //Wait 3 seconds. The player shouldn't be healed yet.
|
||||
Assert::AreEqual(1,player->GetHealth(),L"After waiting for 3 seconds, the player should still be at 1 health."); |
||||
game->SetElapsedTime(3.f); |
||||
game->OnUserUpdate(3.f); //Wait 3 more seconds. The player should be healed now.
|
||||
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"After waiting for 6 seconds, the player should be at full health."); |
||||
} |
||||
TEST_METHOD(HP4RecoveryTest){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)}; |
||||
game->SetElapsedTime(0.1f); |
||||
game->OnUserUpdate(0.1f); //Advance the game by 0.1s so that all healing ticks occur at least once.
|
||||
//Hurt the player for most of their health.
|
||||
player->Hurt(player->GetMaxHealth()-1,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(1,player->GetHealth(),L"Player is at 1 Health."); |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetHP4RecoveryPct(),L"HP4 Recovery % is 100%"); |
||||
|
||||
game->SetElapsedTime(2.f); |
||||
game->OnUserUpdate(2.f); //Wait 2 seconds. The player shouldn't be healed yet.
|
||||
Assert::AreEqual(1,player->GetHealth(),L"After waiting for 2 seconds, the player should still be at 1 health."); |
||||
game->SetElapsedTime(2.f); |
||||
game->OnUserUpdate(2.f); //Wait 2 more seconds. The player should be healed now.
|
||||
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"After waiting for 4 seconds, the player should be at full health."); |
||||
} |
||||
TEST_METHOD(HPRecoveryTest){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor4"s)}; |
||||
game->SetElapsedTime(0.1f); |
||||
game->OnUserUpdate(0.1f); //Advance the game by 0.1s so that all healing ticks occur at least once.
|
||||
//Hurt the player for most of their health.
|
||||
player->Hurt(player->GetMaxHealth()-1,player->OnUpperLevel(),player->GetZ()); |
||||
Assert::AreEqual(1,player->GetHealth(),L"Player is at 1 Health."); |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); |
||||
Assert::AreEqual(100.0_Pct,player->GetHPRecoveryPct(),L"HP Recovery % is 100%"); |
||||
|
||||
game->SetElapsedTime(0.5f); |
||||
game->OnUserUpdate(0.5f); //Wait 0.5 seconds. The player shouldn't be healed yet.
|
||||
Assert::AreEqual(1,player->GetHealth(),L"After waiting for 0.5 seconds, the player should still be at 1 health."); |
||||
game->SetElapsedTime(0.5f); |
||||
game->OnUserUpdate(0.5f); //Wait 0.5 more seconds. The player should be healed now.
|
||||
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"After waiting for 1 second, the player should be at full health."); |
||||
} |
||||
TEST_METHOD(AdrenalineRushSkillBuffTest){ |
||||
testGame->ChangePlayerClass(THIEF); |
||||
player=testGame->GetPlayer(); //The player pointer has been reassigned...
|
||||
Assert::AreEqual(1.f,player->GetMoveSpdMult(),L"Move Speed Multiplier is set to x1.0 by default."); |
||||
Assert::AreEqual(0.f,player->GetAttackRecoveryRateReduction(),L"Attack rate cooldown starts with being reduced by 0 seconds."); |
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); |
||||
Assert::IsTrue(player->GetBuffs(BuffType::ADRENALINE_RUSH).size()>0,L"After using Adrenaline Rush, player has the Adrenaline Rush buff."); |
||||
Assert::AreEqual(1.1f,player->GetMoveSpdMult(),L"Move Speed Multiplier increased by 10% to x1.1"); |
||||
Assert::AreEqual(0.105f,player->GetAttackRecoveryRateReduction(),L"Attack Recovery Rate reduced by 30%, or 0.105s"); |
||||
} |
||||
TEST_METHOD(TrueDamageCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)}; |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); //Equip an item with 100% damage reduction.
|
||||
player->Hurt(20,player->OnUpperLevel(),player->GetZ(),TrueDamageFlag::IGNORE_DAMAGE_RULES); |
||||
Assert::AreEqual(80,player->GetHealth(),L"Player should take 20 damage through true damage."); |
||||
player->ApplyIframes(0.5f); |
||||
player->Hurt(20,player->OnUpperLevel(),player->GetZ(),TrueDamageFlag::IGNORE_DAMAGE_RULES); |
||||
Assert::AreEqual(60,player->GetHealth(),L"Player should take 20 damage through true damage even when iframes are on."); |
||||
} |
||||
TEST_METHOD(DOTDamageCheck){ |
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)}; |
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR); //Equip an item with 100% damage reduction.
|
||||
player->Hurt(20,player->OnUpperLevel(),player->GetZ(),HurtFlag::DOT); |
||||
Assert::AreEqual(80,player->GetHealth(),L"Player should take 20 damage through a DOT."); |
||||
Assert::AreEqual(1,std::accumulate(DAMAGENUMBER_LIST.begin(),DAMAGENUMBER_LIST.end(),0,[](int count,const std::shared_ptr<DamageNumber>&damageNumber){ |
||||
if(damageNumber->type==DamageNumberType::DOT)return count+1; |
||||
else return count; |
||||
}) |
||||
,L"There should be a damage number of type DOT."); |
||||
player->Update(1.0f);//Let some time pass so two DOT numbers don't stack up.
|
||||
player->ApplyIframes(0.5f); |
||||
player->Hurt(20,player->OnUpperLevel(),player->GetZ(),HurtFlag::DOT); |
||||
Assert::AreEqual(80,player->GetHealth(),L"Player should take 20 damage through a DOT even when iframes are on."); //HP Recovery/4s set effect has restored the health of the player to 100. So it should go back down to 80.
|
||||
Assert::AreEqual(2,std::accumulate(DAMAGENUMBER_LIST.begin(),DAMAGENUMBER_LIST.end(),0,[](int count,const std::shared_ptr<DamageNumber>&damageNumber){ |
||||
if(damageNumber->type==DamageNumberType::DOT)return count+1; |
||||
else return count; |
||||
}) |
||||
,L"There should be 2 damage numbers of type DOT."); |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,93 @@ |
||||
#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 "RowItemDisplay.h" |
||||
#include "SoundEffect.h" |
||||
#include "MenuIconButton.h" |
||||
|
||||
INCLUDE_game |
||||
INCLUDE_GFX |
||||
|
||||
class AccessoryRowItemDisplay:public RowItemDisplay{ |
||||
std::weak_ptr<MenuIconButton>lockButton; |
||||
public: |
||||
inline AccessoryRowItemDisplay(geom2d::rect<float>rect,const std::weak_ptr<Item>itemRef,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,ButtonAttr attributes=ButtonAttr::NONE) |
||||
:RowItemDisplay(rect,itemRef,onClick,itemNameLabelName,itemDescriptionLabelName,attributes){} |
||||
inline ~AccessoryRowItemDisplay(){ |
||||
if(!lockButton.expired()){ |
||||
size_t subcomponentCount=parentComponent.lock()->GetSubcomponentCount(); |
||||
parentComponent.lock()->RemoveButton(lockButton); |
||||
if(parentComponent.lock()->GetSubcomponentCount()>=subcomponentCount)ERR(std::format("WARNING! The subcomponent count went from {} -> {} when trying to remove the lock button for item {}! THIS SHOULD NOT BE HAPPENING!",subcomponentCount,parentComponent.lock()->GetSubcomponentCount(),name)); |
||||
lockButton.reset(); |
||||
} |
||||
} |
||||
inline void AfterCreate()override final{ |
||||
lockButton=parentComponent.lock()->ADDSUB(std::format("{}_lock",name),MenuIconButton)(geom2d::rect<float>{rect.pos+vf2d{rect.size.x-10.f,rect.size.y-10.f},vf2d{10.f,10.f}},GFX["unlock.png"].Decal(),[](MenuFuncData data){ |
||||
std::weak_ptr<AccessoryRowItemDisplay>accessoryParent=DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.component.lock()->GetSubcomponentParent()); |
||||
accessoryParent.lock()->SetLocked(!accessoryParent.lock()->IsLocked()); |
||||
if(accessoryParent.lock()->IsLocked()){ |
||||
SoundEffect::PlaySFX("Lock Accessory",SoundEffect::CENTERED); |
||||
}else{ |
||||
SoundEffect::PlaySFX("Unlock Accessory",SoundEffect::CENTERED); |
||||
} |
||||
std::weak_ptr<MenuIconButton>lockButton=DYNAMIC_POINTER_CAST<MenuIconButton>(data.component.lock()); |
||||
return true; |
||||
})END; |
||||
} |
||||
inline void Update(AiL*game)override final{ |
||||
MenuComponent::Update(game); |
||||
if(itemRef.lock()->IsLocked()){ |
||||
lockButton.lock()->SetIcon(GFX["lock.png"].Decal()); |
||||
}else{ |
||||
lockButton.lock()->SetIcon(GFX["unlock.png"].Decal()); |
||||
} |
||||
} |
||||
inline const bool IsLocked()const{ |
||||
return itemRef.lock()->IsLocked(); |
||||
} |
||||
inline void SetLocked(const bool locked){ |
||||
if(locked){ |
||||
itemRef.lock()->Lock(); |
||||
}else{ |
||||
itemRef.lock()->Unlock(); |
||||
} |
||||
} |
||||
inline const std::weak_ptr<MenuIconButton>GetLockButton()const{ |
||||
return lockButton; |
||||
} |
||||
}; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,65 @@ |
||||
#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_game |
||||
|
||||
void Menu::InitializeArtificerDisassembleConfirmWindow(){ |
||||
Menu*artificerDisassembleConfirmWindow=CreateMenu(ARTIFICER_DISASSEMBLE_CONFIRM,CENTERED,vi2d{144,144}); |
||||
|
||||
artificerDisassembleConfirmWindow->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="",}}, |
||||
}); |
||||
} |
@ -0,0 +1,65 @@ |
||||
#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_game |
||||
|
||||
void Menu::InitializeArtificerDisassembleWindow(){ |
||||
Menu*artificerDisassembleWindow=CreateMenu(ARTIFICER_DISASSEMBLE,CENTERED,vi2d{144,144}); |
||||
|
||||
artificerDisassembleWindow->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="",}}, |
||||
}); |
||||
} |
@ -0,0 +1,65 @@ |
||||
#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_game |
||||
|
||||
void Menu::InitializeArtificerEnchantConfirmWindow(){ |
||||
Menu*artificerEnchantConfirmWindow=CreateMenu(ARTIFICER_ENCHANT_CONFIRM,CENTERED,vi2d{144,144}); |
||||
|
||||
artificerEnchantConfirmWindow->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="",}}, |
||||
}); |
||||
} |
@ -0,0 +1,65 @@ |
||||
#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_game |
||||
|
||||
void Menu::InitializeArtificerEnchantWindow(){ |
||||
Menu*artificerEnchantWindow=CreateMenu(ARTIFICER_ENCHANT,CENTERED,vi2d{144,144}); |
||||
|
||||
artificerEnchantWindow->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="",}}, |
||||
}); |
||||
} |
@ -0,0 +1,65 @@ |
||||
#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_game |
||||
|
||||
void Menu::InitializeArtificerRefineConfirmWindow(){ |
||||
Menu*artificerRefineConfirmWindow=CreateMenu(ARTIFICER_REFINE_CONFIRM,CENTERED,vi2d{144,144}); |
||||
|
||||
artificerRefineConfirmWindow->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="",}}, |
||||
}); |
||||
} |
@ -0,0 +1,82 @@ |
||||
#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 "RowInventoryScrollableWindowComponent.h" |
||||
#include "MenuLabel.h" |
||||
|
||||
INCLUDE_game |
||||
|
||||
void Menu::InitializeArtificerRefineWindow(){ |
||||
Menu*artificerRefineWindow=CreateMenu(ARTIFICER_REFINE,CENTERED,game->GetScreenSize()-vi2d{52,52}); |
||||
|
||||
auto inventoryLabel=artificerRefineWindow->ADD("Accessory List Label",MenuLabel)(geom2d::rect<float>{{},{180.f,12.f}},"Choose Accessory:",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END; |
||||
|
||||
auto inventoryDisplay=artificerRefineWindow->ADD("Accessory List",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{0.f,16.f},{artificerRefineWindow->size.x/2-4.f,artificerRefineWindow->size.y-32}},"","",[](MenuFuncData data){ |
||||
return true; |
||||
},DO_NOTHING,DO_NOTHING, |
||||
InventoryCreator::RowPlayer_InventoryUpdate, |
||||
InventoryWindowOptions{.padding=1,.size={artificerRefineWindow->size.x/2-5.f,28}})END; |
||||
|
||||
auto backButton=artificerRefineWindow->ADD("Back",MenuComponent)(geom2d::rect<float>{{0.f,artificerRefineWindow->size.y-12.f},{120.f,12.f}},"Back",[](MenuFuncData data){ |
||||
Menu::CloseMenu(); |
||||
return true; |
||||
})END; |
||||
|
||||
Menu::AddInventoryListener(inventoryDisplay,"Accessories"); |
||||
|
||||
artificerRefineWindow->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="",}}, |
||||
}); |
||||
} |
@ -0,0 +1,101 @@ |
||||
#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 "MenuComponent.h" |
||||
#include "GameState.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "Unlock.h" |
||||
#include "MenuLabel.h" |
||||
#include "VisualNovel.h" |
||||
|
||||
INCLUDE_game |
||||
|
||||
void Menu::InitializeArtificerWindow(){ |
||||
Menu*artificerWindow=CreateMenu(ARTIFICER,CENTERED,vi2d{144,144}); |
||||
|
||||
artificerWindow->ADD("Refine Button",MenuComponent)(geom2d::rect<float>{{0.f,4.f},{144.f,24.f}},"Refine",[](MenuFuncData data){ |
||||
Menu::OpenMenu(MenuType::ARTIFICER_REFINE); |
||||
return true; |
||||
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; |
||||
artificerWindow->ADD("Disassemble Button",MenuComponent)(geom2d::rect<float>{{0.f,32.f},{144.f,24.f}},"Disassemble",[](MenuFuncData data){ |
||||
Menu::OpenMenu(MenuType::ARTIFICER_DISASSEMBLE); |
||||
return true; |
||||
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; |
||||
artificerWindow->ADD("Enchant Button",MenuComponent)(geom2d::rect<float>{{0.f,60.f},{144.f,24.f}},"Enchant",[](MenuFuncData data){ |
||||
Menu::OpenMenu(MenuType::ARTIFICER_ENCHANT); |
||||
return true; |
||||
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; |
||||
artificerWindow->ADD("Help Button",MenuComponent)(geom2d::rect<float>{{0.f,88.f},{144.f,24.f}},"Help",[](MenuFuncData data){ |
||||
VisualNovel::LoadDialog("ARTIFICER_HELP",[](){Menu::OpenMenu(MenuType::ARTIFICER);}); |
||||
return true; |
||||
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; |
||||
artificerWindow->ADD("Leave Button",MenuComponent)(geom2d::rect<float>{{0.f,116.f},{144.f,24.f}},"Leave",[](MenuFuncData data){ |
||||
Menu::CloseMenu(); |
||||
return true; |
||||
},vf2d{2.f,2.f},ButtonAttr::FIT_TO_LABEL)END; |
||||
|
||||
artificerWindow->SetupKeyboardNavigation( |
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
returnData="Refine Button"; |
||||
}, |
||||
{ //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
|
||||
{"Refine Button",{ |
||||
.up="Leave Button", |
||||
.down="Disassemble Button",}}, |
||||
{"Disassemble Button",{ |
||||
.up="Refine Button", |
||||
.down="Enchant Button",}}, |
||||
{"Enchant Button",{ |
||||
.up="Disassemble Button", |
||||
.down="Help Button",}}, |
||||
{"Help Button",{ |
||||
.up="Enchant Button", |
||||
.down="Leave Button",}}, |
||||
{"Leave Button",{ |
||||
.up="Help Button", |
||||
.down="Refine Button",}}, |
||||
}); |
||||
} |
@ -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 "BulletTypes.h" |
||||
#include "AdventuresInLestoria.h" |
||||
|
||||
INCLUDE_ANIMATION_DATA |
||||
INCLUDE_game |
||||
|
||||
BearTrap::BearTrap(vf2d pos,float radius,int damage,float fadeinTime,float fadeoutTime,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale) |
||||
:Bullet(pos,{},radius,damage,"Ability Icons/bear_trap.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,0.f,"Trap Hit"){ |
||||
fadeInTime=fadeinTime; |
||||
animation.AddState("bear_trap.png",ANIMATION_DATA["bear_trap.png"]); |
||||
if(!friendly)ERR("WARNING! Trying to use unimplemented enemy version of the Bear Trap Bullet!"); |
||||
} |
||||
void BearTrap::ModifyOutgoingDamageData(HurtDamageInfo&data){ |
||||
data.hurtFlags|=HurtFlag::PLAYER_ABILITY; |
||||
} |
||||
|
||||
BulletDestroyState BearTrap::PlayerHit(Player*player){ |
||||
fadeOutTime=0.5f; |
||||
animation.ChangeState(internal_animState,"bear_trap.png"); |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
|
||||
BulletDestroyState BearTrap::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||
fadeOutTime=0.5f; |
||||
animation.ChangeState(internal_animState,"bear_trap.png"); |
||||
|
||||
const float bleedDamage{"Trapper.Ability 2.Marked Target Bleed"_f[0]/100.f*game->GetPlayer()->GetAttack()}; |
||||
const float bleedDuration{"Trapper.Ability 2.Marked Target Bleed"_f[1]}; |
||||
const float timeBetweenTicks{"Trapper.Ability 2.Marked Target Bleed"_f[2]}; |
||||
|
||||
if(markStacksBeforeHit>0){ |
||||
const uint8_t resetStackCount{uint8_t("Trapper.Ability 2.Marked Target Stack Count Reset"_I+1U)}; //Add an additional stack because we know the target hit is about to lose one stack.
|
||||
const uint8_t numberOfStacksToReplenish{uint8_t(resetStackCount-monster.GetMarkStacks())}; |
||||
monster.ApplyMark("Trapper.Ability 2.Marked Target Reset Time"_F,numberOfStacksToReplenish); |
||||
monster.ApplyDot(bleedDuration,bleedDamage,timeBetweenTicks); |
||||
} |
||||
|
||||
monster.AddBuff(BuffType::SLOWDOWN,"Trapper.Ability 2.Slowdown Time"_F,"Trapper.Ability 2.Slowdown Amount"_F/100.f); |
||||
monster.Knockup("Trapper.Ability 2.Knockup Amount"_F); |
||||
|
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
@ -0,0 +1,126 @@ |
||||
#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 "AdventuresInLestoria.h" |
||||
#include "DEFINES.h" |
||||
#include "Monster.h" |
||||
#include "MonsterStrategyHelpers.h" |
||||
/*
|
||||
* Attack Strategie: If range to player >700 move closer.
|
||||
if range 400-700 Scratch the ground twice and then charge with 30% Move-Spd bonus in the players direction for a distance of 900. |
||||
If range to player<400 backpaddle with 50% move-spd. if getting hit while backpaddling, start charge sequence. |
||||
*/ |
||||
|
||||
INCLUDE_game |
||||
INCLUDE_ANIMATION_DATA |
||||
using A=Attribute; |
||||
|
||||
void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){ |
||||
enum PhaseName{ |
||||
MOVE, |
||||
SCRATCH, |
||||
CHARGE, |
||||
RECOVERY, |
||||
}; |
||||
|
||||
switch(m.phase){ |
||||
case PhaseName::MOVE:{ |
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos()); |
||||
if(m.canMove&&distToPlayer>=ConfigInt("Closein Range")/100.f*24){ |
||||
m.RemoveBuff(BuffType::SELF_INFLICTED_SLOWDOWN); |
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards"); |
||||
}else |
||||
if(m.HasLineOfSight(game->GetPlayer()->GetPos())&&m.canMove&&distToPlayer<=ConfigInt("Backpedal Range")/100.f*24){ |
||||
m.RemoveBuff(BuffType::SELF_INFLICTED_SLOWDOWN); |
||||
m.AddBuff(BuffType::SELF_INFLICTED_SLOWDOWN,INFINITE,(100-ConfigInt("Backpedal Movespeed"))/100.f); |
||||
m.target=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).rpoint(-100); |
||||
if(m.attackedByPlayer)goto ScratchPhaseTransition; |
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards"); |
||||
m.UpdateFacingDirection(game->GetPlayer()->GetPos()); |
||||
}else{ |
||||
ScratchPhaseTransition: |
||||
m.PerformAnimation("SCRATCH"); |
||||
m.F(A::CASTING_TIMER)=ConfigInt("Ground Scratch Count")*m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||
m.phase=PhaseName::SCRATCH; |
||||
|
||||
vf2d chargeTargetPoint=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).rpoint(ConfigFloat("Charge Distance")/100.f*24); |
||||
float distanceToChargePoint=geom2d::line<float>(m.GetPos(),chargeTargetPoint).length(); |
||||
m.F(A::TARGET_TIMER)=distanceToChargePoint/(100.f*m.GetMoveSpdMult()); //This should be how long a charge takes.
|
||||
m.target=chargeTargetPoint; |
||||
m.UpdateFacingDirection(game->GetPlayer()->GetPos()); |
||||
} |
||||
}break; |
||||
case PhaseName::SCRATCH:{ |
||||
m.RemoveBuff(BuffType::SELF_INFLICTED_SLOWDOWN); |
||||
m.F(A::CASTING_TIMER)-=fElapsedTime; |
||||
if(m.F(A::CASTING_TIMER)<=0.f){ |
||||
m.PerformShootAnimation(); |
||||
m.phase=PhaseName::CHARGE; |
||||
|
||||
m.AddBuff(BuffType::SPEEDBOOST,INFINITE,ConfigFloat("Charge Movespeed")/100.f-1); |
||||
|
||||
m.AddBuff(BuffType::COLLISION_KNOCKBACK_STRENGTH,15,ConfigFloat("Charge Knockback Amount")); |
||||
} |
||||
}break; |
||||
case PhaseName::CHARGE:{ |
||||
m.F(A::TARGET_TIMER)-=fElapsedTime; |
||||
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.target).length(); |
||||
|
||||
auto TransitionToRecoveryPhase=[&](){ |
||||
m.phase=PhaseName::RECOVERY; |
||||
m.F(A::CHARGE_COOLDOWN)=ConfigFloat("Charge Recovery Time"); |
||||
m.PerformIdleAnimation(); |
||||
}; |
||||
|
||||
if(m.F(A::TARGET_TIMER)<=0||m.bumpedIntoTerrain||distToTarget<12.f){ |
||||
TransitionToRecoveryPhase(); |
||||
}else{ |
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards"); |
||||
//m.PerformShootAnimation();
|
||||
if(!m.canMove)TransitionToRecoveryPhase(); |
||||
} |
||||
}break; |
||||
case PhaseName::RECOVERY:{ |
||||
m.RemoveBuff(BuffType::COLLISION_KNOCKBACK_STRENGTH); |
||||
m.F(A::CHARGE_COOLDOWN)-=fElapsedTime; |
||||
m.targetAcquireTimer=0.f; |
||||
m.RemoveBuff(BuffType::SPEEDBOOST); |
||||
if(m.F(A::CHARGE_COOLDOWN)<=0)m.phase=PhaseName::MOVE; |
||||
}break; |
||||
} |
||||
} |
@ -0,0 +1,109 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "util.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "BombBoom.h" |
||||
#include "SoundEffect.h" |
||||
|
||||
INCLUDE_DATA |
||||
INCLUDE_ANIMATION_DATA |
||||
INCLUDE_MONSTER_LIST |
||||
INCLUDE_game |
||||
|
||||
Bomb::Bomb(const vf2d pos,const float z,const float gravity,const float detonationTime,const float bombFadeoutTime,const float bombKnockbackFactor,const vf2d targetPos,const float explosionRadius,const int damage,const bool upperLevel,const bool friendly,const Pixel col,const vf2d scale) |
||||
:Bullet(pos,{},0.f,damage,"goblin_bomb.png",upperLevel,true,INFINITY,false,friendly,col,scale) |
||||
,explosionRadius(explosionRadius),gravity(gravity),detonationTime(detonationTime),bombFadeoutTime(bombFadeoutTime),bombKnockbackFactor(bombKnockbackFactor),targetPos(targetPos){ |
||||
this->z=z; |
||||
bomb_animation.AddState("Fuse",ANIMATION_DATA.at("goblin_bomb_fuse.png")); |
||||
bomb_animation.ChangeState(animation,"Fuse"); |
||||
} |
||||
void Bomb::Update(float fElapsedTime){ |
||||
vel=geom2d::line<float>(pos,targetPos).vector(); |
||||
detonationTime-=fElapsedTime; |
||||
bomb_animation.UpdateState(animation,fElapsedTime); |
||||
if(detonationTime>0.f){ |
||||
zVel+=gravity*fElapsedTime; |
||||
z=std::max(0.f,z+zVel*fElapsedTime); |
||||
if(z==0.f)zVel*=-1.f; |
||||
}else |
||||
if(fadeOutTime==0.f){ |
||||
z=0; //Force the bomb to be grounded.
|
||||
fadeOutTime=bombFadeoutTime; |
||||
game->AddEffect(std::make_unique<BombBoom>(pos,0.f,OnUpperLevel(),vf2d{explosionRadius,explosionRadius}/12.f/1.5f/*Upscale 24x24 to 36x36*/,1.f,vf2d{},WHITE,0.f,0.f,true)); |
||||
SoundEffect::PlaySFX("Bomb Explode",pos); |
||||
float distToPlayer=geom2d::line<float>(pos,game->GetPlayer()->GetPos()).length(); |
||||
if(friendly){ |
||||
const HurtList hurtEnemies=game->Hurt(pos,explosionRadius,damage,OnUpperLevel(),z,HurtType::MONSTER); |
||||
for(auto&[targetPtr,wasHit]:hurtEnemies){ |
||||
if(!std::holds_alternative<Monster*>(targetPtr))ERR("WARNING! Hurt enemies list returned a non-monster pointer!? THIS SHOULD NOT BE HAPPENING!"); |
||||
Monster*monsterPtr=std::get<Monster*>(targetPtr); |
||||
if(wasHit)monsterPtr->ProximityKnockback(pos,bombKnockbackFactor); |
||||
} |
||||
if(distToPlayer<=explosionRadius){ |
||||
game->GetPlayer()->ProximityKnockback(pos,bombKnockbackFactor); |
||||
} |
||||
}else{ |
||||
if(distToPlayer<=explosionRadius){ |
||||
if(game->GetPlayer()->Hurt(damage,OnUpperLevel(),z)){ |
||||
game->GetPlayer()->ProximityKnockback(pos,bombKnockbackFactor); |
||||
} |
||||
} |
||||
for(auto&monsterPtr:MONSTER_LIST){ |
||||
float distToMonster=geom2d::line<float>(pos,monsterPtr->GetPos()).length(); |
||||
if(distToMonster<=explosionRadius){ |
||||
monsterPtr->ProximityKnockback(pos,bombKnockbackFactor); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
BulletDestroyState Bomb::PlayerHit(Player*player){ |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
BulletDestroyState Bomb::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
|
||||
void Bomb::Draw(const Pixel blendCol)const{ |
||||
Bullet::Draw(blendCol); |
||||
game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()},bomb_animation.GetFrame(animation).GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2:0,bomb_animation.GetFrame(animation).GetSourceRect().size/2,bomb_animation.GetFrame(animation).GetSourceRect().pos,bomb_animation.GetFrame(animation).GetSourceRect().size,scale,fadeOutTime==0?col:Pixel{col.r,col.g,col.b,uint8_t(util::lerp(col.a,0,1-((fadeOutTime-GetFadeoutTimer())/fadeOutTime)))});
|
||||
} |
||||
|
||||
void Bomb::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,52 @@ |
||||
#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 "Effect.h" |
||||
#include "DEFINES.h" |
||||
#include "safemap.h" |
||||
|
||||
INCLUDE_GFX |
||||
|
||||
class BombBoom:public Effect{ |
||||
public: |
||||
inline BombBoom(vf2d pos,float lifetime,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false) |
||||
:Effect(pos,lifetime,"bomb_boom.png",upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){} |
||||
|
||||
inline bool Update(float fElapsedTime)override{ |
||||
return Effect::Update(fElapsedTime); |
||||
} |
||||
}; |
@ -0,0 +1,95 @@ |
||||
#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 "MonsterStrategyHelpers.h" |
||||
#include "util.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "SoundEffect.h" |
||||
|
||||
using A=Attribute; |
||||
|
||||
void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string strategy){ |
||||
enum Phase{ |
||||
INITIALIZE, |
||||
RUN, |
||||
}; |
||||
|
||||
if(m.GetHealthRatio()>=ConfigFloat("Break Phase 1 HP % Threshold")/100.f){ |
||||
m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Unbroken Animation Name")); |
||||
}else |
||||
if(m.GetHealthRatio()>=ConfigFloat("Break Phase 2 HP % Threshold")/100.f){ |
||||
m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 1 Animation Name")); |
||||
}else m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 2 Animation Name")); |
||||
|
||||
switch(m.phase){ |
||||
case INITIALIZE:{ |
||||
m.F(A::BREAK_TIME)=ConfigFloat("Break Time"); |
||||
m.F(A::SHAKE_TIMER)=0.2f; |
||||
m.phase=RUN; |
||||
|
||||
m.SetStrategyDeathFunction([&](GameEvent&deathEvent,Monster&m,const std::string&strategy){ |
||||
m.lifetime=0.01f; |
||||
m.B(A::MARKED_DEAD)=true; |
||||
const float bulletAngRandomOffset{util::random(PI/2)}; |
||||
for(int i=0;i<ConfigInt("Death Ring Bullet Count");i++){ |
||||
const float bulletAngle=((2*PI)/ConfigInt("Death Ring Bullet Count"))*i+bulletAngRandomOffset; |
||||
CreateBullet(Bullet)(m.GetPos(),vf2d{ConfigFloat("Death Ring Bullet Speed"),bulletAngle}.cart(),ConfigFloat("Death Ring Bullet Size"),ConfigInt("Death Ring Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Death Ring Bullet Color"),vf2d{ConfigFloat("Death Ring Bullet Size")/3,ConfigFloat("Death Ring Bullet Size")/3})EndBullet; |
||||
BULLET_LIST.back()->SetIframeTimeOnHit(0.15f); |
||||
} |
||||
return false; |
||||
}); |
||||
}break; |
||||
case RUN:{ |
||||
m.F(A::BREAK_TIME)-=fElapsedTime; |
||||
if(m.F(A::BREAK_TIME)<=2.f){ |
||||
if(m.F(A::BREAK_TIME)<=0.f){ |
||||
m._DealTrueDamage(m.GetHealth()); //Kill the pillar, making it crumble.
|
||||
break; |
||||
} |
||||
m.F(A::SHAKE_TIMER)-=fElapsedTime; |
||||
if(m.F(A::SHAKE_TIMER)<=0.f){ |
||||
m.F(A::SHAKE_TIMER)+=0.2f; |
||||
if(m.B(A::SHAKE_DIR))m.SetX(m.GetPos().x-2); |
||||
else m.SetX(m.GetPos().x+2); |
||||
m.B(A::SHAKE_DIR)=!m.B(A::SHAKE_DIR); |
||||
SoundEffect::PlaySFX("Pillar Shake",m.GetPos()); |
||||
} |
||||
} |
||||
}break; |
||||
} |
||||
} |
@ -0,0 +1,130 @@ |
||||
#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 "Buff.h" |
||||
#include "Player.h" |
||||
#include "Monster.h" |
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity) |
||||
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity){} |
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<ItemAttribute> attr) |
||||
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity),attr(attr){} |
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<std::string> attr) |
||||
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity){ |
||||
for(const std::string&s:attr){ |
||||
this->attr.insert(ItemAttribute::attributes.at(s)); |
||||
} |
||||
} |
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc) |
||||
:Buff(attachedTarget,type,overTimeType,duration,intensity,timeBetweenTicks){ |
||||
playerBuffCallbackFunc=expireCallbackFunc; |
||||
} |
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc) |
||||
:Buff(attachedTarget,type,overTimeType,duration,intensity,timeBetweenTicks){ |
||||
monsterBuffCallbackFunc=expireCallbackFunc; |
||||
} |
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks) |
||||
:attachedTarget(attachedTarget),duration(duration),intensity(intensity),nextTick(duration-timeBetweenTicks),timeBetweenTicks(timeBetweenTicks),overTimeType(overTimeType){ |
||||
switch(type){ |
||||
case BuffRestorationType::ONE_OFF:{ |
||||
this->type=ONE_OFF; |
||||
}break; |
||||
case BuffRestorationType::OVER_TIME:{ |
||||
this->type=OVER_TIME; |
||||
}break; |
||||
case BuffRestorationType::OVER_TIME_DURING_CAST:{ |
||||
this->type=OVER_TIME_DURING_CAST; |
||||
}break; |
||||
} |
||||
} |
||||
|
||||
void Buff::Update(AiL*game,float fElapsedTime){ |
||||
duration-=fElapsedTime; |
||||
if(enabled&&overTimeType.has_value()&&nextTick>0&&duration<nextTick){ |
||||
BuffTick(game,fElapsedTime); |
||||
nextTick-=timeBetweenTicks; |
||||
if(type==ONE_OFF)enabled=false; |
||||
} |
||||
} |
||||
|
||||
void Buff::BuffTick(AiL*game,float fElapsedTime){ |
||||
if(!overTimeType.has_value())ERR("WARNING! Trying to run BuffTick without a valid over time type provided! THIS SHOULD NOT BE HAPPENING!"); |
||||
using enum BuffOverTimeType::BuffOverTimeType; |
||||
|
||||
const auto OwnerIsPlayer=[&](){return std::holds_alternative<Player*>(attachedTarget);}; |
||||
const auto OwnerIsMonster=[&](){return std::holds_alternative<std::weak_ptr<Monster>>(attachedTarget);}; |
||||
const auto MonsterIsValid=[&](){return !std::get<std::weak_ptr<Monster>>(attachedTarget).expired();}; |
||||
const auto GetPlayer=[&](){return std::get<Player*>(attachedTarget);}; |
||||
const auto GetMonster=[&](){return std::get<std::weak_ptr<Monster>>(attachedTarget).lock();}; |
||||
|
||||
switch(int(overTimeType.value())){ |
||||
case HP_RESTORATION:{ |
||||
if(OwnerIsPlayer())GetPlayer()->Heal(intensity); |
||||
else if(OwnerIsMonster()){if(MonsterIsValid())GetMonster()->Heal(intensity);} |
||||
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!") |
||||
}break; |
||||
case MP_RESTORATION:{ |
||||
if(OwnerIsPlayer())GetPlayer()->RestoreMana(intensity); |
||||
else if(OwnerIsMonster())ERR("WARNING! Monsters don't have mana, this functionality is not supported!") |
||||
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!") |
||||
}break; |
||||
case HP_PCT_RESTORATION:{ |
||||
if(OwnerIsPlayer())GetPlayer()->Heal(GetPlayer()->GetMaxHealth()*intensity/100.f); |
||||
else if(OwnerIsMonster()){if(MonsterIsValid())GetMonster()->Heal(GetMonster()->GetMaxHealth()*intensity/100.f);} |
||||
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!") |
||||
}break; |
||||
case MP_PCT_RESTORATION:{ |
||||
if(OwnerIsPlayer())GetPlayer()->RestoreMana(GetPlayer()->GetMaxMana()*intensity/100.f); |
||||
else if(OwnerIsMonster()){ERR("WARNING! Monsters don't have mana, this functionality is not supported!")} |
||||
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!") |
||||
}break; |
||||
case HP_DAMAGE_OVER_TIME:{ |
||||
if(OwnerIsPlayer())GetPlayer()->Hurt(intensity,GetPlayer()->OnUpperLevel(),GetPlayer()->GetZ(),HurtFlag::DOT); |
||||
else if(OwnerIsMonster()){if(MonsterIsValid())GetMonster()->Hurt(intensity,GetMonster()->OnUpperLevel(),GetMonster()->GetZ(),HurtFlag::DOT);} |
||||
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!") |
||||
}break; |
||||
case HP_PCT_DAMAGE_OVER_TIME:{ |
||||
if(OwnerIsPlayer())GetPlayer()->Hurt(GetPlayer()->GetMaxHealth()*intensity/100.f,GetPlayer()->OnUpperLevel(),GetPlayer()->GetZ(),HurtFlag::DOT); |
||||
else if(OwnerIsMonster()){if(MonsterIsValid())GetMonster()->Hurt(GetMonster()->GetMaxHealth()*intensity/100.f,GetMonster()->OnUpperLevel(),GetMonster()->GetZ(),HurtFlag::DOT);} |
||||
else ERR("WARNING! Buff Over Time attached Target is somehow not a Player nor a Monster Pointer! THIS SHOULD NOT BE HAPPENING!") |
||||
}break; |
||||
} |
||||
} |
@ -0,0 +1,69 @@ |
||||
Chapter 2 Boss |
||||
|
||||
Stonegolem |
||||
|
||||
|
||||
|
||||
HP: 30 000 |
||||
|
||||
Size: 800% |
||||
|
||||
|
||||
|
||||
>The boss casts 3 Pillars at the beginning of the fight, targeting the player. |
||||
|
||||
|
||||
|
||||
>1 at a time. |
||||
|
||||
>2 sec cast. |
||||
|
||||
>0.5 sec delay until next cast starts. |
||||
|
||||
>40 dmg |
||||
|
||||
>Pillar-radius: 350 |
||||
|
||||
|
||||
|
||||
>every 10% (first time at 90%) use a shockwave where you need to hide behind one of the pillar to avoid the damage. (Shockwave has 3 seconds cast, 60 dmg) |
||||
|
||||
>The pillars get damaged with every shockwave. after the 3rd the pillar break. |
||||
|
||||
|
||||
|
||||
>starting 75% every 10% (75%, 65%, 55% ...) The Stone golem targetst the Player with 3 Pillar attacks like the one at the beginning of the fight. |
||||
|
||||
>2 of the 3 Pillars spawn damaged and will break 3 seconds later again. one is solid and can tank up to 3 shockwaves. |
||||
|
||||
|
||||
|
||||
>Like previously every pillar appears with 2,5 seconds delay from each other. (2 sec with an indicator, 0,5 seconds delay after a pillar got created) |
||||
|
||||
>The Boss continues with its normal behaviour while the pillars are getting casted. |
||||
|
||||
|
||||
|
||||
>every Pillar that breaks Casts a ring of projectiles on death (15 dmg) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
>The golem attacks similar like bear boss but with half the radius on the attack. (35 dmg) |
||||
|
||||
>after every attack there is a 25% chance The Stone golem targets the Player. the targeting gets Indicated around the player. |
||||
|
||||
>after 2.5 seconds the golems throws a giant rock with 250 radius on the players position. Damaging the player and destroying Pillars hit. (55 dmg) |
||||
|
||||
>Stone Throw cant overlap with shockwave. when a 10% mark is reached while a rock is targeting the player, Shockwave cast starts after the rock was thrown. |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if the golem wasnt able to attack the player for 7 seconds it throws 2 stones in the air. |
||||
|
||||
Afterwards Screenwide ~25 indicators appear where small rocks will rain down with a delay of 2,5 - 4 seonds (30 dmg each, 50 radius) |
||||
|
||||
(The trigger condition for this ability may changes in the future, depending how it actually plays out.) |
@ -0,0 +1,87 @@ |
||||
Chapter 2 Monsters: |
||||
|
||||
Boar: |
||||
|
||||
Hp: 260 |
||||
Attack: 24 |
||||
Size: 110% |
||||
Move-Spd: 100% |
||||
|
||||
Attack Strategie: If range to player >700 move closer. |
||||
if range 400-700 Scratch the ground twice and then charge with 30% Move-Spd bonus in the players direction for a distance of 900. |
||||
If range to player<400 backpaddle with 50% move-spd. if getting hit while backpaddling, start charge sequence. |
||||
|
||||
Before a charge occurs, the boar will make sure to have full line-of-sight of the player before beginning a charge. |
||||
Once a charge begins, the direction cannot be modified. |
||||
|
||||
Goblin (Dagger): |
||||
|
||||
Hp: 120 |
||||
Attack: 18 |
||||
Size: 90% |
||||
Move-Spd: 90% |
||||
|
||||
Attack Strategie: Runs infront of player, short preparation for attack (~0.3 - 0.5 secs depending what looks better animation wise) and either slashes with the dagger or stabs with it. 1 sec of idle before loop repeats |
||||
|
||||
Goblin (Bow): |
||||
|
||||
Hp: 120 |
||||
Attack: 18 |
||||
Size: 90% |
||||
Move-Spd: 70% |
||||
|
||||
Attack Strategie: |
||||
if range > 1000 move to a 850 range. |
||||
if range 1000-700. Stand still and shoot. (reload twice as fast) |
||||
if range between 500 and 700. Shoot and run in random direction while reloading without getting out of the 500 - 700 range |
||||
range < 500 while reloading try to get distance. still shoot if reload finishes |
||||
After every attack: reload takes 2 seconds. before shooting takes 1 second. |
||||
|
||||
|
||||
Goblin (Bombs): |
||||
|
||||
Hp: 120 |
||||
Attack: 38 |
||||
Size: 90% |
||||
Move-Spd: 70% |
||||
|
||||
Attack Strategie: Throws every 4 seconds a bomb on top of the player that detonates after 3.5 seconds in a 300 radius. Tries to keeps his distance on a 600 radius. Throws bombs even while moving. |
||||
|
||||
Goblin Boar Rider: |
||||
|
||||
Same stats as Boar. |
||||
On death leaves a Bow Goblin on location of death |
||||
|
||||
Same attack strategie as normal Boar. |
||||
+ the Goblin on Top also shoots every 4 seconds |
||||
|
||||
Hawks |
||||
|
||||
Hp: 10 |
||||
Attack: 22 |
||||
Size: 50% |
||||
Move-Spd: 180% |
||||
|
||||
Attack Strategie: Fly circles at the edge of the Players Screen. every 8 seconds charge on players location to the other corner of the screen. |
||||
|
||||
Stone Elemental |
||||
|
||||
Hp: 990 |
||||
Attack: 52 |
||||
Move-Spd: 50% |
||||
Size: 220% |
||||
|
||||
Attack Strategie: |
||||
Every 2 seconds after completing an attack do one of the following attacks: |
||||
Casts a Stone Pillar on the location of the Player: radius 350. Hits with 3 seconds delay. Becomes an solid object (Player colision) and decays after 5 seconds again. |
||||
If the Stone pillar would hit the Stone Elemental it will move out of its range. |
||||
Shoot a Stone (bullet) in the direction of the target. |
||||
The Stone gets created in form of a Cone and tracks the player movement without moving for the first second. |
||||
Once it locks it doesnt move for another second. then it starts flying towards the player with 3x the normal Bullet speed. |
||||
Dive under earth and show up 1 second later in a random location 400-700 away from player. while emerging shoot a ring of bullets (0.5x Attack) |
||||
(maybe emerge with 200 away from anything with colision to avoid the Elemental getting stuck? Other solutions for that are welcome aswell) |
||||
if range to player > 1200 always use 3rd attack |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,190 @@ |
||||
Pirate |
||||
-Wields a Saber and wears an eye patch, pirate hat, the typical Pirate stereotype. |
||||
|
||||
Animations (5): |
||||
Idle |
||||
Walk |
||||
Slash |
||||
Stab |
||||
Dead |
||||
|
||||
Pirate Marauder |
||||
-Equipped with 2 Swords. |
||||
|
||||
Animations (5): |
||||
Idle |
||||
Walk |
||||
Jump Attack (Plunge) |
||||
Whirlwind Attack (Spinning around frantically) |
||||
Dead |
||||
|
||||
Pirate Buccaneer |
||||
-Equipped with a musket. |
||||
|
||||
Animations (5): |
||||
Idle |
||||
Walk |
||||
Aim |
||||
Firing |
||||
Dead |
||||
|
||||
Pirate Captain |
||||
-Holds a Saber in one hand and a Hand Gun in the other. |
||||
|
||||
Animations (7): |
||||
Idle |
||||
Walk |
||||
Slash using Saber |
||||
Stab using Saber |
||||
Raise + Shoot Handgun |
||||
Drink a bottle of Rum. |
||||
Dead |
||||
|
||||
Parrot |
||||
====== |
||||
(Does not require a death animation) |
||||
|
||||
Animations (3): |
||||
Idle |
||||
Fly |
||||
Charge |
||||
|
||||
Crab |
||||
==== |
||||
Animations (4): |
||||
Idle |
||||
Sidestep / Crab Walk |
||||
Lifts up pincers and charges |
||||
Dead |
||||
|
||||
Sandworm |
||||
======== |
||||
Starts underground (can see silhouette) |
||||
|
||||
Animations (6): |
||||
Idle (Head above ground) |
||||
Dive underground |
||||
Swim in sand |
||||
Rise up from ground |
||||
Shoot/Spit Sand |
||||
Dead |
||||
|
||||
|
||||
Seagull |
||||
======= |
||||
Animations (3): |
||||
Idle |
||||
Flying |
||||
Dead |
||||
|
||||
================= |
||||
|
||||
Pirate |
||||
Size: 100% |
||||
Hp: 350 |
||||
Exp: 33 |
||||
Attack: 31 |
||||
Move-Spd: 110% |
||||
|
||||
Equipped with a Saber. |
||||
Same behaviour like Chapter 2 Dagger Goblins. |
||||
|
||||
Pirate Marauder |
||||
Size: 100% |
||||
Hp: 500 |
||||
Exp: 39 |
||||
Attack: 43 |
||||
Move-Spd: 125% |
||||
|
||||
Equipped with 2 Swords. |
||||
Base behaviour like Pirate. |
||||
every 8 seconds chance to do an ability: |
||||
25% & insight of players vision & further then 400 range away: Jump attack on the player 150 radius impact area |
||||
25% & within 700 range of player & further then 400 range away: whirlwind attack, spinning to the players position +60% Move-spd during this attack |
||||
50% continue base behaviour. |
||||
|
||||
Pirate Buccaneer |
||||
Size: 100% |
||||
Hp: 430 |
||||
Exp: 37 |
||||
Attack: 45 |
||||
Move-Spd: 100% |
||||
|
||||
Equipped with a Muskeet. |
||||
Aim (1.0 Sec) - Lock position (0.3 sec) - Shoot - reload (2 sec) - move if to far away. (trying to stay in 1000 Range) |
||||
|
||||
|
||||
Pirate Captain (+Parrot) |
||||
Size: 120% |
||||
Hp: 1050 |
||||
Exp: 59 |
||||
Attack: 55 |
||||
Move-Spd: 95% |
||||
|
||||
1 hand Saber. 1 Hand Gun |
||||
Also Base Pirate behaviour. |
||||
every 6 seconds if range <400 60% chance to stand still for 0.5 seconds - shoot his gun and continue base movement. |
||||
|
||||
Once the Captain dropps below 300 health he pulls out a bottle of Rum and starts drinking. takes 2 seconds and recovers 150 Health. Only triggers once. |
||||
|
||||
When the Pirate Enters the Screen the Parrot is still sitting on its shoulder and starts flying 2 seconds after the enemy is visible. |
||||
The parrot has the same stats and behaviour like chapter 2 Hawks. |
||||
The Parrot doesnt die, instead it gets stunned for 5 seconds on death and then recovers. |
||||
When the Captain dies the Parrot flies away and despawns out of screen. |
||||
|
||||
Crap |
||||
Size: 60% |
||||
Hp: 600 |
||||
Exp: 34 |
||||
Attack: 23 |
||||
Move-Spd: 120% |
||||
|
||||
Side step circles around the player in a 400 range radius. |
||||
every second running in a circle 20% chance to stop for 0.5 seconds, turn his side towards the player and charges with a pincer attack and runs a bit further to get to 400 range on the other side of the player. |
||||
starts circling the player again. |
||||
|
||||
Giant Crap |
||||
Size: 180% |
||||
Hp: 1800 |
||||
Exp: 61 |
||||
Attack: 49 |
||||
Move-Spd: 90% |
||||
|
||||
Charges player sideways. charges 600 range further then players position. |
||||
After charge 1 second no moving. then starts next charge building up movespeed over first 2 seconds. |
||||
|
||||
Sandworm |
||||
Size: 90% |
||||
Hp: 400 |
||||
Exp: 24 |
||||
Attack: 33 |
||||
Move-Spd: 200% // 0% (stationary on air) |
||||
|
||||
The sandworm spawns underground. |
||||
The sandworm ist completly invisible when its underground. |
||||
|
||||
It moves to a random point within a 400 range around the player. (can be exactly under the player aswell) |
||||
|
||||
once it reaches it position there is an 300 radisu animation and the player gets sucked in with a 30% movespd effect ( |
||||
Examples: |
||||
if playing doesnt move it moves with 30% speed into the middle, |
||||
if moving to the center normal speed +30% |
||||
if moving out normal speed -30% |
||||
|
||||
Effect lasts for 3 seconds. If player is in the middle player receives collision damage. |
||||
|
||||
Worm behaves like Chapter 1 Flower for up to 6 Attacks or if range to player is higher then 1500. |
||||
Then it burrows down again and repeats. |
||||
|
||||
Seagul |
||||
Size: 80% |
||||
Hp: 40 |
||||
Exp: 6 |
||||
Attack: 0 |
||||
Move-Spd: 120% |
||||
When Player gets within 1000 Range Seagul starts flying away. takes 2 seconds to leave the ground. (time needs to be extended if 2 seconds arent enough for a warrior to get in warcry range) |
||||
Despawns once it reaches 4000 Range. |
||||
|
||||
Bosses |
||||
Giant Octopus |
||||
Pirate Captain Ghost |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,56 @@ |
||||
Eagle Boss - King of Birds, Zephy |
||||
|
||||
On entering boss arena: |
||||
8 basic Hawks appear, no exp/dropp |
||||
|
||||
after defeat: |
||||
2 Major Hawks appear. |
||||
|
||||
Major Hawks |
||||
Hp: 520 |
||||
Attack: 31 |
||||
Size: 120% |
||||
Move-Spd: 220% |
||||
no exp or dropps |
||||
|
||||
As long both are active normal Hawk Strategie: |
||||
Attack Strategie: Fly circles at the edge of the Players Screen. every 8 seconds charge on players location to the other corner of the screen. |
||||
|
||||
If only 1 is active instead of every 8 seconds charge every 3. |
||||
|
||||
|
||||
Once those are defeated the boss spawns. |
||||
|
||||
Boss |
||||
|
||||
Hp: 7000 |
||||
Attack: 48 |
||||
Size: 400% |
||||
Move-Spd: 180% |
||||
|
||||
From now on every 10 seconds a basic Hawk spawns. |
||||
|
||||
|
||||
It uses the following attack moves in random order: |
||||
|
||||
- Flys from left to right or right to left in the top of the Screen and shits down. (25 dmg on hit) |
||||
- Lands in the middle of the boss arena (in the area the player is at) and create small Tornados around himself which move in a circle (size: ~80% spd: 90%, ) |
||||
- Flys to the Central top of the arena and does a Fan of feathers. Feathers fly until the end of the arena. you cant outrange it. there are a few gaps inbetween to dodge though. |
||||
(8 dmg each, can be hit from multiple at once) |
||||
Repeats this move once. |
||||
- lands on the left or right side of the arena and starts to create alot of wind with his wings. moving towards the boss slows the movespeed running away increases the move speed. |
||||
starts with +/- 10% and increases by another 10% every second until it reaches 60%. |
||||
On full strength: Projectiles of auto attacks get blown away. Objects appear that deal damage on collision. (Bushes & stones from forest tileset. if possible spinning) |
||||
Collision dmg 40. |
||||
Full strength lasts for 12 Seconds. |
||||
|
||||
|
||||
50%-Phase |
||||
- Boss lands on the Pillar in the top Middle of the arena and creates and tornado. Player gets constantly slightly sucked towards it. |
||||
- The Tornado shoots feather projectiles in random directions. |
||||
(10 dmg, amount of feathers needs a bit of testing. |
||||
it needs to be enough that it is some kind of threat but not to much |
||||
that you are just overwhelmed by them while fighting the other birds.) |
||||
- the 10 second Hawk spawner is disabled until boss reappears. |
||||
- 2 Major hawks spawn + 4 Basic Hawks |
||||
- after every Hawk is defeated the fight goes back to normal mode. (which are the 6 spawned + any bird that still spawned in the fight before and didnt got killed yet) |
@ -0,0 +1,52 @@ |
||||
Artificer |
||||
|
||||
- Unlocks after Chapter 2 - Bonus Boss |
||||
- Unlocks an additional feature after Chapter 3 - Bonus Boss |
||||
- Is available in the hub area |
||||
|
||||
Artificer unlocks after beating chapter 2 bonus boss. |
||||
Player heads to camp. |
||||
Camera pans over to the Artificer's station, and a new NPC appears there. |
||||
Then camera pans back over to the player |
||||
The leave buttons are disabled and a tutorial prompt to visit the artificer appears. |
||||
You go to the artificer and talk to them. |
||||
Artificer explains that your accessories' stats may not have reached their full potential yet and can be refined. |
||||
You can disassemble previously obtained rings to get ring fragments, then refine your rings to increase its power further. |
||||
|
||||
Artificer: Hello adventurer! Welcome to my artificing station! May I have the pleasure of knowing your name? |
||||
[YOU]: Hello, my name is ____. |
||||
Artificer: Excelent! Nice to meet you, [YOU]. Here you have the ability to refine and fix up imperfect jewelry discovered along your journey! |
||||
Artificer: Break down rings that are undesirable to you to retrieve fragments, then use the Refining station to empower your equipment further! |
||||
Artificer: This will let you get use out of all those extra rings you may be carrying around, eh? |
||||
|
||||
(Auto-generate fragment item types for each ring) |
||||
Confirmation dialog for disassembly |
||||
|
||||
Refining: Popup a dialog showing current stats of selected ring, show possible upgraded stats as (+ ??) |
||||
Hit Refine button to consume fragment to upgrade some stat. Play an animation next to stat that was upgraded. |
||||
(Whichever is greater): Always go up by 20%/Always go up by 1 |
||||
|
||||
Artificer has 4 Dialog Options |
||||
- Repair/Enhance/(Refine?) (Name may change in the future) |
||||
- Disassemble |
||||
- Enchant (Requieres Chapter 3 - Bonus boss) |
||||
- Help (Runs through the explanation again.) |
||||
- Leave |
||||
|
||||
|
||||
|
||||
|
||||
Enhancing Gear |
||||
- increases the stats of an item. |
||||
- Increases a random stat by a random amount that did not reach its cap. (cant go higher then 20% of its current amount in on Enhance attempt and cant go higher then the max stat value of the item) |
||||
- Costs 1 piece of the Ring you want to enhance + 20g |
||||
|
||||
Disassemble |
||||
- Destroys a ring to get 1 piece of the ring |
||||
- doesnt cost anything but destroys the item |
||||
- pieces have the same vendor value a ring itself has. |
||||
|
||||
Enchant |
||||
- Gives a ring 1 additional Bonus (there will be 3 different type of bonuses: Base attributes, Class specifics spell modifiers, Unique effects) |
||||
- If a ring is already enchanted you override its current enchant. (Maybe give an "Keep old enchant" option?) |
||||
- Requieres 3 Pieces + 35g |
@ -0,0 +1,171 @@ |
||||
Thief |
||||
|
||||
Autohit: Sword Slash (0.35 sec cd) |
||||
|
||||
|
||||
|
||||
Rightclick - Roll |
||||
|
||||
Dodgeroll that gives an iframe and 2 sec of move spd buff afterwards. |
||||
|
||||
Range: 250 |
||||
|
||||
Movespd buff: 30% |
||||
|
||||
cd: 8 sec |
||||
|
||||
|
||||
|
||||
1 - Hidden Dagger |
||||
|
||||
Backwards Jump while Throwing a Dagger infront |
||||
|
||||
Jump-Range: 200 |
||||
|
||||
Damage: 2x Atk |
||||
|
||||
cd: 8 sec. |
||||
|
||||
20 Mana |
||||
|
||||
|
||||
|
||||
2 - deadly Dash |
||||
|
||||
Dashes to Target location |
||||
|
||||
Damages everything the Thief goes through. |
||||
|
||||
iframe lasting additional 0.5 sec after skill ends. |
||||
|
||||
Damage: 4x Atk |
||||
|
||||
Range: 450 |
||||
|
||||
cd: 15 sec. |
||||
|
||||
Mana 45 |
||||
|
||||
|
||||
|
||||
3 - Adrenaline Rush |
||||
|
||||
30% Attack Speed and 10% Movementspeed buff |
||||
|
||||
Duration: 8 seconds |
||||
|
||||
Cd: 37 sec. |
||||
|
||||
Mana 75 |
||||
|
||||
|
||||
|
||||
Trapper |
||||
|
||||
Autohit: Same as Ranger |
||||
|
||||
|
||||
|
||||
Rightclick - Sprint |
||||
|
||||
3 sec / 60% movespeed buff |
||||
|
||||
cd: 7 sec. |
||||
|
||||
|
||||
|
||||
1 - Mark Target |
||||
|
||||
Range: Screenwide |
||||
|
||||
Cd: 12 sec. |
||||
|
||||
25 Mana |
||||
|
||||
Target receives addition 0.6x Atk dmg every time the target get hit by any auto attack or ability. |
||||
|
||||
7 sec Duration or 5 triggers |
||||
|
||||
NOTE: Ranger abilities can proc this too, due to 4th ability sharing. |
||||
|
||||
2 - Bear Trap |
||||
|
||||
Lays down a trap on Players position. |
||||
|
||||
Damage: 2.5x Attack |
||||
|
||||
Cd: 15 sec |
||||
|
||||
If Target is marked the mark gets reset to 7 seconds with 5 stacks. |
||||
|
||||
If Target was marked the enemy gets a bleed debuff dealing 0.1x Attack every second for 10 seconds. |
||||
|
||||
|
||||
|
||||
3 - Explosive Trap |
||||
|
||||
Trap takes 1.5 sec until its active until it gets thrown somewhere. |
||||
|
||||
Trap explodes either if an enemy touches it or with a 5 second delay. |
||||
|
||||
Damage: 5.5x Attack |
||||
|
||||
If anyone inside the explosion is marget, Target Triggers 2 Marks at once. |
||||
|
||||
Anyone who is not marked inside the explosion gets 1 Stack of mark with 7 sec duration. |
||||
|
||||
|
||||
|
||||
Witch |
||||
|
||||
Autohit: Hooming Dark/Purple Energy Bolt. only starts hooming if it gets really close to an enemy. (0.85 sec cd) |
||||
|
||||
|
||||
|
||||
Righclick - Transform |
||||
|
||||
Transforms to a Cat Leaps for 400 Range and transforms back (iFrame during move) |
||||
|
||||
cd: 8 sec. |
||||
|
||||
|
||||
|
||||
1 Curse of Pain |
||||
|
||||
Single Target dot Target on Mouse location. 6 seconds |
||||
|
||||
Range: 700 |
||||
|
||||
35 Mana |
||||
|
||||
1x attack every 2 seconds for 8 seconds. Last Tick does double damage. |
||||
|
||||
8 sec Cd |
||||
|
||||
|
||||
|
||||
2 Throw Poison |
||||
|
||||
Throws a poison bottle like a grenate. Massive aoe dmg and low dmg ticking dot over long time. |
||||
|
||||
Range: 700 |
||||
|
||||
AoE: 350 Range |
||||
|
||||
Damage: 5x Atk Poisons over 30 seconds (does every 3 secs 0.5x Atk dmg) |
||||
|
||||
40 Mana |
||||
|
||||
16 sec cd |
||||
|
||||
|
||||
|
||||
3 Curse of Death |
||||
|
||||
Curses Target to receive double dmg for next 7 seconds. Target with Mouse location |
||||
|
||||
Range: 700 |
||||
|
||||
cd: 35 sec |
||||
|
||||
35 Mana |
@ -0,0 +1,93 @@ |
||||
#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 "AdventuresInLestoria.h" |
||||
#include "Menu.h" |
||||
#include "ScrollableWindowComponent.h" |
||||
#include "MenuLabel.h" |
||||
|
||||
INCLUDE_WINDOW_SIZE |
||||
|
||||
void Menu::InitializeCreditsWindow(){ |
||||
vf2d windowSize=WINDOW_SIZE-vf2d{78,78}; |
||||
|
||||
Menu*creditsWindow=CreateMenu(MenuType::CREDITS,CENTERED,windowSize); |
||||
|
||||
auto displayScrollWindow=creditsWindow->ADD("Display Text Scroll Window",ScrollableWindowComponent)(geom2d::rect<float>{{4,4},windowSize-vf2d{8,8}})END; |
||||
|
||||
displayScrollWindow->ADD("Display Text",MenuLabel)(geom2d::rect<float>{{2,2},windowSize-vf2d{24,12}},"Test Text\nEven more testing text that we could use for this game.",1.f,ComponentAttr::CENTER)END; |
||||
|
||||
creditsWindow->ADD("Go Back Button",MenuComponent)(geom2d::rect<float>{{windowSize.x/2.f-48.f,windowSize.y},{96.f,12.f}},"Back",[](MenuFuncData data){ |
||||
Menu::CloseMenu(); |
||||
return true; |
||||
})END; |
||||
|
||||
creditsWindow->SetupKeyboardNavigation( |
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
returnData="Go Back Button"; |
||||
}, |
||||
{ //Button Key
|
||||
{{game->KEY_SCROLLUP,Held},{"",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(1.f); |
||||
}}}, |
||||
{{game->KEY_SCROLLDOWN,Held},{"",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(-1.f); |
||||
}}}, |
||||
{{game->KEY_SHOULDER2,Pressed},{"Scroll",[](MenuType type){}}}, |
||||
{{game->KEY_SHOULDER,Pressed},{"Scroll",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(-1.0f); |
||||
}}}, |
||||
{{game->KEY_FASTSCROLLUP,Held,InputEngageGroup::NOT_VISIBLE},{"Scroll",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(-1.0f); |
||||
}}}, |
||||
{{game->KEY_FASTSCROLLDOWN,Held,InputEngageGroup::NOT_VISIBLE},{"Scroll",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(1.0f); |
||||
}}}, |
||||
{{game->KEY_SCROLLVERT_R,Analog},{"Scroll",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(game->KEY_SCROLLVERT.Analog()); |
||||
}}}, |
||||
{{game->KEY_SCROLLVERT_L,Analog,InputEngageGroup::NOT_VISIBLE},{"Scroll",[](MenuType type){ |
||||
Component<ScrollableWindowComponent>(type,"Display Text Scroll Window")->Scroll(game->KEY_SCROLLVERT.Analog()); |
||||
}}}, |
||||
{game->KEY_BACK,{"Back",[](MenuType type){ |
||||
Component<MenuComponent>(type,"Go Back Button")->Click(); |
||||
}}}, |
||||
{game->KEY_CONFIRM,{"Back",[](MenuType type){}}}, |
||||
} |
||||
,{ //Button Navigation Rules
|
||||
}); |
||||
} |
@ -0,0 +1,102 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "SoundEffect.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "DEFINES.h" |
||||
#include "util.h" |
||||
|
||||
INCLUDE_game |
||||
INCLUDE_ANIMATION_DATA |
||||
|
||||
DaggerSlash::DaggerSlash(Monster&sourceMonster,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerSlashDistance,bool friendly,Pixel col) |
||||
:Bullet(sourceMonster.GetPos(),{},radius,damage,"goblin_sword_slash.png",upperLevel,false,daggerFrameDuration*ANIMATION_DATA["goblin_sword_slash.png"].GetFrameCountBasedOnAnimationStyle(),true,friendly,col), |
||||
sourceMonster(sourceMonster),frameDuration(daggerFrameDuration),daggerSlashDistance(daggerSlashDistance),facingDir(facingDir),knockbackAmt(knockbackAmt){} |
||||
void DaggerSlash::Update(float fElapsedTime){ |
||||
ANIMATION_DATA["goblin_sword_slash.png"].ChangeFrameDuration(frameDuration); |
||||
pos=sourceMonster.GetPos(); |
||||
#pragma region Dagger slash offset |
||||
switch(facingDir){ |
||||
case Direction::NORTH:{ |
||||
pos+=vf2d{0,-daggerSlashDistance}; |
||||
}break; |
||||
case Direction::EAST:{ |
||||
pos+=vf2d{daggerSlashDistance,0}; |
||||
}break; |
||||
case Direction::SOUTH:{ |
||||
pos+=vf2d{0,daggerSlashDistance}; |
||||
}break; |
||||
case Direction::WEST:{ |
||||
pos+=vf2d{-daggerSlashDistance,0}; |
||||
}break; |
||||
default:ERR(std::format("WARNING! Unknown direction value {} was supplied! THIS SHOULD NOT BE HAPPENING!",int(facingDir))); |
||||
} |
||||
#pragma endregion |
||||
#pragma region Dagger rotation handling |
||||
switch(facingDir){ |
||||
case Direction::NORTH:{ |
||||
vel={0,-0.001f}; |
||||
}break; |
||||
case Direction::EAST:{ |
||||
vel={0.001f,0}; |
||||
}break; |
||||
case Direction::SOUTH:{ |
||||
vel={0,0.001f}; |
||||
}break; |
||||
case Direction::WEST:{ |
||||
vel={-0.001f,0}; |
||||
}break; |
||||
default:ERR(std::format("WARNING! Unknown direction value {} was supplied! THIS SHOULD NOT BE HAPPENING!",int(facingDir))); |
||||
} |
||||
#pragma endregion |
||||
} |
||||
BulletDestroyState DaggerSlash::PlayerHit(Player*player){ |
||||
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,player->GetSizeMult()*0.25f,0.25,vf2d{})); |
||||
player->Knockback(util::pointTo(sourceMonster.GetPos(),player->GetPos())*knockbackAmt); |
||||
Deactivate(); |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
BulletDestroyState DaggerSlash::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult()*0.25f,0.25,vf2d{})); |
||||
monster.Knockback(util::pointTo(sourceMonster.GetPos(),monster.GetPos())*knockbackAmt); |
||||
Deactivate(); |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
|
||||
void DaggerSlash::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,108 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "SoundEffect.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "DEFINES.h" |
||||
#include "util.h" |
||||
|
||||
INCLUDE_game |
||||
INCLUDE_ANIMATION_DATA |
||||
|
||||
DaggerStab::DaggerStab(Monster&sourceMonster,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerStabDistance,const DirectionOffsets offsets,bool friendly,Pixel col) |
||||
:Bullet(sourceMonster.GetPos(),{},radius,damage,"dagger_stab.png",upperLevel,false,daggerFrameDuration*ANIMATION_DATA["dagger_stab.png"].GetFrameCountBasedOnAnimationStyle(),true,friendly,col), |
||||
sourceMonster(sourceMonster),frameDuration(daggerFrameDuration),daggerStabDistance(daggerStabDistance),facingDir(facingDir),daggerPositionOffsets(offsets),knockbackAmt(knockbackAmt){} |
||||
void DaggerStab::Update(float fElapsedTime){ |
||||
ANIMATION_DATA["dagger_stab.png"].ChangeFrameDuration(frameDuration); |
||||
#pragma region Dagger Position Offset |
||||
vf2d daggerOffset=daggerPositionOffsets.offsets[facingDir]; |
||||
pos=sourceMonster.GetPos()+daggerOffset; |
||||
#pragma endregion |
||||
#pragma region Dagger stabbing |
||||
if(lifetime<=ANIMATION_DATA["dagger_stab.png"].GetTotalAnimationDuration()*0.6667f&& |
||||
lifetime>=ANIMATION_DATA["dagger_stab.png"].GetTotalAnimationDuration()*0.3333f){ |
||||
switch(facingDir){ |
||||
case Direction::NORTH:{ |
||||
pos+=vf2d{0,-daggerStabDistance}; |
||||
}break; |
||||
case Direction::EAST:{ |
||||
pos+=vf2d{daggerStabDistance,0}; |
||||
}break; |
||||
case Direction::SOUTH:{ |
||||
pos+=vf2d{0,daggerStabDistance}; |
||||
}break; |
||||
case Direction::WEST:{ |
||||
pos+=vf2d{-daggerStabDistance,0}; |
||||
}break; |
||||
default:ERR(std::format("WARNING! Unknown direction value {} was supplied! THIS SHOULD NOT BE HAPPENING!",int(facingDir))); |
||||
} |
||||
} |
||||
#pragma endregion |
||||
#pragma region Dagger rotation handling |
||||
switch(facingDir){ |
||||
case Direction::NORTH:{ |
||||
vel={0,-0.001f}; |
||||
}break; |
||||
case Direction::EAST:{ |
||||
vel={0.001f,0}; |
||||
}break; |
||||
case Direction::SOUTH:{ |
||||
vel={0,0.001f}; |
||||
}break; |
||||
case Direction::WEST:{ |
||||
vel={-0.001f,0}; |
||||
}break; |
||||
default:ERR(std::format("WARNING! Unknown direction value {} was supplied! THIS SHOULD NOT BE HAPPENING!",int(facingDir))); |
||||
} |
||||
#pragma endregion |
||||
} |
||||
BulletDestroyState DaggerStab::PlayerHit(Player*player){ |
||||
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,player->GetSizeMult()*0.25f,0.25,vf2d{})); |
||||
player->Knockback(util::pointTo(sourceMonster.GetPos(),player->GetPos())*knockbackAmt); |
||||
Deactivate(); |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
BulletDestroyState DaggerStab::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult()*0.25f,0.25,vf2d{})); |
||||
monster.Knockback(util::pointTo(sourceMonster.GetPos(),monster.GetPos())*knockbackAmt); |
||||
Deactivate(); |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
|
||||
void DaggerStab::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,98 @@ |
||||
#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 "BulletTypes.h" |
||||
#include <ranges> |
||||
#include "util.h" |
||||
#include "AdventuresInLestoria.h" |
||||
|
||||
INCLUDE_ANIMATION_DATA |
||||
INCLUDE_game |
||||
|
||||
DeadlyDash::DeadlyDash(vf2d startPos,vf2d endPos,float radius,float afterImagesLingeringTime,int damage,float knockbackAmt,bool upperLevel,bool friendly,float afterImagesSpreadDist,const std::string&animation,Pixel col) |
||||
:animation(animation),startPos(startPos),endPos(endPos),checkRadius(radius),afterImagesSpreadDist(afterImagesSpreadDist),afterImagesLingeringTime(afterImagesLingeringTime),knockbackAmt(knockbackAmt),Bullet({},{},0.f,damage,"circle.png",upperLevel,true,INFINITE,false,friendly,col,{1.f,1.f},0.f){ |
||||
const geom2d::line<float>afterImageLine{startPos,endPos}; |
||||
const int afterImageCount{int(afterImageLine.length()/afterImagesSpreadDist)+1}; |
||||
lifetime=afterImageCount*afterImagesLingeringTime; |
||||
for(int i:std::ranges::iota_view(0,afterImageCount)){ |
||||
const float offsetDist{afterImagesSpreadDist*(i+1)}; |
||||
const vf2d afterImagePos{geom2d::line<float>{startPos,endPos}.rpoint(offsetDist)}; |
||||
if(friendly){ |
||||
const HurtList&hitList{game->HurtNotHit(afterImagePos,radius,damage,this->hitList,OnUpperLevel(),GetZ(),HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)}; |
||||
for(auto&[entityPtr,wasHit]:hitList){ |
||||
if(wasHit){ |
||||
Monster*monster{std::get<Monster*>(entityPtr)}; |
||||
monster->ProximityKnockback(afterImagePos,knockbackAmt); |
||||
this->hitList.insert(monster); |
||||
} |
||||
} |
||||
}else{ |
||||
const HurtList&hitList{game->HurtNotHit(afterImagePos,radius,damage,this->hitList,OnUpperLevel(),GetZ(),HurtType::PLAYER)}; |
||||
for(auto&[entityPtr,wasHit]:hitList){ |
||||
if(wasHit){ |
||||
Player*player{std::get<Player*>(entityPtr)}; |
||||
player->ProximityKnockback(afterImagePos,knockbackAmt); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void DeadlyDash::Draw(const Pixel blendCol)const{ |
||||
const geom2d::line<float>afterImageLine{startPos,endPos}; |
||||
const int afterImageCount{int(afterImageLine.length()/afterImagesSpreadDist)+1}; |
||||
for(int i:std::ranges::iota_view(0,afterImageCount)){ |
||||
const float fadeTimeBegins{(i+1)*afterImagesLingeringTime}; |
||||
uint8_t alpha{255U}; |
||||
if(GetAliveTime()>fadeTimeBegins)alpha=std::max(0.f,util::lerp(255,0,(GetAliveTime()-fadeTimeBegins)/afterImagesLingeringTime)); |
||||
const Animate2D::FrameSequence&animation{ANIMATION_DATA[this->animation]}; |
||||
const float animationFrameTimer{i*animation.m_fFrameDuration}; |
||||
|
||||
const float offsetDist{afterImagesSpreadDist*(i+1)}; |
||||
const vf2d drawPos{geom2d::line<float>{startPos,endPos}.rpoint(offsetDist)}; |
||||
const Animate2D::Frame&frame{animation.GetFrame(animationFrameTimer)}; |
||||
game->SetDecalMode(DecalMode::ADDITIVE); |
||||
game->view.DrawPartialRotatedDecal(drawPos,frame.GetSourceImage()->Decal(),0.f,frame.GetSourceRect().size/2,frame.GetSourceRect().pos,frame.GetSourceRect().size,{game->GetPlayer()->GetSizeMult(),game->GetPlayer()->GetSizeMult()},{col.r,col.g,col.b,alpha}); |
||||
game->SetDecalMode(DecalMode::NORMAL); |
||||
} |
||||
} |
||||
|
||||
void DeadlyDash::ModifyOutgoingDamageData(HurtDamageInfo&data){ |
||||
//NOTE: Despite it looking like we modify the damage here, the radius passed to Bullet is 0, so this shouldn't matter anyways, the damage happens in the check in Update()
|
||||
if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY; |
||||
} |
@ -0,0 +1,78 @@ |
||||
#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 "MenuComponent.h" |
||||
#include "GameState.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "Unlock.h" |
||||
|
||||
INCLUDE_game |
||||
INCLUDE_GFX |
||||
|
||||
void Menu::InitializeDeathWindow(){ |
||||
Menu*deathWindow=CreateMenu(MenuType::DEATH,CENTERED,vi2d{96,52}); |
||||
|
||||
deathWindow->ADD("Retry Button",MenuComponent)(geom2d::rect<float>{{6.f,0.f},{84.f,24.f}},"Retry",[](MenuFuncData data){ |
||||
Menu::CloseAllMenus(); |
||||
Inventory::GivePlayerLoadoutItemsUsed(); |
||||
GameState::ChangeState(States::GAME_RUN); |
||||
return true; |
||||
},ButtonAttr::FIT_TO_LABEL)END; |
||||
deathWindow->ADD("Return to Camp Button",MenuComponent)(geom2d::rect<float>{{6.f,28.f},{84.f,24.f}},"Return to Camp",[](MenuFuncData data){ |
||||
Component<MenuComponent>(MenuType::PAUSE,"Return to Camp Button")->Click(); |
||||
return true; |
||||
},ButtonAttr::FIT_TO_LABEL)END; |
||||
|
||||
deathWindow->SetupKeyboardNavigation( |
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
returnData="Retry Button"; |
||||
}, |
||||
{ //Button Key
|
||||
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}}, |
||||
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}}, |
||||
} |
||||
,{ //Button Navigation Rules
|
||||
{"Retry Button",{ |
||||
.up="Return to Camp Button", |
||||
.down="Return to Camp Button",}}, |
||||
{"Return to Camp Button",{ |
||||
.up="Retry Button", |
||||
.down="Retry Button",}}, |
||||
}); |
||||
} |
@ -0,0 +1,65 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "util.h" |
||||
|
||||
INCLUDE_GFX |
||||
INCLUDE_game |
||||
|
||||
Debris::Debris(const vf2d pos,const vf2d vel,const int damage,const float radius,const float knockbackAmt,const float rotSpd,const float lifetime,bool upperLevel,bool friendly,Pixel col,const vf2d scale) |
||||
:Bullet(pos,vel,radius,damage,"commercial_assets/wind_solid_objects.png",upperLevel,false,lifetime,false,friendly,col,scale),knockbackAmt(knockbackAmt),rotatingSpd(rotSpd),randomFrame(util::random(GFX["commercial_assets/wind_solid_objects.png"].Sprite()->Size().x/24)){} |
||||
void Debris::Update(float fElapsedTime){ |
||||
image_angle+=rotatingSpd*fElapsedTime; |
||||
} |
||||
BulletDestroyState Debris::PlayerHit(Player*player){ |
||||
player->Knockback(vel.norm()*knockbackAmt); |
||||
fadeOutTime=0.5f; |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
BulletDestroyState Debris::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||
monster.Knockback(vel.norm()*knockbackAmt); |
||||
fadeOutTime=0.5f; |
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
} |
||||
void Debris::Draw(const Pixel blendCol)const{ |
||||
game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY,GetFrame().GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2+image_angle:image_angle,{12,12},vf2d{randomFrame*24.f,0.f},{24,24},scale,blendCol); |
||||
} |
||||
|
||||
void Debris::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,45 @@ |
||||
#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 |
||||
|
||||
enum class Direction{ |
||||
NORTH, |
||||
EAST, |
||||
SOUTH, |
||||
WEST |
||||
}; |
@ -0,0 +1,41 @@ |
||||
#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 "Monster.h" |
||||
|
||||
void Monster::STRATEGY::DONOTHING(Monster&m,float fElapsedTime,std::string strategy){} |
@ -0,0 +1,52 @@ |
||||
#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 "Effect.h" |
||||
|
||||
|
||||
struct ExpandingRing:Effect{ |
||||
inline ExpandingRing(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d expandingSize,vf2d size={1.f,1.f},float fadeout=0.f,vf2d spd={},Pixel col=WHITE,float rotation=0.f,float rotationSpd=0.f,bool additiveBlending=false) |
||||
:expandingSize(expandingSize),Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){} |
||||
inline bool Update(float fElapsedTime){ |
||||
size+=expandingSize*fElapsedTime; |
||||
return true; |
||||
} |
||||
private: |
||||
const vf2d expandingSize; |
||||
}; |
@ -0,0 +1,120 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include <ranges> |
||||
#include "config.h" |
||||
#include "util.h" |
||||
#include "SoundEffect.h" |
||||
|
||||
INCLUDE_ANIMATION_DATA |
||||
INCLUDE_game |
||||
|
||||
ExplosiveTrap::ExplosiveTrap(vf2d pos,float radius,float explosionRadius,float automaticDetonationTime,int damage,float fadeinTime,float fadeoutTime,float activationWaitTime,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale) |
||||
:activationWaitTime(activationWaitTime),automaticDetonationTime(automaticDetonationTime),activationRadius(radius),explosionRadius(explosionRadius),Bullet(pos,{},0.f,damage,"Ability Icons/explosive_trap.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,0.f,"Trap Hit"){ |
||||
fadeInTime=fadeinTime; |
||||
animation.AddState("explosive_trap.png",ANIMATION_DATA["explosive_trap.png"]); |
||||
if(!friendly)ERR("WARNING! Trying to use unimplemented enemy version of the Explosive Trap Bullet!"); |
||||
} |
||||
|
||||
void ExplosiveTrap::Update(float fElapsedTime){ |
||||
const bool trapActivated{radius==activationRadius}; |
||||
|
||||
if(IsDeactivated())return; |
||||
|
||||
if(!trapActivated){ |
||||
activationWaitTime-=fElapsedTime; |
||||
if(activationWaitTime<=0.f){ |
||||
radius=activationRadius; |
||||
animation.ChangeState(internal_animState,"explosive_trap.png"); |
||||
beepCount=std::min(4U,beepCount+1U); |
||||
SoundEffect::PlaySFX(std::format("Beep {}",beepCount),pos); |
||||
lastBeepTime=1.f; |
||||
} |
||||
}else{ |
||||
automaticDetonationTime-=fElapsedTime; |
||||
lastBeepTime-=fElapsedTime; |
||||
if(lastBeepTime<=0.f){ |
||||
beepCount=std::min(4U,beepCount+1U); |
||||
SoundEffect::PlaySFX(std::format("Beep {}",beepCount),pos); |
||||
lastBeepTime=1.f; |
||||
} |
||||
if(IsActivated()&&automaticDetonationTime<=0.f)Detonate(); |
||||
} |
||||
} |
||||
|
||||
void ExplosiveTrap::ModifyOutgoingDamageData(HurtDamageInfo&data){ |
||||
data.hurtFlags|=HurtFlag::PLAYER_ABILITY; |
||||
} |
||||
|
||||
BulletDestroyState ExplosiveTrap::PlayerHit(Player*player){ |
||||
fadeOutTime=0.5f; |
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
||||
|
||||
void ExplosiveTrap::Detonate(){ |
||||
fadeOutTime=0.5f; |
||||
const HurtList list{game->HurtNotHit(pos,"Trapper.Ability 3.Explosion Radius"_F/100.f*24,damage,hitList,OnUpperLevel(),GetZ(),HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)}; |
||||
for(const auto&[targetPtr,wasHit]:list){ |
||||
if(wasHit){ |
||||
std::get<Monster*>(targetPtr)->ApplyMark("Trapper.Ability 3.Explosion Mark Stack Time"_F,"Trapper.Ability 3.Explosion Mark Stack Increase"_I); |
||||
std::get<Monster*>(targetPtr)->ProximityKnockback(pos,"Trapper.Ability 3.Explosion Knockback Amount"_F); |
||||
std::get<Monster*>(targetPtr)->Knockup("Trapper.Ability 3.Explosion Knockup Amount"_F); |
||||
} |
||||
} |
||||
|
||||
std::unique_ptr<Effect>explodeEffect{std::make_unique<Effect>(pos,ANIMATION_DATA["explosionframes.png"].GetTotalAnimationDuration()-0.2f,"explosionframes.png",OnUpperLevel(),scale*2.f,0.2f,vf2d{0,-6.f},WHITE,util::random(2*PI),0.f)}; |
||||
explodeEffect->scaleSpd={0.125f,0.125f}; |
||||
game->AddEffect(std::move(explodeEffect)); |
||||
SoundEffect::PlaySFX("Explosion",pos); |
||||
Deactivate(); |
||||
} |
||||
|
||||
BulletDestroyState ExplosiveTrap::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||
for(int i:std::ranges::iota_view(0,"Trapper.Ability 3.Trap Mark Trigger Count"_I-1)){ |
||||
monster.Hurt(0,monster.OnUpperLevel(),monster.GetZ(),HurtFlag::PLAYER_ABILITY);//Triggers mark multiple times after the first mark.
|
||||
} |
||||
|
||||
monster.ProximityKnockback(pos,"Trapper.Ability 3.Explosion Knockback Amount"_F); |
||||
monster.Knockup("Trapper.Ability 3.Explosion Knockup Amount"_F); |
||||
|
||||
Detonate(); |
||||
|
||||
return BulletDestroyState::KEEP_ALIVE; |
||||
} |
@ -0,0 +1,92 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "DEFINES.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "util.h" |
||||
#include <ranges> |
||||
#include "SoundEffect.h" |
||||
|
||||
INCLUDE_game |
||||
|
||||
FallingStone::FallingStone(vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple,float knockbackAmt,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle,float spellCircleRotation,float spellCircleRotationSpd,Pixel insigniaCol,float insigniaRotation,float insigniaRotationSpd) |
||||
:Bullet(targetPos,vel,radius,damage,"rock.png",upperLevel,false,lifetime+0.1f,false,friendly,col,scale,image_angle),targetPos(targetPos),zVel(zVel),indicatorDisplayTime(indicatorDisplayTime),knockbackAmt(knockbackAmt), |
||||
indicator(targetPos,lifetime+0.1f,"range_indicator.png","spell_insignia.png",upperLevel,radius/12.f,0.5f,{},col,spellCircleRotation,spellCircleRotationSpd,false,radius/12.f,0.f,{},insigniaCol,insigniaRotation,insigniaRotationSpd,false){ |
||||
pos+=-vel*lifetime; |
||||
z=-zVel*lifetime; |
||||
} |
||||
|
||||
void FallingStone::Update(float fElapsedTime){ |
||||
z+=zVel*fElapsedTime; |
||||
lastTrailEffect-=fElapsedTime; |
||||
if(z<=0.f){ |
||||
z=0.f; |
||||
vel={}; |
||||
if(IsActivated()){ |
||||
fadeOutTime=0.5f; |
||||
SoundEffect::PlaySFX("Stone Land",pos); |
||||
if(friendly){ |
||||
for(auto&[monsterPtr,hurt]:game->Hurt(targetPos,radius,damage,OnUpperLevel(),z,HurtType::MONSTER)){ |
||||
if(hurt)std::get<Monster*>(monsterPtr)->ApplyIframes(0.1f); |
||||
} |
||||
} |
||||
else{ |
||||
for(auto&[playerPtr,hurt]:game->Hurt(targetPos,radius,damage,OnUpperLevel(),z,HurtType::PLAYER)){ |
||||
if(hurt)std::get<Player*>(playerPtr)->ApplyIframes(0.1f); |
||||
} |
||||
} |
||||
game->ProximityKnockback(targetPos,radius,knockbackAmt,HurtType::PLAYER|HurtType::MONSTER); |
||||
for(int i:std::ranges::iota_view(0,30))game->AddEffect(std::make_unique<Effect>(pos-vf2d{0.f,GetZ()},util::random_range(0.05f,0.2f),"circle_outline.png",OnUpperLevel(),util::random_range(0.5f,1.f),0.2f,vf2d{util::random_range(-10.f,10.f),util::random_range(-3.f,0.f)},PixelLerp(BLACK,col,util::random(1.f)),0.f,0.f,true)); |
||||
Deactivate(); |
||||
} |
||||
}else{ |
||||
if(lastTrailEffect<=0.f){ |
||||
game->AddEffect(std::make_unique<Effect>(pos-vf2d{0.f,GetZ()},util::random_range(0.05f,0.2f),"circle_outline.png",OnUpperLevel(),util::random_range(0.5f,1.f),0.2f,vf2d{util::random_range(-3.f,3.f),util::random_range(-3.f,3.f)},PixelLerp(BLACK,col,util::random(1.f)),0.f,0.f,true)); |
||||
} |
||||
} |
||||
indicator.Update(fElapsedTime); |
||||
} |
||||
void FallingStone::Draw(const Pixel blendCol)const{ |
||||
if(lifetime<=indicatorDisplayTime){ |
||||
indicator.Draw(); |
||||
} |
||||
Bullet::Draw(blendCol); |
||||
} |
||||
|
||||
void FallingStone::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,47 @@ |
||||
#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 "BulletTypes.h" |
||||
#include "Player.h" |
||||
|
||||
Feather::Feather(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle) |
||||
:Bullet(pos,vel,radius,damage,"feather.png",upperLevel,hitsMultiple,lifetime,true,friendly,col){ |
||||
SetBulletType(BulletType::FEATHER); |
||||
} |
||||
|
||||
void Feather::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,107 @@ |
||||
#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 "MenuComponent.h" |
||||
#include "AdventuresInLestoria.h" |
||||
#include "drawutil.h" |
||||
#include "util.h" |
||||
#include "ScrollableWindowComponent.h" |
||||
|
||||
using A=Attribute; |
||||
|
||||
INCLUDE_game |
||||
|
||||
//A button that directly draws in a location in the game.
|
||||
class FloatingMenuComponent:public MenuComponent{ |
||||
public: |
||||
inline FloatingMenuComponent(geom2d::rect<float>rect,std::string label,MenuFunc onClick,ButtonAttr attributes=ButtonAttr::NONE) |
||||
:MenuComponent(rect,label,onClick,attributes){}; |
||||
|
||||
inline virtual bool HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton)override final{ |
||||
return true; |
||||
}; |
||||
|
||||
inline virtual bool GetHoverState(AiL*game){ |
||||
if(!parentComponent.expired()){ |
||||
return parentComponent.lock()->GetHoverState(game,this); |
||||
}else{ |
||||
return geom2d::overlaps(geom2d::rect<float>{rect.pos,rect.size},game->GetMousePos()); |
||||
} |
||||
} |
||||
|
||||
inline virtual void DrawDecal(ViewPort&window,bool focused)override final{ |
||||
if(background){ |
||||
Pixel backCol=PixelLerp(Menu::themes[Menu::themeSelection].GetButtonCol(),Menu::themes[Menu::themeSelection].GetHighlightCol(),hoverEffect/"ThemeGlobal.HighlightTime"_F); |
||||
if(grayedOut){ |
||||
backCol=DARK_GREY; |
||||
} |
||||
game->FillRectDecal(rect.pos+V(A::DRAW_OFFSET),rect.size,backCol); |
||||
} |
||||
if(selected&&selectionType==HIGHLIGHT){ |
||||
game->FillRectDecal(rect.pos+V(A::DRAW_OFFSET),rect.size,PixelLerp(Menu::themes[Menu::themeSelection].GetButtonCol(),Menu::themes[Menu::themeSelection].GetHighlightCol(),util::lerp(0.75f,1.0f,hoverEffect/"ThemeGlobal.HighlightTime"_F))); |
||||
} |
||||
if(border){ |
||||
game->FillRectDecal(rect.pos+V(A::DRAW_OFFSET),{rect.size.x+1,1},borderCol); |
||||
game->FillRectDecal(rect.pos+V(A::DRAW_OFFSET),{1,rect.size.y+1},borderCol); |
||||
game->FillRectDecal(rect.pos+V(A::DRAW_OFFSET)+vf2d{rect.size.x-1+1,0},{1,rect.size.y+1},borderCol); |
||||
game->FillRectDecal(rect.pos+V(A::DRAW_OFFSET)+vf2d{0,rect.size.y-1+1},{rect.size.x+1,1},borderCol); |
||||
} |
||||
if(showDefaultLabel){ |
||||
vf2d adjustedScale=labelScaling; |
||||
vi2d labelTextSize=vf2d(game->GetTextSizeProp(label))*adjustedScale; |
||||
|
||||
if(fitToLabel){ |
||||
float sizeRatio=(vf2d(labelTextSize).x)/(rect.size.x-2); |
||||
if(sizeRatio>1){ |
||||
adjustedScale.x/=sizeRatio; |
||||
labelTextSize.x/=sizeRatio; |
||||
} |
||||
} |
||||
game->DrawStringPropDecal(rect.pos+V(A::DRAW_OFFSET)+rect.size/2-vf2d(labelTextSize)/2.f,label,WHITE,adjustedScale); |
||||
} |
||||
if(selected){ |
||||
switch(selectionType){ |
||||
case CROSSHAIR:drawutil::DrawCrosshairDecal(game,{rect.pos+V(A::DRAW_OFFSET),rect.size},0);break; |
||||
case INNER_BOX:game->DrawRectDecal(rect.pos+V(A::DRAW_OFFSET)+vi2d{1,1},rect.size-vi2d{2,2});break; |
||||
case HIGHLIGHT:break;//Not used.
|
||||
case SelectionType::NONE:break;//Displays nothing.
|
||||
default:ERR("Undefined selection type selected: "<<int(selectionType)); |
||||
} |
||||
} |
||||
} |
||||
}; |
@ -0,0 +1,58 @@ |
||||
#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 "olcPixelGameEngine.h" |
||||
#include "Effect.h" |
||||
#include "DEFINES.h" |
||||
#include "AdventuresInLestoria.h" |
||||
|
||||
INCLUDE_game |
||||
|
||||
ForegroundEffect::ForegroundEffect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) |
||||
:Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){} |
||||
ForegroundEffect::ForegroundEffect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) |
||||
:Effect(pos,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){} |
||||
void ForegroundEffect::Draw()const{ |
||||
if(additiveBlending)game->SetDecalMode(DecalMode::ADDITIVE); |
||||
if(fadeout==0){ |
||||
game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,col); |
||||
} else { |
||||
game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,{col.r,col.g,col.b,uint8_t(fadeout/original_fadeOutTime*255)}); |
||||
} |
||||
game->SetDecalMode(DecalMode::NORMAL); |
||||
} |
@ -0,0 +1,89 @@ |
||||
#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 "AdventuresInLestoria.h" |
||||
#include "DEFINES.h" |
||||
#include "Monster.h" |
||||
#include "MonsterStrategyHelpers.h" |
||||
#include "BulletTypes.h" |
||||
|
||||
INCLUDE_ANIMATION_DATA |
||||
INCLUDE_BULLET_LIST |
||||
INCLUDE_game |
||||
|
||||
using A=Attribute; |
||||
|
||||
void Monster::STRATEGY::GOBLIN_BOAR_RIDER(Monster&m,float fElapsedTime,std::string strategy){ |
||||
//We have access to GOBLIN_BOW_MOUNTED_X and GOBLIN_BOW_ATTACK_X
|
||||
if(!m.B(A::INITIALIZED_MOUNTED_MONSTER)){ |
||||
m.B(A::INITIALIZED_MOUNTED_MONSTER)=true; |
||||
m.F(A::PERCEPTION_LEVEL)=ConfigFloat("Starting Perception Level"); |
||||
m.internal_mounted_animState=Animate2D::AnimationState{}; |
||||
m.mounted_animation=Animate2D::Animation<std::string>{}; |
||||
|
||||
for(bool firstAnimation=true;const std::string&animation:Config("Imposed Monster Animations").GetValues()){ |
||||
m.mounted_animation.value().AddState(animation,ANIMATION_DATA.at(animation)); |
||||
|
||||
if(firstAnimation)m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),animation); |
||||
firstAnimation=false; |
||||
} |
||||
m.mountedSprOffset=ConfigVec("Imposed Monster Offset"); |
||||
m.deathData.push_back(DeathSpawnInfo{ConfigString("Spawned Monster"),1U}); |
||||
} |
||||
|
||||
BOAR(m,fElapsedTime,"Boar"); |
||||
|
||||
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime; |
||||
if(m.F(A::SHOOT_TIMER)>0.f){ |
||||
m.F(A::SHOOT_TIMER)-=fElapsedTime; |
||||
if(m.F(A::SHOOT_TIMER)<=0.f){ |
||||
geom2d::line pointTowardsPlayer(m.GetPos(),game->GetPlayer()->GetPos()); |
||||
vf2d extendedLine=pointTowardsPlayer.upoint(1.1f); |
||||
CreateBullet(Arrow)(m.GetPos(),extendedLine,pointTowardsPlayer.vector().norm()*ConfigFloat("Arrow Spd"),"goblin_arrow.png",ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet; |
||||
Arrow&arrow=static_cast<Arrow&>(*BULLET_LIST.back()); |
||||
arrow.PointToBestTargetPath(m.F(A::PERCEPTION_LEVEL)); |
||||
|
||||
m.F(A::ATTACK_COOLDOWN)=0.f; |
||||
m.F(A::PERCEPTION_LEVEL)=std::min(ConfigFloat("Maximum Perception Level"),m.F(A::PERCEPTION_LEVEL)+ConfigFloat("Perception Level Increase")); |
||||
m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),std::format("GOBLIN_BOW_MOUNTED_{}",int(m.GetFacingDirection()))); |
||||
} |
||||
}else |
||||
if(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Reload Time")){ |
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time"); |
||||
m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),std::format("GOBLIN_BOW_ATTACK_{}",int(m.GetFacingDirection()))); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue