Layering rendering now proper.

This commit is contained in:
sigonasr2 2023-07-07 06:42:49 -05:00
parent 7348426c71
commit 6b81cd6e84
16 changed files with 189 additions and 116 deletions

View File

@ -5,11 +5,11 @@
INCLUDE_ANIMATION_DATA
INCLUDE_game
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,bool friendly,Pixel col)
:pos(pos),vel(vel),radius(radius),damage(damage),col(col),friendly(friendly){};
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
:pos(pos),vel(vel),radius(radius),damage(damage),col(col),friendly(friendly),upperLevel(upperLevel){};
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,AnimationState animation,bool hitsMultiple,float lifetime,bool rotatesWithAngle,bool friendly,Pixel col)
:pos(pos),vel(vel),radius(radius),damage(damage),col(col),animated(true),rotates(rotatesWithAngle),lifetime(lifetime),hitsMultiple(hitsMultiple),friendly(friendly){
Bullet::Bullet(vf2d pos,vf2d vel,float radius,int damage,AnimationState animation,bool upperLevel,bool hitsMultiple,float lifetime,bool rotatesWithAngle,bool friendly,Pixel col)
:pos(pos),vel(vel),radius(radius),damage(damage),col(col),animated(true),rotates(rotatesWithAngle),lifetime(lifetime),hitsMultiple(hitsMultiple),friendly(friendly),upperLevel(upperLevel){
this->animation.AddState(animation,ANIMATION_DATA[animation]);
this->animation.ChangeState(internal_animState,animation);
};
@ -42,4 +42,5 @@ void Bullet::Draw(){
}
bool Bullet::PlayerHit(Player&player){return true;}
bool Bullet::MonsterHit(Monster&monster){return true;}
bool Bullet::MonsterHit(Monster&monster){return true;}
bool Bullet::OnUpperLevel(){return upperLevel;}

View File

@ -19,6 +19,7 @@ struct Bullet{
bool deactivated=false; //A deactivated bullet no longer interacts with the world. It's just a visual.
float fadeOutTime=0;
bool friendly=false; //Whether or not it's a player bullet or enemy bullet.
bool upperLevel=false;
private:
float fadeOutTimer=0;
void UpdateFadeTime(float fElapsedTime);
@ -26,9 +27,9 @@ public:
Animate2D::Animation<AnimationState>animation;
Animate2D::AnimationState internal_animState;
std::map<Monster*,bool>hitList;
Bullet(vf2d pos,vf2d vel,float radius,int damage,bool friendly=false,Pixel col=WHITE);
Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
//Initializes a bullet with an animation.
Bullet(vf2d pos,vf2d vel,float radius,int damage,AnimationState animation,bool hitsMultiple=false,float lifetime=INFINITE,bool rotatesWithAngle=false,bool friendly=false,Pixel col=WHITE);
Bullet(vf2d pos,vf2d vel,float radius,int damage,AnimationState animation,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool rotatesWithAngle=false,bool friendly=false,Pixel col=WHITE);
public:
virtual void Update(float fElapsedTime);
//Return true when the bullet should be destroyed. Return false to handle it otherwise (like deactivating it instead). You become responsible for getting rid of the bullet.
@ -37,4 +38,5 @@ public:
virtual bool MonsterHit(Monster&monster);
Animate2D::Frame GetFrame();
void Draw();
bool OnUpperLevel();
};

View File

@ -3,7 +3,7 @@
struct EnergyBolt:public Bullet{
float lastParticleSpawn=0;
EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool friendly=false,Pixel col=WHITE);
EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
void Update(float fElapsedTime)override;
bool PlayerHit(Player&player)override;
bool MonsterHit(Monster&monster)override;
@ -11,7 +11,7 @@ struct EnergyBolt:public Bullet{
struct FireBolt:public Bullet{
float lastParticleSpawn=0;
FireBolt(vf2d pos,vf2d vel,float radius,int damage,bool friendly=false,Pixel col=WHITE);
FireBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE);
void Update(float fElapsedTime)override;
bool PlayerHit(Player&player)override;
bool MonsterHit(Monster&monster)override;

View File

@ -5,8 +5,7 @@
INCLUDE_game
INCLUDE_MONSTER_LIST
INCLUDE_PLAYER_BULLET_LIST
INCLUDE_BULLET_LIST
std::map<Class,std::unique_ptr<ClassData>>CLASS_DATA;
@ -147,7 +146,7 @@ bool Warrior::Ability3(){
p.UpdateAnimation(AnimationState::WARRIOR_SWINGSONICSWORD_S);
}break;
}
PLAYER_BULLET_LIST.push_back(std::make_unique<Bullet>(p.pos,bulletVel,30,p.GetAttack()*8,AnimationState::SONICSLASH,true,2.25,true,true,WHITE));
BULLET_LIST.push_back(std::make_unique<Bullet>(p.pos,bulletVel,30,p.GetAttack()*8,AnimationState::SONICSLASH,p.upperLevel,true,2.25,true,true,WHITE));
game->SetupWorldShake(0.5);
return true;
}
@ -283,14 +282,14 @@ bool Wizard::AutoAttack(){
ACCESS_PLAYER
p.attack_cooldown_timer=p.MAGIC_ATTACK_COOLDOWN;
float angleToCursor=atan2(game->GetWorldMousePos().y-p.pos.y,game->GetWorldMousePos().x-p.pos.x);
PLAYER_BULLET_LIST.push_back(std::make_unique<EnergyBolt>(EnergyBolt(p.pos,{cos(angleToCursor)*200,sin(angleToCursor)*200},12,p.GetAttack(),true,WHITE)));
BULLET_LIST.push_back(std::make_unique<EnergyBolt>(EnergyBolt(p.pos,{cos(angleToCursor)*200,sin(angleToCursor)*200},12,p.GetAttack(),p.upperLevel,true,WHITE)));
return true;
}
bool Wizard::Ability1(){
ACCESS_PLAYER
float angleToCursor=atan2(game->GetWorldMousePos().y-p.pos.y,game->GetWorldMousePos().x-p.pos.x);
PLAYER_BULLET_LIST.push_back(std::make_unique<FireBolt>(FireBolt(p.pos,{cos(angleToCursor)*275,sin(angleToCursor)*275},12,p.GetAttack(),true,{240,120,60})));
BULLET_LIST.push_back(std::make_unique<FireBolt>(FireBolt(p.pos,{cos(angleToCursor)*275,sin(angleToCursor)*275},12,p.GetAttack(),p.upperLevel,true,{240,120,60})));
return true;
}
@ -320,7 +319,7 @@ bool Wizard::RightClickAbility(){
p.teleportStartPosition=p.GetPos();
p.iframe_time=0.35;
for(int i=0;i<16;i++){
game->AddEffect(Effect(p.GetPos()+vf2d{(rand()%160-80)/10.f,(rand()%160-80)/10.f},float(rand()%300)/1000,AnimationState::DOT_PARTICLE,0.3,0.2,{float(rand()%1000-500)/100,float(rand()%1000-500)/100},BLACK));
game->AddEffect(Effect(p.GetPos()+vf2d{(rand()%160-80)/10.f,(rand()%160-80)/10.f},float(rand()%300)/1000,AnimationState::DOT_PARTICLE,p.upperLevel,0.3,0.2,{float(rand()%1000-500)/100,float(rand()%1000-500)/100},BLACK));
}
return true;
} else {

View File

@ -21,7 +21,6 @@ std::vector<Monster>MONSTER_LIST;
std::vector<MonsterSpawner>SPAWNER_LIST;
std::vector<DamageNumber>DAMAGENUMBER_LIST;
std::vector<std::unique_ptr<Bullet>>BULLET_LIST;
std::vector<std::unique_ptr<Bullet>>PLAYER_BULLET_LIST;
Crawler*game;
Crawler::Crawler()
@ -642,15 +641,36 @@ void Crawler::UpdateBullets(float fElapsedTime){
b->UpdateFadeTime(fElapsedTime);
b->Update(fElapsedTime);
b->pos+=b->vel*fElapsedTime;
if(!b->deactivated&&geom2d::overlaps(geom2d::circle(player.GetPos(),12*player.GetSizeMult()/2),geom2d::circle(b->pos,b->radius))){
if(player.Hurt(b->damage)){
if(b->PlayerHit(player)){
it=BULLET_LIST.erase(it);
if(it==BULLET_LIST.end()){
break;
if(!b->deactivated){
if(b->friendly){
for(Monster&m:MONSTER_LIST){
if(geom2d::overlaps(geom2d::circle(m.GetPos(),12*m.GetSizeMult()),geom2d::circle(b->pos,b->radius))){
if(b->hitList.find(&m)==b->hitList.end()&&m.Hurt(b->damage)){
if(!b->hitsMultiple){
if(b->MonsterHit(m)){
it=BULLET_LIST.erase(it);
if(it==BULLET_LIST.end()){
goto outsidePlayerBulletLoop;
}
}
goto continuePlayerBulletLoop;
}
b->hitList[&m]=true;
}
}
}
} else {
if(geom2d::overlaps(geom2d::circle(player.GetPos(),12*player.GetSizeMult()/2),geom2d::circle(b->pos,b->radius))){
if(player.Hurt(b->damage)){
if(b->PlayerHit(player)){
it=BULLET_LIST.erase(it);
if(it==BULLET_LIST.end()){
break;
}
}
continue;
}
}
continue;
}
}
if(b->pos.x+b->radius<view.GetWorldTL().x||b->pos.x-b->radius>view.GetWorldBR().x||b->pos.y+b->radius<view.GetWorldTL().y||b->pos.y-b->radius>view.GetWorldBR().y){
@ -669,46 +689,6 @@ void Crawler::UpdateBullets(float fElapsedTime){
continue;
}
b->animation.UpdateState(b->internal_animState,fElapsedTime);
}
for(std::vector<std::unique_ptr<Bullet>>::iterator it=PLAYER_BULLET_LIST.begin();it!=PLAYER_BULLET_LIST.end();++it){
std::unique_ptr<Bullet>&b=*it;
b->UpdateFadeTime(fElapsedTime);
b->Update(fElapsedTime);
b->pos+=b->vel*fElapsedTime;
if(!b->deactivated){
for(Monster&m:MONSTER_LIST){
if(geom2d::overlaps(geom2d::circle(m.GetPos(),12*m.GetSizeMult()),geom2d::circle(b->pos,b->radius))){
if(b->hitList.find(&m)==b->hitList.end()&&m.Hurt(b->damage)){
if(!b->hitsMultiple){
if(b->MonsterHit(m)){
it=PLAYER_BULLET_LIST.erase(it);
if(it==PLAYER_BULLET_LIST.end()){
goto outsidePlayerBulletLoop;
}
}
goto continuePlayerBulletLoop;
}
b->hitList[&m]=true;
}
}
}
}
if(b->pos.x<view.GetWorldTL().x||b->pos.x>view.GetWorldBR().x||b->pos.y<view.GetWorldTL().y||b->pos.y>view.GetWorldBR().y){
it=PLAYER_BULLET_LIST.erase(it);
if(it==PLAYER_BULLET_LIST.end()){
break;
}
continue;
}
b->lifetime-=fElapsedTime;
if(b->lifetime<=0){
it=PLAYER_BULLET_LIST.erase(it);
if(it==PLAYER_BULLET_LIST.end()){
break;
}
continue;
}
b->animation.UpdateState(b->internal_animState,fElapsedTime);
continuePlayerBulletLoop:
continue;
}
@ -723,6 +703,54 @@ void Crawler::HurtEnemies(vf2d pos,float radius,int damage){
}
}
void Crawler::PopulateRenderLists(std::vector<Monster*>&monstersBeforeLower,std::vector<Monster*>&monstersBeforeUpper,std::vector<Monster*>&monstersAfterLower,std::vector<Monster*>&monstersAfterUpper,std::vector<Bullet*>&bulletsLower,std::vector<Bullet*>&bulletsUpper,std::vector<Effect*>&backgroundEffectsLower,std::vector<Effect*>&backgroundEffectsUpper,std::vector<Effect*>&foregroundEffectsLower,std::vector<Effect*>&foregroundEffectsUpper){
Player&pl=player;
for(auto it=MONSTER_LIST.begin();it!=MONSTER_LIST.end();++it){
Monster&m=*it;
if(m.GetPos().y<pl.GetPos().y){//This monster renders before the player does (behind the player)
if(m.OnUpperLevel()){
monstersBeforeUpper.push_back(&m);
}else{
monstersBeforeLower.push_back(&m);
}
} else {//This monster renders after the player does (in front of the player)
if(m.OnUpperLevel()){
monstersAfterUpper.push_back(&m);
}else{
monstersAfterLower.push_back(&m);
}
}
}
for(auto it=BULLET_LIST.begin();it!=BULLET_LIST.end();++it){
Bullet*b=(*it).get();
if(b->OnUpperLevel()){
bulletsUpper.push_back(b);
}else{
bulletsLower.push_back(b);
}
}
for(auto it=foregroundEffects.begin();it!=foregroundEffects.end();++it){
Effect&e=*it;
if(e.OnUpperLevel()){
foregroundEffectsUpper.push_back(&e);
}else{
foregroundEffectsLower.push_back(&e);
}
}
for(auto it=backgroundEffects.begin();it!=backgroundEffects.end();++it){
Effect&e=*it;
if(e.OnUpperLevel()){
backgroundEffectsUpper.push_back(&e);
}else{
backgroundEffectsLower.push_back(&e);
}
}
std::sort(monstersBeforeUpper.begin(),monstersBeforeUpper.end(),[](Monster*m1,Monster*m2){return m1->GetPos().y<m2->GetPos().y;});
std::sort(monstersBeforeLower.begin(),monstersBeforeLower.end(),[](Monster*m1,Monster*m2){return m1->GetPos().y<m2->GetPos().y;});
std::sort(monstersAfterUpper.begin(),monstersAfterUpper.end(),[](Monster*m1,Monster*m2){return m1->GetPos().y<m2->GetPos().y;});
std::sort(monstersAfterLower.begin(),monstersAfterLower.end(),[](Monster*m1,Monster*m2){return m1->GetPos().y<m2->GetPos().y;});
}
void Crawler::RenderWorld(float fElapsedTime){
Clear({100,180,100});
LayerTag*bridgeLayer=nullptr;
@ -769,23 +797,22 @@ void Crawler::RenderWorld(float fElapsedTime){
}
#pragma endregion
//DrawDecal({0,0},MAP_TILESETS["assets/maps/"+MAP_DATA[LEVEL1].TilesetData[1].data["source"]]->Decal());
std::vector<Monster>monstersBefore,monstersAfter;
std::vector<Monster*>monstersBeforeLower,monstersAfterLower,monstersBeforeUpper,monstersAfterUpper;
std::vector<Bullet*>bulletsLower,bulletsUpper;
std::vector<Effect*>backgroundEffectsLower,backgroundEffectsUpper,foregroundEffectsLower,foregroundEffectsUpper;
Player&pl=player;
std::copy_if(MONSTER_LIST.begin(),MONSTER_LIST.end(),std::back_inserter(monstersBefore),[&pl](Monster&m){return m.GetPos().y<pl.GetPos().y;});
std::copy_if(MONSTER_LIST.begin(),MONSTER_LIST.end(),std::back_inserter(monstersAfter),[&pl](Monster&m){return m.GetPos().y>=pl.GetPos().y;});
std::sort(monstersBefore.begin(),monstersBefore.end(),[](Monster&m1,Monster&m2){return m1.GetPos().y<m2.GetPos().y;});
std::sort(monstersAfter.begin(),monstersAfter.end(),[](Monster&m1,Monster&m2){return m1.GetPos().y<m2.GetPos().y;});
PopulateRenderLists(monstersBeforeLower,monstersBeforeUpper,monstersAfterLower,monstersAfterUpper,bulletsLower,bulletsUpper,backgroundEffectsLower,backgroundEffectsUpper,foregroundEffectsLower,foregroundEffectsUpper);
if(player.GetZ()>0){
vf2d shadowScale=vf2d{8/3.f,1}/std::max(1.f,player.GetZ()/4);
view.DrawDecal(player.GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6},GFX_Circle.Decal(),shadowScale);
}
for(Effect&e:backgroundEffects){
e.Draw();
for(Effect*e:backgroundEffectsLower){
e->Draw();
}
for(Monster&m:monstersBefore){
if(!m.OnUpperLevel()){
m.Draw();
}
for(Monster*m:monstersBeforeLower){
m->Draw();
}
vf2d playerScale=vf2d(player.GetSizeMult(),player.GetSizeMult());
vf2d playerPosition=player.GetPos();
@ -802,18 +829,13 @@ void Crawler::RenderWorld(float fElapsedTime){
if(player.GetState()==State::BLOCK){
view.DrawDecal(player.GetPos()-vf2d{12,12},GFX_BLOCK_BUBBLE.Decal());
}
for(Monster&m:monstersAfter){
if(!m.OnUpperLevel()){
m.Draw();
}
for(Monster*m:monstersAfterLower){
m->Draw();
}
for(Effect&e:foregroundEffects){
e.Draw();
for(Effect*e:foregroundEffectsLower){
e->Draw();
}
for(std::unique_ptr<Bullet>&b:BULLET_LIST){
b->Draw();
}
for(std::unique_ptr<Bullet>&b:PLAYER_BULLET_LIST){
for(Bullet*b:bulletsLower){
b->Draw();
}
#pragma region Foreground Rendering
@ -855,18 +877,23 @@ void Crawler::RenderWorld(float fElapsedTime){
}
}
#pragma endregion
for(Monster&m:monstersBefore){
if(m.OnUpperLevel()){
m.Draw();
}
for(Effect*e:backgroundEffectsUpper){
e->Draw();
}
for(Monster*m:monstersBeforeUpper){
m->Draw();
}
if(player.upperLevel){
RENDER_PLAYER
}
for(Monster&m:monstersAfter){
if(m.OnUpperLevel()){
m.Draw();
}
for(Monster*m:monstersAfterUpper){
m->Draw();
}
for(Effect*e:foregroundEffectsUpper){
e->Draw();
}
for(Bullet*b:bulletsUpper){
b->Draw();
}
#pragma region Upper Foreground Rendering
for(TileGroup&group:upperForegroundTileGroups){
@ -1057,6 +1084,15 @@ void Crawler::LoadLevel(MapName map){
}
}
}
int counter=0;
bridgeLayerIndex=-1;
for(LayerTag&layer:MAP_DATA[map].LayerData){
if(IsBridgeLayer(layer)){
bridgeLayerIndex=counter;
}
counter++;
}
player.upperLevel=false; //Assume player starts on lower level.
player.SetPos(MAP_DATA[map].MapData.playerSpawnLocation);
player.path.Initialize();
}
@ -1106,15 +1142,26 @@ bool Crawler::IsBridgeLayer(LayerTag&layer){
geom2d::rect<int>Crawler::GetTileCollision(MapName map,vf2d pos,bool upperLevel){
if(pos.x<0||pos.y<0||pos.x>=WORLD_SIZE.x*24||pos.y>=WORLD_SIZE.y*24)return NO_COLLISION;
//The logic here is, if there's a tile on the bridge, we respect that tile instead if we're on the upper level. So we don't check other layers when we are on the upper level and there is a tile below us.
if(upperLevel&&bridgeLayerIndex!=-1){
int tileID=MAP_DATA[map].LayerData[bridgeLayerIndex].tiles[int(pos.y)/24][int(pos.x)/24]-1;
if(tileID!=-1){
if (GetTileSheet(map,tileID).tileset.collision.find(tileID-GetTileSheet(map,tileID).firstgid+1)!=GetTileSheet(map,tileID).tileset.collision.end()){
return GetTileSheet(map,tileID).tileset.collision[tileID-GetTileSheet(map,tileID).firstgid+1].collision;
}
return NO_COLLISION;
}
}
int counter=0;
for(LayerTag&layer:MAP_DATA[map].LayerData){
auto HasNoClass=[&](){return layer.tag.data.find("class")==layer.tag.data.end()&&!upperLevel;};
auto BridgeCollisionIsActivated=[&](){return (IsBridgeLayer(layer)&&upperLevel);};
if(HasNoClass()||BridgeCollisionIsActivated()){
auto HasNoClass=[&](){return layer.tag.data.find("class")==layer.tag.data.end();};
if(HasNoClass()&&counter!=bridgeLayerIndex){
int tileID=layer.tiles[int(pos.y)/24][int(pos.x)/24]-1;
if(tileID!=-1&&GetTileSheet(map,tileID).tileset.collision.find(tileID-GetTileSheet(map,tileID).firstgid+1)!=GetTileSheet(map,tileID).tileset.collision.end()){
return GetTileSheet(map,tileID).tileset.collision[tileID-GetTileSheet(map,tileID).firstgid+1].collision;
}
}
counter++;
}
return NO_COLLISION;
}

View File

@ -6,6 +6,7 @@
#include "olcPGEX_TransformedView.h"
#include "Player.h"
#include "olcUTIL_Camera2D.h"
#include "Bullet.h"
#include "Effect.h"
#include "Map.h"
#include "TMXParser.h"
@ -38,7 +39,7 @@ private:
MapName currentLevel=MapName::CAMPAIGN_1_1;
std::vector<TileGroup>foregroundTileGroups;
std::vector<TileGroup>upperForegroundTileGroups;
int bridgeLayerIndex=-1;
public:
Crawler();
@ -86,4 +87,5 @@ public:
MapName GetCurrentLevel();
bool IsBridgeLayer(LayerTag&layer);
std::map<std::string,std::vector<geom2d::rect<int>>>&GetZoneData(MapName map);
void PopulateRenderLists(std::vector<Monster*>&monstersBeforeLower,std::vector<Monster*>&monstersBeforeUpper,std::vector<Monster*>&monstersAfterLower,std::vector<Monster*>&monstersAfterUpper,std::vector<Bullet*>&bulletsLower,std::vector<Bullet*>&bulletsUpper,std::vector<Effect*>&backgroundEffectsLower,std::vector<Effect*>&backgroundEffectsUpper,std::vector<Effect*>&foregroundEffectsLower,std::vector<Effect*>&foregroundEffectsUpper);
};

View File

@ -7,7 +7,6 @@
#define INCLUDE_MONSTER_DATA extern std::map<MonsterName,MonsterData>MONSTER_DATA;
#define INCLUDE_BULLET_LIST extern std::vector<std::unique_ptr<Bullet>>BULLET_LIST;
#define INCLUDE_CLASS_DATA extern std::map<Class,std::unique_ptr<ClassData>>CLASS_DATA;
#define INCLUDE_PLAYER_BULLET_LIST extern std::vector<std::unique_ptr<Bullet>>PLAYER_BULLET_LIST;
#define INCLUDE_PARTICLE_LIST extern std::vector<Particle>PARTICLE_LIST;
#define ACCESS_PLAYER Player&p=game->GetPlayer();

View File

@ -5,8 +5,8 @@
INCLUDE_ANIMATION_DATA
INCLUDE_game
Effect::Effect(vf2d pos,float lifetime,AnimationState animation,float size,float fadeout,vf2d spd,Pixel col)
:pos(pos),lifetime(lifetime),size(size),fadeout(fadeout),original_fadeoutTime(fadeout),spd(spd),col(col){
Effect::Effect(vf2d pos,float lifetime,AnimationState animation,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col)
:pos(pos),lifetime(lifetime),upperLevel(upperLevel),size(size),fadeout(fadeout),original_fadeoutTime(fadeout),spd(spd),col(col){
this->animation.AddState(animation,ANIMATION_DATA[animation]);
}
@ -33,4 +33,8 @@ void Effect::Draw(){
Animate2D::Frame Effect::GetFrame(){
return animation.GetFrame(internal_animState);
}
bool Effect::OnUpperLevel(){
return upperLevel;
}

View File

@ -10,12 +10,14 @@ struct Effect{
float size=1;
Pixel col=WHITE;
vf2d spd={};
Effect(vf2d pos,float lifetime,AnimationState animation,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE);
Effect(vf2d pos,float lifetime,AnimationState animation,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE);
bool Update(float fElapsedTime);
Animate2D::Frame GetFrame();
void Draw();
bool OnUpperLevel();
private:
Animate2D::Animation<AnimationState>animation;
Animate2D::AnimationState internal_animState;
float original_fadeoutTime;
bool upperLevel=false;
};

View File

@ -6,14 +6,14 @@
INCLUDE_game
EnergyBolt::EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool friendly,Pixel col)
:Bullet(pos,vel,radius,damage,AnimationState::ENERGY_BOLT,false,INFINITE,true,friendly,col){}
EnergyBolt::EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
:Bullet(pos,vel,radius,damage,AnimationState::ENERGY_BOLT,upperLevel,false,INFINITE,true,friendly,col){}
void EnergyBolt::Update(float fElapsedTime){
lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime);
if(lastParticleSpawn==0){
lastParticleSpawn=0.03;
game->AddEffect(Effect(pos,util::random(1),AnimationState::ENERGY_PARTICLE,util::random(2),0.5,{util::random(60)-30,util::random(60)-30}));
game->AddEffect(Effect(pos,util::random(1),AnimationState::ENERGY_PARTICLE,upperLevel,util::random(2),0.5,{util::random(60)-30,util::random(60)-30}));
}
}
@ -21,7 +21,7 @@ bool EnergyBolt::PlayerHit(Player& player)
{
deactivated=true;
fadeOutTime=0.2f;
game->AddEffect(Effect(player.GetPos(),0,AnimationState::SPLASH_EFFECT,player.GetSizeMult(),0.25));
game->AddEffect(Effect(player.GetPos(),0,AnimationState::SPLASH_EFFECT,upperLevel,player.GetSizeMult(),0.25));
return false;
}
@ -29,6 +29,6 @@ bool EnergyBolt::MonsterHit(Monster& monster)
{
deactivated=true;
fadeOutTime=0.2f;
game->AddEffect(Effect(monster.GetPos(),0,AnimationState::SPLASH_EFFECT,monster.GetSizeMult(),0.25));
game->AddEffect(Effect(monster.GetPos(),0,AnimationState::SPLASH_EFFECT,upperLevel,monster.GetSizeMult(),0.25));
return false;
}

View File

@ -7,14 +7,14 @@
INCLUDE_game
INCLUDE_MONSTER_LIST
FireBolt::FireBolt(vf2d pos,vf2d vel,float radius,int damage,bool friendly,Pixel col)
:Bullet(pos,vel,radius,damage,AnimationState::ENERGY_BOLT,false,INFINITE,true,friendly,col){}
FireBolt::FireBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
:Bullet(pos,vel,radius,damage,AnimationState::ENERGY_BOLT,upperLevel,false,INFINITE,true,friendly,col){}
void FireBolt::Update(float fElapsedTime){
lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime);
if(lastParticleSpawn==0){
lastParticleSpawn=0.03;
game->AddEffect(Effect(pos,util::random(1),AnimationState::ENERGY_PARTICLE,util::random(2),0.3,{util::random(120)-60,-util::random(60)},{255,uint8_t(util::random(250)),0}));
game->AddEffect(Effect(pos,util::random(1),AnimationState::ENERGY_PARTICLE,upperLevel,util::random(2),0.3,{util::random(120)-60,-util::random(60)},{255,uint8_t(util::random(250)),0}));
}
}
@ -22,7 +22,7 @@ bool FireBolt::PlayerHit(Player& player)
{
deactivated=true;
fadeOutTime=0.2f;
game->AddEffect(Effect(player.GetPos(),0,AnimationState::SPLASH_EFFECT,5,0.25,{},{240,120,60}));
game->AddEffect(Effect(player.GetPos(),0,AnimationState::SPLASH_EFFECT,upperLevel,5,0.25,{},{240,120,60}));
return false;
}
@ -31,7 +31,7 @@ bool FireBolt::MonsterHit(Monster& monster)
deactivated=true;
fadeOutTime=0.2f;
for(int i=0;i<72;i++){
game->AddEffect(Effect(monster.GetPos(),util::random(0.5),AnimationState::DOT_PARTICLE,util::random(2),util::random(0.4),{util::random(300)-150,util::random(300)-150},{255,uint8_t(util::random(190)+60),60}));
game->AddEffect(Effect(monster.GetPos(),util::random(0.5),AnimationState::DOT_PARTICLE,upperLevel,util::random(2),util::random(0.4),{util::random(300)-150,util::random(300)-150},{255,uint8_t(util::random(190)+60),60}));
}
game->SetupWorldShake(0.25);
for(Monster&m:MONSTER_LIST){
@ -39,6 +39,6 @@ bool FireBolt::MonsterHit(Monster& monster)
m.Hurt(3*damage);
}
}
game->AddEffect(Effect(monster.GetPos(),0,AnimationState::SPLASH_EFFECT,5,0.25,{},{240,120,60}));
game->AddEffect(Effect(monster.GetPos(),0,AnimationState::SPLASH_EFFECT,upperLevel,5,0.25,{},{240,120,60}));
return false;
}

View File

@ -65,12 +65,14 @@ bool Monster::SetX(float x){
geom2d::rect<int>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
if(collisionRect.pos==vi2d{0,0}&&collisionRect.size==vi2d{1,1}){
pos.x=std::clamp(x,12.f*GetSizeMult(),float(game->WORLD_SIZE.x*24-12*GetSizeMult()));
Moved();
return true;
} else {
geom2d::rect<float>collision={collisionRect.pos,collisionRect.size};
collision.pos+=tilePos;
if(!geom2d::overlaps(newPos,collision)){
pos.x=std::clamp(x,12.f*GetSizeMult(),float(game->WORLD_SIZE.x*24-12*GetSizeMult()));
Moved();
return true;
}
}
@ -82,12 +84,14 @@ bool Monster::SetY(float y){
geom2d::rect<int>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
if(collisionRect.pos==vi2d{0,0}&&collisionRect.size==vi2d{1,1}){
pos.y=std::clamp(y,12.f*GetSizeMult(),float(game->WORLD_SIZE.y*24-12*GetSizeMult()));
Moved();
return true;
} else {
geom2d::rect<float>collision={collisionRect.pos,collisionRect.size};
collision.pos+=tilePos;
if(!geom2d::overlaps(newPos,collision)){
pos.y=std::clamp(y,12.f*GetSizeMult(),float(game->WORLD_SIZE.y*24-12*GetSizeMult()));
Moved();
return true;
}
}
@ -155,7 +159,7 @@ bool Monster::Update(float 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(),false, { 75 / 2,162 / 2,225 / 2 })));
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 })));
}
}
}
@ -281,6 +285,19 @@ bool Monster::SetPosition(vf2d pos){
}
return resultX|resultY;
}
void Monster::Moved(){
ZoneData&zoneData=game->GetZoneData(game->GetCurrentLevel());
for(geom2d::rect<int>&upperLevelZone:zoneData["UpperZone"]){
if(geom2d::overlaps(upperLevelZone,pos)){
upperLevel=true;
}
}
for(geom2d::rect<int>&lowerLevelZone:zoneData["LowerZone"]){
if(geom2d::overlaps(lowerLevelZone,pos)){
upperLevel=false;
}
}
}
AnimationState Monster::GetDeathAnimationName(){
return MONSTER_DATA[type].GetDeathAnimation();
}

