Wolf behavior implemented. Fixed facing direction behavior for AI scripts.
This commit is contained in:
parent
acaf1bc3bf
commit
db9c35f813
@ -1983,7 +1983,7 @@ geom2d::rect<float>AiL::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
|
||||
int tileID=layer.tiles[int(pos.y)/GetCurrentMapData().tilewidth][int(pos.x)/GetCurrentMapData().tilewidth]-1;
|
||||
if(tileID!=-1&&GetTileSheet(map,tileID%1000000).tileset->collision.find(tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1)!=GetTileSheet(map,tileID%1000000).tileset->collision.end()){
|
||||
geom2d::rect<float>collisionRect=GetTileSheet(map,tileID%1000000).tileset->collision[tileID%1000000-GetTileSheet(map,tileID%1000000).firstgid+1].collision;
|
||||
if(foundRect.pos==NO_COLLISION.pos&&foundRect.size==NO_COLLISION.size){
|
||||
if(foundRect==NO_COLLISION){
|
||||
foundRect=collisionRect;
|
||||
}else{
|
||||
//When we find another rectangle in the same square, we expand it to consume the area, whichever tile creates a larger surface or the combination of the two.
|
||||
|
@ -74,4 +74,10 @@ public:
|
||||
}
|
||||
return std::get<vf2d>(attributes[a]);
|
||||
};
|
||||
inline std::vector<std::any>&GetVec(Attribute a){
|
||||
if(attributes.count(a)==0){
|
||||
attributes[a]=std::vector<std::any>{};
|
||||
}
|
||||
return std::get<std::vector<std::any>>(attributes[a]);
|
||||
};
|
||||
};
|
@ -65,7 +65,7 @@ using BackdropName=std::string;
|
||||
|
||||
#define ACCESS_PLAYER Player*p=game->GetPlayer();
|
||||
|
||||
#define VARIANTS float,int,std::string,bool,vf2d
|
||||
#define VARIANTS float,int,std::string,bool,vf2d,std::vector<std::any>
|
||||
#undef INFINITE
|
||||
#define INFINITE 999999
|
||||
|
||||
|
@ -125,7 +125,7 @@ bool Monster::_SetX(float x,const bool monsterInvoked){
|
||||
vf2d newPos={x,pos.y};
|
||||
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
|
||||
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
|
||||
if(collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size){
|
||||
if(collisionRect==game->NO_COLLISION){
|
||||
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
|
||||
Moved();
|
||||
return true;
|
||||
@ -155,7 +155,7 @@ bool Monster::_SetY(float y,const bool monsterInvoked){
|
||||
vf2d newPos={pos.x,y};
|
||||
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
|
||||
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
|
||||
if(collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size){
|
||||
if(collisionRect==game->NO_COLLISION){
|
||||
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
|
||||
Moved();
|
||||
return true;
|
||||
@ -267,6 +267,15 @@ bool Monster::Update(float fElapsedTime){
|
||||
Key Monster::GetFacingDirection(){
|
||||
return facingDirection;
|
||||
}
|
||||
|
||||
void Monster::UpdateFacingDirection(vf2d facingTargetPoint){
|
||||
if(facingTargetPoint.x>GetPos().x){
|
||||
facingDirection=RIGHT;
|
||||
}
|
||||
if(facingTargetPoint.x<GetPos().x){
|
||||
facingDirection=LEFT;
|
||||
}
|
||||
}
|
||||
void Monster::Draw(){
|
||||
if(GetZ()>0){
|
||||
vf2d shadowScale=vf2d{8*GetSizeMult()/3.f,1}/std::max(1.f,GetZ()/24);
|
||||
@ -458,6 +467,10 @@ void Monster::AddBuff(BuffType type,float duration,float intensity){
|
||||
buffList.push_back(Buff{type,duration,intensity});
|
||||
}
|
||||
|
||||
void Monster::RemoveBuff(BuffType type){
|
||||
std::erase_if(buffList,[&](const Buff&buff){return buff.type==type;});
|
||||
}
|
||||
|
||||
bool Monster::StartPathfinding(float pathingTime){
|
||||
SetState(State::PATH_AROUND);
|
||||
path=game->pathfinder.Solve_WalkPath(pos,target,12,OnUpperLevel());
|
||||
@ -480,11 +493,7 @@ void Monster::PathAroundBehavior(float fElapsedTime){
|
||||
pathIndex=0;
|
||||
targetAcquireTimer=0;
|
||||
}
|
||||
if(moveTowardsLine.vector().x>0){
|
||||
facingDirection=RIGHT;
|
||||
} else {
|
||||
facingDirection=LEFT;
|
||||
}
|
||||
UpdateFacingDirection(moveTowardsLine.end);
|
||||
}else{
|
||||
if(pathIndex>=path.points.size()-1){
|
||||
//We have reached the end of the path!
|
||||
|
@ -133,6 +133,8 @@ public:
|
||||
bool IsAlive();
|
||||
vf2d&GetTargetPos();
|
||||
Key GetFacingDirection();
|
||||
//Will make the monster face in the correct direction relative to a given target point to look at.
|
||||
void UpdateFacingDirection(vf2d facingTargetPoint);
|
||||
void Draw();
|
||||
void DrawReflection(float drawRatioX,float multiplierX);
|
||||
void Collision(Player*p);
|
||||
@ -155,6 +157,8 @@ public:
|
||||
void PathAroundBehavior(float fElapsedTime);
|
||||
void AddBuff(BuffType type,float duration,float intensity);
|
||||
std::vector<Buff>GetBuffs(BuffType buff);
|
||||
//Removes all buffs of a given type.
|
||||
void RemoveBuff(BuffType type);
|
||||
State::State GetState();
|
||||
void SetState(State::State newState);
|
||||
static void InitializeStrategies();
|
||||
|
@ -42,6 +42,7 @@ All rights reserved.
|
||||
#define S(attr) GetString(attr)
|
||||
#define B(attr) GetBool(attr)
|
||||
#define V(attr) GetVf2d(attr)
|
||||
#define VEC(attr) GetVec(attr)
|
||||
|
||||
enum class Attribute{
|
||||
IFRAME_TIME_UPON_HIT, //When this is set, the monster gains iframes if they take damage based on the value this is set to.
|
||||
@ -79,4 +80,9 @@ enum class Attribute{
|
||||
PHASE,
|
||||
LOCKON_WAITTIME,
|
||||
LOCKON_POS,
|
||||
TARGET_TIMER,
|
||||
DISENGAGE_PATH,
|
||||
PATH_INDEX,
|
||||
PATH_DIR,
|
||||
PREV_POS,
|
||||
};
|
@ -63,13 +63,13 @@ void Pathfinding::Initialize(){
|
||||
node.x = x; // ...because we give each node its own coordinates
|
||||
node.y = y; // ...because we give each node its own coordinates
|
||||
geom2d::rect<float>tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x),float(y)});
|
||||
if(tile.pos==game->NO_COLLISION.pos&&tile.size==game->NO_COLLISION.size){
|
||||
if(tile==game->NO_COLLISION){
|
||||
node.bObstacle=false;
|
||||
}else{
|
||||
node.bObstacle=geom2d::overlaps(vf2d{fmodf(x+gridSpacing.x/2,24),fmodf(y+gridSpacing.y/2,24)},tile);
|
||||
}
|
||||
tile=game->GetTileCollision(game->GetCurrentLevel(),{float(x),float(y)},true);
|
||||
if(tile.pos==game->NO_COLLISION.pos&&tile.size==game->NO_COLLISION.size){
|
||||
if(tile==game->NO_COLLISION){
|
||||
node.bObstacleUpper=false;
|
||||
}else{
|
||||
node.bObstacleUpper=geom2d::overlaps(vf2d{fmodf(x,24),fmodf(y,24)},tile);
|
||||
|
@ -110,7 +110,7 @@ bool Player::_SetX(float x,const bool playerInvoked){
|
||||
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
|
||||
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
|
||||
#pragma region lambdas
|
||||
auto NoTileCollisionExistsHere=[&](){return collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size;};
|
||||
auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;};
|
||||
#pragma endregion
|
||||
if(NoTileCollisionExistsHere()){
|
||||
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
|
||||
@ -143,7 +143,7 @@ bool Player::_SetY(float y,const bool playerInvoked){
|
||||
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
|
||||
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
|
||||
#pragma region lambdas
|
||||
auto NoTileCollisionExistsHere=[&](){return collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size;};
|
||||
auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;};
|
||||
#pragma endregion
|
||||
if(NoTileCollisionExistsHere()){
|
||||
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
|
||||
|
@ -159,11 +159,15 @@ public:
|
||||
void AddBuff(BuffType type,float duration,float intensity,std::set<ItemAttribute>attr);
|
||||
void AddBuff(BuffType type,float duration,float intensity,std::set<std::string>attr);
|
||||
void AddBuff(BuffType type,float duration,float intensity,float timeBetweenTicks,std::function<void(AiL*,int)>repeatAction);
|
||||
|
||||
const std::vector<Buff>GetBuffs(BuffType buff)const;
|
||||
const std::vector<Buff>GetStatBuffs(const std::vector<std::string>&attr)const;
|
||||
void RemoveBuff(BuffType type); //Removes the first buff found.
|
||||
void RemoveAllBuffs(BuffType type); //Removes all buffs of a certain type.
|
||||
void RemoveAllBuffs(); //Remove every buff.
|
||||
//Removes the first buff found.
|
||||
void RemoveBuff(BuffType type);
|
||||
//Removes all buffs of a certain type.
|
||||
void RemoveAllBuffs(BuffType type);
|
||||
//Remove every buff.
|
||||
void RemoveAllBuffs();
|
||||
|
||||
void RecalculateEquipStats();
|
||||
|
||||
|
@ -58,6 +58,9 @@ void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,std::string str
|
||||
}
|
||||
m.SetState(State::MOVE_TOWARDS);
|
||||
}
|
||||
if(geom2d::line(m.pos,m.target).length()>4.f){
|
||||
m.UpdateFacingDirection(m.target);
|
||||
}
|
||||
switch(m.state){
|
||||
case State::MOVE_TOWARDS:{
|
||||
if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){
|
||||
|
@ -101,11 +101,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,std::string stra
|
||||
if(line.length()<=24.f*ConfigInt("CloseInRange")/100.0f){
|
||||
m.SetState(State::NORMAL);
|
||||
}
|
||||
if(moveTowardsLine.vector().x>0){
|
||||
m.facingDirection=RIGHT;
|
||||
} else {
|
||||
m.facingDirection=LEFT;
|
||||
}
|
||||
m.UpdateFacingDirection(moveTowardsLine.end);
|
||||
m.PerformJumpAnimation();
|
||||
}break;
|
||||
case State::MOVE_AWAY:{
|
||||
@ -129,11 +125,7 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,std::string stra
|
||||
if((m.path.points.size()==0&&!m.canMove)||line.length()>=24.f*ConfigInt("Range")/100.f){
|
||||
m.SetState(State::NORMAL);
|
||||
}
|
||||
if(moveTowardsLine.vector().x>0){
|
||||
m.facingDirection=RIGHT;
|
||||
} else {
|
||||
m.facingDirection=LEFT;
|
||||
}
|
||||
m.UpdateFacingDirection(moveTowardsLine.end);
|
||||
m.PerformJumpAnimation();
|
||||
}break;
|
||||
case State::PATH_AROUND:{
|
||||
|
@ -12,6 +12,8 @@ Settings Menu
|
||||
|
||||
January 31st
|
||||
============
|
||||
Make new unlocked nodes more obvious, made neighboring nodes more obvious
|
||||
|
||||
Implement the rest of the enemy types:
|
||||
- Baby Wolf
|
||||
- Wolf
|
||||
|
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_PATCH 1
|
||||
#define VERSION_BUILD 5761
|
||||
#define VERSION_BUILD 5794
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
@ -124,7 +124,7 @@ void Wizard::InitializeClassAbilities(){
|
||||
vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
|
||||
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,p->OnUpperLevel());
|
||||
#pragma region lambdas
|
||||
auto NoTileCollisionExistsHere=[&](){return collisionRect.pos==game->NO_COLLISION.pos&&collisionRect.size==game->NO_COLLISION.size;};
|
||||
auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;};
|
||||
#pragma endregion
|
||||
collisionRect.pos+=tilePos;
|
||||
#pragma region lambdas
|
||||
|
@ -51,20 +51,74 @@ using A=Attribute;
|
||||
void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){
|
||||
switch(m.I(A::PHASE)){
|
||||
case 0:{ //Run towards the player.
|
||||
float distToPlayer = geom2d::line<float>(game->GetPlayer()->GetPos(),m.GetPos()).length();
|
||||
if(distToPlayer<=ConfigFloat("Lockon Range")){
|
||||
float distToPlayer=geom2d::line<float>(game->GetPlayer()->GetPos(),m.GetPos()).length();
|
||||
if(distToPlayer<=ConfigFloat("Lockon Range")/100*24.f){
|
||||
m.I(A::PHASE)=1;
|
||||
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
|
||||
m.AddBuff(BuffType::)
|
||||
m.V(A::LOCKON_POS)=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).upoint(1.2f);
|
||||
m.AddBuff(BuffType::LOCKON_SPEEDBOOST,INFINITE,ConfigFloat("Lockon Speed Boost")/100);
|
||||
m.F(A::TARGET_TIMER)=5.0f;
|
||||
}
|
||||
|
||||
m.target=game->GetPlayer()->GetPos();
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}break;
|
||||
case 1:{ //Charge the player.
|
||||
|
||||
m.F(A::TARGET_TIMER)=std::max(0.f,m.F(A::TARGET_TIMER)-fElapsedTime);
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.V(A::LOCKON_POS)).length();
|
||||
if(distToTarget<=12.f||m.F(A::TARGET_TIMER)==0.f){
|
||||
m.I(A::PHASE)=2;
|
||||
m.F(A::RECOVERY_TIME)=ConfigFloat("Charge Recovery Time");
|
||||
m.PerformIdleAnimation();
|
||||
}else{
|
||||
m.target=m.V(A::LOCKON_POS);
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}
|
||||
}break;
|
||||
case 3:{
|
||||
|
||||
case 2:{ //Recovery period after charging.
|
||||
m.F(A::RECOVERY_TIME)=std::max(0.f,m.F(A::RECOVERY_TIME)-fElapsedTime);
|
||||
if(m.F(A::RECOVERY_TIME)==0.f){
|
||||
m.RemoveBuff(BuffType::LOCKON_SPEEDBOOST);
|
||||
m.AddBuff(BuffType::SPEEDBOOST,ConfigFloatArr("Disengage Speed Boost",1),ConfigFloatArr("Disengage Speed Boost",0)/100);
|
||||
std::vector<vf2d>disengagePoints;
|
||||
for(float angle=0.f;angle<360.f;angle+=22.5f){
|
||||
float range=50;
|
||||
vf2d checkLoc=vf2d{0,util::degToRad(angle)}.cart();
|
||||
while(range<=ConfigFloat("Disengage Range")){
|
||||
checkLoc=game->GetPlayer()->GetPos()+vf2d{range/100*24.f,util::degToRad(angle)}.cart();
|
||||
if(game->GetTileCollision(game->GetCurrentMapName(),checkLoc,m.OnUpperLevel())!=game->NO_COLLISION){
|
||||
break;
|
||||
}
|
||||
range+=50;
|
||||
}
|
||||
disengagePoints.push_back(checkLoc);
|
||||
}
|
||||
m.path.Initialize(disengagePoints);
|
||||
if(disengagePoints.size()!=16)ERR(std::format("Disengage Points size was not equal to 16! Size: {}",disengagePoints.size()));
|
||||
if(m.path.points.size()!=16)ERR(std::format("Path Points size was not equal to 16! Size: {}",m.path.points.size()));
|
||||
m.I(A::PATH_DIR)=util::random()%2;
|
||||
if(m.I(A::PATH_DIR)==0)m.I(A::PATH_DIR)=-1;
|
||||
m.pathIndex=util::random()%disengagePoints.size();
|
||||
m.I(A::PHASE)=3;
|
||||
m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Disengage Duration");
|
||||
m.PerformJumpAnimation();
|
||||
}
|
||||
}break;
|
||||
case 3:{ //Disengage.
|
||||
m.F(A::RUN_AWAY_TIMER)=std::max(0.f,m.F(A::RUN_AWAY_TIMER)-fElapsedTime);
|
||||
m.pathIndex+=m.I(A::PATH_DIR)*fElapsedTime*1.25f;
|
||||
m.pathIndex=fmod(m.pathIndex,m.path.points.size());
|
||||
if(m.pathIndex<0){ //When the modulus is negative, adding the total number of elements gets us to the correct index.
|
||||
m.pathIndex=m.pathIndex+m.path.points.size();
|
||||
}
|
||||
if(m.F(A::RUN_AWAY_TIMER)==0.f){
|
||||
m.I(A::PHASE)=0;
|
||||
}
|
||||
m.target=m.path.GetSplinePoint(m.pathIndex).pos;
|
||||
geom2d::line<float>moveTowardsLine=geom2d::line(m.pos,m.path.GetSplinePoint(m.pathIndex).pos);
|
||||
|
||||
if(moveTowardsLine.length()>4.f){
|
||||
m.UpdateFacingDirection(moveTowardsLine.end);
|
||||
}
|
||||
m.SetPos(m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult());
|
||||
}break;
|
||||
}
|
||||
}
|
@ -1075,7 +1075,7 @@
|
||||
</object>
|
||||
<object id="4" name="Green Slime" type="Monster" x="1052" y="4304">
|
||||
<properties>
|
||||
<property name="Type" propertytype="MonsterName" value="Frog"/>
|
||||
<property name="Type" propertytype="MonsterName" value="Green Slime"/>
|
||||
<property name="spawner" type="object" value="2"/>
|
||||
</properties>
|
||||
<point/>
|
||||
@ -1716,14 +1716,14 @@
|
||||
</object>
|
||||
<object id="151" name="Green Slime" type="Monster" x="1056" y="4386">
|
||||
<properties>
|
||||
<property name="Type" propertytype="MonsterName" value="Frog"/>
|
||||
<property name="Type" propertytype="MonsterName" value="Green Slime"/>
|
||||
<property name="spawner" type="object" value="2"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
<object id="155" name="Green Slime" type="Monster" x="1050" y="4218">
|
||||
<properties>
|
||||
<property name="Type" propertytype="MonsterName" value="Frog"/>
|
||||
<property name="Type" propertytype="MonsterName" value="Green Slime"/>
|
||||
<property name="spawner" type="object" value="2"/>
|
||||
</properties>
|
||||
<point/>
|
||||
|
@ -198,10 +198,16 @@ MonsterStrategy
|
||||
# The speed boost percentage to increase by during the charge.
|
||||
Lockon Speed Boost = 15%
|
||||
|
||||
# Time waiting after charging.
|
||||
Charge Recovery Time = 1.0s
|
||||
|
||||
# The speed boost percentage to increase by and the duration while disengaging.
|
||||
Disengage Speed Boost = 30%, 3s
|
||||
|
||||
# The distance to disengage the player.
|
||||
Disengage Range = 800
|
||||
|
||||
# The amount of time to spend disengaged.
|
||||
Disengage Duration = 3.0s
|
||||
}
|
||||
}
|
@ -221,7 +221,7 @@ Monsters
|
||||
|
||||
XP = 14
|
||||
|
||||
Strategy = Run Towards
|
||||
Strategy = Wolf
|
||||
|
||||
#Size of each animation frame
|
||||
SheetFrameSize = 24,24
|
||||
|
@ -2428,6 +2428,11 @@ namespace olc::utils::geom2d
|
||||
|
||||
return internal::filter_duplicate_points(intersections);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline const bool operator ==(const rect<T>&r1,const rect<T>&r2){
|
||||
return r1.pos==r2.pos&&r1.size==r2.size;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace olc;
|
||||
|
Loading…
x
Reference in New Issue
Block a user