Compare commits
139 Commits
@ -0,0 +1,113 @@ |
|||||||
|
#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(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
#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.
|
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
#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(); |
||||||
|
}; |
@ -0,0 +1,104 @@ |
|||||||
|
#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.
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
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. |
||||||
|
|
@ -0,0 +1,118 @@ |
|||||||
|
#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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
#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!"); |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
#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; |
||||||
|
}; |
@ -0,0 +1,199 @@ |
|||||||
|
#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, |
||||||
|
}; |
||||||
|
|
||||||
|
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 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)--; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
m.F(A::GHOST_SABER_TIMER)-=fElapsedTime; |
||||||
|
|
||||||
|
if(m.B(A::FIRST_WAVE_COMPLETE)){ |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch(PHASE()){ |
||||||
|
enum CannonPhaseType{ |
||||||
|
CANNON_SHOT, |
||||||
|
SILENCE, |
||||||
|
SHRAPNEL_SHOT, |
||||||
|
}; |
||||||
|
case INIT:{ |
||||||
|
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; |
||||||
|
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; |
||||||
|
} |
||||||
|
}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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
#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){} |
@ -0,0 +1,84 @@ |
|||||||
|
#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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,179 @@ |
|||||||
|
#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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
#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){} |
@ -0,0 +1,130 @@ |
|||||||
|
#pragma region License |
||||||
|
/*
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com> |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, |
||||||
|
are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above |
||||||
|
copyright notice. This list of conditions and the following disclaimer must be |
||||||
|
reproduced in the documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may |
||||||
|
be used to endorse or promote products derived from this software without specific |
||||||
|
prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||||
|
SUCH DAMAGE. |
||||||
|
|
||||||
|
Portions of this software are copyright © 2024 The FreeType |
||||||
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. |
||||||
|
All rights reserved. |
||||||
|
*/ |
||||||
|
#pragma endregion |
||||||
|
|
||||||
|
#include "Menu.h" |
||||||
|
#include "MenuComponent.h" |
||||||
|
#include "GameState.h" |
||||||
|
#include "MenuLabel.h" |
||||||
|
#include "AdventuresInLestoria.h" |
||||||
|
#include "CharacterRotatingDisplay.h" |
||||||
|
#include "ClassInfo.h" |
||||||
|
#include "Unlock.h" |
||||||
|
|
||||||
|
INCLUDE_game |
||||||
|
INCLUDE_GFX |
||||||
|
|
||||||
|
void Menu::InitializeHubPauseWindow(){ |
||||||
|
Menu*hubPauseWindow=CreateMenu(MenuType::HUB_PAUSE,CENTERED,vi2d{96,168}); |
||||||
|
|
||||||
|
hubPauseWindow->ADD("Resume Button",MenuComponent)(geom2d::rect<float>{{6.f,0.f},{84.f,24.f}},"Resume",[](MenuFuncData data){void ClearGarbage(); |
||||||
|
game->ClearGarbage(); |
||||||
|
Menu::CloseMenu(); |
||||||
|
return true; |
||||||
|
},ButtonAttr::FIT_TO_LABEL)END; |
||||||
|
hubPauseWindow->ADD("Character Button",MenuComponent)(geom2d::rect<float>{{6.f,28.f},{84.f,24.f}},"Character",[](MenuFuncData data){ |
||||||
|
Component<CharacterRotatingDisplay>(CHARACTER_MENU,"Character Rotating Display")->SetIcon(GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal()); |
||||||
|
Component<MenuComponent>(CHARACTER_MENU,"Equip Selection Select Button")->Click(); |
||||||
|
Menu::OpenMenu(CHARACTER_MENU); |
||||||
|
return true; |
||||||
|
},ButtonAttr::FIT_TO_LABEL)END; |
||||||
|
hubPauseWindow->ADD("Change Loadout Button",MenuComponent)(geom2d::rect<float>{{6.f,56.f},{84.f,24.f}},"Change\nLoadout",[](MenuFuncData data){ |
||||||
|
Menu::OpenMenu(ITEM_HUB_LOADOUT); |
||||||
|
return true; |
||||||
|
},ButtonAttr::FIT_TO_LABEL)END; |
||||||
|
hubPauseWindow->ADD("Inventory Button",MenuComponent)(geom2d::rect<float>{{6.f,84.f},{84.f,24.f}},"Inventory",[](MenuFuncData data){ |
||||||
|
Menu::OpenMenu(INVENTORY); |
||||||
|
return true; |
||||||
|
},ButtonAttr::FIT_TO_LABEL)END; |
||||||
|
hubPauseWindow->ADD("Settings Button",MenuComponent)(geom2d::rect<float>{{6.f,112.f},{84.f,24.f}},"Settings",[](MenuFuncData data){ |
||||||
|
Menu::OpenMenu(SETTINGS); |
||||||
|
return true; |
||||||
|
},ButtonAttr::FIT_TO_LABEL)END; |
||||||
|
hubPauseWindow->ADD("Return to Camp Button",MenuComponent)(geom2d::rect<float>{{6.f,140.f},{84.f,24.f}},"Leave Area",[](MenuFuncData data){ |
||||||
|
Component<MenuLabel>(LEVEL_COMPLETE,"Stage Complete Label")->SetLabel("Stage Summary"); |
||||||
|
Component<MenuComponent>(LEVEL_COMPLETE,"Level Details Outline")->SetLabel(""); |
||||||
|
|
||||||
|
if(game->GetCurrentMapName()=="HUB"&&game->PreviousStageCompleted())Unlock::UnlockCurrentMap(); //Special unlock for the hub area when leaving.
|
||||||
|
|
||||||
|
if(Unlock::IsUnlocked("STORY_1_1")){ |
||||||
|
Component<MenuComponent>(LEVEL_COMPLETE,"Next Button")->Enable(); |
||||||
|
}else{ |
||||||
|
Component<MenuComponent>(LEVEL_COMPLETE,"Next Button")->Disable(); |
||||||
|
} |
||||||
|
if(GameState::STATE==GameState::states[States::GAME_HUB]){ |
||||||
|
GameState::ChangeState(States::OVERWORLD_MAP,0.4f); |
||||||
|
}else{ |
||||||
|
GameState::ChangeState(States::LEVEL_COMPLETE,0.4f); |
||||||
|
} |
||||||
|
return true; |
||||||
|
},ButtonAttr::FIT_TO_LABEL)END; |
||||||
|
|
||||||
|
hubPauseWindow->SetupKeyboardNavigation( |
||||||
|
[](MenuType type,Data&returnData){ //On Open
|
||||||
|
returnData="Resume Button"; |
||||||
|
}, |
||||||
|
{ //Button Key
|
||||||
|
{game->KEY_SCROLL,{"Navigate",[](MenuType type){}}}, |
||||||
|
{game->KEY_BACK,{"Resume",[](MenuType type){ |
||||||
|
Component<MenuComponent>(type,"Resume Button")->Click(); |
||||||
|
}}}, |
||||||
|
{game->KEY_MENU,{"Resume",[](MenuType type){ |
||||||
|
Component<MenuComponent>(type,"Resume Button")->Click(); |
||||||
|
}}}, |
||||||
|
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}}, |
||||||
|
} |
||||||
|
,{ //Button Navigation Rules
|
||||||
|
{"Resume Button",{ |
||||||
|
.up="Return to Camp Button", |
||||||
|
.down="Character Button",}}, |
||||||
|
{"Character Button",{ |
||||||
|
.up="Resume Button", |
||||||
|
.down="Change Loadout Button",}}, |
||||||
|
{"Change Loadout Button",{ |
||||||
|
.up="Character Button", |
||||||
|
.down="Inventory Button",}}, |
||||||
|
{"Inventory Button",{ |
||||||
|
.up="Change Loadout Button", |
||||||
|
.down="Settings Button",}}, |
||||||
|
{"Settings Button",{ |
||||||
|
.up="Inventory Button", |
||||||
|
.down="Return to Camp Button",}}, |
||||||
|
{"Return to Camp Button",{ |
||||||
|
.up="Settings Button", |
||||||
|
.down="Resume Button",}}, |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
#pragma region License |
||||||
|
/*
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com> |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, |
||||||
|
are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above |
||||||
|
copyright notice. This list of conditions and the following disclaimer must be |
||||||
|
reproduced in the documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may |
||||||
|
be used to endorse or promote products derived from this software without specific |
||||||
|
prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||||
|
SUCH DAMAGE. |
||||||
|
|
||||||
|
Portions of this software are copyright © 2024 The FreeType |
||||||
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. |
||||||
|
All rights reserved. |
||||||
|
*/ |
||||||
|
#pragma endregion |
||||||
|
|
||||||
|
#include "AdventuresInLestoria.h" |
||||||
|
|
||||||
|
INCLUDE_game |
||||||
|
|
||||||
|
Ink::Ink(vf2d pos,float radius,float lifetime,float inkSlowdownDuration,float inkSlowdownPct,float fadeinTime,float fadeoutTime,vf2d size,Pixel col,bool onUpperLevel,float rotation) |
||||||
|
:Effect(pos,lifetime,"ink.png",onUpperLevel,fadeinTime,fadeoutTime,size,{},EffectType::NONE,col,rotation,0.f,false),radius(radius),inkSlowdownDuration(inkSlowdownDuration),inkSlowdownAmount(inkSlowdownPct){} |
||||||
|
bool Ink::Update(float fElapsedTime){ |
||||||
|
const float distToPlayer{util::distance(game->GetPlayer()->GetPos(),pos)}; |
||||||
|
if(distToPlayer<=radius){ |
||||||
|
Buff¤tInkSlowdown{game->GetPlayer()->GetOrAddBuff(BuffType::INK_SLOWDOWN,{inkSlowdownDuration,inkSlowdownAmount})}; |
||||||
|
currentInkSlowdown.duration=inkSlowdownDuration; //Reset the duration if needed.
|
||||||
|
} |
||||||
|
return Effect::Update(fElapsedTime); |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
#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_ANIMATION_DATA |
||||||
|
|
||||||
|
InkBullet::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) |
||||||
|
:Bullet(pos,vel,inkExplosionRadius,0,"inkbullet.png",upperLevel,friendly),targetPos(targetPos),inkExplosionRadius(inkExplosionRadius),inkSlowdownPct(inkSlowdownPct),inkSlowdownDuration(inkSlowdownDuration),inkPuddleLifetime(inkPuddleLifetime),inkPuddleRadius(inkPuddleRadius){ |
||||||
|
additiveBlending=true; |
||||||
|
} |
||||||
|
void InkBullet::Update(float fElapsedTime){ |
||||||
|
const float magnitude{vel.mag()}; |
||||||
|
vel=util::pointTo(pos,targetPos)*magnitude; |
||||||
|
if(util::distance(pos,targetPos)<=8.f){Splat();} |
||||||
|
else if(GetAliveTime()>=30.f)ERR(std::format("WARNING! An Ink Bullet did not resolve to target position {} in time! Last reported pos: {}! THE DISTANCE THRESHOLD FOR INK EXPLOSIONS IS LIKELY TOO HIGH! THIS SHOULD NOT BE HAPPENING!",targetPos.str(),pos.str())); //NOTE: THIS SHOULD NOT BE HAPPENING! If this does, then we probably just set the distance threshold WAY TOO LOW! Let's crash for now. |
||||||
|
} |
||||||
|
void InkBullet::Splat(){ |
||||||
|
game->AddEffect(std::make_unique<Effect>(pos,ANIMATION_DATA["inkbubble_explode.png"].GetTotalAnimationDuration(),"inkbubble_explode.png",OnUpperLevel(),vf2d{1.f,1.f},0.f,vf2d{},WHITE,0.f,0.f,additiveBlending)); |
||||||
|
game->AddEffect(std::make_unique<Ink>(pos,inkPuddleRadius,inkPuddleLifetime,inkSlowdownDuration,inkSlowdownPct,0.4f,0.4f,vf2d{1.f,1.f},WHITE,OnUpperLevel(),int(util::random_range(0,4.f))*PI/2),true); |
||||||
|
lifetime=0.f; |
||||||
|
} |
||||||
|
BulletDestroyState InkBullet::PlayerHit(Player*player){ |
||||||
|
Splat(); |
||||||
|
return BulletDestroyState::DESTROY; |
||||||
|
} |
||||||
|
BulletDestroyState InkBullet::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ |
||||||
|
Splat(); |
||||||
|
return BulletDestroyState::DESTROY; |
||||||
|
} |
||||||
|
void InkBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){} |
@ -0,0 +1,148 @@ |
|||||||
|
#pragma region License |
||||||
|
/*
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com> |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, |
||||||
|
are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above |
||||||
|
copyright notice. This list of conditions and the following disclaimer must be |
||||||
|
reproduced in the documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may |
||||||
|
be used to endorse or promote products derived from this software without specific |
||||||
|
prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||||
|
SUCH DAMAGE. |
||||||
|
|
||||||
|
Portions of this software are copyright © 2024 The FreeType |
||||||
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. |
||||||
|
All rights reserved. |
||||||
|
*/ |
||||||
|
#pragma endregion |
||||||
|
#include "AdventuresInLestoria.h" |
||||||
|
#include "DEFINES.h" |
||||||
|
#include "Menu.h" |
||||||
|
#include "MenuLabel.h" |
||||||
|
#include "MenuItemLoadoutButton.h" |
||||||
|
#include "State_OverworldMap.h" |
||||||
|
|
||||||
|
INCLUDE_game |
||||||
|
using A=Attribute; |
||||||
|
|
||||||
|
void Menu::InitializeItemHubLoadoutWindow(){ |
||||||
|
Menu*itemHubLoadoutWindow=CreateMenu(ITEM_HUB_LOADOUT,CENTERED,game->GetScreenSize()-vi2d{4,4}); |
||||||
|
|
||||||
|
float itemLoadoutWindowWidth=(game->GetScreenSize().x-5.f); |
||||||
|
|
||||||
|
itemHubLoadoutWindow->ADD("Loadout Label",MenuLabel)(geom2d::rect<float>{{0,24},{itemLoadoutWindowWidth,24}},"Setup Item Loadout",2,ComponentAttr::SHADOW|ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE)END; |
||||||
|
|
||||||
|
itemHubLoadoutWindow->ADD("Loadout Description Label",MenuLabel)(geom2d::rect<float>{{0,58},{itemLoadoutWindowWidth,24}},"Bring up to 3 items with you.",1,ComponentAttr::SHADOW)END; |
||||||
|
|
||||||
|
float buttonBorderPadding=64; |
||||||
|
|
||||||
|
itemHubLoadoutWindow->ADD("Loadout Item 1",MenuItemLoadoutButton)(geom2d::rect<float>{{64,90},{48,48}},0,[](MenuFuncData data){ |
||||||
|
Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=0;
|
||||||
|
Menu::OpenMenu(INVENTORY_CONSUMABLES); |
||||||
|
return true; |
||||||
|
},[](MenuFuncData data){ |
||||||
|
std::weak_ptr<MenuItemLoadoutButton>loadoutButton=DYNAMIC_POINTER_CAST<MenuItemLoadoutButton>(data.component); |
||||||
|
if(!ISBLANK(loadoutButton.lock()->GetItem())){ |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Name Label")->SetLabel(loadoutButton.lock()->GetItem().lock()->DisplayName()); |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Description")->SetLabel(loadoutButton.lock()->GetItem().lock()->Description()); |
||||||
|
} |
||||||
|
return true; |
||||||
|
},[](MenuFuncData data){ |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Name Label")->SetLabel(""); |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Description")->SetLabel(""); |
||||||
|
return true; |
||||||
|
})END |
||||||
|
->SetIconScale({2.f,2.f}); |
||||||
|
itemHubLoadoutWindow->ADD("Loadout Item 2",MenuItemLoadoutButton)(geom2d::rect<float>{{itemLoadoutWindowWidth/2-24,90},{48,48}},1,[](MenuFuncData data){ |
||||||
|
Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=1; |
||||||
|
Menu::OpenMenu(INVENTORY_CONSUMABLES); |
||||||
|
return true; |
||||||
|
},[](MenuFuncData data){ |
||||||
|
std::weak_ptr<MenuItemLoadoutButton>loadoutButton=DYNAMIC_POINTER_CAST<MenuItemLoadoutButton>(data.component); |
||||||
|
if(!ISBLANK(loadoutButton.lock()->GetItem())){ |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Name Label")->SetLabel(loadoutButton.lock()->GetItem().lock()->DisplayName()); |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Description")->SetLabel(loadoutButton.lock()->GetItem().lock()->Description()); |
||||||
|
} |
||||||
|
return true; |
||||||
|
},[](MenuFuncData data){ |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Name Label")->SetLabel(""); |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Description")->SetLabel(""); |
||||||
|
return true; |
||||||
|
})END |
||||||
|
->SetIconScale({2.f,2.f}); |
||||||
|
itemHubLoadoutWindow->ADD("Loadout Item 3",MenuItemLoadoutButton)(geom2d::rect<float>{{itemLoadoutWindowWidth-48-64,90},{48,48}},2,[](MenuFuncData data){ |
||||||
|
Menu::menus.at(INVENTORY_CONSUMABLES)->I(A::LOADOUT_SLOT)=2; |
||||||
|
Menu::OpenMenu(INVENTORY_CONSUMABLES); |
||||||
|
return true; |
||||||
|
},[](MenuFuncData data){ |
||||||
|
std::weak_ptr<MenuItemLoadoutButton>loadoutButton=DYNAMIC_POINTER_CAST<MenuItemLoadoutButton>(data.component); |
||||||
|
if(!ISBLANK(loadoutButton.lock()->GetItem())){ |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Name Label")->SetLabel(loadoutButton.lock()->GetItem().lock()->DisplayName()); |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Description")->SetLabel(loadoutButton.lock()->GetItem().lock()->Description()); |
||||||
|
} |
||||||
|
return true; |
||||||
|
},[](MenuFuncData data){ |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Name Label")->SetLabel(""); |
||||||
|
Component<MenuLabel>(ITEM_HUB_LOADOUT,"Item Description")->SetLabel(""); |
||||||
|
return true; |
||||||
|
})END |
||||||
|
->SetIconScale({2.f,2.f}); |
||||||
|
|
||||||
|
itemHubLoadoutWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{0,146},{itemLoadoutWindowWidth,12}},"",1,ComponentAttr::SHADOW)END; |
||||||
|
itemHubLoadoutWindow->ADD("Item Description",MenuLabel)(geom2d::rect<float>{{0,158},{itemLoadoutWindowWidth,24}},"",1,ComponentAttr::SHADOW)END; |
||||||
|
|
||||||
|
itemHubLoadoutWindow->ADD("Back Button",MenuComponent)(geom2d::rect<float>{{itemLoadoutWindowWidth/2-32,202},{64,16}},"Back",[](MenuFuncData data){Menu::CloseMenu();return true;})END; |
||||||
|
|
||||||
|
itemHubLoadoutWindow->SetupKeyboardNavigation( |
||||||
|
[](MenuType type,Data&returnData){ //On Open
|
||||||
|
returnData="Loadout Item 1"; |
||||||
|
}, |
||||||
|
{ //Button Key
|
||||||
|
{game->KEY_BACK,{"Back",[](MenuType type){ |
||||||
|
Component<MenuComponent>(type,"Back Button")->Click(); |
||||||
|
}}}, |
||||||
|
{game->KEY_CONFIRM,{"Select",[](MenuType type){}}}, |
||||||
|
} |
||||||
|
,{ //Button Navigation Rules
|
||||||
|
{"Loadout Item 1",{ |
||||||
|
.up="Back Button", |
||||||
|
.down="Back Button", |
||||||
|
.left="Loadout Item 3", |
||||||
|
.right="Loadout Item 2",}}, |
||||||
|
{"Loadout Item 2",{ |
||||||
|
.up="Back Button", |
||||||
|
.down="Back Button", |
||||||
|
.left="Loadout Item 1", |
||||||
|
.right="Loadout Item 3",}}, |
||||||
|
{"Loadout Item 3",{ |
||||||
|
.up="Back Button", |
||||||
|
.down="Back Button", |
||||||
|
.left="Loadout Item 2", |
||||||
|
.right="Loadout Item 1",}}, |
||||||
|
{"Back Button",{ |
||||||
|
.up="Loadout Item 1", |
||||||
|
.down="Loadout Item 1", |
||||||
|
.left="Back Button", |
||||||
|
.right="Back Button",}}, |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
#pragma region License |
||||||
|
/*
|
||||||
|
License (OLC-3) |
||||||
|
~~~~~~~~~~~~~~~ |
||||||
|
|
||||||
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com> |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, |
||||||
|
are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
1. Redistributions or derivations of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions or derivative works in binary form must reproduce the above |
||||||
|
copyright notice. This list of conditions and the following disclaimer must be |
||||||
|
reproduced in the documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may |
||||||
|
be used to endorse or promote products derived from this software without specific |
||||||
|
prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||||
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||||
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||||
|
SUCH DAMAGE. |
||||||
|
|
||||||
|
Portions of this software are copyright © 2024 The FreeType |
||||||
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information. |
||||||
|
All rights reserved. |
||||||
|
*/ |
||||||
|
#pragma endregion |
||||||
|
#include "AdventuresInLestoria.h" |
||||||
|
#include "BulletTypes.h" |
||||||
|
|
||||||
|
INCLUDE_ITEM_SCRIPTS |
||||||
|
INCLUDE_game |
||||||
|
INCLUDE_ANIMATION_DATA |
||||||
|
|
||||||
|
void ItemInfo::InitializeScripts(){ |
||||||
|
|
||||||
|
ITEM_SCRIPTS["Restore"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){ |
||||||
|
for(const auto&[propName,buffType]:NameToBuffType){ |
||||||
|
int restoreAmt=props.GetInt(propName); |
||||||
|
|
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::ONE_OFF,NameToBuffType.at(propName),0.01f,float(restoreAmt),0.0f); |
||||||
|
if(restoreAmt>0&&props.PropCount(propName)==3){ |
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::OVER_TIME,NameToBuffType.at(propName),props.GetFloat(propName,2),float(restoreAmt),props.GetFloat(propName,1)); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
}; |
||||||
|
|
||||||
|
for(auto&[key,value]:ItemAttribute::attributes){ |
||||||
|
if(!DATA.GetProperty("ItemScript.Buff").HasProperty(key)){ |
||||||
|
ERR("WARNING! Buff Item Script does not support Buff "<<std::quoted(value.Name())<<"!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ITEM_SCRIPTS["Buff"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){ |
||||||
|
for(auto&[key,value]:ItemAttribute::attributes){ |
||||||
|
float intensity=props.GetFloat(key,0); |
||||||
|
if(intensity==0.f)continue; |
||||||
|
if(ItemAttribute::Get(key).DisplayAsPercent())intensity/=100; |
||||||
|
game->GetPlayer()->AddBuff(BuffType::STAT_UP,props.GetFloat(key,1),intensity,{ItemAttribute::Get(key)}); |
||||||
|
} |
||||||
|
return true; |
||||||
|
}; |
||||||
|
ITEM_SCRIPTS["RestoreDuringCast"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){ |
||||||
|
for(const auto&[propName,buffType]:NameToBuffType){ |
||||||
|
int restoreAmt=props.GetInt(propName); |
||||||
|
|
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::ONE_OFF,NameToBuffType.at(propName),0.01f,float(restoreAmt),0.0f); |
||||||
|
if(restoreAmt>0&&props.PropCount(propName)==3){ |
||||||
|
game->GetPlayer()->AddBuff(BuffRestorationType::OVER_TIME_DURING_CAST,NameToBuffType.at(propName),props.GetFloat(propName,2),float(restoreAmt),props.GetFloat(propName,1)); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
}; |
||||||
|
ITEM_SCRIPTS["Projectile"]=[](AiL*game,std::optional<vf2d>targetingPos,ItemProps props){ |
||||||
|
const int projectileDamage{props.GetInt("Base Damage")+int(game->GetPlayer()->GetAttack()*props.GetFloat("Player Damage Mult"))}; |
||||||
|
std::optional<LingeringEffect>lingeringEffect{}; |
||||||
|
if(props.GetFloat("Linger Time")>0.f){ |
||||||
|
const int damage{props.GetInt("Tick Base Damage")+int(props.GetFloat("Tick Player Damage Mult")*game->GetPlayer()->GetAttack())}; |
||||||
|
lingeringEffect.emplace(vf2d{},props.GetString("Lingering Effect"),props.GetString("Lingering Sound"),props.GetFloat("Linger Radius")/100.f*24,damage,props.GetFloat("Tick Rate"),HurtType::MONSTER,props.GetFloat("Linger Time"),5.f,game->GetPlayer()->OnUpperLevel(),1.f,vf2d{},DARK_RED,util::random(2*PI),0.f,false,0.2f, |
||||||
|
[](const Effect&self){ |
||||||
|
return Effect{self.pos,0.5f,"fire_ring.png",self.OnUpperLevel(),util::random_range(0.7f,1.f),0.1f,{},PixelLerp(Pixel(0xF7B752),Pixel(0xE74F30),util::random(1.f)),util::random(2*PI),0.f,true}; |
||||||
|
}); |
||||||
|
} |
||||||
|
CreateBullet(ThrownProjectile)(game->GetPlayer()->GetPos(),targetingPos.value(),props.GetString("Image"),props.GetFloat("Cast Size")/100.f*24,game->GetPlayer()->GetZ(),0.3f,8.f,projectileDamage,game->GetPlayer()->OnUpperLevel(),false,INFINITE,true,WHITE,props.GetFloat("Cast Size")/100.f*vf2d{1.f,1.f},0.f, |
||||||
|
Effect{vf2d{},ANIMATION_DATA.at("explosionframes.png").GetTotalAnimationDuration(),"explosionframes.png",game->GetPlayer()->OnUpperLevel(),props.GetFloat("Cast Size")/100.f*vf2d{1.f,1.f}},props.GetString("Explode Sound Effect"),lingeringEffect)EndBullet; |
||||||
|
return true; |
||||||
|
}; |
||||||
|
|
||||||
|
ITEM_SCRIPTS.SetInitialized(); |
||||||
|
LOG(ITEM_SCRIPTS.size()<<" item scripts have been loaded."); |
||||||
|
} |
@ -0,0 +1,221 @@ |
|||||||
|
#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 "Arc.h" |
||||||
|
|
||||||
|
using A=Attribute; |
||||||
|
|
||||||
|
INCLUDE_game |
||||||
|
INCLUDE_MONSTER_LIST |
||||||
|
INCLUDE_MONSTER_DATA |
||||||
|
|
||||||
|
void Monster::STRATEGY::OCTOPUS_ARM(Monster&m,float fElapsedTime,std::string strategy){ |
||||||
|
enum PhaseName{ |
||||||
|
INIT, |
||||||
|
RISE_ANIMATION, |
||||||
|
SEARCH, |
||||||
|
PREPARE_ATTACK, |
||||||
|
ATTACK_ANIMATION, |
||||||
|
ATTACK_RECOVERY, |
||||||
|
SUBMERGE, |
||||||
|
}; |
||||||
|
|
||||||
|
const auto GetAttackArc=[attackRadius=ConfigFloat("Attack Radius"),attackArc=ConfigFloat("Attack Arc")](const Monster&m){ |
||||||
|
return Arc{m.GetPos(),attackRadius/100.f*24,util::dirToAngle(m.GetFacingDirection()),util::degToRad(attackArc)}; |
||||||
|
}; |
||||||
|
|
||||||
|
if(m.ANY(A::STORED_ARC).has_value()){ |
||||||
|
game->DrawShadowStringDecal({100,100},std::format("Stored Arc Active: {}",std::any_cast<Arc>(m.ANY(A::STORED_ARC)).pos.str())); |
||||||
|
const float growthRate=((ConfigFloat("Attack Radius")/100.f*24)/ConfigFloat("Attack Effect Time"))*fElapsedTime; |
||||||
|
std::any_cast<Arc>(m.ANY(A::STORED_ARC)).GrowRadius(growthRate); |
||||||
|
m.F(A::ENVIRONMENT_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::ENVIRONMENT_TIMER)<=0.f)m.ANY(A::STORED_ARC).reset(); |
||||||
|
} |
||||||
|
|
||||||
|
fElapsedTime*=m.F(A::ATTACK_ANIMATION_SPEED); |
||||||
|
|
||||||
|
switch(PHASE()){ |
||||||
|
case INIT:{ |
||||||
|
m.I(A::SUBMERGE_STRAT_ID)=SUBMERGE; |
||||||
|
m.F(A::ATTACK_ANIMATION_SPEED)=1.f; |
||||||
|
if(ConfigFloat("Attack Swing Damage Wait Time")>m.GetAnimation("ATTACKING").GetTotalAnimationDuration())ERR(std::format("The Attack Swing Damage Wait Time ({}s) should not be greater than the total attack time animation duration! ({}s)",ConfigFloat("Attack Swing Damage Wait Time"),m.GetAnimation("ATTACKING").GetTotalAnimationDuration())); |
||||||
|
m.PerformAnimation("RISE",game->GetPlayer()->GetPos()); |
||||||
|
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
SETPHASE(RISE_ANIMATION); |
||||||
|
m.SetStrategyDeathFunction([deathFadeTime=ConfigFloat("Death Fade Time"),bossDamageOnDeath=ConfigInt("Boss Damage On Death"),armHealAmtOnDeath=ConfigInt("Arm Heal Amount On Death"),reemergeWaitTime=ConfigFloat("Re-Emerge Wait Time"),delayTimeIncreasePerArm=ConfigFloat("Delay Time Increase Per Arm"),armAttackBuffOnDeath=ConfigFloat("Arm Attack Buff On Death"),bossAttackBuffOnDeath=ConfigFloat("Boss Attack Buff On Death")](GameEvent&event,Monster&m,const StrategyName&strategyName){ |
||||||
|
m.SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){}); |
||||||
|
const std::string GIANT_OCTOPUS_NAME{"Giant Octopus"}; |
||||||
|
const std::string OCTOPUS_ARM_NAME{"Octopus Arm"}; |
||||||
|
|
||||||
|
if(!MONSTER_DATA.count(GIANT_OCTOPUS_NAME))ERR(std::format("WARNING! {} does not exist on the map! THIS SHOULD NOT BE HAPPENING!",GIANT_OCTOPUS_NAME)); |
||||||
|
|
||||||
|
auto boss{std::find_if(MONSTER_LIST.begin(),MONSTER_LIST.end(),[&GIANT_OCTOPUS_NAME](const std::shared_ptr<Monster>&m){ |
||||||
|
return m->GetName()==GIANT_OCTOPUS_NAME; |
||||||
|
})}; |
||||||
|
if(boss!=MONSTER_LIST.end()){ |
||||||
|
Monster&bossMonster{**boss}; |
||||||
|
bossMonster._DealTrueDamage(bossDamageOnDeath); |
||||||
|
} |
||||||
|
float delayTimePerArm{0.f}; |
||||||
|
std::for_each(MONSTER_LIST.begin(),MONSTER_LIST.end(),[&OCTOPUS_ARM_NAME,&GIANT_OCTOPUS_NAME,&strategyName,&armHealAmtOnDeath,&reemergeWaitTime,&delayTimeIncreasePerArm,&delayTimePerArm,&armAttackBuffOnDeath,&bossAttackBuffOnDeath](const std::shared_ptr<Monster>&m){ |
||||||
|
if(m->GetName()==OCTOPUS_ARM_NAME){ |
||||||
|
m->PerformAnimation("SUBMERGE"); |
||||||
|
m->SetPhase(strategyName,SUBMERGE); |
||||||
|
m->V(A::JUMP_TARGET_POS)=m->GetPos(); |
||||||
|
m->SetCollisionRadius(0.f); |
||||||
|
if(!m->IsDead())m->Heal(armHealAmtOnDeath,true); |
||||||
|
m->GetFloat(A::RECOVERY_TIME)=reemergeWaitTime+delayTimePerArm; |
||||||
|
m->SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){}); |
||||||
|
if(m->HasBuff(BuffType::STAT_UP)){ |
||||||
|
bool found{false}; |
||||||
|
for(Buff&b:m->EditBuffs(BuffType::STAT_UP)){ |
||||||
|
for(const ItemAttribute&attr:b.attr){ |
||||||
|
if(attr.ActualName()=="Attack %"){ |
||||||
|
found=true; |
||||||
|
b.intensity+=armAttackBuffOnDeath/100.f; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if(!found)m->AddBuff(BuffType::STAT_UP,INFINITE,armAttackBuffOnDeath/100.f,{"Attack %"}); |
||||||
|
}else m->AddBuff(BuffType::STAT_UP,INFINITE,armAttackBuffOnDeath/100.f,{"Attack %"}); |
||||||
|
delayTimePerArm+=delayTimeIncreasePerArm; |
||||||
|
}else if(m->GetName()==GIANT_OCTOPUS_NAME){ |
||||||
|
if(m->HasBuff(BuffType::STAT_UP)){ |
||||||
|
bool found{false}; |
||||||
|
for(Buff&b:m->EditBuffs(BuffType::STAT_UP)){ |
||||||
|
for(const ItemAttribute&attr:b.attr){ |
||||||
|
if(attr.ActualName()=="Attack %"){ |
||||||
|
found=true; |
||||||
|
b.intensity+=bossAttackBuffOnDeath/100.f; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if(!found)m->AddBuff(BuffType::STAT_UP,INFINITE,bossAttackBuffOnDeath/100.f,{"Attack %"}); |
||||||
|
}else m->AddBuff(BuffType::STAT_UP,INFINITE,bossAttackBuffOnDeath/100.f,{"Attack %"}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
m.SetLifetime(deathFadeTime); |
||||||
|
return false; |
||||||
|
}); |
||||||
|
}break; |
||||||
|
case RISE_ANIMATION:{ |
||||||
|
m.F(A::CASTING_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::CASTING_TIMER)<=0.f){ |
||||||
|
m.PerformAnimation("IDLE",game->GetPlayer()->GetPos()); |
||||||
|
m.SetCollisionRadius(m.GetOriginalCollisionRadius()); |
||||||
|
SETPHASE(SEARCH); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case SEARCH:{ |
||||||
|
if(util::distance(m.GetPos(),game->GetPlayer()->GetPos())<=ConfigFloat("Attack Radius")/100.f*24){ |
||||||
|
SETPHASE(PREPARE_ATTACK); |
||||||
|
|
||||||
|
m.F(A::ATTACK_COOLDOWN)=util::random_range(ConfigFloatArr("Attack Wiggle Time Range",0),ConfigFloatArr("Attack Wiggle Time Range",1)); |
||||||
|
m.PerformAnimation("ATTACK",game->GetPlayer()->GetPos()); |
||||||
|
|
||||||
|
Arc attackArc{GetAttackArc(m)}; |
||||||
|
|
||||||
|
m.SetStrategyDrawFunction([arc=attackArc,&storedArc=m.ANY(A::STORED_ARC),&alphaTimer=m.F(A::ENVIRONMENT_TIMER),attackEffectTime=ConfigFloat("Attack Effect Time")](AiL*game,Monster&monster,const std::string&strategy){ |
||||||
|
const float alphaTimer{float(std::fmod(game->GetRunTime(),2.f))}; |
||||||
|
uint8_t alpha{util::lerp(uint8_t(0),uint8_t(128),alphaTimer)}; |
||||||
|
if(alphaTimer>1.f)alpha=util::lerp(0,128,1-(alphaTimer-1)); |
||||||
|
const_cast<Arc&>(arc).Draw(game,{0,0,128,uint8_t(alpha)}); |
||||||
|
if(storedArc.has_value()){ |
||||||
|
const uint8_t effectAlpha{util::lerp(uint8_t(0),uint8_t(128),alphaTimer/attackEffectTime)}; |
||||||
|
std::any_cast<Arc>(storedArc).Draw(game,{255,255,255,effectAlpha}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case PREPARE_ATTACK:{ |
||||||
|
m.F(A::ATTACK_COOLDOWN)-=fElapsedTime; |
||||||
|
if(m.F(A::ATTACK_COOLDOWN)<=0.f){ |
||||||
|
SETPHASE(ATTACK_ANIMATION); |
||||||
|
m.PerformAnimation("ATTACKING"); |
||||||
|
m.F(A::ENVIRONMENT_TIMER)=m.F(A::RECOVERY_TIME)=ConfigFloat("Attack Effect Time"); |
||||||
|
m.F(A::SWING_OCCURRED)=ConfigFloat("Attack Swing Damage Wait Time"); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case ATTACK_ANIMATION:{ |
||||||
|
m.F(A::SWING_OCCURRED)-=fElapsedTime; |
||||||
|
if(m.F(A::SWING_OCCURRED)<=0.f){ |
||||||
|
Arc attackArc{GetAttackArc(m)}; |
||||||
|
if(attackArc.overlaps(game->GetPlayer()->GetPos())){ |
||||||
|
game->GetPlayer()->Knockback(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Attack Knockback")); |
||||||
|
game->GetPlayer()->Hurt(m.GetAttack(),m.OnUpperLevel(),m.GetZ()); |
||||||
|
} |
||||||
|
m.F(A::RECOVERY_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
m.ANY(A::STORED_ARC)=GetAttackArc(m); |
||||||
|
m.SetStrategyDrawFunction([&storedArc=m.ANY(A::STORED_ARC),attackEffectTime=ConfigFloat("Attack Effect Time")](AiL*game,Monster&monster,const std::string&strategy){ |
||||||
|
const float alphaTimer{float(std::fmod(game->GetRunTime(),2.f))}; |
||||||
|
if(storedArc.has_value()){ |
||||||
|
const uint8_t effectAlpha{util::lerp(uint8_t(0),uint8_t(255),alphaTimer/attackEffectTime)}; |
||||||
|
std::any_cast<Arc>(storedArc).Draw(game,{255,255,255,effectAlpha}); |
||||||
|
} |
||||||
|
}); |
||||||
|
SETPHASE(ATTACK_RECOVERY); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case ATTACK_RECOVERY:{ |
||||||
|
m.F(A::RECOVERY_TIME)-=fElapsedTime; |
||||||
|
if(m.F(A::RECOVERY_TIME)<=0.f){ |
||||||
|
m.PerformAnimation("SUBMERGE"); |
||||||
|
SETPHASE(SUBMERGE); |
||||||
|
m.GetFloat(A::RECOVERY_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration()*util::random_range(1.f,2.f); |
||||||
|
m.SetCollisionRadius(0.f); |
||||||
|
m.V(A::JUMP_TARGET_POS)=m.GetPos(); |
||||||
|
m.SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){}); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case SUBMERGE:{ |
||||||
|
m.F(A::RECOVERY_TIME)-=fElapsedTime; |
||||||
|
if(m.F(A::RECOVERY_TIME)<=0.f){ |
||||||
|
m.PerformAnimation("RISE"); |
||||||
|
m.SetPos(m.V(A::JUMP_TARGET_POS)); |
||||||
|
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
SETPHASE(RISE_ANIMATION); |
||||||
|
} |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
Giant Octopus |
||||||
|
42000 HP |
||||||
|
Boss boss has a permanent 80% DMG reduction. |
||||||
|
|
||||||
|
Tentacle |
||||||
|
6000 HP |
||||||
|
When a Tentacle dies boss takes 6000 Dmg |
||||||
|
There are 8 Tentacles |
||||||
|
|
||||||
|
Base Damage |
||||||
|
Bullets: 40 |
||||||
|
Big bullet: 90 |
||||||
|
Tentacle: 50 |
||||||
|
|
||||||
|
Attack moves |
||||||
|
- Boss shots 1 bullet every second |
||||||
|
- Every 6. Shot Boss Shots a big bullet that explodes when i gets close to the Player and form a new ring of bullets flying away in a whirl instead of a straight line. (Boss rests for 2 seconds after big bullet) |
||||||
|
- Tentacels do a delayed attack |
||||||
|
- Every Tentacle and the boss get a 10% Attack buff on each Tentacle death |
||||||
|
|
||||||
|
Tentacles: |
||||||
|
- The Tentacles wiggle a bit around between attacks |
||||||
|
- Wiggle phase is 2-4 seconds. |
||||||
|
- Tentacle stopps moving for 1 sec before attacking, already indicating where it will attack. |
||||||
|
- The Tentacle rest on the floor for a short period after an attack ~1 sec (can vary a bit depending how animation looks smoother) |
||||||
|
- The Tentacle attacks with its entire wheight slaming the position where the player was when the movement stopped. |
||||||
|
|
||||||
|
Reference for Tentacle attack https://youtu.be/YtR521QdsNk?si=_TUxuH-3HLnwr_t9&t=475 (Should start at time stamp 07:55 with a bossfight) |
||||||
|
Instead of wiggling around the tentacles supmerge here in between attacks. |
||||||
|
Also instead of this piercing attack i would prefer if its closer to a slam. |
||||||
|
But i really like how the tentacle indicates that its about to attack in a specific direction. |
||||||
|
|
||||||
|
|
||||||
|
Phases |
||||||
|
12k missing Health on boss: |
||||||
|
- boss starts Shooting an Ink Bubble instead of a normal projectile every 15 seconds that explodes and leaves an ink area Lasting for 30 seconds. moving into the ink gives a -10 movespd debuff for 25 seconds. |
||||||
|
- 6 Attacks after first ink attack, every second small bullet has a different Color and has a homing effect. Homing effect is greatly reduced if Player is covered in ink |
||||||
|
|
||||||
|
30k missing Health |
||||||
|
Tentacles get +10% Speed on every animation or attack |
||||||
|
every Projectile get the homing effect |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
other stuff |
||||||
|
- On Tentacle death, all Tentacles submerge and recover 1k hp each. after 3 seconds tentacles ascend again one after another 0.25 seconds delay between each Tentacle |
||||||
|
Once the first Tentacle got defeated every 15-25 seconds a Tentacle far away from the Player submerges and appear 2 seconds later again in an area close to the Player, (Only Triggers if there is a free Tentacle spawn close to the Player) |
||||||
|
|
||||||
|
also: technically the limps of an Octopus arent called Tentacles... but whatever, dont feel like changing this in the entire document now. |
@ -0,0 +1,74 @@ |
|||||||
|
#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::PARROT(Monster&m,float fElapsedTime,std::string strategy){ |
||||||
|
enum PhaseName{ |
||||||
|
RUN, |
||||||
|
FLY_AWAY, |
||||||
|
}; |
||||||
|
switch(PHASE()){ |
||||||
|
case RUN:{ |
||||||
|
if(!m.attachedTarget.expired()&&m.attachedTarget.lock()->IsAlive()){ |
||||||
|
m.PerformAnimation("FLYING"); |
||||||
|
HAWK(m,fElapsedTime,"Hawk"); |
||||||
|
}else{ |
||||||
|
m.lifetime=10.f; |
||||||
|
SETPHASE(FLY_AWAY); |
||||||
|
m.F(A::PATH_DIR)=1.f; |
||||||
|
if(util::random(1.f)<0.5f)m.F(A::PATH_DIR)=-1.f; |
||||||
|
m.manualIgnoreTerrain=true; |
||||||
|
} |
||||||
|
}break; |
||||||
|
case FLY_AWAY:{ |
||||||
|
if(m.F(A::PATH_DIR)>0.f)m.PerformAnimation("FLYING",Direction::EAST); |
||||||
|
else m.PerformAnimation("FLYING",Direction::WEST); |
||||||
|
m.z+=fElapsedTime*ConfigFloat("Fly Away Z Speed"); |
||||||
|
m.SetVelocity({m.F(A::PATH_DIR)*ConfigFloat("Fly Away Speed"),0.f}); |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,129 @@ |
|||||||
|
#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::PIRATE_BUCCANEER(Monster&m,float fElapsedTime,std::string strategy){ |
||||||
|
#pragma region Phase, Animation, and Helper function setup |
||||||
|
enum PhaseName{ |
||||||
|
INIT, |
||||||
|
MOVE, |
||||||
|
LOCKON, |
||||||
|
WINDUP, |
||||||
|
FIRE_ANIMATION, |
||||||
|
}; |
||||||
|
#pragma endregion |
||||||
|
|
||||||
|
switch(PHASE()){ |
||||||
|
case INIT:{ |
||||||
|
m.SetStrategyDeathFunction([](GameEvent&ev,Monster&m,const std::string&strategy){ |
||||||
|
m.SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){}); |
||||||
|
return false; |
||||||
|
}); |
||||||
|
SETPHASE(MOVE); |
||||||
|
}break; |
||||||
|
case MOVE:{ |
||||||
|
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime; |
||||||
|
|
||||||
|
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos()); |
||||||
|
|
||||||
|
const bool outsideMaxShootingRange=distToPlayer>=ConfigPixelsArr("Stand Still and Shoot Range",1); |
||||||
|
|
||||||
|
auto PrepareToShoot=[&](){ |
||||||
|
SETPHASE(WINDUP); |
||||||
|
m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time"); |
||||||
|
m.PerformAnimation("SHOOT",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); |
||||||
|
}; |
||||||
|
|
||||||
|
if(outsideMaxShootingRange){ |
||||||
|
m.target=game->GetPlayer()->GetPos(); |
||||||
|
RUN_TOWARDS(m,fElapsedTime,"Run Towards"); |
||||||
|
}else |
||||||
|
if(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Reload Time")){ |
||||||
|
PrepareToShoot(); |
||||||
|
}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(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Reload Time")/2.f){ //Only the stand still and shoot range remains, which is twice as fast...
|
||||||
|
PrepareToShoot(); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case WINDUP:{ |
||||||
|
m.F(A::SHOOT_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::SHOOT_TIMER)<=0){
|
||||||
|
m.F(A::ATTACK_COOLDOWN)=0.f; |
||||||
|
CreateBullet(ChargedArrow)("musket_bullet.png","musket_trail.png",m.GetPos(),util::pointTo(m.GetPos(),m.V(A::LOCKON_POS))*ConfigFloat("Arrow Spd"),ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet; |
||||||
|
m.PerformAnimation("SHOOTING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); |
||||||
|
m.SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){}); |
||||||
|
SETPHASE(FIRE_ANIMATION); |
||||||
|
m.F(A::SHOOT_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
}else |
||||||
|
if(m.F(A::SHOOT_TIMER)>=ConfigFloat("Attack Lockon Time")){ |
||||||
|
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos(); |
||||||
|
m.PerformAnimation("SHOOT",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); |
||||||
|
const float arrowHitboxRadius{ConfigFloat("Arrow Hitbox Radius")}; |
||||||
|
m.SetStrategyDrawFunction([arrowHitboxRadius](AiL*game,Monster&monster,const std::string&strategy){ |
||||||
|
const float alphaTimer{float(std::fmod(game->GetRunTime(),2.f))}; |
||||||
|
uint8_t alpha{util::lerp(uint8_t(0),uint8_t(255),alphaTimer)}; |
||||||
|
if(alphaTimer>1.f)alpha=util::lerp(0,255,1-(alphaTimer-1)); |
||||||
|
vf2d midpoint{geom2d::line<float>(monster.GetPos(),monster.V(A::LOCKON_POS)).rpoint(800.f)}; |
||||||
|
float shootingAngle{util::angleTo(monster.GetPos(),monster.V(A::LOCKON_POS))+PI/2}; |
||||||
|
vf2d imgSize{arrowHitboxRadius*2.f,800.f}; |
||||||
|
game->view.DrawPartialRotatedDecal(midpoint,GFX["line_indicator.png"].Decal(),shootingAngle,GFX["line_indicator.png"].Sprite()->Size()/2,{},GFX["line_indicator.png"].Sprite()->Size(),imgSize/GFX["line_indicator.png"].Sprite()->Size(),{255,255,255,uint8_t(alpha)}); |
||||||
|
}); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case FIRE_ANIMATION:{ |
||||||
|
m.F(A::SHOOT_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::SHOOT_TIMER)<=0.f){ |
||||||
|
m.PerformAnimation("IDLE",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); |
||||||
|
SETPHASE(MOVE); |
||||||
|
} |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
#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_ANIMATION_DATA |
||||||
|
|
||||||
|
void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string strategy){ |
||||||
|
enum PhaseName{ |
||||||
|
INIT, |
||||||
|
MOVE, |
||||||
|
PREPARE_SHOOT, |
||||||
|
SHOOT_RELOAD, |
||||||
|
DRINK_RUM, |
||||||
|
WINDUP, |
||||||
|
RECOVERY, |
||||||
|
}; |
||||||
|
|
||||||
|
enum AttackType{ |
||||||
|
STAB, |
||||||
|
SLASH |
||||||
|
}; |
||||||
|
|
||||||
|
switch(PHASE()){ |
||||||
|
case INIT:{ |
||||||
|
m.F(A::TARGET_TIMER)=ConfigFloat("Shooting Frequency"); |
||||||
|
m.F(A::PARROT_FLY_TIMER)=ConfigFloat("Parrot Fly Wait Time"); |
||||||
|
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); |
||||||
|
firstAnimation=false; |
||||||
|
} |
||||||
|
m.mountedSprOffset=ConfigVec("Imposed Monster Offset"); |
||||||
|
m.deathData.emplace_back(ConfigString("Spawned Monster"),1U); |
||||||
|
SETPHASE(MOVE); |
||||||
|
}break; |
||||||
|
case MOVE:{ |
||||||
|
if(m.F(A::PARROT_FLY_TIMER)>0.f){ |
||||||
|
m.F(A::PARROT_FLY_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::PARROT_FLY_TIMER)<=0.f){ |
||||||
|
m.mounted_animation.reset(); |
||||||
|
Monster&parrot{game->SpawnMonster(m.GetPos(),MONSTER_DATA.at("Parrot"),m.OnUpperLevel())}; |
||||||
|
parrot.attachedTarget=m.GetWeakPointer(); |
||||||
|
} |
||||||
|
} |
||||||
|
m.F(A::TARGET_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::TARGET_TIMER)<=0.f){ |
||||||
|
const float diceRoll{util::random(100)}; |
||||||
|
if(diceRoll<=ConfigFloat("Shooting Chance")){ |
||||||
|
const float distToPlayer{util::distance(game->GetPlayer()->GetPos(),m.GetPos())}; |
||||||
|
if(distToPlayer<=ConfigFloat("Shoot Max Range")/100.f*24){ |
||||||
|
m.F(A::SHOOT_TIMER)=ConfigFloat("Shooting Delay"); |
||||||
|
SETPHASE(PREPARE_SHOOT); |
||||||
|
m.PerformAnimation("SHOOT",game->GetPlayer()->GetPos()); |
||||||
|
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos(); |
||||||
|
} |
||||||
|
} |
||||||
|
m.F(A::TARGET_TIMER)=ConfigFloat("Shooting Frequency"); |
||||||
|
}else |
||||||
|
if(m.GetHealth()<ConfigInt("Rum Drink Threshold")&&m.I(A::RUM_DRINK_COUNT)<ConfigInt("Rum Drink Count")){ |
||||||
|
m.PerformAnimation("DRINK"); |
||||||
|
m.F(A::BREAK_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
SETPHASE(DRINK_RUM); |
||||||
|
m.I(A::RUM_DRINK_COUNT)++; |
||||||
|
} |
||||||
|
else{ |
||||||
|
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.I(A::ATTACK_TYPE)=util::random()%2; //Choose randomly between stab or slash.
|
||||||
|
switch(m.I(A::ATTACK_TYPE)){ |
||||||
|
case STAB:{ |
||||||
|
m.F(A::CASTING_TIMER)=ConfigFloat("Stab Windup Time"); |
||||||
|
m.PerformAnimation("STAB",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); |
||||||
|
}break; |
||||||
|
case SLASH:{ |
||||||
|
m.F(A::CASTING_TIMER)=ConfigFloat("Slash Windup Time"); |
||||||
|
m.PerformAnimation("SLASH",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); |
||||||
|
}break; |
||||||
|
default:ERR(std::format("WARNING! Invalid Attack type {} provided. THIS SHOULD NOT BE HAPPENING!",m.I(A::ATTACK_TYPE))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}break; |
||||||
|
case PREPARE_SHOOT:{ |
||||||
|
m.F(A::SHOOT_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::SHOOT_TIMER)<=0.f){ |
||||||
|
CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),m.V(A::LOCKON_POS))*ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),m.GetAttack(),m.OnUpperLevel(),false,ConfigPixel("Bullet Color"),vf2d{1.f,1.f}*ConfigFloat("Bullet Radius")/3.f)EndBullet; |
||||||
|
m.PerformAnimation("SHOOTING"); |
||||||
|
m.F(A::SHOOT_ANIMATION_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
SETPHASE(SHOOT_RELOAD); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case SHOOT_RELOAD:{ |
||||||
|
m.F(A::SHOOT_ANIMATION_TIME)-=fElapsedTime; |
||||||
|
if(m.F(A::SHOOT_ANIMATION_TIME)<=0.f){ |
||||||
|
m.PerformAnimation("IDLE"); |
||||||
|
SETPHASE(MOVE); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case DRINK_RUM:{ |
||||||
|
m.F(A::BREAK_TIME)-=fElapsedTime; |
||||||
|
if(m.F(A::BREAK_TIME)<=0.f){ |
||||||
|
m.Heal(ConfigInt("Rum Health Recovery"),true); |
||||||
|
SETPHASE(MOVE); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case WINDUP:{ |
||||||
|
m.F(A::CASTING_TIMER)-=fElapsedTime; |
||||||
|
if(m.F(A::CASTING_TIMER)<=0){ |
||||||
|
SETPHASE(RECOVERY); |
||||||
|
switch(m.I(A::ATTACK_TYPE)){ |
||||||
|
case STAB:{ |
||||||
|
vf2d stabTarget=game->GetPlayer()->GetPos(); |
||||||
|
m.PerformAnimation("STABBING"); |
||||||
|
CreateBullet(DaggerStab)(m,ConfigString("Dagger Stab Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Stab Knockback"),m.OnUpperLevel(),m.GetFacingDirection(),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"); |
||||||
|
CreateBullet(DaggerSlash)(m,ConfigString("Dagger Slash Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Slash Knockback"),m.OnUpperLevel(),m.GetFacingDirection(),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))); |
||||||
|
} |
||||||
|
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration(); |
||||||
|
m.F(A::RECOVERY_TIME)=ConfigFloat("Attack Recovery Time"); |
||||||
|
} |
||||||
|
}break; |
||||||
|
case RECOVERY:{ |
||||||
|
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); |
||||||
|
}break; |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue