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.
This commit is contained in:
parent
139d4f4eac
commit
7cf3c53e91
@ -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};
|
||||||
@ -62,10 +77,3 @@ Arc::Arc(const vf2d pos,const float radius,const float pointingAngle,const float
|
|||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -213,3 +213,14 @@ 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…
x
Reference in New Issue
Block a user