diff --git a/Adventures in Lestoria/Chapter_2_Boss.txt b/Adventures in Lestoria/Chapter_2_Boss.txt index 889f6fab..8cb37d02 100644 --- a/Adventures in Lestoria/Chapter_2_Boss.txt +++ b/Adventures in Lestoria/Chapter_2_Boss.txt @@ -10,19 +10,19 @@ Size: 800% -The boss casts 3 Pillars at the beginning of the fight, targeting the player. +>The boss casts 3 Pillars at the beginning of the fight, targeting the player. -1 at a time. +>1 at a time. -2 sec cast. +>2 sec cast. -0.5 sec delay until next cast starts. +>0.5 sec delay until next cast starts. -40 dmg +>40 dmg -Pillar-radius: 350 +>Pillar-radius: 350 diff --git a/Adventures in Lestoria/MonsterAttribute.h b/Adventures in Lestoria/MonsterAttribute.h index 5ea2c76c..26181654 100644 --- a/Adventures in Lestoria/MonsterAttribute.h +++ b/Adventures in Lestoria/MonsterAttribute.h @@ -122,6 +122,8 @@ enum class Attribute{ WIND_STRENGTH, WIND_PHASE_TIMER, MARKED_DEAD, - LOOPING_SOUND_ID, //Store looping sound IDs temporarily with this value. The game automatically cleans this up in Monster::OnDestroy() if you don't, since a monster that dies should not have their looping sound still playing. + LOOPING_SOUND_ID, //NOTE: Store looping sound IDs temporarily with this value. The game automatically cleans this up in Monster::OnDestroy() if you don't, since a monster that dies should not have their looping sound still playing. PLAYED_FLAG, + HEALTH_PCT_PHASE, //Used for tracking the percentage a mechanic was done in Stone Golem Fight. + STAGE_POLYGONS, }; \ No newline at end of file diff --git a/Adventures in Lestoria/StageMaskPolygon.cpp b/Adventures in Lestoria/StageMaskPolygon.cpp index b52b40dc..0814e441 100644 --- a/Adventures in Lestoria/StageMaskPolygon.cpp +++ b/Adventures in Lestoria/StageMaskPolygon.cpp @@ -58,10 +58,6 @@ StageMaskPolygon::StageMaskPolygon(const std::vector&points,const std::str StageMaskPolygon::StageMaskOverlay::StageMaskOverlay(const std::vector&points,const std::string&texture,const Pixel overlayCol) :overlayPolygon(geom2d::polygon{points}),texture(texture),overlayCol(overlayCol){ if(!GFX.count(texture))ERR(std::format("WARNING! Texture {} does not exist while trying to initialize Stage Mask Overlay!",texture)); - - overlayPolygon.pos.insert(overlayPolygon.pos.begin(),overlayPolygon.middle()); //Insert the middle point to the front of the overlay polygon's point list. - //Copy the first point provided from the original polygon to the end of the new overlay polygon since we have to fully connect the polygon for the texture to show up correctly. - overlayPolygon.pos.push_back(points[0]); bool first=false; std::transform(overlayPolygon.pos.begin(),overlayPolygon.pos.end(),std::back_inserter(overlayUVs.pos),[&](const vf2d&point){ if(!first){ diff --git a/Adventures in Lestoria/StageMaskPolygon.h b/Adventures in Lestoria/StageMaskPolygon.h index bb38cde2..f4d4b69b 100644 --- a/Adventures in Lestoria/StageMaskPolygon.h +++ b/Adventures in Lestoria/StageMaskPolygon.h @@ -55,6 +55,7 @@ class StageMaskPolygon{ Pixel overlayCol; }; public: + //The stage mask polygon should define the first point as the center point, and all other points fanning out from the original point. Note that when doing this technique it is common to specify the second point a second time at the end to connect the final triangle. StageMaskPolygon(const std::vector&points,const std::string&texture="",const Pixel overlayCol=BLANK); void SetBlendColor(const Pixel overlayCol); const bool HasOverlay()const; diff --git a/Adventures in Lestoria/State_GameRun.cpp b/Adventures in Lestoria/State_GameRun.cpp index 48a9c268..279f5c9f 100644 --- a/Adventures in Lestoria/State_GameRun.cpp +++ b/Adventures in Lestoria/State_GameRun.cpp @@ -46,6 +46,7 @@ All rights reserved. #include "MenuComponent.h" #include "Unlock.h" #include "Tutorial.h" +#include "StageMaskPolygon.h" INCLUDE_MONSTER_LIST INCLUDE_game @@ -87,6 +88,21 @@ void State_GameRun::OnUserUpdate(AiL*game){ } void State_GameRun::Draw(AiL*game){ game->RenderWorld(game->GetElapsedTime()); + + if(game->GetCurrentMapData().provideOptimization&&!LoadingScreen::loading){ + StageMaskPolygon poly{ + { + game->view.ScreenToWorld(game->GetMousePos()-vf2d{50,50}), + game->view.ScreenToWorld(game->GetMousePos()+vf2d{100,-20}), + game->view.ScreenToWorld(game->GetMousePos()+vf2d{200,200}), + game->view.ScreenToWorld(game->GetMousePos()+vf2d{-50,150}), + game->view.ScreenToWorld(game->GetMousePos()+vf2d{-120,90}), + },"safeIndicatorGradient.png",PixelLerp(VERY_DARK_BLUE,BLACK,sin(geom2d::pi*game->GetRunTime()*2)) + }; + poly.Draw(); +} + + game->RenderHud(); Tutorial::Draw(); } \ No newline at end of file diff --git a/Adventures in Lestoria/StoneGolem.cpp b/Adventures in Lestoria/StoneGolem.cpp index 6d1b5278..e861cc94 100644 --- a/Adventures in Lestoria/StoneGolem.cpp +++ b/Adventures in Lestoria/StoneGolem.cpp @@ -43,8 +43,10 @@ All rights reserved. #include "util.h" #include "BulletTypes.h" #include "SoundEffect.h" +#include "StageMaskPolygon.h" INCLUDE_game +INCLUDE_MONSTER_LIST using A=Attribute; @@ -56,12 +58,57 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str STANDARD, STONE_THROW_CAST, STONE_THROW_FINISH_ANIMATION, + SHOCKWAVE, + }; + + const auto PrepareSafeAreas=[&](){ + m.VEC(A::STAGE_POLYGONS).clear(); + std::for_each(MONSTER_LIST.begin(),MONSTER_LIST.end(),[&](const std::unique_ptr&monsterPtr){ + if(monsterPtr->GetName()!="Stone Golem Pillar")return; + const Monster&pillar=*monsterPtr; + if(geom2d::overlaps(geom2d::circle{m.GetPos(),m.GetCollisionRadius()},geom2d::circle{pillar.GetPos(),pillar.GetCollisionRadius()}))return; + + #pragma region Solve for Safe Quad + //First draw 4 lines to the corners. + const vf2d upperLeftCorner{pillar.GetPos()+vf2d{-8,-8}*pillar.GetSizeMult()}; + const vf2d upperRightCorner{pillar.GetPos()+vf2d{8,-8}*pillar.GetSizeMult()}; + const vf2d lowerLeftCorner{pillar.GetPos()+vf2d{-8,8}*pillar.GetSizeMult()}; + const vf2d lowerRightCorner{pillar.GetPos()+vf2d{8,8}*pillar.GetSizeMult()}; + + const std::vectorcorners{upperLeftCorner,upperRightCorner,lowerLeftCorner,lowerRightCorner}; + + using AngleDiff=float; + using Corner=vf2d; + std::pairlargestCorner{}; + std::pairsmallestCorner{}; + + std::for_each(corners.begin(),corners.end(),[&](const vf2d&corner){ + float angleToMiddle{geom2d::line{m.GetPos(),pillar.GetPos()}.vector().polar().y}; + float angleToCorner{geom2d::line{m.GetPos(),corner}.vector().polar().y}; + AngleDiff diff{util::angle_difference(angleToCorner,angleToMiddle)}; + if(largestCorner.firstdiff)smallestCorner={diff,corner}; + }); + + const vf2d extendedLargestCornerPoint{geom2d::line{m.GetPos(),largestCorner.second}.rpoint((100000.f))}; + const vf2d extendedSmallestCornerPoint{geom2d::line{m.GetPos(),smallestCorner.second}.rpoint((100000.f))}; + #pragma endregion + + m.VEC(A::STAGE_POLYGONS).emplace_back(StageMaskPolygon{{largestCorner.second,extendedLargestCornerPoint,extendedSmallestCornerPoint,smallestCorner.second},"safeIndicatorGradient.png",PixelLerp(VERY_DARK_BLUE,BLACK,sin(geom2d::pi*game->GetRunTime()*2))}); + }); + m.SetStrategyDrawFunction([&](AiL*game,Monster&m,const std::string&strategyName){ + std::for_each(m.VEC(A::STAGE_POLYGONS).begin(),m.VEC(A::STAGE_POLYGONS).end(),[](std::any&data){ + const StageMaskPolygon&polygon{std::any_cast(data)}; + polygon.Draw(); + }); + }); }; switch(m.phase){ case INITIALIZE:{ m.F(A::RECOVERY_TIME)=ConfigFloat("Beginning Phase.Pillar Cast Delay Time"); m.I(A::PATTERN_REPEAT_COUNT)=ConfigInt("Beginning Phase.Repeat Count"); + m.F(A::HEALTH_PCT_PHASE)=1.f; m.phase=SPAWN_PILLAR_PREPARE; }break; case SPAWN_PILLAR_PREPARE:{ @@ -97,6 +144,14 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str //Extending the bear script's variables to read the state of it... const bool SlamHasFinished=m.I(A::ATTACK_COUNT)!=m.I(A::BEAR_STOMP_COUNT); if(SlamHasFinished){ + + if(m.F(A::HEALTH_PCT_PHASE)-m.GetHealthRatio()>=0.1f){ + m.F(A::HEALTH_PCT_PHASE)-=0.1f; + PrepareSafeAreas(); + m.phase=SHOCKWAVE; + break; + } + const bool StoneThrowRollSucceeds=util::random(100.f)<=ConfigFloat("Standard Attack.Stone Throw Chance"); m.I(A::ATTACK_COUNT)=m.I(A::BEAR_STOMP_COUNT); //Make sure the slams are now reset if necessary. if(StoneThrowRollSucceeds){ //The intent is one or the other attack is supposed to happen. We can't do the slam and a throw, rerolling repeatedly each tick is unncessary. @@ -132,5 +187,8 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str m.phase=STANDARD; } }break; + case SHOCKWAVE:{ + game->SetWorldColor(PixelLerp(VERY_DARK_BLUE,BLACK,sin(geom2d::pi*game->GetRunTime()*2))); + }break; } } \ No newline at end of file diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt index 8e40f272..77f5469a 100644 --- a/Adventures in Lestoria/TODO.txt +++ b/Adventures in Lestoria/TODO.txt @@ -19,4 +19,10 @@ New Monster Sound Effects Add a Helper Function: Change proximity knockback checks regardless of player/monster friendliness to be a function call instead. DEMO -==== \ No newline at end of file +==== + + +PGETinker notes +=============== +Changing zoom size does not affect the indentation breadcumb immediately (requires scrolling to change the view) +Enabling javid mode does not immediately re-evaluate code. \ No newline at end of file diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 41e72ede..52bbb9ef 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_PATCH 3 -#define VERSION_BUILD 9678 +#define VERSION_BUILD 9683 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/olcPixelGameEngine.h b/Adventures in Lestoria/olcPixelGameEngine.h index 833bd45b..cd470915 100644 --- a/Adventures in Lestoria/olcPixelGameEngine.h +++ b/Adventures in Lestoria/olcPixelGameEngine.h @@ -818,14 +818,16 @@ namespace olc SCREEN, MODEL3D, }; - - enum class DecalStructure - { - LINE, - FAN, - STRIP, - LIST - }; + #ifndef OLC_DECALSTRUCT + #define OLC_DECALSTRUCT + enum class DecalStructure + { + LINE, + FAN, + STRIP, + LIST + }; + #endif // O------------------------------------------------------------------------------O // | olc::Renderable - Convenience class to keep a sprite and decal together | diff --git a/Adventures in Lestoria/olcUTIL_Geometry2D.h b/Adventures in Lestoria/olcUTIL_Geometry2D.h index 8b05b2e8..1e67b351 100644 --- a/Adventures in Lestoria/olcUTIL_Geometry2D.h +++ b/Adventures in Lestoria/olcUTIL_Geometry2D.h @@ -503,7 +503,18 @@ namespace olc #include "olcPixelGameEngine.h" #endif - +#ifndef OLC_DECALSTRUCT + #define OLC_DECALSTRUCT + namespace olc{ + enum class DecalStructure + { + LINE, + FAN, + STRIP, + LIST + }; + } +#endif namespace olc::utils::geom2d { @@ -790,6 +801,31 @@ namespace olc::utils::geom2d vf2d total=std::accumulate(pos.begin(),pos.end(),olc::v_2d{},[](const olc::v_2d&middle,const olc::v_2d&point){return std::move(middle)+point;}); return total/pos.size(); } + + inline constexpr T area()const{ + return abs(signed_area()); + } + + //Maths. https://www.omnicalculator.com/math/irregular-polygon-area + inline constexpr T signed_area()const{ + T sum{}; + for(int i=0;icentroid()const{ + v_2dcentroid{}; + for(int i=0;i