Fix all octopus arm AI config variable references to actually use the Config functions instead of direct config file parameters. Add in attack cone effect.

master
sigonasr2 3 weeks ago
parent 139d4f4eac
commit 7cf3c53e91
  1. 22
      Adventures in Lestoria/Arc.cpp
  2. 4
      Adventures in Lestoria/Arc.h
  3. 10
      Adventures in Lestoria/GiantOctopus.cpp
  4. 2
      Adventures in Lestoria/MonsterAttribute.h
  5. 66
      Adventures in Lestoria/OctopusArm.cpp
  6. 6
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  7. 11
      Adventures in Lestoria/util.cpp
  8. 2
      Adventures in Lestoria/util.h

@ -44,6 +44,21 @@ INCLUDE_game
Arc::Arc(const vf2d pos,const float radius,const float pointingAngle,const float sweepAngle) Arc::Arc(const vf2d pos,const float radius,const float pointingAngle,const float sweepAngle)
:pos(pos),radius(radius),pointingAngle(pointingAngle),sweepAngle(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)); 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 //Use cut-off point between two angles
poly.pos.emplace_back(pos); //Always add 0,0 poly.pos.emplace_back(pos); //Always add 0,0
float smallestAng{util::radToDeg(pointingAngle-sweepAngle)+90}; float smallestAng{util::radToDeg(pointingAngle-sweepAngle)+90};
@ -61,11 +76,4 @@ Arc::Arc(const vf2d pos,const float radius,const float pointingAngle,const float
poly.pos.emplace_back(pos+game->circleCooldownPoints[i]*radius); poly.pos.emplace_back(pos+game->circleCooldownPoints[i]*radius);
} }
poly.pos.emplace_back(pos); //Connect back to itself. poly.pos.emplace_back(pos); //Connect back to itself.
}
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);
} }

@ -48,10 +48,12 @@ public:
Arc(const vf2d pos,const float radius,const float pointingAngle,const float sweepAngle); Arc(const vf2d pos,const float radius,const float pointingAngle,const float sweepAngle);
void Draw(AiL*game,const Pixel col); void Draw(AiL*game,const Pixel col);
const bool overlaps(const vf2d checkPos)const; const bool overlaps(const vf2d checkPos)const;
void GrowRadius(const float growAmt);
private: private:
void GenerateArc();
const vf2d pos; const vf2d pos;
const float pointingAngle; const float pointingAngle;
const float sweepAngle; const float sweepAngle;
const float radius; float radius;
geom2d::polygon<float>poly; geom2d::polygon<float>poly;
}; };

@ -48,14 +48,14 @@ INCLUDE_game
void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string strategy){ void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string strategy){
enum PhaseName{ enum PhaseName{
RUN, IDENTIFY_ARMS,
FLY_AWAY, NORMAL,
}; };
switch(PHASE()){ switch(PHASE()){
case RUN:{ case IDENTIFY_ARMS:{
}break; }break;
case FLY_AWAY:{ case NORMAL:{
}break; }break;
} }

@ -152,4 +152,6 @@ enum class Attribute{
SPEED_RAMPUP_TIMER, SPEED_RAMPUP_TIMER,
BULLET_HAS_BEEN_SHOT, BULLET_HAS_BEEN_SHOT,
SUCTION_TIMER, SUCTION_TIMER,
STORED_ARC,
SWING_OCCURRED,
}; };

