Fix bug with double rendering. Not using iterator marker to determine what else to draw. Fix bug with maximum health not healing to maximum when health is affected by Health %. Refactored wind speed to be a global value within the game's engine. Included speed reduction properties for wind when warrior blocks. Include projectiles/player being affected by wind and casting to be allowed when pushed by wind. Release Build 9507.

pull/57/head
sigonasr2 6 months ago
parent 05caa062e1
commit a8bb30e12f
  1. 38
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 3
      Adventures in Lestoria/AdventuresInLestoria.h
  3. 3
      Adventures in Lestoria/Bullet.cpp
  4. 2
      Adventures in Lestoria/Monster.cpp
  5. 46
      Adventures in Lestoria/Player.cpp
  6. 19
      Adventures in Lestoria/Player.h
  7. 1
      Adventures in Lestoria/TODO.txt
  8. 2
      Adventures in Lestoria/Version.h
  9. 4
      Adventures in Lestoria/Zephy.cpp
  10. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1403,15 +1403,19 @@ void AiL::RenderWorld(float fElapsedTime){
}
#pragma endregion
#pragma region Remaining Rendering
for(Monster*m:monstersBeforeLower){
while(monstersBeforeLowerIt!=monstersBeforeLower.end()){
Monster*const m=*monstersBeforeLowerIt;
m->strategyDraw(this,*m,MONSTER_DATA[m->GetName()].GetAIStrategy());
m->Draw();
++monstersBeforeLowerIt;
}
for(const Effect*const e:backgroundEffectsLower){
e->Draw();
}
for(const int dropInd:dropsBeforeLower){
while(dropsBeforeLowerIt!=dropsBeforeLower.end()){
const int dropInd=*dropsBeforeLowerIt;
ItemDrop::drops[dropInd].Draw();
++dropsBeforeLowerIt;
}
if(!player->rendered&&!player->upperLevel){
player->rendered=true;
@ -1421,11 +1425,14 @@ void AiL::RenderWorld(float fElapsedTime){
}
RenderPlayer(player->GetPos(),{1,1});
}
for(Monster* m:monstersAfterLower){
while(monstersAfterLowerIt!=monstersAfterLower.end()){
Monster*const m=*monstersAfterLowerIt;
m->strategyDraw(this,*m,MONSTER_DATA[m->GetName()].GetAIStrategy());
m->Draw();
++monstersAfterLowerIt;
}
for(const int dropInd:dropsAfterLower){
while(dropsAfterLowerIt!=dropsAfterLower.end()){
const int dropInd=*dropsAfterLowerIt;
ItemDrop::drops[dropInd].Draw();
}
for(const Bullet*const b:bulletsLower){
@ -1697,14 +1704,17 @@ void AiL::RenderWorld(float fElapsedTime){
}
#pragma endregion
#pragma region Remaining Upper Rendering
for(Monster*m:monstersBeforeUpper){
while(monstersBeforeUpperIt!=monstersBeforeUpper.end()){
Monster*const m=*monstersBeforeUpperIt;
m->strategyDraw(this,*m,MONSTER_DATA[m->GetName()].GetAIStrategy());
m->Draw();
++monstersBeforeUpperIt;
}
for(const Effect*const e:backgroundEffectsUpper){
e->Draw();
}
for(const int dropInd:dropsBeforeUpper){
while(dropsBeforeUpperIt!=dropsBeforeUpper.end()){
const int dropInd=*dropsBeforeUpperIt;
ItemDrop::drops[dropInd].Draw();
}
if(!player->rendered&&player->upperLevel){
@ -1715,12 +1725,16 @@ void AiL::RenderWorld(float fElapsedTime){
}
RenderPlayer(player->GetPos(),{1,1});
}
for(Monster*m:monstersAfterUpper){
while(monstersAfterUpperIt!=monstersAfterUpper.end()){
Monster*const m=*monstersAfterUpperIt;
m->strategyDraw(this,*m,MONSTER_DATA[m->GetName()].GetAIStrategy());
m->Draw();
++monstersAfterUpperIt;
}
for(const int dropInd:dropsAfterUpper){
while(dropsAfterUpperIt!=dropsAfterUpper.end()){
const int dropInd=*dropsAfterUpperIt;
ItemDrop::drops[dropInd].Draw();
++dropsAfterUpperIt;
}
for(const Bullet*const b:bulletsUpper){
b->Draw();
@ -2285,6 +2299,7 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
totalDamageDealt=0;
encounterStarted=false;
totalBossEncounterMobs=0;
SetWindSpeed({});
Inventory::Clear("Monster Loot");
Inventory::Clear("Stage Loot");
Inventory::ResetLoadoutItemsUsed();
@ -4273,4 +4288,11 @@ Overlay&AiL::GetOverlay(){
void AiL::SetOverlay(std::string animationName,Pixel overlayCol){
hudOverlay=Overlay{animationName,overlayCol};
}
void AiL::SetWindSpeed(vf2d newWindSpd){
windSpd=newWindSpd;
}
const vf2d&AiL::GetWindSpeed()const{
return windSpd;
}

@ -196,6 +196,7 @@ private:
float vignetteDisplayTime=0.f;
bool savingFile=false;
bool prevStageCompleted=false;
vf2d windSpd{};
void ValidateGameStatus();
void _PrepareLevel(MapName map,MusicChange changeMusic);
@ -351,6 +352,8 @@ public:
void SetBossIndicatorPos(const vf2d pos);
void SetOverlay(std::string animationName,Pixel overlayCol);
Overlay&GetOverlay();
void SetWindSpeed(vf2d newWindSpd);
const vf2d&GetWindSpeed()const;
struct TileGroupData{
vi2d tilePos;

@ -139,6 +139,7 @@ void Bullet::_Update(const float fElapsedTime){
}else{
pos+=vel*fElapsedTime;
}
if(IsPlayerAutoAttackProjectile()){pos+=game->GetWindSpeed()*game->GetElapsedTime();}
if(/*World size in PIXELS!*/vi2d worldSize=game->GetCurrentMapData().MapSize*game->GetCurrentMapData().TileSize;pos.x+radius<-WINDOW_SIZE.x||pos.x-radius>worldSize.x+WINDOW_SIZE.x||pos.y+radius<-WINDOW_SIZE.y||pos.y-radius>worldSize.y+WINDOW_SIZE.y){
dead=true;
return;
@ -225,5 +226,5 @@ const bool Bullet::IsPlayerAutoAttackProjectile()const{
}
void Bullet::AddVelocity(vf2d vel){
this->vel+=vel;
this->vel+=vel*game->GetElapsedTime();
}

@ -440,7 +440,7 @@ void Monster::Draw()const{
if(GetZ()>0){
vf2d shadowScale=vf2d{8*GetSizeMult()/3.f,1}/std::max(1.f,GetZ()/24);
game->view.DrawDecal(drawPos-vf2d{3,3}*shadowScale/2+vf2d{0,6*GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
game->view.DrawDecal(GetPos()+hitTimerOffset-vf2d{3,3}*shadowScale/2+vf2d{0,6*GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
const bool NotOnTitleScreen=GameState::STATE!=GameState::states[States::MAIN_MENU];

@ -143,7 +143,7 @@ void Player::ForceSetPos(vf2d pos){
Moved();
}
bool Player::_SetX(float x,const bool playerInvoked){
bool Player::_SetX(float x,MoveFlag::MoveFlag flags,const bool playerInvoked){
vf2d newPos={x,pos.y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
@ -152,7 +152,7 @@ bool Player::_SetX(float x,const bool playerInvoked){
#pragma endregion
if(NoTileCollisionExistsHere()){
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
Moved(flags);
return true;
} else {
geom2d::rect<float>collision={collisionRect.pos,collisionRect.size};
@ -162,21 +162,21 @@ bool Player::_SetX(float x,const bool playerInvoked){
collision.pos+=tilePos;
if(NoPlayerCollisionWithTile()){
pos.x=std::clamp(x,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().width*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
Moved(flags);
return true;
}else
if(playerInvoked){ //If player invoked, we'll try the smart move system.
vf2d pushDir=geom2d::line<float>(collision.middle(),pos).vector().norm();
newPos={newPos.x,pos.y+pushDir.y*12};
if(NoPlayerCollisionWithTile()){
return _SetY(pos.y+pushDir.y*game->GetElapsedTime()*12,false);
return _SetY(pos.y+pushDir.y*game->GetElapsedTime()*12,flags,false);
}
}
}
return false;
};
bool Player::_SetY(float y,const bool playerInvoked){
bool Player::_SetY(float y,MoveFlag::MoveFlag flags,const bool playerInvoked){
vf2d newPos={pos.x,y};
vi2d tilePos=vi2d(newPos/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos,upperLevel);
@ -185,7 +185,7 @@ bool Player::_SetY(float y,const bool playerInvoked){
#pragma endregion
if(NoTileCollisionExistsHere()){
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
Moved(flags);
return true;
} else {
geom2d::rect<float>collision={collisionRect.pos,collisionRect.size};
@ -195,37 +195,37 @@ bool Player::_SetY(float y,const bool playerInvoked){
collision.pos+=tilePos;
if(NoPlayerCollisionWithTile()){
pos.y=std::clamp(y,game->GetCurrentMapData().tilewidth/2.f*GetSizeMult(),float(game->GetCurrentMapData().height*game->GetCurrentMapData().tilewidth-game->GetCurrentMapData().tilewidth/2.f*GetSizeMult()));
Moved();
Moved(flags);
return true;
}else
if(playerInvoked){ //If player invoked, we'll try the smart move system.{
vf2d pushDir=geom2d::line<float>(collision.middle(),pos).vector().norm();
newPos={pos.x+pushDir.x*12,newPos.y};
if(NoPlayerCollisionWithTile()){
return _SetX(pos.x+pushDir.x*game->GetElapsedTime()*12,false);
return _SetX(pos.x+pushDir.x*game->GetElapsedTime()*12,flags,false);
}
}
}
return false;
}
bool Player::SetX(float x){
return _SetX(x);
bool Player::SetX(float x,MoveFlag::MoveFlag flags){
return _SetX(x,flags);
}
bool Player::SetY(float y){
return _SetY(y);
bool Player::SetY(float y,MoveFlag::MoveFlag flags){
return _SetY(y,flags);
}
void Player::SetZ(float z){
this->z=z;
}
bool Player::SetPos(vf2d pos){
bool resultX=SetX(pos.x);
bool resultY=SetY(pos.y);
bool Player::SetPos(vf2d pos,MoveFlag::MoveFlag flags){
bool resultX=SetX(pos.x,flags);
bool resultY=SetY(pos.y,flags);
if(resultY&&!resultX){
resultX=SetX(pos.x);
resultX=SetX(pos.x,flags);
}
return resultX||resultY;
}
@ -548,6 +548,12 @@ void Player::Update(float fElapsedTime){
ERR(std::format("WARNING! The velocity vector for the player is NOT normal! Current vel:{} . Attempting manual resetting of velocity.",vel.str()));
vel={};
}
vf2d finalWindSpd=game->GetWindSpeed();
if(GetState()==State::BLOCK)finalWindSpd*=1-("Warrior.Right Click Ability.Knockback Reduction"_F/100.f);
game->GetPlayer()->SetPos(game->GetPlayer()->GetPos()+finalWindSpd*game->GetElapsedTime(),MoveFlag::PREVENT_CAST_CANCELLING);
if(vel!=vf2d{0,0}){
float newX=pos.x+vel.x*fElapsedTime;
float newY=pos.y+vel.y*fElapsedTime;
@ -875,8 +881,8 @@ void Player::CancelCast(){
}
}
void Player::Moved(){
if(state==State::CASTING){
void Player::Moved(MoveFlag::MoveFlag flags){
if(!(flags&MoveFlag::PREVENT_CAST_CANCELLING)&&state==State::CASTING){
state=State::NORMAL;
castPrepAbility->waitForRelease=true;
CancelCast();
@ -1078,7 +1084,7 @@ void Player::_SetIframes(float duration){
}
bool Player::Heal(int damage,bool suppressDamageNumber){
hp=std::clamp(hp+damage,0,int(GetStat("Health")));
hp=std::clamp(hp+damage,0,int(GetMaxHealth()));
if(!suppressDamageNumber&&damage>0){
DAMAGENUMBER_LIST.push_back(std::make_shared<DamageNumber>(GetPos(),damage,true,HEALTH_GAIN));
}
@ -1577,5 +1583,5 @@ void Player::ProximityKnockback(const vf2d centerPoint,const float knockbackFact
}
void Player::AddVelocity(vf2d vel){
this->vel+=vel;
this->vel+=vel*game->GetElapsedTime();
}

@ -62,6 +62,13 @@ struct CastInfo{
vf2d castPos{};
};
namespace MoveFlag{
enum MoveFlag{
NONE = 0b0,
PREVENT_CAST_CANCELLING = 0b1,
};
};
class EntityStats{
friend class Inventory;
ItemAttributable equipStats; //The stats after gear calculations are applied.
@ -148,12 +155,12 @@ public:
void RestoreMana(int amt,bool suppressDamageNumber=false);
void ConsumeMana(int amt);
//Returns true if the move was valid and successful.
bool SetX(float x);
bool SetX(float x,MoveFlag::MoveFlag flags=MoveFlag::NONE);
//Returns true if the move was valid and successful.
bool SetY(float y);
bool SetY(float y,MoveFlag::MoveFlag flags=MoveFlag::NONE);
void SetZ(float z);
//Returns true if the move was valid and successful.
bool SetPos(vf2d pos);
bool SetPos(vf2d pos,MoveFlag::MoveFlag flags=MoveFlag::NONE);
//Ignores collision checking and sets the player at the given position.
void ForceSetPos(vf2d pos);
void SetState(State::State newState);
@ -189,7 +196,7 @@ public:
bool IsOutOfCombat();
float GetEndZoneStandTime();
//Triggers when the player has moved.
void Moved();
void Moved(MoveFlag::MoveFlag flags=MoveFlag::NONE);
virtual ~Player()=default;
virtual Class GetClass()=0;
virtual bool AutoAttack()=0;
@ -311,12 +318,12 @@ private:
//If playerInvoked is true, this means the player was the one that instantiated this input, and it's not an extra movement done via collision.
//Set playerInvoked to false when you don't want a movement loop due to collisions.
//Typical usage is playerInvoked is true on first call, and playerInvoked is false on all subsequent chained calls.
bool _SetX(float x,const bool playerInvoked=true);
bool _SetX(float x,MoveFlag::MoveFlag flags=MoveFlag::NONE,const bool playerInvoked=true);
//Returns true if the move was valid and successful.
//If playerInvoked is true, this means the player was the one that instantiated this input, and it's not an extra movement done via collision.
//Set playerInvoked to false when you don't want a movement loop due to collisions.
//Typical usage is playerInvoked is true on first call, and playerInvoked is false on all subsequent chained calls.
bool _SetY(float y,const bool playerInvoked=true);
bool _SetY(float y,MoveFlag::MoveFlag flags=MoveFlag::NONE,const bool playerInvoked=true);
const bool UsingAutoAim()const;
void InitializeMinimapImage();
bool lowHealthSoundPlayed=false;

@ -23,4 +23,5 @@ New Monster Sound Effects
DEMO
====
Health % max health healing bug
Go back and apply new rendering code.

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_BUILD 9497
#define VERSION_BUILD 9507
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -256,10 +256,8 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
vf2d windSpd={m.F(A::WIND_STRENGTH)*"Player.MoveSpd"_F,0.f}; //Assume we landed left and causing a wind attack to the right.
if(!LeftLandingSite)windSpd*=-1;
game->GetPlayer()->AddVelocity(windSpd);
game->SetWindSpeed(windSpd);
m.F(A::CASTING_TIMER)=ConfigFloat("Wind Attack.Wind Duration");
std::for_each(BULLET_LIST.begin(),BULLET_LIST.end(),[&](const std::unique_ptr<Bullet>&bullet){if(bullet->IsPlayerAutoAttackProjectile()){bullet->AddVelocity(windSpd);}});
#pragma endregion
if(m.F(A::CASTING_TIMER)<=0.f){

Loading…
Cancel
Save