Implemented centroid and signed_area functions to the geom2d util. Just a bunch of maths, I don't know why it works. Release Build 9683.

mac-build
sigonasr2 5 months ago
parent db515cca4c
commit d27009efa3
  1. 12
      Adventures in Lestoria/Chapter_2_Boss.txt
  2. 4
      Adventures in Lestoria/MonsterAttribute.h
  3. 4
      Adventures in Lestoria/StageMaskPolygon.cpp
  4. 1
      Adventures in Lestoria/StageMaskPolygon.h
  5. 16
      Adventures in Lestoria/State_GameRun.cpp
  6. 58
      Adventures in Lestoria/StoneGolem.cpp
  7. 8
      Adventures in Lestoria/TODO.txt
  8. 2
      Adventures in Lestoria/Version.h
  9. 18
      Adventures in Lestoria/olcPixelGameEngine.h
  10. 38
      Adventures in Lestoria/olcUTIL_Geometry2D.h
  11. BIN
      x64/Release/Adventures in Lestoria.exe

@ -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

@ -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,
};

@ -58,10 +58,6 @@ StageMaskPolygon::StageMaskPolygon(const std::vector<vf2d>&points,const std::str
StageMaskPolygon::StageMaskOverlay::StageMaskOverlay(const std::vector<vf2d>&points,const std::string&texture,const Pixel overlayCol)
:overlayPolygon(geom2d::polygon<float>{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){

@ -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<vf2d>&points,const std::string&texture="",const Pixel overlayCol=BLANK);
void SetBlendColor(const Pixel overlayCol);
const bool HasOverlay()const;

@ -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();
}

@ -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<Monster>&monsterPtr){
if(monsterPtr->GetName()!="Stone Golem Pillar")return;
const Monster&pillar=*monsterPtr;
if(geom2d::overlaps(geom2d::circle<float>{m.GetPos(),m.GetCollisionRadius()},geom2d::circle<float>{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::vector<vf2d>corners{upperLeftCorner,upperRightCorner,lowerLeftCorner,lowerRightCorner};
using AngleDiff=float;
using Corner=vf2d;
std::pair<AngleDiff,Corner>largestCorner{};
std::pair<AngleDiff,Corner>smallestCorner{};
std::for_each(corners.begin(),corners.end(),[&](const vf2d&corner){
float angleToMiddle{geom2d::line<float>{m.GetPos(),pillar.GetPos()}.vector().polar().y};
float angleToCorner{geom2d::line<float>{m.GetPos(),corner}.vector().polar().y};
AngleDiff diff{util::angle_difference(angleToCorner,angleToMiddle)};
if(largestCorner.first<diff)largestCorner={diff,corner};
if(smallestCorner.first>diff)smallestCorner={diff,corner};
});
const vf2d extendedLargestCornerPoint{geom2d::line<float>{m.GetPos(),largestCorner.second}.rpoint((100000.f))};
const vf2d extendedSmallestCornerPoint{geom2d::line<float>{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<StageMaskPolygon>(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;
}
}

@ -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
====
====
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.

@ -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

@ -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 |

@ -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<T>{},[](const olc::v_2d<T>&middle,const olc::v_2d<T>&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;i<pos.size();i++){
sum+=(pos[i].x*pos[(i+1)%pos.size()].y)-(pos[(i+1)%pos.size()].x*pos[i].y);
}
sum*=0.5f;
return sum;
}
//Because Maths. https://www.omnicalculator.com/math/centroid
inline constexpr v_2d<T>centroid()const{
v_2d<T>centroid{};
for(int i=0;i<pos.size();i++){
centroid.x+=(pos[i].x+pos[(i+1)%pos.size()].x)*((pos[i].x*pos[(i+1)%pos.size()].y)-(pos[(i+1)%pos.size()].x*pos[i].y));
centroid.y+=(pos[i].y+pos[(i+1)%pos.size()].y)*((pos[i].x*pos[(i+1)%pos.size()].y)-(pos[(i+1)%pos.size()].x*pos[i].y));
}
centroid*=1.f/(6.f*signed_area());
return centroid;
}
};

Loading…
Cancel
Save