From 6a12a45cb27046a82236836cd14ecd3460361eb2 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Sun, 13 Aug 2023 02:12:19 -0500 Subject: [PATCH] Moved all enemy reading over to purely using config files. Removed all enemy enum dependencies. Removed bugs involving loading infinite maps by accident. --- Crawler/Animation.cpp | 49 ------- Crawler/Crawler.cpp | 7 +- Crawler/Crawler.h | 2 +- Crawler/Crawler.vcxproj | 4 + Crawler/Crawler.vcxproj.filters | 15 ++ Crawler/DEFINES.h | 4 +- Crawler/Monster.cpp | 145 +++----------------- Crawler/Monster.h | 57 ++++---- Crawler/MonsterData.cpp | 120 +++++++++++----- Crawler/Player.cpp | 1 + Crawler/RUN_STRATEGY.cpp | 15 ++ Crawler/RunTowards.cpp | 35 +++++ Crawler/ShootAfar.cpp | 95 +++++++++++++ Crawler/TMXParser.h | 12 +- Crawler/Turret.cpp | 5 + Crawler/Version.h | 2 +- Crawler/assets/config/MonsterStrategies.txt | 12 +- Crawler/assets/config/Monsters.txt | 65 +++++++-- Crawler/assets/config/Player.txt | 2 +- Crawler/assets/config/gfx/gfx.txt | 1 - Crawler/assets/monsters/Blue Slime.png | Bin 0 -> 2751 bytes Crawler/assets/monsters/Flower Turret.png | Bin 0 -> 10853 bytes Crawler/assets/monsters/Flower Turret.xcf | Bin 0 -> 18081 bytes Crawler/assets/monsters/Green Slime.png | Bin 0 -> 2732 bytes Crawler/assets/monsters/Red Slime.png | Bin 0 -> 2655 bytes Crawler/assets/monsters/Yellow Slime.png | Bin 0 -> 2749 bytes 26 files changed, 377 insertions(+), 271 deletions(-) create mode 100644 Crawler/RUN_STRATEGY.cpp create mode 100644 Crawler/RunTowards.cpp create mode 100644 Crawler/ShootAfar.cpp create mode 100644 Crawler/Turret.cpp create mode 100644 Crawler/assets/monsters/Blue Slime.png create mode 100644 Crawler/assets/monsters/Flower Turret.png create mode 100644 Crawler/assets/monsters/Flower Turret.xcf create mode 100644 Crawler/assets/monsters/Green Slime.png create mode 100644 Crawler/assets/monsters/Red Slime.png create mode 100644 Crawler/assets/monsters/Yellow Slime.png diff --git a/Crawler/Animation.cpp b/Crawler/Animation.cpp index a128bf2b..8f76f1b7 100644 --- a/Crawler/Animation.cpp +++ b/Crawler/Animation.cpp @@ -171,55 +171,6 @@ void sig::Animation::InitializeAnimations(){ } ANIMATION_DATA["WIZARD_CAST_W"]=pl_wizard_cast_w; - //Load slime animations. - for(int slime=0;slime<4;slime++){ - std::string colorName=""; - switch(slime){ - case 0:{ - colorName="GREEN"; - }break; - case 1:{ - colorName="BLUE"; - }break; - case 2:{ - colorName="RED"; - }break; - case 3:{ - colorName="YELLOW"; - }break; - } - for(int state=0;state<5;state++){ - Animate2D::FrameSequence anim; - if(state==4){//These are death animations. - anim=Animate2D::FrameSequence(0.1f,Animate2D::Style::OneShot); - } - if(state==2){//These are death animations. - anim=Animate2D::FrameSequence(0.06f); - } - for (int frame=0;frame<10;frame++){ - anim.AddFrame({&game->GFX_Slime_Sheet,{vi2d{frame,state+5*slime}*24,{24,24}}}); - } - std::string stateName=""; - switch(state){ - case 0:{ - stateName="IDLE"; - }break; - case 1:{ - stateName="ROLL"; - }break; - case 2:{ - stateName="JUMP"; - }break; - case 3:{ - stateName="SPIT"; - }break; - case 4:{ - stateName="DIE"; - }break; - } - ANIMATION_DATA[colorName+"_SLIME_"+stateName]=anim; - } - } CreateHorizontalAnimationSequence(game->GFX_Effect_GroundSlam_Back,5,{64,64},"GROUND_SLAM_ATTACK_BACK",{0.02,Animate2D::Style::OneShot}); CreateHorizontalAnimationSequence(game->GFX_Effect_GroundSlam_Front,5,{64,64},"GROUND_SLAM_ATTACK_FRONT",{0.02,Animate2D::Style::OneShot}); CreateHorizontalAnimationSequence(game->GFX_Battlecry_Effect,5,{84,84},"BATTLECRY_EFFECT",{0.02,Animate2D::Style::OneShot}); diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index d0eafe32..a650a571 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -94,7 +94,6 @@ bool Crawler::OnUserCreate(){ //Graphics LOADIMG(GFX_Warrior_Sheet) - LOADIMG(GFX_Slime_Sheet) LOADIMG(GFX_Circle) LOADIMG(GFX_Effect_GroundSlam_Back) LOADIMG(GFX_Effect_GroundSlam_Front) @@ -904,14 +903,12 @@ void Crawler::LoadLevel(MapName map){ for(auto key:MAP_DATA[map].SpawnerData){ SpawnerTag&spawnData=MAP_DATA[map].SpawnerData[key.first]; - std::vector>monster_list; + std::vector>monster_list; vf2d spawnerRadius=vf2d{spawnData.ObjectData.GetFloat("width"),spawnData.ObjectData.GetFloat("height")}/2; for(XMLTag&monster:spawnData.monsters){ int monsterTypeID=monster.GetInteger("value")-1; - if(monsterTypeID>=0&&monsterTypeIDplayer; - Renderable GFX_Warrior_Sheet,GFX_Slime_Sheet, + Renderable GFX_Warrior_Sheet, GFX_Effect_GroundSlam_Back,GFX_Effect_GroundSlam_Front, GFX_Heart,GFX_BLOCK_BUBBLE,GFX_Ranger_Sheet,GFX_Wizard_Sheet, GFX_Battlecry_Effect,GFX_Mana,GFX_SonicSlash,GFX_EnergyParticle, diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index 136f6737..85ddd133 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -305,6 +305,7 @@ + @@ -312,8 +313,11 @@ + + + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 9efb89ab..2fd7327f 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -37,6 +37,9 @@ {fd547111-0670-4be5-85cf-28fbd92c765f} + + {3d2f7a3f-5781-45ab-a66d-c6d57d9de13c} + @@ -215,6 +218,18 @@ Source Files\Bullet Types + + Source Files\Monster Strategies + + + Source Files\Monster Strategies + + + Source Files\Monster Strategies + + + Source Files\Monster Strategies + diff --git a/Crawler/DEFINES.h b/Crawler/DEFINES.h index 491ccc1e..7b31598b 100644 --- a/Crawler/DEFINES.h +++ b/Crawler/DEFINES.h @@ -4,12 +4,12 @@ #define INCLUDE_SPAWNER_LIST extern std::vectorSPAWNER_LIST; #define INCLUDE_DAMAGENUMBER_LIST extern std::vector>DAMAGENUMBER_LIST; #define INCLUDE_game extern Crawler*game; -#define INCLUDE_MONSTER_DATA extern std::mapMONSTER_DATA; +#define INCLUDE_MONSTER_DATA extern std::mapMONSTER_DATA; #define INCLUDE_BULLET_LIST extern std::vector>BULLET_LIST; #define INCLUDE_PARTICLE_LIST extern std::vectorPARTICLE_LIST; #define INCLUDE_EMITTER_LIST extern std::vector>EMITTER_LIST; #define INCLUDE_DATA extern utils::datafile DATA; -#define INCLUDE_STRATEGY_DATA extern safemapSTRATEGY_DATA; +#define INCLUDE_STRATEGY_DATA extern safemapSTRATEGY_DATA; #define ACCESS_PLAYER Player*p=game->GetPlayer(); diff --git a/Crawler/Monster.cpp b/Crawler/Monster.cpp index 0a0e1be3..56e907c9 100644 --- a/Crawler/Monster.cpp +++ b/Crawler/Monster.cpp @@ -15,10 +15,11 @@ INCLUDE_BULLET_LIST INCLUDE_DATA INCLUDE_STRATEGY_DATA -safemapSTRATEGY_DATA; +safemapSTRATEGY_DATA; +std::mapMonsterData::imgs; Monster::Monster(vf2d pos,MonsterData data,bool upperLevel): - pos(pos),hp(data.GetHealth()),maxhp(data.GetHealth()),atk(data.GetAttack()),moveSpd(data.GetMoveSpdMult()),size(data.GetSizeMult()),strategy(data.GetAIStrategy()),type(data.GetType()),upperLevel(upperLevel){ + pos(pos),hp(data.GetHealth()),maxhp(data.GetHealth()),atk(data.GetAttack()),moveSpd(data.GetMoveSpdMult()),size(data.GetSizeMult()),strategy(data.GetAIStrategy()),id(data.GetID()),upperLevel(upperLevel){ bool firstAnimation=true; for(std::string&anim:data.GetAnimations()){ animation.AddState(anim,ANIMATION_DATA[anim]); @@ -59,10 +60,13 @@ void Monster::UpdateAnimation(std::string state){ animation.ChangeState(internal_animState,state); } void Monster::PerformJumpAnimation(){ - animation.ChangeState(internal_animState,MONSTER_DATA[type].GetJumpAnimation()); + animation.ChangeState(internal_animState,MONSTER_DATA[id].GetJumpAnimation()); } void Monster::PerformShootAnimation(){ - animation.ChangeState(internal_animState,MONSTER_DATA[type].GetShootAnimation()); + animation.ChangeState(internal_animState,MONSTER_DATA[id].GetShootAnimation()); +} +void Monster::PerformIdleAnimation(){ + animation.ChangeState(internal_animState,MONSTER_DATA[id].GetIdleAnimation()); } bool Monster::SetX(float x){ vf2d newPos={x,pos.y}; @@ -138,124 +142,7 @@ bool Monster::Update(float fElapsedTime){ facingDirection=LEFT; } } - switch(strategy){ - case RUN_TOWARDS:{ - targetAcquireTimer=std::max(0.f,targetAcquireTimer-fElapsedTime); - if(targetAcquireTimer==0){ - targetAcquireTimer=3; - target=geom2d::line(pos,game->GetPlayer()->GetPos()).upoint(1.2); - SetState(MOVE_TOWARDS); - hasHitPlayer=false; - } - switch(state){ - case MOVE_TOWARDS:{ - if(geom2d::line(pos,target).length()>100*fElapsedTime*GetMoveSpdMult()){ - vf2d newPos=pos+geom2d::line(pos,target).vector().norm()*100*fElapsedTime*GetMoveSpdMult(); - if(!SetX(newPos.x)||!SetY(newPos.y)){ - StartPathfinding(4); - } - PerformJumpAnimation(); - } else { - SetState(NORMAL);//Revert state once we've finished moving towards target. - UpdateAnimation(MONSTER_DATA[type].GetAnimations()[0]); - } - }break; - case PATH_AROUND:{ - PathAroundBehavior(fElapsedTime); - }break; - default:{ - } - } - }break; - case SHOOT_AFAR:{ - targetAcquireTimer=std::max(0.f,targetAcquireTimer-fElapsedTime); - attackCooldownTimer=std::max(0.f,attackCooldownTimer-fElapsedTime); - if(queueShotTimer>0){ - queueShotTimer-=fElapsedTime; - if(queueShotTimer<0){ - queueShotTimer=0; - { - BULLET_LIST.push_back(std::make_unique(Bullet(pos + vf2d{ 0,-4 }, geom2d::line(pos + vf2d{ 0,-4 }, game->GetPlayer()->GetPos()).vector().norm() * 24 * 3.f, 2, GetAttack(),upperLevel,false, { 75 / 2,162 / 2,225 / 2 }))); - } - } - } - geom2d::line line(pos,game->GetPlayer()->GetPos()); - if(targetAcquireTimer==0&&queueShotTimer==0){ - targetAcquireTimer=1; - if(line.length()<24*6){ - target=line.upoint(-1.2); - if(canMove){ - SetState(MOVE_AWAY); - } else { - SetState(NORMAL); - } - } else - if(line.length()>24*7){ - target=line.upoint(1.2); - SetState(MOVE_TOWARDS); - } else { - SetState(NORMAL); - } - } - canMove=true; - geom2d::line moveTowardsLine=geom2d::line(pos,target); - bool pathfindingDecision=false; - switch(state){ - case MOVE_TOWARDS:{ - if(moveTowardsLine.length()>1){ - vf2d newPos=pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult(); - bool movedX=SetX(newPos.x); - bool movedY=SetY(newPos.y); - pathfindingDecision=movedX|movedY; - canMove=movedX&&movedY; - } - if(!pathfindingDecision){ - StartPathfinding(2.5); - }else - if(line.length()<=24*7){ - SetState(NORMAL); - } - if(moveTowardsLine.vector().x>0){ - facingDirection=RIGHT; - } else { - facingDirection=LEFT; - } - PerformJumpAnimation(); - }break; - case MOVE_AWAY:{ - if(moveTowardsLine.length()>1){ - vf2d newPos=pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult(); - bool movedX=SetX(newPos.x); - bool movedY=SetY(newPos.y); - pathfindingDecision=movedX|movedY; - canMove=movedX&&movedY; - } - if(!pathfindingDecision){ - StartPathfinding(2.5); - }else - if(line.length()>=24*6){ - SetState(NORMAL); - } - if(moveTowardsLine.vector().x>0){ - facingDirection=RIGHT; - } else { - facingDirection=LEFT; - } - PerformJumpAnimation(); - }break; - case PATH_AROUND:{ - PathAroundBehavior(fElapsedTime); - }break; - default:{ - if(attackCooldownTimer==0){ - attackCooldownTimer=1; - queueShotTimer=0.7; - PerformShootAnimation(); - } - } - } - }break; - } + Monster::STRATEGY::RUN_STRATEGY(*this,fElapsedTime); if(vel.x>0){ vel.x=std::max(0.f,vel.x-friction*fElapsedTime); } else { @@ -292,9 +179,9 @@ void Monster::Draw(){ } } void Monster::Collision(Player*p){ - if(MONSTER_DATA[type].GetCollisionDmg()>0&&!hasHitPlayer){ + if(MONSTER_DATA[id].GetCollisionDmg()>0&&!hasHitPlayer){ hasHitPlayer=true; - p->Hurt(MONSTER_DATA[type].GetCollisionDmg(),OnUpperLevel()); + p->Hurt(MONSTER_DATA[id].GetCollisionDmg(),OnUpperLevel()); } Collision(); } @@ -302,7 +189,7 @@ void Monster::Collision(Monster&m){ Collision(); } void Monster::Collision(){ - if(strategy==RUN_TOWARDS&&GetState()==MOVE_TOWARDS){ + if(strategy==0&&GetState()==MOVE_TOWARDS){//The run towards strategy causes state to return to normal upon a collision. SetState(NORMAL); } } @@ -331,7 +218,7 @@ void Monster::Moved(){ } } std::string Monster::GetDeathAnimationName(){ - return MONSTER_DATA[type].GetDeathAnimation(); + return MONSTER_DATA[id].GetDeathAnimation(); } bool Monster::Hurt(int damage,bool onUpperLevel){ if(hp<=0||onUpperLevel!=OnUpperLevel()) return false; @@ -363,7 +250,7 @@ vf2d&Monster::GetTargetPos(){ } MonsterSpawner::MonsterSpawner(){} -MonsterSpawner::MonsterSpawner(vf2d pos,vf2d range,std::vector>monsters,bool upperLevel): +MonsterSpawner::MonsterSpawner(vf2d pos,vf2d range,std::vector>monsters,bool upperLevel): pos(pos),range(range),monsters(monsters),upperLevel(upperLevel){ } bool MonsterSpawner::SpawnTriggered(){ @@ -378,7 +265,7 @@ vf2d MonsterSpawner::GetPos(){ void MonsterSpawner::SetTriggered(bool trigger,bool spawnMonsters){ triggered=trigger; if(spawnMonsters){ - for(std::pair&monsterInfo:monsters){ + for(std::pair&monsterInfo:monsters){ MONSTER_LIST.push_back(Monster(pos+monsterInfo.second,MONSTER_DATA[monsterInfo.first],DoesUpperLevelSpawning())); } } @@ -448,7 +335,7 @@ void Monster::SetState(State newState){ void Monster::InitializeStrategies(){ int readCounter=0; while(DATA["MonsterStrategy"].HasProperty(std::to_string(readCounter))){ - STRATEGY_DATA[DATA["MonsterStrategy"][std::to_string(readCounter)]["Name"].GetString()]=MonsterStrategy(readCounter); + STRATEGY_DATA[DATA["MonsterStrategy"][std::to_string(readCounter)]["Name"].GetString()]=readCounter; readCounter++; } STRATEGY_DATA.SetInitialized(); diff --git a/Crawler/Monster.h b/Crawler/Monster.h index e088b0de..9ff83493 100644 --- a/Crawler/Monster.h +++ b/Crawler/Monster.h @@ -8,52 +8,38 @@ struct Player; -enum MonsterStrategy{ - /// - /// NOTE: When adding a new strategy, update MonsterStrategies.txt!! - /// - RUN_TOWARDS, - SHOOT_AFAR, - TURRET -}; - -enum MonsterName{ - SLIME_GREEN, - SLIME_BLUE, - SLIME_RED, - SLIME_YELLOW, - FLOWER_TURRET, - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////// - /*//*/END,//Used for detecting the end of the list, DO NOT USE OR TOUCH. Add all monsters above this//*//*/ - /////////////////////////////////////////////////////////////////////////////////////////////////////////// +enum MonsterAnimation{ + IDLE, + JUMP, + SHOOT, + DEATH }; struct MonsterData{ private: + int id; + std::string name; int hp; int atk; float moveSpd;//1.0=100% float size; std::vector animations; - MonsterStrategy strategy; - MonsterName type; + int strategy; int collisionDmg; std::string jumpAnimation="WARRIOR_IDLE_S"; std::string shootAnimation="WARRIOR_IDLE_S"; std::string deathAnimation="WARRIOR_IDLE_S"; public: MonsterData(); - //When specifying animations, the first one will become the default animation. The last becomes the death animation. - MonsterData(MonsterName type,int hp,int atk,std::vectoranimations,std::string jumpAnimation,std::string shootAnimation,std::string deathAnimation,float moveSpd=1.0f,float size=1.0f,MonsterStrategy strategy=RUN_TOWARDS,int collisionDmg=0); + MonsterData(int id,std::string name,int hp,int atk,std::vectoranimations,float moveSpd=1.0f,float size=1.0f,int strategy=0,int collisionDmg=0); int GetHealth(); int GetAttack(); float GetMoveSpdMult(); float GetSizeMult(); - MonsterName GetType(); - MonsterStrategy GetAIStrategy(); + int GetAIStrategy(); int GetCollisionDmg(); + int GetID(); + std::string GetIdleAnimation(); std::string GetJumpAnimation(); std::string GetShootAnimation(); std::string GetDeathAnimation(); @@ -61,10 +47,13 @@ struct MonsterData{ return animations; } static void InitializeMonsterData(); + static std::mapimgs; }; struct Monster{ + friend struct STRATEGY; private: + int id=0; vf2d pos; vf2d vel={0,0}; float friction=400; @@ -77,13 +66,12 @@ struct Monster{ float attackCooldownTimer=0; float queueShotTimer=0; Key facingDirection; - MonsterStrategy strategy; + int strategy; State state=State::NORMAL; Animate2D::Animationanimation; Animate2D::AnimationState internal_animState; float randomFrameOffset=0.f; float deathTimer=0.f; - MonsterName type; std::vectorbuffList; std::string GetDeathAnimationName(); bool hasHitPlayer=false; @@ -125,6 +113,7 @@ public: bool SetY(float y); void PerformJumpAnimation(); void PerformShootAnimation(); + void PerformIdleAnimation(); bool OnUpperLevel(); void Moved(); void StartPathfinding(float pathingTime); @@ -134,19 +123,27 @@ public: State GetState(); void SetState(State newState); static void InitializeStrategies(); +private: + static struct STRATEGY{ + static void RUN_STRATEGY(Monster&m,float fElapsedTime); + + static void RUN_TOWARDS(Monster&m,float fElapsedTime); + static void SHOOT_AFAR(Monster&m,float fElapsedTime); + static void TURRET(Monster&m,float fElapsedTime); + }; }; struct MonsterSpawner{ private: vf2d pos; vf2d range; - std::vector>monsters; + std::vector>monsters; bool triggered=false; bool upperLevel=false; public: MonsterSpawner(); //For the monster list, the second pair item is the position relative to the spawner to spawn the monster. - MonsterSpawner(vf2d pos,vf2d range,std::vector>MONSTER_LIST,bool upperLevel=false); + MonsterSpawner(vf2d pos,vf2d range,std::vector>MONSTER_LIST,bool upperLevel=false); bool SpawnTriggered(); vf2d GetRange(); vf2d GetPos(); diff --git a/Crawler/MonsterData.cpp b/Crawler/MonsterData.cpp index 3e957fb1..75ce62a0 100644 --- a/Crawler/MonsterData.cpp +++ b/Crawler/MonsterData.cpp @@ -7,47 +7,98 @@ INCLUDE_DATA INCLUDE_STRATEGY_DATA +INCLUDE_ANIMATION_DATA -std::mapMONSTER_DATA; +std::mapMONSTER_DATA; MonsterData::MonsterData(){} -MonsterData::MonsterData(MonsterName type,int hp,int atk,std::vectoranimations,std::string jumpAnimation,std::string shootAnimation,std::string deathAnimation - ,float moveSpd,float size,MonsterStrategy strategy,int collisionDmg): - type(type),hp(hp),atk(atk),moveSpd(moveSpd),size(size),strategy(strategy),animations(animations),collisionDmg(collisionDmg) - ,jumpAnimation(jumpAnimation),shootAnimation(shootAnimation),deathAnimation(deathAnimation){ -} +MonsterData::MonsterData(int id,std::string name,int hp,int atk,std::vectoranimations,float moveSpd,float size,int strategy,int collisionDmg): + id(id),name(name),hp(hp),atk(atk),moveSpd(moveSpd),size(size),strategy(strategy),animations(animations),collisionDmg(collisionDmg){} + void MonsterData::InitializeMonsterData(){ - for(int i=0;ianimations{ - ID+"_JUMP", - ID+"_SPIT", - ID+"_DIE", - ID+"_IDLE", + MonsterName+"_IDLE", + MonsterName+"_JUMP", + MonsterName+"_SPIT", + MonsterName+"_DIE", }; + MonsterData::imgs[id]=new Renderable(); + MonsterData::imgs[id]->Load("assets/monsters/"+MonsterName+".png"); + + for(int i=0;iGetPlayer()->GetPos()).upoint(1.2); + m.SetState(MOVE_TOWARDS); + m.hasHitPlayer=false; + } + switch(m.state){ + case MOVE_TOWARDS:{ + if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){ + vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); + if(!m.SetX(newPos.x)||!m.SetY(newPos.y)){ + m.StartPathfinding(4); + } + m.PerformJumpAnimation(); + } else { + m.SetState(NORMAL);//Revert state once we've finished moving towards target. + m.UpdateAnimation(MONSTER_DATA[m.id].GetIdleAnimation()); + } + }break; + case PATH_AROUND:{ + m.PathAroundBehavior(fElapsedTime); + }break; + default:{ + } + } +} \ No newline at end of file diff --git a/Crawler/ShootAfar.cpp b/Crawler/ShootAfar.cpp new file mode 100644 index 00000000..4d5a09ad --- /dev/null +++ b/Crawler/ShootAfar.cpp @@ -0,0 +1,95 @@ +#include "Monster.h" +#include "DEFINES.h" +#include "Crawler.h" + +INCLUDE_BULLET_LIST +INCLUDE_game + +void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime){ + m.targetAcquireTimer=std::max(0.f,m.targetAcquireTimer-fElapsedTime); + m.attackCooldownTimer=std::max(0.f,m.attackCooldownTimer-fElapsedTime); + if(m.queueShotTimer>0){ + m.queueShotTimer-=fElapsedTime; + if(m.queueShotTimer<0){ + m.queueShotTimer=0; + { + BULLET_LIST.push_back(std::make_unique(Bullet(m.pos + vf2d{ 0,-4 }, geom2d::line(m.pos + vf2d{ 0,-4 }, game->GetPlayer()->GetPos()).vector().norm() * 24 * 3.f, 2, m.GetAttack(),m.upperLevel,false, { 75 / 2,162 / 2,225 / 2 }))); + } + } + } + geom2d::line line(m.pos,game->GetPlayer()->GetPos()); + if(m.targetAcquireTimer==0&&m.queueShotTimer==0){ + m.targetAcquireTimer=1; + if(line.length()<24*6){ + m.target=line.upoint(-1.2); + if(m.canMove){ + m.SetState(MOVE_AWAY); + } else { + m.SetState(NORMAL); + } + } else + if(line.length()>24*7){ + m.target=line.upoint(1.2); + m.SetState(MOVE_TOWARDS); + } else { + m.SetState(NORMAL); + } + } + m.canMove=true; + geom2d::line moveTowardsLine=geom2d::line(m.pos,m.target); + bool pathfindingDecision=false; + switch(m.state){ + case MOVE_TOWARDS:{ + if(moveTowardsLine.length()>1){ + vf2d newPos=m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); + bool movedX=m.SetX(newPos.x); + bool movedY=m.SetY(newPos.y); + pathfindingDecision=movedX|movedY; + m.canMove=movedX&&movedY; + } + if(!pathfindingDecision){ + m.StartPathfinding(2.5); + }else + if(line.length()<=24*7){ + m.SetState(NORMAL); + } + if(moveTowardsLine.vector().x>0){ + m.facingDirection=RIGHT; + } else { + m.facingDirection=LEFT; + } + m.PerformJumpAnimation(); + }break; + case MOVE_AWAY:{ + if(moveTowardsLine.length()>1){ + vf2d newPos=m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult(); + bool movedX=m.SetX(newPos.x); + bool movedY=m.SetY(newPos.y); + pathfindingDecision=movedX|movedY; + m.canMove=movedX&&movedY; + } + if(!pathfindingDecision){ + m.StartPathfinding(2.5); + }else + if(line.length()>=24*6){ + m.SetState(NORMAL); + } + if(moveTowardsLine.vector().x>0){ + m.facingDirection=RIGHT; + } else { + m.facingDirection=LEFT; + } + m.PerformJumpAnimation(); + }break; + case PATH_AROUND:{ + m.PathAroundBehavior(fElapsedTime); + }break; + default:{ + if(m.attackCooldownTimer==0){ + m.attackCooldownTimer=1; + m.queueShotTimer=0.7; + m.PerformShootAnimation(); + } + } + } +} \ No newline at end of file diff --git a/Crawler/TMXParser.h b/Crawler/TMXParser.h index 6e3da7d0..a0383563 100644 --- a/Crawler/TMXParser.h +++ b/Crawler/TMXParser.h @@ -61,6 +61,7 @@ class TMXParser{ XMLTag monsterTag; XMLTag spawnerLinkTag; std::vectoraccumulatedMonsterTags; + bool infiniteMap=false; public: TMXParser(std::string file); }; @@ -209,6 +210,10 @@ typedef std::map>> ZoneData; XMLTag newTag=ReadNextTag(); if (newTag.tag=="map") { + if(stoi(newTag.data["infinite"])==1){ + infiniteMap=true; + return; + } parsedMapInfo.MapData={stoi(newTag.data["width"]),stoi(newTag.data["height"])}; } else if (newTag.tag=="tileset") { @@ -269,7 +274,7 @@ typedef std::map>> ZoneData; std::string accumulator=""; - while (f.good()) { + while (f.good()&&!infiniteMap) { std::string data; f>>data; if (data.empty()) continue; @@ -306,10 +311,13 @@ typedef std::map>> ZoneData; } } + if(infiniteMap){ + std::cout<<"Infinite map detected. Parsing stopped early."<ku7!nVUqjz(}qr_>pZ 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 literal 0 HcmV?d00001 diff --git a/Crawler/assets/monsters/Flower Turret.xcf b/Crawler/assets/monsters/Flower Turret.xcf new file mode 100644 index 0000000000000000000000000000000000000000..d855c152c6c21aa10f8b34d660b09822b72a8b38 GIT binary patch literal 18081 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 literal 0 HcmV?d00001 diff --git a/Crawler/assets/monsters/Red Slime.png b/Crawler/assets/monsters/Red Slime.png new file mode 100644 index 0000000000000000000000000000000000000000..64d315a5256bf81990b2a8a54f41ae2cdb827c8d GIT binary patch literal 2655 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 literal 0 HcmV?d00001