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<std::pair<MonsterName,vf2d>>monster_list; + std::vector<std::pair<int,vf2d>>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&&monsterTypeID<MonsterName::END){ - monster_list.push_back({MonsterName(monsterTypeID),{monster.GetInteger("x")-spawnData.ObjectData.GetFloat("x"),monster.GetInteger("y")-spawnData.ObjectData.GetFloat("y")}}); - } + monster_list.push_back({monsterTypeID,{monster.GetInteger("x")-spawnData.ObjectData.GetFloat("x"),monster.GetInteger("y")-spawnData.ObjectData.GetFloat("y")}}); } SPAWNER_LIST.push_back(MonsterSpawner{{spawnData.ObjectData.GetFloat("x"),spawnData.ObjectData.GetFloat("y")},spawnerRadius*2,monster_list,spawnData.upperLevel}); } diff --git a/Crawler/Crawler.h b/Crawler/Crawler.h index 4de77a7a..218057f6 100644 --- a/Crawler/Crawler.h +++ b/Crawler/Crawler.h @@ -22,7 +22,7 @@ class Crawler : public olc::PixelGameEngine friend class sig::Animation; Camera2D camera; std::unique_ptr<Player>player; - 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 @@ <ClCompile Include="LightningBoltEmitter.cpp" /> <ClCompile Include="Map.cpp" /> <ClCompile Include="Meteor.cpp" /> + <ClCompile Include="RunTowards.cpp" /> <ClCompile Include="Pathfinding.cpp" /> <ClCompile Include="pixelGameEngine.cpp" /> <ClCompile Include="Player.cpp" /> @@ -312,8 +313,11 @@ <ClCompile Include="MonsterData.cpp" /> <ClCompile Include="PulsatingFire.cpp" /> <ClCompile Include="Ranger.cpp" /> + <ClCompile Include="RUN_STRATEGY.cpp" /> + <ClCompile Include="ShootAfar.cpp" /> <ClCompile Include="Thief.cpp" /> <ClCompile Include="Trapper.cpp" /> + <ClCompile Include="Turret.cpp" /> <ClCompile Include="Warrior.cpp" /> <ClCompile Include="utils.cpp" /> <ClCompile Include="Witch.cpp" /> 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 @@ <Filter Include="Configurations\Classes"> <UniqueIdentifier>{fd547111-0670-4be5-85cf-28fbd92c765f}</UniqueIdentifier> </Filter> + <Filter Include="Source Files\Monster Strategies"> + <UniqueIdentifier>{3d2f7a3f-5781-45ab-a66d-c6d57d9de13c}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClInclude Include="olcPixelGameEngine.h"> @@ -215,6 +218,18 @@ <ClCompile Include="ChargedArrow.cpp"> <Filter>Source Files\Bullet Types</Filter> </ClCompile> + <ClCompile Include="RunTowards.cpp"> + <Filter>Source Files\Monster Strategies</Filter> + </ClCompile> + <ClCompile Include="ShootAfar.cpp"> + <Filter>Source Files\Monster Strategies</Filter> + </ClCompile> + <ClCompile Include="Turret.cpp"> + <Filter>Source Files\Monster Strategies</Filter> + </ClCompile> + <ClCompile Include="RUN_STRATEGY.cpp"> + <Filter>Source Files\Monster Strategies</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="cpp.hint" /> 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::vector<MonsterSpawner>SPAWNER_LIST; #define INCLUDE_DAMAGENUMBER_LIST extern std::vector<std::shared_ptr<DamageNumber>>DAMAGENUMBER_LIST; #define INCLUDE_game extern Crawler*game; -#define INCLUDE_MONSTER_DATA extern std::map<MonsterName,MonsterData>MONSTER_DATA; +#define INCLUDE_MONSTER_DATA extern std::map<int,MonsterData>MONSTER_DATA; #define INCLUDE_BULLET_LIST extern std::vector<std::unique_ptr<Bullet>>BULLET_LIST; #define INCLUDE_PARTICLE_LIST extern std::vector<Particle>PARTICLE_LIST; #define INCLUDE_EMITTER_LIST extern std::vector<std::unique_ptr<Emitter>>EMITTER_LIST; #define INCLUDE_DATA extern utils::datafile DATA; -#define INCLUDE_STRATEGY_DATA extern safemap<std::string,MonsterStrategy>STRATEGY_DATA; +#define INCLUDE_STRATEGY_DATA extern safemap<std::string,int>STRATEGY_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 -safemap<std::string,MonsterStrategy>STRATEGY_DATA; +safemap<std::string,int>STRATEGY_DATA; +std::map<int,Renderable*>MonsterData::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>(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<std::pair<MonsterName,vf2d>>monsters,bool upperLevel): +MonsterSpawner::MonsterSpawner(vf2d pos,vf2d range,std::vector<std::pair<int,vf2d>>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<MonsterName,vf2d>&monsterInfo:monsters){ + for(std::pair<int,vf2d>&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{ - /// <summary> - /// NOTE: When adding a new strategy, update MonsterStrategies.txt!! - /// </summary> - 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<std::string> 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::vector<std::string>animations,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::vector<std::string>animations,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::map<int,Renderable*>imgs; }; 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::Animation<std::string>animation; Animate2D::AnimationState internal_animState; float randomFrameOffset=0.f; float deathTimer=0.f; - MonsterName type; std::vector<Buff>buffList; 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<std::pair<MonsterName,vf2d>>monsters; + std::vector<std::pair<int,vf2d>>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<std::pair<MonsterName,vf2d>>MONSTER_LIST,bool upperLevel=false); + MonsterSpawner(vf2d pos,vf2d range,std::vector<std::pair<int,vf2d>>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::map<MonsterName,MonsterData>MONSTER_DATA; +std::map<int,MonsterData>MONSTER_DATA; MonsterData::MonsterData(){} -MonsterData::MonsterData(MonsterName type,int hp,int atk,std::vector<std::string>animations,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::vector<std::string>animations,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;i<MonsterName::END;i++){ - std::string ID=DATA["Monsters"][std::to_string(i)]["DisplayName"].GetString(1); + int id=0; + while(DATA["Monsters"].HasProperty(std::to_string(id))){ + std::string MonsterName=DATA["Monsters"][std::to_string(id)]["DisplayName"].GetString(); std::vector<std::string>animations{ - 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;i<animations.size();i++){ + std::string animationConfigName=""; + std::string imgName=""; + switch(i){ + case 0:{ + animationConfigName="Idle"; + imgName="_IDLE"; + }break; + case 1:{ + animationConfigName="Jump"; + imgName="_JUMP"; + }break; + case 2:{ + animationConfigName="Shoot"; + imgName="_SPIT"; + }break; + case 3:{ + animationConfigName="Death"; + imgName="_DIE"; + }break; + } + Animate2D::Style style=Animate2D::Style::Repeat; + if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="Repeat"){ + style=Animate2D::Style::Repeat; + } else + if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="OneShot"){ + style=Animate2D::Style::OneShot; + } else + if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="PingPong"){ + style=Animate2D::Style::PingPong; + } else + if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="Reverse"){ + style=Animate2D::Style::Reverse; + } + + auto CreateHorizontalAnimationSequence=[&](Renderable&img,int frameCount,vf2d size,std::string state,int row,AnimationData data={}){ + Animate2D::FrameSequence anim(data.frameDuration,data.style); + for(int i=0;i<frameCount;i++){ + anim.AddFrame({&img,{{int(i*size.x),int(row*size.y)},size}}); + } + ANIMATION_DATA[state]=anim; + }; + + int frameCount = DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetInt(0); + vf2d frameSize = vf2d{float(DATA["Monsters"][std::to_string(id)]["SheetFrameSize"].GetInt(0)),float(DATA["Monsters"][std::to_string(id)]["SheetFrameSize"].GetInt(1))}; + CreateHorizontalAnimationSequence(*MonsterData::imgs[id],frameCount,frameSize,MonsterName+imgName,i,AnimationData{float(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetReal(1)),style}); + } + //Add additional custom animations defined in the config. int animationCounter=0; - while(DATA["Monsters"][std::to_string(i)].HasProperty("ANIMATION["+std::to_string(animationCounter)+"]")){ - animations.push_back(DATA["Monsters"][std::to_string(i)]["ANIMATION["+std::to_string(animationCounter)+"]"].GetString()); + while(DATA["Monsters"][std::to_string(id)].HasProperty("ANIMATION["+std::to_string(animationCounter)+"]")){ + animations.push_back(DATA["Monsters"][std::to_string(id)]["ANIMATION["+std::to_string(animationCounter)+"]"].GetString()); animationCounter++; } MonsterData monster( - MonsterName(i), - DATA["Monsters"][std::to_string(i)]["Health"].GetInt(), - DATA["Monsters"][std::to_string(i)]["Attack"].GetInt(), + id, + MonsterName, + DATA["Monsters"][std::to_string(id)]["Health"].GetInt(), + DATA["Monsters"][std::to_string(id)]["Attack"].GetInt(), animations, - ID+"_JUMP", - ID+"_SPIT", - ID+"_DIE", - DATA["Monsters"][std::to_string(i)]["MoveSpd"].GetReal()/100, - DATA["Monsters"][std::to_string(i)]["Size"].GetReal()/100, - STRATEGY_DATA[DATA["Monsters"][std::to_string(i)]["Strategy"].GetString()], - DATA["Monsters"][std::to_string(i)]["CollisionDmg"].GetInt() + DATA["Monsters"][std::to_string(id)]["MoveSpd"].GetReal()/100, + DATA["Monsters"][std::to_string(id)]["Size"].GetReal()/100, + STRATEGY_DATA[DATA["Monsters"][std::to_string(id)]["Strategy"].GetString()], + DATA["Monsters"][std::to_string(id)]["CollisionDmg"].GetInt() ); - MONSTER_DATA[MonsterName(i)]=monster; + MONSTER_DATA[id]=monster; + + id++; } } int MonsterData::GetHealth(){ @@ -65,21 +116,22 @@ float MonsterData::GetSizeMult(){ int MonsterData::GetCollisionDmg(){ return collisionDmg; } -MonsterName MonsterData::GetType(){ - return type; +int MonsterData::GetID(){ + return id; } -MonsterStrategy MonsterData::GetAIStrategy(){ +int MonsterData::GetAIStrategy(){ return strategy; } +std::string MonsterData::GetIdleAnimation(){ + return animations[IDLE]; +} std::string MonsterData::GetJumpAnimation(){ - return jumpAnimation; + return animations[JUMP]; } std::string MonsterData::GetShootAnimation(){ - return shootAnimation; + return animations[SHOOT]; } - -std::string MonsterData::GetDeathAnimation() -{ - return deathAnimation; +std::string MonsterData::GetDeathAnimation(){ + return animations[DEATH]; } diff --git a/Crawler/Player.cpp b/Crawler/Player.cpp index 40fb62e3..a69b0b0f 100644 --- a/Crawler/Player.cpp +++ b/Crawler/Player.cpp @@ -169,6 +169,7 @@ void Player::Knockback(vf2d vel){ } void Player::Update(float fElapsedTime){ + Ability&rightClickAbility=GetRightClickAbility(), &ability=GetAbility1(), &ability2=GetAbility2(), diff --git a/Crawler/RUN_STRATEGY.cpp b/Crawler/RUN_STRATEGY.cpp new file mode 100644 index 00000000..83a47086 --- /dev/null +++ b/Crawler/RUN_STRATEGY.cpp @@ -0,0 +1,15 @@ +#include "Monster.h" + +void Monster::STRATEGY::RUN_STRATEGY(Monster&m,float fElapsedTime){ + switch(m.strategy){ + case 0:{//Run Towards + Monster::STRATEGY::RUN_TOWARDS(m,fElapsedTime); + }break; + case 1:{//Shoot Afar + Monster::STRATEGY::SHOOT_AFAR(m,fElapsedTime); + }break; + case 2:{//Turret. + Monster::STRATEGY::TURRET(m,fElapsedTime); + }break; + } +} \ No newline at end of file diff --git a/Crawler/RunTowards.cpp b/Crawler/RunTowards.cpp new file mode 100644 index 00000000..7c677af0 --- /dev/null +++ b/Crawler/RunTowards.cpp @@ -0,0 +1,35 @@ +#include "Monster.h" +#include "DEFINES.h" +#include "Crawler.h" + +INCLUDE_game +INCLUDE_MONSTER_DATA + +void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime){ + m.targetAcquireTimer=std::max(0.f,m.targetAcquireTimer-fElapsedTime); + if(m.targetAcquireTimer==0){ + m.targetAcquireTimer=3; + m.target=geom2d::line(m.pos,game->GetPlayer()->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>(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::vector<XMLTag>accumulatedMonsterTags; + bool infiniteMap=false; public: TMXParser(std::string file); }; @@ -209,6 +210,10 @@ typedef std::map<std::string,std::vector<geom2d::rect<int>>> 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<std::string,std::vector<geom2d::rect<int>>> 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<std::string,std::vector<geom2d::rect<int>>> ZoneData; } } + if(infiniteMap){ + std::cout<<"Infinite map detected. Parsing stopped early."<<std::endl; + } + for(XMLTag&monster:accumulatedMonsterTags){ parsedMapInfo.SpawnerData[monster.GetInteger("spawnerLink")].monsters.push_back(monster); } - std::cout<<"Parsed Map Data:\n"<<parsedMapInfo<<"\n"; } #endif \ No newline at end of file diff --git a/Crawler/Turret.cpp b/Crawler/Turret.cpp new file mode 100644 index 00000000..06e53cd0 --- /dev/null +++ b/Crawler/Turret.cpp @@ -0,0 +1,5 @@ +#include "Monster.h" + +void Monster::STRATEGY::TURRET(Monster&m,float fElapsedTime){ + +} \ No newline at end of file diff --git a/Crawler/Version.h b/Crawler/Version.h index b758c420..9d5513e5 100644 --- a/Crawler/Version.h +++ b/Crawler/Version.h @@ -2,7 +2,7 @@ #define VERSION_MAJOR 0 #define VERSION_MINOR 2 #define VERSION_PATCH 0 -#define VERSION_BUILD 976 +#define VERSION_BUILD 988 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Crawler/assets/config/MonsterStrategies.txt b/Crawler/assets/config/MonsterStrategies.txt index 149ab8f5..686cc0d6 100644 --- a/Crawler/assets/config/MonsterStrategies.txt +++ b/Crawler/assets/config/MonsterStrategies.txt @@ -12,7 +12,7 @@ # ========================================== # ========================================== # -# Monster0 +# 0 # { # Name = Green Slime # Health = 10 @@ -23,7 +23,7 @@ # MoveSpd = 110 # Size = 80 # -# Strategy = RUN_TOWARDS +# Strategy = Run Towards # WaitTime = 5 # # [....Cut for length purposes] @@ -38,7 +38,7 @@ MonsterStrategy { 0 { - Name = RUN_TOWARDS + Name = Run Towards # How long to wait before attempting to path again. WaitTime = 2 # How far the monster will travel before reassessing for a new path. @@ -46,7 +46,7 @@ MonsterStrategy } 1 { - Name = SHOOT_AFAR + Name = Shoot Afar # How far away the monster attempts to distance itself from the player Range = 800 # How often the enemy shoots. @@ -57,8 +57,8 @@ MonsterStrategy } 2 { - Name = TURRET - # How far away the monster starts from shooting from + Name = Turret + # How far away the monster starts shooting from Range = 800 # How often the enemy shoots. ShootingSpeed = 1 diff --git a/Crawler/assets/config/Monsters.txt b/Crawler/assets/config/Monsters.txt index d6e7d765..bf287983 100644 --- a/Crawler/assets/config/Monsters.txt +++ b/Crawler/assets/config/Monsters.txt @@ -2,7 +2,7 @@ Monsters { 0 { - DisplayName = Green Slime, GREEN_SLIME + DisplayName = Green Slime Health = 10 Attack = 5 @@ -11,14 +11,23 @@ Monsters MoveSpd = 110 Size = 80 - Strategy = RUN_TOWARDS + Strategy = Run Towards + + #Size of each animation frame + SheetFrameSize = 24,24 + + # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse) + IdleAnimation = 10, 0.1, Repeat + JumpAnimation = 10, 0.06, Repeat + ShootAnimation = 10, 0.1, OneShot + DeathAnimation = 10, 0.1, OneShot #Additional custom animations go down below. Start with ANIMATION[0] #ANIMATION[0] = MY_NEW_ANIMATION } 1 { - DisplayName = Blue Slime, BLUE_SLIME + DisplayName = Blue Slime Health = 30 Attack = 10 @@ -27,14 +36,23 @@ Monsters MoveSpd = 80 Size = 100 - Strategy = SHOOT_AFAR + Strategy = Shoot Afar + + #Size of each animation frame + SheetFrameSize = 24,24 + + # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse) + IdleAnimation = 10, 0.1, Repeat + JumpAnimation = 10, 0.06, Repeat + ShootAnimation = 10, 0.1, OneShot + DeathAnimation = 10, 0.1, OneShot #Additional custom animations go down below. Start with ANIMATION[0] #ANIMATION[0] = MY_NEW_ANIMATION } 2 { - DisplayName = Red Slime, RED_SLIME + DisplayName = Red Slime Health = 25 Attack = 10 @@ -43,14 +61,23 @@ Monsters MoveSpd = 95 Size = 120 - Strategy = RUN_TOWARDS + Strategy = Run Towards + + #Size of each animation frame + SheetFrameSize = 24,24 + + # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse) + IdleAnimation = 10, 0.1, Repeat + JumpAnimation = 10, 0.06, Repeat + ShootAnimation = 10, 0.1, OneShot + DeathAnimation = 10, 0.1, OneShot #Additional custom animations go down below. Start with ANIMATION[0] #ANIMATION[0] = MY_NEW_ANIMATION } 3 { - DisplayName = Yellow Slime, YELLOW_SLIME + DisplayName = Yellow Slime Health = 175 Attack = 10 @@ -59,14 +86,23 @@ Monsters MoveSpd = 40 Size = 160 - Strategy = RUN_TOWARDS + Strategy = Run Towards + + #Size of each animation frame + SheetFrameSize = 24,24 + + # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse) + IdleAnimation = 10, 0.1, Repeat + JumpAnimation = 10, 0.06, Repeat + ShootAnimation = 10, 0.1, OneShot + DeathAnimation = 10, 0.1, OneShot #Additional custom animations go down below. Start with ANIMATION[0] #ANIMATION[0] = MY_NEW_ANIMATION } 4 { - DisplayName = Flower Turret, FLOWER_TURRET + DisplayName = Flower Turret Health = 40 Attack = 10 @@ -75,7 +111,16 @@ Monsters MoveSpd = 0 Size = 100 - Strategy = TURRET + Strategy = Turret + + #Size of each animation frame + SheetFrameSize = 24,24 + + # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse) + IdleAnimation = 7, 0.1, PingPong + JumpAnimation = 1, 0.1, OneShot + ShootAnimation = 5, 0.1, OneShot + DeathAnimation = 5, 0.2, OneShot #Additional custom animations go down below. Start with ANIMATION[0] #ANIMATION[0] = MY_NEW_ANIMATION diff --git a/Crawler/assets/config/Player.txt b/Crawler/assets/config/Player.txt index b94642a2..c10b4c9d 100644 --- a/Crawler/assets/config/Player.txt +++ b/Crawler/assets/config/Player.txt @@ -1,6 +1,6 @@ Player { - BaseHealth = 1000 + BaseHealth = 100 BaseMana = 100 MoveSpd = 100 diff --git a/Crawler/assets/config/gfx/gfx.txt b/Crawler/assets/config/gfx/gfx.txt index f68136d3..ef95be9e 100644 --- a/Crawler/assets/config/gfx/gfx.txt +++ b/Crawler/assets/config/gfx/gfx.txt @@ -3,7 +3,6 @@ GFX_Prefix = assets/ Images { GFX_Warrior_Sheet = nico-warrior.png - GFX_Slime_Sheet = slime.png GFX_Circle = circle.png GFX_Effect_GroundSlam_Back = ground-slam-attack-back.png GFX_Effect_GroundSlam_Front = ground-slam-attack-front.png diff --git a/Crawler/assets/monsters/Blue Slime.png b/Crawler/assets/monsters/Blue Slime.png new file mode 100644 index 00000000..e280c557 Binary files /dev/null and b/Crawler/assets/monsters/Blue Slime.png differ diff --git a/Crawler/assets/monsters/Flower Turret.png b/Crawler/assets/monsters/Flower Turret.png new file mode 100644 index 00000000..c42862be Binary files /dev/null and b/Crawler/assets/monsters/Flower Turret.png differ diff --git a/Crawler/assets/monsters/Flower Turret.xcf b/Crawler/assets/monsters/Flower Turret.xcf new file mode 100644 index 00000000..d855c152 Binary files /dev/null and b/Crawler/assets/monsters/Flower Turret.xcf differ diff --git a/Crawler/assets/monsters/Green Slime.png b/Crawler/assets/monsters/Green Slime.png new file mode 100644 index 00000000..964c9a8c Binary files /dev/null and b/Crawler/assets/monsters/Green Slime.png differ diff --git a/Crawler/assets/monsters/Red Slime.png b/Crawler/assets/monsters/Red Slime.png new file mode 100644 index 00000000..64d315a5 Binary files /dev/null and b/Crawler/assets/monsters/Red Slime.png differ diff --git a/Crawler/assets/monsters/Yellow Slime.png b/Crawler/assets/monsters/Yellow Slime.png new file mode 100644 index 00000000..81c73592 Binary files /dev/null and b/Crawler/assets/monsters/Yellow Slime.png differ