View File

@ -111,6 +111,7 @@ protected:
void PerformJumpAnimation();
void PerformShootAnimation();
bool OnUpperLevel();
void Moved();
void AddBuff(BuffType type,float duration,float intensity);
std::vector<Buff>GetBuffs(BuffType buff);

View File

@ -12,7 +12,6 @@ INCLUDE_ANIMATION_DATA
INCLUDE_SPAWNER_LIST
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_CLASS_DATA
INCLUDE_PLAYER_BULLET_LIST
INCLUDE_game
const float Player::GROUND_SLAM_SPIN_TIME=0.6f;
@ -209,7 +208,7 @@ void Player::Update(float fElapsedTime){
spin_angle=0;
z=0;
game->HurtEnemies(pos,3*12,GetAttack()*2.5);
game->AddEffect(Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_FRONT,1.33f,0.6f},Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_BACK,1.33f,0.6f});
game->AddEffect(Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_FRONT,upperLevel,1.33f,0.6f},Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_BACK,upperLevel,1.33f,0.6f});
}
if(lastAnimationFlip>0){
lastAnimationFlip=std::max(0.f,lastAnimationFlip-fElapsedTime);

View File

@ -2,7 +2,7 @@
#define VERSION_MAJOR 0
#define VERSION_MINOR 2
#define VERSION_PATCH 0
#define VERSION_BUILD 503
#define VERSION_BUILD 522
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -1040,7 +1040,7 @@
<objectgroup id="4" name="Object Layer 1">
<object id="2" type="LowerBridgeCollision" x="3048" y="1440" width="24" height="96"/>
<object id="3" type="LowerBridgeCollision" x="3840" y="1440" width="24" height="96"/>
<object id="5" type="UpperZone" x="2208" y="1152" width="312" height="672"/>
<object id="5" type="UpperZone" x="1944" y="1152" width="576" height="672"/>
<object id="6" type="LowerZone" x="1608" y="1224" width="336" height="600"/>
<object id="9" name="Player Spawn" type="PlayerSpawnLocation" x="1872" y="1488" width="24" height="24"/>
</objectgroup>