Compare commits
509 Commits
master
...
removeExpo
Author | SHA1 | Date | |
---|---|---|---|
|
c5769ad0a8 | ||
1f9d54f0cb | |||
b9f5b80300 | |||
3c40b3ee41 | |||
4774f94d9b | |||
eae5c2915d | |||
|
6c7e7a311f | ||
|
23654dec15 | ||
56f731db36 | |||
9640014442 | |||
830b893b1c | |||
13eeb46000 | |||
47530d4822 | |||
|
795c612f16 | ||
|
b6638d3b97 | ||
1ad54ee167 | |||
b799a8ab4b | |||
688514fd97 | |||
0a73d825cf | |||
2ccb63a74d | |||
db50617371 | |||
5a4aa61136 | |||
14bad7cf69 | |||
7100bec234 | |||
3fecf73b3c | |||
a5f7973c4d | |||
f27caf4382 | |||
d72113506e | |||
223bf1b2fe | |||
41aab6d8dd | |||
e748bff898 | |||
5ae21944d6 | |||
5c1144a3bf | |||
ce043c19cb | |||
|
5a7c60ac7c | ||
23c2bfb45c | |||
d7351bd872 | |||
0df19d7b56 | |||
05d81e4446 | |||
0cb52d1c0f | |||
8025680617 | |||
345a4abb48 | |||
c3e7c62bd3 | |||
a1eb109e70 | |||
ee79ef225b | |||
efc976758b | |||
b91b1a8362 | |||
aefa81d71f | |||
2cb6fe9d87 | |||
a9b59b5eba | |||
2a50695f51 | |||
11691e5cba | |||
84e823aeb8 | |||
3a40d44fd1 | |||
8c9d8cdcde | |||
e0178fb08b | |||
64f726b50d | |||
55b5cb4738 | |||
14f9a71346 | |||
9d290aae5a | |||
6ce8ff535a | |||
5e6f46b85a | |||
747c9bbffe | |||
b2fc642723 | |||
78804d666b | |||
|
11b45499fd | ||
|
7266e5eb2e | ||
1450d01edf | |||
fe91e36b60 | |||
ed4a755d1c | |||
352847ab27 | |||
d2841e0e0c | |||
26af9def75 | |||
6becdd4f6b | |||
c80ec17999 | |||
5f40ee8306 | |||
34510e732a | |||
c307178d95 | |||
5b32eb268b | |||
38710151c4 | |||
210dd3b9de | |||
b63372230e | |||
ed5ab319de | |||
421ba4bc4a | |||
191aa24dc2 | |||
ddcec79102 | |||
a308fcc4d8 | |||
9551d71f75 | |||
784aa022e2 | |||
3560ddc31e | |||
9d95796062 | |||
01d61c95f4 | |||
29f97a901a | |||
3e8c7ddfc0 | |||
1e4266e0cd | |||
9dbc5b46f8 | |||
fab21ab693 | |||
ba2d568f77 | |||
f7ec3ef630 | |||
a8d04d258f | |||
|
41e8384012 | ||
|
256e41599c | ||
|
37f46c9d91 | ||
|
2f6c6f5c96 | ||
|
21d4cbcaae | ||
|
7484c94708 | ||
a7b9017fa7 | |||
c7fca6a694 | |||
|
efcaac792f | ||
|
a25545bd99 | ||
|
2ef2a9b1f9 | ||
|
d0d96c83ce | ||
4828b95a8c | |||
|
ad4549cc5d | ||
|
48718453df | ||
|
3ab6ef678d | ||
|
d45e9e2dab | ||
|
2cd2104914 | ||
6ea8588feb | |||
048f2cce74 | |||
ff358a7598 | |||
fd62ab749c | |||
6cb67ccaa6 | |||
afff03e201 | |||
d8c2c615dc | |||
d6d02cc986 | |||
f355b01171 | |||
e4a1cddd77 | |||
e33b3552f1 | |||
dcea90fedb | |||
abb18767d4 | |||
1f635a667d | |||
ddeef9973a | |||
2a8bc4cdcb | |||
|
ef0bf13747 | ||
|
98f30c0ad4 | ||
a82d06940d | |||
8699f1b808 | |||
64823fb2f3 | |||
dc2394cd17 | |||
c83aff8962 | |||
c6c3e5cf72 | |||
0eb28bd029 | |||
5f77321d3d | |||
9dcfa55407 | |||
c8e74cb647 | |||
a740d6eb01 | |||
cd1a734b19 | |||
0fd1d5ee8a | |||
|
5cc1bedeac | ||
|
a11240c372 | ||
|
77da812319 | ||
eb3562eca3 | |||
c625b0767e | |||
4a77391dec | |||
09d3b2b233 | |||
1066a7a37d | |||
01df50e8da | |||
c738bdac0b | |||
96790c3073 | |||
6dc986d424 | |||
597e861677 | |||
5af5cac7e8 | |||
0dd8ca5ae9 | |||
cea4372737 | |||
8c5438b73b | |||
82e8dca6ee | |||
5f0a516be5 | |||
15cf736be0 | |||
5918846fbd | |||
cfbd4dce97 | |||
c55a3a1e6b | |||
f401138006 | |||
4af39499e4 | |||
b6c88a0caa | |||
cd10f12590 | |||
68778bac0a | |||
8a12cc0c66 | |||
a8bb30e12f | |||
05caa062e1 | |||
5150f4f218 | |||
92a3c463f7 | |||
|
f1ec24df66 | ||
d500e48e67 | |||
df56fec448 | |||
35bcdb3ec2 | |||
1f8c62188b | |||
d3374062fe | |||
3a9bd4afff | |||
bbdef1cdfb | |||
b956a103df | |||
ad14544418 | |||
e1ed802fed | |||
f115786584 | |||
e648c31dd2 | |||
2b6201f641 | |||
17ab92eeee | |||
c7b5660a70 | |||
01ea60b3a5 | |||
862d047b06 | |||
|
f3ca091124 | ||
626a9ee742 | |||
4720f59ba7 | |||
|
a075ed79f0 | ||
6902852b51 | |||
21b8a9c3cb | |||
64ea11b4af | |||
38efe127be | |||
153589a232 | |||
45be6e80e6 | |||
fa0caa5fa9 | |||
177d62af58 | |||
ea2dc34bb6 | |||
29a36770aa | |||
|
6bd76453f3 | ||
2903c45d16 | |||
c2b2652810 | |||
|
d8777cfff6 | ||
39303e2438 | |||
42b8cf5da7 | |||
7292094358 | |||
5ef4b90b29 | |||
|
6107d98075 | ||
081bc71612 | |||
e9ed495d39 | |||
b2b1fa81cf | |||
3f463bcf2f | |||
ee2a66f5fe | |||
|
8c8e05c15e | ||
79b595bbef | |||
f5bfc0d34d | |||
cb35d2c1fa | |||
beb1bdfc79 | |||
5c7e5a3ab3 | |||
c83f84f29c | |||
3051422383 | |||
cc3a95094a | |||
365d66098e | |||
253bbc610e | |||
8827d1ca4e | |||
7375d37d99 | |||
280a321abb | |||
8a18e0bbaf | |||
dbe52a2a4a | |||
16c52bf397 | |||
70906d5594 | |||
f3f91f560e | |||
cdda8c9a99 | |||
|
b9f64982f8 | ||
76c1396fec | |||
00b18355c1 | |||
6b6bdf741b | |||
c37d52d186 | |||
7320fe9348 | |||
e294c56c32 | |||
69ee9ce5be | |||
d40ab48e78 | |||
|
392afbb8be | ||
f2c18a76a1 | |||
5912efeb4f | |||
045e10b888 | |||
adb35f2ce5 | |||
119efab573 | |||
016251fde3 | |||
f5acd59b5b | |||
42aab4b5e9 | |||
4fb84cf365 | |||
|
9d0dfe9562 | ||
|
f10f3a0fe4 | ||
ec83c6351a | |||
95c43b06b0 | |||
236b212eab | |||
a88b0b810b | |||
d10127b721 | |||
35acba879e | |||
50515c515c | |||
f769a77e42 | |||
bdf126820a | |||
e0aea34a43 | |||
512c46ea8a | |||
4c8a251e33 | |||
cc8f79d34a | |||
|
1a48066922 | ||
ef7fc2c826 | |||
5446da7ef8 | |||
f91ddf542d | |||
3d897b1aba | |||
bba33d43d2 | |||
25075217dd | |||
145ecdaa01 | |||
4a8ccd2e20 | |||
|
00fc1b4a92 | ||
317ea5a672 | |||
2e39c0a3f5 | |||
64a25dc66c | |||
f2b17e8c85 | |||
5d09e8d66a | |||
df28a12c63 | |||
91a660c5b4 | |||
|
626cf84d99 | ||
60696e9956 | |||
089e84134f | |||
c378939994 | |||
4306f4eab8 | |||
254b4b3ea0 | |||
8c759f0367 | |||
90beda6807 | |||
d2079f2a90 | |||
|
5abd80932e | ||
37a8847e4e | |||
99d449400b | |||
28003c88a0 | |||
020f47f1f9 | |||
3f13c574c0 | |||
35c269bd8b | |||
|
c864e0f155 | ||
|
24888f35db | ||
|
23f17ec459 | ||
bcbe2150ee | |||
c362721096 | |||
02e7d5665a | |||
d6e140cfb4 | |||
65c3b2cdb6 | |||
8b3113fbde | |||
7cdcebc71e | |||
|
ba5c187ec7 | ||
|
cf6788a418 | ||
78082c6bd4 | |||
0a42be844b | |||
fcb557a2d1 | |||
ddfe0cf13d | |||
71acd6ef7d | |||
3c2c19f822 | |||
ad79910848 | |||
4c81506c64 | |||
282f918d1d | |||
4870e5952b | |||
826aae50f0 | |||
e052f6e089 | |||
2599cfd14b | |||
dfff1762f3 | |||
e15f23bad1 | |||
1c72a86945 | |||
21e0cd5b65 | |||
68e4934355 | |||
3256a686a3 | |||
d1f95e03c9 | |||
9a3cbc40f9 | |||
523050d3ab | |||
c3f8c81a0d | |||
c5e0df5f44 | |||
d24df058b1 | |||
a3fe98f542 | |||
ae08ab316a | |||
794d38cdb3 | |||
18bfa4a26b | |||
daa90538ad | |||
ea74eec6f8 | |||
f2929d915d | |||
32d681f130 | |||
ee382488f9 | |||
02933fdc38 | |||
9ca52cbead | |||
|
78c591f64e | ||
3c315da193 | |||
d348d6505c | |||
88572368b5 | |||
561f403cd2 | |||
b9f9d0ceb2 | |||
6e03f99543 | |||
758696d28b | |||
cb39f0859b | |||
8a3a101d28 | |||
81d6e103cc | |||
b4c9b29be4 | |||
c291aff00a | |||
0830185829 | |||
37d738fbf9 | |||
2f8dee095f | |||
bb86146a04 | |||
5016dc9b66 | |||
db5b071689 | |||
cd1e989e5e | |||
ecc0f1f733 | |||
bf6c78c286 | |||
2f6e3b1718 | |||
|
5f56b14b4c | ||
02ef18826d | |||
3be02315a5 | |||
bdad7cf186 | |||
fa79df59de | |||
195ec1a0f8 | |||
8011310d96 | |||
06ee669034 | |||
6593f43b96 | |||
7aa65eae72 | |||
6cd34639ef | |||
8768b89f38 | |||
03ac0be3f3 | |||
b13fa843de | |||
48464e3d8b | |||
7a683fffa6 | |||
f73cadcbff | |||
51964406a7 | |||
a71978c819 | |||
f8e7144a02 | |||
|
b6e6cb58b1 | ||
|
d1c8b1d06b | ||
|
c360898800 | ||
15d5d2fb4a | |||
585cd209be | |||
de82e3f913 | |||
cb0e6105f2 | |||
9133ab9e94 | |||
0484908b37 | |||
4d912ab259 | |||
18699fb4ca | |||
48ce0c6cd9 | |||
25654fc0da | |||
454963c80e | |||
d4d83a1e44 | |||
6c59787373 | |||
965029e224 | |||
ecab0ae259 | |||
751513135e | |||
1abbf05298 | |||
5dc5a74ed6 | |||
60e5ca2033 | |||
a687bbe455 | |||
2de0ba3810 | |||
5a8a3d64e9 | |||
13854574c9 | |||
72c5a7394b | |||
468302833f | |||
|
63fc61cc09 | ||
da1774bfdd | |||
3f352eeeeb | |||
6ff939e491 | |||
588e184764 | |||
68bdb6bc26 | |||
dec1f0516f | |||
6e4f206391 | |||
5fc5169ddd | |||
59eb24b65b | |||
822bab3318 | |||
7ada663056 | |||
227d43bd77 | |||
8ec8a5a263 | |||
0fd57311be | |||
05e59f8ca4 | |||
3dcce251ef | |||
f272e774f5 | |||
a6bd3c0c43 | |||
a8b5b3f84a | |||
b3a632a375 | |||
592db62c9a | |||
09a0a16bdc | |||
462710a44d | |||
93081fe7f0 | |||
9a6f710201 | |||
72f4693cfc | |||
5992adbc5f | |||
2d3b506556 | |||
3aaa7d03e4 | |||
a9a640be82 | |||
79d50fdf96 | |||
a506c47aea | |||
78cbad9297 | |||
886eff7d7a | |||
eb0477a787 | |||
e9b2773f7f | |||
686fa7916c | |||
|
ff01195c01 | ||
f8408dc530 | |||
613e2be21f | |||
84e713b9a2 | |||
f1dc45ea95 | |||
519a373fa8 | |||
|
ab01cc6acc | ||
|
1b2831bbb2 | ||
8be4f12212 | |||
8b7703f457 | |||
|
a53aa64b07 | ||
94f314a7aa | |||
469c360e51 | |||
a51d3bc898 | |||
aa4d35d754 | |||
93e5921e57 | |||
2d0702af2c | |||
543a0b6792 | |||
f6c822f868 | |||
4b3b36bdfd | |||
fecabce054 | |||
85dcc64e2e | |||
0c619aa4f6 | |||
fa8dfd3c6f | |||
3261ddf6f5 | |||
3faff12015 | |||
b96b5735e9 | |||
03e6d3bd80 | |||
08aa7a309d | |||
e8ac5a957f | |||
53b5c1f967 | |||
b0f0e7dcdd | |||
2253c14f4c | |||
efa4f6dc95 | |||
eb1133c2e2 | |||
35e8a70059 | |||
ae26813f05 |
@ -1,25 +0,0 @@
|
||||
name: Emscripten Build
|
||||
run-name: Emscripten build initiated by ${{ gitea.actor }} for ${{ gitea.repository }}
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
Build_and_Deploy_Web_Build:
|
||||
runs-on: emscripten
|
||||
container:
|
||||
volumes:
|
||||
- /home/sigonasr2/divar/server/files/web:/web
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
- name: Generate emcmake configuration
|
||||
run: |
|
||||
emcmake cmake . -B ${{ gitea.event.repository.name }}
|
||||
- name: Build Project
|
||||
run: cmake --build ${{ gitea.event.repository.name }}/ -j 8
|
||||
- name: Move Files and Finalize
|
||||
run: |
|
||||
mkdir -p /web/${{ gitea.event.repository.name }}
|
||||
mv ${{ gitea.event.repository.name }}/bin/* /web/${{ gitea.event.repository.name }}
|
||||
echo "Move files to final directory (/web/${{ gitea.event.repository.name }})"
|
||||
- name: Cleanup - Web Build Available
|
||||
run: echo "Emscripten build now available at http://projectdivar.com/files/web/${{ gitea.event.repository.name }}"
|
@ -47,8 +47,7 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\include;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria;$(SolutionDir)Adventures in Lestoria;$(SolutionDir)Adventures in Lestoria\include$(SolutionDir);$(IncludePath)</IncludePath>
|
||||
<OutDir>..\x64\Unit Testing</OutDir>
|
||||
<IncludePath>C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
@ -58,17 +57,16 @@
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\include;J:\AdventuresInLestoria\Adventures in Lestoria\steam;J:\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\LabUser\OneDrive\Documents\include;C:\Users\sigon\OneDrive\Documents\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<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>stdcpplatest</LanguageStandard>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalLibraryDirectories>$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>discord_game_sdk.dll.lib;freetype.lib;steam_api64.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File ../unit-testing-prebuild.ps1</Command>
|
||||
@ -103,22 +101,6 @@
|
||||
<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="BuffTests.cpp">
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'">$(ProjectDir)..\Adventures in Lestoria\discord-files;$(ProjectDir)..\Adventures in Lestoria\steam;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\sigon\OneDrive\Documents\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EnchantTests.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EngineTests.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'">$(ProjectDir)..\Adventures in Lestoria\discord-files;$(ProjectDir)..\Adventures in Lestoria\steam;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\sigon\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\sigon\OneDrive\Documents\include;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileTests.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GeometryTests.cpp" />
|
||||
<ClCompile Include="ItemTests.cpp">
|
||||
<SubType>
|
||||
@ -138,12 +120,6 @@
|
||||
<Project>{8e3067af-cfe7-4b11-bc6b-b867c32753d7}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GameHelper.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
@ -69,22 +69,5 @@
|
||||
<ClCompile Include="ItemTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EnchantTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EngineTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BuffTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileTests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GameHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,173 +0,0 @@
|
||||
#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"
|
||||
#include "GameHelper.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace olc::utils;
|
||||
|
||||
INCLUDE_MONSTER_DATA
|
||||
INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
INCLUDE_DAMAGENUMBER_LIST
|
||||
INCLUDE_MONSTER_LIST
|
||||
INCLUDE_INITIALIZEGAMECONFIGURATIONS
|
||||
|
||||
namespace BuffTests
|
||||
{
|
||||
TEST_CLASS(BuffTest)
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<AiL>testGame;
|
||||
#pragma region Setup Functions
|
||||
void SetupTestMonster(){
|
||||
InitializeGameConfigurations();
|
||||
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);
|
||||
testGame->ResetLevelStates();
|
||||
|
||||
#pragma region Setup a fake test map
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
ItemDrop::ClearDrops();
|
||||
#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::ClearDrops();
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
TEST_METHOD_INITIALIZE(BuffInitialize){
|
||||
SetupTestMonster();
|
||||
SetupMockMap();
|
||||
}
|
||||
TEST_METHOD_CLEANUP(CleanupTests){
|
||||
testGame->EndGame();
|
||||
testGame->OnUserUpdate(0.f);
|
||||
testGame.reset();
|
||||
}
|
||||
TEST_METHOD(AddBuffMonsterCallbackExpireFunctionTest){
|
||||
Monster&m{game->SpawnMonster({},MONSTER_DATA.at("TestName"))};
|
||||
Game::Update(0.f);
|
||||
Assert::AreEqual(size_t(0),m.GetBuffs(BuffType::AFFECTED_BY_LIGHTNING_BOLT).size(),L"Monsters do not have any lightning bolt affected buffs when spawning.");
|
||||
m.AddBuff(BuffType::AFFECTED_BY_LIGHTNING_BOLT,3.f,1,[](std::weak_ptr<Monster>attachedTarget,Buff&b){attachedTarget.lock()->Hurt(5,attachedTarget.lock()->OnUpperLevel(),attachedTarget.lock()->GetZ());});
|
||||
Assert::AreEqual(size_t(1),m.GetBuffs(BuffType::AFFECTED_BY_LIGHTNING_BOLT).size(),L"Monster now has Affected By Lightning Bolt buff. Should get hurt for 5 damage in 3 seconds...");
|
||||
Game::Update(0.f);
|
||||
Game::Update(3.f);
|
||||
Assert::AreEqual(25,m.GetHealth(),L"Monster should have taken 5 health from the expired callback provided.");
|
||||
}
|
||||
TEST_METHOD(AddBuffPlayerCallbackExpireFunctionTest){
|
||||
Assert::AreEqual(size_t(0),game->GetPlayer()->GetBuffs(BuffType::AFFECTED_BY_LIGHTNING_BOLT).size(),L"Player does not have any lightning bolt affected buffs when spawning.");
|
||||
game->GetPlayer()->AddBuff(BuffType::AFFECTED_BY_LIGHTNING_BOLT,3.f,1,[](Player*attachedTarget,Buff&b){attachedTarget->Hurt(5,attachedTarget->OnUpperLevel(),attachedTarget->GetZ());});
|
||||
Game::Update(0.f);
|
||||
Assert::AreEqual(size_t(1),game->GetPlayer()->GetBuffs(BuffType::AFFECTED_BY_LIGHTNING_BOLT).size(),L"Player is now affected By Lightning Bolt buff. Should get hurt for 5 damage in 3 seconds...");
|
||||
Game::Update(0.f);
|
||||
Game::Update(3.f);
|
||||
Assert::AreEqual(95,game->GetPlayer()->GetHealth(),L"Player should have taken 5 health from the expired callback provided.");
|
||||
}
|
||||
TEST_METHOD(MonsterHasBuffFunctionTest){
|
||||
Monster&m{game->SpawnMonster({},MONSTER_DATA.at("TestName"))};
|
||||
Game::Update(0.f);
|
||||
Assert::AreEqual(false,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should not have a speedboost buff before being given one.");
|
||||
m.AddBuff(BuffType::ADRENALINE_RUSH,1.f,1.f);
|
||||
Assert::AreEqual(false,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should not have a speedboost buff when given an unrelated buff.");
|
||||
m.AddBuff(BuffType::SPEEDBOOST,1.f,1.f);
|
||||
Assert::AreEqual(true,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should now have a speedboost buff.");
|
||||
m.AddBuff(BuffType::SPEEDBOOST,2.f,1.f);
|
||||
Assert::AreEqual(true,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should still report having a speedboost buff.");
|
||||
Game::Update(0.f);
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(true,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should still have one speedboost buff.");
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(false,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should no longer have a speedboost buff.");
|
||||
m.AddBuff(BuffType::SPEEDBOOST,1.f,1.f);
|
||||
m.RemoveBuff(BuffType::SPEEDBOOST);
|
||||
Assert::AreEqual(false,m.HasBuff(BuffType::SPEEDBOOST),L"Monster should no longer have a speedboost buff.");
|
||||
}
|
||||
TEST_METHOD(PlayerHasBuffFunctionTest){
|
||||
Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should not have a speedboost buff before being given one.");
|
||||
Game::AddBuffToPlayer(BuffType::ADRENALINE_RUSH,1.f,1.f);
|
||||
Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should not have a speedboost buff when given an unrelated buff.");
|
||||
Game::AddBuffToPlayer(BuffType::SPEEDBOOST,1.f,1.f);
|
||||
Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should now have a speedboost buff.");
|
||||
Game::AddBuffToPlayer(BuffType::SPEEDBOOST,2.f,1.f);
|
||||
Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should still report having a speedboost buff.");
|
||||
Game::Update(0.f);
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should still have one speedboost buff.");
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should no longer have a speedboost buff.");
|
||||
Game::AddBuffToPlayer(BuffType::SPEEDBOOST,1.f,1.f);
|
||||
game->GetPlayer()->RemoveBuff(BuffType::SPEEDBOOST);
|
||||
Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should no longer have a speedboost buff.");
|
||||
}
|
||||
TEST_METHOD(PlayerDoesNotImmediatelyReceiveBuffTest){
|
||||
Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should not have a speedboost buff before being given one.");
|
||||
game->GetPlayer()->AddBuff(BuffType::SPEEDBOOST,1.f,1.f);
|
||||
Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should still not have a speedboost buff since it is in the add queue.");
|
||||
Game::Update(0.f);
|
||||
Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should now have a speedboost buff.");
|
||||
}
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,194 +0,0 @@
|
||||
#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"
|
||||
#include <ranges>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
INCLUDE_GFX
|
||||
INCLUDE_ITEM_DATA
|
||||
INCLUDE_DAMAGENUMBER_LIST
|
||||
INCLUDE_INITIALIZEGAMECONFIGURATIONS
|
||||
|
||||
extern std::mt19937 rng;
|
||||
|
||||
namespace EngineTests
|
||||
{
|
||||
TEST_CLASS(EngineTest)
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<AiL>testGame;
|
||||
InputGroup testKeyboardInput;
|
||||
Player*player;
|
||||
HWButton*testKey;
|
||||
TEST_METHOD_INITIALIZE(PlayerInitialize){
|
||||
InitializeGameConfigurations();
|
||||
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
|
||||
testGame.reset(new AiL(true));
|
||||
ItemAttribute::Initialize();
|
||||
ItemInfo::InitializeItems();
|
||||
testGame->InitializeGraphics();
|
||||
testGame->InitializeClasses();
|
||||
sig::Animation::InitializeAnimations();
|
||||
testGame->InitializeDefaultKeybinds();
|
||||
testGame->InitializePlayer();
|
||||
sig::Animation::SetupPlayerAnimations();
|
||||
Menu::InitializeMenus();
|
||||
Tutorial::Initialize();
|
||||
Stats::InitializeDamageReductionTable();
|
||||
|
||||
GameState::Initialize();
|
||||
GameState::STATE=GameState::states.at(States::State::GAME_RUN);
|
||||
|
||||
#pragma region Setup a fake test map and test monster
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
ItemDrop::ClearDrops();
|
||||
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
MONSTER_DATA["TestName"]=testMonsterData;
|
||||
#pragma endregion
|
||||
testGame->ResetLevelStates();
|
||||
|
||||
player=testGame->GetPlayer();
|
||||
//Setup key "0" as a test input
|
||||
testKeyboardInput.AddKeybind(Input{InputType::KEY,0});
|
||||
testKey=testGame->GetKeyboardState(0);
|
||||
testGame->olc_UpdateKeyFocus(true); //Force the game to be "focused" for tests. Required if we want keyboard inputs to work.
|
||||
Menu::themes.SetInitialized();
|
||||
GFX.SetInitialized();
|
||||
|
||||
DAMAGENUMBER_LIST.clear();
|
||||
}
|
||||
TEST_METHOD_CLEANUP(CleanupTests){
|
||||
testGame->EndGame();
|
||||
testGame->OnUserUpdate(0.f);
|
||||
testGame.reset();
|
||||
}
|
||||
TEST_METHOD(StripColorTest){
|
||||
std::string noColCode{"Hello World!"};
|
||||
Assert::AreEqual("Hello World!"s,testGame->stripCol(noColCode),L"Function should not strip out any text when there's no color codes.");
|
||||
std::string leadingColCode{"#FFFFFFHello World!"};
|
||||
Assert::AreEqual("Hello World!"s,testGame->stripCol(leadingColCode),L"Function should strip out all color codes.");
|
||||
std::string extraColCodes{"#FFFFFFHello #00FF00World!"};
|
||||
Assert::AreEqual("Hello World!"s,testGame->stripCol(extraColCodes),L"Function should strip out all color codes.");
|
||||
std::u32string u32noColCode{noColCode.begin(),noColCode.end()};
|
||||
std::u32string u32noColCodeResult{testGame->stripCol(u32noColCode)};
|
||||
Assert::AreEqual("Hello World!"s,std::string{u32noColCodeResult.begin(),u32noColCodeResult.end()},L"Function should not strip out any text when there's no color codes.");
|
||||
std::u32string u32leadingColCode{noColCode.begin(),noColCode.end()};
|
||||
std::u32string u32leadingColCodeResult{testGame->stripCol(u32leadingColCode)};
|
||||
Assert::AreEqual("Hello World!"s,std::string{u32leadingColCodeResult.begin(),u32leadingColCodeResult.end()},L"Function should strip out all color codes.");
|
||||
std::u32string u32extraColCodes{extraColCodes.begin(),extraColCodes.end()};
|
||||
std::u32string u32extraColCodesResult{testGame->stripCol(u32extraColCodes)};
|
||||
Assert::AreEqual("Hello World!"s,std::string{u32extraColCodesResult.begin(),u32extraColCodesResult.end()},L"Function should strip out all color codes.");
|
||||
}
|
||||
TEST_METHOD(StripLeadingColorTest){
|
||||
std::string noColCode{"Hello World!"};
|
||||
Assert::AreEqual("Hello World!"s,testGame->stripLeadingCol(noColCode),L"Function should not strip out any text when there's no color codes.");
|
||||
std::string leadingColCode{"#FFFFFFHello World!"};
|
||||
Assert::AreEqual("Hello World!"s,testGame->stripLeadingCol(leadingColCode),L"Function should strip out color code at beginning of text.");
|
||||
std::string extraColCodes{"#FFFFFFHello #00FF00World!"};
|
||||
Assert::AreEqual("Hello #00FF00World!"s,testGame->stripLeadingCol(extraColCodes),L"Function should only strip out color code at beginning of text.");
|
||||
std::u32string u32noColCode{noColCode.begin(),noColCode.end()};
|
||||
std::u32string u32noColCodeResult{testGame->stripLeadingCol(u32noColCode)};
|
||||
Assert::AreEqual("Hello World!"s,std::string{u32noColCodeResult.begin(),u32noColCodeResult.end()},L"Function should not strip out any text when there's no color codes.");
|
||||
std::u32string u32leadingColCode{noColCode.begin(),noColCode.end()};
|
||||
std::u32string u32leadingColCodeResult{testGame->stripLeadingCol(u32leadingColCode)};
|
||||
Assert::AreEqual("Hello World!"s,std::string{u32leadingColCodeResult.begin(),u32leadingColCodeResult.end()},L"Function should strip out color code at beginning of text.");
|
||||
std::u32string u32extraColCodes{extraColCodes.begin(),extraColCodes.end()};
|
||||
std::u32string u32extraColCodesResult{testGame->stripLeadingCol(u32extraColCodes)};
|
||||
Assert::AreEqual("Hello #00FF00World!"s,std::string{u32extraColCodesResult.begin(),u32extraColCodesResult.end()},L"Function should only strip out color code at beginning of text.");
|
||||
}
|
||||
TEST_METHOD(GetFinalRenderColorTest){
|
||||
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,"Hello World!").n,L"Should use source color as there's no HTML color code.");
|
||||
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,"#0000FFHello World!").n,L"Should use color in string since it has a leading HTML color code.");
|
||||
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,"#0000FFHello #00FF00World!").n,L"Should use color in string since it has a leading HTML color code.");
|
||||
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,"Hello #00FF00World!").n,L"Should use source color since there's no leading HTML color code.");
|
||||
std::string testStr{"Hello World!"};
|
||||
std::u32string u32testStr{testStr.begin(),testStr.end()};
|
||||
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,testStr).n,L"Should use source color as there's no HTML color code.");
|
||||
std::string colorCodeStr{"#0000FFHello World!"};
|
||||
std::u32string u32colorCodeStr{colorCodeStr.begin(),colorCodeStr.end()};
|
||||
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,colorCodeStr).n,L"Should use color in string since it has a leading HTML color code.");
|
||||
std::string extraColorCodeStr{"#0000FFHello #00FF00World!"};
|
||||
std::u32string u32extraColorCodeStr{extraColorCodeStr.begin(),extraColorCodeStr.end()};
|
||||
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,extraColorCodeStr).n,L"Should use color in string since it has a leading HTML color code.");
|
||||
std::string middleColorCodeStr{"Hello #00FF00World!"};
|
||||
std::u32string u32middleColorCodeStr{middleColorCodeStr.begin(),middleColorCodeStr.end()};
|
||||
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,middleColorCodeStr).n,L"Should use source color since there's no leading HTML color code.");
|
||||
}
|
||||
TEST_METHOD(UtilMapRangeTest){
|
||||
Assert::AreEqual(0.f,util::map_range<float>(0.f,0,100,0,100),L"0 in input range 0-100 output range 0-100 maps to 0");
|
||||
Assert::AreEqual(100.f,util::map_range<float>(100.f,0,100,0,100),L"100 in input range 0-100 output range 0-100 maps to 100");
|
||||
Assert::AreEqual(50.f,util::map_range<float>(50.f,0,100,0,100),L"50 in input range 0-100 output range 0-100 maps to 100");
|
||||
|
||||
Assert::AreEqual(0.f,util::map_range<float>(0.f,0,50,0,100),L"0 in input range 0-50 output range 0-100 maps to 0");
|
||||
Assert::AreEqual(200.f,util::map_range<float>(100.f,0,50,0,100),L"100 in input range 0-50 output range 0-100 maps to 200");
|
||||
Assert::AreEqual(100.f,util::map_range<float>(50.f,0,50,0,100),L"50 in input range 0-50 output range 0-100 maps to 100");
|
||||
|
||||
Assert::AreEqual(100.f,util::map_range<float>(0.f,0,100,100,200),L"0 in input range 0-100 output range 100-200 maps to 100");
|
||||
Assert::AreEqual(200.f,util::map_range<float>(100.f,0,100,100,200),L"100 in input range 0-100 output range 100-200 maps to 200");
|
||||
Assert::AreEqual(150.f,util::map_range<float>(50.f,0,100,100,200),L"50 in input range 0-100 output range 100-200 maps to 150");
|
||||
|
||||
Assert::AreEqual(0.f,util::map_range<float>(0.f,50,100,100,200),L"0 in input range 50-100 output range 100-200 maps to 0");
|
||||
Assert::AreEqual(200.f,util::map_range<float>(100.f,50,100,100,200),L"100 in input range 50-100 output range 100-200 maps to 200");
|
||||
Assert::AreEqual(100.f,util::map_range<float>(50.f,50,100,100,200),L"50 in input range 50-100 output range 100-200 maps to 100");
|
||||
}
|
||||
|
||||
TEST_METHOD(ClassUtilTest){
|
||||
Assert::AreEqual(int(Class::WARRIOR),int(classutils::StringToClass("Warrior")),L"Expected Warrior to return the warrior class.");
|
||||
Assert::AreEqual(int(Class::WIZARD),int(classutils::StringToClass("Wizard")),L"Expected Wizard to return the wizard class.");
|
||||
Assert::AreEqual(int(Class::RANGER),int(classutils::StringToClass("Ranger")),L"Expected Ranger to return the ranger class.");
|
||||
Assert::AreEqual(int(Class::THIEF),int(classutils::StringToClass("Thief")),L"Expected Thief to return the thief class.");
|
||||
Assert::AreEqual(int(Class::WITCH),int(classutils::StringToClass("Witch")),L"Expected Witch to return the witch class.");
|
||||
Assert::AreEqual(int(Class::TRAPPER),int(classutils::StringToClass("Trapper")),L"Expected Trapper to return the trapper class.");
|
||||
|
||||
Assert::AreEqual("Warrior",classutils::ClassToString(Class::WARRIOR).c_str(),L"Expected class to string to convert properly");
|
||||
Assert::AreEqual("Wizard",classutils::ClassToString(Class::WIZARD).c_str(),L"Expected class to string to convert properly");
|
||||
Assert::AreEqual("Ranger",classutils::ClassToString(Class::RANGER).c_str(),L"Expected class to string to convert properly");
|
||||
Assert::AreEqual("Thief",classutils::ClassToString(Class::THIEF).c_str(),L"Expected class to string to convert properly");
|
||||
Assert::AreEqual("Witch",classutils::ClassToString(Class::WITCH).c_str(),L"Expected class to string to convert properly");
|
||||
Assert::AreEqual("Trapper",classutils::ClassToString(Class::TRAPPER).c_str(),L"Expected class to string to convert properly");
|
||||
}
|
||||
};
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
#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"
|
||||
#include "GameHelper.h"
|
||||
#include "SaveFile.h"
|
||||
#include <ranges>
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace olc::utils;
|
||||
|
||||
INCLUDE_MONSTER_DATA
|
||||
INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
INCLUDE_DAMAGENUMBER_LIST
|
||||
INCLUDE_MONSTER_LIST
|
||||
INCLUDE_INITIALIZEGAMECONFIGURATIONS
|
||||
|
||||
namespace FileTests
|
||||
{
|
||||
TEST_CLASS(FileTest)
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<AiL>testGame;
|
||||
#pragma region Setup Functions
|
||||
void SetupTest(){
|
||||
InitializeGameConfigurations();
|
||||
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);
|
||||
testGame->ResetLevelStates();
|
||||
|
||||
#pragma region Setup a fake test map
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
ItemDrop::ClearDrops();
|
||||
#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::ClearDrops();
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
TEST_METHOD_INITIALIZE(FileTestInitialize){
|
||||
SetupTest();
|
||||
SetupMockMap();
|
||||
}
|
||||
TEST_METHOD_CLEANUP(CleanupFileTests){
|
||||
testGame->EndGame();
|
||||
testGame->OnUserUpdate(0.f);
|
||||
testGame.reset();
|
||||
}
|
||||
};
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
#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 "CppUnitTest.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace Game{
|
||||
enum class CastWaitProperty{
|
||||
WAIT_FOR_CAST_TIME,
|
||||
NO_WAIT,
|
||||
};
|
||||
inline void Update(const float fElapsedTime){
|
||||
game->SetElapsedTime(fElapsedTime);
|
||||
game->OnUserUpdate(fElapsedTime);
|
||||
}
|
||||
inline void CastAbilityAtLocation(Ability&ability,const vf2d&worldLoc,const CastWaitProperty castWaitTime=CastWaitProperty::WAIT_FOR_CAST_TIME){ //NOTE: screenLoc is the actual screen coordinates, NOT the world coordinates! You are defining the mouse position essentially.
|
||||
game->GetPlayer()->SetTestScreenAimingLocation(worldLoc);
|
||||
game->GetPlayer()->PrepareCast(ability);
|
||||
game->GetPlayer()->CastSpell(ability);
|
||||
Game::Update(ability.precastInfo.castTime);
|
||||
}
|
||||
inline void ChangeClass(Player*&player_in,const Class&cl){
|
||||
game->ChangePlayerClass(cl);
|
||||
player_in=game->GetPlayer();
|
||||
}
|
||||
inline std::weak_ptr<Item>GiveAndEquipEnchantedRing(const std::string_view enchantName,const EquipSlot slot=EquipSlot::RING1){
|
||||
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
|
||||
Inventory::EquipItem(nullRing,slot);
|
||||
nullRing.lock()->_EnchantItem(enchantName);
|
||||
return nullRing;
|
||||
}
|
||||
//Adds the buff directly to the player instead of the buffs added list. (By calling an update tick.)
|
||||
inline void AddBuffToPlayer(BuffType type,float duration,float intensity){
|
||||
game->GetPlayer()->AddBuff(type,duration,intensity);
|
||||
Update(0.f);
|
||||
}
|
||||
//NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!!
|
||||
inline void AddBuffToPlayer(BuffType type,float duration,float intensity,std::set<ItemAttribute>attr){
|
||||
game->GetPlayer()->AddBuff(type,duration,intensity,attr);
|
||||
Update(0.f);
|
||||
}
|
||||
//NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!!
|
||||
inline void AddBuffToPlayer(BuffType type,float duration,float intensity,std::set<std::string>attr){
|
||||
game->GetPlayer()->AddBuff(type,duration,intensity,attr);
|
||||
Update(0.f);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Test
|
||||
{
|
||||
template<class T>
|
||||
inline static void InRange(T initialVal,std::pair<T,T>range,std::wstring_view assertMessage){
|
||||
Assert::IsTrue(initialVal>=range.first&&initialVal<=range.second,std::format(L"Expected: {}~{} Actual: {} - {}",range.first,range.second,initialVal,assertMessage).c_str());
|
||||
}
|
||||
}
|
@ -41,15 +41,12 @@ All rights reserved.
|
||||
#include <random>
|
||||
#include <format>
|
||||
#include "ItemDrop.h"
|
||||
#include <ranges>
|
||||
#include "GameHelper.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace olc::utils;
|
||||
|
||||
INCLUDE_GFX
|
||||
INCLUDE_ITEM_DATA
|
||||
INCLUDE_INITIALIZEGAMECONFIGURATIONS
|
||||
|
||||
extern std::mt19937 rng;
|
||||
|
||||
@ -63,7 +60,6 @@ namespace ItemTests
|
||||
Player*player;
|
||||
HWButton*testKey;
|
||||
TEST_METHOD_INITIALIZE(ItemInitialize){
|
||||
InitializeGameConfigurations();
|
||||
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
|
||||
testGame.reset(new AiL(true));
|
||||
ItemAttribute::Initialize();
|
||||
@ -77,29 +73,27 @@ namespace ItemTests
|
||||
Menu::InitializeMenus();
|
||||
Tutorial::Initialize();
|
||||
Stats::InitializeDamageReductionTable();
|
||||
|
||||
GameState::Initialize();
|
||||
GameState::STATE=GameState::states.at(States::State::GAME_RUN);
|
||||
|
||||
testGame->ResetLevelStates();
|
||||
#pragma region Setup a fake test map and test monster
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
ItemDrop::ClearDrops();
|
||||
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
MONSTER_DATA["TestName"]=testMonsterData;
|
||||
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->GetKeyboardState(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(ItemCleanupTests){
|
||||
TEST_METHOD_CLEANUP(CleanupTests){
|
||||
testGame->EndGame();
|
||||
testGame->OnUserUpdate(0.f);
|
||||
testGame.reset();
|
||||
}
|
||||
TEST_METHOD(ItemGiveTest){
|
||||
Inventory::AddItem("Health Potion"s,3);
|
||||
@ -136,26 +130,25 @@ namespace ItemTests
|
||||
Inventory::AddItem("Minor Health Potion"s,5U);
|
||||
game->SetLoadoutItem(0,"Minor Health Potion");
|
||||
testKey->bHeld=true; //Simulate key being pressed.
|
||||
player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput);
|
||||
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->GetItem1().GetCooldownTime(),player->GetItem1().cooldown,L"Item 1 is now on cooldown.");
|
||||
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->GetItem1(),testKeyboardInput);},L"If all buffs are properly applied, then some of these stat up buffs are illegal and will catch an exception.");
|
||||
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->ConsumeMana(76);
|
||||
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->GetItem1(),testKeyboardInput);
|
||||
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
|
||||
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.");
|
||||
@ -163,12 +156,11 @@ namespace ItemTests
|
||||
}
|
||||
TEST_METHOD(PctRestoreScriptTest){
|
||||
player->Hurt(75,player->OnUpperLevel(),player->GetZ());
|
||||
player->ConsumeMana(76);
|
||||
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->GetItem2(),testKeyboardInput);
|
||||
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
|
||||
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.");
|
||||
@ -179,8 +171,7 @@ namespace ItemTests
|
||||
Inventory::AddItem("Bandages"s,5U);
|
||||
game->SetLoadoutItem(2,"Bandages");
|
||||
testKey->bHeld=true; //Simulate key being pressed.
|
||||
player->CheckAndPerformAbility(player->GetItem3(),testKeyboardInput);
|
||||
game->OnUserUpdate(0.f); //Wait an extra tick for the buff to begin going down.
|
||||
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.");
|
||||
@ -223,7 +214,6 @@ namespace ItemTests
|
||||
std::weak_ptr<Item>testArmor{Inventory::AddItem("Test Armor"s)};
|
||||
Assert::IsFalse(testArmor.lock()->CanBeRefined(),L"Test Armor should not be allowed to be refined since it's not an accessory.");
|
||||
Inventory::AddItem(slimeKingRing.lock()->FragmentName(),50U);
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
|
||||
Assert::IsTrue(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should now be allowed to be refined since we meet all requirements.");
|
||||
player->SetMoney(0);
|
||||
Assert::IsFalse(slimeKingRing.lock()->CanBeRefined(),L"Ring of the Slime King should not be allowed to be refined since we do not have enough money.");
|
||||
@ -235,122 +225,6 @@ namespace ItemTests
|
||||
for(const auto&[attr,val]:slimeKingRing.lock()->RandomStats()){
|
||||
Assert::AreEqual(ITEM_DATA[slimeKingRing.lock()->ActualName()].GetMaxStats().A_Read(attr),val,L"The current stats should be equal to the maximum stats when refinement is done.");
|
||||
}
|
||||
/*Ring of the Slime King has the following refining stats:
|
||||
* StatValues = Health,Mana,Move Spd %
|
||||
MinStats = 5,1,1
|
||||
MaxStats = 20,4,3
|
||||
*
|
||||
Therefore, after this process is done the player should have 20 more health, 4 more mana, and 3% more move speed than normal.
|
||||
*/
|
||||
//These checks make sure that the refining call actually modifies already equipped items!
|
||||
Assert::AreEqual(120,player->GetMaxHealth(),L"Player should now have 120 max health.");
|
||||
Assert::AreEqual(104,player->GetMaxMana(),L"Player should now have 104 max mana.");
|
||||
Assert::AreEqual(1.03f,player->GetMoveSpdMult(),L"Player should now have 103% move speed.");
|
||||
}
|
||||
TEST_METHOD(EnchantTestCheck){
|
||||
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};
|
||||
Assert::IsFalse(slimeKingRing.lock()->HasEnchant());
|
||||
bool obtainedDuplicate{false};
|
||||
for(int i:std::ranges::iota_view(0,1000)){
|
||||
std::optional<ItemEnchant>previousEnchant{slimeKingRing.lock()->GetEnchant()};
|
||||
std::string previousEnchantName{};
|
||||
if(slimeKingRing.lock()->HasEnchant())previousEnchantName=slimeKingRing.lock()->GetEnchant().value().Name();
|
||||
slimeKingRing.lock()->_EnchantItem(ItemEnchant::RollRandomEnchant(previousEnchant));
|
||||
Assert::IsTrue(slimeKingRing.lock()->HasEnchant());
|
||||
if(previousEnchantName==slimeKingRing.lock()->GetEnchant().value().Name()){
|
||||
bool improvementExists{false};
|
||||
std::string statDowngrades{};
|
||||
if(previousEnchant.value().HasAttributes()){
|
||||
for(const auto&[attr,val]:previousEnchant.value()){
|
||||
if(slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName())>val){
|
||||
improvementExists=true;
|
||||
break;
|
||||
}else statDowngrades+=std::format("{} - Previous:{}, New:{}\n",attr.Name(),val,slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName()));
|
||||
}
|
||||
}else improvementExists=true;
|
||||
Assert::IsTrue(improvementExists,util::wformat("Could not find a stat improvement for same name stat! {} THIS SHOULD NOT BE ALLOWED!",statDowngrades).c_str());
|
||||
obtainedDuplicate=true;
|
||||
}
|
||||
if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())Assert::AreEqual(int(player->GetClass()),int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability.
|
||||
}
|
||||
testGame->ChangePlayerClass(WIZARD);
|
||||
player=testGame->GetPlayer(); //The player pointer has been reassigned...
|
||||
for(int i:std::ranges::iota_view(0,1000)){
|
||||
std::optional<ItemEnchant> previousEnchant{slimeKingRing.lock()->GetEnchant()};
|
||||
std::string previousEnchantName{};
|
||||
if(slimeKingRing.lock()->HasEnchant())previousEnchantName=slimeKingRing.lock()->GetEnchant().value().Name();
|
||||
slimeKingRing.lock()->_EnchantItem(ItemEnchant::RollRandomEnchant(previousEnchant));
|
||||
Assert::IsTrue(slimeKingRing.lock()->HasEnchant());
|
||||
if(previousEnchantName==slimeKingRing.lock()->GetEnchant().value().Name()){
|
||||
bool improvementExists{false};
|
||||
std::string statDowngrades{};
|
||||
if(previousEnchant.value().HasAttributes()){
|
||||
for(const auto&[attr,val]:previousEnchant.value()){
|
||||
if(slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName())>val){
|
||||
improvementExists=true;
|
||||
break;
|
||||
}else statDowngrades+=std::format("{} - Previous:{}, New:{}\n",attr.Name(),val,slimeKingRing.lock()->GetEnchant().value().GetAttribute(attr.ActualName()));
|
||||
}
|
||||
}else improvementExists=true;
|
||||
Assert::IsTrue(improvementExists,util::wformat("Could not find a stat improvement for same name stat! {} THIS SHOULD NOT BE ALLOWED!",statDowngrades).c_str());
|
||||
obtainedDuplicate=true;
|
||||
}
|
||||
if(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().has_value())Assert::AreEqual(int(player->GetClass()),int(ItemEnchantInfo::ENCHANT_LIST.at(slimeKingRing.lock()->GetEnchant().value().Name()).GetClass().value())); //Validate enchant is only for this class if it's a class-based ability.
|
||||
}
|
||||
Assert::IsTrue(obtainedDuplicate,L"During this test a duplicate enchant was never obtained! THIS SHOULD BE ALLOWED!");
|
||||
}
|
||||
TEST_METHOD(AccessoryAntiCompatibilityCheck){
|
||||
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
|
||||
std::weak_ptr<Item>extraRing2{Inventory::AddItem("Ring of the Slime King"s)};
|
||||
|
||||
Inventory::EquipItem(extraRing,EquipSlot::RING2);
|
||||
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(extraRing2,EquipSlot::RING1),L"The game should allow equipping of any two normal rings that are not the same ring.");
|
||||
Assert::AreEqual(false,Item::SelectedEquipIsDifferent(extraRing,EquipSlot::RING1),L"The game should not allow equipping the same ring if it's already equipped.");
|
||||
Inventory::UnequipItem(EquipSlot::RING2);
|
||||
Assert::AreEqual(true,Item::SelectedEquipIsDifferent(extraRing,EquipSlot::RING1),L"The game should allow equipping a ring to either blank slot if they're open.");
|
||||
}
|
||||
TEST_METHOD(AccessoryRandomEnchantTest){
|
||||
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
|
||||
std::unordered_map<ItemEnchantInfo::ItemEnchantCategory,uint32_t>enchantCounts;
|
||||
for(int i:std::ranges::iota_view(0,1000)){
|
||||
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
|
||||
player->AddMoney("Fragment Enchant Cost"_i[1]);
|
||||
const ItemEnchant&resultEnchant{extraRing.lock()->ApplyRandomEnchant()};
|
||||
if(resultEnchant.GetClass().has_value())Assert::AreEqual(int(resultEnchant.GetClass().value()),int(player->GetClass()),L"Player's class matches the class of the enchant.");
|
||||
enchantCounts[resultEnchant.Category()]++;
|
||||
Assert::AreEqual(true,extraRing.lock()->GetEnchant().has_value(),L"Ring is expected to be enchanted.");
|
||||
Assert::AreEqual(resultEnchant.Name(),extraRing.lock()->GetEnchant().value().Name(),L"Ring is expected to be enchanted with the same enchant that was selected.");
|
||||
Assert::AreEqual(false,player->HasEnchant(resultEnchant.Name()),L"Player is not expected to have the same enchant that was selected while the ring is unequipped.");
|
||||
Inventory::EquipItem(extraRing,EquipSlot::RING1);
|
||||
Assert::AreEqual(true,player->HasEnchant(resultEnchant.Name()),L"Player is expected to have the same enchant that was selected.");
|
||||
Inventory::UnequipItem(EquipSlot::RING1);
|
||||
}
|
||||
Test::InRange(enchantCounts[ItemEnchantInfo::ItemEnchantCategory::GENERAL],{450U,550U},util::wformat("General enchants % is approx 50%."));
|
||||
Test::InRange(enchantCounts[ItemEnchantInfo::ItemEnchantCategory::CLASS],{350U,450U},util::wformat("Class enchants % is approx 40%."));
|
||||
Test::InRange(enchantCounts[ItemEnchantInfo::ItemEnchantCategory::UNIQUE],{50U,150U},util::wformat("Unique enchants % is approx 40%."));
|
||||
}
|
||||
TEST_METHOD(EnchantColorTest){
|
||||
Assert::AreEqual("Item Enchants.General Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Health Boost").DisplayCol().n,L"Expecting a general enchant to have the general enchant pixel display color.");
|
||||
Assert::AreEqual("Item Enchants.Class Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Quickdraw").DisplayCol().n,L"Expecting a class enchant to have the class enchant pixel display color.");
|
||||
Assert::AreEqual("Item Enchants.Unique Enchants.Enchant Display Color"_Pixel.n,ItemEnchantInfo::GetEnchant("Magical Protection").DisplayCol().n,L"Expecting a unique enchant to have the unique enchant pixel display color.");
|
||||
}
|
||||
TEST_METHOD(CanBeEnchantedTest){
|
||||
std::weak_ptr<Item>extraRing{Inventory::AddItem("Ring of the Slime King"s)};
|
||||
std::weak_ptr<Item>testArmor{Inventory::AddItem("Test Armor"s)};
|
||||
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"We don't have the money nor required fragments to enchant this item.");
|
||||
Assert::AreEqual(false,testArmor.lock()->CanBeEnchanted(),L"We can't enchant armor.");
|
||||
player->SetMoney(2000U);
|
||||
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"We don't have the required fragments to enchant this item.");
|
||||
player->SetMoney(0U);
|
||||
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
|
||||
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"We don't have the required money to enchant this item.");
|
||||
player->SetMoney(2000U);
|
||||
Assert::AreEqual(true,extraRing.lock()->CanBeEnchanted(),L"We don't have the required money to enchant this item.");
|
||||
extraRing.lock()->ApplyRandomEnchant();
|
||||
Assert::AreEqual(false,extraRing.lock()->CanBeEnchanted(),L"Ring cannot be enchanted again due to consumption of fragments.");
|
||||
Inventory::AddItem(extraRing.lock()->FragmentName(),"Fragment Enchant Cost"_i[0]);
|
||||
Assert::AreEqual(true,extraRing.lock()->CanBeEnchanted(),L"Ring can be enchanted again with the right amount of fragments.");
|
||||
Assert::AreEqual(uint32_t(2000-"Fragment Enchant Cost"_i[1]),player->GetMoney(),util::wformat("Lost {} money due to enchanting ring.","Fragment Enchant Cost"_i[1]).c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ All rights reserved.
|
||||
#include "ItemDrop.h"
|
||||
#include "Tutorial.h"
|
||||
#include "DamageNumber.h"
|
||||
#include "GameHelper.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace olc::utils;
|
||||
@ -51,7 +50,6 @@ INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
INCLUDE_DAMAGENUMBER_LIST
|
||||
INCLUDE_MONSTER_LIST
|
||||
INCLUDE_INITIALIZEGAMECONFIGURATIONS
|
||||
|
||||
TEST_MODULE_INITIALIZE(AiLTestSuite)
|
||||
{
|
||||
@ -67,15 +65,12 @@ namespace MonsterTests
|
||||
#pragma region Setup Functions
|
||||
//Makes MONSTER_DATA["TestName"] available.
|
||||
void SetupTestMonster(){
|
||||
InitializeGameConfigurations();
|
||||
testGame.reset(new AiL(true));
|
||||
ItemAttribute::Initialize();
|
||||
ItemInfo::InitializeItems();
|
||||
testGame->InitializeGraphics();
|
||||
testGame->InitializeClasses();
|
||||
sig::Animation::InitializeAnimations();
|
||||
Monster::InitializeStrategies();
|
||||
MonsterData::InitializeMonsterData();
|
||||
testGame->InitializeDefaultKeybinds();
|
||||
testGame->InitializePlayer();
|
||||
sig::Animation::SetupPlayerAnimations();
|
||||
@ -85,12 +80,13 @@ namespace MonsterTests
|
||||
|
||||
GameState::Initialize();
|
||||
GameState::STATE=GameState::states.at(States::State::GAME_RUN);
|
||||
testGame->ResetLevelStates();
|
||||
|
||||
#pragma region Setup a fake test map
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
game->_SetCurrentLevel("CAMPAIGN_1_1");
|
||||
ItemDrop::ClearDrops();
|
||||
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};
|
||||
@ -102,7 +98,7 @@ namespace MonsterTests
|
||||
}
|
||||
void SetupMockMap(){
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
ItemDrop::ClearDrops();
|
||||
ItemDrop::drops.clear();
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@ -110,10 +106,8 @@ namespace MonsterTests
|
||||
SetupTestMonster();
|
||||
SetupMockMap();
|
||||
}
|
||||
TEST_METHOD_CLEANUP(MonsterTestCleanup){
|
||||
testGame->EndGame();
|
||||
testGame->OnUserUpdate(0.f);
|
||||
testGame.reset();
|
||||
~MonsterTest(){
|
||||
testGame.release();
|
||||
}
|
||||
TEST_METHOD(DisplayNameCheck){
|
||||
Assert::AreEqual("Test Monster",MONSTER_DATA["TestName"].GetDisplayName().c_str());
|
||||
@ -402,7 +396,7 @@ namespace MonsterTests
|
||||
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->GetType()==DamageNumberType::DOT)return count+1;
|
||||
if(damageNumber->type==DamageNumberType::DOT)return count+1;
|
||||
else return count;
|
||||
})
|
||||
,L"There should be 2 damage numbers of type DOT.");
|
||||
@ -410,7 +404,8 @@ namespace MonsterTests
|
||||
TEST_METHOD(TrapperMarkTest){
|
||||
MonsterData testMonsterData{"TestName","Test Monster",30,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
game->SpawnMonster({},testMonsterData);
|
||||
Game::Update(0.1f); //A monster that is spawned needs to be added to the monster list in the next tick.
|
||||
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.");
|
||||
@ -461,99 +456,5 @@ namespace MonsterTests
|
||||
|
||||
Assert::AreEqual(uint8_t(0),testMonster.lock()->GetMarkStacks(),L"The marks should have expired after 10 seconds.");
|
||||
}
|
||||
TEST_METHOD(HurtFailsWhenDead){
|
||||
MonsterData testMonsterData{"TestName","Test Monster",30,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
Monster&m{game->SpawnMonster({},testMonsterData)};
|
||||
m.Hurt(50,m.OnUpperLevel(),m.GetZ());
|
||||
Assert::IsTrue(m.IsDead(),L"Monster is considered dead.");
|
||||
Assert::IsTrue(!m.IsAlive(),L"Monster is considered dead.");
|
||||
|
||||
Assert::IsFalse(m.Hurt(50,m.OnUpperLevel(),m.GetZ()),L"When hurting a dead monster, it should not take any damage and should not be triggering anything else.");
|
||||
Assert::IsFalse(m.Hurt(50,m.OnUpperLevel(),m.GetZ(),HurtFlag::DOT),L"When hurting a dead monster, it should not take any damage and should not be triggering anything else.");
|
||||
Assert::IsFalse(m.Hurt(50,m.OnUpperLevel(),m.GetZ(),HurtFlag::PLAYER_ABILITY),L"When hurting a dead monster, it should not take any damage and should not be triggering anything else.");
|
||||
Assert::IsFalse(m._DealTrueDamage(50),L"When hurting a dead monster, it should not take any damage and should not be triggering anything else.");
|
||||
Assert::IsFalse(m._DealTrueDamage(50,HurtFlag::DOT),L"When hurting a dead monster, it should not take any damage and should not be triggering anything else.");
|
||||
Assert::IsFalse(m._DealTrueDamage(50,HurtFlag::PLAYER_ABILITY),L"When hurting a dead monster, it should not take any damage and should not be triggering anything else.");
|
||||
}
|
||||
TEST_METHOD(HurtSucceedsWhenAlive){
|
||||
MonsterData testMonsterData{"TestName","Test Monster",3000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
Monster&m{game->SpawnMonster({},testMonsterData)};
|
||||
m.Hurt(50,m.OnUpperLevel(),m.GetZ());
|
||||
Assert::IsTrue(!m.IsDead(),L"Monster is considered alive.");
|
||||
Assert::IsTrue(m.IsAlive(),L"Monster is considered alive.");
|
||||
|
||||
Assert::IsTrue(m.Hurt(50,m.OnUpperLevel(),m.GetZ()),L"When hurting a monster that is alive, it should take damage and should be triggering like normal.");
|
||||
Assert::IsTrue(m.Hurt(50,m.OnUpperLevel(),m.GetZ(),HurtFlag::DOT),L"When hurting a monster that is alive, it should take damage and should be triggering like normal.");
|
||||
Assert::IsTrue(m.Hurt(50,m.OnUpperLevel(),m.GetZ(),HurtFlag::PLAYER_ABILITY),L"When hurting a monster that is alive, it should take damage and should be triggering like normal.");
|
||||
Assert::IsTrue(m._DealTrueDamage(50),L"When hurting a monster that is alive, it should take damage and should be triggering like normal.");
|
||||
Assert::IsTrue(m._DealTrueDamage(50,HurtFlag::DOT),L"When hurting a monster that is alive, it should take damage and should be triggering like normal.");
|
||||
Assert::IsTrue(m._DealTrueDamage(50,HurtFlag::PLAYER_ABILITY),L"When hurting a monster that is alive, it should take damage and should be triggering like normal.");
|
||||
}
|
||||
TEST_METHOD(HurtSucceedsWhenDyingWithExactHealth){
|
||||
MonsterData testMonsterData{"TestName","Test Monster",50,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||
Monster&m{game->SpawnMonster({},testMonsterData)};
|
||||
m.Hurt(50,m.OnUpperLevel(),m.GetZ());
|
||||
Assert::IsTrue(m.IsDead(),L"Monster is considered dead.");
|
||||
Assert::IsTrue(!m.IsAlive(),L"Monster is considered dead.");
|
||||
m.Heal(50);
|
||||
Assert::IsTrue(m.Hurt(50,m.OnUpperLevel(),m.GetZ()),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal.");
|
||||
m.Heal(50);
|
||||
Assert::IsTrue(m.Hurt(50,m.OnUpperLevel(),m.GetZ(),HurtFlag::DOT),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal.");
|
||||
m.Heal(50);
|
||||
Assert::IsTrue(m.Hurt(50,m.OnUpperLevel(),m.GetZ(),HurtFlag::PLAYER_ABILITY),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal.");
|
||||
m.Heal(50);
|
||||
Assert::IsTrue(m._DealTrueDamage(50),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal.");
|
||||
m.Heal(50);
|
||||
Assert::IsTrue(m._DealTrueDamage(50,HurtFlag::DOT),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal.");
|
||||
m.Heal(50);
|
||||
Assert::IsTrue(m._DealTrueDamage(50,HurtFlag::PLAYER_ABILITY),L"When hurting a monster that was alive and takes enough damage to die, it should still take damage and should be triggering like normal.");
|
||||
}
|
||||
TEST_METHOD(UnconsciousMonsterTest){
|
||||
Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))};
|
||||
Game::Update(1.f);
|
||||
Assert::IsFalse(parrot.IsUnconscious(),L"Parrot should not be immediately unconscious.");
|
||||
parrot.Hurt(1,parrot.OnUpperLevel(),parrot.GetZ());
|
||||
Assert::IsFalse(parrot.IsUnconscious(),L"Parrot should not be unconscious after taking 1 damage.");
|
||||
parrot.Hurt(parrot.GetMaxHealth(),parrot.OnUpperLevel(),parrot.GetZ());
|
||||
Assert::IsTrue(parrot.IsUnconscious(),L"Parrot should now be unconscious.");
|
||||
Assert::IsTrue(parrot.IsDead(),L"Parrot is considered dead.");
|
||||
Assert::IsTrue(parrot.InUndamageableState(parrot.OnUpperLevel(),parrot.GetZ()),L"Parrot should be in an undamageable state. Hopefully for obvious reasons.");
|
||||
Game::Update(MONSTER_DATA.at("Parrot").UnconsciousTime()/2);
|
||||
Assert::IsTrue(parrot.IsUnconscious(),L"Parrot should still be unconscious.");
|
||||
Assert::IsTrue(parrot.IsDead(),L"Parrot should still be considered dead.");
|
||||
Assert::IsTrue(parrot.InUndamageableState(parrot.OnUpperLevel(),parrot.GetZ()),L"Parrot should still be in an undamageable state.");
|
||||
Game::Update(MONSTER_DATA.at("Parrot").UnconsciousTime()/2);
|
||||
Assert::IsTrue(!parrot.IsUnconscious(),L"Parrot should no longer be unconscious.");
|
||||
Assert::IsTrue(parrot.IsAlive(),L"Parrot should be alive.");
|
||||
Assert::IsTrue(!parrot.InUndamageableState(parrot.OnUpperLevel(),parrot.GetZ()),L"Parrot should not be in an undamageable state.");
|
||||
}
|
||||
TEST_METHOD(UnconsciousMonsterHurtTest){
|
||||
Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))};
|
||||
Game::Update(1.f);
|
||||
parrot.Hurt(parrot.GetMaxHealth(),parrot.OnUpperLevel(),parrot.GetZ());
|
||||
Assert::IsTrue(parrot.IsUnconscious(),L"Parrot should now be unconscious.");
|
||||
}
|
||||
TEST_METHOD(MonsterCollisionRadiusTest){
|
||||
Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))};
|
||||
Game::Update(1.f);
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(parrot.GetOriginalCollisionRadius(),parrot.GetCollisionRadius(),L"Parrot collision radius should be normal.");
|
||||
|
||||
Assert::AreEqual(int(game->GetPlayer()->GetMaxHealth()-parrot.GetCollisionDamage()),game->GetPlayer()->GetHealth(),L"Player should take collision damage from the parrot.");
|
||||
parrot.SetCollisionRadius(0.f);
|
||||
Assert::AreEqual(0.f,parrot.GetCollisionRadius(),L"Parrot collision radius should now be zero.");
|
||||
game->GetPlayer()->Heal(game->GetPlayer()->GetMaxHealth());
|
||||
game->GetPlayer()->_SetIframes(0.f);
|
||||
parrot.SetPos({});
|
||||
game->GetPlayer()->ForceSetPos({});
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(game->GetPlayer()->GetMaxHealth(),game->GetPlayer()->GetHealth(),L"Player should be full health.");
|
||||
parrot.SetCollisionRadius(parrot.GetOriginalCollisionRadius());
|
||||
Game::Update(1.f);
|
||||
Assert::AreEqual(int(game->GetPlayer()->GetMaxHealth()-parrot.GetCollisionDamage()),game->GetPlayer()->GetHealth(),L"Player should take collision damage from the parrot.");
|
||||
}
|
||||
TEST_METHOD(MonsterCollisionRadiusSizeTest){
|
||||
Monster&parrot{game->SpawnMonster({},MONSTER_DATA.at("Parrot"))};
|
||||
}
|
||||
};
|
||||
}
|
@ -42,8 +42,6 @@ All rights reserved.
|
||||
#include <format>
|
||||
#include "ItemDrop.h"
|
||||
#include "DamageNumber.h"
|
||||
#include <ranges>
|
||||
#include "GameHelper.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
using namespace olc::utils;
|
||||
@ -51,7 +49,6 @@ using namespace olc::utils;
|
||||
INCLUDE_GFX
|
||||
INCLUDE_ITEM_DATA
|
||||
INCLUDE_DAMAGENUMBER_LIST
|
||||
INCLUDE_INITIALIZEGAMECONFIGURATIONS
|
||||
|
||||
extern std::mt19937 rng;
|
||||
|
||||
@ -65,7 +62,6 @@ namespace PlayerTests
|
||||
Player*player;
|
||||
HWButton*testKey;
|
||||
TEST_METHOD_INITIALIZE(PlayerInitialize){
|
||||
InitializeGameConfigurations();
|
||||
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
|
||||
testGame.reset(new AiL(true));
|
||||
ItemAttribute::Initialize();
|
||||
@ -85,16 +81,15 @@ namespace PlayerTests
|
||||
|
||||
#pragma region Setup a fake test map and test monster
|
||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||
ItemDrop::ClearDrops();
|
||||
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
|
||||
testGame->ResetLevelStates();
|
||||
|
||||
player=testGame->GetPlayer();
|
||||
//Setup key "0" as a test input
|
||||
testKeyboardInput.AddKeybind(Input{InputType::KEY,0});
|
||||
testKey=testGame->GetKeyboardState(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();
|
||||
@ -103,8 +98,6 @@ namespace PlayerTests
|
||||
}
|
||||
TEST_METHOD_CLEANUP(CleanupTests){
|
||||
testGame->EndGame();
|
||||
testGame->OnUserUpdate(0.f);
|
||||
testGame.reset();
|
||||
}
|
||||
TEST_METHOD(PlayerAlive){
|
||||
Assert::IsTrue(player->IsAlive());
|
||||
@ -322,11 +315,11 @@ namespace PlayerTests
|
||||
Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100.");
|
||||
}
|
||||
TEST_METHOD(HealthStatUpBuffCheck){
|
||||
Game::AddBuffToPlayer(BuffType::STAT_UP,5.f,1000.f,{"Health"});
|
||||
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){
|
||||
Game::AddBuffToPlayer(BuffType::STAT_UP,5.f,1000.0_Pct,{"Health %"});
|
||||
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){
|
||||
@ -438,18 +431,6 @@ namespace PlayerTests
|
||||
player->CheckAndPerformAbility(player->GetAbility2(),testKeyboardInput);
|
||||
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput);
|
||||
player->CheckAndPerformAbility(player->GetAbility4(),testKeyboardInput);
|
||||
|
||||
Inventory::AddItem("Health Potion"s);
|
||||
Inventory::AddItem("Berries"s);
|
||||
Inventory::AddItem("Mana Potion"s);
|
||||
game->SetLoadoutItem(0,"Health Potion");
|
||||
game->SetLoadoutItem(1,"Berries");
|
||||
game->SetLoadoutItem(2,"Mana Potion");
|
||||
|
||||
player->CheckAndPerformAbility(player->GetItem1(),testKeyboardInput);
|
||||
player->CheckAndPerformAbility(player->GetItem2(),testKeyboardInput);
|
||||
player->CheckAndPerformAbility(player->GetItem3(),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.");
|
||||
@ -457,9 +438,6 @@ namespace PlayerTests
|
||||
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.");
|
||||
Assert::AreNotEqual(0.f,player->GetItem1().cooldown,L"Using an item ability with 100% CDR should not affect the cooldown.");
|
||||
Assert::AreNotEqual(0.f,player->GetItem2().cooldown,L"Using an item ability with 100% CDR should not affect the cooldown.");
|
||||
Assert::AreNotEqual(0.f,player->GetItem3().cooldown,L"Using an item ability with 100% CDR should not affect the cooldown.");
|
||||
}
|
||||
TEST_METHOD(CritRateStatEquipCheck){
|
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor2"s)};
|
||||
@ -505,10 +483,10 @@ namespace PlayerTests
|
||||
}
|
||||
TEST_METHOD(DamageReductionStatEquipCheck){
|
||||
std::weak_ptr<Item>setArmor{Inventory::AddItem("Test Armor3"s)};
|
||||
Assert::AreEqual(0.0_Pct,player->GetDamageReductionFromBuffs(),L"Damage Reduction is 0%");
|
||||
Assert::AreEqual(0.0_Pct,player->GetDamageReductionPct(),L"Damage Reduction is 0%");
|
||||
|
||||
Inventory::EquipItem(setArmor,EquipSlot::ARMOR);
|
||||
Assert::AreEqual(75.0_Pct,player->GetDamageReductionFromBuffs(),L"Max Damage Reduction is 75%, even if a piece has 100% damage reduction.");
|
||||
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)};
|
||||
@ -594,7 +572,6 @@ namespace PlayerTests
|
||||
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);
|
||||
Game::Update(0.f);
|
||||
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");
|
||||
@ -614,7 +591,7 @@ namespace PlayerTests
|
||||
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->GetType()==DamageNumberType::DOT)return count+1;
|
||||
if(damageNumber->type==DamageNumberType::DOT)return count+1;
|
||||
else return count;
|
||||
})
|
||||
,L"There should be a damage number of type DOT.");
|
||||
@ -623,181 +600,10 @@ namespace PlayerTests
|
||||
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->GetType()==DamageNumberType::DOT)return count+1;
|
||||
if(damageNumber->type==DamageNumberType::DOT)return count+1;
|
||||
else return count;
|
||||
})
|
||||
,L"There should be 2 damage numbers of type DOT.");
|
||||
}
|
||||
TEST_METHOD(HasEnchantCheck){
|
||||
std::weak_ptr<Item>slimeKingRing{Inventory::AddItem("Ring of the Slime King"s)};
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
|
||||
slimeKingRing.lock()->_EnchantItem("Emergency Recovery"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Emergency Recovery"));
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING2);
|
||||
Assert::IsTrue(player->HasEnchant("Emergency Recovery"));
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
|
||||
slimeKingRing.lock()->_EnchantItem("Reaper of Souls"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Reaper of Souls"));
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING2);
|
||||
Assert::IsTrue(player->HasEnchant("Reaper of Souls"));
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING1);
|
||||
slimeKingRing.lock()->_EnchantItem("Attack Boost"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Attack Boost"));
|
||||
Inventory::EquipItem(slimeKingRing,EquipSlot::RING2);
|
||||
Assert::IsTrue(player->HasEnchant("Attack Boost"));
|
||||
|
||||
std::weak_ptr<Item>leatherHelmet{Inventory::AddItem("Leather Helmet"s)};
|
||||
std::weak_ptr<Item>leatherArmor{Inventory::AddItem("Leather Armor"s)};
|
||||
std::weak_ptr<Item>leatherPants{Inventory::AddItem("Leather Pants"s)};
|
||||
std::weak_ptr<Item>leatherGloves{Inventory::AddItem("Leather Gloves"s)};
|
||||
std::weak_ptr<Item>leatherShoes{Inventory::AddItem("Leather Shoes"s)};
|
||||
std::weak_ptr<Item>woodenSword{Inventory::AddItem("Wooden Sword"s)};
|
||||
|
||||
Inventory::EquipItem(leatherHelmet,EquipSlot::HELMET);
|
||||
Inventory::EquipItem(leatherArmor,EquipSlot::ARMOR);
|
||||
Inventory::EquipItem(leatherPants,EquipSlot::PANTS);
|
||||
Inventory::EquipItem(leatherGloves,EquipSlot::GLOVES);
|
||||
Inventory::EquipItem(leatherShoes,EquipSlot::SHOES);
|
||||
Inventory::EquipItem(woodenSword,EquipSlot::WEAPON);
|
||||
|
||||
leatherHelmet.lock()->_EnchantItem("Wizard's Soul"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Wizard's Soul"));
|
||||
leatherArmor.lock()->_EnchantItem("Ability Haste"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Ability Haste"));
|
||||
leatherPants.lock()->_EnchantItem("Improved Ground Slam"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Improved Ground Slam"));
|
||||
leatherGloves.lock()->_EnchantItem("Battle Shout"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Battle Shout"));
|
||||
leatherShoes.lock()->_EnchantItem("Attack Boost"sv);
|
||||
Inventory::UnequipItem(EquipSlot::RING2);
|
||||
Assert::IsTrue(player->HasEnchant("Attack Boost"));
|
||||
woodenSword.lock()->_EnchantItem("Mana Pool"sv);
|
||||
Assert::IsTrue(player->HasEnchant("Mana Pool"));
|
||||
|
||||
Inventory::UnequipItem(EquipSlot::HELMET);
|
||||
Inventory::UnequipItem(EquipSlot::ARMOR);
|
||||
Inventory::UnequipItem(EquipSlot::PANTS);
|
||||
Inventory::UnequipItem(EquipSlot::GLOVES);
|
||||
Inventory::UnequipItem(EquipSlot::SHOES);
|
||||
Inventory::UnequipItem(EquipSlot::WEAPON);
|
||||
Inventory::UnequipItem(EquipSlot::RING1);
|
||||
Inventory::UnequipItem(EquipSlot::RING2);
|
||||
|
||||
Assert::IsFalse(player->HasEnchant("Emergency Recovery"));
|
||||
Assert::IsFalse(player->HasEnchant("Reaper of Souls"));
|
||||
Assert::IsFalse(player->HasEnchant("Attack Boost"));
|
||||
Assert::IsFalse(player->HasEnchant("Wizard's Soul"));
|
||||
Assert::IsFalse(player->HasEnchant("Ability Haste"));
|
||||
Assert::IsFalse(player->HasEnchant("Improved Ground Slam"));
|
||||
Assert::IsFalse(player->HasEnchant("Battle Shout"));
|
||||
Assert::IsFalse(player->HasEnchant("Attack Boost"));
|
||||
Assert::IsFalse(player->HasEnchant("Mana Pool"));
|
||||
}
|
||||
TEST_METHOD(AutoAttackReductionFunctionCheck){
|
||||
Assert::AreEqual(0.f,player->GetAutoAttackTimer(),L"Auto attack timer is not on cooldown.");
|
||||
player->AutoAttack();
|
||||
Assert::AreEqual("Warrior.Auto Attack.Cooldown"_F,player->GetAutoAttackTimer(),L"Auto attack timer should've went on cooldown.");
|
||||
player->ReduceAutoAttackTimer(0.2f);
|
||||
Assert::AreEqual("Warrior.Auto Attack.Cooldown"_F-0.2f,player->GetAutoAttackTimer(),L"Auto attack timer should've been reduced by 0.2 seconds.");
|
||||
}
|
||||
TEST_METHOD(CooldownChargesCheck){
|
||||
player->GetAbility4()=Ranger::ability1;
|
||||
|
||||
for(const std::reference_wrapper<Ability>&a:player->GetAbilities()){
|
||||
if(a.get().name=="???")continue;
|
||||
Assert::AreEqual(uint8_t(1),a.get().charges,util::wformat("Ability {} start with 1 charge.",a.get().name).c_str());
|
||||
Assert::AreEqual(0.f,a.get().cooldown,util::wformat("Ability {} start off cooldown.",a.get().name).c_str());
|
||||
|
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
player->CheckAndPerformAbility(a.get(),testKeyboardInput);
|
||||
Assert::AreEqual(uint8_t(0),a.get().charges,util::wformat("Ability {} should consume a charge when used.",a.get().name).c_str());
|
||||
Assert::AreEqual(a.get().COOLDOWN_TIME,a.get().cooldown,util::wformat("Ability {} go on cooldown when used.",a.get().name).c_str());
|
||||
player->RestoreMana(100);
|
||||
player->SetState(State::NORMAL);
|
||||
player->CheckAndPerformAbility(a.get(),testKeyboardInput);
|
||||
a.get().MAX_CHARGES=5;
|
||||
for(int i:std::ranges::iota_view(0,5)){
|
||||
testGame->SetElapsedTime(a.get().COOLDOWN_TIME);
|
||||
testGame->OnUserUpdate(a.get().COOLDOWN_TIME);
|
||||
|
||||
Assert::AreEqual(uint8_t(i+1),a.get().charges,util::wformat("Ability {} should increment their charge count when they are completely off cooldown.",a.get().name).c_str());
|
||||
if(i!=4)Assert::AreEqual(a.get().COOLDOWN_TIME,a.get().cooldown,util::wformat("Ability {} should go on cooldown again when their stack count is not maxed out.",a.get().name).c_str());
|
||||
else Assert::AreEqual(0.f,a.get().cooldown,util::wformat("Ability {} should not go on cooldown again when their stack count is maxed out.",a.get().name).c_str());
|
||||
}
|
||||
player->RestoreMana(100);
|
||||
player->SetState(State::NORMAL);
|
||||
}
|
||||
}
|
||||
TEST_METHOD(ChargesEquipBehaviorCheck){
|
||||
testGame->ChangePlayerClass(RANGER);
|
||||
player=testGame->GetPlayer();
|
||||
player->GetAbility3().charges=3;
|
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
testGame->SetElapsedTime(0.25f);
|
||||
testGame->OnUserUpdate(0.25f);
|
||||
Assert::AreEqual(uint8_t(1),player->GetAbility3().charges,L"Back down to 1 charge without having Multi-Multishot enchant equipped.");
|
||||
player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput);
|
||||
Assert::AreEqual(uint8_t(0),player->GetAbility3().charges,L"Back down to 0 charges without having Multi-Multishot enchant equipped.");
|
||||
}
|
||||
TEST_METHOD(CooldownEquipBehaviorCheck){
|
||||
testGame->ChangePlayerClass(RANGER);
|
||||
player=testGame->GetPlayer();
|
||||
float oldCooldownTime{player->GetAbility3().GetCooldownTime()};
|
||||
player->GetAbility3().cooldown=player->GetAbility3().GetCooldownTime();
|
||||
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
|
||||
Inventory::EquipItem(nullRing,EquipSlot::RING1);
|
||||
nullRing.lock()->_EnchantItem("Multi-Multishot"sv);
|
||||
testKey->bHeld=true; //Force the key to be held down for testing purposes.
|
||||
Assert::AreEqual(player->GetAbility3().GetCooldownTime(),oldCooldownTime-oldCooldownTime*"Multi-Multishot"_ENC["COOLDOWN REDUCTION PCT"]/100.f,L"Old cooldown time with multishot cooldown reduction pct matches.");
|
||||
testGame->SetElapsedTime(0.f);
|
||||
testGame->OnUserUpdate(0.f);
|
||||
Assert::AreEqual(player->GetAbility3().GetCooldownTime(),player->GetAbility3().cooldown,L"Cooldown now matches the new reduced amount.");
|
||||
}
|
||||
TEST_METHOD(PlayerGetShieldCheck){
|
||||
player=testGame->GetPlayer();
|
||||
Assert::AreEqual(0U,player->GetShield(),L"Player should have no shields at first.");
|
||||
}
|
||||
TEST_METHOD(PlayerAddShieldCheck){
|
||||
player=testGame->GetPlayer();
|
||||
player->AddShield(60U,5.f,PlayerTimerType::ADVANCE_SHIELD_TIMER);
|
||||
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
|
||||
testGame->SetElapsedTime(5.f);
|
||||
testGame->OnUserUpdate(5.f);
|
||||
Assert::AreEqual(0U,player->GetShield(),L"Player should lose the shield after 5 seconds.");
|
||||
}
|
||||
TEST_METHOD(PlayerMultiShieldCheck){
|
||||
player=testGame->GetPlayer();
|
||||
player->AddShield(60U,5.f,PlayerTimerType::ADVANCE_SHIELD_TIMER);
|
||||
player->AddShield(100U,2.f,PlayerTimerType::PLAYER_OUTLINE_TIMER);
|
||||
Assert::AreEqual(160U,player->GetShield(),L"Player has 160 shield points.");
|
||||
testGame->SetElapsedTime(2.f);
|
||||
testGame->OnUserUpdate(2.f);
|
||||
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
|
||||
testGame->SetElapsedTime(3.f);
|
||||
testGame->OnUserUpdate(3.f);
|
||||
Assert::AreEqual(0U,player->GetShield(),L"Player should lose the shield after 5 seconds.");
|
||||
}
|
||||
TEST_METHOD(PlayerSubtractShieldCheck){
|
||||
player=testGame->GetPlayer();
|
||||
player->AddShield(60U,5.f,PlayerTimerType::ADVANCE_SHIELD_TIMER);
|
||||
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
|
||||
player->SubtractShield(40U);
|
||||
Assert::AreEqual(20U,player->GetShield(),L"Player has 20 shield points.");
|
||||
player->SubtractShield(200U);
|
||||
Assert::AreEqual(0U,player->GetShield(),L"Player should have 0 shield points.");
|
||||
}
|
||||
TEST_METHOD(PlayerDamageShieldCheck){
|
||||
player=testGame->GetPlayer();
|
||||
player->AddShield(60U,5.f,PlayerTimerType::ADVANCE_SHIELD_TIMER);
|
||||
Assert::AreEqual(60U,player->GetShield(),L"Player has 60 shield points.");
|
||||
player->Hurt(20U,player->OnUpperLevel(),player->GetZ());
|
||||
Assert::AreEqual(40U,player->GetShield(),L"Player has 40 shield points.");
|
||||
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"Player should still be full health.");
|
||||
player->Hurt(200U,player->OnUpperLevel(),player->GetZ());
|
||||
Assert::AreEqual(0U,player->GetShield(),L"Player has 0 shield points.");
|
||||
Assert::AreEqual(player->GetMaxHealth(),player->GetHealth(),L"Player should still be full health.");
|
||||
player->Hurt(10U,player->OnUpperLevel(),player->GetZ());
|
||||
Assert::AreEqual(player->GetMaxHealth()-10,player->GetHealth(),L"Player now takes damage with 0 shield.");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -49,12 +49,6 @@ PrecastData::PrecastData(float castTime,float range,float size)
|
||||
|
||||
InputGroup Ability::DEFAULT;
|
||||
|
||||
float Ability::iconMouseoverTime{};
|
||||
float Ability::alphaMouseoverTime{};
|
||||
std::string Ability::tooltipText{""};
|
||||
std::string Ability::tooltipTitle{""};
|
||||
Pixel Ability::tooltipTitleCol{};
|
||||
|
||||
Ability::Ability()
|
||||
:name("???"),shortName("???"),description("???"),cooldown(0),COOLDOWN_TIME(0),input(&DEFAULT){};
|
||||
Ability::Ability(std::string name,std::string shortName,std::string description,float cooldownTime,int manaCost,InputGroup*input,std::string icon,Pixel barColor1,Pixel barColor2,PrecastData precastInfo,bool canCancelCast)
|
||||
@ -62,154 +56,4 @@ Ability::Ability(std::string name,std::string shortName,std::string description,
|
||||
|
||||
const float Ability::GetCooldownTime()const{
|
||||
return COOLDOWN_TIME;
|
||||
}
|
||||
|
||||
const bool Ability::operator==(const Ability&a)const{
|
||||
return name==a.name&&isOriginalAbility==a.isOriginalAbility;
|
||||
}
|
||||
|
||||
const std::string&Ability::GetName()const{
|
||||
return name;
|
||||
}
|
||||
|
||||
const std::string_view Ability::GetDescription()const{
|
||||
return description;
|
||||
}
|
||||
|
||||
const std::string Ability::GetNameWithPlayerModifiers()const{
|
||||
std::string prefixName{};
|
||||
std::string newName{name};
|
||||
std::string suffixName{};
|
||||
for(const auto&enchant:ItemEnchantInfo::GetAllEnchantsAffectingAbility(name)){
|
||||
const ItemEnchantInfo::AbilityDescriptionModifiers&modifiers{enchant.get().GetModifiers()};
|
||||
if(prefixName.length()>0&&modifiers.preName)prefixName+=" ";
|
||||
prefixName+=modifiers.preName.value_or("");
|
||||
if(modifiers.newName)newName=modifiers.newName.value();
|
||||
if(suffixName.length()>0&&modifiers.preName)suffixName+=" ";
|
||||
suffixName+=modifiers.postName.value_or("");
|
||||
}
|
||||
std::string finalName{prefixName};
|
||||
if(finalName.length()>0&&newName.length()>0)finalName+=' ';
|
||||
finalName+=newName;
|
||||
if(finalName.length()>0&&suffixName.length()>0)finalName+=' ';
|
||||
finalName+=suffixName;
|
||||
return finalName;
|
||||
}
|
||||
|
||||
const std::string Ability::GetDescriptionWithPlayerModifiers()const{
|
||||
using SpecialKey=std::string;
|
||||
using TranslateFunc=std::function<std::string(const datafile&data,const std::string&val)>;
|
||||
std::unordered_map<SpecialKey,TranslateFunc>specialPrefixes{
|
||||
#define TranslateFunc [&](const datafile&data,const std::string&val)->std::string
|
||||
{"DamageMult",TranslateFunc{
|
||||
float damageMult{stof(data.at(val).GetString())};
|
||||
int finalDmg{int(damageMult*game->GetPlayer()->GetAttack())};
|
||||
return std::format("{}",finalDmg);
|
||||
}},
|
||||
};
|
||||
const Pixel specialValCol{69,249,255};
|
||||
|
||||
const auto ParseVariables=[&](const std::string_view abilityText)->std::string{
|
||||
std::string finalText{};
|
||||
size_t marker{};
|
||||
std::string variableName{};
|
||||
bool bracesFound{false};
|
||||
|
||||
const auto FindAndParse=[&finalText,&bracesFound,&variableName,&specialPrefixes,&specialValCol](datafile&data)->bool{
|
||||
if(data.HasProperty(variableName)){
|
||||
finalText+=specialValCol.toHTMLColorCode()+data.GetProperty(variableName).GetFullString()+WHITE.toHTMLColorCode();
|
||||
bracesFound=false;
|
||||
variableName="";
|
||||
return true;
|
||||
}else if(size_t separatorMarker{std::string::npos};(separatorMarker=variableName.find(':'))!=std::string::npos){
|
||||
std::string separatorKey{variableName.substr(0,separatorMarker)};
|
||||
std::string separatorVal{variableName.substr(separatorMarker+1)};
|
||||
if(!specialPrefixes.count(separatorKey))ERR(std::format("Could not find translation function for key {} inside special prefixes map!",separatorKey))
|
||||
else {
|
||||
finalText+=specialValCol.toHTMLColorCode()+specialPrefixes[separatorKey](data,separatorVal)+WHITE.toHTMLColorCode();
|
||||
bracesFound=false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
while(marker<abilityText.length()){
|
||||
const char c{abilityText[marker]};
|
||||
if(!bracesFound&&c=='{')bracesFound=true;
|
||||
else if(bracesFound&&c=='}'){ //Parse the variable.
|
||||
if(!FindAndParse((*abilityConfig).get()))
|
||||
ERR(std::format("Could not find variable {} for Ability {}",variableName,GetName()));
|
||||
}else if(bracesFound)variableName+=c;
|
||||
else finalText+=c;
|
||||
marker++;
|
||||
}
|
||||
return finalText;
|
||||
};
|
||||
|
||||
std::string prefixDescription{};
|
||||
std::string newDescription{ParseVariables(description)};
|
||||
std::string suffixDescription{};
|
||||
bool descriptionHasBeenModifiedAgain{false};
|
||||
for(const auto&enchant:ItemEnchantInfo::GetAllEnchantsAffectingAbility(name)){
|
||||
const ItemEnchantInfo::AbilityDescriptionModifiers&modifiers{enchant.get().GetModifiers()};
|
||||
if(prefixDescription.length()>0&&modifiers.preDescription)prefixDescription+=" ";
|
||||
prefixDescription+=modifiers.preDescription.value_or("");
|
||||
if(modifiers.newDescription)newDescription=*modifiers.newDescription;
|
||||
if(suffixDescription.length()>0&&modifiers.preDescription)suffixDescription+=" ";
|
||||
suffixDescription+=modifiers.postDescription.value_or("");
|
||||
|
||||
const auto ParseVariablesForEnchant=[&](const std::string_view abilityText)->std::string{
|
||||
std::string finalText{};
|
||||
size_t marker{};
|
||||
std::string variableName{};
|
||||
bool bracesFound{false};
|
||||
|
||||
const auto FindAndParse=[&finalText,&bracesFound,&variableName,&specialPrefixes,&specialValCol](datafile&data)->bool{
|
||||
if(data.HasProperty(variableName)){
|
||||
finalText+=specialValCol.toHTMLColorCode()+data.GetProperty(variableName).GetFullString()+WHITE.toHTMLColorCode();
|
||||
bracesFound=false;
|
||||
variableName="";
|
||||
return true;
|
||||
}else if(size_t separatorMarker{std::string::npos};(separatorMarker=variableName.find(':'))!=std::string::npos){
|
||||
std::string separatorKey{variableName.substr(0,separatorMarker)};
|
||||
std::string separatorVal{variableName.substr(separatorMarker+1)};
|
||||
if(!specialPrefixes.count(separatorKey))ERR(std::format("Could not find translation function for key {} inside special prefixes map!",separatorKey))
|
||||
else {
|
||||
finalText+=specialValCol.toHTMLColorCode()+specialPrefixes[separatorKey](data,separatorVal)+WHITE.toHTMLColorCode();
|
||||
bracesFound=false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
while(marker<abilityText.length()){
|
||||
const char c{abilityText[marker]};
|
||||
if(!bracesFound&&c=='{')bracesFound=true;
|
||||
else if(bracesFound&&c=='}'){ //Parse the variable.
|
||||
if(!FindAndParse(enchant.get().GetData()["Ability Settings"]))
|
||||
if(!FindAndParse(enchant.get().GetData()))
|
||||
if(!FindAndParse((*(*enchant.get().GetAbility())->abilityConfig).get()))
|
||||
ERR(std::format("Could not find variable {} for enchant Ability Settings of enchantment {}",variableName,enchant.get().Name()));
|
||||
}else if(bracesFound)variableName+=c;
|
||||
else finalText+=c;
|
||||
marker++;
|
||||
}
|
||||
return finalText;
|
||||
};
|
||||
prefixDescription=ParseVariablesForEnchant(prefixDescription);
|
||||
if(!descriptionHasBeenModifiedAgain){
|
||||
descriptionHasBeenModifiedAgain=true;
|
||||
newDescription=description;
|
||||
}
|
||||
newDescription=ParseVariablesForEnchant(newDescription);
|
||||
suffixDescription=ParseVariablesForEnchant(suffixDescription);
|
||||
}
|
||||
std::string finalDescription{prefixDescription};
|
||||
if(finalDescription.length()>0&&newDescription.length()>0)finalDescription+=' ';
|
||||
finalDescription+=newDescription;
|
||||
if(finalDescription.length()>0&&suffixDescription.length()>0)finalDescription+=' ';
|
||||
finalDescription+=suffixDescription;
|
||||
return finalDescription;
|
||||
}
|
@ -37,8 +37,7 @@ All rights reserved.
|
||||
#pragma endregion
|
||||
#pragma once
|
||||
#include "Key.h"
|
||||
#include <string_view>
|
||||
#include "olcUTIL_DataFile.h"
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
class InputGroup;
|
||||
|
||||
@ -63,41 +62,24 @@ struct Ability{
|
||||
std::string description="";
|
||||
float cooldown=0;
|
||||
float COOLDOWN_TIME=0;
|
||||
uint8_t charges{1};
|
||||
uint8_t MAX_CHARGES{1};
|
||||
int manaCost=0;
|
||||
Pixel barColor1,barColor2;
|
||||
PrecastData precastInfo;
|
||||
bool canCancelCast=false;
|
||||
InputGroup*input;
|
||||
std::string icon{"pixel.png"};
|
||||
std::string icon;
|
||||
//If set to true, this ability is tied to using an item.
|
||||
bool itemAbility=false;
|
||||
//If set to true, this ability instead activates immediately when a cast occurs. When the cast finishes, nothing happens instead.
|
||||
bool actionPerformedDuringCast=false;
|
||||
bool waitForRelease=false;
|
||||
bool keyReleaseRequiredToReactivate{false}; //When the player presses an ability, they cannot use it again until they have released the key to press it down again. So this gets set to true everytime an ability activates, and set to false once the player releases that key.
|
||||
bool isOriginalAbility{false};
|
||||
//Ability action function, returns true if the ability can be casted, otherwise returns false.
|
||||
// Argument 1: Player* - player pointer
|
||||
// Argument 2: vf2d - The returned precast target position (if the ability needs to be aimed, otherwise {})
|
||||
std::function<bool(Player*,vf2d)>action=[](Player*,vf2d){return false;};
|
||||
static InputGroup DEFAULT;
|
||||
const float GetCooldownTime()const;
|
||||
const bool operator==(const Ability&a)const;
|
||||
static float iconMouseoverTime;
|
||||
static float alphaMouseoverTime; //Goes from 0 to 0.5 which affects the alpha of the tooltip.
|
||||
static std::string tooltipText;
|
||||
static std::string tooltipTitle;
|
||||
static Pixel tooltipTitleCol;
|
||||
std::optional<std::reference_wrapper<datafile>>abilityConfig;
|
||||
constexpr static float MAX_DESCRIPTION_HOVER_TIME{0.3f};
|
||||
Ability();
|
||||
//NOTE: icon expects the actual name relative to the "Ability Icons" directory for this constructor!
|
||||
Ability(std::string name,std::string shortName,std::string description,float cooldownTime,int manaCost,InputGroup*input,std::string icon,Pixel barColor1=VERY_DARK_RED,Pixel barColor2=DARK_RED,PrecastData precastInfo={},bool canCancelCast=false);
|
||||
const std::string&GetName()const;
|
||||
const std::string_view GetDescription()const;
|
||||
|
||||
const std::string GetNameWithPlayerModifiers()const;
|
||||
const std::string GetDescriptionWithPlayerModifiers()const;
|
||||
};
|
@ -29,24 +29,6 @@
|
||||
"object"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"name": "AudioEvent",
|
||||
"storageType": "string",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"LowHealth",
|
||||
"TitleScreenLoaded",
|
||||
"BlacksmithUnlock",
|
||||
"Chapter2Unlock",
|
||||
"Chapter3Unlock",
|
||||
"BossFanfare",
|
||||
"BossDefeated",
|
||||
"PreBossPhase",
|
||||
"Default Volume"
|
||||
],
|
||||
"valuesAsFlags": false
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"name": "Backdrop",
|
||||
@ -70,11 +52,7 @@
|
||||
"foresty1_1",
|
||||
"overworld",
|
||||
"foresty_boss",
|
||||
"base_camp",
|
||||
"mountain",
|
||||
"mountain_boss",
|
||||
"beach",
|
||||
"beach_boss"
|
||||
"base_camp"
|
||||
],
|
||||
"valuesAsFlags": false
|
||||
},
|
||||
@ -197,11 +175,7 @@
|
||||
"Birds2",
|
||||
"Birds3",
|
||||
"Birds4",
|
||||
"Campfire",
|
||||
"Crashing Waves",
|
||||
"Gull1",
|
||||
"Gull2",
|
||||
"Gull3"
|
||||
"Campfire"
|
||||
],
|
||||
"valuesAsFlags": false
|
||||
},
|
||||
@ -295,28 +269,14 @@
|
||||
"BOSS_2",
|
||||
"BOSS_2_B",
|
||||
"STORY_2_1",
|
||||
"STORY_2_2",
|
||||
"NONE",
|
||||
"CAMPAIGN_3_1",
|
||||
"CAMPAIGN_3_2",
|
||||
"CAMPAIGN_3_3",
|
||||
"CAMPAIGN_3_4",
|
||||
"CAMPAIGN_3_5",
|
||||
"CAMPAIGN_3_6",
|
||||
"CAMPAIGN_3_7",
|
||||
"CAMPAIGN_3_8",
|
||||
"CAMPAIGN_3_B1",
|
||||
"BOSS_3",
|
||||
"BOSS_3_B",
|
||||
"STORY_3_1",
|
||||
"STORY_3_2"
|
||||
"STORY_2_2"
|
||||
],
|
||||
"valuesAsFlags": false
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "LevelType",
|
||||
"storageType": "int",
|
||||
"storageType": "string",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"Dungeon",
|
||||
@ -359,12 +319,6 @@
|
||||
"drawFill": true,
|
||||
"id": 19,
|
||||
"members": [
|
||||
{
|
||||
"name": "Audio Event",
|
||||
"propertyType": "AudioEvent",
|
||||
"type": "string",
|
||||
"value": "Default Volume"
|
||||
},
|
||||
{
|
||||
"name": "Backdrop",
|
||||
"propertyType": "Backdrop",
|
||||
@ -415,8 +369,8 @@
|
||||
{
|
||||
"name": "Level Type",
|
||||
"propertyType": "LevelType",
|
||||
"type": "int",
|
||||
"value": 4
|
||||
"type": "string",
|
||||
"value": "World Map"
|
||||
},
|
||||
{
|
||||
"name": "Optimize",
|
||||
|
@ -130,8 +130,6 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Desktop|x64'">
|
||||
<OutDir>$(SolutionDir)$(PlatformTarget)\Release</OutDir>
|
||||
<CodeAnalysisRuleSet>..\CodeAnalysisRuleset.ruleset</CodeAnalysisRuleSet>
|
||||
<IncludePath>$(ProjectDir)include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
@ -140,21 +138,13 @@
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>$(ProjectDir)include;$(VCInstallDir)Auxiliary\VS\UnitTest\include;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(VCInstallDir)Auxiliary\VS\UnitTest\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(VCInstallDir)Auxiliary\VS\UnitTest\lib;$(LibraryPath)</LibraryPath>
|
||||
<CodeAnalysisRuleSet>..\CodeAnalysisRuleset.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Unit Testing|x64'">
|
||||
<IncludePath>$(ProjectDir)include;$(VCInstallDir)Auxiliary\VS\UnitTest\include;$(IncludePath)</IncludePath>
|
||||
<IncludePath>$(VCInstallDir)Auxiliary\VS\UnitTest\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(VCInstallDir)Auxiliary\VS\UnitTest\lib;$(LibraryPath)</LibraryPath>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<CodeAnalysisRuleSet>..\CodeAnalysisRuleset.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Emscripten|x64'">
|
||||
<CodeAnalysisRuleSet>..\CodeAnalysisRuleset.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Emscripten Debug|x64'">
|
||||
<CodeAnalysisRuleSet>..\CodeAnalysisRuleset.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@ -228,7 +218,8 @@
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;$(ProjectDir)steam;$(ProjectDir)discord-files;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\LabUser\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\LabUser\Documents\include;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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/MP20 %(AdditionalOptions)</AdditionalOptions>
|
||||
<TreatSpecificWarningsAsErrors>4099;5030;4715;4172;4834</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
@ -253,7 +244,8 @@
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)steam;$(ProjectDir)discord-files;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\LabUser\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<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</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/MP20 %(AdditionalOptions)</AdditionalOptions>
|
||||
<TreatSpecificWarningsAsErrors>4099;5030;4715;4172;4834</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
@ -283,7 +275,8 @@
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;$(ProjectDir)steam;$(ProjectDir)discord-files;C:\Users\LabUser\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\LabUser\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\discord-files;C:\Users\niconiconii\OneDrive\Documents\include;C:\Users\niconiconii\source\repos\AdventuresInLestoria\Adventures in Lestoria\steam;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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/MP20 %(AdditionalOptions)</AdditionalOptions>
|
||||
<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</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@ -375,10 +368,6 @@
|
||||
<ClInclude Include="Ability.h" />
|
||||
<ClInclude Include="AccessoryRowItemDisplay.h" />
|
||||
<ClInclude Include="Animation.h" />
|
||||
<ClInclude Include="Arc.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Attributable.h" />
|
||||
<ClInclude Include="AttributableStat.h">
|
||||
<SubType>
|
||||
@ -392,10 +381,6 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BlackHole.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BombBoom.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -405,14 +390,6 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DynamicMenuLabel.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Entity.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HurtDamageInfo.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -534,20 +511,12 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MenuDecal.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MenuDefinitions.h" />
|
||||
<ClInclude Include="MenuItemLabel.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MenuItemLoadoutButton.h" />
|
||||
<ClInclude Include="MenuRefineLabel.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MenuType.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -558,10 +527,6 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="MonsterData.h" />
|
||||
<ClInclude Include="olcPGEX_SplashScreen.h" />
|
||||
<ClInclude Include="Oscillator.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Overlay.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -620,10 +585,6 @@
|
||||
<ClInclude Include="olcUTIL_Geometry2D.h" />
|
||||
<ClInclude Include="Pathfinding.h" />
|
||||
<ClInclude Include="Player.h" />
|
||||
<ClInclude Include="PlayerTimerType.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PopupMenuLabel.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -661,7 +622,6 @@
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="State.h" />
|
||||
<ClInclude Include="State_Arena.h" />
|
||||
<ClInclude Include="State_Death.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -747,15 +707,10 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="TitleScreen.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TrailEffect.h">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Tutorial.h" />
|
||||
<ClInclude Include="VisualNovel.h" />
|
||||
<ClInclude Include="Test.h" />
|
||||
@ -774,11 +729,11 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Ability.cpp" />
|
||||
<ClCompile Include="Animation.cpp" />
|
||||
<ClCompile Include="Arc.cpp">
|
||||
<ClCompile Include="Arrow.cpp" />
|
||||
<ClCompile Include="ArtificerDisassembleConfirmWindow.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Arrow.cpp" />
|
||||
<ClCompile Include="ArtificerDisassembleWindow.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -791,7 +746,7 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArtificerRefineResultWindow.cpp">
|
||||
<ClCompile Include="ArtificerRefineConfirmWindow.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
@ -840,49 +795,10 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BurstBullet.cpp" />
|
||||
<ClCompile Include="CollectedCoinEffect.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Crab.cpp" />
|
||||
<ClCompile Include="Entity.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExplosiveTrap.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FadeInOutEffect.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FlipCoinEffect.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GhostSaber.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GhostOfPirateCaptain.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GiantCrab.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GiantOctopus.cpp" />
|
||||
<ClCompile Include="HomingBullet.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HubPauseMenu.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IBullet.cpp" />
|
||||
<ClCompile Include="BuyItemWindow.cpp">
|
||||
<SubType>
|
||||
@ -1005,14 +921,6 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Ink.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InkBullet.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InputHelper.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -1044,15 +952,7 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ItemHubLoadout.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ItemLoadoutWindow.cpp" />
|
||||
<ClCompile Include="ItemScript.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Key.cpp" />
|
||||
<ClCompile Include="LargeStone.cpp" />
|
||||
<ClCompile Include="LargeTornado.cpp">
|
||||
@ -1100,15 +1000,10 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MonsterSoul.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NPC.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OctopusArm.cpp" />
|
||||
<ClCompile Include="Overlay.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -1119,40 +1014,15 @@
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="packkey.cpp" />
|
||||
<ClCompile Include="Parrot.cpp" />
|
||||
<ClCompile Include="PauseMenu.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirates_Treasure.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirates_Coin.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirate_Buccaneer.cpp" />
|
||||
<ClCompile Include="Pirate_Captain.cpp" />
|
||||
<ClCompile Include="Pirate_Marauder.cpp" />
|
||||
<ClCompile Include="Pixel.cpp" />
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RotateBullet.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="State_Arena.cpp" />
|
||||
<ClCompile Include="ThrownProjectile.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonPool.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PurpleEnergyBall.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -1171,7 +1041,6 @@
|
||||
<ClCompile Include="PulsatingFire.cpp" />
|
||||
<ClCompile Include="Ranger.cpp" />
|
||||
<ClCompile Include="RUN_STRATEGY.cpp" />
|
||||
<ClCompile Include="Sandworm.cpp" />
|
||||
<ClCompile Include="SaveFile.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -1180,7 +1049,6 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Seagull.cpp" />
|
||||
<ClCompile Include="SellItemWindow.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
@ -1204,7 +1072,7 @@
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FallingBullet.cpp">
|
||||
<ClCompile Include="FallingStone.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
@ -1259,10 +1127,6 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="Test.cpp" />
|
||||
<ClCompile Include="Thief.cpp" />
|
||||
<ClCompile Include="Timer.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TitleScreen.cpp" />
|
||||
<ClCompile Include="Tornado.cpp">
|
||||
<SubType>
|
||||
@ -1290,23 +1154,7 @@
|
||||
<ClCompile Include="Zephy.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\commit.bat" />
|
||||
<None Include="..\debug.sh" />
|
||||
<None Include="..\debugGame.sh" />
|
||||
<None Include="..\distribute.ps1" />
|
||||
<None Include="..\distribute.sh" />
|
||||
<None Include="..\emscripten_build.ps1" />
|
||||
<None Include="..\emscripten_build.sh" />
|
||||
<None Include="..\emscripten_debug_build.ps1" />
|
||||
<None Include="..\emscripten_debug_build.sh" />
|
||||
<None Include="..\emscripten_run.ps1" />
|
||||
<None Include="..\emscripten_run.sh" />
|
||||
<None Include="..\read_debug_log.ps1" />
|
||||
<None Include="..\release.sh" />
|
||||
<None Include="..\runGame - NO STEAM.bat" />
|
||||
<None Include="..\runGame.bat" />
|
||||
<None Include="..\runGame.sh" />
|
||||
<None Include="..\unit-testing-prebuild.ps1" />
|
||||
<None Include="ClassDiagram.cd" />
|
||||
<None Include="ClassDiagram2.cd" />
|
||||
<None Include="cpp.hint" />
|
||||
@ -1314,13 +1162,6 @@
|
||||
<None Include="steam\steam_api.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\Accessories-test.txt" />
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\Equipment-test.txt" />
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\ItemDatabase-test.txt" />
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\ItemEnchants-test.txt" />
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\items-test.txt" />
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\ItemSets-test.txt" />
|
||||
<Text Include="..\x64\Unit Testing\debug.log" />
|
||||
<Text Include="assets\config\Achievements.txt" />
|
||||
<Text Include="assets\config\audio\audio.txt" />
|
||||
<Text Include="assets\config\audio\bgm.txt" />
|
||||
@ -1371,10 +1212,8 @@
|
||||
<Text Include="Chapter_1_Creatures_Part_2.txt" />
|
||||
<Text Include="Chapter_2_Boss.txt" />
|
||||
<Text Include="Chapter_2_Monsters.txt" />
|
||||
<Text Include="Chapter_3_FinalBoss.txt" />
|
||||
<Text Include="Chapter_3_Monsters.txt" />
|
||||
<Text Include="characters.txt" />
|
||||
<Text Include="ConsoleCommands.txt" />
|
||||
<Text Include="Crawler_2_Bonus_Boss.txt" />
|
||||
<Text Include="Crawler_Artificer.txt" />
|
||||
<Text Include="Crawler_System_Overview.txt" />
|
||||
@ -1383,7 +1222,6 @@
|
||||
<Text Include="Merchant%27s Items.txt" />
|
||||
<Text Include="NewClasses.txt" />
|
||||
<Text Include="InitialConcept.txt" />
|
||||
<Text Include="Oktopus boss.txt" />
|
||||
<Text Include="Slime_King_Encounter.txt" />
|
||||
<Text Include="StatCalculations.txt" />
|
||||
<Text Include="TODO.txt" />
|
||||
|
@ -97,18 +97,6 @@
|
||||
<Filter Include="Header Files\Engine">
|
||||
<UniqueIdentifier>{aaa148fb-5e34-4c35-a5bf-65ee8f2c0fb1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Documentation\Admin">
|
||||
<UniqueIdentifier>{e565fb16-43e6-4d18-a450-af7474df70b9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Scripts">
|
||||
<UniqueIdentifier>{eba9dd86-1d5d-4c68-8dcb-760c759099c0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Debug">
|
||||
<UniqueIdentifier>{c4119802-3fc8-4555-9013-a7a3ac9b204d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Configurations\Items\Test Configurations">
|
||||
<UniqueIdentifier>{354b389b-2ec1-4cf6-ace0-6597b14edfab}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="olcPixelGameEngine.h">
|
||||
@ -240,6 +228,9 @@
|
||||
<ClInclude Include="MenuAnimatedIconToggleButton.h">
|
||||
<Filter>Header Files\Interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toggleable.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConnectionPoint.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -684,42 +675,6 @@
|
||||
<ClInclude Include="ItemEnchant.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PlayerTimerType.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toggleable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Timer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BlackHole.h">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TrailEffect.h">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Oscillator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MenuDecal.h">
|
||||
<Filter>Header Files\Interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MenuRefineLabel.h">
|
||||
<Filter>Header Files\Interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DynamicMenuLabel.h">
|
||||
<Filter>Header Files\Interface</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Arc.h">
|
||||
<Filter>Header Files\Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="State_Arena.h">
|
||||
<Filter>Header Files\State</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Entity.h">
|
||||
<Filter>Header Files\State</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Player.cpp">
|
||||
@ -1178,7 +1133,7 @@
|
||||
<ClCompile Include="RockLaunch.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FallingBullet.cpp">
|
||||
<ClCompile Include="FallingStone.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShineEffect.cpp">
|
||||
@ -1202,7 +1157,7 @@
|
||||
<ClCompile Include="PurpleEnergyBall.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ThrownProjectile.cpp">
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="State_Dialog.cpp">
|
||||
@ -1214,9 +1169,15 @@
|
||||
<ClCompile Include="ArtificerRefineWindow.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArtificerRefineConfirmWindow.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArtificerDisassembleWindow.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArtificerDisassembleConfirmWindow.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArtificerEnchantWindow.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
@ -1226,105 +1187,6 @@
|
||||
<ClCompile Include="ItemEnchant.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MonsterSoul.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Timer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FadeInOutEffect.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonPool.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArtificerRefineResultWindow.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirate_Marauder.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Crab.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Parrot.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirate_Buccaneer.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirate_Captain.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Sandworm.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Seagull.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GiantCrab.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ItemScript.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GiantOctopus.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OctopusArm.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Arc.cpp">
|
||||
<Filter>Source Files\Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BurstBullet.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Entity.cpp">
|
||||
<Filter>Source Files\Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RotateBullet.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InkBullet.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Ink.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HomingBullet.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HubPauseMenu.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ItemHubLoadout.cpp">
|
||||
<Filter>Source Files\Interface</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GhostOfPirateCaptain.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GhostSaber.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirates_Treasure.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pirates_Coin.cpp">
|
||||
<Filter>Source Files\Monster Strategies</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FlipCoinEffect.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CollectedCoinEffect.cpp">
|
||||
<Filter>Source Files\Effects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="State_Arena.cpp">
|
||||
<Filter>Source Files\Game States</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
@ -1337,54 +1199,6 @@
|
||||
<Filter>Header Files\steam</Filter>
|
||||
</None>
|
||||
<None Include="..\read_debug_log.ps1" />
|
||||
<None Include="..\commit.bat">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\debug.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\debugGame.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\distribute.ps1">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\distribute.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\emscripten_build.ps1">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\emscripten_build.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\emscripten_debug_build.ps1">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\emscripten_debug_build.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\emscripten_run.ps1">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\emscripten_run.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\release.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\runGame - NO STEAM.bat">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\runGame.bat">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\runGame.sh">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
<None Include="..\unit-testing-prebuild.ps1">
|
||||
<Filter>Scripts</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="InitialConcept.txt">
|
||||
@ -1574,36 +1388,6 @@
|
||||
<Text Include="assets\config\items\ItemEnchants.txt">
|
||||
<Filter>Configurations\Items</Filter>
|
||||
</Text>
|
||||
<Text Include="ConsoleCommands.txt">
|
||||
<Filter>Documentation\Admin</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\debug.log">
|
||||
<Filter>Debug</Filter>
|
||||
</Text>
|
||||
<Text Include="Oktopus boss.txt">
|
||||
<Filter>Documentation\Mechanics</Filter>
|
||||
</Text>
|
||||
<Text Include="Chapter_3_FinalBoss.txt">
|
||||
<Filter>Documentation\Mechanics</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\Accessories-test.txt">
|
||||
<Filter>Configurations\Items\Test Configurations</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\Equipment-test.txt">
|
||||
<Filter>Configurations\Items\Test Configurations</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\ItemDatabase-test.txt">
|
||||
<Filter>Configurations\Items\Test Configurations</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\ItemEnchants-test.txt">
|
||||
<Filter>Configurations\Items\Test Configurations</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\ItemSets-test.txt">
|
||||
<Filter>Configurations\Items\Test Configurations</Filter>
|
||||
</Text>
|
||||
<Text Include="..\x64\Unit Testing\assets\config\items\items-test.txt">
|
||||
<Filter>Configurations\Items\Test Configurations</Filter>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="assets\heart.ico">
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -60,28 +60,22 @@ All rights reserved.
|
||||
#include "Overlay.h"
|
||||
#include <variant>
|
||||
|
||||
#undef KEY_MENU
|
||||
#undef KEY_SELECT
|
||||
#undef KEY_SCROLLDOWN
|
||||
#undef KEY_SCROLLUP
|
||||
#undef KEY_ENTER
|
||||
|
||||
class SteamKeyboardCallbackHandler;
|
||||
class SteamStatsReceivedHandler;
|
||||
|
||||
INCLUDE_BULLET_LIST
|
||||
|
||||
#define CreateBullet(type) \
|
||||
(*((type*const)BULLET_LIST.emplace_back(std::make_unique<type>(type
|
||||
#define EndBullet )).get()))
|
||||
#define CreateBullet(type) INCLUDE_BULLET_LIST \
|
||||
BULLET_LIST.push_back(std::make_unique<type>(type
|
||||
#define EndBullet ));
|
||||
|
||||
using HurtReturnValue=bool;
|
||||
using HurtListItem=std::pair<std::variant<Monster*,Player*>,HurtReturnValue>;
|
||||
using HurtList=std::vector<HurtListItem>;
|
||||
using HurtList=std::vector<std::pair<std::variant<Monster*,Player*>,HurtReturnValue>>;
|
||||
using StackCount=uint8_t;
|
||||
using MarkTime=float;
|
||||
using ForegroundWrapper=std::reference_wrapper<Effect>;
|
||||
using BackgroundWrapper=std::reference_wrapper<Effect>;
|
||||
|
||||
enum class HurtType{
|
||||
PLAYER=0b01,
|
||||
MONSTER=0b10,
|
||||
};
|
||||
|
||||
enum class KnockbackCondition{
|
||||
KNOCKBACK_HURT_TARGETS, //Knockback only targets that took damage.
|
||||
@ -101,23 +95,12 @@ class AiL : public olc::PixelGameEngine
|
||||
friend class sig::Animation;
|
||||
friend class Audio;
|
||||
friend class Minimap;
|
||||
friend class MiniAudio;
|
||||
friend class Arc;
|
||||
friend class MonsterTests::MonsterTest;
|
||||
friend class PlayerTests::PlayerTest;
|
||||
friend class ItemTests::ItemTest;
|
||||
std::unique_ptr<Player>player;
|
||||
SplashScreen splash;
|
||||
public:
|
||||
class Notification{
|
||||
std::string message;
|
||||
float time;
|
||||
Pixel col;
|
||||
public:
|
||||
#undef GetMessage
|
||||
Notification(const std::string_view message,const float time,const Pixel col);
|
||||
void Update(const float fElapsedTime);
|
||||
const bool Expired()const;
|
||||
const bool operator==(const Notification&n)const;
|
||||
void Draw(PixelGameEngine&pge);
|
||||
};
|
||||
enum MusicChange{
|
||||
NO_MUSIC_CHANGE,
|
||||
PLAY_LEVEL_MUSIC,
|
||||
@ -168,11 +151,11 @@ public:
|
||||
double levelTime=0.;
|
||||
Camera2D camera;
|
||||
std::map<MapName,Map>MAP_DATA;
|
||||
|
||||
ResourcePack gamepack;
|
||||
private:
|
||||
std::vector<std::unique_ptr<Effect>>foregroundEffects,backgroundEffects,foregroundEffectsToBeInserted,backgroundEffectsToBeInserted;
|
||||
std::vector<std::unique_ptr<Effect>>foregroundEffects,backgroundEffects,foregroundEffectsToBeInserted,backgroundEffectsToBeInserted;
|
||||
std::vector<TileRenderData*>tilesWithCollision,tilesWithoutCollision;
|
||||
std::vector<int>dropsBeforeLower,dropsAfterLower,dropsBeforeUpper,dropsAfterUpper;
|
||||
std::vector<ZoneData>endZones,upperEndZones;
|
||||
std::vector<vf2d>circleCooldownPoints;
|
||||
std::vector<vf2d>squareCircleCooldownPoints;
|
||||
std::map<std::string,TilesetData>MAP_TILESETS;
|
||||
@ -187,7 +170,11 @@ private:
|
||||
std::vector<TileGroup>upperForegroundTileGroups;
|
||||
int bridgeLayerIndex=-1;
|
||||
float bridgeFadeFactor=0.f;
|
||||
void InitializeClasses();
|
||||
int DEBUG_PATHFINDING=0;
|
||||
std::vector<Monster*>monstersBeforeLower,monstersAfterLower,monstersBeforeUpper,monstersAfterUpper;
|
||||
std::vector<IBullet*>bulletsLower,bulletsUpper;
|
||||
std::vector<Effect*>backgroundEffectsLower,backgroundEffectsUpper,foregroundEffectsLower,foregroundEffectsUpper;
|
||||
float reflectionUpdateTimer=0;
|
||||
float reflectionStepTime=0;
|
||||
std::set<vi2d>visibleTiles;
|
||||
@ -202,7 +189,7 @@ private:
|
||||
float fadeOutDuration=0;
|
||||
States::State transitionState=States::State::GAME_RUN;
|
||||
bool gameEnd=false;
|
||||
std::vector<std::shared_ptr<Monster>>monstersToBeSpawned;
|
||||
std::vector<Monster>monstersToBeSpawned;
|
||||
bool aMonsterIsMarkedForDeletion=false; //DO NOT MODIFY DIRECTLY! Use AMonsterIsMarkedForDeletion() instead!
|
||||
time_t gameStarted;
|
||||
std::function<void(std::string_view)>responseCallback;
|
||||
@ -211,14 +198,13 @@ private:
|
||||
bool disableFadeIn=false;
|
||||
DynamicCounter healthCounter;
|
||||
DynamicCounter manaCounter;
|
||||
DynamicCounter shieldCounter;
|
||||
int playerShieldDisplayAmt{};
|
||||
Pixel worldColor=WHITE;
|
||||
std::function<Pixel(vi2d)>worldColorFunc=[](vi2d pos){return WHITE;};
|
||||
std::map<std::string,std::vector<::ZoneData>>ZONE_LIST;
|
||||
float lastMouseMovement=0.f; //Amount of time since the last time the cursor was moved or interacted with.
|
||||
vi2d lastMousePos={};
|
||||
bool gameInitialized=false;
|
||||
ResourcePack gamepack;
|
||||
uint8_t mosaicEffectTransition=1U;
|
||||
float saveGameDisplayTime=0.f;
|
||||
float loadingWaitTime=0.f;
|
||||
@ -244,13 +230,7 @@ private:
|
||||
float targetZoom{1.f};
|
||||
float zoomAdjustSpeed{0.1f};
|
||||
std::vector<std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>>lockOnTargets;
|
||||
std::vector<std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>>lockOnSpecialTargets;
|
||||
float lastLockOnTargetTime{};
|
||||
float lastLockOnSpecialTargetTime{};
|
||||
void AdminConsole();
|
||||
virtual bool OnConsoleCommand(const std::string& sCommand)override final;
|
||||
Pixel vignetteOverlayCol{"Interface.Vignette Color"_Pixel};
|
||||
std::vector<Notification>notifications;
|
||||
public:
|
||||
AiL(bool testingMode=false);
|
||||
bool OnUserCreate() override;
|
||||
@ -275,10 +255,9 @@ public:
|
||||
void RenderHud();
|
||||
void RenderMenu();
|
||||
bool MenuClicksDeactivated()const;
|
||||
std::pair<ForegroundWrapper,BackgroundWrapper>AddEffect(std::unique_ptr<Effect>foreground,std::unique_ptr<Effect>background);
|
||||
void AddEffect(std::unique_ptr<Effect>foreground,std::unique_ptr<Effect>background);
|
||||
//If back is true, places the effect in the background
|
||||
Effect&AddEffect(std::unique_ptr<Effect>foreground,bool back=false);
|
||||
const std::vector<Effect*>GetEffect(EffectType type);
|
||||
void AddEffect(std::unique_ptr<Effect>foreground,bool back=false);
|
||||
const HurtList Hurt(vf2d pos,float radius,int damage,bool upperLevel,float z,const HurtType hurtTargets,HurtFlag::HurtFlag hurtFlags=HurtFlag::NONE)const;
|
||||
const HurtList HurtMonsterType(vf2d pos,float radius,int damage,bool upperLevel,float z,const std::string_view monsterName,HurtFlag::HurtFlag hurtFlags=HurtFlag::NONE)const;
|
||||
//NOTE: This function will also add any enemies that were hit into the hit list!
|
||||
@ -335,7 +314,6 @@ public:
|
||||
bool IsReflectiveTile(TilesheetData tileSheet,int tileID);
|
||||
Monster&SpawnMonster(vf2d pos,MonsterData&data,bool upperLevel=false,bool isBossSpawn=false); //Queues a monster for spawning on the next frame.
|
||||
void DrawPie(vf2d center,float radius,float degreesCut,Pixel col);
|
||||
void DrawPieArc(const std::string_view texture,vf2d center,float radius,float degreesCut,Pixel col); //Draws an arc that has UV coordinates going from (0,0) in the center out to (1,1)
|
||||
void DrawSquarePie(vf2d center,float radius,float degreesCut,Pixel col);
|
||||
void RenderCooldowns();
|
||||
void InitializeDefaultKeybinds();
|
||||
@ -359,7 +337,7 @@ public:
|
||||
int GetLoadoutSize()const;
|
||||
void RestockLoadoutItems();
|
||||
//Returns true if the item can be used (we have >0 of it)
|
||||
bool UseLoadoutItem(int slot,const std::optional<vf2d>targetingPos={});
|
||||
bool UseLoadoutItem(int slot);
|
||||
//Blanks out this loadout item.
|
||||
void ClearLoadoutItem(int slot);
|
||||
void RenderFadeout();
|
||||
@ -386,7 +364,7 @@ public:
|
||||
void UpdateMonsters();
|
||||
void ActivateActionSetForAllControllers(InputActionSetHandle_t actionSetHandle);
|
||||
const float GetEncounterDuration()const;
|
||||
void ShowDamageVignetteOverlay(const Pixel="Interface.Vignette Color"_Pixel);
|
||||
void ShowDamageVignetteOverlay();
|
||||
void GlobalGameUpdates();
|
||||
const bool QuitRequested()const;
|
||||
void SetQuitAllowed(bool quittingAllowed); //Locks the game from quitting during sensitive operations such as file saving/loading.
|
||||
@ -401,23 +379,13 @@ public:
|
||||
Overlay&GetOverlay();
|
||||
void SetWindSpeed(vf2d newWindSpd);
|
||||
const vf2d&GetWindSpeed()const;
|
||||
void InitializeGameConfigurations();
|
||||
void InitializePlayer();
|
||||
void SetWorldZoom(float newZoomScale);
|
||||
//Plays the correct footstep sound based on player's current tile.
|
||||
void PlayFootstepSound();
|
||||
const std::map<std::string,TilesetData>&GetTilesets()const;
|
||||
void AddToMarkedTargetList(std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>markData);
|
||||
void AddToSpecialMarkedTargetList(std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>markData);
|
||||
void InitializeClasses();
|
||||
void _SetCurrentLevel(const MapName map); //NOTE: This will modify the currentLevel variable without triggering anything else in-game, this will normally mess up the state in the game. Ideally this is only used when initializing a test level.
|
||||
void InitializeCamera();
|
||||
void AddNotification(const Notification&n);
|
||||
|
||||
void ResetLevelStates();
|
||||
|
||||
std::vector<Effect*>GetForegroundEffects()const;
|
||||
std::vector<Effect*>GetBackgroundEffects()const;
|
||||
std::vector<Effect*>GetAllEffects()const; //Foreground and background effects in one vector.
|
||||
|
||||
struct TileGroupData{
|
||||
vi2d tilePos;
|
||||
@ -426,4 +394,4 @@ public:
|
||||
return layer<rhs.layer||(layer==rhs.layer&&tilePos<rhs.tilePos);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
@ -48,6 +48,7 @@ INCLUDE_GFX
|
||||
|
||||
void sig::Animation::InitializeAnimations(){
|
||||
ANIMATION_DATA.Reset();
|
||||
|
||||
auto CreateStillAnimation=[&](std::string imgName,vf2d size,AnimationData data={}){
|
||||
Animate2D::FrameSequence anim(data.frameDuration,data.style);
|
||||
anim.AddFrame({&GFX[imgName],{{0,0},size}});
|
||||
@ -346,28 +347,6 @@ void sig::Animation::InitializeAnimations(){
|
||||
ANIMATION_DATA["WITCH_TRANSFORM_W"]=pl_witch_transform_w;
|
||||
ANIMATION_DATA["WITCH_TRANSFORM_E"]=pl_witch_transform_e;
|
||||
|
||||
|
||||
Animate2D::FrameSequence pl_witch_cat_walk_s(0.2f);
|
||||
for(int i=0;i<2;i++){
|
||||
pl_witch_cat_walk_s.AddFrame({&GFX["nico-witch.png"],{vi2d{0+i,4}*24,{24,24}}});
|
||||
}
|
||||
ANIMATION_DATA["WITCH_CAT_WALK_S"]=pl_witch_cat_walk_s;
|
||||
Animate2D::FrameSequence pl_witch_cat_walk_n(0.2f);
|
||||
for(int i=0;i<2;i++){
|
||||
pl_witch_cat_walk_n.AddFrame({&GFX["nico-witch.png"],{vi2d{0+i,5}*24,{24,24}}});
|
||||
}
|
||||
ANIMATION_DATA["WITCH_CAT_WALK_N"]=pl_witch_cat_walk_n;
|
||||
Animate2D::FrameSequence pl_witch_cat_walk_w(0.2f);
|
||||
for(int i=0;i<2;i++){
|
||||
pl_witch_cat_walk_w.AddFrame({&GFX["nico-witch.png"],{vi2d{0+i,6}*24,{24,24}}});
|
||||
}
|
||||
ANIMATION_DATA["WITCH_CAT_WALK_W"]=pl_witch_cat_walk_w;
|
||||
Animate2D::FrameSequence pl_witch_cat_walk_e(0.2f);
|
||||
for(int i=0;i<2;i++){
|
||||
pl_witch_cat_walk_e.AddFrame({&GFX["nico-witch.png"],{vi2d{0+i,7}*24,{24,24}}});
|
||||
}
|
||||
ANIMATION_DATA["WITCH_CAT_WALK_E"]=pl_witch_cat_walk_e;
|
||||
|
||||
CreateHorizontalAnimationSequence("ground-slam-attack-back.png",5,{64,64},{0.02f,Animate2D::Style::OneShot});
|
||||
CreateHorizontalAnimationSequence("ground-slam-attack-front.png",5,{64,64},{0.02f,Animate2D::Style::OneShot});
|
||||
CreateHorizontalAnimationSequence("battlecry_effect.png",5,{84,84},{0.02f,Animate2D::Style::OneShot});
|
||||
@ -392,7 +371,6 @@ void sig::Animation::InitializeAnimations(){
|
||||
|
||||
CreateStillAnimation("chain_lightning.png",{1,9});
|
||||
|
||||
CreateHorizontalAnimationSequence("monstersoul.png",3,{24,24},AnimationData{.frameDuration{0.25f},.style{Animate2D::Style::Repeat}});
|
||||
CreateHorizontalAnimationSequence("lightning_splash_effect.png",5,{24,24});
|
||||
CreateHorizontalAnimationSequence("dagger_stab.png",2,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::PingPong}});
|
||||
CreateHorizontalAnimationSequence("goblin_sword_slash.png",3,{24,24},{0.05f,Animate2D::Style::OneShot});
|
||||
@ -401,9 +379,6 @@ void sig::Animation::InitializeAnimations(){
|
||||
CreateHorizontalAnimationSequence("bomb_boom.png",5,{36,36},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::OneShot}});
|
||||
CreateHorizontalAnimationSequence("tornado2.png",4,{24,48},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
||||
CreateHorizontalAnimationSequence("large_tornado.png",4,{72,144},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
||||
CreateHorizontalAnimationSequence("sand_suction.png",4,{72,72},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::Repeat}});
|
||||
CreateHorizontalAnimationSequence("bomb.png",4,{24,24},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::OneShot}});
|
||||
CreateHorizontalAnimationSequence("ghost_dagger.png",3,{24,24},{0.1f,Animate2D::Style::PingPong});
|
||||
|
||||
CreateStillAnimation("meteor.png",{192,192});
|
||||
|
||||
@ -436,14 +411,6 @@ void sig::Animation::InitializeAnimations(){
|
||||
ANIMATION_DATA[std::format("GOBLIN_BOW_ATTACK_{}",animationIndex)]=mountShootAnimation;
|
||||
}
|
||||
|
||||
Animate2D::FrameSequence parrot_sit_n,parrot_sit_e,parrot_sit_s,parrot_sit_w;
|
||||
//Idle sequences for the sitting parrot.
|
||||
for(int animationIndex=0;animationIndex<4;animationIndex++){
|
||||
Animate2D::FrameSequence mountAnimation{0.6f};
|
||||
mountAnimation.AddFrame(Animate2D::Frame{&GFX["monsters/commercial_assets/Parrot_foreground.png"],{{0,animationIndex*48},{48,48}}});
|
||||
ANIMATION_DATA[std::format("PARROT_MOUNTED_{}",animationIndex)]=mountAnimation;
|
||||
}
|
||||
|
||||
#pragma region Trapper Target Mark Debuff
|
||||
AnimationData targetAnimData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}};
|
||||
Animate2D::FrameSequence targetAnim(targetAnimData.frameDuration,targetAnimData.style);
|
||||
@ -451,28 +418,12 @@ void sig::Animation::InitializeAnimations(){
|
||||
targetAnim.AddFrame({&GFX["target.png"],{{int(0*24),0},{24,24}}});
|
||||
targetAnim.AddFrame({&GFX["target.png"],{{int(1*24),0},{24,24}}});
|
||||
ANIMATION_DATA["target.png"]=targetAnim;
|
||||
AnimationData specialTargetAnimData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}};
|
||||
Animate2D::FrameSequence specialTargetAnim(specialTargetAnimData.frameDuration,specialTargetAnimData.style);
|
||||
specialTargetAnim.AddFrame({&GFX["special_target.png"],{{int(0*24),0},{24,24}}});
|
||||
specialTargetAnim.AddFrame({&GFX["special_target.png"],{{int(0*24),0},{24,24}}});
|
||||
specialTargetAnim.AddFrame({&GFX["special_target.png"],{{int(1*24),0},{24,24}}});
|
||||
ANIMATION_DATA["special_target.png"]=specialTargetAnim;
|
||||
#pragma endregion
|
||||
|
||||
CreateHorizontalAnimationSequence("bear_trap.png",3,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::PingPong}});
|
||||
CreateHorizontalAnimationSequence("explosive_trap.png",4,{48,48},AnimationData{.frameDuration{0.06f},.style{Animate2D::Style::PingPong}});
|
||||
CreateHorizontalAnimationSequence("explosionframes.png",21,{24,24},AnimationData{.frameDuration{0.02f},.style{Animate2D::Style::OneShot}});
|
||||
CreateHorizontalAnimationSequence("fire_ring.png",5,{60,60},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
||||
|
||||
CreateHorizontalAnimationSequence("portal.png",8,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
|
||||
|
||||
CreateHorizontalAnimationSequence("burstbullet.png",4,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::OneShot}});
|
||||
CreateHorizontalAnimationSequence("inkbubble_explode.png",4,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::OneShot}});
|
||||
|
||||
//!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//DO NOT CREATE MORE ANIMATION SEQUENCES UNDERNEATH HERE AS THE NEXT BLOCK WILL CREATE DEFAULT ANIMATIONS
|
||||
//FOR ALL NON-SPECIFIED ANIMATION SEQUENCES!
|
||||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
for(auto&dat:GFX){
|
||||
std::string imgFile=dat.first;
|
||||
if(!ANIMATION_DATA.count(imgFile)){
|
||||
|
@ -1,79 +0,0 @@
|
||||
#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 "Arc.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
Arc::Arc(const vf2d pos,const float radius,const float pointingAngle,const float sweepAngle)
|
||||
:pos(pos),radius(radius),pointingAngle(pointingAngle),sweepAngle(sweepAngle){
|
||||
if(sweepAngle<0.f)ERR(std::format("WARNING! Sweep angle must be greater than or equal to 0! Provided Sweep Angle: {}",sweepAngle));
|
||||
GenerateArc();
|
||||
}
|
||||
void Arc::Draw(AiL*game,const Pixel col){
|
||||
game->SetDecalStructure(DecalStructure::FAN);
|
||||
game->view.DrawPolygonDecal(nullptr,poly.pos,poly.pos,col);
|
||||
}
|
||||
const bool Arc::overlaps(const vf2d checkPos)const{
|
||||
return geom2d::overlaps(checkPos,poly);
|
||||
}
|
||||
void Arc::GrowRadius(const float growAmt){
|
||||
radius+=growAmt;
|
||||
GenerateArc();
|
||||
}
|
||||
void Arc::GenerateArc(){
|
||||
poly.pos.clear();
|
||||
//Use cut-off point between two angles
|
||||
poly.pos.emplace_back(pos); //Always add 0,0
|
||||
float smallestAng{util::radToDeg(pointingAngle-sweepAngle)+90};
|
||||
float largestAng{util::radToDeg(pointingAngle+sweepAngle)+90};
|
||||
while(smallestAng<0.f)smallestAng+=360;
|
||||
while(largestAng<0.f)largestAng+=360;
|
||||
int startInd{int(std::fmod(smallestAng/4+1,90))};
|
||||
int endInd{int(std::fmod(largestAng/4+1,90))};
|
||||
if(startInd>endInd){
|
||||
startInd=(startInd+3)%90;
|
||||
}else{
|
||||
endInd=(endInd+3)%90;
|
||||
}
|
||||
for(int i=startInd;i!=endInd;i=(i+1)%game->circleCooldownPoints.size()){
|
||||
poly.pos.emplace_back(pos+game->circleCooldownPoints[i]*radius);
|
||||
}
|
||||
poly.pos.emplace_back(pos); //Connect back to itself.
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#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 "olcUTIL_Geometry2D.h"
|
||||
#include "Pixel.h"
|
||||
|
||||
class AiL;
|
||||
|
||||
class Arc{
|
||||
public:
|
||||
//Define a sweep angle such that each direction will arc that way. Example: PI/2 means a sweep angle from -PI/2 to PI/2. MUST BE POSITIVE
|
||||
Arc(const vf2d pos,const float radius,const float pointingAngle,const float sweepAngle);
|
||||
void Draw(AiL*game,const Pixel col);
|
||||
const bool overlaps(const vf2d checkPos)const;
|
||||
void GrowRadius(const float growAmt);
|
||||
const vf2d pos;
|
||||
const float pointingAngle;
|
||||
const float sweepAngle;
|
||||
float radius;
|
||||
geom2d::polygon<float>poly;
|
||||
private:
|
||||
void GenerateArc();
|
||||
};
|
@ -43,7 +43,6 @@ All rights reserved.
|
||||
#include "olcUTIL_Geometry2D.h"
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
|
||||
Arrow::Arrow(vf2d pos,vf2d targetPos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
||||
:finalDistance(geom2d::line(pos,targetPos).length()*1.2f),acc(PI/2*250),targetPos(targetPos),
|
||||
@ -95,7 +94,6 @@ BulletDestroyState Arrow::PlayerHit(Player*player)
|
||||
{
|
||||
fadeOutTime=0.2f;
|
||||
game->AddEffect(std::make_unique<Effect>(player->GetPos(),0,"splash_effect.png",upperLevel,player->GetSizeMult(),0.25));
|
||||
if(poisonArrow)player->AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,"Poisonous Arrow"_ENC["POISON DURATION"],damage*("Poisonous Arrow"_ENC["POISON TICK DAMAGE"]/100.f),1.f);
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
@ -103,22 +101,9 @@ BulletDestroyState Arrow::MonsterHit(Monster&monster,const uint8_t markStacksBef
|
||||
{
|
||||
fadeOutTime=0.2f;
|
||||
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),0.25));
|
||||
if(poisonArrow)monster.AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,"Poisonous Arrow"_ENC["POISON DURATION"],damage*("Poisonous Arrow"_ENC["POISON TICK DAMAGE"]/100.f),1.f);
|
||||
if(game->GetPlayer()->HasEnchant("Burning Arrow")&&markStacksBeforeHit>0&&monster.GetBuffs(BuffType::BURNING_ARROW_BURN).size()<"Burning Arrow"_ENC["MAX BURN STACK"])monster.AddBuff(BuffType::BURNING_ARROW_BURN,BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,"Burning Arrow"_ENC["BURN DURATION"],game->GetPlayer()->GetAttack()*"Burning Arrow"_ENC["BURN DAMAGE"]/100.f,2.f);
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
void Arrow::ModifyOutgoingDamageData(HurtDamageInfo&data){
|
||||
if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY;
|
||||
}
|
||||
|
||||
void Arrow::Draw(const Pixel blendCol)const{
|
||||
if(poisonArrow){
|
||||
game->SetDecalMode(DecalMode::ADDITIVE);
|
||||
game->view.DrawRotatedDecal(pos,GFX["glow.png"].Decal(),image_angle,GFX["glow.png"].Sprite()->Size()/2,scale,{255,255,0,120});
|
||||
game->SetDecalMode(DecalMode::NORMAL);
|
||||
Bullet::Draw({128,192,0,blendCol.a});
|
||||
}else{
|
||||
Bullet::Draw(blendCol);
|
||||
}
|
||||
}
|
@ -35,23 +35,31 @@ Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||
All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
#pragma once
|
||||
|
||||
#include "MenuComponent.h"
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
|
||||
class MenuDecal:public MenuComponent{
|
||||
public:
|
||||
inline MenuDecal(geom2d::rect<float>rect,std::optional<std::reference_wrapper<Renderable>>image={},MenuFunc onClick=DO_NOTHING,MenuFunc onHover=DO_NOTHING)
|
||||
:image(image),MenuComponent(rect,"",onClick,ButtonAttr::NONE){
|
||||
SetHoverFunc(onHover);
|
||||
}
|
||||
inline void DrawDecal(ViewPort&window,bool focused)override{
|
||||
if(image)window.DrawDecal(rect.pos,image.value().get().Decal(),rect.size/image.value().get().Sprite()->Size());
|
||||
}
|
||||
inline void SetImage(Renderable&renderable){
|
||||
image=renderable;
|
||||
}
|
||||
INCLUDE_game
|
||||
|
||||
private:
|
||||
std::optional<std::reference_wrapper<Renderable>>image;
|
||||
};
|
||||
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="",}},
|
||||
});
|
||||
}
|
@ -38,112 +38,15 @@ All rights reserved.
|
||||
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "RowInventoryScrollableWindowComponent.h"
|
||||
#include "InventoryCreator.h"
|
||||
#include "MenuItemItemButton.h"
|
||||
#include "RowItemDisplay.h"
|
||||
#include "MenuDecal.h"
|
||||
#include "SoundEffect.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Menu::InitializeArtificerDisassembleWindow(){
|
||||
Menu*artificerDisassembleWindow=CreateMenu(ARTIFICER_DISASSEMBLE,CENTERED,game->GetScreenSize()-vi2d{52,52});
|
||||
Menu*artificerDisassembleWindow=CreateMenu(ARTIFICER_DISASSEMBLE,CENTERED,vi2d{144,144});
|
||||
|
||||
const auto ResetDisassemblyDisplay=[artificerDisassembleWindow](){
|
||||
MenuType menuType{artificerDisassembleWindow->GetType()};
|
||||
Component<MenuDecal>(menuType,"Down Arrow")->Disable();
|
||||
Component<MenuIconButton>(menuType,"Disassembly Result")->Disable();
|
||||
Component<MenuLabel>(menuType,"Disassembly Result Title")->Disable();
|
||||
Component<MenuLabel>(menuType,"Fragment Total Count")->Disable();
|
||||
Component<MenuComponent>(menuType,"Disassemble Button")->Disable();
|
||||
};
|
||||
const auto EnableDisassemblyDisplay=[artificerDisassembleWindow](){
|
||||
MenuType menuType{artificerDisassembleWindow->GetType()};
|
||||
Component<MenuDecal>(menuType,"Down Arrow")->Enable();
|
||||
Component<MenuIconButton>(menuType,"Disassembly Result")->Enable();
|
||||
Component<MenuLabel>(menuType,"Disassembly Result Title")->Enable();
|
||||
Component<MenuLabel>(menuType,"Fragment Total Count")->Enable();
|
||||
Component<MenuComponent>(menuType,"Disassemble Button")->Enable();
|
||||
};
|
||||
|
||||
auto disassemblyTitleLabel{artificerDisassembleWindow->ADD("Disassembly Title Label",MenuLabel)(geom2d::rect<float>{{},{artificerDisassembleWindow->size.x,24.f}},"Accessory Disassembly",2.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
|
||||
|
||||
auto inventoryLabel=artificerDisassembleWindow->ADD("Accessory List Label",MenuLabel)(geom2d::rect<float>{{0.f,28.f},{180.f,12.f}},"Choose Accessory:",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
|
||||
|
||||
auto itemIcon{artificerDisassembleWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>({artificerDisassembleWindow->size.x/2+4.f,44.f},{48,48}),Item::BLANK,DO_NOTHING,"","Item Description",IconButtonAttr::NOT_SELECTABLE)END};
|
||||
itemIcon->SetIconScale({2.f,2.f});
|
||||
itemIcon->SetCompactDescriptions(true);
|
||||
|
||||
auto accessoryDescription{artificerDisassembleWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{artificerDisassembleWindow->size.x/2+56.f,44.f},{artificerDisassembleWindow->size.x/2-56.f,72.f}},"",0.5f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
auto downArrow{artificerDisassembleWindow->ADD("Down Arrow",MenuDecal)(geom2d::rect<float>{{artificerDisassembleWindow->size.x/2+4.f,92.f},{48,48}},GFX.at("downarrow.png"),DO_NOTHING,DO_NOTHING)END};
|
||||
|
||||
auto disassemblyResult{artificerDisassembleWindow->ADD("Disassembly Result",MenuIconButton)(geom2d::rect<float>{{artificerDisassembleWindow->size.x/2+4.f,141.f},{48,48}},nullptr,DO_NOTHING,IconButtonAttr::NOT_SELECTABLE)END};
|
||||
|
||||
auto disassemblyResultTitle{artificerDisassembleWindow->ADD("Disassembly Result Title",MenuLabel)(geom2d::rect<float>{{artificerDisassembleWindow->size.x/2+56.f,141.f},{artificerDisassembleWindow->size.x/2-56.f,24.f}},"",2.f,ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIT_TO_LABEL)END};
|
||||
|
||||
auto disassemblyTotalCount{artificerDisassembleWindow->ADD("Fragment Total Count",MenuLabel)(geom2d::rect<float>{{artificerDisassembleWindow->size.x/2+56.f,127.f},{artificerDisassembleWindow->size.x/2-56.f,12.f}},"",0.85f,ComponentAttr::RIGHT_ALIGN|ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL)END};
|
||||
|
||||
auto disassembleButton{artificerDisassembleWindow->ADD("Disassemble Button",MenuComponent)(geom2d::rect<float>{{artificerDisassembleWindow->size.x/2+72.f,171.f},{artificerDisassembleWindow->size.x/2-72.f,16.f}},"Disassemble",[ResetDisassemblyDisplay](MenuFuncData data){
|
||||
auto it{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()};
|
||||
if(Inventory::Disassemble(it)==Inventory::DisassembleResult::SUCCESS){
|
||||
SoundEffect::PlaySFX("Disassemble Item",SoundEffect::CENTERED);
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(Item::BLANK);
|
||||
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(nullptr);
|
||||
Component<MenuLabel>(data.menu.type,"Disassembly Result Title")->SetLabel("");
|
||||
Component<MenuLabel>(data.menu.type,"Fragment Total Count")->SetLabel("");
|
||||
ResetDisassemblyDisplay();
|
||||
}else{
|
||||
SoundEffect::PlaySFX("Locked Item",SoundEffect::CENTERED);
|
||||
game->AddNotification(AiL::Notification{"Cannot disassemble locked accessory!",5.f,RED});
|
||||
}
|
||||
return true;
|
||||
})END};
|
||||
|
||||
auto inventoryDisplay=artificerDisassembleWindow->ADD("Accessory List",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{0.f,44.f},{artificerDisassembleWindow->size.x/2-4.f,artificerDisassembleWindow->size.y-60}},"","",[](MenuFuncData data){
|
||||
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
|
||||
DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->SelectChild(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component));
|
||||
return true;
|
||||
},[EnableDisassemblyDisplay](MenuFuncData data){
|
||||
EnableDisassemblyDisplay();
|
||||
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem().lock());
|
||||
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(item.GetItem().lock()->Icon().Decal());
|
||||
Component<MenuLabel>(data.menu.type,"Disassembly Result Title")->SetLabel(item.GetItem().lock()->FragmentName());
|
||||
Component<MenuLabel>(data.menu.type,"Fragment Total Count")->SetLabel(std::format("Currently Owned: {}",Inventory::GetItemCount(item.GetItem().lock()->FragmentName())));
|
||||
return true;
|
||||
},[ResetDisassemblyDisplay,EnableDisassemblyDisplay](MenuFuncData data){
|
||||
ResetDisassemblyDisplay();
|
||||
auto childComponent{DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->GetSelectedChild()};
|
||||
if(childComponent){
|
||||
RowItemDisplay&item{childComponent.value().get()};
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem().lock());
|
||||
Component<MenuIconButton>(data.menu.type,"Disassembly Result")->SetIcon(item.GetItem().lock()->Icon().Decal());
|
||||
Component<MenuLabel>(data.menu.type,"Disassembly Result Title")->SetLabel(item.GetItem().lock()->FragmentName());
|
||||
Component<MenuLabel>(data.menu.type,"Fragment Total Count")->SetLabel(std::format("Currently Owned: {}",Inventory::GetItemCount(item.GetItem().lock()->FragmentName())));
|
||||
EnableDisassemblyDisplay();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
InventoryCreator::RowPlayer_InventoryUpdate,
|
||||
InventoryWindowOptions{.padding=1,.size={artificerDisassembleWindow->size.x/2-5.f-12.f,28}})END;
|
||||
|
||||
auto backButton=artificerDisassembleWindow->ADD("Back",MenuComponent)(geom2d::rect<float>{{0.f,artificerDisassembleWindow->size.y-12.f},{96.f,16.f}},"Back",[](MenuFuncData data){
|
||||
Menu::CloseMenu();
|
||||
return true;
|
||||
})END;
|
||||
|
||||
Menu::AddInventoryListener(inventoryDisplay,"Accessories");
|
||||
|
||||
ResetDisassemblyDisplay();
|
||||
|
||||
artificerDisassembleWindow->SetupKeyboardNavigation(
|
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
auto&items{Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents()};
|
||||
if(Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild())returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild().value().get().GetName();
|
||||
else if(items.size()>0)returnData=items[0];
|
||||
else returnData="Back";
|
||||
returnData="";
|
||||
},
|
||||
{ //Button Key
|
||||
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}},
|
||||
@ -151,49 +54,12 @@ void Menu::InitializeArtificerDisassembleWindow(){
|
||||
Menu::CloseMenu();
|
||||
}}},
|
||||
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}},
|
||||
{{game->KEY_SHOULDER2,Pressed},{"Scroll Up/Down",[](MenuType type){}}},
|
||||
{{game->KEY_SHOULDER,Pressed},{"Scroll Up/Down",[](MenuType type){}}},
|
||||
{{game->KEY_FASTSCROLLUP,PressedDAS},{"Scroll",[](MenuType type){
|
||||
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->DecreaseSelectionIndex(3.f);
|
||||
}}},
|
||||
{{game->KEY_FASTSCROLLDOWN,PressedDAS},{"Scroll",[](MenuType type){
|
||||
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->IncreaseSelectionIndex(3.f);
|
||||
}}},
|
||||
}
|
||||
,{ //Button Navigation Rules
|
||||
{"Accessory List",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
Menu::ScrollUp(type,"Accessory List",returnData,"Back");
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
Menu::ScrollDown(type,"Accessory List",returnData,"Back");
|
||||
},
|
||||
.left="Back",
|
||||
.right=[&](MenuType type,Data&returnData){
|
||||
Menu::menus[type]->GetSelection().lock()->Click();
|
||||
returnData="Disassemble Button";
|
||||
},}},
|
||||
{"Back",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().back().lock()->GetName();
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().front().lock()->GetName();
|
||||
},
|
||||
.left="Disassemble Button",
|
||||
.right="Disassemble Button",}},
|
||||
{"Disassemble Button",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().back().lock()->GetName();
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().front().lock()->GetName();
|
||||
},
|
||||
.left=[&](MenuType type,Data&returnData){
|
||||
if(Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild())returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild().value().get().GetName();
|
||||
else returnData="Back";
|
||||
},
|
||||
.right="Back",}},
|
||||
}
|
||||
);
|
||||
{"Sample Button",{
|
||||
.up="",
|
||||
.down="",
|
||||
.left="",
|
||||
.right="",}},
|
||||
});
|
||||
}
|
@ -38,81 +38,28 @@ All rights reserved.
|
||||
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "MenuIconButton.h"
|
||||
#include "MenuItemLabel.h"
|
||||
#include "SoundEffect.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Menu::InitializeArtificerEnchantConfirmWindow(){
|
||||
Menu*artificerEnchantConfirmWindow=CreateMenu(ARTIFICER_ENCHANT_CONFIRM,CENTERED,vi2d{240,144});
|
||||
|
||||
auto enchantSuccessLabel{artificerEnchantConfirmWindow->ADD("Enchant Success Label",MenuLabel)(geom2d::rect<float>{{0.f,-14.f},vf2d{artificerEnchantConfirmWindow->size.x,12.f}},"Enchantment Success!",1.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
|
||||
|
||||
auto chooseResultLabel{artificerEnchantConfirmWindow->ADD("Choose Result Label",MenuLabel)(geom2d::rect<float>{vf2d{0.f,0.f},vf2d{artificerEnchantConfirmWindow->size.x,12.f}},"Choose a Result",1.f,ComponentAttr::SHADOW)END};
|
||||
|
||||
auto backgroundOld{artificerEnchantConfirmWindow->ADD("Old Background",MenuLabel)(geom2d::rect<float>{{-2.f,12.f},{artificerEnchantConfirmWindow->size.x/2.f-4.f,124.f}},"",1.f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END};
|
||||
auto backgroundNew{artificerEnchantConfirmWindow->ADD("New Background",MenuLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f-2.f+8.f,12.f},{artificerEnchantConfirmWindow->size.x/2.f-4.f,124.f}},"",1.f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END};
|
||||
|
||||
const float oldLabelTextWidth{game->GetTextSize("OLD").x*2.f};
|
||||
auto oldLabel{artificerEnchantConfirmWindow->ADD("Old Item Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/4.f-oldLabelTextWidth/2-4.f,14.f},vf2d{oldLabelTextWidth+8.f,24.f}},"OLD",2.f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND)END};
|
||||
|
||||
const float newLabelTextWidth{game->GetTextSize("NEW").x*2.f};
|
||||
auto newLabel{artificerEnchantConfirmWindow->ADD("New Item Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f+artificerEnchantConfirmWindow->size.x/4.f-oldLabelTextWidth/2+2.f,14.f},vf2d{oldLabelTextWidth+8.f,24.f}},"NEW",2.f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND)END};
|
||||
|
||||
auto oldIcon{artificerEnchantConfirmWindow->ADD("Old Item Icon",MenuIconButton)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/4.f-16.f,42.f},{24,24}},nullptr,DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE)END};
|
||||
auto newIcon{artificerEnchantConfirmWindow->ADD("New Item Icon",MenuIconButton)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f+artificerEnchantConfirmWindow->size.x/4.f-16.f+4.f,42.f},{24,24}},nullptr,DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE)END};
|
||||
|
||||
auto oldItemDescription{artificerEnchantConfirmWindow->ADD("Old Item Description",MenuItemLabel)(geom2d::rect<float>{{0.f,74.f},{artificerEnchantConfirmWindow->size.x/2.f-8.f,60.f}},Item::BLANK,ItemLabelDescriptionType::ITEM_DESCRIPTION,0.5f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::LEFT_ALIGN)END};
|
||||
auto newItemDescription{artificerEnchantConfirmWindow->ADD("New Item Description",MenuItemLabel)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/2.f+8.f,74.f},{artificerEnchantConfirmWindow->size.x/2.f-8.f,60.f}},Item::BLANK,ItemLabelDescriptionType::ITEM_DESCRIPTION,0.5f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
const float takeOldTextWidth{float(game->GetTextSize("Take Old").x)*2.f};
|
||||
auto takeOldButton{artificerEnchantConfirmWindow->ADD("Take Old Button",MenuComponent)(geom2d::rect<float>{{artificerEnchantConfirmWindow->size.x/4.f-takeOldTextWidth/2.f,138.f},{takeOldTextWidth-8.f,20.f}},"Take Old",[](MenuFuncData data){
|
||||
onClick:
|
||||
std::any&oldEnchant{Menu::menus[ARTIFICER_ENCHANT]->ANY(A::ENCHANT)};
|
||||
std::weak_ptr<Item>newItem{std::get<std::weak_ptr<Item>>(Component<MenuItemLabel>(data.menu.GetType(),"New Item Description")->GetItem())}; //NOTE: We're making an assumption here that the new item description holds a weak pointer. This should be true because the only way to get here was to set it up through clicking the Enchant button in Artificer Enchant Window.
|
||||
if(oldEnchant.has_value())newItem.lock()->_EnchantItem(std::any_cast<ItemEnchant&>(oldEnchant));
|
||||
else newItem.lock()->RemoveEnchant();
|
||||
Component<MenuComponent>(ARTIFICER_ENCHANT,"Fragment Enchant Button")->SetGrayedOut(!newItem.lock()->CanBeEnchanted());
|
||||
const std::string_view fragmentName{newItem.lock()->FragmentName()};
|
||||
const Pixel fragmentItemDisplayCol{Inventory::GetItemCount(fragmentName)>="Fragment Enchant Cost"_i[0]?WHITE:RED};
|
||||
const Pixel moneyCostDisplayCol{game->GetPlayer()->GetMoney()>="Fragment Enchant Cost"_i[1]?WHITE:RED};
|
||||
Component<MenuLabel>(ARTIFICER_ENCHANT,"Fragment Label")->SetLabel(std::format("{}{} x{} ({})",fragmentItemDisplayCol.toHTMLColorCode(),fragmentName,"Fragment Enchant Cost"_i[0],Inventory::GetItemCount(fragmentName)));
|
||||
Component<MenuLabel>(ARTIFICER_ENCHANT,"Fragment Money Cost Label")->SetLabel(std::format("{}{} gold",moneyCostDisplayCol.toHTMLColorCode(),"Fragment Enchant Cost"_i[1]));
|
||||
SoundEffect::PlaySFX("Take Enchant",SoundEffect::CENTERED);
|
||||
Menu::CloseMenu();
|
||||
return true;
|
||||
},vf2d{2.f,2.f})END};
|
||||
|
||||
const float takeNewTextWidth{float(game->GetTextSize("Take New").x)*2.f};
|
||||
auto takeNewButton{artificerEnchantConfirmWindow->ADD("Take New Button",MenuComponent)(geom2d::rect<float>{vf2d{artificerEnchantConfirmWindow->size.x/2.f+8.f+artificerEnchantConfirmWindow->size.x/4.f-takeNewTextWidth/2.f,138.f},{takeNewTextWidth-8.f,20.f}},"Take New",[](MenuFuncData data){
|
||||
onClick:
|
||||
std::weak_ptr<Item>newItem{std::get<std::weak_ptr<Item>>(Component<MenuItemLabel>(data.menu.GetType(),"New Item Description")->GetItem())};
|
||||
Component<MenuComponent>(ARTIFICER_ENCHANT,"Fragment Enchant Button")->SetGrayedOut(!newItem.lock()->CanBeEnchanted());
|
||||
const std::string_view fragmentName{newItem.lock()->FragmentName()};
|
||||
const Pixel fragmentItemDisplayCol{Inventory::GetItemCount(fragmentName)>="Fragment Enchant Cost"_i[0]?WHITE:RED};
|
||||
const Pixel moneyCostDisplayCol{game->GetPlayer()->GetMoney()>="Fragment Enchant Cost"_i[1]?WHITE:RED};
|
||||
Component<MenuLabel>(ARTIFICER_ENCHANT,"Fragment Label")->SetLabel(std::format("{}{} x{} ({})",fragmentItemDisplayCol.toHTMLColorCode(),fragmentName,"Fragment Enchant Cost"_i[0],Inventory::GetItemCount(fragmentName)));
|
||||
Component<MenuLabel>(ARTIFICER_ENCHANT,"Fragment Money Cost Label")->SetLabel(std::format("{}{} gold",moneyCostDisplayCol.toHTMLColorCode(),"Fragment Enchant Cost"_i[1]));
|
||||
SoundEffect::PlaySFX("Take Enchant",SoundEffect::CENTERED);
|
||||
Menu::CloseMenu();
|
||||
return true;
|
||||
},vf2d{2.f,2.f})END};
|
||||
Menu*artificerEnchantConfirmWindow=CreateMenu(ARTIFICER_ENCHANT_CONFIRM,CENTERED,vi2d{144,144});
|
||||
|
||||
artificerEnchantConfirmWindow->SetupKeyboardNavigation(
|
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
returnData="Take New Button";
|
||||
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
|
||||
{"Take Old Button",{
|
||||
.left="Take New Button",
|
||||
.right="Take New Button",}},
|
||||
{"Take New Button",{
|
||||
.left="Take Old Button",
|
||||
.right="Take Old Button",}},
|
||||
{"Sample Button",{
|
||||
.up="",
|
||||
.down="",
|
||||
.left="",
|
||||
.right="",}},
|
||||
});
|
||||
}
|
@ -38,144 +38,15 @@ All rights reserved.
|
||||
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "RowInventoryScrollableWindowComponent.h"
|
||||
#include "MenuItemItemButton.h"
|
||||
#include "MenuItemLabel.h"
|
||||
#include "MenuDecal.h"
|
||||
#include "PlayerMoneyLabel.h"
|
||||
#include "SoundEffect.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Menu::InitializeArtificerEnchantWindow(){
|
||||
Menu*artificerEnchantWindow=CreateMenu(ARTIFICER_ENCHANT,CENTERED,game->GetScreenSize()-vi2d{52,52});
|
||||
|
||||
auto enchantingTitleLabel{artificerEnchantWindow->ADD("Enchanting Title Label",MenuLabel)(geom2d::rect<float>{{0.f,-16.f},{artificerEnchantWindow->size.x,24.f}},"Accessory Enchanting",2.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
|
||||
|
||||
auto inventoryLabel{artificerEnchantWindow->ADD("Accessory List Label",MenuLabel)(geom2d::rect<float>{{0.f,12.f},{180.f,12.f}},"Choose Accessory:",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
auto itemIcon{artificerEnchantWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>({artificerEnchantWindow->size.x/2+4.f,16.f},{48,48}),Item::BLANK,DO_NOTHING,"","Item Description",IconButtonAttr::NOT_SELECTABLE)END};
|
||||
itemIcon->SetIconScale({2.f,2.f});
|
||||
itemIcon->SetCompactDescriptions(true);
|
||||
|
||||
auto accessoryDescription{artificerEnchantWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+56.f,16.f},{artificerEnchantWindow->size.x/2-40.f,72.f}},"",0.5f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
Menu*artificerEnchantWindow=CreateMenu(ARTIFICER_ENCHANT,CENTERED,vi2d{144,144});
|
||||
|
||||
const auto ResetEnchantDisplay{[artificerEnchantWindow](){
|
||||
MenuType menuType{artificerEnchantWindow->GetType()};
|
||||
Component<MenuItemItemButton>(menuType,"Item Icon")->SetItem(Item::BLANK);
|
||||
Component<ScrollableWindowComponent>(menuType,"Enchant Container")->Disable();
|
||||
Component<MenuLabel>(menuType,"Enchant List Header")->Disable();
|
||||
Component<MenuLabel>(menuType,"Enchant Cost Label")->Disable();
|
||||
Component<MenuDecal>(menuType,"Fragment Cost Icon")->Disable();
|
||||
Component<MenuLabel>(menuType,"Fragment Label")->Disable();
|
||||
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->Disable();
|
||||
Component<MenuComponent>(menuType,"Fragment Enchant Button")->Disable();
|
||||
}};
|
||||
const auto EnableEnchantDisplay{[artificerEnchantWindow](){
|
||||
MenuType menuType{artificerEnchantWindow->GetType()};
|
||||
const std::weak_ptr<Item>&selectedItem{Component<MenuItemItemButton>(menuType,"Item Icon")->GetItem()};
|
||||
std::vector<ItemEnchantInfo>availableEnchants{ItemEnchant::GetAvailableEnchants()};
|
||||
std::sort(availableEnchants.begin(),availableEnchants.end(),[](const ItemEnchantInfo&enchant1,const ItemEnchantInfo&enchant2){
|
||||
return enchant1.Category()!=enchant2.Category()?int(enchant1.Category())<int(enchant2.Category()):enchant1.Name()<enchant2.Name();
|
||||
});
|
||||
std::string enchantList{std::accumulate(availableEnchants.begin(),availableEnchants.end(),""s,[](const std::string&acc,const ItemEnchantInfo&enchant){
|
||||
return std::format("{}{}{}#FFFFFF\n",acc,enchant.DisplayCol().toHTMLColorCode(),enchant.Name());
|
||||
})};
|
||||
Component<ScrollableWindowComponent>(menuType,"Enchant Container")->Enable();
|
||||
Component<MenuLabel>(menuType,"Enchant List")->SetLabel(enchantList);
|
||||
Component<MenuLabel>(menuType,"Enchant List Header")->Enable();
|
||||
Component<MenuLabel>(menuType,"Enchant Cost Label")->Enable();
|
||||
Component<MenuDecal>(menuType,"Fragment Cost Icon")->Enable();
|
||||
Component<MenuDecal>(menuType,"Fragment Cost Icon")->SetImage(GFX.at(selectedItem.lock()->FragmentIcon().value()));
|
||||
Component<MenuLabel>(menuType,"Fragment Label")->Enable();
|
||||
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->Enable();
|
||||
Component<MenuComponent>(menuType,"Fragment Enchant Button")->Enable();
|
||||
Component<MenuComponent>(menuType,"Fragment Enchant Button")->SetGrayedOut(!selectedItem.lock()->CanBeEnchanted());
|
||||
|
||||
const std::string_view fragmentName{selectedItem.lock()->FragmentName()};
|
||||
const Pixel fragmentItemDisplayCol{Inventory::GetItemCount(fragmentName)>="Fragment Enchant Cost"_i[0]?WHITE:RED};
|
||||
const Pixel moneyCostDisplayCol{game->GetPlayer()->GetMoney()>="Fragment Enchant Cost"_i[1]?WHITE:RED};
|
||||
Component<MenuLabel>(menuType,"Fragment Label")->SetLabel(std::format("{}{} x{} ({})",fragmentItemDisplayCol.toHTMLColorCode(),fragmentName,"Fragment Enchant Cost"_i[0],Inventory::GetItemCount(fragmentName)));
|
||||
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->SetLabel(std::format("{}{} gold",moneyCostDisplayCol.toHTMLColorCode(),"Fragment Enchant Cost"_i[1]));
|
||||
}};
|
||||
|
||||
auto inventoryDisplay{artificerEnchantWindow->ADD("Accessory List",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{0.f,28.f},{artificerEnchantWindow->size.x/2-4.f,artificerEnchantWindow->size.y-44}},"","",[](MenuFuncData data){
|
||||
OnClick:
|
||||
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
|
||||
DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->SelectChild(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component));
|
||||
return true;
|
||||
},[EnableEnchantDisplay](MenuFuncData data){OnHover:
|
||||
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem());
|
||||
EnableEnchantDisplay();
|
||||
return true;
|
||||
},[ResetEnchantDisplay,EnableEnchantDisplay](MenuFuncData data){OnMouseOut:
|
||||
ResetEnchantDisplay();
|
||||
auto childComponent{DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->GetSelectedChild()};
|
||||
if(childComponent){
|
||||
RowItemDisplay&item{childComponent.value().get()};
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem());
|
||||
EnableEnchantDisplay();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
InventoryCreator::RowPlayer_InventoryUpdate,
|
||||
InventoryWindowOptions{.padding=1,.size={artificerEnchantWindow->size.x/2-5.f-12.f,28}})END};
|
||||
|
||||
auto enchantListHeaderLabel{artificerEnchantWindow->ADD("Enchant List Header",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+4.f,92.f},{artificerEnchantWindow->size.x/2+12.f,12.f}},"Possible Enchantments:",1.f,ComponentAttr::FIT_TO_LABEL|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END};
|
||||
|
||||
auto enchantContainer{artificerEnchantWindow->ADD("Enchant Container",ScrollableWindowComponent)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+4.f,104.f},{artificerEnchantWindow->size.x/2+12.f,44.f}})END};
|
||||
auto enchantList{enchantContainer->ADD("Enchant List",MenuLabel)(geom2d::rect<float>{{0.f,2.f},{enchantContainer->GetSize().x-12.f,0.f}},"",1.f,ComponentAttr::CENTER|ComponentAttr::SHADOW)END};
|
||||
|
||||
auto enchantCostLabel{artificerEnchantWindow->ADD("Enchant Cost Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+4.f,152.f},{64.f,20.f}},"Enchant Cost:",1.f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END};
|
||||
|
||||
auto fragmentCostIcon{artificerEnchantWindow->ADD("Fragment Cost Icon",MenuDecal)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+68.f,152.f},{12.f,12.f}})END};
|
||||
auto fragmentDisplayLabel{artificerEnchantWindow->ADD("Fragment Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+80.f,152.f},{artificerEnchantWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL|ComponentAttr::LEFT_ALIGN)END};
|
||||
auto fragmentMoneyCostLabel{artificerEnchantWindow->ADD("Fragment Money Cost Label",MenuLabel)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+80.f,164.f},{artificerEnchantWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
auto fragmentEnchantButton{artificerEnchantWindow->ADD("Fragment Enchant Button",MenuComponent)(geom2d::rect<float>{{artificerEnchantWindow->size.x/2+96.f,180.f},{artificerEnchantWindow->size.x/2-80.f,12.f}},"Enchant",[EnableEnchantDisplay](MenuFuncData data){
|
||||
onClick:
|
||||
EnableEnchantDisplay(); //Refresh the current display contents.
|
||||
SoundEffect::PlaySFX("Enchant Item",SoundEffect::CENTERED);
|
||||
std::weak_ptr<Item>item{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()};
|
||||
std::optional<ItemEnchant>previousEnchant{item.lock()->GetEnchant()};
|
||||
data.menu.ANY(A::ENCHANT)={};
|
||||
if(previousEnchant.has_value())data.menu.ANY(A::ENCHANT)=previousEnchant.value();
|
||||
Component<MenuIconButton>(ARTIFICER_ENCHANT_CONFIRM,"Old Item Icon")->SetIcon(item.lock()->Icon().Decal());
|
||||
Component<MenuItemLabel>(ARTIFICER_ENCHANT_CONFIRM,"Old Item Description")->SetItem(*item.lock());
|
||||
|
||||
item.lock()->ApplyRandomEnchant();
|
||||
const ItemEnchant&newEnchant{item.lock()->GetEnchant().value()};
|
||||
Component<MenuIconButton>(ARTIFICER_ENCHANT_CONFIRM,"New Item Icon")->SetIcon(item.lock()->Icon().Decal());
|
||||
Component<MenuItemLabel>(ARTIFICER_ENCHANT_CONFIRM,"New Item Description")->SetItem(item.lock());
|
||||
|
||||
Menu::OpenMenu(ARTIFICER_ENCHANT_CONFIRM,true);
|
||||
return true;
|
||||
})END};
|
||||
|
||||
#pragma region Money Display
|
||||
vf2d moneyIconPos{artificerEnchantWindow->size.x/2-28.f,artificerEnchantWindow->size.y-12.f};
|
||||
auto moneyIcon=artificerEnchantWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
|
||||
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
|
||||
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
|
||||
auto moneyDisplay=artificerEnchantWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos+vf2d{26.f,4.f},moneyTextSize},2,1.85f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
|
||||
Player::AddMoneyListener(moneyDisplay);
|
||||
#pragma endregion
|
||||
|
||||
auto backButton{artificerEnchantWindow->ADD("Back",MenuComponent)(geom2d::rect<float>{{0.f,artificerEnchantWindow->size.y-12.f},{96.f,16.f}},"Back",[](MenuFuncData data){
|
||||
Menu::CloseMenu();
|
||||
return true;
|
||||
})END};
|
||||
|
||||
ResetEnchantDisplay();
|
||||
Menu::AddInventoryListener(inventoryDisplay,"Accessories");
|
||||
|
||||
artificerEnchantWindow->SetupKeyboardNavigation(
|
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
auto&items{Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents()};
|
||||
if(Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild())returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild().value().get().GetName();
|
||||
else if(items.size()>0)returnData=items[0];
|
||||
else returnData="Back";
|
||||
returnData="";
|
||||
},
|
||||
{ //Button Key
|
||||
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}},
|
||||
@ -183,52 +54,12 @@ void Menu::InitializeArtificerEnchantWindow(){
|
||||
Menu::CloseMenu();
|
||||
}}},
|
||||
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}},
|
||||
{{game->KEY_SHOULDER2,Pressed},{"Scroll Up/Down",[](MenuType type){}}},
|
||||
{{game->KEY_SHOULDER,Pressed},{"Scroll Up/Down",[](MenuType type){}}},
|
||||
{{game->KEY_FASTSCROLLUP,PressedDAS},{"Scroll",[](MenuType type){
|
||||
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->DecreaseSelectionIndex(3.f);
|
||||
}}},
|
||||
{{game->KEY_FASTSCROLLDOWN,PressedDAS},{"Scroll",[](MenuType type){
|
||||
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->IncreaseSelectionIndex(3.f);
|
||||
}}},
|
||||
{{game->KEY_SCROLLVERT_R,Analog,InputEngageGroup::NOT_VISIBLE},{"Scroll Enchants",[](MenuType type){
|
||||
Component<ScrollableWindowComponent>(type,"Enchant Container")->Scroll(game->KEY_SCROLLVERT.Analog());
|
||||
}}},
|
||||
}
|
||||
,{ //Button Navigation Rules
|
||||
{"Accessory List",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
Menu::ScrollUp(type,"Accessory List",returnData,"Back");
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
Menu::ScrollDown(type,"Accessory List",returnData,"Back");
|
||||
},
|
||||
.left="Back",
|
||||
.right=[&](MenuType type,Data&returnData){
|
||||
Menu::menus[type]->GetSelection().lock()->Click();
|
||||
returnData="Fragment Enchant Button";
|
||||
},}},
|
||||
{"Back",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().back().lock()->GetName();
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().front().lock()->GetName();
|
||||
},
|
||||
.left="Fragment Enchant Button",
|
||||
.right="Fragment Enchant Button",}},
|
||||
{"Fragment Enchant Button",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().back().lock()->GetName();
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().front().lock()->GetName();
|
||||
},
|
||||
.left=[&](MenuType type,Data&returnData){
|
||||
if(Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild())returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild().value().get().GetName();
|
||||
else returnData="Back";
|
||||
},
|
||||
.right="Back",}},
|
||||
}
|
||||
);
|
||||
{"Sample Button",{
|
||||
.up="",
|
||||
.down="",
|
||||
.left="",
|
||||
.right="",}},
|
||||
});
|
||||
}
|
@ -36,20 +36,30 @@ All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "MonsterStrategyHelpers.h"
|
||||
#include "Entity.h"
|
||||
|
||||
using A=Attribute;
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Monster::STRATEGY::PIRATES_COIN(Monster&m,float fElapsedTime,std::string strategy){
|
||||
if(m.B(Attribute::COLLIDED_WITH_PLAYER)){
|
||||
game->AddEffect(std::make_unique<CollectCoinEffect>(game->GetPlayer(),ConfigFloat("Coin Collect Rise Amount"),ConfigFloat("Coin Rise Timer"),"coin.png",m.OnUpperLevel()),true);
|
||||
game->GetPlayer()->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_COIN,INFINITY,1);
|
||||
m._DealTrueDamage(m.GetHealth(),HurtFlag::NO_DAMAGE_NUMBER);
|
||||
m.SetLifetime(0.f);
|
||||
m.SetSize(0.f,false);
|
||||
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="",}},
|
||||
});
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
#pragma region License
|
||||
/*
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce the above
|
||||
copyright notice. This list of conditions and the following disclaimer must be
|
||||
reproduced in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Portions of this software are copyright © 2024 The FreeType
|
||||
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||
All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "MenuItemItemButton.h"
|
||||
#include "DynamicMenuLabel.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Menu::InitializeArtificerRefineResultWindow(){
|
||||
Menu*artificerRefineResultWindow=CreateMenu(ARTIFICER_REFINE_RESULT,CENTERED,vi2d{144,144});
|
||||
|
||||
auto itemIcon{artificerRefineResultWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{artificerRefineResultWindow->size.x/2.f-24.f,0.f},{48.f,48.f}},Item::BLANK,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END};
|
||||
itemIcon->SetIconScale({2.f,2.f});
|
||||
|
||||
auto refineItemTextdisplay{artificerRefineResultWindow->ADD("Refine Item Text Display",MenuLabel)(geom2d::rect<float>{vf2d{0.f,artificerRefineResultWindow->size.y/2.f},vf2d{artificerRefineResultWindow->size.x,12.f}},"",1.f,ComponentAttr::SHADOW)END};
|
||||
|
||||
auto refineResultDisplay{artificerRefineResultWindow->ADD("Refine Result",DynamicMenuLabel)(geom2d::rect<float>{vf2d{0.f,artificerRefineResultWindow->size.y/2.f+artificerRefineResultWindow->size.y*0.33f},vf2d{artificerRefineResultWindow->size.x,12.f}},[](){return "";},1.f,ComponentAttr::SHADOW)END};
|
||||
|
||||
auto continueButton{artificerRefineResultWindow->ADD("Continue Button",MenuComponent)(geom2d::rect<float>{vf2d{artificerRefineResultWindow->size.x/4.f,artificerRefineResultWindow->size.y-6.f},{artificerRefineResultWindow->size.x/2.f,12.f}},"Continue",[](MenuFuncData data){
|
||||
onClick:
|
||||
Menu::CloseMenu();
|
||||
return true;
|
||||
})END};
|
||||
|
||||
artificerRefineResultWindow->SetupKeyboardNavigation(
|
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
returnData="Continue 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
|
||||
});
|
||||
}
|
@ -39,128 +39,31 @@ All rights reserved.
|
||||
#include "Menu.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "RowInventoryScrollableWindowComponent.h"
|
||||
#include "MenuItemItemButton.h"
|
||||
#include "MenuRefineLabel.h"
|
||||
#include "PlayerMoneyLabel.h"
|
||||
#include "MenuDecal.h"
|
||||
#include "SoundEffect.h"
|
||||
#include "DynamicMenuLabel.h"
|
||||
#include "MenuLabel.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Menu::InitializeArtificerRefineWindow(){
|
||||
Menu*const artificerRefineWindow{CreateMenu(ARTIFICER_REFINE,CENTERED,game->GetScreenSize()-vi2d{52,52})};
|
||||
|
||||
auto refiningTitleLabel{artificerRefineWindow->ADD("Refining Title Label",MenuLabel)(geom2d::rect<float>{{0.f,-16.f},{artificerRefineWindow->size.x,24.f}},"Accessory Refinement",2.f,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END};
|
||||
|
||||
auto inventoryLabel{artificerRefineWindow->ADD("Accessory List Label",MenuLabel)(geom2d::rect<float>{{0.f,12.f},{180.f,12.f}},"Choose Accessory:",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
auto itemIcon{artificerRefineWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>({artificerRefineWindow->size.x/2+4.f,28.f},{48,48}),Item::BLANK,DO_NOTHING,"","Item Description",IconButtonAttr::NOT_SELECTABLE)END};
|
||||
itemIcon->SetIconScale({2.f,2.f});
|
||||
itemIcon->SetCompactDescriptions(true);
|
||||
|
||||
auto accessoryDescription{artificerRefineWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+56.f,28.f},{artificerRefineWindow->size.x/2-40.f,72.f}},"",0.5f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
Menu*artificerRefineWindow=CreateMenu(ARTIFICER_REFINE,CENTERED,game->GetScreenSize()-vi2d{52,52});
|
||||
|
||||
const auto ResetRefineDisplay{[artificerRefineWindow](){
|
||||
MenuType menuType{artificerRefineWindow->GetType()};
|
||||
Component<MenuItemItemButton>(menuType,"Item Icon")->SetItem(Item::BLANK);
|
||||
Component<MenuRefineLabel>(menuType,"Stats Block")->Disable();
|
||||
Component<MenuLabel>(menuType,"Refine Cost Label")->Disable();
|
||||
Component<MenuDecal>(menuType,"Fragment Cost Icon")->Disable();
|
||||
Component<MenuLabel>(menuType,"Fragment Label")->Disable();
|
||||
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->Disable();
|
||||
Component<MenuComponent>(menuType,"Fragment Refine Button")->Disable();
|
||||
}};
|
||||
const auto EnableRefineDisplay{[artificerRefineWindow](){
|
||||
MenuType menuType{artificerRefineWindow->GetType()};
|
||||
const std::weak_ptr<Item>&selectedItem{Component<MenuItemItemButton>(menuType,"Item Icon")->GetItem()};
|
||||
Component<MenuRefineLabel>(menuType,"Stats Block")->SetItem(selectedItem);
|
||||
Component<MenuRefineLabel>(menuType,"Stats Block")->Enable();
|
||||
Component<MenuLabel>(menuType,"Refine Cost Label")->Enable();
|
||||
Component<MenuDecal>(menuType,"Fragment Cost Icon")->Enable();
|
||||
Component<MenuDecal>(menuType,"Fragment Cost Icon")->SetImage(GFX.at(selectedItem.lock()->FragmentIcon().value()));
|
||||
Component<MenuLabel>(menuType,"Fragment Label")->Enable();
|
||||
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->Enable();
|
||||
Component<MenuComponent>(menuType,"Fragment Refine Button")->Enable();
|
||||
Component<MenuComponent>(menuType,"Fragment Refine Button")->SetGrayedOut(!selectedItem.lock()->CanBeRefined());
|
||||
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;
|
||||
|
||||
const std::string_view fragmentName{selectedItem.lock()->FragmentName()};
|
||||
const Pixel fragmentItemDisplayCol{Inventory::GetItemCount(fragmentName)>="Fragment Refine Cost"_i[0]?WHITE:RED};
|
||||
const Pixel moneyCostDisplayCol{game->GetPlayer()->GetMoney()>="Fragment Refine Cost"_i[1]?WHITE:RED};
|
||||
Component<MenuLabel>(menuType,"Fragment Label")->SetLabel(std::format("{}{} x{} ({})",fragmentItemDisplayCol.toHTMLColorCode(),fragmentName,"Fragment Refine Cost"_i[0],Inventory::GetItemCount(fragmentName)));
|
||||
Component<MenuLabel>(menuType,"Fragment Money Cost Label")->SetLabel(std::format("{}{} gold",moneyCostDisplayCol.toHTMLColorCode(),"Fragment Refine Cost"_i[1]));
|
||||
}};
|
||||
|
||||
auto inventoryDisplay{artificerRefineWindow->ADD("Accessory List",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{0.f,28.f},{artificerRefineWindow->size.x/2-4.f,artificerRefineWindow->size.y-44}},"","",[](MenuFuncData data){
|
||||
OnClick:
|
||||
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
|
||||
DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->SelectChild(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component));
|
||||
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;
|
||||
},[EnableRefineDisplay](MenuFuncData data){OnHover:
|
||||
RowItemDisplay&item{*DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component)};
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem());
|
||||
EnableRefineDisplay();
|
||||
return true;
|
||||
},[ResetRefineDisplay,EnableRefineDisplay](MenuFuncData data){OnMouseOut:
|
||||
ResetRefineDisplay();
|
||||
auto childComponent{DYNAMIC_POINTER_CAST<RowInventoryScrollableWindowComponent>(data.parentComponent.lock())->GetSelectedChild()};
|
||||
if(childComponent){
|
||||
RowItemDisplay&item{childComponent.value().get()};
|
||||
Component<MenuItemItemButton>(data.menu.type,"Item Icon")->SetItem(item.GetItem());
|
||||
EnableRefineDisplay();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},DO_NOTHING,DO_NOTHING,
|
||||
InventoryCreator::RowPlayer_InventoryUpdate,
|
||||
InventoryWindowOptions{.padding=1,.size={artificerRefineWindow->size.x/2-5.f-12.f,28}})END};
|
||||
InventoryWindowOptions{.padding=1,.size={artificerRefineWindow->size.x/2-5.f,28}})END;
|
||||
|
||||
auto statsBlock{artificerRefineWindow->ADD("Stats Block",MenuRefineLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+4.f,104.f},{artificerRefineWindow->size.x/2+12.f,44.f}},Item::BLANK,1.f,ComponentAttr::BACKGROUND|ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::FIXED_WIDTH_FONT|ComponentAttr::FIT_TO_LABEL|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
auto refineCostLabel{artificerRefineWindow->ADD("Refine Cost Label",MenuLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+4.f,152.f},{64.f,20.f}},"Refine Cost:",1.f,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END};
|
||||
|
||||
auto fragmentCostIcon{artificerRefineWindow->ADD("Fragment Cost Icon",MenuDecal)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+68.f,152.f},{12.f,12.f}})END};
|
||||
auto fragmentDisplayLabel{artificerRefineWindow->ADD("Fragment Label",MenuLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+80.f,152.f},{artificerRefineWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::FIT_TO_LABEL|ComponentAttr::LEFT_ALIGN)END};
|
||||
auto fragmentMoneyCostLabel{artificerRefineWindow->ADD("Fragment Money Cost Label",MenuLabel)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+80.f,164.f},{artificerRefineWindow->size.x/2-60.f,12.f}},"",1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END};
|
||||
|
||||
auto fragmentRefineButton{artificerRefineWindow->ADD("Fragment Refine Button",MenuComponent)(geom2d::rect<float>{{artificerRefineWindow->size.x/2+96.f,180.f},{artificerRefineWindow->size.x/2-80.f,12.f}},"Refine",[EnableRefineDisplay](MenuFuncData data){
|
||||
onClick:
|
||||
SoundEffect::PlaySFX("Refine Item",SoundEffect::CENTERED);
|
||||
std::weak_ptr<Item>item{Component<MenuItemItemButton>(data.menu.type,"Item Icon")->GetItem()};
|
||||
RefineResult result{item.lock()->Refine()};
|
||||
Component<MenuItemItemButton>(ARTIFICER_REFINE_RESULT,"Item Icon")->SetItem(item);
|
||||
Component<DynamicMenuLabel>(ARTIFICER_REFINE_RESULT,"Refine Result")->SetLabelUpdateFunction([result](){
|
||||
const Pixel shimmeringColor{PixelLerp(WHITE,{220,220,220},sin((70*game->GetRunTime())/2.f+0.5f))};
|
||||
return std::format("{} -> {}+{}{} #FFFF00UP!",result.first.Name(),shimmeringColor.toHTMLColorCode(),result.second,result.first.DisplayAsPercent()?"%":"");
|
||||
});
|
||||
Component<MenuLabel>(ARTIFICER_REFINE_RESULT,"Refine Item Text Display")->SetLabel(std::format("#FFFF00{} has been refined!",item.lock()->DisplayName()));
|
||||
EnableRefineDisplay(); //Refresh the current display contents.
|
||||
Menu::OpenMenu(ARTIFICER_REFINE_RESULT,true);
|
||||
return true;
|
||||
})END};
|
||||
|
||||
#pragma region Money Display
|
||||
vf2d moneyIconPos{artificerRefineWindow->size.x/2-28.f,artificerRefineWindow->size.y-12.f};
|
||||
auto moneyIcon=artificerRefineWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
|
||||
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
|
||||
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
|
||||
auto moneyDisplay=artificerRefineWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos+vf2d{26.f,4.f},moneyTextSize},2,1.85f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
|
||||
Player::AddMoneyListener(moneyDisplay);
|
||||
#pragma endregion
|
||||
|
||||
auto backButton{artificerRefineWindow->ADD("Back",MenuComponent)(geom2d::rect<float>{{0.f,artificerRefineWindow->size.y-12.f},{96.f,16.f}},"Back",[](MenuFuncData data){
|
||||
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};
|
||||
|
||||
})END;
|
||||
|
||||
Menu::AddInventoryListener(inventoryDisplay,"Accessories");
|
||||
|
||||
artificerRefineWindow->SetupKeyboardNavigation(
|
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
auto&items{Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents()};
|
||||
if(Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild())returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild().value().get().GetName();
|
||||
else if(items.size()>0)returnData=items[0];
|
||||
else returnData="Back";
|
||||
returnData="";
|
||||
},
|
||||
{ //Button Key
|
||||
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}},
|
||||
@ -168,49 +71,12 @@ void Menu::InitializeArtificerRefineWindow(){
|
||||
Menu::CloseMenu();
|
||||
}}},
|
||||
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}},
|
||||
{{game->KEY_SHOULDER2,Pressed},{"Scroll Up/Down",[](MenuType type){}}},
|
||||
{{game->KEY_SHOULDER,Pressed},{"Scroll Up/Down",[](MenuType type){}}},
|
||||
{{game->KEY_FASTSCROLLUP,PressedDAS},{"Scroll",[](MenuType type){
|
||||
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->DecreaseSelectionIndex(3.f);
|
||||
}}},
|
||||
{{game->KEY_FASTSCROLLDOWN,PressedDAS},{"Scroll",[](MenuType type){
|
||||
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->IncreaseSelectionIndex(3.f);
|
||||
}}},
|
||||
}
|
||||
,{ //Button Navigation Rules
|
||||
{"Accessory List",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
Menu::ScrollUp(type,"Accessory List",returnData,"Back");
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
Menu::ScrollDown(type,"Accessory List",returnData,"Back");
|
||||
},
|
||||
.left="Back",
|
||||
.right=[&](MenuType type,Data&returnData){
|
||||
Menu::menus[type]->GetSelection().lock()->Click();
|
||||
returnData="Fragment Refine Button";
|
||||
},}},
|
||||
{"Back",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().back().lock()->GetName();
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().front().lock()->GetName();
|
||||
},
|
||||
.left="Fragment Refine Button",
|
||||
.right="Fragment Refine Button",}},
|
||||
{"Fragment Refine Button",{
|
||||
.up=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().back().lock()->GetName();
|
||||
},
|
||||
.down=[&](MenuType type,Data&returnData){
|
||||
returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetComponents().front().lock()->GetName();
|
||||
},
|
||||
.left=[&](MenuType type,Data&returnData){
|
||||
if(Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild())returnData=Component<RowInventoryScrollableWindowComponent>(type,"Accessory List")->GetSelectedChild().value().get().GetName();
|
||||
else returnData="Back";
|
||||
},
|
||||
.right="Back",}},
|
||||
}
|
||||
);
|
||||
{"Sample Button",{
|
||||
.up="",
|
||||
.down="",
|
||||
.left="",
|
||||
.right="",}},
|
||||
});
|
||||
}
|
@ -43,26 +43,22 @@ All rights reserved.
|
||||
#include "Unlock.h"
|
||||
#include "MenuLabel.h"
|
||||
#include "VisualNovel.h"
|
||||
#include "RowInventoryScrollableWindowComponent.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Menu::InitializeArtificerWindow(){
|
||||
Menu*artificerWindow=CreateMenu(ARTIFICER,CENTERED,vi2d{144,144});
|
||||
|
||||
artificerWindow->ADD("Disassemble Button",MenuComponent)(geom2d::rect<float>{{0.f,4.f},{144.f,24.f}},"Disassemble",[](MenuFuncData data){
|
||||
Menu::OpenMenu(MenuType::ARTIFICER_DISASSEMBLE);
|
||||
Component<RowInventoryScrollableWindowComponent>(MenuType::ARTIFICER_DISASSEMBLE,"Accessory List")->ClearSelectedChild();
|
||||
|
||||
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("Refine Button",MenuComponent)(geom2d::rect<float>{{0.f,32.f},{144.f,24.f}},"Refine",[](MenuFuncData data){
|
||||
Menu::OpenMenu(MenuType::ARTIFICER_REFINE);
|
||||
Component<RowInventoryScrollableWindowComponent>(MenuType::ARTIFICER_REFINE,"Accessory List")->ClearSelectedChild();
|
||||
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);
|
||||
Component<RowInventoryScrollableWindowComponent>(MenuType::ARTIFICER_ENCHANT,"Accessory List")->ClearSelectedChild();
|
||||
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){
|
||||
@ -76,7 +72,7 @@ void Menu::InitializeArtificerWindow(){
|
||||
|
||||
artificerWindow->SetupKeyboardNavigation(
|
||||
[](MenuType type,Data&returnData){ //On Open
|
||||
returnData="Disassemble Button";
|
||||
returnData="Refine Button";
|
||||
},
|
||||
{ //Button Key
|
||||
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}},
|
||||
@ -86,20 +82,20 @@ void Menu::InitializeArtificerWindow(){
|
||||
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}},
|
||||
}
|
||||
,{ //Button Navigation Rules
|
||||
{"Disassemble Button",{
|
||||
.up="Leave Button",
|
||||
.down="Refine Button",}},
|
||||
{"Refine Button",{
|
||||
.up="Disassemble Button",
|
||||
.up="Leave Button",
|
||||
.down="Disassemble Button",}},
|
||||
{"Disassemble Button",{
|
||||
.up="Refine Button",
|
||||
.down="Enchant Button",}},
|
||||
{"Enchant Button",{
|
||||
.up="Refine Button",
|
||||
.up="Disassemble Button",
|
||||
.down="Help Button",}},
|
||||
{"Help Button",{
|
||||
.up="Enchant Button",
|
||||
.down="Leave Button",}},
|
||||
{"Leave Button",{
|
||||
.up="Help Button",
|
||||
.down="Disassemble Button",}},
|
||||
.down="Refine Button",}},
|
||||
});
|
||||
}
|
@ -40,58 +40,50 @@ All rights reserved.
|
||||
#include "DEFINES.h"
|
||||
#include <variant>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
class IAttributable{
|
||||
public:
|
||||
inline virtual ~IAttributable(){};
|
||||
std::map<Attribute,std::any>attributes;
|
||||
std::map<Attribute,std::variant<VARIANTS>>attributes;
|
||||
inline float&GetFloat(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=0.f;
|
||||
}
|
||||
return std::any_cast<float&>(attributes[a]);
|
||||
return std::get<float>(attributes[a]);
|
||||
};
|
||||
inline int&GetInt(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=0;
|
||||
}
|
||||
return std::any_cast<int&>(attributes[a]);
|
||||
return std::get<int>(attributes[a]);
|
||||
};
|
||||
inline std::string&GetString(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=""s;
|
||||
attributes[a]="";
|
||||
}
|
||||
return std::any_cast<std::string&>(attributes[a]);
|
||||
return std::get<std::string>(attributes[a]);
|
||||
};
|
||||
inline bool&GetBool(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=false;
|
||||
}
|
||||
return std::any_cast<bool&>(attributes[a]);
|
||||
return std::get<bool>(attributes[a]);
|
||||
};
|
||||
inline vf2d&GetVf2d(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=vf2d{};
|
||||
}
|
||||
return std::any_cast<vf2d&>(attributes[a]);
|
||||
return std::get<vf2d>(attributes[a]);
|
||||
};
|
||||
inline std::vector<std::any>&GetVec(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=std::vector<std::any>{};
|
||||
}
|
||||
return std::any_cast<std::vector<std::any>&>(attributes[a]);
|
||||
};
|
||||
inline std::any&GetAny(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=std::any{};
|
||||
}
|
||||
return attributes[a];
|
||||
return std::get<std::vector<std::any>>(attributes[a]);
|
||||
};
|
||||
inline size_t&GetSizeT(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=size_t(0);
|
||||
}
|
||||
return std::any_cast<size_t&>(attributes[a]);
|
||||
return std::get<size_t>(attributes[a]);
|
||||
};
|
||||
};
|
@ -113,9 +113,6 @@ public:
|
||||
inline auto end()const{
|
||||
return attributes.end();
|
||||
}
|
||||
inline auto size()const{
|
||||
return attributes.size();
|
||||
}
|
||||
inline void clear(){
|
||||
attributes.clear();
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ All rights reserved.
|
||||
#include "util.h"
|
||||
#include "LoadingScreen.h"
|
||||
#include "Menu.h"
|
||||
#include "SoundEffect.h"
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_DATA
|
||||
@ -71,12 +70,7 @@ void Audio::Initialize(){
|
||||
|
||||
while(data.HasProperty(std::format("channel[{}]",channelCounter))){
|
||||
std::string channelName=data[std::format("channel[{}]",channelCounter)].GetString();
|
||||
if(!game->gamepack.Loaded()){
|
||||
if(!std::filesystem::exists("bgm_directory"_S+channelName))ERR(std::format("WARNING! Could not load file {} for track {}",channelName,songFileName));
|
||||
if("GENERATE_GAMEPACK"_B){
|
||||
game->gamepack.AddFile("bgm_directory"_S+channelName);
|
||||
}
|
||||
}
|
||||
if(!std::filesystem::exists("bgm_directory"_S+channelName))ERR(std::format("WARNING! Could not load file {} for track {}",channelName,songFileName));
|
||||
bgm.AddChannel(channelName);
|
||||
channelCounter++;
|
||||
}
|
||||
@ -89,9 +83,6 @@ void Audio::Initialize(){
|
||||
bgm.SetFadeTime(defaultFadeTime);
|
||||
}
|
||||
|
||||
if(data.HasProperty("Loop Repeat Start Point")){
|
||||
bgm.SetLoopStartTime(data["Loop Repeat Start Point"].GetReal());
|
||||
}
|
||||
|
||||
if(data.HasProperty("Events")){
|
||||
for(auto&eventName:Self().events){
|
||||
@ -127,8 +118,8 @@ MiniAudio&Audio::Engine(){
|
||||
void Audio::Play(const std::string_view sound){
|
||||
Engine().Play(std::string(sound));
|
||||
};
|
||||
const size_t Audio::LoadAndPlaySFX(const std::string_view sound,const bool loop){
|
||||
size_t soundID=Engine().LoadSound(std::string(sound),MiniAudio::SFX);
|
||||
const size_t Audio::LoadAndPlay(const std::string_view sound,const bool loop){
|
||||
size_t soundID=Engine().LoadSound(std::string(sound));
|
||||
Engine().Play(soundID,loop);
|
||||
return soundID;
|
||||
};
|
||||
@ -184,7 +175,6 @@ void Audio::BGM::Load(){
|
||||
}
|
||||
Self().currentBGM=songFileName;
|
||||
Self().currentLoopIndex=0;
|
||||
Self().lastTimestamp=0.f;
|
||||
BGM&newBgm=Self().bgm[songFileName];
|
||||
if(newBgm.channels.size()>0)ERR(std::format("WARNING! The size of the channels list is greater than zero! Size: {}",newBgm.channels.size()));
|
||||
}else{
|
||||
@ -333,17 +323,6 @@ void Audio::PlayBGM(const std::string_view sound,const bool loop){
|
||||
}
|
||||
|
||||
void Audio::Update(){
|
||||
if(Self().BGMFullyLoaded()&&Self().BGMIsPlaying()){
|
||||
Audio::BGM&track=Self().bgm[Self().GetTrackName()];
|
||||
float currentTimestamp{Engine().GetCursorMilliseconds(track.GetChannelIDs()[0])/1000.f};
|
||||
if(Self().lastTimestamp>currentTimestamp){
|
||||
for(int trackID:track.GetChannelIDs()){
|
||||
Engine().Seek(trackID,(unsigned long long)(track.GetLoopStartTime())*1000);
|
||||
}
|
||||
currentTimestamp=Engine().GetCursorMilliseconds(track.GetChannelIDs()[0])/1000.f; //Update to new timestamp now that it's been shifted over.
|
||||
}
|
||||
Self().lastTimestamp=currentTimestamp;
|
||||
}
|
||||
if(Self().fadeToTargetVolumeTime==0.f&&Self().playBGMWaitTime>0.f){
|
||||
Self().playBGMWaitTime=std::max(Self().playBGMWaitTime-game->GetElapsedTime(),0.f);
|
||||
if(Self().playBGMWaitTime==0.f&&Self().immediatelyLoadAudio){
|
||||
@ -351,7 +330,7 @@ void Audio::Update(){
|
||||
UpdateLoop(); //We immediately load the file. In a loading screen setting we would defer UpdateLoop() such that we have extra time to update the screen, UpdateLoop() is divided into many parts of the music loading process.
|
||||
}
|
||||
|
||||
//Start playing the tracks and setup a callback to repeat at looped time.
|
||||
//Start playing the tracks.
|
||||
Audio::BGM&track=Self().bgm[Self().GetTrackName()];
|
||||
for(int trackID:track.GetChannelIDs()){
|
||||
Engine().Play(trackID,true);
|
||||
@ -376,16 +355,11 @@ const float&Audio::BGM::GetFadeTime()const{
|
||||
return fadeTime;
|
||||
}
|
||||
|
||||
const float&Audio::BGM::GetLoopStartTime()const{
|
||||
return loopStartTime;
|
||||
}
|
||||
|
||||
void Audio::SetBGMVolume(float vol){
|
||||
bgmVol=vol;
|
||||
UpdateBGMVolume();
|
||||
}
|
||||
void Audio::SetBGMPitch(float pitch){
|
||||
if(game->TestingModeEnabled())return;
|
||||
BGM&track=Self().bgm[Self().playParams.sound];
|
||||
for(int channelListIndex=0;int trackID:track.GetChannelIDs()){
|
||||
Engine().SetPitch(trackID,pitch);
|
||||
@ -417,11 +391,8 @@ float Audio::GetCalculatedBGMVolume(const float channelVol){
|
||||
}
|
||||
return channelVol*GetBGMVolume()*GetMuteMult()*pauseMult;
|
||||
}
|
||||
float Audio::GetCalculatedSFXVolume(const SoundEffect&sfx){
|
||||
return sfx.TreatAsBPM()?Audio::GetCalculatedBGMVolume(sfx.GetVolume()):sfx.GetVolume()*GetSFXVolume()*GetMuteMult();
|
||||
}
|
||||
float Audio::GetCalculatedSFXVolume(const float sfxVol){
|
||||
return sfxVol*GetSFXVolume()*GetMuteMult();
|
||||
float Audio::GetCalculatedSFXVolume(const float vol){
|
||||
return vol*GetSFXVolume()*GetMuteMult();
|
||||
}
|
||||
float Audio::GetMuteMult(){
|
||||
if(muted)return 0.f;
|
||||
@ -431,8 +402,4 @@ float Audio::GetMuteMult(){
|
||||
int Audio::GetPrepareBGMLoopIterations(std::string_view sound){
|
||||
BGM&newBgm=Self().bgm[std::string(sound)];
|
||||
return newBgm.GetChannels().size()*2+2; //The channels list gets populated by calling newBgm.Load(), which then provides the list of channels that need to be loaded and played. This is why we multiply by 2. Each of the loading phases also consist of an initialization phase, so we add 2 as well.
|
||||
}
|
||||
|
||||
void Audio::BGM::SetLoopStartTime(const float loopStartTime){
|
||||
this->loopStartTime=loopStartTime;
|
||||
}
|
||||
}
|
@ -49,8 +49,6 @@ using ChannelIDList=std::vector<ChannelID>;
|
||||
using Volume=float;
|
||||
using VolumeList=std::vector<Volume>;
|
||||
|
||||
class SoundEffect;
|
||||
|
||||
class Audio{
|
||||
friend class AiL;
|
||||
public:
|
||||
@ -61,7 +59,7 @@ public:
|
||||
static void UpdateLoop();
|
||||
static void Play(const std::string_view sound);
|
||||
[[nodiscard]]
|
||||
static const size_t LoadAndPlaySFX(const std::string_view sound,const bool loop=true);
|
||||
static const size_t LoadAndPlay(const std::string_view sound,const bool loop=true);
|
||||
//Prepares a BGM for loading. This means we call UpdateLoop() repeatedly until the loading of the music is complete. Names are found in bgm.txt configuration file.
|
||||
static void PrepareBGM(const std::string_view sound,const bool loop=true);
|
||||
//Play immediately a BGM given a name found in bgm.txt configuration file.
|
||||
@ -82,8 +80,7 @@ public:
|
||||
//This will get a prepared BGM loop iteration count which is useful for loading stages.
|
||||
static int GetPrepareBGMLoopIterations(std::string_view sound);
|
||||
static float GetCalculatedBGMVolume(const float channelVol);
|
||||
static float GetCalculatedSFXVolume(const SoundEffect&sfx);
|
||||
static float GetCalculatedSFXVolume(const float sfxVol); //NOTE: This is a more manually invoked function! If you are trying to play a specific sound effect from an event, use the SoundEffect version instead!! This accounts for any additional flags related to volume.
|
||||
static float GetCalculatedSFXVolume(const float vol);
|
||||
private:
|
||||
bool trackLoadStarted=false;
|
||||
bool trackLoadComplete=false;
|
||||
@ -121,15 +118,12 @@ private:
|
||||
const ChannelID&GetChannelID(const int index);
|
||||
const ChannelIDList&GetChannelIDs()const;
|
||||
const float&GetFadeTime()const;
|
||||
const float&GetLoopStartTime()const;
|
||||
void SetLoopStartTime(const float loopStartTime);
|
||||
private:
|
||||
std::string songName; //Name of the track.
|
||||
std::string songFileName; //Name of the key in bgm.
|
||||
ChannelIDList channels;
|
||||
std::vector<ChannelName>channelNames;
|
||||
EventData eventVolumes;
|
||||
float loopStartTime{0.f};
|
||||
float fadeTime="BGM.Default Fade Time"_F;
|
||||
void Unload();
|
||||
};
|
||||
@ -147,7 +141,6 @@ private:
|
||||
float playBGMWaitTime=0.0f;
|
||||
BGMPlayParams playParams;
|
||||
static bool muted;
|
||||
float lastTimestamp{0.f};
|
||||
};
|
||||
|
||||
std::string operator""_SFX(const char*key,size_t length);
|
@ -52,11 +52,11 @@ INCLUDE_MONSTER_DATA
|
||||
using A=Attribute;
|
||||
|
||||
void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
switch(PHASE()){
|
||||
switch(m.I(A::PHASE)){
|
||||
case 0:{
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
if(distToPlayer<operator""_Pixels(ConfigFloat("Attack Range"))){
|
||||
SETPHASE(1);
|
||||
m.I(A::PHASE)=1;
|
||||
m.PerformShootAnimation();
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Chargeup Time");
|
||||
//The bear slam attack indicator will move with the bear, and the LOCKON_POS variable will hold a polar coordinate indicating distance and angle for where it should be attacking relative to its position.
|
||||
@ -75,7 +75,7 @@ void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
case 1:{
|
||||
m.F(A::CASTING_TIMER)=std::max(0.f,m.F(A::CASTING_TIMER)-fElapsedTime);
|
||||
if(m.F(A::CASTING_TIMER)==0.f){
|
||||
SETPHASE(2);
|
||||
m.I(A::PHASE)=2;
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Attack Animation Wait Time");
|
||||
m.PerformAnimation("SLAM");
|
||||
}
|
||||
@ -85,7 +85,7 @@ void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
if(m.F(A::CASTING_TIMER)==0.f){
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
SoundEffect::PlaySFX("Bear Slam Attack",m.GetPos()+m.V(A::LOCKON_POS).cart());
|
||||
SETPHASE(0);
|
||||
m.I(A::PHASE)=0;
|
||||
m.I(A::BEAR_STOMP_COUNT)++;
|
||||
geom2d::circle<float>attackCircle={m.GetPos()+m.V(A::LOCKON_POS).cart(),float(operator""_Pixels(ConfigFloat("Smash Attack Diameter"))/2.f)};
|
||||
if(geom2d::overlaps(attackCircle,game->GetPlayer()->Hitbox())){
|
||||
|
@ -63,11 +63,9 @@ BulletDestroyState BearTrap::MonsterHit(Monster&monster,const uint8_t markStacks
|
||||
animation.ChangeState(internal_animState,"bear_trap.png");
|
||||
|
||||
const float bleedDamage{"Trapper.Ability 2.Marked Target Bleed"_f[0]/100.f*game->GetPlayer()->GetAttack()};
|
||||
float bleedDuration{"Trapper.Ability 2.Marked Target Bleed"_f[1]};
|
||||
const float bleedDuration{"Trapper.Ability 2.Marked Target Bleed"_f[1]};
|
||||
const float timeBetweenTicks{"Trapper.Ability 2.Marked Target Bleed"_f[2]};
|
||||
|
||||
if(game->GetPlayer()->HasEnchant("Lingering Scent"))bleedDuration+="Lingering Scent"_ENC["BLEED EXTRA DURATION"];
|
||||
|
||||
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())};
|
||||
|
@ -1,59 +0,0 @@
|
||||
#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 "AdventuresInLestoria.h"
|
||||
#include "util.h"
|
||||
#include "Effect.h"
|
||||
|
||||
INCLUDE_MONSTER_LIST
|
||||
|
||||
struct BlackHole:FadeInOutEffect{
|
||||
inline BlackHole(Oscillator<vf2d>pos,const std::string&img,float lifetime,bool onUpperLevel,Oscillator<vf2d>size,vf2d spd,Oscillator<Pixel>col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={})
|
||||
:FadeInOutEffect(pos,img,lifetime,onUpperLevel,size,spd,col,rotation,rotationSpd,additiveBlending,particleSpawnFreq,particleGenerator){}
|
||||
|
||||
inline bool Update(float fElapsedTime){
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
float distToMonster{util::distance(pos,m->GetPos())};
|
||||
if(!m->IsSolid()&&m->OnUpperLevel()==OnUpperLevel()&&m->GetZ()<1.f&&distToMonster<="Black Hole"_ENC["PULL IN RADIUS"]/100.f*24){
|
||||
float pullInForce{util::map_range<float>(distToMonster,0,"Black Hole"_ENC["PULL IN RADIUS"]/100.f*24,"Black Hole"_ENC["PULL IN FORCE MAX"],"Black Hole"_ENC["PULL IN FORCE MIN"])};
|
||||
m->AddAddedVelocity(util::pointTo(m->GetPos(),pos)*pullInForce);
|
||||
}
|
||||
}
|
||||
return FadeInOutEffect::Update(fElapsedTime);
|
||||
}
|
||||
};
|
@ -212,7 +212,7 @@ void Menu::InitializeBlacksmithCraftingWindow(){
|
||||
auto moneyIcon=blacksmithWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
|
||||
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
|
||||
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
|
||||
auto moneyDisplay=blacksmithWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,1.85f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
|
||||
auto moneyDisplay=blacksmithWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
|
||||
moneyDisplay->SetRightAlignment(true);
|
||||
Player::AddMoneyListener(moneyDisplay);
|
||||
#pragma endregion
|
||||
|
@ -57,7 +57,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
RECOVERY,
|
||||
};
|
||||
|
||||
switch(PHASE()){
|
||||
switch(m.phase){
|
||||
case PhaseName::MOVE:{
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
if(m.canMove&&distToPlayer>=ConfigInt("Closein Range")/100.f*24){
|
||||
@ -75,7 +75,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
ScratchPhaseTransition:
|
||||
m.PerformAnimation("SCRATCH");
|
||||
m.F(A::CASTING_TIMER)=ConfigInt("Ground Scratch Count")*m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
SETPHASE(PhaseName::SCRATCH);
|
||||
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();
|
||||
@ -89,7 +89,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
m.PerformShootAnimation();
|
||||
SETPHASE(PhaseName::CHARGE);
|
||||
m.phase=PhaseName::CHARGE;
|
||||
|
||||
m.AddBuff(BuffType::SPEEDBOOST,INFINITE,ConfigFloat("Charge Movespeed")/100.f-1);
|
||||
|
||||
@ -102,7 +102,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.target).length();
|
||||
|
||||
auto TransitionToRecoveryPhase=[&](){
|
||||
SETPHASE(PhaseName::RECOVERY);
|
||||
m.phase=PhaseName::RECOVERY;
|
||||
m.F(A::CHARGE_COOLDOWN)=ConfigFloat("Charge Recovery Time");
|
||||
m.PerformIdleAnimation();
|
||||
};
|
||||
@ -120,7 +120,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::CHARGE_COOLDOWN)-=fElapsedTime;
|
||||
m.targetAcquireTimer=0.f;
|
||||
m.RemoveBuff(BuffType::SPEEDBOOST);
|
||||
if(m.F(A::CHARGE_COOLDOWN)<=0)SETPHASE(PhaseName::MOVE);
|
||||
if(m.F(A::CHARGE_COOLDOWN)<=0)m.phase=PhaseName::MOVE;
|
||||
}break;
|
||||
}
|
||||
}
|
@ -103,7 +103,7 @@ BulletDestroyState Bomb::MonsterHit(Monster&monster,const uint8_t markStacksBefo
|
||||
|
||||
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,uint8_t(0),1-((fadeOutTime-GetFadeoutTimer())/fadeOutTime)))});
|
||||
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){}
|
@ -56,11 +56,11 @@ void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string
|
||||
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(PHASE()){
|
||||
switch(m.phase){
|
||||
case INITIALIZE:{
|
||||
m.F(A::BREAK_TIME)=ConfigFloat("Break Time");
|
||||
m.F(A::SHAKE_TIMER)=0.2f;
|
||||
SETPHASE(RUN);
|
||||
m.phase=RUN;
|
||||
|
||||
m.SetStrategyDeathFunction([&](GameEvent&deathEvent,Monster&m,const std::string&strategy){
|
||||
m.lifetime=0.01f;
|
||||
|
@ -41,93 +41,48 @@ All rights reserved.
|
||||
#include "Monster.h"
|
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity)
|
||||
:Buff(attachedTarget,type,duration,intensity,[](std::weak_ptr<Monster>m,Buff&b){}){}
|
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,PlayerBuffExpireCallbackFunction expireCallbackFunc)
|
||||
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity),originalDuration(this->duration),playerBuffCallbackFunc(expireCallbackFunc){}
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,MonsterBuffExpireCallbackFunction expireCallbackFunc)
|
||||
:attachedTarget(attachedTarget),type(type),duration(duration),intensity(intensity),originalDuration(this->duration),monsterBuffCallbackFunc(expireCallbackFunc){}
|
||||
|
||||
: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),originalDuration(this->duration){}
|
||||
: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),originalDuration(this->duration){
|
||||
: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 restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc)
|
||||
:Buff(attachedTarget,ONE_OFF,restorationType,overTimeType,duration,intensity,timeBetweenTicks){
|
||||
playerBuffCallbackFunc=expireCallbackFunc;
|
||||
switch(restorationType){
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc)
|
||||
:Buff(attachedTarget,ONE_OFF,restorationType,overTimeType,duration,intensity,timeBetweenTicks){
|
||||
monsterBuffCallbackFunc=expireCallbackFunc;
|
||||
switch(restorationType){
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks)
|
||||
:Buff(attachedTarget,ONE_OFF,restorationType,overTimeType,duration,intensity,timeBetweenTicks){
|
||||
switch(restorationType){
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Buff::Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc)
|
||||
:Buff(attachedTarget,type,restorationType,overTimeType,duration,intensity,timeBetweenTicks){
|
||||
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,BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc)
|
||||
:Buff(attachedTarget,type,restorationType,overTimeType,duration,intensity,timeBetweenTicks){
|
||||
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,BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks)
|
||||
:attachedTarget(attachedTarget),type(type),restorationType(restorationType),duration(duration),intensity(intensity),nextTick(timeBetweenTicks),timeBetweenTicks(timeBetweenTicks),overTimeType(overTimeType),originalDuration(this->duration){}
|
||||
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){
|
||||
if(!waitOneTick){
|
||||
duration-=fElapsedTime;
|
||||
lifetime+=fElapsedTime;
|
||||
if(enabled&&overTimeType.has_value()&&lifetime>=nextTick){
|
||||
BuffTick(game,fElapsedTime);
|
||||
nextTick+=timeBetweenTicks;
|
||||
if(restorationType==BuffRestorationType::ONE_OFF)enabled=false;
|
||||
}
|
||||
duration-=fElapsedTime;
|
||||
if(enabled&&overTimeType.has_value()&&nextTick>0&&duration<nextTick){
|
||||
BuffTick(game,fElapsedTime);
|
||||
nextTick-=timeBetweenTicks;
|
||||
if(type==ONE_OFF)enabled=false;
|
||||
}
|
||||
waitOneTick=false;
|
||||
}
|
||||
|
||||
void Buff::BuffTick(AiL*game,float fElapsedTime){
|
||||
|
@ -50,77 +50,58 @@ enum BuffType{
|
||||
FIXED_COLLISION_DMG, //Does a fixed amount of collision damage based on intensity of this buff.
|
||||
COLLISION_KNOCKBACK_STRENGTH, //Causes an amount of knockback based on intensity when hit via collision with this buff
|
||||
SELF_INFLICTED_SLOWDOWN, //Used for monsters and can't be applied by any player abilities.
|
||||
ADRENALINE_RUSH, //Intensity indicates the stack count (used by the Bloodlust enchant) this buff gives which in turn increases attack.
|
||||
ADRENALINE_RUSH,
|
||||
TRAPPER_MARK,
|
||||
SPECIAL_MARK, //The mark applied by the Opportunity Shot.
|
||||
OVER_TIME,
|
||||
ONE_OFF, //This is used as a hack fix for the RestoreDuringCast Item script since they require us to restore 1 tick immediately. Over time buffs do not apply a tick immediately.
|
||||
ONE_OFF,
|
||||
OVER_TIME_DURING_CAST,
|
||||
GLOW_PURPLE,
|
||||
COLOR_MOD,
|
||||
DAMAGE_AMPLIFICATION, //Multiplies all incoming damage by this amount.
|
||||
LETHAL_TEMPO,
|
||||
BURNING_ARROW_BURN,
|
||||
SWORD_ENCHANTMENT,
|
||||
CURSE_OF_PAIN,
|
||||
CURSE_OF_DEATH,
|
||||
AFFECTED_BY_LIGHTNING_BOLT, //Intensity indicates number of repeats remaining.
|
||||
INK_SLOWDOWN, //Intensity indicates % movespd slowdown.
|
||||
PIRATE_GHOST_CAPTAIN_PRECURSE, //A coin icon appears above the player's head.
|
||||
PIRATE_GHOST_CAPTAIN_CURSE_COIN, //A coin icon appears above the player's head.
|
||||
PIRATE_GHOST_CAPTAIN_CURSE_DOT, //The same as above, but now is a damage over time as well.
|
||||
};
|
||||
enum class BuffRestorationType{
|
||||
ONE_OFF, //This is used as a hack fix for the RestoreDuringCast Item script since they require us to restore 1 tick immediately. Over time buffs do not apply a tick immediately.
|
||||
ONE_OFF,
|
||||
OVER_TIME,
|
||||
OVER_TIME_DURING_CAST,
|
||||
};
|
||||
namespace BuffOverTimeType{
|
||||
enum BuffOverTimeType{
|
||||
HP_RESTORATION,
|
||||
HP_PCT_RESTORATION, //Percentage should be the raw percentage, NOT % form! (So 1 for 1%, not 0.01)
|
||||
HP_PCT_RESTORATION,
|
||||
MP_RESTORATION,
|
||||
MP_PCT_RESTORATION, //Percentage should be the raw percentage, NOT % form! (So 1 for 1%, not 0.01)
|
||||
MP_PCT_RESTORATION,
|
||||
HP_DAMAGE_OVER_TIME,
|
||||
HP_PCT_DAMAGE_OVER_TIME, //Percentage should be the raw percentage, NOT % form! (So 1 for 1%, not 0.01)
|
||||
HP_PCT_DAMAGE_OVER_TIME,
|
||||
};
|
||||
};
|
||||
|
||||
class AiL;
|
||||
|
||||
struct Buff{
|
||||
|
||||
using PlayerBuffExpireCallbackFunction=std::function<void(Player*attachedTarget,Buff&b)>;
|
||||
using MonsterBuffExpireCallbackFunction=std::function<void(std::weak_ptr<Monster>attachedTarget,Buff&b)>;
|
||||
|
||||
BuffType type;
|
||||
BuffRestorationType restorationType{BuffRestorationType::ONE_OFF};
|
||||
float duration=1;
|
||||
float timeBetweenTicks=1;
|
||||
float intensity=1;
|
||||
float nextTick=0;
|
||||
float lifetime{};
|
||||
std::set<ItemAttribute> attr;
|
||||
std::variant<Player*,std::weak_ptr<Monster>>attachedTarget; //Who has this buff.
|
||||
PlayerBuffExpireCallbackFunction playerBuffCallbackFunc=[](Player*p,Buff&b){};
|
||||
MonsterBuffExpireCallbackFunction monsterBuffCallbackFunc=[](std::weak_ptr<Monster>m,Buff&b){};
|
||||
float originalDuration{};
|
||||
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<ItemAttribute> attr);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,std::set<std::string> attr);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,PlayerBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,float duration,float intensity,MonsterBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,PlayerBuffExpireCallbackFunction expireCallbackFunc);
|
||||
Buff(std::variant<Player*,std::weak_ptr<Monster>>attachedTarget,BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,MonsterBuffExpireCallbackFunction expireCallbackFunc);
|
||||
|
||||
void Update(AiL*game,float fElapsedTime);
|
||||
private:
|
||||
bool waitOneTick{true};
|
||||
bool enabled{true}; //This is only turned off because the ONE_OFF effect. See BuffType::ONE_OFF for more details.
|
||||
bool enabled{true};
|
||||
std::optional<BuffOverTimeType::BuffOverTimeType>overTimeType;
|
||||
void BuffTick(AiL*game,float fElapsedTime);
|
||||
};
|
@ -41,6 +41,6 @@ All rights reserved.
|
||||
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale,float image_angle)
|
||||
:IBullet(pos,vel,radius,damage,upperLevel,friendly,col,scale,image_angle){}
|
||||
//Initializes a bullet with an animation.
|
||||
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,const std::string&animation,bool upperLevel,bool hitsMultiple,float lifetime,bool rotatesWithAngle,bool friendly,Pixel col,vf2d scale,float image_angle,std::string_view hitSound)
|
||||
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,std::string animation,bool upperLevel,bool hitsMultiple,float lifetime,bool rotatesWithAngle,bool friendly,Pixel col,vf2d scale,float image_angle,std::string_view hitSound)
|
||||
:IBullet(pos,vel,radius,damage,animation,upperLevel,hitsMultiple,lifetime,rotatesWithAngle,friendly,col,scale,image_angle,hitSound){}
|
||||
void Bullet::ModifyOutgoingDamageData(HurtDamageInfo&data){}
|
@ -43,7 +43,7 @@ class Bullet:public IBullet{
|
||||
public:
|
||||
Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f);
|
||||
//Initializes a bullet with an animation.
|
||||
Bullet(vf2d pos,vf2d vel,float radius,int damage,const std::string&animation,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool rotatesWithAngle=false,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f,std::string_view hitSound="");
|
||||
Bullet(vf2d pos,vf2d vel,float radius,int damage,std::string animation,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool rotatesWithAngle=false,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f,std::string_view hitSound="");
|
||||
protected:
|
||||
virtual void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
virtual void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
@ -39,10 +39,6 @@ All rights reserved.
|
||||
#include "Bullet.h"
|
||||
#include "Direction.h"
|
||||
#include "Effect.h"
|
||||
#include "TrailEffect.h"
|
||||
#include "Entity.h"
|
||||
#include <variant>
|
||||
#include <memory>
|
||||
|
||||
struct EnergyBolt:public Bullet{
|
||||
float lastParticleSpawn=0;
|
||||
@ -50,7 +46,7 @@ struct EnergyBolt:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct FireBolt:public Bullet{
|
||||
@ -59,25 +55,16 @@ struct FireBolt:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
std::optional<std::reference_wrapper<TrailEffect>>flameTrail;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct LightningBolt:public Bullet{
|
||||
enum ChainLightningStatus{//Any other number represents something not defined here, the number of hits remaining.
|
||||
ORIGINAL_BOLT=-1,
|
||||
NO_MORE_HITS=0,
|
||||
};
|
||||
float lastParticleSpawn=0;
|
||||
LightningBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
/*chainLightningRepeatCount - Set to higher amount to indicate when this monster instead affects monsters due to the enchantment, which changes which shock count variable we look at and will be used for counting down shock times.*/
|
||||
static void ApplyLightningShock(const Monster&monster,const bool onUpperLevel,const ChainLightningStatus chainLightningRepeatCount=ORIGINAL_BOLT);
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct Arrow:public Bullet{
|
||||
@ -85,7 +72,6 @@ struct Arrow:public Bullet{
|
||||
float finalDistance=0;
|
||||
float acc=PI/2*250;
|
||||
vf2d targetPos;
|
||||
bool poisonArrow{false};
|
||||
Arrow(vf2d pos,vf2d targetPos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
|
||||
Arrow(vf2d pos,vf2d targetPos,vf2d vel,const std::string_view gfx,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
|
||||
void Update(float fElapsedTime)override;
|
||||
@ -94,20 +80,16 @@ struct Arrow:public Bullet{
|
||||
const vf2d PointToBestTargetPath(const uint8_t perceptionLevel);
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct ChargedArrow:public Bullet{
|
||||
vf2d lastLaserPos;
|
||||
ChargedArrow(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
|
||||
ChargedArrow(const std::string&shotArrowGraphic,const std::string&laserGraphic,vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
std::string laserGraphic;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct FrogTongue:public Bullet{
|
||||
@ -121,7 +103,7 @@ struct FrogTongue:public Bullet{
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct Wisp:public Bullet{
|
||||
@ -129,7 +111,7 @@ struct Wisp:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
enum class HorizontalFlip{
|
||||
@ -154,11 +136,11 @@ struct DaggerStab:public Bullet{
|
||||
float daggerStabDistance;
|
||||
float knockbackAmt;
|
||||
DirectionOffsets daggerPositionOffsets;
|
||||
DaggerStab(Monster&sourceMonster,const std::string&image,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerStabDistance,const DirectionOffsets offsets,bool friendly=false,Pixel col=WHITE);
|
||||
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=false,Pixel col=WHITE);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct DaggerSlash:public Bullet{
|
||||
@ -167,11 +149,11 @@ struct DaggerSlash:public Bullet{
|
||||
float frameDuration;
|
||||
float daggerSlashDistance;
|
||||
float knockbackAmt;
|
||||
DaggerSlash(Monster&sourceMonster,const std::string&image,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerSlashDistance,bool friendly=false,Pixel col=WHITE);
|
||||
DaggerSlash(Monster&sourceMonster,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerSlashDistance,bool friendly=false,Pixel col=WHITE);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct Bomb:public Bullet{
|
||||
@ -188,7 +170,7 @@ struct Bomb:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
};
|
||||
|
||||
@ -214,7 +196,7 @@ struct LevitatingRock:public Bullet{
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
void AssignMaster(LevitatingRock*masterRock);
|
||||
const bool IsMaster()const;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct Tornado:public Bullet{
|
||||
@ -229,7 +211,7 @@ struct Tornado:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct Debris:public Bullet{
|
||||
@ -240,7 +222,7 @@ struct Debris:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
};
|
||||
|
||||
@ -253,12 +235,12 @@ struct LargeTornado:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct Feather:public Bullet{
|
||||
Feather(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f);
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct LargeStone:public Bullet{
|
||||
@ -268,7 +250,7 @@ protected:
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
private:
|
||||
const float gravity;
|
||||
const float fixedTimeStep{1/30.f};
|
||||
@ -282,13 +264,13 @@ private:
|
||||
const float knockbackAmt;
|
||||
};
|
||||
|
||||
struct FallingBullet:public Bullet{
|
||||
//The position for this bullet represents where the falling bullet should land.
|
||||
FallingBullet(const std::string&imgName,vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple=false,float knockbackAmt=0.f,float lifetime=INFINITE,bool friendly=false,Pixel spellCircleCol=WHITE,vf2d scale={1,1},float image_angle=0.f,float spellCircleRotation=0.f,float spellCircleRotationSpd=0.f,Pixel insigniaCol=WHITE,float insigniaRotation=0.f,float insigniaRotationSpd=0.f);
|
||||
struct FallingStone:public Bullet{
|
||||
//The position for this bullet represents where the falling stone should land.
|
||||
FallingStone(vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple=false,float knockbackAmt=0.f,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f,float spellCircleRotation=0.f,float spellCircleRotationSpd=0.f,Pixel insigniaCol=WHITE,float insigniaRotation=0.f,float insigniaRotationSpd=0.f);
|
||||
protected:
|
||||
void Update(float fElapsedTime)override;
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
private:
|
||||
const vf2d targetPos;
|
||||
const float zVel{};
|
||||
@ -296,7 +278,6 @@ private:
|
||||
SpellCircle indicator;
|
||||
const float knockbackAmt;
|
||||
float lastTrailEffect{};
|
||||
const float collisionRadius{};
|
||||
};
|
||||
|
||||
//While not a bullet directly, the DeadlyDash class generates a bunch of afterimages and collision checks.
|
||||
@ -304,7 +285,7 @@ struct DeadlyDash:public Bullet{
|
||||
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);
|
||||
protected:
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
private:
|
||||
const vf2d startPos;
|
||||
const vf2d endPos;
|
||||
@ -319,7 +300,7 @@ struct BearTrap:public Bullet{
|
||||
BearTrap(vf2d pos,float radius,int damage,float fadeinTime,float fadeoutTime,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1});
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
};
|
||||
|
||||
struct ExplosiveTrap:public Bullet{
|
||||
@ -327,7 +308,7 @@ struct ExplosiveTrap:public Bullet{
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
private:
|
||||
void Detonate();
|
||||
const float explosionRadius{};
|
||||
@ -335,13 +316,11 @@ private:
|
||||
float automaticDetonationTime{};
|
||||
float activationWaitTime{};
|
||||
float lastBeepTime{};
|
||||
int explosionCount{1};
|
||||
float rearmTime{};
|
||||
uint8_t beepCount{1U};
|
||||
};
|
||||
|
||||
struct PurpleEnergyBall:public Bullet{
|
||||
PurpleEnergyBall(vf2d pos,float radius,float homingRadius,int damage,bool upperLevel,vf2d speed,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1});
|
||||
PurpleEnergyBall(vf2d pos,float radius,float homingRadius,int damage,bool upperLevel,vf2d speed,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1});
|
||||
void Update(float fElapsedTime)override;
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
@ -349,115 +328,20 @@ struct PurpleEnergyBall:public Bullet{
|
||||
private:
|
||||
const vf2d initialScale;
|
||||
const float homingRadius;
|
||||
int bounceCount{1};
|
||||
float lastHitTimer{0.f};
|
||||
std::optional<std::weak_ptr<Monster>>lastHitTarget;
|
||||
std::optional<std::weak_ptr<Monster>>homingTarget;
|
||||
};
|
||||
|
||||
struct ThrownProjectile:public Bullet{
|
||||
ThrownProjectile(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f},const std::optional<Effect>explodeEffect={},const std::optional<std::string>explodeSoundEffect={},const std::optional<LingeringEffect>lingeringEffect={});
|
||||
struct PoisonBottle:public Bullet{
|
||||
PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
|
||||
void Update(float fElapsedTime)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
void _OnGroundLand();
|
||||
virtual void OnGroundLand(); //This is called when the projectile lands, damage is not dealt yet, default behavior just deals damage in the area.
|
||||
protected:
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
private:
|
||||
const vf2d targetPos;
|
||||
const vf2d startingPos;
|
||||
const float totalFallTime;
|
||||
float originalRisingTime;
|
||||
float originalFallingTime;
|
||||
const float originalRisingTime,originalFallingTime;
|
||||
float risingTime,fallingTime;
|
||||
const float initialZ;
|
||||
const float totalRiseZAmt;
|
||||
const float explodeRadius;
|
||||
const std::optional<Effect>explodeEffect;
|
||||
const std::optional<std::string>explodeSoundEffect;
|
||||
const std::optional<LingeringEffect>lingeringEffect;
|
||||
const std::string img;
|
||||
};
|
||||
|
||||
struct PoisonBottle:public ThrownProjectile{
|
||||
PoisonBottle(vf2d pos,vf2d targetPos,const std::string&img,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
virtual void OnGroundLand()override final; //This is called when the projectile lands, damage is not dealt yet, default behavior just deals damage in the area.
|
||||
private:
|
||||
const float bounceExplodeRadius;
|
||||
int additionalBounceCount;
|
||||
};
|
||||
|
||||
struct BurstBullet:public Bullet{
|
||||
BurstBullet(vf2d pos,vf2d vel,const Entity target,const float explodeDist,const int extraBulletCount,const float extraBulletHeadingAngleChange,const float extraBulletRadius,const vf2d extraBulletScale,const float extraBulletStartSpeed,const float extraBulletAcc,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
void Explode();
|
||||
const Entity target;
|
||||
const float explodeDist;
|
||||
Oscillator<vf2d>scaleOscillator;
|
||||
bool activated{false};
|
||||
float explodeTimer{0.f};
|
||||
const int extraBulletCount;
|
||||
const float extraBulletHeadingAngleChange; //In radians.
|
||||
const float extraBulletRadius;
|
||||
const vf2d extraBulletScale;
|
||||
const float extraBulletAcc;
|
||||
const float extraBulletStartSpeed;
|
||||
};
|
||||
|
||||
struct RotateBullet:public Bullet{
|
||||
//headingAngleChange determines how the startingAngle adjusts over time (in radians).
|
||||
RotateBullet(vf2d pos,float startingAng,float startSpeed,float acc,float headingAngleChange,float radius,int damage,bool upperLevel,bool friendly,Pixel col);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
const float headingAngleChange;
|
||||
};
|
||||
|
||||
struct InkBullet:public Bullet{
|
||||
InkBullet(const vf2d pos,const vf2d targetPos,const vf2d vel,const float inkExplosionRadius,const float inkPuddleLifetime,const float inkSlowdownDuration,const float inkSlowdownPct,const float inkPuddleRadius,const bool upperLevel,const bool friendly);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
void Splat();
|
||||
const vf2d targetPos;
|
||||
const float inkExplosionRadius;
|
||||
const float inkSlowdownPct;
|
||||
const float inkSlowdownDuration;
|
||||
const float inkPuddleLifetime;
|
||||
const float inkPuddleRadius;
|
||||
};
|
||||
|
||||
struct HomingBullet:public Bullet{
|
||||
HomingBullet(const vf2d pos,const Entity target,const float rotateTowardsSpeed,const float rotateTowardsSpeedCoveredInInk,const float lifetime,const float speed,const float radius,const int damage,const bool upperLevel,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1},const float image_angle=0.f);
|
||||
void Update(float fElapsedTime)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
const Entity target;
|
||||
const float rotateTowardsSpeed;
|
||||
const float rotateTowardsSpeedCoveredInInk;
|
||||
};
|
||||
|
||||
struct GhostSaber:public Bullet{
|
||||
GhostSaber(const vf2d pos,const std::weak_ptr<Monster>target,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const float expandSpd,const int damage,const bool upperLevel,const float rotSpd,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1},const float image_angle=0.f);
|
||||
void Update(float fElapsedTime)override;
|
||||
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
|
||||
BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data)override;
|
||||
private:
|
||||
const std::weak_ptr<Monster>attachedMonster;
|
||||
const float rotSpd;
|
||||
float distFromTarget;
|
||||
float rot;
|
||||
const float knockbackAmt;
|
||||
float particleTimer{};
|
||||
float aliveTime{};
|
||||
float expandSpd{};
|
||||
Oscillator<uint8_t>alphaOscillator{128U,255U,0.6f};
|
||||
};
|
@ -1,104 +0,0 @@
|
||||
#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 "Effect.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include "DEFINES.h"
|
||||
#include "util.h"
|
||||
#include "SoundEffect.h"
|
||||
#include "TrailEffect.h"
|
||||
#include <ranges>
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_MONSTER_LIST
|
||||
INCLUDE_ANIMATION_DATA
|
||||
|
||||
BurstBullet::BurstBullet(vf2d pos,vf2d vel,const Entity target,const float explodeDist,const int extraBulletCount,const float extraBulletHeadingAngleChange,const float extraBulletRadius,const vf2d extraBulletScale,const float extraBulletStartSpeed,const float extraBulletAcc,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale)
|
||||
:Bullet(pos,vel,radius,damage,"burstrotatebullet.png",upperLevel,false,INFINITE,true,friendly,col,scale),extraBulletStartSpeed(extraBulletStartSpeed),extraBulletAcc(extraBulletAcc),extraBulletRadius(extraBulletRadius),extraBulletScale(extraBulletScale),extraBulletHeadingAngleChange(extraBulletHeadingAngleChange),extraBulletCount(extraBulletCount),target(target),explodeDist(explodeDist),scaleOscillator({0.85f,0.85f},{1.f,1.f},0.3f){
|
||||
animation.mult=0.f; //Prevent animating.
|
||||
}
|
||||
|
||||
void BurstBullet::Explode(){
|
||||
fadeOutTime=0.1f;
|
||||
HurtType targets{friendly?HurtType::MONSTER:HurtType::PLAYER};
|
||||
for(const auto&[target,isHurt]:game->Hurt(pos,radius,damage,OnUpperLevel(),GetZ(),targets)){
|
||||
if(isHurt){
|
||||
Entity ent{target};
|
||||
ent.SetIframeTime(0.3f);
|
||||
}
|
||||
}
|
||||
SoundEffect::PlaySFX("Burst Bullet Explode",pos);
|
||||
for(int i:std::ranges::iota_view(0,extraBulletCount+1)){
|
||||
const float angle{(360.f/extraBulletCount)*i};
|
||||
const vf2d newPos{pos+vf2d{radius/2.f,angle}.cart()};
|
||||
CreateBullet(RotateBullet)(newPos,angle,extraBulletStartSpeed,extraBulletAcc,extraBulletHeadingAngleChange,extraBulletRadius,damage,OnUpperLevel(),friendly,WHITE)EndBullet;
|
||||
}
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
void BurstBullet::Update(float fElapsedTime){
|
||||
if(IsActivated()){
|
||||
if(!activated){
|
||||
scaleOscillator.Update(fElapsedTime);
|
||||
if(util::distance(target.GetPos(),pos)<=explodeDist){
|
||||
activated=true;
|
||||
animation.mult=1.f;
|
||||
explodeTimer=ANIMATION_DATA[animation.currentStateName].GetTotalAnimationDuration();
|
||||
}
|
||||
}else if(explodeTimer>0.f){
|
||||
explodeTimer-=fElapsedTime;
|
||||
if(explodeTimer<=0.f)Explode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BulletDestroyState BurstBullet::PlayerHit(Player*player){
|
||||
Explode();
|
||||
player->ApplyIframes(0.2f);
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
BulletDestroyState BurstBullet::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){
|
||||
Explode();
|
||||
monster.ApplyIframes(0.2f);
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
void BurstBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){
|
||||
data.damage=0; //Nullify the damage because proximity damage will occur instead.
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
Ghost of Pirate Captain
|
||||
|
||||
60 000 HP
|
||||
Size 600%
|
||||
Movespeed 50%
|
||||
|
||||
Damage:
|
||||
Canonshot 80 dmg
|
||||
Shrapnel 35 dmg
|
||||
Sabres 75 dmg
|
||||
Collision 50 dmg
|
||||
Curse nothing for first 15 seconds, after that 5 dmg per second.
|
||||
|
||||
----------------------
|
||||
|
||||
Skills:
|
||||
- Command Canons (2 Kinds of Attack, Big Canon Balls and spreaded shrapnel shot)
|
||||
- Spreading the curse (The Ghost Throws a Coin towards the Player, and the Player gets marked. starts getting dot damage after time if the coin doesnt get returned to the Pirate Treasure, which has a fixed position on the map)
|
||||
- Hide and Seek (The Ghost hides Behind a Rock, and Canons Bombard the area until player hits the Captain once)
|
||||
- Ghost Sabers (Spinning Ghost Swords)
|
||||
|
||||
- 70%, 40% & 10% Curse + Hide - permanent Bombardment + Shrapnel Shot every 5 seconds.
|
||||
|
||||
Once found on the 10% Hide and Seek Spawns a Cage around the Pirate Treasure, and Spreads the curse on the player again. (If player didnt cleanse the curse before finding the captain it just stays up.)
|
||||
Both kind of Canon attacks happen at all time now, 10 Ghost Sabers spawn that circle around the boss in different directions
|
||||
Boss stands still in the middle (maybe raising an arm animation wise or something to show that he is controlling the Sabers)
|
||||
|
||||
----------------------
|
||||
Normal Fight
|
||||
|
||||
Canon Shots happen with a 0,4 second delay. between shooting an impact is a 2.5 second delay.
|
||||
Canon have a 350 impact size
|
||||
|
||||
Shrapnel shoots similar to small bolders from chapter 2 boss. 25 hits, 50 radius. Delay between shooting and impact also 2.5 seconds.
|
||||
|
||||
its 8 canon shots, 2 second silence, 1 shrapnel shot, 2 second silence, repeat.
|
||||
|
||||
the Canon shots can happen in one of the following patterns:
|
||||
- Bombardment: every shot is at a random location within 900 Range of the player. (This is also the move during hide and seek.)
|
||||
- precise Bombardment: same as before but within 700 range of the player.
|
||||
- Shooting in a line: the 4th or 5th hit would hit the player if the player doesnt move at all.
|
||||
- Sharpshooter: aiming directly at the player, skipping every 2nd shot. (only 4 instead of 8 shots)
|
||||
- prediction: shoots in the direction of the current or last players movement and predicts where the player would be in 2.8 seconds. (slightly further then where the player would be at impact. thats why 2.8 instead of 2.5 seconds)
|
||||
|
||||
Boss spawns after the first Canon rotation ends.
|
||||
|
||||
Boss constantly Slowly floats towards player, within 400 range it uses Ghost Sabers once every 4 seconds. (Throws them, Similar movement like Hammers of a Hammerdin in Diablo 2 - https://www.youtube.com/watch?v=jZfH4SY4Lsc - both the spinning animation and the flightpath are a good example. Sabre always spawn of oposite side towards player to give meeles time to dodge.)
|
||||
On Collision damage trigger a slash animation with its saber.
|
||||
|
@ -68,8 +68,8 @@ protected:
|
||||
|
||||
vi2d descriptionPos=iconPos+vi2d{int(rect.size.y)-2,9};
|
||||
|
||||
window.DrawShadowStringPropDecal(descriptionPos-vf2d{0,10},ability->name,{0xFF,0xAF,0x56},BLACK,{0.8f,1.f},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
|
||||
window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
|
||||
window.DrawShadowStringPropDecal(descriptionPos-vf2d{0,10},ability->name,{0xFF,0xAF,0x56},BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
|
||||
window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
|
||||
|
||||
InputType controlType=KEY;
|
||||
if(Input::UsingGamepad())controlType=CONTROLLER;
|
||||
|
@ -53,7 +53,6 @@ All rights reserved.
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include "steam/isteamuserstats.h"
|
||||
#endif
|
||||
#include <bit>
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
@ -67,19 +66,41 @@ namespace CharacterMenuWindow{
|
||||
AttributeData{"Health",[]()->int{return game->GetPlayer()->GetMaxHealth();}},
|
||||
AttributeData{"Attack",[]()->int{return game->GetPlayer()->GetAttack();}},
|
||||
AttributeData{"Defense",[]()->int{return game->GetPlayer()->GetDefense();}},
|
||||
AttributeData{"Move Spd %",[]()->int{return round(game->GetPlayer()->GetMoveSpdMult()*100);}},
|
||||
AttributeData{"CDR",[]()->int{return round(game->GetPlayer()->GetCooldownReductionPct()*100);}},
|
||||
AttributeData{"Crit Rate",[]()->int{return round(game->GetPlayer()->GetCritRatePct()*100);}},
|
||||
AttributeData{"Crit Dmg",[]()->int{return round(game->GetPlayer()->GetCritDmgPct()*100);}},
|
||||
AttributeData{"Move Spd %",[]()->int{return ceil(game->GetPlayer()->GetMoveSpdMult()*100);}},
|
||||
AttributeData{"CDR",[]()->int{return ceil(game->GetPlayer()->GetCooldownReductionPct()*100);}},
|
||||
AttributeData{"Crit Rate",[]()->int{return ceil(game->GetPlayer()->GetCritRatePct()*100);}},
|
||||
AttributeData{"Crit Dmg",[]()->int{return ceil(game->GetPlayer()->GetCritDmgPct()*100);}},
|
||||
};
|
||||
const static std::array<std::string,8>slotNames{"Helmet","Weapon","Armor","Gloves","Pants","Shoes","Ring 1","Ring 2"};
|
||||
template<class T>
|
||||
std::shared_ptr<RowItemDisplay>GenerateItemDisplay(std::shared_ptr<ScrollableWindowComponent>parent,int invIndex,const std::weak_ptr<Item>it){
|
||||
auto component=parent->ADD("Equip Item "+std::to_string(invIndex),T)(geom2d::rect<float>{{2,2+invIndex*29.f},{120-15,28}},it,
|
||||
[&](MenuFuncData data){
|
||||
const auto SelectedEquipIsDifferent=[](std::weak_ptr<RowItemDisplay>comp){
|
||||
EquipSlot slot=EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE));
|
||||
std::weak_ptr<Item>currentItem=Inventory::GetEquip(slot);
|
||||
|
||||
switch(slot){
|
||||
case EquipSlot::RING1:
|
||||
case EquipSlot::RING2:{
|
||||
std::weak_ptr<Item>otherItem;
|
||||
|
||||
if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
|
||||
else
|
||||
if(slot&EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1);
|
||||
return ISBLANK(otherItem)||
|
||||
(&*comp.lock()->GetItem().lock()!=&*otherItem.lock()&&
|
||||
&*comp.lock()->GetItem().lock()!=&*currentItem.lock());
|
||||
}break;
|
||||
default:{
|
||||
return &*comp.lock()->GetItem().lock()!=&*currentItem.lock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::weak_ptr<T>comp=DYNAMIC_POINTER_CAST<T>(data.component.lock());
|
||||
if(!comp.expired()){
|
||||
if(Item::SelectedEquipIsDifferent(comp.lock()->GetItem(),EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE)))){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
|
||||
if(SelectedEquipIsDifferent(comp)){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
|
||||
Inventory::EquipItem(comp.lock()->GetItem(),EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE)));
|
||||
|
||||
#pragma region Fully Decked Out Achievement
|
||||
@ -123,14 +144,13 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
vf2d windowSize=game->GetScreenSize()-vf2d{52,52};
|
||||
Menu*characterMenuWindow=CreateMenu(CHARACTER_MENU,CENTERED,windowSize);
|
||||
|
||||
characterMenuWindow->ADD("Character Label",MenuLabel)(geom2d::rect<float>{{0,-4},{float(windowSize.x*0.5f)-1,24}},"Character",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
|
||||
characterMenuWindow->ADD("Character Label",MenuLabel)(geom2d::rect<float>{{0,-4},{float(windowSize.x)-1,24}},"Character",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
|
||||
characterMenuWindow->ADD("Equip Slot Outline",MenuComponent)(geom2d::rect<float>{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
|
||||
//Don't load this graphical element if testing mode is enabled.
|
||||
characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)(geom2d::rect<float>{{118,18},{130,windowSize.y-28}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END;
|
||||
|
||||
characterMenuWindow->ADD("Level Class Outline",MenuComponent)(geom2d::rect<float>{{windowSize.x*0.5f+4.f,-4},{float(windowSize.x*0.5f)-4,24}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
|
||||
characterMenuWindow->ADD("Level Class Display",MenuLabel)(geom2d::rect<float>{vf2d{windowSize.x*0.5f+10.f,0.f},{118.f,8.f}},std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()),1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
|
||||
characterMenuWindow->ADD("XP Bar",ProgressBar)(geom2d::rect<float>{vf2d{windowSize.x*0.5f+10.f,10.f},{118.f,8.f}},Pixel{247,183,82},BLACK,game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired(),"xp")END;
|
||||
characterMenuWindow->ADD("Level Class Display",MenuLabel)(geom2d::rect<float>{vf2d{126.f,windowSize.y-28},{118.f,8.f}},std::format("Lv{} {}",game->GetPlayer()->Level(),game->GetPlayer()->GetClassName()),1.f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
|
||||
characterMenuWindow->ADD("XP Bar",ProgressBar)(geom2d::rect<float>{vf2d{126.f,10.f+windowSize.y-28},{118.f,8.f}},Pixel{247,183,82},BLACK,game->GetPlayer()->CurrentXP(),game->GetPlayer()->NextLevelXPRequired(),"xp")END;
|
||||
|
||||
characterMenuWindow->ADD("Equip Selection Outline",MenuComponent)(geom2d::rect<float>{{123,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END
|
||||
->Disable();
|
||||
@ -178,138 +198,155 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
EquipSlot slot=EquipSlot(equipSlot);
|
||||
auto equipmentSlot=characterMenuWindow->ADD("Equip Slot "+CharacterMenuWindow::slotNames[i],EquipSlotButton)(geom2d::rect<float>{{x,y+28},{24,24}},slot,
|
||||
[&](MenuFuncData data){
|
||||
if(CanModifyEquipSlots()){
|
||||
EquipSlot slot=EquipSlot(data.component.lock()->I(Attribute::EQUIP_TYPE));
|
||||
data.menu.I(A::EQUIP_TYPE)=int(slot);
|
||||
EquipSlot slot=EquipSlot(data.component.lock()->I(Attribute::EQUIP_TYPE));
|
||||
data.menu.I(A::EQUIP_TYPE)=int(slot);
|
||||
|
||||
const std::vector<std::shared_ptr<Item>>&equips=Inventory::get("Equipment");
|
||||
const std::vector<std::shared_ptr<Item>>&accessories=Inventory::get("Accessories");
|
||||
std::vector<std::weak_ptr<Item>>availableEquipment;
|
||||
std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](const std::shared_ptr<Item>it){
|
||||
return it->GetEquipSlot()&slot;
|
||||
});
|
||||
std::copy_if(accessories.begin(),accessories.end(),std::back_inserter(availableEquipment),[&](const std::shared_ptr<Item>it){
|
||||
return it->GetEquipSlot()&slot;
|
||||
});
|
||||
const std::vector<std::shared_ptr<Item>>&equips=Inventory::get("Equipment");
|
||||
const std::vector<std::shared_ptr<Item>>&accessories=Inventory::get("Accessories");
|
||||
std::vector<std::weak_ptr<Item>>availableEquipment;
|
||||
std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](const std::shared_ptr<Item>it){
|
||||
return it->GetEquipSlot()&slot;
|
||||
});
|
||||
std::copy_if(accessories.begin(),accessories.end(),std::back_inserter(availableEquipment),[&](const std::shared_ptr<Item>it){
|
||||
return it->GetEquipSlot()&slot;
|
||||
});
|
||||
|
||||
std::shared_ptr<ScrollableWindowComponent>equipList=Component<ScrollableWindowComponent>(data.component.lock()->parentMenu,"Equip List");
|
||||
equipList->RemoveAllComponents();
|
||||
for(int counter=0;const std::weak_ptr<Item>it:availableEquipment){
|
||||
std::shared_ptr<RowItemDisplay>equip;
|
||||
const bool isAccessorySlot=slot&(EquipSlot::RING1|EquipSlot::RING2);
|
||||
if(isAccessorySlot){
|
||||
equip=CharacterMenuWindow::GenerateItemDisplay<AccessoryRowItemDisplay>(equipList,counter,it);
|
||||
}else{
|
||||
equip=CharacterMenuWindow::GenerateItemDisplay<RowItemDisplay>(equipList,counter,it);
|
||||
}
|
||||
|
||||
equip->SetHoverFunc(
|
||||
[&](MenuFuncData data){
|
||||
if(!data.component.lock()->GetSubcomponentParent().expired())return true;
|
||||
std::weak_ptr<RowItemDisplay>button=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
|
||||
if(!button.expired()){
|
||||
const std::weak_ptr<Item>buttonItem=button.lock()->GetItem();
|
||||
std::vector<float>statsBeforeEquip;
|
||||
EquipSlot slot=EquipSlot(button.lock()->I(Attribute::EQUIP_TYPE));
|
||||
for(const CharacterMenuWindow::AttributeData&attribute:CharacterMenuWindow::displayAttrs){
|
||||
statsBeforeEquip.push_back(attribute.calcFunc());
|
||||
}
|
||||
|
||||
int healthBeforeEquip=game->GetPlayer()->GetHealth();
|
||||
int manaBeforeEquip=game->GetPlayer()->GetMana();
|
||||
|
||||
std::weak_ptr<Item>equippedItem=Inventory::GetEquip(slot);
|
||||
std::weak_ptr<Item>otherItem;
|
||||
if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
|
||||
else
|
||||
if(slot&EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1);
|
||||
if(Item::SelectedEquipIsDifferent(button.lock()->GetItem(),slot)){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
|
||||
Inventory::EquipItem(buttonItem,slot);
|
||||
for(int counter=0;const CharacterMenuWindow::AttributeData&attribute:CharacterMenuWindow::displayAttrs){
|
||||
std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute.attrName).Name())+" Label");
|
||||
int statChangeAmt=attribute.calcFunc()-statsBeforeEquip[counter];
|
||||
statDisplayLabel.lock()->SetStatChangeAmt(statChangeAmt);
|
||||
counter++;
|
||||
}
|
||||
Inventory::UnequipItem(slot);
|
||||
if(!ISBLANK(equippedItem)){
|
||||
Inventory::EquipItem(equippedItem,slot);
|
||||
game->GetPlayer()->Heal(healthBeforeEquip-game->GetPlayer()->GetHealth(),true);
|
||||
game->GetPlayer()->RestoreMana(manaBeforeEquip-game->GetPlayer()->GetMana(),true);
|
||||
}
|
||||
if(!ISBLANK(otherItem)){
|
||||
if(slot&EquipSlot::RING1)Inventory::EquipItem(otherItem,EquipSlot::RING2);
|
||||
else
|
||||
if(slot&EquipSlot::RING2)Inventory::EquipItem(otherItem,EquipSlot::RING1);
|
||||
}
|
||||
}
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Name")->SetItem(buttonItem);
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Description")->SetItem(buttonItem);
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Name")->Enable();
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Description")->Enable();
|
||||
}else{
|
||||
ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
equip->SetMouseOutFunc(
|
||||
[](MenuFuncData data){
|
||||
for(int counter=0;const CharacterMenuWindow::AttributeData&attribute:CharacterMenuWindow::displayAttrs){
|
||||
std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute.attrName).Name())+" Label");
|
||||
statDisplayLabel.lock()->SetStatChangeAmt(0);
|
||||
counter++;
|
||||
}
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Name")->Disable();
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Description")->Disable();
|
||||
return true;
|
||||
});
|
||||
|
||||
equip->SetShowQuantity(false);
|
||||
equip->SetSelectionType(SelectionType::NONE);
|
||||
|
||||
equip->I(Attribute::EQUIP_TYPE)=int(slot);
|
||||
if(Inventory::GetEquip(slot)==it){
|
||||
equip->SetSelected(true);
|
||||
}
|
||||
equip->SetCompactDescriptions(NON_COMPACT);
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
equipList->I(Attribute::INDEXED_THEME)=data.component.lock()->I(Attribute::INDEXED_THEME);
|
||||
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Outline")->Enable();
|
||||
equipList->Enable();
|
||||
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Bottom Outline")->Enable();
|
||||
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Select Button")->Enable();
|
||||
Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Disable();
|
||||
equipmentWindowOpened=true;
|
||||
|
||||
auto equipmentList=equipList->GetComponents();
|
||||
auto itemEquipped=std::find_if(equipmentList.begin(),equipmentList.end(),[&](std::weak_ptr<MenuComponent>&component){
|
||||
return !ISBLANK(Inventory::GetEquip(slot))&&component.lock()->GetSubcomponentParent().expired()&&&*DYNAMIC_POINTER_CAST<RowItemDisplay>(component)->GetItem().lock()==&*Inventory::GetEquip(slot).lock();
|
||||
});
|
||||
if(itemEquipped!=equipmentList.end()){
|
||||
data.menu.SetSelection(*itemEquipped,true,true);
|
||||
if(Menu::UsingMouseNavigation()){
|
||||
equipList->HandleOutsideDisabledButtonSelection(*itemEquipped);
|
||||
}
|
||||
data.menu.I(A::ITEM_SLOT)=equipList->GetComponentIndex(*itemEquipped);
|
||||
}else
|
||||
if(equipmentList.size()>0){
|
||||
data.menu.SetSelection(equipmentList[0],true,true);
|
||||
if(Menu::UsingMouseNavigation()){
|
||||
equipList->HandleOutsideDisabledButtonSelection(equipmentList[0]);
|
||||
}
|
||||
data.menu.I(A::ITEM_SLOT)=0;
|
||||
std::shared_ptr<ScrollableWindowComponent>equipList=Component<ScrollableWindowComponent>(data.component.lock()->parentMenu,"Equip List");
|
||||
equipList->RemoveAllComponents();
|
||||
for(int counter=0;const std::weak_ptr<Item>it:availableEquipment){
|
||||
std::shared_ptr<RowItemDisplay>equip;
|
||||
const bool isAccessorySlot=slot&(EquipSlot::RING1|EquipSlot::RING2);
|
||||
if(isAccessorySlot){
|
||||
equip=CharacterMenuWindow::GenerateItemDisplay<AccessoryRowItemDisplay>(equipList,counter,it);
|
||||
}else{
|
||||
data.menu.SetSelection("Equip Selection Select Button"sv);
|
||||
equip=CharacterMenuWindow::GenerateItemDisplay<RowItemDisplay>(equipList,counter,it);
|
||||
}
|
||||
|
||||
equip->SetHoverFunc(
|
||||
[&](MenuFuncData data){
|
||||
const auto SelectedEquipIsDifferent=[](std::weak_ptr<RowItemDisplay>comp){
|
||||
EquipSlot slot=EquipSlot(comp.lock()->I(Attribute::EQUIP_TYPE));
|
||||
std::weak_ptr<Item>currentItem=Inventory::GetEquip(slot);
|
||||
|
||||
return true;
|
||||
}else{
|
||||
game->AddNotification(AiL::Notification{"Cannot change equipment in a stage!",5.f,YELLOW});
|
||||
return false;
|
||||
switch(slot){
|
||||
case EquipSlot::RING1:
|
||||
case EquipSlot::RING2:{
|
||||
std::weak_ptr<Item>otherItem;
|
||||
|
||||
if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
|
||||
else
|
||||
if(slot&EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1);
|
||||
return ISBLANK(otherItem)||
|
||||
(&*comp.lock()->GetItem().lock()!=&*otherItem.lock()&&
|
||||
&*comp.lock()->GetItem().lock()!=&*currentItem.lock());
|
||||
}break;
|
||||
default:{
|
||||
return &*comp.lock()->GetItem().lock()!=&*currentItem.lock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(!data.component.lock()->GetSubcomponentParent().expired())return true;
|
||||
std::weak_ptr<RowItemDisplay>button=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
|
||||
if(!button.expired()){
|
||||
const std::weak_ptr<Item>buttonItem=button.lock()->GetItem();
|
||||
std::vector<float>statsBeforeEquip;
|
||||
EquipSlot slot=EquipSlot(button.lock()->I(Attribute::EQUIP_TYPE));
|
||||
for(const CharacterMenuWindow::AttributeData&attribute:CharacterMenuWindow::displayAttrs){
|
||||
statsBeforeEquip.push_back(attribute.calcFunc());
|
||||
}
|
||||
|
||||
int healthBeforeEquip=game->GetPlayer()->GetHealth();
|
||||
int manaBeforeEquip=game->GetPlayer()->GetMana();
|
||||
|
||||
std::weak_ptr<Item>equippedItem=Inventory::GetEquip(slot);
|
||||
std::weak_ptr<Item>otherItem;
|
||||
if(slot&EquipSlot::RING1)otherItem=Inventory::GetEquip(EquipSlot::RING2);
|
||||
else
|
||||
if(slot&EquipSlot::RING2)otherItem=Inventory::GetEquip(EquipSlot::RING1);
|
||||
if(SelectedEquipIsDifferent(button.lock())){ //If we find that the opposite ring slot is equipped to us, this would be an item swap or the exact same ring, therefore no stat calculations apply.
|
||||
Inventory::EquipItem(buttonItem,slot);
|
||||
for(int counter=0;const CharacterMenuWindow::AttributeData&attribute:CharacterMenuWindow::displayAttrs){
|
||||
std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute.attrName).Name())+" Label");
|
||||
int statChangeAmt=attribute.calcFunc()-statsBeforeEquip[counter];
|
||||
statDisplayLabel.lock()->SetStatChangeAmt(statChangeAmt);
|
||||
counter++;
|
||||
}
|
||||
Inventory::UnequipItem(slot);
|
||||
if(!ISBLANK(equippedItem)){
|
||||
Inventory::EquipItem(equippedItem,slot);
|
||||
game->GetPlayer()->Heal(healthBeforeEquip-game->GetPlayer()->GetHealth(),true);
|
||||
game->GetPlayer()->RestoreMana(manaBeforeEquip-game->GetPlayer()->GetMana(),true);
|
||||
}
|
||||
if(!ISBLANK(otherItem)){
|
||||
if(slot&EquipSlot::RING1)Inventory::EquipItem(otherItem,EquipSlot::RING2);
|
||||
else
|
||||
if(slot&EquipSlot::RING2)Inventory::EquipItem(otherItem,EquipSlot::RING1);
|
||||
}
|
||||
}
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Name")->SetItem(buttonItem);
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Description")->SetItem(buttonItem);
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Name")->Enable();
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Description")->Enable();
|
||||
}else{
|
||||
ERR("WARNING! Attempting to cast a button that isn't a RowItemDisplay!");
|
||||
}
|
||||
return true;
|
||||
});
|
||||
equip->SetMouseOutFunc(
|
||||
[](MenuFuncData data){
|
||||
for(int counter=0;const CharacterMenuWindow::AttributeData&attribute:CharacterMenuWindow::displayAttrs){
|
||||
std::weak_ptr<StatLabel>statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute.attrName).Name())+" Label");
|
||||
statDisplayLabel.lock()->SetStatChangeAmt(0);
|
||||
counter++;
|
||||
}
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Name")->Disable();
|
||||
Component<MenuItemLabel>(data.menu.GetType(),"Item Description")->Disable();
|
||||
return true;
|
||||
});
|
||||
|
||||
equip->SetShowQuantity(false);
|
||||
equip->SetSelectionType(SelectionType::NONE);
|
||||
|
||||
equip->I(Attribute::EQUIP_TYPE)=int(slot);
|
||||
if(Inventory::GetEquip(slot)==it){
|
||||
equip->SetSelected(true);
|
||||
}
|
||||
equip->SetCompactDescriptions(NON_COMPACT);
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
equipList->I(Attribute::INDEXED_THEME)=data.component.lock()->I(Attribute::INDEXED_THEME);
|
||||
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Outline")->Enable();
|
||||
equipList->Enable();
|
||||
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Bottom Outline")->Enable();
|
||||
Component<MenuComponent>(data.component.lock()->parentMenu,"Equip Selection Select Button")->Enable();
|
||||
Component<CharacterRotatingDisplay>(data.component.lock()->parentMenu,"Character Rotating Display")->Disable();
|
||||
equipmentWindowOpened=true;
|
||||
|
||||
auto equipmentList=equipList->GetComponents();
|
||||
auto itemEquipped=std::find_if(equipmentList.begin(),equipmentList.end(),[&](std::weak_ptr<MenuComponent>&component){
|
||||
return !ISBLANK(Inventory::GetEquip(slot))&&component.lock()->GetSubcomponentParent().expired()&&&*DYNAMIC_POINTER_CAST<RowItemDisplay>(component)->GetItem().lock()==&*Inventory::GetEquip(slot).lock();
|
||||
});
|
||||
if(itemEquipped!=equipmentList.end()){
|
||||
data.menu.SetSelection(*itemEquipped,true,true);
|
||||
if(Menu::UsingMouseNavigation()){
|
||||
equipList->HandleOutsideDisabledButtonSelection(*itemEquipped);
|
||||
}
|
||||
data.menu.I(A::ITEM_SLOT)=equipList->GetComponentIndex(*itemEquipped);
|
||||
}else
|
||||
if(equipmentList.size()>0){
|
||||
data.menu.SetSelection(equipmentList[0],true,true);
|
||||
if(Menu::UsingMouseNavigation()){
|
||||
equipList->HandleOutsideDisabledButtonSelection(equipmentList[0]);
|
||||
}
|
||||
data.menu.I(A::ITEM_SLOT)=0;
|
||||
}else{
|
||||
data.menu.SetSelection("Equip Selection Select Button"sv);
|
||||
}
|
||||
|
||||
return true;
|
||||
},[](MenuFuncData data){//On Mouse Hover
|
||||
EquipSlot slot=DYNAMIC_POINTER_CAST<EquipSlotButton>(data.component.lock())->GetSlot();
|
||||
const std::weak_ptr<Item>equip=Inventory::GetEquip(slot);
|
||||
@ -377,17 +414,15 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
},[](MenuType type){
|
||||
if(!Menu::menus[type]->GetSelection().expired()&&
|
||||
Menu::menus[type]->GetSelection().lock()->GetName().starts_with("Equip Slot ")){
|
||||
if(CanModifyEquipSlots()){
|
||||
EquipSlot slot=EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE));
|
||||
if(!ISBLANK(Inventory::GetEquip(slot))){
|
||||
Inventory::UnequipItem(slot);
|
||||
if(slot&EquipSlot::RING1||slot&EquipSlot::RING2){
|
||||
SoundEffect::PlaySFX("Unequip Accessory",SoundEffect::CENTERED);
|
||||
}else{
|
||||
SoundEffect::PlaySFX("Unequip Armor",SoundEffect::CENTERED);
|
||||
}
|
||||
EquipSlot slot=EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE));
|
||||
if(!ISBLANK(Inventory::GetEquip(slot))){
|
||||
Inventory::UnequipItem(slot);
|
||||
if(slot&EquipSlot::RING1||slot&EquipSlot::RING2){
|
||||
SoundEffect::PlaySFX("Unequip Accessory",SoundEffect::CENTERED);
|
||||
}else{
|
||||
SoundEffect::PlaySFX("Unequip Armor",SoundEffect::CENTERED);
|
||||
}
|
||||
}else game->AddNotification(AiL::Notification{"Cannot change equipment in a stage!",5.f,YELLOW});
|
||||
}
|
||||
}
|
||||
}}},
|
||||
{{game->KEY_FACELEFT,Pressed},{[](MenuFuncData data){
|
||||
@ -402,17 +437,15 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
},[](MenuType type){
|
||||
if(!Menu::menus[type]->GetSelection().expired()&&
|
||||
Menu::menus[type]->GetSelection().lock()->GetName().starts_with("Equip Slot ")){
|
||||
if(CanModifyEquipSlots()){
|
||||
EquipSlot slot=EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE));
|
||||
if(!ISBLANK(Inventory::GetEquip(slot))){
|
||||
Inventory::UnequipItem(slot);
|
||||
if(slot&EquipSlot::RING1||slot&EquipSlot::RING2){
|
||||
SoundEffect::PlaySFX("Unequip Accessory",SoundEffect::CENTERED);
|
||||
}else{
|
||||
SoundEffect::PlaySFX("Unequip Armor",SoundEffect::CENTERED);
|
||||
}
|
||||
EquipSlot slot=EquipSlot(Menu::menus[type]->GetSelection().lock()->I(Attribute::EQUIP_TYPE));
|
||||
if(!ISBLANK(Inventory::GetEquip(slot))){
|
||||
Inventory::UnequipItem(slot);
|
||||
if(slot&EquipSlot::RING1||slot&EquipSlot::RING2){
|
||||
SoundEffect::PlaySFX("Unequip Accessory",SoundEffect::CENTERED);
|
||||
}else{
|
||||
SoundEffect::PlaySFX("Unequip Armor",SoundEffect::CENTERED);
|
||||
}
|
||||
}else game->AddNotification(AiL::Notification{"Cannot change equipment in a stage!",5.f,YELLOW});
|
||||
}
|
||||
}
|
||||
}}},
|
||||
{game->KEY_BACK,{"Back",[](MenuType type){
|
||||
@ -482,10 +515,28 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
,{ //Button Navigation Rules
|
||||
{"Equip List",{
|
||||
.up=[](MenuType type,Data&returnData){
|
||||
Menu::ScrollUp(type,"Equip List",returnData,"Equip Selection Select Button");
|
||||
if(!Menu::menus[type]->GetSelection().expired()){
|
||||
auto selection=Menu::menus[type]->GetSelection().lock();
|
||||
size_t index=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponentIndex(selection);
|
||||
index--;
|
||||
if(index>=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponents().size()){
|
||||
returnData="Equip Selection Select Button";
|
||||
}else{
|
||||
returnData=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponents()[index];
|
||||
}
|
||||
}
|
||||
},
|
||||
.down=[](MenuType type,Data&returnData){
|
||||
Menu::ScrollDown(type,"Equip List",returnData,"Equip Selection Select Button");
|
||||
if(!Menu::menus[type]->GetSelection().expired()){
|
||||
auto selection=Menu::menus[type]->GetSelection().lock();
|
||||
size_t index=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponentIndex(selection);
|
||||
index++;
|
||||
if(index>=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponents().size()){
|
||||
returnData="Equip Selection Select Button";
|
||||
}else{
|
||||
returnData=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponents()[index];
|
||||
}
|
||||
}
|
||||
},
|
||||
.left=[](MenuType type,Data&returnData){
|
||||
auto equipList=Component<ScrollableWindowComponent>(type,"Equip List")->GetComponents();
|
||||
|
@ -45,23 +45,16 @@ All rights reserved.
|
||||
INCLUDE_game
|
||||
|
||||
ChargedArrow::ChargedArrow(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
||||
:lastLaserPos(pos),laserGraphic("laser.png"),
|
||||
Bullet(pos,vel,radius,damage,"charged_shot_arrow.png",upperLevel,true,INFINITE,true,friendly,col){}
|
||||
|
||||
ChargedArrow::ChargedArrow(const std::string&shotArrowGraphic,const std::string&laserGraphic,vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
||||
:lastLaserPos(pos),laserGraphic(laserGraphic),
|
||||
Bullet(pos,vel,radius,damage,shotArrowGraphic,upperLevel,true,INFINITE,true,friendly,col){}
|
||||
:lastLaserPos(pos),
|
||||
Bullet(pos,vel,radius,damage,
|
||||
"charged_shot_arrow.png",upperLevel,true,INFINITE,true,friendly,col){}
|
||||
|
||||
void ChargedArrow::Update(float fElapsedTime){
|
||||
geom2d::line lineToCurrentPos(geom2d::line(lastLaserPos,pos));
|
||||
float dist=lineToCurrentPos.length();
|
||||
if(dist>=1){
|
||||
vf2d midpoint(lineToCurrentPos.rpoint(0.5));
|
||||
|
||||
const float normalBeamRadius{12*"Ranger.Ability 2.Radius"_F/100/5};
|
||||
float laserWidth{radius/normalBeamRadius};
|
||||
|
||||
game->AddEffect(std::make_unique<Effect>(midpoint,0.1f,laserGraphic,upperLevel,vf2d{laserWidth,dist*2},0.3f,vf2d{},Pixel{192,128,238},atan2(pos.y-lastLaserPos.y,pos.x-lastLaserPos.x)+PI/2,0,true));
|
||||
game->AddEffect(std::make_unique<Effect>(midpoint,0.1f,"laser.png",upperLevel,vf2d{1,dist},0.3f,vf2d{},Pixel{192,128,238},atan2(pos.y-lastLaserPos.y,pos.x-lastLaserPos.x)+PI/2,0,true));
|
||||
lastLaserPos=pos;
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,6 @@ enum Class{
|
||||
};
|
||||
|
||||
namespace classutils{//Classes have bit-wise operator capabilities.
|
||||
inline std::unordered_map<Class,std::string>classList;
|
||||
static inline Class StringToClass(std::string className){
|
||||
const std::vector<std::string>&classList=DATA["class_list"].GetValues();
|
||||
auto it=std::find(classList.begin(),classList.end(),className);
|
||||
@ -64,7 +63,4 @@ namespace classutils{//Classes have bit-wise operator capabilities.
|
||||
int element=int(std::distance(classList.begin(),it));
|
||||
return Class(1<<element); //Yes...It's bitwise flags, who in god's name knows why I did this.
|
||||
};
|
||||
static inline std::string ClassToString(const Class&cl){
|
||||
return classList.at(cl);
|
||||
}
|
||||
};
|
@ -50,7 +50,6 @@ All rights reserved.
|
||||
#endif
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_DEMO_BUILD
|
||||
using A=Attribute;
|
||||
|
||||
void Menu::InitializeClassSelectionWindow(){
|
||||
@ -169,12 +168,6 @@ void Menu::InitializeClassSelectionWindow(){
|
||||
|
||||
classSprite->S(A::CLASS_SELECTION)=className;
|
||||
|
||||
if(DEMO_BUILD&&i>2){ //Disable the three later classes in the demo build.
|
||||
classLabel->SetGrayedOut(true);
|
||||
classButton->SetGrayedOut(true);
|
||||
classSprite->SetGrayedOut(true);
|
||||
}
|
||||
|
||||
toggleGroup.push_back(classSprite);
|
||||
}
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
#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 "AdventuresInLestoria.h"
|
||||
#include "DEFINES.h"
|
||||
#include "util.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
CollectCoinEffect::CollectCoinEffect(const Entity attachedTarget,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:Effect(attachedTarget.GetPos(),lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending),attachedTarget(attachedTarget),sizeFlipper(-1.f,1.f,lifetime*2.f),zRiser(0.f,coinArcRiseZAmt,1/lifetime){
|
||||
}
|
||||
bool CollectCoinEffect::Update(float fElapsedTime){
|
||||
zRiser.Update(fElapsedTime);
|
||||
pos=attachedTarget.GetPos()-vf2d{0,zRiser.get()}; //A hack, instead of using the built-in z (which would display a shadow, we instead manipulate the final position based on the zRiser output).
|
||||
size.x=sizeFlipper.Update(fElapsedTime);
|
||||
return Effect::Update(fElapsedTime);
|
||||
}
|
||||
void CollectCoinEffect::Draw(const Pixel blendCol)const{
|
||||
Effect::Draw(blendCol);
|
||||
}
|
@ -55,6 +55,6 @@ void ConnectionPoint::ResetVisitedFlag(){
|
||||
visited=false;
|
||||
}
|
||||
|
||||
[[nodiscard]]const bool ConnectionPoint::Visited()const{
|
||||
const bool ConnectionPoint::Visited()const{
|
||||
return visited;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
To open the admin console, press F12 in-game. The admin console is turned on via the ADMIN_MODE variable located in AdventuresInLestoria.cpp
|
||||
|
||||
Required arguments are in <>. Optional arguments are in [].
|
||||
|
||||
Items with spaces must be surrounded in quotation marks "".
|
||||
|
||||
Valid Commands are:
|
||||
/help - Shows this help file.
|
||||
/give <Item Name> [Amount] - Gives an item of type <Item Name> into the player's inventory. Optionally adds [Amount] amount of items to the player's inventory.
|
||||
/accessory <Accessory Name> [Enchant Name] - Gives an accessory of type <Accessory Name> into the player's inventory. Optionally adds an enchant of type [Enchant Name].
|
@ -133,7 +133,7 @@ void Menu::InitializeConsumableCraftingWindow(){
|
||||
auto moneyIcon=consumableCraftingWindow->ADD("Money Icon",MenuIconButton)(geom2d::rect<float>{moneyIconPos,{24,24}},GFX["money.png"].Decal(),DO_NOTHING,IconButtonAttr::NOT_SELECTABLE|IconButtonAttr::NO_OUTLINE|IconButtonAttr::NO_BACKGROUND)END;
|
||||
std::string moneyText=std::to_string(game->GetPlayer()->GetMoney());
|
||||
vf2d moneyTextSize=game->GetTextSizeProp(moneyText)*2;
|
||||
auto moneyDisplay=consumableCraftingWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,1.85f,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
|
||||
auto moneyDisplay=consumableCraftingWindow->ADD("Money Label",PlayerMoneyLabel)(geom2d::rect<float>{moneyIconPos-vf2d{2+moneyTextSize.x,-2},moneyTextSize},2,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN|ComponentAttr::FIT_TO_LABEL)END;
|
||||
moneyDisplay->SetRightAlignment(true);
|
||||
Player::AddMoneyListener(moneyDisplay);
|
||||
#pragma endregion
|
||||
|
@ -1,118 +0,0 @@
|
||||
#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"
|
||||
#include "BulletTypes.h"
|
||||
|
||||
using A=Attribute;
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Monster::STRATEGY::CRAB(Monster&m,float fElapsedTime,std::string strategy){
|
||||
enum PhaseName{
|
||||
INIT,
|
||||
MOVE,
|
||||
PREPARE_CHARGE,
|
||||
CHARGE,
|
||||
};
|
||||
|
||||
switch(PHASE()){
|
||||
case INIT:{
|
||||
m.B(A::RANDOM_DIRECTION)=util::random()%2;
|
||||
m.F(A::RANDOM_RANGE)=util::random_range(ConfigPixelsArr("Random Direction Range",0),ConfigPixelsArr("Random Direction Range",1));
|
||||
SETPHASE(MOVE);
|
||||
}break;
|
||||
case MOVE:{
|
||||
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
|
||||
m.F(A::LAST_JUMP_TIMER)+=fElapsedTime;
|
||||
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
|
||||
const bool outsideMaxShootingRange=distToPlayer>=ConfigPixelsArr("Stand Still and Shoot Range",1);
|
||||
|
||||
if(m.F(A::LAST_JUMP_TIMER)>=ConfigFloat("Stop Check Interval")){
|
||||
if(util::random(100)<=ConfigFloat("Stop Percent")){
|
||||
SETPHASE(PREPARE_CHARGE);
|
||||
m.PerformAnimation("CHARGEUP",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Charge Wait Time");
|
||||
m.target=game->GetPlayer()->GetPos()+util::distance(m.GetPos(),game->GetPlayer()->GetPos())*util::pointTo(m.GetPos(),game->GetPlayer()->GetPos());
|
||||
}else
|
||||
if(util::random(100)<=ConfigFloat("Change Direction Chance"))m.B(A::RANDOM_DIRECTION)=!m.B(A::RANDOM_DIRECTION);
|
||||
m.F(A::LAST_JUMP_TIMER)=0.f;
|
||||
}else
|
||||
if(outsideMaxShootingRange){
|
||||
m.target=game->GetPlayer()->GetPos();
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}else
|
||||
if(distToPlayer<ConfigPixels("Run Away Range")){
|
||||
m.target=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).upoint(-1);
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}else
|
||||
if(distToPlayer>=ConfigPixelsArr("Random Direction Range",0)&&distToPlayer<ConfigPixelsArr("Random Direction Range",1)){
|
||||
#define CW true
|
||||
#define CCW false
|
||||
//We are going to walk in a circular direction either CW or CCW (determined in windup phase)
|
||||
float dirFromPlayer=util::angleTo(game->GetPlayer()->GetPos(),m.GetPos());
|
||||
float targetDir=m.B(A::RANDOM_DIRECTION)==CW?dirFromPlayer+PI/4:dirFromPlayer-PI/4;
|
||||
m.target=game->GetPlayer()->GetPos()+vf2d{m.F(A::RANDOM_RANGE),targetDir}.cart();
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
m.F(A::CHASE_TIMER)=1.f;
|
||||
}
|
||||
}break;
|
||||
case PREPARE_CHARGE:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
SETPHASE(CHARGE);
|
||||
m.F(A::CHASE_TIMER)=ConfigFloat("Charge Max Time");
|
||||
m.PerformAnimation("PINCER");
|
||||
}
|
||||
}break;
|
||||
case CHARGE:{
|
||||
m.F(A::CHASE_TIMER)-=fElapsedTime;
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
if(m.F(A::CHASE_TIMER)<=0.f||m.ReachedTargetPos()){
|
||||
SETPHASE(MOVE);
|
||||
m.B(A::RANDOM_DIRECTION)=util::random()%2;
|
||||
m.F(A::RANDOM_RANGE)=util::random_range(ConfigPixelsArr("Random Direction Range",0),ConfigPixelsArr("Random Direction Range",1));
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ void Menu::InitializeCraftItemWindow(){
|
||||
for(const auto&[name,amt]:consumedResources.GetItems()){
|
||||
Inventory::RemoveItem(Inventory::GetItem(name)[0],amt);
|
||||
}
|
||||
game->GetPlayer()->RemoveMoney(consumedResources.GetCost());
|
||||
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-consumedResources.GetCost());
|
||||
|
||||
Inventory::UpdateBlacksmithInventoryLists();
|
||||
SoundEffect::PlaySFX("Craft Equip",SoundEffect::CENTERED);
|
||||
|
@ -36,7 +36,7 @@ Artificer has 4 Dialog Options
|
||||
|
||||
|
||||
|
||||
Refining Gear
|
||||
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
|
||||
|
@ -61,9 +61,6 @@ using MonsterSpawnerID=int;
|
||||
#define DO_NOTHING [](MenuFuncData data){return true;}
|
||||
#define INCLUDE_WINDOW_SIZE extern vi2d WINDOW_SIZE;
|
||||
#define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS;
|
||||
#define INCLUDE_ADMIN_MODE extern bool ADMIN_MODE;
|
||||
#define INCLUDE_DEMO_BUILD extern bool DEMO_BUILD;
|
||||
#define INCLUDE_ITEM_SCRIPTS extern safemap<std::string,ItemScript>ITEM_SCRIPTS;
|
||||
|
||||
#define INCLUDE_PACK_KEY extern std::string PACK_KEY;
|
||||
|
||||
@ -75,68 +72,22 @@ using MonsterSpawnerID=int;
|
||||
|
||||
#define ACCESS_PLAYER Player*p=game->GetPlayer();
|
||||
|
||||
#define INCLUDE_INITIALIZEGAMECONFIGURATIONS extern void InitializeGameConfigurations();
|
||||
|
||||
#define VARIANTS float,int,std::string,bool,vf2d,std::vector<std::any>,size_t
|
||||
#undef INFINITE
|
||||
#define INFINITE 999999
|
||||
|
||||
#define MAX_BULLETS size_t(512)
|
||||
|
||||
#define SETUP_CLASS(class) \
|
||||
class::class() \
|
||||
:Player::Player(){} \
|
||||
class::class(Player*player) \
|
||||
:Player::Player(player){} \
|
||||
Class class::GetClass(){return cl;} \
|
||||
void class::CreateOriginalCopies(){ \
|
||||
original_rightClickAbility=rightClickAbility; \
|
||||
original_ability1=ability1; \
|
||||
original_ability2=ability2; \
|
||||
original_ability3=ability3; \
|
||||
original_ability4=ability4; \
|
||||
original_rightClickAbility.isOriginalAbility=true; \
|
||||
original_ability1.isOriginalAbility=true; \
|
||||
original_ability2.isOriginalAbility=true; \
|
||||
original_ability3.isOriginalAbility=true; \
|
||||
original_ability4.isOriginalAbility=true; \
|
||||
} \
|
||||
void class::ResetToOriginalAbilities(){ \
|
||||
rightClickAbility.COOLDOWN_TIME=original_rightClickAbility.COOLDOWN_TIME; \
|
||||
ability1.COOLDOWN_TIME=original_ability1.COOLDOWN_TIME; \
|
||||
ability2.COOLDOWN_TIME=original_ability2.COOLDOWN_TIME; \
|
||||
ability3.COOLDOWN_TIME=original_ability3.COOLDOWN_TIME; \
|
||||
ability4.COOLDOWN_TIME=original_ability4.COOLDOWN_TIME; \
|
||||
rightClickAbility.MAX_CHARGES=original_rightClickAbility.MAX_CHARGES; \
|
||||
ability1.MAX_CHARGES=original_ability1.MAX_CHARGES; \
|
||||
ability2.MAX_CHARGES=original_ability2.MAX_CHARGES; \
|
||||
ability3.MAX_CHARGES=original_ability3.MAX_CHARGES; \
|
||||
ability4.MAX_CHARGES=original_ability4.MAX_CHARGES; \
|
||||
rightClickAbility.precastInfo=original_rightClickAbility.precastInfo; \
|
||||
ability1.precastInfo=original_ability1.precastInfo; \
|
||||
ability2.precastInfo=original_ability2.precastInfo; \
|
||||
ability3.precastInfo=original_ability3.precastInfo; \
|
||||
ability4.precastInfo=original_ability4.precastInfo; \
|
||||
} \
|
||||
const std::string&class::GetClassName(){return name;} \
|
||||
Ability&class::GetRightClickAbility(){return rightClickAbility;}; \
|
||||
Ability&class::GetAbility1(){return ability1;}; \
|
||||
Ability&class::GetAbility2(){return ability2;}; \
|
||||
Ability&class::GetAbility3(){return ability3;}; \
|
||||
Ability&class::GetAbility4(){return ability4;}; \
|
||||
Ability&class::_GetOriginalAbility1(){return GetOriginalAbility1();}; \
|
||||
Ability&class::_GetOriginalAbility2(){return GetOriginalAbility2();}; \
|
||||
Ability&class::_GetOriginalAbility3(){return GetOriginalAbility2();}; \
|
||||
Ability&class::_GetOriginalRightClickAbility(){return GetOriginalRightClickAbility();}; \
|
||||
Ability&class::GetOriginalAbility1(){return original_ability1;}; \
|
||||
Ability&class::GetOriginalAbility2(){return original_ability2;}; \
|
||||
Ability&class::GetOriginalAbility3(){return original_ability3;}; \
|
||||
Ability&class::GetOriginalRightClickAbility(){return original_rightClickAbility;}; \
|
||||
void class::SetAbility4(const Ability&originalAbility){ \
|
||||
auto it=std::find(Player::ABILITY_LIST.begin(),Player::ABILITY_LIST.end(),originalAbility); \
|
||||
if(it==Player::ABILITY_LIST.end())ERR(std::format("WARNING! Could not find ability {} in original ability list! This function MUST be supplied with an original ability. Was original ability: {}",originalAbility.name,originalAbility.isOriginalAbility)); \
|
||||
class::original_ability4=class::ability4=originalAbility; \
|
||||
} \
|
||||
std::string&class::GetWalkNAnimation(){return walk_n;}; \
|
||||
std::string&class::GetWalkEAnimation(){return walk_e;}; \
|
||||
std::string&class::GetWalkSAnimation(){return walk_s;}; \
|
||||
@ -152,11 +103,6 @@ Ability class::ability1=Ability(); \
|
||||
Ability class::ability2=Ability(); \
|
||||
Ability class::ability3=Ability(); \
|
||||
Ability class::ability4=Ability(); \
|
||||
Ability class::original_rightClickAbility=Ability(); \
|
||||
Ability class::original_ability1=Ability(); \
|
||||
Ability class::original_ability2=Ability(); \
|
||||
Ability class::original_ability3=Ability(); \
|
||||
Ability class::original_ability4=Ability(); \
|
||||
std::string class::idle_n="WARRIOR_IDLE_N"; \
|
||||
std::string class::idle_e="WARRIOR_IDLE_E"; \
|
||||
std::string class::idle_s="WARRIOR_IDLE_S"; \
|
||||
|
@ -45,8 +45,8 @@ All rights reserved.
|
||||
INCLUDE_game
|
||||
INCLUDE_ANIMATION_DATA
|
||||
|
||||
DaggerSlash::DaggerSlash(Monster&sourceMonster,const std::string&image,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,image,upperLevel,false,daggerFrameDuration*ANIMATION_DATA["goblin_sword_slash.png"].GetFrameCountBasedOnAnimationStyle(),true,friendly,col),
|
||||
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);
|
||||
|
@ -45,8 +45,8 @@ All rights reserved.
|
||||
INCLUDE_game
|
||||
INCLUDE_ANIMATION_DATA
|
||||
|
||||
DaggerStab::DaggerStab(Monster&sourceMonster,const std::string&image,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,image,upperLevel,false,daggerFrameDuration*ANIMATION_DATA["dagger_stab.png"].GetFrameCountBasedOnAnimationStyle(),true,friendly,col),
|
||||
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);
|
||||
|
@ -44,8 +44,13 @@ const float DamageNumber::MOVE_UP_TIME=0.4f;
|
||||
|
||||
using enum DamageNumberType::DamageNumberType;
|
||||
|
||||
DamageNumber::DamageNumber()
|
||||
:damage(0){
|
||||
}
|
||||
|
||||
DamageNumber::DamageNumber(vf2d pos,int damage,bool friendly,DamageNumberType::DamageNumberType type):
|
||||
pos(pos),damage(damage),friendly(friendly),type(type),invertedDirection(type==INTERRUPT),riseSpd(GetOriginalRiseSpd()){
|
||||
pos(pos),damage(damage),friendly(friendly),type(type),invertedDirection(type==INTERRUPT),riseSpd(20.f){
|
||||
if(type==INTERRUPT||type==MANA_GAIN)riseSpd=40.f;
|
||||
RecalculateSize();
|
||||
}
|
||||
|
||||
@ -54,35 +59,17 @@ void DamageNumber::RecalculateSize(){
|
||||
if(!friendly){
|
||||
float newSize=std::clamp(roundf(damageMultRatio),1.0f,4.0f);
|
||||
|
||||
float riseSpd{GetOriginalRiseSpd()};
|
||||
if(type==HEALTH_LOSS||type==CRIT||type==DOT)riseSpd*=newSize;
|
||||
if(type==HEALTH_LOSS||type==CRIT)riseSpd*=newSize;
|
||||
|
||||
size=vf2d{newSize,newSize};
|
||||
}
|
||||
}
|
||||
|
||||
void DamageNumber::Update(){
|
||||
if(pauseTime>0){
|
||||
pauseTime-=game->GetElapsedTime();
|
||||
} else{
|
||||
lifetime+=game->GetElapsedTime();
|
||||
if(lifetime<=1){
|
||||
if(lifetime<DamageNumber::MOVE_UP_TIME){
|
||||
if(invertedDirection){
|
||||
pos.y+=riseSpd*game->GetElapsedTime();
|
||||
}else{
|
||||
pos.y-=riseSpd*game->GetElapsedTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DamageNumber::Draw(){
|
||||
#define NumberScalesWithDamage true
|
||||
#define NormalNumber false
|
||||
|
||||
auto DrawDamageNumber=[&](const bool ScaleWithNumber,std::string_view text,std::pair<Pixel,Pixel>colorWhenAppliedToMonster,std::pair<Pixel,Pixel>colorWhenAppliedToPlayer,vf2d scaling={1.f,1.f}){
|
||||
auto DrawDamageNumber=[&](const bool ScaleWithNumber,std::string_view text,std::pair<Pixel,Pixel>colorsEnemy,std::pair<Pixel,Pixel>colorsFriendly,vf2d scaling={1.f,1.f}){
|
||||
vf2d textSize=game->GetTextSizeProp(text)*scaling;
|
||||
if(!friendly){
|
||||
vf2d additionalScaling={1.f,1.f};
|
||||
@ -94,13 +81,13 @@ void DamageNumber::Draw(){
|
||||
drawPos.x=std::clamp(drawPos.x,game->view.GetWorldTL().x,game->view.GetWorldBR().x-textSize.x);
|
||||
drawPos.y=std::clamp(drawPos.y,game->view.GetWorldTL().y,game->view.GetWorldBR().y-textSize.y);
|
||||
|
||||
game->view.DrawShadowStringPropDecal(drawPos,text,colorWhenAppliedToMonster.first,colorWhenAppliedToMonster.second,scaling*additionalScaling);
|
||||
game->view.DrawShadowStringPropDecal(drawPos,text,colorsEnemy.first,colorsEnemy.second,scaling*additionalScaling);
|
||||
}else{
|
||||
vf2d drawPos=pos-textSize/2.f;
|
||||
drawPos.x=std::clamp(drawPos.x,game->view.GetWorldTL().x,game->view.GetWorldBR().x-textSize.x);
|
||||
drawPos.y=std::clamp(drawPos.y,game->view.GetWorldTL().y,game->view.GetWorldBR().y-textSize.y);
|
||||
|
||||
game->view.DrawShadowStringPropDecal(drawPos,text,colorWhenAppliedToPlayer.first,colorWhenAppliedToPlayer.second,scaling);
|
||||
game->view.DrawShadowStringPropDecal(drawPos,text,colorsFriendly.first,colorsFriendly.second,scaling);
|
||||
}
|
||||
};
|
||||
|
||||
@ -127,54 +114,8 @@ void DamageNumber::Draw(){
|
||||
}break;
|
||||
case DOT:{
|
||||
std::string text=std::to_string(damage);
|
||||
DrawDamageNumber(NormalNumber,text,std::pair<Pixel,Pixel>{0xE1BEE7,0x1F083A},std::pair<Pixel,Pixel>{0xDCE775,0x37320A});
|
||||
}break;
|
||||
case BACKSTAB:{
|
||||
std::string text=std::to_string(damage);
|
||||
DrawDamageNumber(NumberScalesWithDamage,text,std::pair<Pixel,Pixel>{0x888093,0x150035},{BLACK,{0,0,0,0}});
|
||||
}break;
|
||||
case SHIELD_LOSS:{
|
||||
std::string text=std::to_string(damage);
|
||||
DrawDamageNumber(NumberScalesWithDamage,text,std::pair<Pixel,Pixel>{DARK_BLUE,0x68d7ef},std::pair<Pixel,Pixel>{BLUE,0x4141be});
|
||||
DrawDamageNumber(NormalNumber,text,{0xE1BEE7,0x1F083A},{0xDCE775,0x37320A});
|
||||
}break;
|
||||
default:ERR(std::format("Damage Number Type {} is not implemented! THIS SHOULD NOT BE HAPPENING!",int(type)));
|
||||
}
|
||||
}
|
||||
|
||||
float DamageNumber::GetOriginalRiseSpd(){
|
||||
float riseSpd{20.f};
|
||||
switch(type){
|
||||
case INTERRUPT:
|
||||
case HEALTH_GAIN:{
|
||||
riseSpd=40.f;
|
||||
}break;
|
||||
case MANA_GAIN:{
|
||||
riseSpd=-20.f;
|
||||
}break;
|
||||
case DOT:{
|
||||
riseSpd=-10.f;
|
||||
}break;
|
||||
}
|
||||
return riseSpd;
|
||||
}
|
||||
|
||||
void DamageNumber::AddDamage(int damageAmt){
|
||||
if(damageAmt<0)ERR(std::format("WARNING! Trying to apply a negative damage amount to a damage counter: {}! THIS SHOULD NOT BE HAPPENING!",damageAmt));
|
||||
damage+=damageAmt;
|
||||
}
|
||||
|
||||
const bool DamageNumber::IsDead()const{
|
||||
return lifetime>1.f;
|
||||
}
|
||||
|
||||
void DamageNumber::SetPauseTimer(const float pauseTime){
|
||||
this->pauseTime=pauseTime;
|
||||
}
|
||||
|
||||
void DamageNumber::SetType(const DamageNumberType::DamageNumberType type){
|
||||
this->type=type;
|
||||
}
|
||||
|
||||
const DamageNumberType::DamageNumberType DamageNumber::GetType()const{
|
||||
return type;
|
||||
}
|
@ -46,32 +46,23 @@ namespace DamageNumberType{
|
||||
INTERRUPT,
|
||||
CRIT,
|
||||
DOT,
|
||||
BACKSTAB,
|
||||
SHIELD_LOSS,
|
||||
};
|
||||
}
|
||||
|
||||
struct DamageNumber{
|
||||
//The friendly flag indicates if the number was for a friendly/player target or if it's for a monster target (set to false)
|
||||
DamageNumber(vf2d pos,int damage,bool friendly=false,DamageNumberType::DamageNumberType type=DamageNumberType::HEALTH_LOSS);
|
||||
void Update();
|
||||
void Draw();
|
||||
void AddDamage(int damageAmt);
|
||||
const bool IsDead()const;
|
||||
void SetPauseTimer(const float pauseTime);
|
||||
void SetType(const DamageNumberType::DamageNumberType type);
|
||||
const DamageNumberType::DamageNumberType GetType()const;
|
||||
private:
|
||||
vf2d pos;
|
||||
int damage;
|
||||
float lifetime=0;
|
||||
float lifeTime=0;
|
||||
float pauseTime=0;
|
||||
DamageNumberType::DamageNumberType type=DamageNumberType::HEALTH_LOSS;
|
||||
float riseSpd=0.f;
|
||||
vf2d size{1.f,1.f};
|
||||
bool friendly=false;
|
||||
bool invertedDirection=false;
|
||||
DamageNumberType::DamageNumberType type=DamageNumberType::HEALTH_LOSS;
|
||||
const static float MOVE_UP_TIME;
|
||||
float GetOriginalRiseSpd();
|
||||
DamageNumber();
|
||||
//The friendly flag indicates if the number was for a friendly/player target or if it's for a monster target (set to false)
|
||||
DamageNumber(vf2d pos,int damage,bool friendly=false,DamageNumberType::DamageNumberType type=DamageNumberType::HEALTH_LOSS);
|
||||
void RecalculateSize();
|
||||
void Draw();
|
||||
};
|
@ -79,7 +79,7 @@ void DeadlyDash::Draw(const Pixel blendCol)const{
|
||||
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,float(util::lerp(255,0,(GetAliveTime()-fadeTimeBegins)/afterImagesLingeringTime)));
|
||||
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};
|
||||
|
||||
|
@ -1,57 +0,0 @@
|
||||
#pragma region License
|
||||
/*
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce the above
|
||||
copyright notice. This list of conditions and the following disclaimer must be
|
||||
reproduced in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Portions of this software are copyright © 2024 The FreeType
|
||||
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||
All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
#pragma once
|
||||
|
||||
#include "MenuLabel.h"
|
||||
|
||||
//A class holding a lambda function where you return an updated menu label that is declared on creation to reduce boilerplate for menu labels that just need to update text differently..
|
||||
class DynamicMenuLabel:public MenuLabel{
|
||||
public:
|
||||
inline DynamicMenuLabel(geom2d::rect<float>rect,std::function<const std::string()>labelUpdateFunc,float scale=1,ComponentAttr attributes=ComponentAttr::NONE)
|
||||
:labelUpdateFunc(labelUpdateFunc),MenuLabel(rect,"",scale,attributes){}
|
||||
inline void SetLabelUpdateFunction(std::function<const std::string()>labelUpdateFunc){
|
||||
this->labelUpdateFunc=labelUpdateFunc;
|
||||
}
|
||||
protected:
|
||||
inline virtual void Update(AiL*game)override{
|
||||
SetLabel(labelUpdateFunc());
|
||||
MenuLabel::Update(game);
|
||||
}
|
||||
private:
|
||||
std::function<const std::string()>labelUpdateFunc;
|
||||
};
|
@ -42,23 +42,18 @@ All rights reserved.
|
||||
|
||||
INCLUDE_ANIMATION_DATA
|
||||
INCLUDE_game
|
||||
INCLUDE_GFX
|
||||
|
||||
//Note: The fadeout time activates when the original lifetime of the bullet expires! It doesn't begin immediately.
|
||||
Effect::Effect(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:Effect(pos,lifetime,imgFile,upperLevel,0.f,fadeout,vf2d{size,size},spd,col,rotation,rotationSpd,additiveBlending){}
|
||||
Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:Effect::Effect(pos,lifetime,imgFile,upperLevel,0.f,fadeout,vf2d{size,size},spd,col,rotation,rotationSpd,additiveBlending){}
|
||||
|
||||
//Note: The fadeout time activates when the original lifetime of the bullet expires! It doesn't begin immediately.
|
||||
Effect::Effect(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:Effect(pos,lifetime,imgFile,upperLevel,0.f,fadeout,size,spd,col,rotation,rotationSpd,additiveBlending){}
|
||||
Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadeout(fadeout),original_fadeOutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending){
|
||||
this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile));
|
||||
this->animation.ChangeState(internal_animState,imgFile);
|
||||
}
|
||||
|
||||
//Note: The fadeout time activates when the original lifetime of the bullet expires! It doesn't begin immediately.
|
||||
Effect::Effect(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:Effect(pos,lifetime,imgFile,upperLevel,fadein,fadeout,size,spd,EffectType::NONE,col,rotation,rotationSpd,additiveBlending){}
|
||||
|
||||
//Note: The fadeout time activates when the original lifetime of the bullet expires! It doesn't begin immediately.
|
||||
Effect::Effect(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,EffectType type,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadein(fadein),original_fadeInTime(fadein),fadeout(fadeout),original_fadeOutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending),type(type){
|
||||
Effect::Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadein(fadein),original_fadeInTime(fadein),fadeout(fadeout),original_fadeOutTime(fadeout),spd(spd),col(col),rotation(rotation),rotationSpd(rotationSpd),additiveBlending(additiveBlending){
|
||||
this->animation.AddState(imgFile,ANIMATION_DATA.at(imgFile));
|
||||
this->animation.ChangeState(internal_animState,imgFile);
|
||||
}
|
||||
@ -84,36 +79,22 @@ bool Effect::Update(float fElapsedTime){
|
||||
return true;
|
||||
}
|
||||
|
||||
void Effect::_Draw()const{
|
||||
void Effect::Draw()const{
|
||||
if(additiveBlending)game->SetDecalMode(DecalMode::ADDITIVE);
|
||||
const bool FadeInFinished{original_fadeInTime==0||fadein==original_fadeInTime};
|
||||
const bool HasFadeout{fadeout>0};
|
||||
|
||||
if(GetZ()>0){
|
||||
vf2d shadowScale=vf2d{8*size.x/3.f,1}/std::max(1.f,GetZ()/8);
|
||||
game->view.DrawDecal(pos-vf2d{3,3}*shadowScale/2+vf2d{0,12*size.y},GFX["circle.png"].Decal(),shadowScale,BLACK);
|
||||
}
|
||||
|
||||
Pixel blendCol{col};
|
||||
[[unlikely]]if(!FadeInFinished){
|
||||
blendCol={col.r,col.g,col.b,uint8_t(fadein/original_fadeInTime*col.a)};
|
||||
game->view.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(fadein/original_fadeInTime*col.a)});
|
||||
}else
|
||||
[[likely]]if(HasFadeout){
|
||||
blendCol={col.r,col.g,col.b,uint8_t(fadeout/original_fadeOutTime*col.a)};
|
||||
game->view.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*col.a)});
|
||||
}else{
|
||||
game->view.DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,col);
|
||||
}
|
||||
Draw(blendCol);
|
||||
|
||||
game->SetDecalMode(DecalMode::NORMAL);
|
||||
}
|
||||
|
||||
void Effect::Draw(const Pixel blendCol)const{
|
||||
if(GetZ()>0){
|
||||
vf2d shadowScale=vf2d{8*size.x/3.f,1}/std::max(1.f,GetZ()/24);
|
||||
game->view.DrawDecal(pos-vf2d{3,3}*shadowScale/2+vf2d{0,6*size.x},GFX["circle.png"].Decal(),shadowScale,BLACK);
|
||||
}
|
||||
game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()},GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,blendCol);
|
||||
}
|
||||
|
||||
Animate2D::Frame Effect::GetFrame()const{
|
||||
return animation.GetFrame(internal_animState);
|
||||
}
|
||||
@ -124,16 +105,4 @@ bool Effect::OnUpperLevel(){
|
||||
|
||||
const EffectType Effect::GetType()const{
|
||||
return type;
|
||||
}
|
||||
|
||||
const float Effect::GetZ()const{
|
||||
return z;
|
||||
}
|
||||
|
||||
void Effect::SetType(const EffectType type){
|
||||
this->type=type;
|
||||
}
|
||||
|
||||
const bool&Effect::OnUpperLevel()const{
|
||||
return upperLevel;
|
||||
}
|
@ -37,11 +37,8 @@ All rights reserved.
|
||||
#pragma endregion
|
||||
#pragma once
|
||||
#include "Animation.h"
|
||||
#include "IBullet.h"
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
#include "Oscillator.h"
|
||||
#include "Entity.h"
|
||||
class Monster;
|
||||
class Player;
|
||||
using HitList=std::unordered_set<std::variant<Monster*,Player*>>;
|
||||
@ -49,14 +46,11 @@ using HitList=std::unordered_set<std::variant<Monster*,Player*>>;
|
||||
enum class EffectType{
|
||||
NONE,
|
||||
SPELL_CIRCLE,
|
||||
MONSTER_SOUL,
|
||||
BLINK_PORTAL,
|
||||
TRAIL_OF_FIRE,
|
||||
};
|
||||
|
||||
struct Effect{
|
||||
friend class AiL;
|
||||
friend struct FallingBullet;
|
||||
friend struct FallingStone;
|
||||
vf2d pos={0,0};
|
||||
float lifetime=0;
|
||||
float fadeout=0;
|
||||
@ -68,66 +62,43 @@ struct Effect{
|
||||
float rotationSpd=0;
|
||||
vf2d scaleSpd{};
|
||||
bool additiveBlending=false;
|
||||
float z{};
|
||||
private:
|
||||
bool dead=false;
|
||||
public:
|
||||
Effect(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
Effect(vf2d pos,float lifetime,const std::string&imgFile,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(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
Effect(vf2d pos,float lifetime,const std::string&imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,EffectType type,Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
Effect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
Effect(vf2d pos,float lifetime,std::string imgFile,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(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float fadein,float fadeout,vf2d size,vf2d spd,Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
virtual bool Update(float fElapsedTime);
|
||||
Animate2D::Frame GetFrame()const;
|
||||
void _Draw()const;
|
||||
virtual void Draw(const Pixel blendCol)const;
|
||||
virtual void Draw()const;
|
||||
bool OnUpperLevel();
|
||||
const EffectType GetType()const;
|
||||
const float GetZ()const;
|
||||
const bool&OnUpperLevel()const;
|
||||
void SetType(const EffectType type);
|
||||
protected:
|
||||
float original_fadeOutTime;
|
||||
float original_fadeInTime{};
|
||||
EffectType type{EffectType::NONE};
|
||||
private:
|
||||
Animate2D::Animation<std::string>animation;
|
||||
Animate2D::AnimationState internal_animState;
|
||||
private:
|
||||
bool upperLevel=false;
|
||||
double aliveTime{};
|
||||
};
|
||||
|
||||
struct Meteor:Effect{
|
||||
enum MeteorSetting{
|
||||
METEOR,
|
||||
COMET,
|
||||
SOLAR_FLARE,
|
||||
COMET_FLARE,
|
||||
};
|
||||
Meteor(const vf2d pos,const float lifetime,const bool upperLevel,const MeteorSetting setting,const float fadeout=0.0f,const vf2d spd={},const Pixel col=WHITE,const float rotation=0,const float rotationSpd=0,const bool additiveBlending=false);
|
||||
Meteor(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
float startLifetime=0;
|
||||
bool shakeField=false;
|
||||
bool Update(float fElapsedTime)override;
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
private:
|
||||
int meteorImpactParticles{};
|
||||
float randomColorTintR{};
|
||||
float randomColorTintG{};
|
||||
float randomColorTintB{};
|
||||
float meteorRadius{};
|
||||
float fallSpdMult{1.f};
|
||||
float damageMult{1.f};
|
||||
float fireRingLifetime{};
|
||||
std::string meteorCrashSFX{};
|
||||
void Draw()const override;
|
||||
};
|
||||
|
||||
struct PulsatingFire:Effect{
|
||||
PulsatingFire(vf2d pos,float lifetime,const float radius,std::string imgFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
PulsatingFire(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
std::vector<float>pulsatingFireValues;
|
||||
float lastParticleTimer=0;
|
||||
float lastDamageTimer=0;
|
||||
float radius;
|
||||
bool Update(float fElapsedTime)override;
|
||||
void Draw(const Pixel blendCol)const override;
|
||||
void Draw()const override;
|
||||
};
|
||||
|
||||
struct SwordSlash:Effect{
|
||||
@ -143,7 +114,7 @@ private:
|
||||
struct ForegroundEffect:Effect{
|
||||
ForegroundEffect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
ForegroundEffect(vf2d pos,float lifetime,std::string imgFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
virtual void Draw(const Pixel blendCol)const override final;
|
||||
virtual void Draw()const override final;
|
||||
};
|
||||
|
||||
struct SpellCircle:Effect{
|
||||
@ -151,7 +122,7 @@ struct SpellCircle:Effect{
|
||||
SpellCircle(vf2d pos,float lifetime,std::string imgFile,std::string spellInsigniaFile,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false,vf2d insigniaSize={1,1},float insigniaFadeout=0.0f,vf2d insigniaSpd={},Pixel insigniaCol=WHITE,float insigniaRotation=0,float insigniaRotationSpd=0,bool insigniaAdditiveBlending=false);
|
||||
Effect spellInsignia{vf2d{},0.f,"spell_insignia.png",false,{}};
|
||||
virtual bool Update(float fElapsedTime)override final;
|
||||
virtual void Draw(const Pixel blendCol)const override final;
|
||||
virtual void Draw()const override final;
|
||||
};
|
||||
|
||||
struct RockLaunch:Effect{
|
||||
@ -170,79 +141,4 @@ struct ShineEffect:Effect{
|
||||
private:
|
||||
float fadeinTime;
|
||||
const Pixel originalCol;
|
||||
};
|
||||
|
||||
//Spawns and moves towards the player after some amount of time.
|
||||
struct MonsterSoul:Effect{
|
||||
MonsterSoul(vf2d pos,float fadeoutTime,float size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending=false);
|
||||
virtual bool Update(float fElapsedTime)override final;
|
||||
virtual void Draw(const Pixel blendCol)const override final;
|
||||
public:
|
||||
enum Phase{
|
||||
RISING,
|
||||
TRACKING_PLAYER,
|
||||
DEAD,
|
||||
};
|
||||
uint8_t alpha{0U};
|
||||
Phase phase{RISING};
|
||||
float moveSpd{24.f};
|
||||
float fadeoutTime;
|
||||
};
|
||||
|
||||
struct FadeInOutEffect:Effect{
|
||||
//cycleSpd is how long it takes to get from fully opaque to fully transparent, and back to fully opaque
|
||||
FadeInOutEffect(vf2d pos,const std::string&img,float lifetime,float cycleSpd,bool onUpperLevel,float size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={});
|
||||
//A version with oscillators for position and colors, for extra animation effects!
|
||||
FadeInOutEffect(Oscillator<vf2d>pos,const std::string&img,float lifetime,bool onUpperLevel,Oscillator<vf2d>size,vf2d spd,Oscillator<Pixel>col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={});
|
||||
virtual bool Update(float fElapsedTime)override;
|
||||
virtual void Draw(const Pixel blendCol)const override;
|
||||
std::function<Effect(const Effect&self)>particleGenerator;
|
||||
const float particleSpawnFreq;
|
||||
Oscillator<vf2d>posOscillator;
|
||||
Oscillator<vf2d>sizeOscillator;
|
||||
Oscillator<Pixel>colOscillator;
|
||||
float particleSpawnTimer{};
|
||||
const float originalParticleSpawnTimer{};
|
||||
};
|
||||
|
||||
struct LingeringEffect:FadeInOutEffect{
|
||||
LingeringEffect(vf2d pos,const std::string&img,const std::string&soundEffect,float radius,int damage,float damageFreq,const HurtType friendly,float lifetime,float cycleSpd,bool onUpperLevel,float size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending=false,float particleSpawnFreq=0.f,const std::function<Effect(const Effect&self)>&particleGenerator={});
|
||||
virtual bool Update(float fElapsedTime)override final;
|
||||
const int damage;
|
||||
float damageTimer{};
|
||||
const float originalDamageTimer{};
|
||||
const float radius;
|
||||
HurtType friendly;
|
||||
const size_t sfxID{};
|
||||
};
|
||||
|
||||
struct Ink:Effect{
|
||||
//NOTE: The way ink works is that every update frame, each ink stack will check for distance to player, and if the player collides with the radius of the ink it will add one to the ink debuff for the player.
|
||||
// At the end of AdventuresInLestoria::HandleUserInput(), the ink stack counter is reset after handling user input for moving the player.
|
||||
Ink(vf2d pos,float radius,float lifetime,float inkSlowdownDuration,float inkSlowdownPct,float fadeinTime,float fadeoutTime,vf2d size,Pixel col,bool onUpperLevel,float rotation);
|
||||
virtual bool Update(float fElapsedTime)override final;
|
||||
private:
|
||||
const float radius;
|
||||
const float inkSlowdownDuration;
|
||||
const float inkSlowdownAmount;
|
||||
};
|
||||
|
||||
struct FlipCoinEffect:Effect{
|
||||
//cycleSpd is how long it takes to get from fully opaque to fully transparent, and back to fully opaque
|
||||
FlipCoinEffect(Oscillator<vf2d>startEndPos,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
virtual bool Update(float fElapsedTime)override;
|
||||
virtual void Draw(const Pixel blendCol)const override;
|
||||
Oscillator<vf2d>startEndPos;
|
||||
Oscillator<float>sizeFlipper;
|
||||
Oscillator<float>zRiser;
|
||||
};
|
||||
|
||||
struct CollectCoinEffect:Effect{
|
||||
//cycleSpd is how long it takes to get from fully opaque to fully transparent, and back to fully opaque
|
||||
CollectCoinEffect(const Entity attachedTarget,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
|
||||
virtual bool Update(float fElapsedTime)override;
|
||||
virtual void Draw(const Pixel blendCol)const override;
|
||||
const Entity attachedTarget;
|
||||
Oscillator<float>sizeFlipper;
|
||||
Oscillator<float>zRiser;
|
||||
};
|
@ -45,7 +45,7 @@ INCLUDE_game
|
||||
|
||||
EnergyBolt::EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
||||
:Bullet(pos,vel,radius,damage,
|
||||
"energy_bolt.png",upperLevel,game->GetPlayer()->HasEnchant("Piercing Bolt")?true:false,INFINITE,true,friendly,col){}
|
||||
"energy_bolt.png",upperLevel,false,INFINITE,true,friendly,col){}
|
||||
|
||||
void EnergyBolt::Update(float fElapsedTime){
|
||||
lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime);
|
||||
@ -53,7 +53,7 @@ void EnergyBolt::Update(float fElapsedTime){
|
||||
lastParticleSpawn="Wizard.Auto Attack.ParticleFrequency"_F;
|
||||
game->AddEffect(std::make_unique<Effect>(pos,"Wizard.Auto Attack.ParticleLifetimeRange"_FRange,"energy_particle.png",upperLevel,"Wizard.Auto Attack.ParticleSizeRange"_FRange,"Wizard.Auto Attack.ParticleFadeoutTime"_F,vf2d{"Wizard.Auto Attack.ParticleSpeedRange"_FRange,"Wizard.Auto Attack.ParticleSpeedRange"_FRange}));
|
||||
}
|
||||
if(distanceTraveled>"Wizard.Auto Attack.Range"_F&&IsActivated()){
|
||||
if(distanceTraveled>"Wizard.Auto Attack.Max Range"_F&&IsActivated()){
|
||||
fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
|
||||
}
|
||||
}
|
||||
@ -67,9 +67,8 @@ BulletDestroyState EnergyBolt::PlayerHit(Player*player)
|
||||
|
||||
BulletDestroyState EnergyBolt::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)
|
||||
{
|
||||
if(!game->GetPlayer()->HasEnchant("Piercing Bolt"))fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
|
||||
fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
|
||||
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Auto Attack.SplashEffectFadeoutTime"_F));
|
||||
if(hitList.size()==1)damage=ceil(damage*"Piercing Bolt"_ENC["MULTI TARGET DAMAGE"]/100.f);
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ protected:
|
||||
for(float yOffset=0;const auto&[attr,value]:itemRef.lock()->GetEnhancementInfo()[itemRef.lock()->EnhancementLevel()].stats){
|
||||
std::string attributeLabel=std::format("{}",attr.Name());
|
||||
float attributeLabelWidth=game->GetTextSizeProp(attributeLabel).x;
|
||||
window.DrawShadowStringPropDecal(drawPos+vf2d{maxAttributeLabelSize/2+24,yOffset}-vf2d{attributeLabelWidth/2,0},attributeLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
|
||||
window.DrawShadowStringPropDecal(drawPos+vf2d{maxAttributeLabelSize/2+24,yOffset}-vf2d{attributeLabelWidth/2,0},attributeLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f);
|
||||
uint8_t nextEnhanceLevel=itemRef.lock()->EnhancementLevel()+1;
|
||||
float nextStageValue=0;
|
||||
if(itemRef.lock()->GetEnhancementInfo().size()>nextEnhanceLevel){
|
||||
@ -92,21 +92,21 @@ protected:
|
||||
if(Inventory::GetItemCount(itemRef.lock()->ActualName())==0){ //This item hasn't been created yet, so just show that we are developing the item first.
|
||||
window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:<5}",(
|
||||
(attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign)))),
|
||||
WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
|
||||
WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f);
|
||||
}else{ //This item is getting enhanced to the next level.
|
||||
window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:>5} ->#00AA00 {:<5}",
|
||||
attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign),
|
||||
(nextEnhanceLevel<="Item.Item Max Enhancement Level"_I&&itemRef.lock()->GetEnhancementInfo()[nextEnhanceLevel].chapterAvailable<=game->GetCurrentChapter())?
|
||||
attr.ShowAsDecimal()?std::format("{:<4.2f}{}",nextStageValue,percentageSign):std::format("{}{}",nextStageValue,percentageSign)
|
||||
:"MAX"),
|
||||
WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
|
||||
WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f);
|
||||
}
|
||||
yOffset+=16;
|
||||
}
|
||||
}else{
|
||||
std::string text="Cannot be enhanced.";
|
||||
float textWidth=game->GetTextSizeProp(text).x;
|
||||
window.DrawShadowStringPropDecal(rect.middle()-vf2d{textWidth,0}/2,text,RED,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
|
||||
window.DrawShadowStringPropDecal(rect.middle()-vf2d{textWidth,0}/2,text,RED,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f);
|
||||
}
|
||||
}
|
||||
};
|
@ -1,60 +0,0 @@
|
||||
#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 "Entity.h"
|
||||
#include "Player.h"
|
||||
|
||||
#define is(type) std::holds_alternative<type>(entity)
|
||||
#define get(type) std::get<type>(entity)
|
||||
|
||||
Entity::Entity(Player*player):entity(player){}
|
||||
Entity::Entity(Monster*monster):entity(monster){}
|
||||
Entity::Entity(const std::variant<Monster*,Player*>ent):entity(ent){}
|
||||
|
||||
const vf2d Entity::GetPos()const{
|
||||
if(is(Player*))return get(Player*)->GetPos();
|
||||
if(is(Monster*))return get(Monster*)->GetPos();
|
||||
ERR("Entity is not a valid type! THIS SHOULD NOT BE HAPPENING!");
|
||||
return {}; //NOTE: Unreachable
|
||||
}
|
||||
|
||||
void Entity::SetIframeTime(const float iframeTime){
|
||||
if(is(Player*))get(Player*)->ApplyIframes(iframeTime);
|
||||
else if(is(Monster*))get(Monster*)->ApplyIframes(iframeTime);
|
||||
else ERR("Entity is not a valid type! THIS SHOULD NOT BE HAPPENING!");
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#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 "olcUTIL_Geometry2D.h"
|
||||
#include <variant>
|
||||
|
||||
class Player;
|
||||
class Monster;
|
||||
|
||||
//A helper class to connect multiple entity types based on their commonalities without using inheritance shenanigans.
|
||||
class Entity{
|
||||
public:
|
||||
Entity(Player*player);
|
||||
Entity(Monster*monster);
|
||||
Entity(const std::variant<Monster*,Player*>ent);
|
||||
const vf2d GetPos()const;
|
||||
void SetIframeTime(const float iframeTime);
|
||||
private:
|
||||
const std::variant<Monster*,Player*>entity;
|
||||
};
|
@ -67,8 +67,7 @@ void EnvironmentalAudio::SetAudioName(const std::string_view audioName){
|
||||
}
|
||||
void EnvironmentalAudio::Activate(){
|
||||
if(activated)return;
|
||||
soundInstance=Audio::LoadAndPlaySFX(operator""_SFX(SOUND_DATA[audioName].file.c_str(),SOUND_DATA[audioName].file.length()),true);
|
||||
Audio::Engine().SetPitch(soundInstance,util::random_range(0.9f,1.1f));
|
||||
soundInstance=Audio::LoadAndPlay(operator""_SFX(SOUND_DATA[audioName].file.c_str(),SOUND_DATA[audioName].file.length()),true);
|
||||
activated=true;
|
||||
}
|
||||
void EnvironmentalAudio::Deactivate(){
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
inline void OnEquipStatsUpdate()override{
|
||||
const std::weak_ptr<Item>equip=Inventory::GetEquip(slot);
|
||||
if(!ISBLANK(equip)){
|
||||
icon=const_cast<Decal*>(equip.lock()->Icon().Decal());
|
||||
icon=const_cast<Decal*>(equip.lock()->Decal());
|
||||
itemRef=equip;
|
||||
}else{
|
||||
icon=nullptr;
|
||||
|
@ -43,9 +43,6 @@ All rights reserved.
|
||||
#include <memory>
|
||||
#include <source_location>
|
||||
#include <fstream>
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#include <stacktrace>
|
||||
#endif
|
||||
|
||||
inline std::ofstream debugLogger;
|
||||
|
||||
@ -83,9 +80,7 @@ inline std::ofstream debugLogger;
|
||||
}
|
||||
inline static void log(std::stringstream&str,std::source_location loc){
|
||||
debugLogger<<loc.file_name()<<"("<<loc.line()<<":"<<loc.column()<<") "<<loc.function_name()<<": "<<str.str()<<std::endl;
|
||||
debugLogger<<std::stacktrace::current()<<std::endl;
|
||||
std::cout<<loc.file_name()<<"("<<loc.line()<<":"<<loc.column()<<") "<<loc.function_name()<<": "<<str.str()<<std::endl;
|
||||
std::cout<<std::stacktrace::current()<<std::endl;
|
||||
throw std::runtime_error{std::format("{}({}:{}) {}: {}",loc.file_name(),loc.line(),loc.column(),loc.function_name(),str.str()).c_str()};
|
||||
}
|
||||
};
|
||||
@ -93,16 +88,16 @@ inline std::ofstream debugLogger;
|
||||
#define ERR(err) { \
|
||||
std::stringstream errStream; \
|
||||
errStream<<err; \
|
||||
Error::log(errStream);}
|
||||
Error::log(errStream,std::source_location::current());}
|
||||
#define LOG(err) { \
|
||||
std::stringstream errStream; \
|
||||
errStream<<err; \
|
||||
Error::log(errStream);}
|
||||
Error::log(errStream,std::source_location::current());}
|
||||
|
||||
class Error{
|
||||
public:
|
||||
inline static void log(std::stringstream&str){
|
||||
std::cout<<__FILE__<<"("<<__LINE__<<":) "<<str.str()<<std::endl;
|
||||
inline static void log(std::stringstream&str,std::source_location loc){
|
||||
std::cout<<loc.file_name()<<"("<<loc.line()<<":"<<loc.column()<<") "<<loc.function_name()<<": "<<str.str()<<std::endl;
|
||||
#ifdef __DEBUG__
|
||||
throw;
|
||||
#endif
|
||||
@ -120,15 +115,15 @@ type DYNAMIC_CAST(auto variable){
|
||||
}
|
||||
|
||||
template<typename T,typename U>
|
||||
const std::shared_ptr<T>DYNAMIC_POINTER_CAST(const std::shared_ptr<U>&variable){
|
||||
const std::shared_ptr<T> newVariable=dynamic_pointer_cast<T>(variable);
|
||||
std::shared_ptr<T>DYNAMIC_POINTER_CAST(const std::shared_ptr<U>&variable){
|
||||
std::shared_ptr<T> newVariable=dynamic_pointer_cast<T>(variable);
|
||||
if(!newVariable)ERR("Could not dynamic cast to pointer type "<<typeid(newVariable).name()<<"!");
|
||||
return newVariable;
|
||||
}
|
||||
|
||||
template<typename T,typename U>
|
||||
const std::shared_ptr<T>DYNAMIC_POINTER_CAST(const std::weak_ptr<U>&variable){
|
||||
const std::shared_ptr<T> newVariable=dynamic_pointer_cast<T>(variable.lock());
|
||||
std::shared_ptr<T>DYNAMIC_POINTER_CAST(const std::weak_ptr<U>&variable){
|
||||
std::shared_ptr<T> newVariable=dynamic_pointer_cast<T>(variable.lock());
|
||||
if(!newVariable)ERR("Could not dynamic cast to pointer type "<<typeid(newVariable).name()<<"!");
|
||||
return newVariable;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ 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),explosionCount(game->GetPlayer()->HasEnchant("Concussive Trap")?"Concussive Trap"_ENC["TRAP EXPLODE COUNT"]:1),Bullet(pos,{},0.f,damage,"Ability Icons/explosive_trap.png",upperLevel,hitsMultiple,INFINITE,false,friendly,col,scale,0.f,"Trap Hit"){
|
||||
: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!");
|
||||
@ -58,10 +58,7 @@ void ExplosiveTrap::Update(float fElapsedTime){
|
||||
|
||||
if(IsDeactivated())return;
|
||||
|
||||
if(rearmTime>0.f){
|
||||
rearmTime-=fElapsedTime;
|
||||
if(rearmTime<=0.f)Detonate();
|
||||
}else if(!trapActivated){
|
||||
if(!trapActivated){
|
||||
activationWaitTime-=fElapsedTime;
|
||||
if(activationWaitTime<=0.f){
|
||||
radius=activationRadius;
|
||||
@ -92,14 +89,13 @@ BulletDestroyState ExplosiveTrap::PlayerHit(Player*player){
|
||||
}
|
||||
|
||||
void ExplosiveTrap::Detonate(){
|
||||
const HurtList list{game->Hurt(pos,"Trapper.Ability 3.Explosion Radius"_F/100.f*24,damage,OnUpperLevel(),GetZ(),HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
|
||||
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);
|
||||
if(explosionCount==1)std::get<Monster*>(targetPtr)->ProximityKnockback(pos,"Trapper.Ability 3.Explosion Knockback Amount"_F);
|
||||
else std::get<Monster*>(targetPtr)->ProximityKnockback(pos,20.f);
|
||||
if(explosionCount==1)std::get<Monster*>(targetPtr)->Knockup("Trapper.Ability 3.Explosion Knockup Amount"_F);
|
||||
else std::get<Monster*>(targetPtr)->Knockup(0.1f);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,13 +103,7 @@ void ExplosiveTrap::Detonate(){
|
||||
explodeEffect->scaleSpd={0.125f,0.125f};
|
||||
game->AddEffect(std::move(explodeEffect));
|
||||
SoundEffect::PlaySFX("Explosion",pos);
|
||||
explosionCount--;
|
||||
if(explosionCount>0)rearmTime=0.6f;
|
||||
else{
|
||||
fadeOutTime=0.5f;
|
||||
Deactivate();
|
||||
lifetime=0.f;
|
||||
}
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
BulletDestroyState ExplosiveTrap::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){
|
||||
@ -121,6 +111,9 @@ BulletDestroyState ExplosiveTrap::MonsterHit(Monster&monster,const uint8_t markS
|
||||
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;
|
||||
|
@ -1,65 +0,0 @@
|
||||
#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 "AdventuresInLestoria.h"
|
||||
#include "DEFINES.h"
|
||||
#include "util.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
FadeInOutEffect::FadeInOutEffect(vf2d pos,const std::string&img,float lifetime,float cycleSpd,bool onUpperLevel,float size,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending,float particleSpawnFreq,const std::function<Effect(const Effect&self)>&particleGenerator)
|
||||
:FadeInOutEffect({pos,pos,cycleSpd},img,lifetime,onUpperLevel,{{size,size},{size,size},cycleSpd},spd,{col,{col.r,col.g,col.b,0},cycleSpd},rotation,rotationSpd,additiveBlending,particleSpawnFreq,particleGenerator){}
|
||||
FadeInOutEffect::FadeInOutEffect(Oscillator<vf2d>pos,const std::string&img,float lifetime,bool onUpperLevel,Oscillator<vf2d>size,vf2d spd,Oscillator<Pixel>col,float rotation,float rotationSpd,bool additiveBlending,float particleSpawnFreq,const std::function<Effect(const Effect&self)>&particleGenerator)
|
||||
:particleSpawnFreq(particleSpawnFreq),particleGenerator(particleGenerator),particleSpawnTimer(particleSpawnFreq),originalParticleSpawnTimer(particleSpawnTimer),posOscillator(pos),colOscillator(col),sizeOscillator(size),Effect(pos.first,lifetime,img,onUpperLevel,size.first,0.25f,spd,col.first,rotation,rotationSpd,additiveBlending){}
|
||||
bool FadeInOutEffect::Update(float fElapsedTime){
|
||||
if(particleGenerator){
|
||||
particleSpawnTimer-=fElapsedTime;
|
||||
if(particleSpawnTimer<=0.f){
|
||||
particleSpawnTimer+=originalParticleSpawnTimer;
|
||||
game->AddEffect(std::make_unique<Effect>(particleGenerator(*this)));
|
||||
}
|
||||
}
|
||||
pos=posOscillator.Update(fElapsedTime);
|
||||
size=sizeOscillator.Update(fElapsedTime);
|
||||
col=colOscillator.Update(fElapsedTime);
|
||||
return Effect::Update(fElapsedTime);
|
||||
}
|
||||
void FadeInOutEffect::Draw(const Pixel blendCol)const{
|
||||
Effect::Draw(blendCol);
|
||||
}
|
@ -45,14 +45,14 @@ All rights reserved.
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
FallingBullet::FallingBullet(const std::string&imgName,vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple,float knockbackAmt,float lifetime,bool friendly,Pixel spellCircleCol,vf2d scale,float image_angle,float spellCircleRotation,float spellCircleRotationSpd,Pixel insigniaCol,float insigniaRotation,float insigniaRotationSpd)
|
||||
:Bullet(targetPos,vel,0.f,damage,imgName,upperLevel,false,lifetime+0.1f,false,friendly,WHITE,scale,image_angle),targetPos(targetPos),zVel(zVel),indicatorDisplayTime(indicatorDisplayTime),knockbackAmt(knockbackAmt),collisionRadius(radius),
|
||||
indicator(targetPos,lifetime+0.1f,"range_indicator.png","spell_insignia.png",upperLevel,radius/12.f,0.5f,{},spellCircleCol,spellCircleRotation,spellCircleRotationSpd,false,radius/12.f,0.f,{},insigniaCol,insigniaRotation,insigniaRotationSpd,false){
|
||||
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 FallingBullet::Update(float fElapsedTime){
|
||||
void FallingStone::Update(float fElapsedTime){
|
||||
z+=zVel*fElapsedTime;
|
||||
lastTrailEffect-=fElapsedTime;
|
||||
if(z<=0.f){
|
||||
@ -62,15 +62,16 @@ void FallingBullet::Update(float fElapsedTime){
|
||||
fadeOutTime=0.5f;
|
||||
SoundEffect::PlaySFX("Stone Land",pos);
|
||||
if(friendly){
|
||||
for(auto&[monsterPtr,hurt]:game->Hurt(pos,collisionRadius,damage,OnUpperLevel(),z,HurtType::MONSTER)){
|
||||
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(pos,collisionRadius,damage,OnUpperLevel(),z,HurtType::PLAYER)){
|
||||
}
|
||||
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(pos,collisionRadius,knockbackAmt,HurtType::PLAYER|HurtType::MONSTER);
|
||||
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();
|
||||
}
|
||||
@ -81,11 +82,11 @@ void FallingBullet::Update(float fElapsedTime){
|
||||
}
|
||||
indicator.Update(fElapsedTime);
|
||||
}
|
||||
void FallingBullet::Draw(const Pixel blendCol)const{
|
||||
void FallingStone::Draw(const Pixel blendCol)const{
|
||||
if(lifetime<=indicatorDisplayTime){
|
||||
indicator._Draw();
|
||||
indicator.Draw();
|
||||
}
|
||||
Bullet::Draw(blendCol);
|
||||
}
|
||||
|
||||
void FallingBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){}
|
||||
void FallingStone::ModifyOutgoingDamageData(HurtDamageInfo&data){}
|
@ -41,16 +41,13 @@ All rights reserved.
|
||||
#include "DEFINES.h"
|
||||
#include "util.h"
|
||||
#include "SoundEffect.h"
|
||||
#include "TrailEffect.h"
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_MONSTER_LIST
|
||||
|
||||
FireBolt::FireBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
||||
:Bullet(pos,vel,radius,damage,
|
||||
"energy_bolt.png",upperLevel,false,INFINITE,true,friendly,col){
|
||||
if(game->GetPlayer()->HasEnchant("Trail of Fire"))flameTrail=dynamic_cast<TrailEffect&>(game->AddEffect(std::make_unique<TrailEffect>(pos,"Trail of Fire"_ENC["TRAIL DURATION"],"FlamesTexture.png","Trail of Fire"_ENC["TRAIL DAMAGE"]/100.f*game->GetPlayer()->GetAttack(),"Trail of Fire"_ENC["TRAIL TICK FREQUENCY"],upperLevel,1.f,vf2d{1.f,2.f},30000.f,Oscillator<Pixel>{{255,0,0,128},Pixel(0xE74F30),2.f},EffectType::TRAIL_OF_FIRE,true),true));
|
||||
}
|
||||
"energy_bolt.png",upperLevel,false,INFINITE,true,friendly,col){}
|
||||
|
||||
void FireBolt::Update(float fElapsedTime){
|
||||
lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime);
|
||||
@ -75,8 +72,6 @@ void FireBolt::Update(float fElapsedTime){
|
||||
|
||||
SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos);
|
||||
}
|
||||
|
||||
if(flameTrail)flameTrail.value().get().SetEndPos(pos);
|
||||
}
|
||||
|
||||
BulletDestroyState FireBolt::PlayerHit(Player*player)
|
||||
|
@ -1,58 +0,0 @@
|
||||
#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 "AdventuresInLestoria.h"
|
||||
#include "DEFINES.h"
|
||||
#include "util.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
FlipCoinEffect::FlipCoinEffect(Oscillator<vf2d>startEndPos,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending)
|
||||
:Effect(startEndPos.first,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending),startEndPos(startEndPos),sizeFlipper(-1.f,1.f,lifetime*2.f),zRiser(0.f,coinArcRiseZAmt,1/lifetime){
|
||||
}
|
||||
bool FlipCoinEffect::Update(float fElapsedTime){
|
||||
zRiser.Update(fElapsedTime);
|
||||
pos=startEndPos.Update(fElapsedTime);
|
||||
size.y=sizeFlipper.Update(fElapsedTime);
|
||||
z=zRiser.get();
|
||||
return Effect::Update(fElapsedTime);
|
||||
}
|
||||
void FlipCoinEffect::Draw(const Pixel blendCol)const{
|
||||
Effect::Draw(blendCol);
|
||||
}
|
@ -47,6 +47,12 @@ ForegroundEffect::ForegroundEffect(vf2d pos,float lifetime,std::string imgFile,b
|
||||
: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 Pixel blendCol)const{
|
||||
game->DrawPartialRotatedDecal(pos,GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,blendCol);
|
||||
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);
|
||||
}
|
@ -51,11 +51,11 @@ using A=Attribute;
|
||||
void Monster::STRATEGY::FROG(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::LOCKON_WAITTIME)=std::max(0.0f,m.F(A::LOCKON_WAITTIME)-fElapsedTime);
|
||||
phase:
|
||||
switch(PHASE()){
|
||||
switch(m.I(A::PHASE)){
|
||||
case 0:{ //Move towards phase.
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
if(distToPlayer<24*ConfigInt("Range")/100.f){
|
||||
SETPHASE(PHASE()+1);
|
||||
m.I(A::PHASE)++;
|
||||
m.F(A::LOCKON_WAITTIME)=ConfigFloat("Lockon Wait Time");
|
||||
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
|
||||
m.RotateTowardsPos(m.V(A::LOCKON_POS));
|
||||
@ -72,23 +72,23 @@ void Monster::STRATEGY::FROG(Monster&m,float fElapsedTime,std::string strategy){
|
||||
SoundEffect::PlaySFX("Slime Shoot",m.pos);
|
||||
CreateBullet(FrogTongue)(m,tongueMaxRangePos,ConfigFloat("Attack Duration"),m.GetAttack(),m.OnUpperLevel(),ConfigFloat("Tongue Knockback Strength"),false,ConfigPixel("Tongue Color"))EndBullet;
|
||||
m.PerformShootAnimation();
|
||||
SETPHASE(2);
|
||||
m.I(A::PHASE)=2;
|
||||
}
|
||||
m.PerformIdleAnimation();
|
||||
}break;
|
||||
case 2:{
|
||||
if(m.F(A::LOCKON_WAITTIME)==0.0f){
|
||||
m.F(A::LOCKON_WAITTIME)=ConfigFloat("Attack Recovery Time");
|
||||
SETPHASE(3);
|
||||
m.I(A::PHASE)=3;
|
||||
}
|
||||
}break;
|
||||
case 3:{
|
||||
if(m.F(A::LOCKON_WAITTIME)==0.0f){
|
||||
SETPHASE(0);
|
||||
m.I(A::PHASE)=0;
|
||||
}
|
||||
}break;
|
||||
default:{
|
||||
ERR(std::format("Unhandled phase {} for {} strategy!",PHASE(),strategy));
|
||||
ERR(std::format("Unhandled phase {} for {} strategy!",m.I(A::PHASE),strategy));
|
||||
}
|
||||
}
|
||||
}
|
@ -56,14 +56,13 @@ void FrogTongue::Update(float fElapsedTime){
|
||||
geom2d::line<float>lineToTarget(pos,targetPos);
|
||||
vf2d drawVec=lineToTarget.vector().norm()*3;
|
||||
|
||||
tongueLength=util::lerp(0.f,lineToTarget.length(),pow(sin((lifetime*PI)/duration),20.f));
|
||||
tongueLength=util::lerp(0,lineToTarget.length(),pow(sin((lifetime*PI)/duration),20.f));
|
||||
|
||||
vf2d tongueEndPos=geom2d::line<float>(pos+drawVec,targetPos).upoint(pow(sin((lifetime*PI)/duration),20.f));
|
||||
geom2d::line<float>tongueLine(pos+drawVec,tongueEndPos);
|
||||
|
||||
if(!friendly&&hitList.find(game->GetPlayer())==hitList.end()&&geom2d::overlaps(game->GetPlayer()->Hitbox(),tongueLine)){
|
||||
if(!friendly&&geom2d::overlaps(game->GetPlayer()->Hitbox(),tongueLine)){
|
||||
_PlayerHit(game->GetPlayer());
|
||||
hitList.insert(game->GetPlayer());
|
||||
}
|
||||
if(friendly){
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
|
@ -37,7 +37,7 @@ All rights reserved.
|
||||
#pragma endregion
|
||||
|
||||
#include "GameEvent.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
std::vector<std::unique_ptr<GameEvent>>GameEvent::events;
|
||||
|
||||
|
@ -44,7 +44,6 @@ All rights reserved.
|
||||
#include "State_GameHub.h"
|
||||
#include "State_Death.h"
|
||||
#include "State_Dialog.h"
|
||||
#include "State_Arena.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
@ -61,7 +60,6 @@ void GameState::Initialize(){
|
||||
NEW_STATE(States::GAME_HUB,State_GameHub);
|
||||
NEW_STATE(States::DEATH,State_Death);
|
||||
NEW_STATE(States::DIALOG,State_Dialog);
|
||||
NEW_STATE(States::ARENA,State_Arena);
|
||||
}
|
||||
|
||||
void GameState::_ChangeState(States::State newState){
|
||||
|
@ -54,7 +54,6 @@ namespace States{
|
||||
DIALOG,
|
||||
KEYBIND,
|
||||
DEATH,
|
||||
ARENA,
|
||||
};
|
||||
};
|
||||
|
||||
@ -79,6 +78,5 @@ public:
|
||||
virtual void GetAnyMouseRelease(int32_t mouseButton);
|
||||
static void ChangeState(States::State newState,float fadeOutDuration=0,uint8_t mosaicEffect=1U);
|
||||
virtual void OnLevelLoad()=0;
|
||||
virtual bool IsPlayerControlledGameplayState()=0; //Return true if this state allows the player to be controlled. Returns false for any other type of state.
|
||||
static States::State GetCurrentState();
|
||||
};
|
@ -1,298 +0,0 @@
|
||||
#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"
|
||||
#include "BulletTypes.h"
|
||||
#include <ranges>
|
||||
|
||||
using A=Attribute;
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_MONSTER_LIST
|
||||
|
||||
void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string strategy){
|
||||
enum PhaseName{
|
||||
INIT,
|
||||
NORMAL,
|
||||
AFTERIMAGE_FADEIN,
|
||||
GHOSTSABER_SLASH=999,
|
||||
TOSS_COIN,
|
||||
HIDING,
|
||||
};
|
||||
|
||||
enum CannonShotType{
|
||||
BOMBARDMENT,//every shot is at a random location within 900 Range of the player. (This is also the move during hide and seek.)
|
||||
PRECISE_BOMBARDMENT,//same as before but within 700 range of the player.
|
||||
LINE,//the 4th or 5th hit would hit the player if the player doesnt move at all.
|
||||
SHARPSHOOTER,//aiming directly at the player, skipping every 2nd shot. (only 4 instead of 8 shots)
|
||||
PREDICTION,//shoots in the direction of the current or last players movement and predicts where the player would be in 2.8 seconds. (slightly further then where the player would be at impact. thats why 2.8 instead of 2.5 seconds)
|
||||
};
|
||||
|
||||
static const uint8_t PHASE_COUNT{uint8_t(DATA.GetProperty("MonsterStrategy.Ghost of Pirate Captain.Cannon Cycle").GetValueCount())};
|
||||
static uint8_t TOTAL_CANNON_SHOTS{0};
|
||||
const bool IsHiding{m.V(A::HIDING_POS)!=vf2d{}};
|
||||
|
||||
const auto AdvanceCannonPhase{[&m,&strategy](){
|
||||
m.GetFloat(A::CANNON_TIMER)=0.f;
|
||||
if(m.GetInt(A::CANNON_PHASE)+1>=PHASE_COUNT)m.GetInt(A::CANNON_SHOT_TYPE)=util::random()%5;
|
||||
const int prevCannonPhase{m.I(A::CANNON_PHASE)};
|
||||
m.I(A::CANNON_PHASE)=(m.I(A::CANNON_PHASE)+1)%PHASE_COUNT;
|
||||
if(prevCannonPhase>m.I(A::CANNON_PHASE)){//Phase has wrapped around, reset cannon shot count.
|
||||
m.I(A::CANNON_SHOT_COUNT)=0;
|
||||
if(!m.B(A::FIRST_WAVE_COMPLETE)){
|
||||
m.B(A::FIRST_WAVE_COMPLETE)=true;
|
||||
m.ForceSetPos(m.spawnPos);
|
||||
m.SetupAfterImage();
|
||||
m.afterImagePos=m.GetPos();
|
||||
m.SetCollisionRadius(0.f);
|
||||
m.F(A::CASTING_TIMER)=1.f;
|
||||
SETPHASE(AFTERIMAGE_FADEIN);
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
if(m.F(A::SHRAPNEL_SHOT_FALL_TIMER)>0.f){
|
||||
m.F(A::SHRAPNEL_SHOT_FALL_TIMER)-=fElapsedTime;
|
||||
while(m.I(A::SHRAPNEL_SHOT_COUNT)&&m.F(A::SHRAPNEL_SHOT_FALL_TIMER)<=0.f){
|
||||
const float randomAng{util::random_range(0,2*PI)};
|
||||
const float range{util::random_range(0,ConfigPixels("Bombardment Max Distance"))};
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
|
||||
m.F(A::SHRAPNEL_SHOT_FALL_TIMER)+=ConfigFloat("Shrapnel Shot Bullet Separation");
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Shrapnel Shot Bullet Radius"),ConfigInt("Shrapnel Shot Damage"),m.OnUpperLevel(),false,ConfigFloat("Shrapnel Knockback Amt"),ConfigFloat("Shrapnel Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Shrapnel Shot Bullet Radius")/100.f*1.75f,ConfigFloat("Shrapnel Shot Bullet Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
m.I(A::SHRAPNEL_SHOT_COUNT)--;
|
||||
}
|
||||
}
|
||||
|
||||
switch(PHASE()){
|
||||
enum CannonPhaseType{
|
||||
CANNON_SHOT,
|
||||
SILENCE,
|
||||
SHRAPNEL_SHOT,
|
||||
};
|
||||
case INIT:{
|
||||
m.SetStrategyDeathFunction([](GameEvent&ev,Monster&m,const std::string&strategy){
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
if(m->IsAlive())m->_DealTrueDamage(m->GetHealth(),HurtFlag::NONE);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
for(int i:std::ranges::iota_view(0U,PHASE_COUNT)){
|
||||
const int cannonCycle{DATA.GetProperty("MonsterStrategy.Ghost of Pirate Captain.Cannon Cycle").GetInt(i)};
|
||||
m.VEC(A::CANNON_PHASES).emplace_back(cannonCycle);
|
||||
if(cannonCycle==CANNON_SHOT)TOTAL_CANNON_SHOTS++;
|
||||
}
|
||||
m.B(A::FIRST_WAVE_COMPLETE)=false;
|
||||
m.I(A::CANNON_SHOT_TYPE)=BOMBARDMENT;
|
||||
m.ForceSetPos({-400.f,-400.f});
|
||||
SETPHASE(NORMAL);
|
||||
}break;
|
||||
case NORMAL:{
|
||||
m.F(A::CANNON_TIMER)+=fElapsedTime;
|
||||
m.F(A::SHRAPNEL_CANNON_TIMER)+=fElapsedTime;
|
||||
const int phase{std::any_cast<int>(m.VEC(A::CANNON_PHASES)[m.I(A::CANNON_PHASE)])};
|
||||
switch(phase){
|
||||
case CANNON_SHOT:{//Normal Cannon Shot. Takes on one of five varieties.
|
||||
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Cannon Shot Delay")){
|
||||
switch(m.I(A::CANNON_SHOT_TYPE)){
|
||||
case BOMBARDMENT:{
|
||||
const float randomAng{util::random_range(0,2*PI)};
|
||||
const float range{util::random_range(0,ConfigPixels("Bombardment Max Distance"))};
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case PRECISE_BOMBARDMENT:{
|
||||
const float randomAng{util::random_range(0,2*PI)};
|
||||
const float range{util::random_range(0,ConfigPixels("Precise Bombardment Max Distance"))};
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case LINE:{
|
||||
//Draw a line from one side of the screen to the other, drawing through the middle.
|
||||
if(m.I(A::CANNON_SHOT_COUNT)==0)m.F(A::LINE_SHOT_ANG)=util::random_range(0,2*PI);
|
||||
const vf2d targetPos{geom2d::line{game->GetPlayer()->GetPos()+vf2d{float(game->ScreenHeight()),m.F(A::LINE_SHOT_ANG)}.cart(),game->GetPlayer()->GetPos()+vf2d{float(game->ScreenHeight()),m.F(A::LINE_SHOT_ANG)+PI}.cart()}.upoint(float(m.I(A::CANNON_SHOT_COUNT))/TOTAL_CANNON_SHOTS)};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case SHARPSHOOTER:{
|
||||
if(m.I(A::CANNON_SHOT_COUNT)%2==0)CreateBullet(FallingBullet)("cannonball.png",game->GetPlayer()->GetPos(),ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case PREDICTION:{
|
||||
LOG(std::format("Previous Pos: {} Current: {}",game->GetPlayer()->GetPreviousPos().str(),game->GetPlayer()->GetPos().str()));
|
||||
const float angle{util::angleTo(game->GetPlayer()->GetPreviousPos(),game->GetPlayer()->GetPos())};
|
||||
const float range{util::random_range(0,100.f*game->GetPlayer()->GetMoveSpdMult())*ConfigFloat("Cannon Shot Impact Time")};
|
||||
LOG(std::format("Range/Angle: {}",vf2d{range,angle}.str()));
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,angle}.cart()};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
}
|
||||
AdvanceCannonPhase();
|
||||
m.I(A::CANNON_SHOT_COUNT)++;
|
||||
}
|
||||
}break;
|
||||
case SILENCE:{
|
||||
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Silence Time"))AdvanceCannonPhase();
|
||||
}break;
|
||||
case SHRAPNEL_SHOT:{
|
||||
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Shrapnel Shot Delay")){
|
||||
m.I(A::SHRAPNEL_SHOT_COUNT)=ConfigInt("Shrapnel Shot Bullet Count");
|
||||
m.F(A::SHRAPNEL_SHOT_FALL_TIMER)=ConfigFloat("Shrapnel Shot Bullet Separation");
|
||||
AdvanceCannonPhase();
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
||||
if(Config("Curse Thresholds").GetValueCount()>m.I(A::CURSE_THRESHOLD_ARRAY_IND)&&
|
||||
m.GetHealthRatio()<=ConfigFloatArr("Curse Thresholds",m.I(A::CURSE_THRESHOLD_ARRAY_IND))/100.f){
|
||||
m.I(A::CURSE_THRESHOLD_ARRAY_IND)++;
|
||||
m.F(A::TOSS_COIN_WAIT_TIMER)=ConfigFloat("Coin Toss Pause Time");
|
||||
m.V(A::TOSS_COIN_TARGET)=game->GetPlayer()->GetPos();
|
||||
game->AddEffect(std::make_unique<FlipCoinEffect>(Oscillator<vf2d>{m.GetPos(),m.V(A::TOSS_COIN_TARGET),1.f/m.F(A::TOSS_COIN_WAIT_TIMER)/2.f},ConfigFloat("Coin Toss Rise Amount"),m.F(A::TOSS_COIN_WAIT_TIMER),"coin.png",m.OnUpperLevel(),3.f));
|
||||
|
||||
#pragma region Determine a hiding spot
|
||||
const auto&hidingSpots{game->GetZones().at("Hiding Spot")};
|
||||
if(hidingSpots.size()==0)ERR("WARNING! Could not find any zones with the name \"Hiding Spot\" on the map!! THIS SHOULD NOT BE HAPPENING!")
|
||||
m.V(A::HIDING_POS)=hidingSpots[util::random()%hidingSpots.size()].zone.middle();
|
||||
#pragma endregion
|
||||
SETPHASE(TOSS_COIN);
|
||||
}
|
||||
|
||||
m.F(A::GHOST_SABER_TIMER)-=fElapsedTime;
|
||||
m.F(A::LAST_COLLISION_TIMER)-=fElapsedTime;
|
||||
|
||||
if(m.B(A::FIRST_WAVE_COMPLETE)){
|
||||
if(m.F(A::LAST_COLLISION_TIMER)<=0.f){
|
||||
m.UpdateFacingDirection(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.SetVelocity(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*100.f*m.GetMoveSpdMult());
|
||||
const float distToPlayer{util::distance(m.GetPos(),game->GetPlayer()->GetPos())};
|
||||
if(m.F(A::GHOST_SABER_TIMER)<=0.f&&distToPlayer<=ConfigPixels("Ghost Saber Activation Range")){
|
||||
m.F(A::GHOST_SABER_TIMER)=ConfigFloat("Ghost Saber Cooldown");
|
||||
const float playerToMonsterAngle{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y};
|
||||
CreateBullet(GhostSaber)(m.GetPos(),m.GetWeakPointer(),ConfigFloat("Ghost Saber Lifetime"),ConfigFloat("Ghost Saber Distance"),ConfigFloat("Ghost Saber Knockback Amt"),playerToMonsterAngle,ConfigFloat("Ghost Saber Radius"),ConfigFloat("Ghost Saber Expand Spd"),ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")))EndBullet;
|
||||
}
|
||||
}
|
||||
if(m.B(A::COLLIDED_WITH_PLAYER)){
|
||||
m.PerformAnimation("SLASHING");
|
||||
m.F(A::GHOST_SABER_SLASH_ANIMATION_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
m.F(A::LAST_COLLISION_TIMER)=ConfigFloat("Collision Recovery Time");
|
||||
m.I(A::PREVIOUS_PHASE)=PHASE();
|
||||
SETPHASE(GHOSTSABER_SLASH);
|
||||
}
|
||||
}
|
||||
}break;
|
||||
case AFTERIMAGE_FADEIN:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
SETPHASE(NORMAL);
|
||||
m.SetCollisionRadius(m.GetOriginalCollisionRadius());
|
||||
}
|
||||
}break;
|
||||
case GHOSTSABER_SLASH:{ //This was triggered by GhostSaber when
|
||||
m.F(A::GHOST_SABER_SLASH_ANIMATION_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::GHOST_SABER_SLASH_ANIMATION_TIMER)<=0.f){
|
||||
m.PerformIdleAnimation();
|
||||
SETPHASE(m.I(A::PREVIOUS_PHASE));
|
||||
}
|
||||
}break;
|
||||
case TOSS_COIN:{
|
||||
m.F(A::TOSS_COIN_WAIT_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::TOSS_COIN_WAIT_TIMER)<=0.f){
|
||||
const float curseDmgPctOverTime{ConfigFloat("Curse Damage")};
|
||||
game->GetPlayer()->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_PRECURSE,ConfigFloat("Curse Damage Wait Time"),ceil(game->GetPlayer()->GetMaxHealth()*ConfigFloat("Curse Damage")/100.f)+1,[curseDmgPctOverTime](Player*attachedTarget,Buff&b){
|
||||
attachedTarget->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_DOT,BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_PCT_DAMAGE_OVER_TIME,INFINITY,curseDmgPctOverTime,1.f);
|
||||
});
|
||||
game->SpawnMonster(m.V(A::TOSS_COIN_TARGET),MONSTER_DATA["Pirate's Coin"],m.OnUpperLevel());
|
||||
m.SetupAfterImage();
|
||||
m.afterImagePos=m.GetPos();
|
||||
m.SetPos(m.V(A::HIDING_POS));
|
||||
m.SetVelocity({});
|
||||
m.arrowIndicator=false; //While the boss is hiding, the indicator will not show up.
|
||||
m.SetStrategyOnHitFunction([&m](const HurtDamageInfo damageData,Monster&monster,const StrategyName&strategyName)->void{
|
||||
m.SetPhase(strategyName,NORMAL);
|
||||
m.arrowIndicator=true;
|
||||
m.SetStrategyOnHitFunction({});
|
||||
});
|
||||
SETPHASE(HIDING);
|
||||
}
|
||||
}break;
|
||||
case HIDING:{
|
||||
m.F(A::CANNON_TIMER)+=fElapsedTime;
|
||||
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Cannon Shot Delay")){
|
||||
switch(m.I(A::CANNON_SHOT_TYPE)){
|
||||
case BOMBARDMENT:{
|
||||
const float randomAng{util::random_range(0,2*PI)};
|
||||
const float range{util::random_range(0,ConfigPixels("Bombardment Max Distance"))};
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case PRECISE_BOMBARDMENT:{
|
||||
const float randomAng{util::random_range(0,2*PI)};
|
||||
const float range{util::random_range(0,ConfigPixels("Precise Bombardment Max Distance"))};
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,randomAng}.cart()};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case LINE:{
|
||||
//Draw a line from one side of the screen to the other, drawing through the middle.
|
||||
if(m.I(A::CANNON_SHOT_COUNT)==0)m.F(A::LINE_SHOT_ANG)=util::random_range(0,2*PI);
|
||||
const vf2d targetPos{geom2d::line{game->GetPlayer()->GetPos()+vf2d{float(game->ScreenHeight()),m.F(A::LINE_SHOT_ANG)}.cart(),game->GetPlayer()->GetPos()+vf2d{float(game->ScreenHeight()),m.F(A::LINE_SHOT_ANG)+PI}.cart()}.upoint(float(m.I(A::CANNON_SHOT_COUNT))/TOTAL_CANNON_SHOTS)};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case SHARPSHOOTER:{
|
||||
if(m.I(A::CANNON_SHOT_COUNT)%2==0)CreateBullet(FallingBullet)("cannonball.png",game->GetPlayer()->GetPos(),ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
case PREDICTION:{
|
||||
LOG(std::format("Previous Pos: {} Current: {}",game->GetPlayer()->GetPreviousPos().str(),game->GetPlayer()->GetPos().str()));
|
||||
const float angle{util::angleTo(game->GetPlayer()->GetPreviousPos(),game->GetPlayer()->GetPos())};
|
||||
const float range{util::random_range(0,100.f*game->GetPlayer()->GetMoveSpdMult())*ConfigFloat("Cannon Shot Impact Time")};
|
||||
LOG(std::format("Range/Angle: {}",vf2d{range,angle}.str()));
|
||||
const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,angle}.cart()};
|
||||
CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet;
|
||||
}break;
|
||||
}
|
||||
AdvanceCannonPhase();
|
||||
m.I(A::CANNON_SHOT_COUNT)++;
|
||||
}
|
||||
if(m.F(A::SHRAPNEL_CANNON_TIMER)>=ConfigFloat("Shrapnel Hiding Shot Delay")){
|
||||
m.I(A::SHRAPNEL_SHOT_COUNT)=ConfigInt("Shrapnel Shot Bullet Count");
|
||||
m.F(A::SHRAPNEL_SHOT_FALL_TIMER)=ConfigFloat("Shrapnel Shot Bullet Separation");
|
||||
m.F(A::SHRAPNEL_CANNON_TIMER)=0.f;
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
#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 "Attributable.h"
|
||||
|
||||
GhostSaber::GhostSaber(const vf2d pos,const std::weak_ptr<Monster>target,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const float expandSpd,const int damage,const bool upperLevel,const float rotSpd,const bool friendly,const Pixel col,const vf2d scale,const float image_angle)
|
||||
:Bullet(target.lock()->GetPos()+vf2d{distFromTarget,initialRot}.cart(),{},radius,damage,"ghost_dagger.png",upperLevel,false,INFINITE,false,friendly,col,scale,image_angle),attachedMonster(target),rotSpd(rotSpd),distFromTarget(distFromTarget),rot(initialRot),aliveTime(lifetime),knockbackAmt(knockbackAmt),expandSpd(expandSpd){}
|
||||
void GhostSaber::Update(float fElapsedTime){
|
||||
alphaOscillator.Update(fElapsedTime);
|
||||
particleTimer-=fElapsedTime;
|
||||
aliveTime-=fElapsedTime;
|
||||
distFromTarget+=expandSpd*fElapsedTime;
|
||||
if(particleTimer<=0.f){
|
||||
particleTimer+=0.05f;
|
||||
game->AddEffect(std::make_unique<ShineEffect>(pos,0.1f,0.1f,"pixel.png",2.f,vf2d{},Pixel{239,215,98,192},util::random(2*PI),0.f,true));
|
||||
}
|
||||
rot+=rotSpd*fElapsedTime;
|
||||
if(!attachedMonster.expired()){
|
||||
pos=attachedMonster.lock()->GetPos()+vf2d{distFromTarget,rot}.cart();
|
||||
}
|
||||
if(aliveTime<=0.f&&!IsDeactivated()){
|
||||
Deactivate();
|
||||
fadeOutTime=0.5f;
|
||||
}
|
||||
col.a=alphaOscillator.get();
|
||||
image_angle=-rot*2;
|
||||
};
|
||||
|
||||
BulletDestroyState GhostSaber::PlayerHit(Player*player){
|
||||
player->ApplyIframes(0.2f);
|
||||
player->Knockback(vf2d{knockbackAmt,rot}.cart());
|
||||
if(!attachedMonster.expired()){
|
||||
const int GHOSTSABER_SLASH_PHASEID{999};
|
||||
attachedMonster.lock()->GetInt(Attribute::PREVIOUS_PHASE)=attachedMonster.lock()->GetPhase(attachedMonster.lock()->GetStrategyName());
|
||||
attachedMonster.lock()->SetPhase(attachedMonster.lock()->GetStrategyName(),GHOSTSABER_SLASH_PHASEID);
|
||||
attachedMonster.lock()->PerformAnimation("SLASHING");
|
||||
attachedMonster.lock()->GetFloat(Attribute::GHOST_SABER_SLASH_ANIMATION_TIMER)=attachedMonster.lock()->GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
}
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
BulletDestroyState GhostSaber::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){
|
||||
monster.ApplyIframes(0.2f);
|
||||
monster.Knockback(vf2d{knockbackAmt,rot}.cart());
|
||||
return BulletDestroyState::KEEP_ALIVE;
|
||||
}
|
||||
|
||||
void GhostSaber::ModifyOutgoingDamageData(HurtDamageInfo&data){}
|
@ -1,84 +0,0 @@
|
||||
#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"
|
||||
#include "BulletTypes.h"
|
||||
|
||||
using A=Attribute;
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
void Monster::STRATEGY::GIANT_CRAB(Monster&m,float fElapsedTime,std::string strategy){
|
||||
enum PhaseName{
|
||||
PREPARE_CHARGE,
|
||||
CHARGE,
|
||||
};
|
||||
switch(PHASE()){
|
||||
case PREPARE_CHARGE:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
m.AddBuff(BuffType::SPEEDBOOST,ConfigFloat("Charge Time"),0.f);
|
||||
m.F(A::SPEED_RAMPUP_TIMER)=0.1f;
|
||||
SETPHASE(CHARGE);
|
||||
}
|
||||
}break;
|
||||
case CHARGE:{
|
||||
m.F(A::CHASE_TIMER)+=fElapsedTime;
|
||||
m.F(A::SPEED_RAMPUP_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CHASE_TIMER)>=ConfigFloat("Charge Time")||m.ReachedTargetPos()){
|
||||
m.F(A::CHASE_TIMER)=0.f;
|
||||
m.target=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).rpoint(util::distance(m.GetPos(),game->GetPlayer()->GetPos())+ConfigPixels("Charge Extend Distance"));
|
||||
m.RemoveBuff(BuffType::SPEEDBOOST);
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Charge Wait Time");
|
||||
SETPHASE(PREPARE_CHARGE);
|
||||
}
|
||||
if(m.F(A::SPEED_RAMPUP_TIMER)<=0.f){
|
||||
m.F(A::SPEED_RAMPUP_TIMER)=0.1f;
|
||||
if(m.HasBuff(BuffType::SPEEDBOOST)){
|
||||
const float buffIntensity{m.GetBuffs(BuffType::SPEEDBOOST)[0].intensity};
|
||||
m.EditBuff(BuffType::SPEEDBOOST,0).intensity=std::min(buffIntensity+ConfigFloat("Movespeed Rampup Final Amount")/100.f/(ConfigFloat("Movespeed Rampup Time")/0.1f),ConfigFloat("Movespeed Rampup Final Amount")/100.f);
|
||||
}
|
||||
}
|
||||
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}break;
|
||||
}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
#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"
|
||||
#include "BulletTypes.h"
|
||||
|
||||
using A=Attribute;
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_MONSTER_LIST
|
||||
|
||||
void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string strategy){
|
||||
enum PhaseName{
|
||||
INIT,
|
||||
IDENTIFY_ARMS,
|
||||
NORMAL,
|
||||
HURT_ANIMATION,
|
||||
DEAD,
|
||||
};
|
||||
|
||||
if(!m.B(A::ARM_SPEEDS_INCREASED)&&m.GetHealth()<=ConfigInt("Arm Speedup Health Threshold")){
|
||||
m.B(A::ARM_SPEEDS_INCREASED)=true;
|
||||
for(std::shared_ptr<Monster>&arm:MONSTER_LIST){
|
||||
const std::string OCTOPUS_ARM_NAME{"Octopus Arm"};
|
||||
if(arm->GetName()==OCTOPUS_ARM_NAME){
|
||||
std::weak_ptr<Monster>armPtr{arm};
|
||||
if(!armPtr.expired())armPtr.lock()->GetFloat(A::ATTACK_ANIMATION_SPEED)=1.f+ConfigFloat("Arm Animation Speed Increase")/100.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(PHASE()){
|
||||
case INIT:{
|
||||
m.F(A::BREAK_TIME)=0.5f;
|
||||
m.AddBuff(BuffType::DAMAGE_REDUCTION,INFINITE,ConfigFloat("Permanent Resistance Buff")/100.f);
|
||||
m.SetStrategyDeathFunction([](GameEvent&event,Monster&m,const std::string&strategy){
|
||||
std::string takoyakiImgDir{"item_img_directory"_S+"Takoyaki.png"};
|
||||
if(!GFX.count(takoyakiImgDir))ERR(std::format("WARNING! Could not find item image {}",takoyakiImgDir));
|
||||
game->AddEffect(std::make_unique<Effect>(m.GetPos(),INFINITE,"item_img_directory"_S+"Takoyaki.png",m.OnUpperLevel(),1.f,0.f,vf2d{4.f,4.f},vf2d{},WHITE));
|
||||
m.SetLifetime(4.f);
|
||||
SETPHASE(DEAD);
|
||||
return false;
|
||||
});
|
||||
SETPHASE(IDENTIFY_ARMS);
|
||||
}break;
|
||||
case IDENTIFY_ARMS:{
|
||||
m.F(A::BREAK_TIME)-=fElapsedTime;
|
||||
if(m.F(A::BREAK_TIME)<=0.f){
|
||||
m.F(A::CASTING_TIMER)=util::random_range(ConfigFloatArr("Arm Move Timer",0),ConfigFloatArr("Arm Move Timer",1));
|
||||
for(std::shared_ptr<Monster>&arm:MONSTER_LIST){
|
||||
const std::string OCTOPUS_ARM_NAME{"Octopus Arm"};
|
||||
if(arm->GetName()==OCTOPUS_ARM_NAME){
|
||||
std::weak_ptr<Monster>armPtr{arm};
|
||||
m.VEC(A::ARM_LIST).emplace_back(armPtr);
|
||||
m.VEC(A::ARM_LOCATIONS).emplace_back(armPtr.lock()->GetPos());
|
||||
}
|
||||
}
|
||||
SETPHASE(NORMAL);
|
||||
}
|
||||
}break;
|
||||
case NORMAL:{
|
||||
const bool InSecondPhase{m.GetHealth()<=ConfigInt("Phase 2 Health Threshold")};
|
||||
m.F(A::SHOOT_TIMER)-=fElapsedTime;
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
m.F(A::LAST_SHOOT_TIMER)-=fElapsedTime;
|
||||
m.F(A::LAST_INK_SHOOT_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::SHOOT_ANIMATION_TIME)>0.f){
|
||||
m.F(A::SHOOT_ANIMATION_TIME)-=fElapsedTime;
|
||||
if(m.F(A::SHOOT_ANIMATION_TIME)<=0.f)m.PerformIdleAnimation();
|
||||
}
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
int deadMonsterCount{0};
|
||||
std::vector<vf2d>unoccupiedArmLocs;
|
||||
AddAllUnoccupedArmLocations:
|
||||
std::for_each(m.VEC(A::ARM_LOCATIONS).begin(),m.VEC(A::ARM_LOCATIONS).end(),[&unoccupiedArmLocs](const std::any&armLoc){unoccupiedArmLocs.emplace_back(std::any_cast<vf2d>(armLoc));});
|
||||
std::vector<std::any>liveArms;
|
||||
RemoveOccupiedArmLocationsAndDetectAliveArms:
|
||||
std::copy_if(m.VEC(A::ARM_LIST).begin(),m.VEC(A::ARM_LIST).end(),std::back_inserter(liveArms),[&unoccupiedArmLocs,&deadMonsterCount](const std::any&arm){
|
||||
const std::weak_ptr<Monster>&m{std::any_cast<std::weak_ptr<Monster>>(arm)};
|
||||
const bool isLive{!m.expired()&&m.lock()->IsAlive()};
|
||||
if(isLive)std::erase_if(unoccupiedArmLocs,[&m](const vf2d&armLoc){return m.lock()->GetPos()==armLoc;});
|
||||
else deadMonsterCount++;
|
||||
return isLive;
|
||||
});
|
||||
RemoveArmLocationsTooFarFromPlayer:
|
||||
std::erase_if(unoccupiedArmLocs,[maxDist=ConfigPixels("Arm Max Move Distance From Player")](const vf2d&armLoc){return util::distance(game->GetPlayer()->GetPos(),armLoc)>maxDist;});
|
||||
const bool AtLeastOneArmAlive{deadMonsterCount!=m.VEC(A::ARM_LIST).size()};
|
||||
if(deadMonsterCount>0&&AtLeastOneArmAlive&&unoccupiedArmLocs.size()>0){
|
||||
const std::weak_ptr<Monster>&randomArm{std::any_cast<std::weak_ptr<Monster>>(liveArms[util::random()%liveArms.size()])};
|
||||
const vf2d&randomLoc{std::any_cast<vf2d>(unoccupiedArmLocs[util::random()%unoccupiedArmLocs.size()])};
|
||||
randomArm.lock()->PerformAnimation("SUBMERGE");
|
||||
randomArm.lock()->SetPhase("Octopus Arm",randomArm.lock()->I(A::SUBMERGE_STRAT_ID));
|
||||
randomArm.lock()->GetFloat(A::RECOVERY_TIME)=randomArm.lock()->GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
randomArm.lock()->SetCollisionRadius(0.f);
|
||||
randomArm.lock()->V(A::JUMP_TARGET_POS)=randomLoc;
|
||||
randomArm.lock()->SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){});
|
||||
}
|
||||
m.F(A::CASTING_TIMER)=util::random_range(ConfigFloatArr("Arm Move Timer",0),ConfigFloatArr("Arm Move Timer",1));
|
||||
}
|
||||
if(m.F(A::SHOOT_TIMER)<=0.f){
|
||||
const auto CreateBurstBullet=[&](){
|
||||
CreateBullet(BurstBullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Big Bullet Speed"),game->GetPlayer(),ConfigPixels("Big Bullet Detection Radius"),ConfigInt("Big Bullet Extra Bullet Count"),util::degToRad(ConfigFloat("Big Bullet Extra Bullet Rotate Speed")),ConfigFloat("Big Bullet Extra Bullet Radius"),vf2d{ConfigFloatArr("Big Bullet Extra Bullet Image Scale",0),ConfigFloatArr("Big Bullet Extra Bullet Image Scale",1)},ConfigFloat("Big Bullet Extra Bullet Speed"),ConfigFloat("Big Bullet Extra Bullet Acceleration"),ConfigFloat("Big Bullet Radius"),ConfigInt("Big Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Big Bullet Color"),vf2d{ConfigFloat("Big Bullet Image Scale"),ConfigFloat("Big Bullet Image Scale")})EndBullet;
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Big Bullet Boss Rest Time");
|
||||
m.I(A::ATTACK_COUNT)=-1;
|
||||
};
|
||||
if(InSecondPhase){
|
||||
if(m.F(A::LAST_INK_SHOOT_TIMER)<=0.f){
|
||||
CreateBullet(InkBullet)(m.GetPos(),game->GetPlayer()->GetPos(),vf2d{ConfigFloat("Phase 2.Ink Bullet Speed"),0.f},ConfigFloat("Phase 2.Ink Explosion Radius"),ConfigFloat("Phase 2.Ink Puddle Lifetime"),ConfigFloat("Phase 2.Ink Slowdown Time"),ConfigFloat("Phase 2.Ink Slowdown Amount")/100.f,ConfigFloat("Phase 2.Ink Puddle Collision Radius"),m.OnUpperLevel(),false)EndBullet;
|
||||
m.F(A::LAST_INK_SHOOT_TIMER)=ConfigFloat("Phase 2.Ink Bullet Frequency");
|
||||
goto BulletShot;
|
||||
}else
|
||||
if(m.I(A::BULLET_COUNT_AFTER_INK_ATTACK)>ConfigInt("Phase 2.Homing Bullet Starts After")){
|
||||
if(m.I(A::ATTACK_COUNT)%ConfigInt("Phase 2.Homing Bullet Frequency")==0)CreateBullet(HomingBullet)(m.GetPos(),Entity{game->GetPlayer()},util::degToRad(ConfigFloat("Phase 2.Homing Bullet Rotation Speed")),util::degToRad(ConfigFloat("Phase 2.Homing Bullet Rotation Speed Player Covered In Ink")),ConfigFloat("Phase 2.Homing Bullet Lifetime"),ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Phase 2.Homing Bullet Color"),{ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Radius")})EndBullet;
|
||||
else
|
||||
if(m.I(A::ATTACK_COUNT)>=ConfigInt("Big Bullet Frequency")-1)CreateBurstBullet();
|
||||
else CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Bullet Color"),{ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Radius")})EndBullet;
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Shoot Frequency");
|
||||
goto BulletShot;
|
||||
}
|
||||
m.I(A::BULLET_COUNT_AFTER_INK_ATTACK)++;
|
||||
}
|
||||
if(m.I(A::ATTACK_COUNT)>=ConfigInt("Big Bullet Frequency")-1)CreateBurstBullet();
|
||||
else{
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Shoot Frequency");
|
||||
CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Bullet Color"),{ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Radius")})EndBullet;
|
||||
}
|
||||
BulletShot:
|
||||
m.F(A::LAST_SHOOT_TIMER)=1.f;
|
||||
m.PerformShootAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.I(A::ATTACK_COUNT)++;
|
||||
}
|
||||
if(m.F(A::LAST_SHOOT_TIMER)<=0.f){
|
||||
m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
}
|
||||
}break;
|
||||
case HURT_ANIMATION:{
|
||||
|
||||
}break;
|
||||
case DEAD:{}break;
|
||||
}
|
||||
}
|
@ -52,16 +52,17 @@ void Monster::STRATEGY::GOBLIN_BOAR_RIDER(Monster&m,float fElapsedTime,std::stri
|
||||
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,animation);
|
||||
if(firstAnimation)m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),animation);
|
||||
firstAnimation=false;
|
||||
}
|
||||
m.mountedSprOffset=ConfigVec("Imposed Monster Offset");
|
||||
m.deathData.emplace_back(ConfigString("Spawned Monster"),1U);
|
||||
m.deathData.push_back(DeathSpawnInfo{ConfigString("Spawned Monster"),1U});
|
||||
}
|
||||
|
||||
BOAR(m,fElapsedTime,"Boar");
|
||||
@ -78,11 +79,11 @@ void Monster::STRATEGY::GOBLIN_BOAR_RIDER(Monster&m,float fElapsedTime,std::stri
|
||||
|
||||
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,std::format("GOBLIN_BOW_MOUNTED_{}",int(m.GetFacingDirection())));
|
||||
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,std::format("GOBLIN_BOW_ATTACK_{}",int(m.GetFacingDirection())));
|
||||
m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),std::format("GOBLIN_BOW_ATTACK_{}",int(m.GetFacingDirection())));
|
||||
}
|
||||
}
|
@ -60,12 +60,12 @@ void Monster::STRATEGY::GOBLIN_BOMB(Monster&m,float fElapsedTime,std::string str
|
||||
RUN,
|
||||
};
|
||||
|
||||
switch(PHASE()){
|
||||
switch(m.phase){
|
||||
case INITIALIZE:{
|
||||
m.F(A::SHOOT_TIMER)=m.randomFrameOffset;
|
||||
SETPHASE(RUN);
|
||||
m.phase=RUN;
|
||||
}break;
|
||||
case RUN:{
|
||||
case RUN:{
|
||||
m.F(A::SHOOT_TIMER)+=fElapsedTime;
|
||||
m.F(A::SHOOT_ANIMATION_TIME)-=fElapsedTime;
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
|
@ -66,10 +66,10 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
switch(PHASE()){
|
||||
switch(m.phase){
|
||||
case INITIALIZE_PERCEPTION:{
|
||||
m.F(A::PERCEPTION_LEVEL)=ConfigFloat("Starting Perception Level");
|
||||
SETPHASE(MOVE);
|
||||
m.phase=MOVE;
|
||||
}break;
|
||||
case MOVE:{
|
||||
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
|
||||
@ -79,7 +79,7 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
const bool outsideMaxShootingRange=distToPlayer>=ConfigPixelsArr("Stand Still and Shoot Range",1);
|
||||
|
||||
auto PrepareToShoot=[&](){
|
||||
SETPHASE(LOCKON);
|
||||
m.phase=LOCKON;
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time");
|
||||
|
||||
m.PerformAnimation("SHOOT",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
@ -118,7 +118,7 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
m.V(A::EXTENDED_LINE)=extendedLine;
|
||||
Arrow tempArrow{m.GetPos(),extendedLine,pointTowardsPlayer.vector().norm()*ConfigFloat("Arrow Spd"),"goblin_arrow.png",ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel()};
|
||||
m.V(A::FIRE_VELOCITY)=tempArrow.PointToBestTargetPath(m.F(A::PERCEPTION_LEVEL));
|
||||
SETPHASE(WINDUP);
|
||||
m.phase=WINDUP;
|
||||
}
|
||||
}break;
|
||||
case WINDUP:{
|
||||
@ -128,7 +128,7 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
CreateBullet(Arrow)(m.GetPos(),m.V(A::EXTENDED_LINE),m.V(A::FIRE_VELOCITY),"goblin_arrow.png",ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet;
|
||||
m.F(A::PERCEPTION_LEVEL)=std::min(ConfigFloat("Maximum Perception Level"),m.F(A::PERCEPTION_LEVEL)+ConfigFloat("Perception Level Increase"));
|
||||
m.PerformAnimation("IDLE",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
SETPHASE(MOVE);
|
||||
m.phase=MOVE;
|
||||
}
|
||||
m.B(A::RANDOM_DIRECTION)=util::random()%2;
|
||||
m.F(A::RANDOM_RANGE)=util::random_range(ConfigPixelsArr("Random Direction Range",0),ConfigPixelsArr("Random Direction Range",1));
|
||||
|
@ -66,13 +66,13 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
|
||||
SLASH
|
||||
};
|
||||
|
||||
switch(PHASE()){
|
||||
switch(m.phase){
|
||||
case MOVE:{
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
if(distToPlayer>ConfigFloat("Attack Spacing")/100.f*24){
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}else{
|
||||
SETPHASE(WINDUP);
|
||||
m.phase=WINDUP;
|
||||
m.I(A::ATTACK_TYPE)=util::random()%2; //Choose randomly between stab or slash.
|
||||
switch(m.I(A::ATTACK_TYPE)){
|
||||
case STAB:{
|
||||
@ -90,18 +90,18 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
|
||||
case WINDUP:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){
|
||||
SETPHASE(RECOVERY);
|
||||
m.phase=RECOVERY;
|
||||
switch(m.I(A::ATTACK_TYPE)){
|
||||
case STAB:{
|
||||
vf2d stabTarget=game->GetPlayer()->GetPos();
|
||||
m.PerformAnimation("STABBING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
CreateBullet(DaggerStab)(m,ConfigString("Dagger Stab Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Stab Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(stabTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Stab Distance"),
|
||||
CreateBullet(DaggerStab)(m,ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Stab Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(stabTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Stab Distance"),
|
||||
DaggerStab::DirectionOffsets{ConfigVec("Dagger Up Offset"),ConfigVec("Dagger Down Offset"),ConfigVec("Dagger Right Offset"),ConfigVec("Dagger Left Offset")})EndBullet;
|
||||
}break;
|
||||
case SLASH:{
|
||||
vf2d slashTarget=game->GetPlayer()->GetPos();
|
||||
m.PerformAnimation("SLASHING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
CreateBullet(DaggerSlash)(m,ConfigString("Dagger Slash Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Slash Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(slashTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Slash Distance"))EndBullet;
|
||||
CreateBullet(DaggerSlash)(m,ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Slash Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(slashTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Slash Distance"))EndBullet;
|
||||
}break;
|
||||
default:ERR(std::format("WARNING! Invalid Attack type {} provided. THIS SHOULD NOT BE HAPPENING!",m.I(A::ATTACK_TYPE)));
|
||||
}
|
||||
@ -113,7 +113,7 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
m.F(A::RECOVERY_TIME)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));}
|
||||
if(m.F(A::RECOVERY_TIME)<=0)SETPHASE(MOVE);
|
||||
if(m.F(A::RECOVERY_TIME)<=0)m.phase=MOVE;
|
||||
}break;
|
||||
}
|
||||
}
|
@ -63,11 +63,11 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.SetZ(std::max(float(m.F(A::FLYING_HEIGHT)+ConfigFloat("Flight Oscillation Amount")*sin((PI*m.TimeSpentAlive())/1.5f)),0.f));
|
||||
#pragma endregion
|
||||
|
||||
switch(PHASE()){
|
||||
switch(m.phase){
|
||||
case INITIALIZE:{
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=m.F(A::FLYING_HEIGHT)=ConfigFloat("Flight Height")-ConfigFloat("Flight Height Variance")+util::random_range(0,ConfigFloat("Flight Height Variance")*2);
|
||||
m.B(A::RANDOM_DIRECTION)=int(util::random_range(0,2));
|
||||
SETPHASE(FLYING);
|
||||
m.phase=FLYING;
|
||||
m.AddBuff(BuffType::SELF_INFLICTED_SLOWDOWN,INFINITE,util::random_range(0,ConfigFloat("Flight Speed Variance")/100));
|
||||
m.F(A::ATTACK_COOLDOWN)=util::random_range(1.f,ConfigFloat("Flight Charge Cooldown"));
|
||||
}break;
|
||||
@ -76,7 +76,7 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
if(m.F(A::FLYING_HEIGHT)<m.F(A::TARGET_FLYING_HEIGHT))m.F(A::FLYING_HEIGHT)=std::min(m.F(A::TARGET_FLYING_HEIGHT),m.F(A::FLYING_HEIGHT)+ConfigFloat("Attack Z Speed")*fElapsedTime);
|
||||
if(m.F(A::ATTACK_COOLDOWN)<=0){
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Attack Wait Time");
|
||||
SETPHASE(PREPARE_CHARGE);
|
||||
m.phase=PREPARE_CHARGE;
|
||||
}else{
|
||||
float dirToPlayer{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y};
|
||||
dirToPlayer+=m.B(A::RANDOM_DIRECTION)?0.25*PI:-0.25*PI;
|
||||
@ -89,7 +89,7 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.UpdateFacingDirection(game->GetPlayer()->GetPos());
|
||||
m.PerformAnimation("ATTACK");
|
||||
if(m.F(A::CASTING_TIMER)<=0){
|
||||
SETPHASE(CHARGE);
|
||||
m.phase=CHARGE;
|
||||
m.PerformAnimation("ATTACKING");
|
||||
m.target=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).rpoint(ConfigFloat("Flight Distance")*2.f);
|
||||
m.UpdateFacingDirection(m.target);
|
||||
@ -103,7 +103,7 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.target).length();
|
||||
if(distToTarget<12.f){
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=ConfigFloat("Flight Height")-ConfigFloat("Flight Height Variance")+util::random_range(0,ConfigFloat("Flight Height Variance")*2);
|
||||
SETPHASE(FLYING);
|
||||
m.phase=FLYING;
|
||||
m.F(A::ATTACK_COOLDOWN)=ConfigFloat("Flight Charge Cooldown");
|
||||
m.PerformJumpAnimation();
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
#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"
|
||||
|
||||
HomingBullet::HomingBullet(const vf2d pos,const Entity target,const float rotateTowardsSpeed,const float rotateTowardsSpeedCoveredInInk,const float lifetime,const float speed,const float radius,const int damage,const bool upperLevel,const bool friendly,const Pixel col,const vf2d scale,const float image_angle)
|
||||
:Bullet(pos,util::pointTo(pos,target.GetPos())*speed,radius,damage,upperLevel,friendly,col,scale,image_angle),target(target),rotateTowardsSpeed(rotateTowardsSpeed),rotateTowardsSpeedCoveredInInk(rotateTowardsSpeedCoveredInInk){
|
||||
this->lifetime=lifetime;
|
||||
}
|
||||
void HomingBullet::Update(float fElapsedTime){
|
||||
const float magnitude{vel.mag()};
|
||||
float bulletAngle{vel.polar().y};
|
||||
float bulletRotationSpeed{rotateTowardsSpeed};
|
||||
if(game->GetPlayer()->CoveredInInk())bulletRotationSpeed=rotateTowardsSpeedCoveredInInk;
|
||||
if(util::distance(pos,game->GetPlayer()->GetPos())>=7*24.f)bulletRotationSpeed=0.f; //Don't rotate if basically offscreen.
|
||||
util::turn_towards_target(bulletAngle,pos,target.GetPos(),bulletRotationSpeed,fElapsedTime);
|
||||
vel=vf2d{magnitude,bulletAngle}.cart();
|
||||
}
|
||||
void HomingBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user