diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 09793e20..670e2386 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -539,6 +539,9 @@ void AiL::HandleUserInput(float fElapsedTime){ else Menu::OpenMenu(MenuType::PAUSE); } float animationSpd=0.f; + if(player->GetPreviousPos()!=player->GetPos()){ + player->previousPos=player->GetPos(); + } player->movementVelocity={}; if((player->GetVelocity().mag()<"Player.Move Allowed Velocity Lower Limit"_F&&player->CanMove())||(player->GetState()==State::ROLL&&"Thief.Right Click Ability.Roll Time"_F-player->rolling_timer>=0.2f)){ auto GetPlayerStaircaseDirection=[&](){ diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h index d8bfb817..3172ec35 100644 --- a/Adventures in Lestoria/BulletTypes.h +++ b/Adventures in Lestoria/BulletTypes.h @@ -445,7 +445,7 @@ private: }; struct GhostSaber:public Bullet{ - GhostSaber(const vf2d pos,const std::weak_ptrtarget,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const int damage,const bool upperLevel,const float rotSpd,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1},const float image_angle=0.f); + GhostSaber(const vf2d pos,const std::weak_ptrtarget,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const float expandSpd,const int damage,const bool upperLevel,const float rotSpd,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1},const float image_angle=0.f); void Update(float fElapsedTime)override; BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!! BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!! @@ -453,10 +453,11 @@ struct GhostSaber:public Bullet{ private: const std::weak_ptrattachedMonster; const float rotSpd; - const float distFromTarget; + float distFromTarget; float rot; const float knockbackAmt; float particleTimer{}; float aliveTime{}; + float expandSpd{}; OscillatoralphaOscillator{128U,255U,0.6f}; }; \ No newline at end of file diff --git a/Adventures in Lestoria/GhostOfPirateCaptain.cpp b/Adventures in Lestoria/GhostOfPirateCaptain.cpp index e73942bd..25bdbeed 100644 --- a/Adventures in Lestoria/GhostOfPirateCaptain.cpp +++ b/Adventures in Lestoria/GhostOfPirateCaptain.cpp @@ -68,7 +68,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std const auto AdvanceCannonPhase{[&m,&strategy](){ m.GetFloat(A::CANNON_TIMER)=0.f; - //if(m.GetInt(A::CANNON_PHASE)+1>=PHASE_COUNT)m.GetInt(A::CANNON_SHOT_TYPE)=util::random()%5; + if(m.GetInt(A::CANNON_PHASE)+1>=PHASE_COUNT)m.GetInt(A::CANNON_SHOT_TYPE)=util::random()%5; const int prevCannonPhase{m.I(A::CANNON_PHASE)}; m.I(A::CANNON_PHASE)=(m.I(A::CANNON_PHASE)+1)%PHASE_COUNT; if(prevCannonPhase>m.I(A::CANNON_PHASE)){//Phase has wrapped around, reset cannon shot count. @@ -84,7 +84,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std } } }}; - + if(m.F(A::SHRAPNEL_SHOT_FALL_TIMER)>0.f){ m.F(A::SHRAPNEL_SHOT_FALL_TIMER)-=fElapsedTime; while(m.I(A::SHRAPNEL_SHOT_COUNT)&&m.F(A::SHRAPNEL_SHOT_FALL_TIMER)<=0.f){ @@ -101,10 +101,11 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std if(m.B(A::FIRST_WAVE_COMPLETE)){ m.SetVelocity(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())); - if(m.F(A::GHOST_SABER_TIMER)<=0.f){ + const float distToPlayer{util::distance(m.GetPos(),game->GetPlayer()->GetPos())}; + if(m.F(A::GHOST_SABER_TIMER)<=0.f&&distToPlayer<=ConfigPixels("Ghost Saber Activation Range")){ m.F(A::GHOST_SABER_TIMER)=ConfigFloat("Ghost Saber Cooldown"); const float playerToMonsterAngle{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y}; - CreateBullet(GhostSaber)(m.GetPos(),m.GetWeakPointer(),ConfigFloat("Ghost Saber Lifetime"),ConfigFloat("Ghost Saber Distance"),ConfigFloat("Ghost Saber Knockback Amt"),playerToMonsterAngle,ConfigFloat("Ghost Saber Radius"),ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")))EndBullet; + CreateBullet(GhostSaber)(m.GetPos(),m.GetWeakPointer(),ConfigFloat("Ghost Saber Lifetime"),ConfigFloat("Ghost Saber Distance"),ConfigFloat("Ghost Saber Knockback Amt"),playerToMonsterAngle,ConfigFloat("Ghost Saber Radius"),ConfigFloat("Ghost Saber Expand Spd"),ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")))EndBullet; } } @@ -121,7 +122,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std if(cannonCycle==CANNON_SHOT)TOTAL_CANNON_SHOTS++; } m.B(A::FIRST_WAVE_COMPLETE)=false; - m.I(A::CANNON_SHOT_TYPE)=PREDICTION; + m.I(A::CANNON_SHOT_TYPE)=BOMBARDMENT; m.ForceSetPos({-400.f,-400.f}); SETPHASE(NORMAL); }break; @@ -157,6 +158,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std LOG(std::format("Previous Pos: {} Current: {}",game->GetPlayer()->GetPreviousPos().str(),game->GetPlayer()->GetPos().str())); const float angle{util::angleTo(game->GetPlayer()->GetPreviousPos(),game->GetPlayer()->GetPos())}; const float range{util::random_range(0,100.f*game->GetPlayer()->GetMoveSpdMult())*ConfigFloat("Cannon Shot Impact Time")}; + LOG(std::format("Range/Angle: {}",vf2d{range,angle}.str())); const vf2d targetPos{game->GetPlayer()->GetPos()+vf2d{range,angle}.cart()}; CreateBullet(FallingBullet)("cannonball.png",targetPos,ConfigVec("Cannon Vel"),ConfigFloatArr("Cannon Vel",2),ConfigFloat("Indicator Time"),ConfigPixels("Cannon Radius"),ConfigInt("Cannon Damage"),m.OnUpperLevel(),false,ConfigFloat("Cannon Knockback Amt"),ConfigFloat("Cannon Shot Impact Time"),false,ConfigPixel("Cannon Spell Circle Color"),vf2d{ConfigFloat("Cannon Radius")/100.f*1.75f,ConfigFloat("Cannon Radius")/100.f*1.75f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Circle Rotation Spd")),ConfigPixel("Cannon Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Cannon Spell Insignia Rotation Spd")))EndBullet; }break; diff --git a/Adventures in Lestoria/GhostSaber.cpp b/Adventures in Lestoria/GhostSaber.cpp index 20bee8df..b7917f70 100644 --- a/Adventures in Lestoria/GhostSaber.cpp +++ b/Adventures in Lestoria/GhostSaber.cpp @@ -38,12 +38,13 @@ All rights reserved. #include "BulletTypes.h" -GhostSaber::GhostSaber(const vf2d pos,const std::weak_ptrtarget,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const int damage,const bool upperLevel,const float rotSpd,const bool friendly,const Pixel col,const vf2d scale,const float image_angle) -:Bullet(target.lock()->GetPos()+vf2d{distFromTarget,initialRot}.cart(),{},radius,damage,"ghost_dagger.png",upperLevel,false,INFINITE,false,friendly,col,scale,image_angle),attachedMonster(target),rotSpd(rotSpd),distFromTarget(distFromTarget),rot(initialRot),aliveTime(lifetime),knockbackAmt(knockbackAmt){} +GhostSaber::GhostSaber(const vf2d pos,const std::weak_ptrtarget,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const float expandSpd,const int damage,const bool upperLevel,const float rotSpd,const bool friendly,const Pixel col,const vf2d scale,const float image_angle) +:Bullet(target.lock()->GetPos()+vf2d{distFromTarget,initialRot}.cart(),{},radius,damage,"ghost_dagger.png",upperLevel,false,INFINITE,false,friendly,col,scale,image_angle),attachedMonster(target),rotSpd(rotSpd),distFromTarget(distFromTarget),rot(initialRot),aliveTime(lifetime),knockbackAmt(knockbackAmt),expandSpd(expandSpd){} void GhostSaber::Update(float fElapsedTime){ alphaOscillator.Update(fElapsedTime); particleTimer-=fElapsedTime; aliveTime-=fElapsedTime; + distFromTarget+=expandSpd*fElapsedTime; if(particleTimer<=0.f){ particleTimer+=0.05f; game->AddEffect(std::make_unique(pos,0.1f,0.1f,"pixel.png",2.f,vf2d{},Pixel{239,215,98,192},util::random(2*PI),0.f,true)); diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index c5b48298..98b726b0 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -282,6 +282,7 @@ void Monster::Update(const float fElapsedTime){ specialMarkApplicationTimer=std::max(0.f,specialMarkApplicationTimer-fElapsedTime); lastFacingDirectionChange+=fElapsedTime; timeSpentAlive+=fElapsedTime; + floatOscillator.Update(fElapsedTime); if(IsUnconscious()){ unconsciousTimer=std::max(0.f,unconsciousTimer-fElapsedTime); @@ -501,7 +502,7 @@ void Monster::Draw()const{ else if(glowPurpleBuff.has_value())blendCol=Pixel{uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().duration))),uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().duration))),uint8_t(128+127*abs(sin(1.4*glowPurpleBuff.value().duration)))}; const vf2d hitTimerOffset=vf2d{sin(20*PI*lastHitTimer+randomFrameOffset),0.f}*2.f*GetSizeMult(); - const vf2d zOffset=-vf2d{0,GetZ()}; + const vf2d zOffset=-vf2d{0,GetZ()}+vf2d{0,(MONSTER_DATA[GetName()].IsFloating()?floatOscillator.get():0.f)}; const vf2d drawPos=GetPos()+zOffset+hitTimerOffset; diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index b7c164f3..702fe628 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -49,6 +49,7 @@ All rights reserved. #include "MonsterData.h" #include "Direction.h" #include "HurtDamageInfo.h" +#include "Oscillator.h" INCLUDE_ITEM_DATA INCLUDE_MONSTER_DATA @@ -344,6 +345,7 @@ private: const float TIME_BETWEEN_LINE_REMOVALS{0.025f}; uint8_t scanLine{24}; vf2d afterImagePos{}; + OscillatorfloatOscillator{0.f,8.f,0.5f}; struct STRATEGY{ static std::string ERR; diff --git a/Adventures in Lestoria/MonsterData.cpp b/Adventures in Lestoria/MonsterData.cpp index 5fe12cef..21aa7def 100644 --- a/Adventures in Lestoria/MonsterData.cpp +++ b/Adventures in Lestoria/MonsterData.cpp @@ -202,6 +202,7 @@ void MonsterData::InitializeMonsterData(){ if(DATA["Monsters"][MonsterName].HasProperty("ShowBossIndicator"))monster.hasArrowIndicator=DATA["Monsters"][MonsterName]["ShowBossIndicator"].GetBool(); if(DATA["Monsters"][MonsterName].HasProperty("Unconscious Time"))monster.totalUnconsciousTime=DATA["Monsters"][MonsterName]["Unconscious Time"].GetReal(); + if(DATA["Monsters"][MonsterName].HasProperty("Floating"))monster.floating=DATA["Monsters"][MonsterName]["Floating"].GetBool(); if(DATA["Monsters"][MonsterName].HasProperty("Rectangle Collision")){ const datafile&rectData{DATA["Monsters"][MonsterName]["Rectangle Collision"]}; @@ -481,4 +482,8 @@ const bool MonsterData::FaceTarget()const{ } const float MonsterData::UnconsciousTime()const{ return totalUnconsciousTime; +} + +const bool MonsterData::IsFloating()const{ + return floating; } \ No newline at end of file diff --git a/Adventures in Lestoria/MonsterData.h b/Adventures in Lestoria/MonsterData.h index 641ff930..bfde7481 100644 --- a/Adventures in Lestoria/MonsterData.h +++ b/Adventures in Lestoria/MonsterData.h @@ -109,6 +109,7 @@ public: const bool FadeoutWhenStandingBehind()const; const bool FaceTarget()const; const float UnconsciousTime()const; + const bool IsFloating()const; //A floating monster oscillates back and forth. private: std::string name; int hp; @@ -142,4 +143,5 @@ private: bool hasArrowIndicator{false}; std::optional>rectCollision; float totalUnconsciousTime{}; + bool floating{false}; }; \ No newline at end of file diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 5035174e..4eb93cae 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -399,7 +399,7 @@ void Player::Update(float fElapsedTime){ hpRecoveryTimer=std::max(0.f,hpRecoveryTimer-fElapsedTime); hp6RecoveryTimer=std::max(0.f,hp6RecoveryTimer-fElapsedTime); hp4RecoveryTimer=std::max(0.f,hp4RecoveryTimer-fElapsedTime); - const vf2d previousPosTemp{GetPos()}; //The logic here is that the previous position must be a different value from the actual position and should only change when the position of the player actually changes, this means we must wait for the entire frame to resolve to see if any movement has occurred to update to the "new previous position". This variable is resolved at the bottom of this Update function. + RunTimers(); PerformHPRecovery(); @@ -856,10 +856,6 @@ void Player::Update(float fElapsedTime){ } } #pragma endregion - - if(previousPosTemp!=GetPos()){ - previousPos=previousPosTemp; //The logic here is that the previous position must be a different value from the actual position and should only change when the position of the player actually changes, this means we must wait for the entire frame to resolve to see if any movement has occurred to update to the "new previous position". The previous position temp variable is set at the top of this Update function. - } } float Player::GetSwordSwingTimer(){ diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 779d31cf..ddce327a 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 1 #define VERSION_MINOR 3 #define VERSION_PATCH 0 -#define VERSION_BUILD 12016 +#define VERSION_BUILD 12029 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 863fff02..edb9e0a0 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -1333,13 +1333,16 @@ MonsterStrategy # Degrees/sec. Positive is CW, Negative is CCW. Cannon Spell Insignia Rotation Spd = 50 + Ghost Saber Activation Range = 600 Ghost Saber Cooldown = 4s Ghost Saber Lifetime = 4s # Distance from the boss the ghost sabers should spin around. - Ghost Saber Distance = 56px + Ghost Saber Distance = 48px Ghost Saber Radius = 8px Ghost Saber Damage = 75 - Ghost Saber Rotation Spd = 180deg/s + Ghost Saber Rotation Spd = 210deg/s Ghost Saber Knockback Amt = 100 + # Amount of pixels/sec the ghost saber circle expands outwards. + Ghost Saber Expand Spd = 14px } } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/Monsters.txt b/Adventures in Lestoria/assets/config/Monsters.txt index 938448a8..3f55a5a3 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -1827,6 +1827,9 @@ Monsters # A flag to show an arrow indicator when the boss is off-screen. ShowBossIndicator = True + # Adds a floating up-and-down animation. Only a visual effect. + Floating = True + Ignore Collisions = True Strategy = Ghost of Pirate Captain diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 47e26443..fa1a6343 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