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)
|
||||
: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};
|
||||
@ -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.
|
||||
}
|
||||
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);
|
||||
void Draw(AiL*game,const Pixel col);
|
||||
const bool overlaps(const vf2d checkPos)const;
|
||||
void GrowRadius(const float growAmt);
|
||||
private:
|
||||
void GenerateArc();
|
||||
const vf2d pos;
|
||||
const float pointingAngle;
|
||||
const float sweepAngle;
|
||||
const float radius;
|
||||
float radius;
|
||||
geom2d::polygon<float>poly;
|
||||
};
|
@ -48,14 +48,14 @@ INCLUDE_game
|
||||
|
||||
void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string strategy){
|
||||
enum PhaseName{
|
||||
RUN,
|
||||
FLY_AWAY,
|
||||
IDENTIFY_ARMS,
|
||||
NORMAL,
|
||||
};
|
||||
switch(PHASE()){
|
||||
case RUN:{
|
||||
case IDENTIFY_ARMS:{
|
||||
|
||||
}break;
|
||||
case FLY_AWAY:{
|
||||
case NORMAL:{
|
||||
|
||||
}break;
|
||||
}
|
||||
|
@ -152,4 +152,6 @@ enum class Attribute{
|
||||
SPEED_RAMPUP_TIMER,
|
||||
BULLET_HAS_BEEN_SHOT,
|
||||
SUCTION_TIMER,
|
||||
STORED_ARC,
|
||||
SWING_OCCURRED,
|
||||
};
|
@ -53,29 +53,23 @@ void Monster::STRATEGY::OCTOPUS_ARM(Monster&m,float fElapsedTime,std::string str
|
||||
RISE_ANIMATION,
|
||||
SEARCH,
|
||||
PREPARE_ATTACK,
|
||||
ATTACK_ANIMATION,
|
||||
};
|
||||
|
||||
const auto GetAttackArc=[](const Monster&m){
|
||||
float arcAngle{};
|
||||
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)};
|
||||
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()){
|
||||
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()){
|
||||
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.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
SETPHASE(RISE_ANIMATION);
|
||||
@ -88,30 +82,52 @@ void Monster::STRATEGY::OCTOPUS_ARM(Monster&m,float fElapsedTime,std::string str
|
||||
}
|
||||
}break;
|
||||
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);
|
||||
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)};
|
||||
|
||||
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)};
|
||||
uint8_t alpha{util::lerp(0,255,alphaTimer)};
|
||||
if(alphaTimer>1.f)alpha=util::lerp(0,255,1-(alphaTimer-1));
|
||||
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;
|
||||
case PREPARE_ATTACK:{
|
||||
m.F(A::ATTACK_COOLDOWN)-=fElapsedTime;
|
||||
if(m.F(A::ATTACK_COOLDOWN)<=0.f){
|
||||
Arc attackArc{GetAttackArc(m)};
|
||||
if(attackArc.overlaps(game->GetPlayer()->GetPos())){
|
||||
game->GetPlayer()->Knockback(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*"Attack Knockback"_F);
|
||||
game->GetPlayer()->Hurt(m.GetAttack(),m.OnUpperLevel(),m.GetZ());
|
||||
m.PerformAnimation("ATTACKING");
|
||||
m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Attack Effect Time");
|
||||
m.F(A::SWING_OCCURRED)=ConfigFloat("Attack Swing Damage Wait Time");
|
||||
}
|
||||
}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;
|
||||
}
|
||||
}
|
@ -1208,6 +1208,12 @@ MonsterStrategy
|
||||
Attack Arc = 60deg
|
||||
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
|
||||
|
||||
# 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){
|
||||
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 "olcUTIL_Geometry2D.h"
|
||||
#include "olcPGEX_TTF.h"
|
||||
#include "Direction.h"
|
||||
#include <random>
|
||||
|
||||
namespace olc::util{
|
||||
@ -54,6 +55,7 @@ namespace olc::util{
|
||||
float angleTo(vf2d posFrom,vf2d posTo);
|
||||
float degToRad(float deg);
|
||||
float radToDeg(float rad);
|
||||
const float dirToAngle(const Direction dir);
|
||||
|
||||
#pragma region Lerp templates + specializations
|
||||
template<class T,class U>
|
||||
|
Loading…
x
Reference in New Issue
Block a user