Moved all enemy reading over to purely using config files. Removed all enemy enum dependencies. Removed bugs involving loading infinite maps by accident.

pull/28/head
sigonasr2 1 year ago
parent 13eff22485
commit 6a12a45cb2
  1. 49
      Crawler/Animation.cpp
  2. 7
      Crawler/Crawler.cpp
  3. 2
      Crawler/Crawler.h
  4. 4
      Crawler/Crawler.vcxproj
  5. 15
      Crawler/Crawler.vcxproj.filters
  6. 4
      Crawler/DEFINES.h
  7. 145
      Crawler/Monster.cpp
  8. 57
      Crawler/Monster.h
  9. 120
      Crawler/MonsterData.cpp
  10. 1
      Crawler/Player.cpp
  11. 15
      Crawler/RUN_STRATEGY.cpp
  12. 35
      Crawler/RunTowards.cpp
  13. 95
      Crawler/ShootAfar.cpp
  14. 12
      Crawler/TMXParser.h
  15. 5
      Crawler/Turret.cpp
  16. 2
      Crawler/Version.h
  17. 12
      Crawler/assets/config/MonsterStrategies.txt
  18. 65
      Crawler/assets/config/Monsters.txt
  19. 2
      Crawler/assets/config/Player.txt
  20. 1
      Crawler/assets/config/gfx/gfx.txt
  21. BIN
      Crawler/assets/monsters/Blue Slime.png
  22. BIN
      Crawler/assets/monsters/Flower Turret.png
  23. BIN
      Crawler/assets/monsters/Flower Turret.xcf
  24. BIN
      Crawler/assets/monsters/Green Slime.png
  25. BIN
      Crawler/assets/monsters/Red Slime.png
  26. BIN
      Crawler/assets/monsters/Yellow Slime.png

@ -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});

@ -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});
}

@ -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,

@ -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" />

@ -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" />

@ -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();

@ -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();

@ -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();

@ -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];
}

@ -169,6 +169,7 @@ void Player::Knockback(vf2d vel){
}
void Player::Update(float fElapsedTime){
Ability&rightClickAbility=GetRightClickAbility(),
&ability=GetAbility1(),
&ability2=GetAbility2(),

@ -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;
}
}

@ -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:{
}
}
}

@ -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();
}
}
}
}

@ -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

@ -0,0 +1,5 @@
#include "Monster.h"
void Monster::STRATEGY::TURRET(Monster&m,float fElapsedTime){
}

@ -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

@ -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

@ -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

@ -1,6 +1,6 @@
Player
{
BaseHealth = 1000
BaseHealth = 100
BaseMana = 100
MoveSpd = 100

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Loading…
Cancel
Save