@ -53,29 +53,23 @@ void Monster::STRATEGY::OCTOPUS_ARM(Monster&m,float fElapsedTime,std::string str
RISE_ANIMATION, RISE_ANIMATION,
SEARCH, SEARCH,
PREPARE_ATTACK, PREPARE_ATTACK,
ATTACK_ANIMATION,
}; };
const auto GetAttackArc=[](const Monster&m){ const auto GetAttackArc=[attackRadius=ConfigFloat("Attack Radius"),attackArc=ConfigFloat("Attack Arc")](const Monster&m){
float arcAngle{}; return Arc{m.GetPos(),attackRadius/100.f*24,util::dirToAngle(m.GetFacingDirection()),util::degToRad(attackArc)};
switch(m.GetFacingDirection()){
case Direction::NORTH:{
arcAngle=-PI/2;
}break;
case Direction::EAST:{
arcAngle=0.f;
}break;
case Direction::WEST:{
arcAngle=PI;
}break;
case Direction::SOUTH:{
arcAngle=PI/2;
}break;
}
return Arc{m.GetPos(),"Attack Radius"_F/100.f*24,arcAngle,util::degToRad("Attack Arc"_F)};
}; };
if(m.ANY(A::STORED_ARC).has_value()){
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();
}
switch(PHASE()){ switch(PHASE()){
case INIT:{ case INIT:{
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.PerformAnimation("RISE",game->GetPlayer()->GetPos());
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration(); m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
SETPHASE(RISE_ANIMATION); SETPHASE(RISE_ANIMATION);
@ -88,30 +82,52 @@ void Monster::STRATEGY::OCTOPUS_ARM(Monster&m,float fElapsedTime,std::string str
} }
}break; }break;
case SEARCH:{ case SEARCH:{
if(util::distance(m.GetPos(),game->GetPlayer()->GetPos())<="Attack Radius"_F/100.f*24){ if(util::distance(m.GetPos(),game->GetPlayer()->GetPos())<=ConfigFloat("Attack Radius")/100.f*24){
SETPHASE(PREPARE_ATTACK); SETPHASE(PREPARE_ATTACK);
m.F(A::ATTACK_COOLDOWN)="Attack Wiggle Time Range"_FRange;
m.PerformAnimation("ATTACKING",game->GetPlayer()->GetPos()); 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)}; Arc attackArc{GetAttackArc(m)};
m.SetStrategyDrawFunction([&attackArc](AiL*game,Monster&monster,const std::string&strategy){ m.SetStrategyDrawFunction([&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{std::fmod(game->GetRunTime(),2.f)}; const float alphaTimer{std::fmod(game->GetRunTime(),2.f)};
uint8_t alpha{util::lerp(0,255,alphaTimer)}; uint8_t alpha{util::lerp(0,255,alphaTimer)};
if(alphaTimer>1.f)alpha=util::lerp(0,255,1-(alphaTimer-1)); if(alphaTimer>1.f)alpha=util::lerp(0,255,1-(alphaTimer-1));
attackArc.Draw(game,{0,0,255,uint8_t(alpha)}); attackArc.Draw(game,{0,0,255,uint8_t(alpha)});
if(storedArc.has_value()){
const uint8_t effectAlpha{util::lerp(0,255,alphaTimer/attackEffectTime)};
std::any_cast<Arc>(storedArc).Draw(game,{255,255,255,effectAlpha});
}
}); });
} }
}break; }break;
case PREPARE_ATTACK:{ case PREPARE_ATTACK:{
m.F(A::ATTACK_COOLDOWN)-=fElapsedTime; m.F(A::ATTACK_COOLDOWN)-=fElapsedTime;
if(m.F(A::ATTACK_COOLDOWN)<=0.f){ if(m.F(A::ATTACK_COOLDOWN)<=0.f){
Arc attackArc{GetAttackArc(m)}; m.PerformAnimation("ATTACKING");
if(attackArc.overlaps(game->GetPlayer()->GetPos())){ m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Attack Effect Time");
game->GetPlayer()->Knockback(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*"Attack Knockback"_F); m.F(A::SWING_OCCURRED)=ConfigFloat("Attack Swing Damage Wait Time");
game->GetPlayer()->Hurt(m.GetAttack(),m.OnUpperLevel(),m.GetZ()); }
}break;
case ATTACK_ANIMATION:{
m.F(A::RECOVERY_TIME)-=fElapsedTime;
if(m.F(A::SWING_OCCURRED)>0.f){
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);
} }
} }
if(m.F(A::RECOVERY_TIME)<=0.f){
m.PerformIdleAnimation();
SETPHASE(SEARCH);
}
}break; }break;
} }
} }

@ -1208,6 +1208,12 @@ MonsterStrategy
Attack Arc = 60deg Attack Arc = 60deg
Attack Wait Time = 1s Attack Wait Time = 1s
# For synchronization purposes. Damage doesn't come out until this amount of time has passed in the animation.
Attack Swing Damage Wait Time = 0.45s
Attack Knockback = 50 Attack Knockback = 50
# Amount of time the spreading aftershock effect appears for.
Attack Effect Time = 0.4s
} }
} }

@ -212,4 +212,15 @@ void util::turn_towards_direction(float&angle,float target,float rate)
std::wstring util::to_wstring(const std::string&str){ std::wstring util::to_wstring(const std::string&str){
return {str.begin(),str.end()}; return {str.begin(),str.end()};
}
const float util::dirToAngle(const Direction dir){
switch(dir){
case Direction::NORTH:return -PI/2;break;
case Direction::EAST:return 0.f;break;
case Direction::WEST:return PI;break;
case Direction::SOUTH:return PI/2;break;
}
ERR(std::format("WARNING! Could not find Direction {}! THIS SHOULD NOT BE HAPPENING!",int(dir)));
return 0.f;
} }

@ -39,6 +39,7 @@ All rights reserved.
#include <stdlib.h> #include <stdlib.h>
#include "olcUTIL_Geometry2D.h" #include "olcUTIL_Geometry2D.h"
#include "olcPGEX_TTF.h" #include "olcPGEX_TTF.h"
#include "Direction.h"
#include <random> #include <random>
namespace olc::util{ namespace olc::util{
@ -54,6 +55,7 @@ namespace olc::util{
float angleTo(vf2d posFrom,vf2d posTo); float angleTo(vf2d posFrom,vf2d posTo);
float degToRad(float deg); float degToRad(float deg);
float radToDeg(float rad); float radToDeg(float rad);
const float dirToAngle(const Direction dir);
#pragma region Lerp templates + specializations #pragma region Lerp templates + specializations
template<class T,class U> template<class T,class U>

Loading…
Cancel
Save