Refactor redundant deactivation variable to now be tied to the fade out time. Make collision checks for bullets with radii of 0 no longer occur. Add mid phase tornado. Release Build 9546.

pull/57/head
sigonasr2 6 months ago
parent 15cf736be0
commit 5f0a516be5
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 9
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 1
      Adventures in Lestoria/Animation.cpp
  4. 15
      Adventures in Lestoria/Arrow.cpp
  5. 25
      Adventures in Lestoria/Bomb.cpp
  6. 37
      Adventures in Lestoria/Bullet.cpp
  7. 24
      Adventures in Lestoria/Bullet.h
  8. 67
      Adventures in Lestoria/BulletTypes.h
  9. 8
      Adventures in Lestoria/ChargedArrow.cpp
  10. 10
      Adventures in Lestoria/DaggerSlash.cpp
  11. 10
      Adventures in Lestoria/DaggerStab.cpp
  12. 10
      Adventures in Lestoria/Debris.cpp
  13. 13
      Adventures in Lestoria/EnergyBolt.cpp
  14. 13
      Adventures in Lestoria/FireBolt.cpp
  15. 11
      Adventures in Lestoria/FrogTongue.cpp
  16. 76
      Adventures in Lestoria/LargeTornado.cpp
  17. 22
      Adventures in Lestoria/LevitatingRock.cpp
  18. 13
      Adventures in Lestoria/LightningBolt.cpp
  19. 8
      Adventures in Lestoria/Player.cpp
  20. 1
      Adventures in Lestoria/Player.h
  21. 10
      Adventures in Lestoria/Tornado.cpp
  22. 1
      Adventures in Lestoria/Ursule.cpp
  23. 2
      Adventures in Lestoria/Version.h
  24. 10
      Adventures in Lestoria/Wisp.cpp
  25. 20
      Adventures in Lestoria/Zephy.cpp
  26. 9
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  27. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  28. BIN
      Adventures in Lestoria/assets/gamepack.pak
  29. BIN
      Adventures in Lestoria/assets/large_tornado.png
  30. BIN
      x64/Release/Adventures in Lestoria.exe

@ -782,6 +782,10 @@
<ClCompile Include="ItemDrop.cpp" />
<ClCompile Include="ItemLoadoutWindow.cpp" />
<ClCompile Include="Key.cpp" />
<ClCompile Include="LargeTornado.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="LevelCompleteWindow.cpp" />
<ClCompile Include="LevitatingRock.cpp">
<SubType>

@ -1070,6 +1070,15 @@
<ClCompile Include="Overlay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Debris.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ForegroundEffect.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LargeTornado.cpp">
<Filter>Source Files\Bullet Types</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

@ -240,6 +240,7 @@ void sig::Animation::InitializeAnimations(){
CreateHorizontalAnimationSequence("goblin_bomb_fuse.png",4,{24,24},AnimationData{.frameDuration{1.f},.style{Animate2D::Style::OneShot}});
CreateHorizontalAnimationSequence("bomb_boom.png",5,{36,36},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::OneShot}});
CreateHorizontalAnimationSequence("tornado2.png",4,{24,48},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
CreateHorizontalAnimationSequence("large_tornado.png",4,{72,144},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
CreateStillAnimation("meteor.png",{192,192});

@ -57,8 +57,7 @@ void Arrow::Update(float fElapsedTime){
float speed=vel.mag();
travelDistance+=speed*fElapsedTime;
vel.y+=acc*fElapsedTime;
if(!deactivated&&travelDistance>=finalDistance){
deactivated=true;
if(IsActivated()&&travelDistance>=finalDistance){
fadeOutTime=0.2f;
}
}
@ -75,7 +74,7 @@ void Arrow::PointToBestTargetPath(const uint8_t perceptionLevel){
simulatedArrow.vel=simulatedAimingDir.cart();
vf2d originalSimulatedShootingAngle=simulatedArrow.vel;
while(!simulatedArrow.deactivated){
while(simulatedArrow.IsActivated()){
simulatedArrow.SimulateUpdate(1/30.f);
float distToPlayer=geom2d::line<float>(simulatedArrow.pos,game->GetPlayer()->GetPos()).length();
if(distToPlayer<closestDist){
@ -90,18 +89,16 @@ void Arrow::PointToBestTargetPath(const uint8_t perceptionLevel){
vel=closestVel;
}
bool Arrow::PlayerHit(Player*player)
BulletDestroyState Arrow::PlayerHit(Player*player)
{
deactivated=true;
fadeOutTime=0.2f;
game->AddEffect(std::make_unique<Effect>(player->GetPos(),0,"splash_effect.png",upperLevel,player->GetSizeMult(),0.25));
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool Arrow::MonsterHit(Monster& monster)
BulletDestroyState Arrow::MonsterHit(Monster& monster)
{
deactivated=true;
fadeOutTime=0.2f;
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),0.25));
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -47,11 +47,10 @@ INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_LIST
INCLUDE_game
Bomb::Bomb(const vf2d pos,const float z,const float gravity,const float detonationTime,const float bombFadeoutTime,const float bombKnockbackFactor,const vf2d targetPos,const float radius,const int damage,const bool upperLevel,const bool friendly,const Pixel col,const vf2d scale)
:Bullet(pos,{},radius,damage,"goblin_bomb.png",upperLevel,true,INFINITY,false,friendly,col,scale)
,gravity(gravity),detonationTime(detonationTime),bombFadeoutTime(bombFadeoutTime),bombKnockbackFactor(bombKnockbackFactor),targetPos(targetPos){
Bomb::Bomb(const vf2d pos,const float z,const float gravity,const float detonationTime,const float bombFadeoutTime,const float bombKnockbackFactor,const vf2d targetPos,const float explosionRadius,const int damage,const bool upperLevel,const bool friendly,const Pixel col,const vf2d scale)
:Bullet(pos,{},0.f,damage,"goblin_bomb.png",upperLevel,true,INFINITY,false,friendly,col,scale)
,explosionRadius(explosionRadius),gravity(gravity),detonationTime(detonationTime),bombFadeoutTime(bombFadeoutTime),bombKnockbackFactor(bombKnockbackFactor),targetPos(targetPos){
this->z=z;
deactivated=true;
bomb_animation.AddState("Fuse",ANIMATION_DATA.at("goblin_bomb_fuse.png"));
bomb_animation.ChangeState(animation,"Fuse");
}
@ -67,39 +66,39 @@ void Bomb::Update(float fElapsedTime){
if(fadeOutTime==0.f){
z=0; //Force the bomb to be grounded.
fadeOutTime=bombFadeoutTime;
game->AddEffect(std::make_unique<BombBoom>(pos,0.f,OnUpperLevel(),vf2d{radius,radius}/12.f/1.5f/*Upscale 24x24 to 36x36*/,1.f,vf2d{},WHITE,0.f,0.f,true));
game->AddEffect(std::make_unique<BombBoom>(pos,0.f,OnUpperLevel(),vf2d{explosionRadius,explosionRadius}/12.f/1.5f/*Upscale 24x24 to 36x36*/,1.f,vf2d{},WHITE,0.f,0.f,true));
SoundEffect::PlaySFX("Bomb Explode",pos);
float distToPlayer=geom2d::line<float>(pos,game->GetPlayer()->GetPos()).length();
if(friendly){
const HurtList hurtEnemies=game->Hurt(pos,radius,damage,OnUpperLevel(),z,HurtType::MONSTER);
const HurtList hurtEnemies=game->Hurt(pos,explosionRadius,damage,OnUpperLevel(),z,HurtType::MONSTER);
for(auto&[targetPtr,wasHit]:hurtEnemies){
if(!std::holds_alternative<Monster*>(targetPtr))ERR("WARNING! Hurt enemies list returned a non-monster pointer!? THIS SHOULD NOT BE HAPPENING!");
Monster*monsterPtr=std::get<Monster*>(targetPtr);
if(wasHit)monsterPtr->ProximityKnockback(pos,bombKnockbackFactor);
}
if(distToPlayer<=radius){
if(distToPlayer<=explosionRadius){
game->GetPlayer()->ProximityKnockback(pos,bombKnockbackFactor);
}
}else{
if(distToPlayer<=radius){
if(distToPlayer<=explosionRadius){
if(game->GetPlayer()->Hurt(damage,OnUpperLevel(),z)){
game->GetPlayer()->ProximityKnockback(pos,bombKnockbackFactor);
}
}
for(auto&monsterPtr:MONSTER_LIST){
float distToMonster=geom2d::line<float>(pos,monsterPtr->GetPos()).length();
if(distToMonster<=radius){
if(distToMonster<=explosionRadius){
monsterPtr->ProximityKnockback(pos,bombKnockbackFactor);
}
}
}
}
}
bool Bomb::PlayerHit(Player*player){
return false;
BulletDestroyState Bomb::PlayerHit(Player*player){
return BulletDestroyState::KEEP_ALIVE;
}
bool Bomb::MonsterHit(Monster&monster){
return false;
BulletDestroyState Bomb::MonsterHit(Monster&monster){
return BulletDestroyState::KEEP_ALIVE;
}
void Bomb::Draw(const Pixel blendCol)const{

@ -64,7 +64,6 @@ void Bullet::UpdateFadeTime(float fElapsedTime)
if(fadeInTime>0){
if(fadeInTimer<fadeInTime){
fadeInTimer=std::min(fadeInTime,fadeInTimer+fElapsedTime);
if(fadeInTimer==fadeInTime)deactivated=false;
}
}
if(fadeOutTime>0){
@ -84,10 +83,12 @@ void Bullet::SimulateUpdate(const float fElapsedTime){
}
void Bullet::_Update(const float fElapsedTime){
if(dead)return;
UpdateFadeTime(fElapsedTime);
Update(fElapsedTime);
animation.UpdateState(internal_animState,fElapsedTime);
if(!deactivated){
const bool CollisionCheckRequired=IsActivated()&&fadeInTimer==fadeInTime&&radius!=0.f;
if(CollisionCheckRequired){
float totalDistance=(vel*fElapsedTime).mag();
int iterations=int(std::max(1.f,(vel*fElapsedTime).mag()));
int totalIterations=iterations;
@ -100,7 +101,7 @@ void Bullet::_Update(const float fElapsedTime){
if(geom2d::overlaps(m->BulletCollisionHitbox(),geom2d::circle(pos,radius))){
if(hitList.find(&*m)==hitList.end()&&m->Hurt(damage,OnUpperLevel(),z)){
if(!hitsMultiple){
if(_MonsterHit(*m)){
if(_MonsterHit(*m)==BulletDestroyState::DESTROY){
dead=true;
}
return false;
@ -113,7 +114,7 @@ void Bullet::_Update(const float fElapsedTime){
if(geom2d::overlaps(game->GetPlayer()->Hitbox(),geom2d::circle(pos,radius))){
if(hitList.find(game->GetPlayer())==hitList.end()&&game->GetPlayer()->Hurt(damage,OnUpperLevel(),z)){
if(!hitsMultiple){
if(_PlayerHit(&*game->GetPlayer())){
if(_PlayerHit(&*game->GetPlayer())==BulletDestroyState::DESTROY){
dead=true;
}
return false;
@ -181,18 +182,18 @@ void Bullet::Draw(const Pixel blendCol)const{
}
}
bool Bullet::_PlayerHit(Player*player){
const bool destroyBullet=PlayerHit(player);
BulletDestroyState Bullet::_PlayerHit(Player*player){
const BulletDestroyState destroyBullet=PlayerHit(player);
if(iframeTimerOnHit>0.f)player->ApplyIframes(iframeTimerOnHit);
return destroyBullet;
}
bool Bullet::_MonsterHit(Monster&monster){
const bool destroyBullet=MonsterHit(monster);
BulletDestroyState Bullet::_MonsterHit(Monster&monster){
const BulletDestroyState destroyBullet=MonsterHit(monster);
if(iframeTimerOnHit>0.f)monster.ApplyIframes(iframeTimerOnHit);
return destroyBullet;
}
bool Bullet::PlayerHit(Player*player){return true;}
bool Bullet::MonsterHit(Monster&monster){return true;}
BulletDestroyState Bullet::PlayerHit(Player*player){return BulletDestroyState::DESTROY;}
BulletDestroyState Bullet::MonsterHit(Monster&monster){return BulletDestroyState::DESTROY;}
bool Bullet::OnUpperLevel(){return upperLevel;}
const bool Bullet::IsDead()const{
@ -211,7 +212,6 @@ Bullet&Bullet::SetFadeinTime(float fadeInTime){
const float durationDiff=fadeInTime-fadeInTimer;
this->fadeInTime=fadeInTime;
lifetime+=durationDiff;
deactivated=true;
return *this;
}
@ -231,3 +231,18 @@ const bool Bullet::IsPlayerAutoAttackProjectile()const{
void Bullet::AddVelocity(vf2d vel){
this->vel+=vel*game->GetElapsedTime();
}
void Bullet::SetBulletType(const BulletType type){
this->type=type;
}
const BulletType Bullet::GetBulletType()const{
return type;
}
const bool Bullet::IsActivated()const{
return !IsDeactivated();
}
const bool Bullet::IsDeactivated()const{
return fadeOutTime>0.f;
}

@ -41,6 +41,16 @@ All rights reserved.
#include "Monster.h"
#include "DEFINES.h"
enum class BulletType{
UNDEFINED,
LARGE_TORNADO,
};
enum class BulletDestroyState{
KEEP_ALIVE,
DESTROY,
};
struct Bullet{
friend class AiL;
vf2d vel;
@ -52,7 +62,6 @@ struct Bullet{
bool hitsMultiple=false;
bool rotates=false;
bool animated=false;
bool deactivated=false; //A deactivated bullet no longer interacts with the world. It's just a visual.
float fadeOutTime=0; //Setting the fade out time causes the bullet's lifetime to be set to the fadeout time as well, as that's when the bullet's alpha will reach 0, so it dies.
bool friendly=false; //Whether or not it's a player bullet or enemy bullet.
bool upperLevel=false;
@ -73,11 +82,13 @@ private:
float iframeTimerOnHit{0.f};
bool playerAutoAttackProjectile=false; //Set to true for bullets that are auto attack projectiles to identify them.
void _Draw()const;
BulletType type{BulletType::UNDEFINED};
protected:
float drawOffsetY{};
bool _PlayerHit(Player*player);
bool _MonsterHit(Monster&monster);
BulletDestroyState _PlayerHit(Player*player); //Return true to destroy the bullet on hit, return false otherwise. THE BULLET HIT HAS ALREADY OCCURRED.
BulletDestroyState _MonsterHit(Monster&monster); //Return true to destroy the bullet on hit, return false otherwise. THE BULLET HIT HAS ALREADY OCCURRED.
const float&GetFadeoutTimer()const;
void SetBulletType(const BulletType type);
public:
Animate2D::Animation<std::string>animation;
Animate2D::AnimationState internal_animState;
@ -92,10 +103,10 @@ public:
void _Update(const float fElapsedTime);
//Used by special bullets to control custom despawning behavior! 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.
//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
virtual bool PlayerHit(Player*player);
virtual BulletDestroyState PlayerHit(Player*player);
//Used by special bullets to control custom despawning behavior! 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.
//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
virtual bool MonsterHit(Monster&monster);
virtual BulletDestroyState MonsterHit(Monster&monster);
Animate2D::Frame GetFrame()const;
virtual void Draw(const Pixel blendCol)const;
bool OnUpperLevel();
@ -106,4 +117,7 @@ public:
Bullet&SetIsPlayerAutoAttackProjectile(); //Enables the playerAutoAttackProjectile flag.
const bool IsPlayerAutoAttackProjectile()const;
void AddVelocity(vf2d vel);
const BulletType GetBulletType()const;
const bool IsActivated()const;
const bool IsDeactivated()const;
};

@ -43,24 +43,24 @@ struct EnergyBolt:public Bullet{
float lastParticleSpawn=0;
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;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct FireBolt:public Bullet{
float lastParticleSpawn=0;
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;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct LightningBolt:public Bullet{
float lastParticleSpawn=0;
LightningBolt(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;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct Arrow:public Bullet{
@ -74,16 +74,16 @@ struct Arrow:public Bullet{
// Change the arrow's heading by predicting a path somewhere in the future and aiming at the closest possible spot to its targetPos.
// The perception level can be a value from 0-90 indicating the sweep angle to check beyond the initial aiming angle.
void PointToBestTargetPath(const uint8_t perceptionLevel);
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct ChargedArrow:public Bullet{
vf2d lastLaserPos;
ChargedArrow(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;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct FrogTongue:public Bullet{
@ -94,16 +94,16 @@ struct FrogTongue:public Bullet{
Monster&sourceMonster;
FrogTongue(Monster&sourceMonster,vf2d targetPos,float lifetime,int damage,bool upperLevel,float knockbackStrength=1.0f,bool friendly=false,Pixel col=WHITE);
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
void Draw(const Pixel blendCol)const override;
};
struct Wisp:public Bullet{
Wisp(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;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
enum class HorizontalFlip{
@ -130,8 +130,8 @@ struct DaggerStab:public Bullet{
DirectionOffsets daggerPositionOffsets;
DaggerStab(Monster&sourceMonster,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerStabDistance,const DirectionOffsets offsets,bool friendly=false,Pixel col=WHITE);
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct DaggerSlash:public Bullet{
@ -142,8 +142,8 @@ struct DaggerSlash:public Bullet{
float knockbackAmt;
DaggerSlash(Monster&sourceMonster,float radius,int damage,const float knockbackAmt,bool upperLevel,const Direction facingDir,const float daggerFrameDuration,const float daggerSlashDistance,bool friendly=false,Pixel col=WHITE);
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct Bomb:public Bullet{
@ -151,14 +151,15 @@ struct Bomb:public Bullet{
float zVel{0.f};
float detonationTime{0.f};
float bombFadeoutTime{0.f};
const float explosionRadius{};
const vf2d targetPos{};
Animate2D::AnimationState animation;
Animate2D::Animation<std::string>bomb_animation;
float bombKnockbackFactor{0.f};
Bomb(const vf2d pos,const float z,const float gravity,const float detonationTime,const float bombFadeoutTime,float bombKnockbackFactor,const vf2d targetPos,const float radius,const int damage,const bool upperLevel,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1});
Bomb(const vf2d pos,const float z,const float gravity,const float detonationTime,const float bombFadeoutTime,float bombKnockbackFactor,const vf2d targetPos,const float explosionRadius,const int damage,const bool upperLevel,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1});
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
void Draw(const Pixel blendCol)const override;
};
@ -172,14 +173,15 @@ struct LevitatingRock:public Bullet{
float flyingSpd;
float fadeInTime;
const float fadeInOriginalTime;
const float collisionRadius;
std::optional<vf2d>targetVel;
std::vector<LevitatingRock*>slaveRocks;
//The rock will rotate around the attachedTarget towards the attackingTarget. The facingRotOffset determines the angle relative to the direction pointed towards this rock will be positioned at. Once the wait time expires, the rock is fired at target speed in that given direction.
//The lock on time is how long the rocks will follow the player. The wait time is how long to wait until firing.
LevitatingRock(const Monster&attachedTarget,const vf2d&attackingTarget,const float fadeInTime,const float facingRotOffset,const float distance,const float lockOnTime,const float waitTime,const float targetSpd,const float radius,const int damage,const bool upperLevel,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1});
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
void Draw(const Pixel blendCol)const override;
void AssignMaster(LevitatingRock*masterRock);
const bool IsMaster()const;
@ -195,8 +197,8 @@ struct Tornado:public Bullet{
float rot{0.f};
Tornado(vf2d centerPoint,float distance,float initialRot,float rotSpd,int damage,const float knockupAmt,const float knockbackAmt,const float lifetime,bool upperLevel,bool friendly=false,Pixel col=WHITE,const vf2d scale={1,1});
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};
struct Debris:public Bullet{
@ -205,7 +207,18 @@ struct Debris:public Bullet{
float knockbackAmt{};
Debris(const vf2d pos,const vf2d vel,const int damage,const float radius,const float knockbackAmt,const float rotSpd,const float lifetime,bool upperLevel,bool friendly=false,Pixel col=WHITE,const vf2d scale={1,1});
void Update(float fElapsedTime)override;
bool PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
bool MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
void Draw(const Pixel blendCol)const override;
};
struct LargeTornado:public Bullet{
float suctionAmt{}; //Suction amount in pixels/sec
float knockupDuration{};
float knockbackAmt{};
//Suction amount in pixels/sec
LargeTornado(vf2d pos,const float suctionAmt,const float knockupAmt,const float knockbackAmt,const int damage,const float radius,const float lifetime,bool upperLevel,bool friendly=false,Pixel col=WHITE,const vf2d scale={1,1});
void Update(float fElapsedTime)override;
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
BulletDestroyState MonsterHit(Monster&monster)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!!
};

@ -59,12 +59,12 @@ void ChargedArrow::Update(float fElapsedTime){
}
}
bool ChargedArrow::PlayerHit(Player*player)
BulletDestroyState ChargedArrow::PlayerHit(Player*player)
{
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool ChargedArrow::MonsterHit(Monster& monster)
BulletDestroyState ChargedArrow::MonsterHit(Monster& monster)
{
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -86,15 +86,13 @@ void DaggerSlash::Update(float fElapsedTime){
}
#pragma endregion
}
bool DaggerSlash::PlayerHit(Player*player){
deactivated=true;
BulletDestroyState DaggerSlash::PlayerHit(Player*player){
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,player->GetSizeMult()*0.25f,0.25,vf2d{}));
player->Knockback(util::pointTo(sourceMonster.GetPos(),player->GetPos())*knockbackAmt);
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool DaggerSlash::MonsterHit(Monster&monster){
deactivated=true;
BulletDestroyState DaggerSlash::MonsterHit(Monster&monster){
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult()*0.25f,0.25,vf2d{}));
monster.Knockback(util::pointTo(sourceMonster.GetPos(),monster.GetPos())*knockbackAmt);
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -92,15 +92,13 @@ void DaggerStab::Update(float fElapsedTime){
}
#pragma endregion
}
bool DaggerStab::PlayerHit(Player*player){
deactivated=true;
BulletDestroyState DaggerStab::PlayerHit(Player*player){
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,player->GetSizeMult()*0.25f,0.25,vf2d{}));
player->Knockback(util::pointTo(sourceMonster.GetPos(),player->GetPos())*knockbackAmt);
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool DaggerStab::MonsterHit(Monster&monster){
deactivated=true;
BulletDestroyState DaggerStab::MonsterHit(Monster&monster){
game->AddEffect(std::make_unique<Effect>(pos,0,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult()*0.25f,0.25,vf2d{}));
monster.Knockback(util::pointTo(sourceMonster.GetPos(),monster.GetPos())*knockbackAmt);
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -48,17 +48,15 @@ Debris::Debris(const vf2d pos,const vf2d vel,const int damage,const float radius
void Debris::Update(float fElapsedTime){
image_angle+=rotatingSpd*fElapsedTime;
}
bool Debris::PlayerHit(Player*player){
BulletDestroyState Debris::PlayerHit(Player*player){
player->Knockback(vel.norm()*knockbackAmt);
deactivated=true;
fadeOutTime=0.5f;
return true;
return BulletDestroyState::KEEP_ALIVE;
}
bool Debris::MonsterHit(Monster&monster){
BulletDestroyState Debris::MonsterHit(Monster&monster){
monster.Knockback(vel.norm()*knockbackAmt);
deactivated=true;
fadeOutTime=0.5f;
return true;
return BulletDestroyState::KEEP_ALIVE;
}
void Debris::Draw(const Pixel blendCol)const{
game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()}+drawOffsetY,GetFrame().GetSourceImage()->Decal(),rotates?atan2(vel.y,vel.x)-PI/2+image_angle:image_angle,{12,12},vf2d{randomFrame*24.f,0.f},{24,24},scale,blendCol);

@ -53,24 +53,21 @@ void EnergyBolt::Update(float fElapsedTime){
lastParticleSpawn="Wizard.Auto Attack.ParticleFrequency"_F;
game->AddEffect(std::make_unique<Effect>(pos,"Wizard.Auto Attack.ParticleLifetimeRange"_FRange,"energy_particle.png",upperLevel,"Wizard.Auto Attack.ParticleSizeRange"_FRange,"Wizard.Auto Attack.ParticleFadeoutTime"_F,vf2d{"Wizard.Auto Attack.ParticleSpeedRange"_FRange,"Wizard.Auto Attack.ParticleSpeedRange"_FRange}));
}
if(distanceTraveled>"Wizard.Auto Attack.Max Range"_F&&!deactivated){
deactivated=true;
if(distanceTraveled>"Wizard.Auto Attack.Max Range"_F&&IsActivated()){
fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
}
}
bool EnergyBolt::PlayerHit(Player*player)
BulletDestroyState EnergyBolt::PlayerHit(Player*player)
{
deactivated=true;
fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
game->AddEffect(std::make_unique<Effect>(player->GetPos(),0,"splash_effect.png",upperLevel,player->GetSizeMult(),"Wizard.Auto Attack.SplashEffectFadeoutTime"_F));
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool EnergyBolt::MonsterHit(Monster& monster)
BulletDestroyState EnergyBolt::MonsterHit(Monster& monster)
{
deactivated=true;
fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Auto Attack.SplashEffectFadeoutTime"_F));
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -55,8 +55,7 @@ void FireBolt::Update(float fElapsedTime){
lastParticleSpawn="Wizard.Ability 1.ParticleFrequency"_F;
game->AddEffect(std::make_unique<Effect>(pos,"Wizard.Ability 1.ParticleLifetimeRange"_FRange,"energy_particle.png",upperLevel,"Wizard.Ability 1.ParticleSizeRange"_FRange,"Wizard.Ability 1.ParticleFadeoutTime"_F,vf2d{"Wizard.Ability 1.ParticleXSpeedRange"_FRange,"Wizard.Ability 1.ParticleYSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.ParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.ParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.ParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.ParticleAlphaRange"_FRange)}));
}
if(distanceTraveled>"Wizard.Ability 1.Max Range"_F&&!deactivated){
deactivated=true;
if(distanceTraveled>"Wizard.Ability 1.Max Range"_F&&IsActivated()){
fadeOutTime="Wizard.Ability 1.BulletHitFadeoutTime"_F;
for(int i=0;i<"Wizard.Ability 1.BulletHitExplosionParticleCount"_I;i++){
game->AddEffect(std::make_unique<Effect>(pos,"Wizard.Ability 1.BulletHitExplosionParticleLifetimeRange"_FRange,"circle.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionParticleSizeRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleFadeoutTimeRange"_FRange,vf2d{"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.BulletHitExplosionParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleAlphaRange"_FRange)}));
@ -75,9 +74,8 @@ void FireBolt::Update(float fElapsedTime){
}
}
bool FireBolt::PlayerHit(Player*player)
BulletDestroyState FireBolt::PlayerHit(Player*player)
{
deactivated=true;
fadeOutTime="Wizard.Ability 1.BulletHitFadeoutTime"_F;
for(int i=0;i<"Wizard.Ability 1.BulletHitExplosionParticleCount"_I;i++){
game->AddEffect(std::make_unique<Effect>(player->GetPos(),"Wizard.Ability 1.BulletHitExplosionParticleLifetimeRange"_FRange,"circle.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionParticleSizeRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleFadeoutTimeRange"_FRange,vf2d{"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.BulletHitExplosionParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleAlphaRange"_FRange)}));
@ -86,12 +84,11 @@ bool FireBolt::PlayerHit(Player*player)
game->AddEffect(std::make_unique<Effect>(player->GetPos(),0,"splash_effect.png",upperLevel,5,0.25,vf2d{},Pixel{240,120,60}));
SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos);
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool FireBolt::MonsterHit(Monster& monster)
BulletDestroyState FireBolt::MonsterHit(Monster& monster)
{
deactivated=true;
fadeOutTime="Wizard.Ability 1.BulletHitFadeoutTime"_F;
for(int i=0;i<"Wizard.Ability 1.BulletHitExplosionParticleCount"_I;i++){
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),"Wizard.Ability 1.BulletHitExplosionParticleLifetimeRange"_FRange,"circle.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionParticleSizeRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleFadeoutTimeRange"_FRange,vf2d{"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.BulletHitExplosionParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleAlphaRange"_FRange)}));
@ -101,5 +98,5 @@ bool FireBolt::MonsterHit(Monster& monster)
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionRange"_F/100*2,"Wizard.Ability 1.BulletHitExplosionFadeoutTime"_F,vf2d{},"Wizard.Ability 1.BulletHitExplosionColor"_Pixel));
SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos);
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -74,22 +74,21 @@ void FrogTongue::Update(float fElapsedTime){
}
}
}
bool FrogTongue::PlayerHit(Player*player){
if(!deactivated){
BulletDestroyState FrogTongue::PlayerHit(Player*player){
if(IsActivated()){
player->Hurt(damage,OnUpperLevel(),0);
geom2d::line<float>lineToTarget(pos,targetPos);
vf2d drawVec=lineToTarget.vector().norm()*3;
player->Knockback(geom2d::line<float>(pos+drawVec,targetPos).vector()*knockbackStrength);
deactivated=true;
}
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool FrogTongue::MonsterHit(Monster&monster){
BulletDestroyState FrogTongue::MonsterHit(Monster&monster){
monster.Hurt(damage,OnUpperLevel(),0);
geom2d::line<float>lineToTarget(pos,targetPos);
vf2d drawVec=lineToTarget.vector().norm()*3;
monster.Knockback(geom2d::line<float>(pos+drawVec,targetPos).vector()*knockbackStrength);
return false;
return BulletDestroyState::KEEP_ALIVE;
}
void FrogTongue::Draw(const Pixel blendCol)const{
geom2d::line<float>lineToTarget(pos,targetPos);

@ -0,0 +1,76 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "BulletTypes.h"
#include "Player.h"
#include "util.h"
#include "DEFINES.h"
#include "AdventuresInLestoria.h"
INCLUDE_game
LargeTornado::LargeTornado(vf2d pos,const float suctionAmt,const float knockupAmt,const float knockbackAmt,const int damage,const float radius,const float lifetime,bool upperLevel,bool friendly,Pixel col,const vf2d scale)
:suctionAmt(suctionAmt),knockupDuration(knockupAmt),knockbackAmt(knockbackAmt),Bullet(pos,vf2d{},radius,damage,"large_tornado.png",upperLevel,true,lifetime+0.5f,false,friendly,col,scale){
SetBulletType(BulletType::LARGE_TORNADO);
}
void LargeTornado::Update(float fElapsedTime){
if(game->GetPlayer()->IsAlive()&&IsActivated())game->SetWindSpeed(util::pointTo(game->GetPlayer()->GetPos(),pos)*suctionAmt); //The tornado shouldn't begin pulling in the player until it is actually active (due to fade-in)
hitList.clear(); //This is a slight hack to make it so these tornados will always hit things multiple times.
}
BulletDestroyState LargeTornado::PlayerHit(Player*player){
player->Knockback(util::pointTo(pos,player->GetPos())*knockbackAmt);
player->Knockup(knockupDuration);
player->ApplyIframes(knockupDuration*2);
return BulletDestroyState::KEEP_ALIVE;
}
BulletDestroyState LargeTornado::MonsterHit(Monster&monster){
if(monster.IgnoresTerrainCollision())return BulletDestroyState::KEEP_ALIVE; //All airborne enemy types won't care about this.
monster.Knockback(util::pointTo(pos,monster.GetPos())*knockbackAmt);
monster.Knockup(knockupDuration);
monster.ApplyIframes(knockupDuration*2);
return BulletDestroyState::KEEP_ALIVE;
}

@ -44,8 +44,8 @@ INCLUDE_game
INCLUDE_GFX
LevitatingRock::LevitatingRock(const Monster&attachedTarget,const vf2d&attackingTarget,const float fadeInTime,const float facingRotOffset,const float distance,const float lockOnTime,const float waitTime,const float targetSpd,const float radius,const int damage,const bool upperLevel,const bool friendly,const Pixel col,const vf2d scale)
:attachedTarget(attachedTarget),attackingTarget(attackingTarget),fadeInTime(fadeInTime),fadeInOriginalTime(fadeInTime),facingRotOffset(facingRotOffset),dist(distance),lockOnTime(lockOnTime),initialWaitTime(waitTime),flyingSpd(targetSpd),
Bullet(attachedTarget.GetPos(),{},radius,damage,"rock.png",upperLevel,false,INFINITE,false,friendly,col){}
:attachedTarget(attachedTarget),attackingTarget(attackingTarget),fadeInTime(fadeInTime),fadeInOriginalTime(fadeInTime),facingRotOffset(facingRotOffset),dist(distance),lockOnTime(lockOnTime),initialWaitTime(waitTime),flyingSpd(targetSpd),collisionRadius(radius),
Bullet(attachedTarget.GetPos(),{},0.f,damage,"rock.png",upperLevel,false,INFINITE,false,friendly,col){}
void LevitatingRock::Update(float fElapsedTime){
if(attachedTarget.IsDead()){
if(fadeOutTime==0.f){
@ -78,29 +78,27 @@ void LevitatingRock::Update(float fElapsedTime){
const bool RocksHaveLaunched=initialWaitTime<=0.f;
if(!RocksHaveLaunched){
deactivated=true;
radius=0.f;//Prevent the rocks from hitting anything by making their radius be zero.
drawOffsetY=cos(PI*game->GetRunTime())*3.f;
}
else if(targetVel.has_value()){
vel=targetVel.value();
deactivated=false;
radius=collisionRadius;
}else ERR(std::format("WARNING! Levitating Rock does not have a target velocity! It did not get assigned somehow! Is Master: {}. THIS SHOULD NOT BE HAPPENING!",IsMaster()))
}
bool LevitatingRock::PlayerHit(Player*player){
if(initialWaitTime>0.f)return false;
BulletDestroyState LevitatingRock::PlayerHit(Player*player){
if(initialWaitTime>0.f)return BulletDestroyState::KEEP_ALIVE;
deactivated=true;
fadeOutTime=0.5f;
player->Knockback(vel/3.f);
return true;
return BulletDestroyState::KEEP_ALIVE;
}
bool LevitatingRock::MonsterHit(Monster&monster){
if(initialWaitTime>0.f)return false;
BulletDestroyState LevitatingRock::MonsterHit(Monster&monster){
if(initialWaitTime>0.f)return BulletDestroyState::KEEP_ALIVE;
deactivated=true;
fadeOutTime=0.5f;
monster.Knockback(vel/3.f);
return true;
return BulletDestroyState::KEEP_ALIVE;
}
void LevitatingRock::Draw(const Pixel blendCol)const{

@ -72,25 +72,22 @@ void LightningBolt::Update(float fElapsedTime){
}break;
}
}
if(distanceTraveled>"Wizard.Ability 2.Max Range"_F&&!deactivated){
deactivated=true;
if(distanceTraveled>"Wizard.Ability 2.Max Range"_F&&IsActivated()){
fadeOutTime="Wizard.Ability 2.BulletFadeoutTime"_F;
}
}
bool LightningBolt::PlayerHit(Player*player)
BulletDestroyState LightningBolt::PlayerHit(Player*player)
{
deactivated=true;
fadeOutTime="Wizard.Ability 2.BulletFadeoutTime"_F;
game->AddEffect(std::make_unique<Effect>(player->GetPos(),"Wizard.Ability 2.SplashLifetime"_F,"lightning_splash_effect.png",upperLevel,player->GetSizeMult(),"Wizard.Ability 2.SplashFadeoutTime"_F,vf2d{},WHITE,"Wizard.Ability 2.SplashRotationRange"_FRange));
SoundEffect::PlaySFX("Wizard Lightning Bolt Hit",pos);
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool LightningBolt::MonsterHit(Monster& monster)
BulletDestroyState LightningBolt::MonsterHit(Monster& monster)
{
deactivated=true;
fadeOutTime="Wizard.Ability 2.BulletFadeoutTime"_F;
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),"Wizard.Ability 2.SplashLifetime"_F,"lightning_splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Ability 2.SplashFadeoutTime"_F,vf2d{},WHITE,"Wizard.Ability 2.SplashRotationRange"_FRange));
int targetsHit=0;
@ -109,5 +106,5 @@ bool LightningBolt::MonsterHit(Monster& monster)
}
SoundEffect::PlaySFX("Wizard Lightning Bolt Hit",pos);
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -769,7 +769,7 @@ bool Player::HasIframes(){
}
bool Player::Hurt(int damage,bool onUpperLevel,float z){
if(hp<=0||HasIframes()||OnUpperLevel()!=onUpperLevel||abs(GetZ()-z)>1) return false;
if(!IsAlive()||HasIframes()||OnUpperLevel()!=onUpperLevel||abs(GetZ()-z)>1) return false;
float mod_dmg=float(damage);
if(GetState()==State::BLOCK){
mod_dmg=0;
@ -805,7 +805,7 @@ bool Player::Hurt(int damage,bool onUpperLevel,float z){
hp=std::max(0,hp-int(mod_dmg));
if(hp==0&&GameState::STATE!=GameState::states[States::DEATH])GameState::ChangeState(States::DEATH);
if(!IsAlive()&&GameState::STATE!=GameState::states[States::DEATH])GameState::ChangeState(States::DEATH);
hurtRumbleTime="Player.Hurt Rumble Time"_F;
Input::StartVibration();
@ -1589,3 +1589,7 @@ void Player::AddVelocity(vf2d vel){
const float Player::GetHealthRatio()const{
return GetHealth()/float(GetMaxHealth());
}
const bool Player::IsAlive()const{
return GetHealth()>0;
}

@ -265,6 +265,7 @@ public:
const Renderable&GetMinimapImage()const;
void AddVelocity(vf2d vel);
const float GetHealthRatio()const;
const bool IsAlive()const;
private:
int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I;

@ -57,22 +57,22 @@ void Tornado::Update(float fElapsedTime){
hitList.clear(); //This is a slight hack to make it so these tornados will always hit things multiple times.
}
bool Tornado::PlayerHit(Player*player){
BulletDestroyState Tornado::PlayerHit(Player*player){
player->Knockback(util::pointTo(centerPoint,player->GetPos())*knockbackAmt);
player->Knockup(knockupDuration);
player->ApplyIframes(knockupDuration*2);
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool Tornado::MonsterHit(Monster&monster){
if(monster.IgnoresTerrainCollision())return false; //All airborne enemy types won't care about this.
BulletDestroyState Tornado::MonsterHit(Monster&monster){
if(monster.IgnoresTerrainCollision())return BulletDestroyState::KEEP_ALIVE; //All airborne enemy types won't care about this.
monster.Knockback(util::pointTo(centerPoint,monster.GetPos())*knockbackAmt);
monster.Knockup(knockupDuration);
monster.ApplyIframes(knockupDuration*2);
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -63,7 +63,6 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
m.SetStrategyDeathFunction([&](GameEvent&event,Monster&m,const std::string&strategy){
if(!m.B(A::BULLETS_REMOVED)){
for(const std::unique_ptr<Bullet>&b:BULLET_LIST){
b->deactivated=true;
b->fadeOutTime=ConfigFloat("Phase 4.End Wisp Fadeout Time");
}
m.B(A::BULLETS_REMOVED)=true;

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

@ -48,17 +48,15 @@ Wisp::Wisp(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friend
void Wisp::Update(float fElapsedTime){
}
bool Wisp::PlayerHit(Player*player){
deactivated=true;
BulletDestroyState Wisp::PlayerHit(Player*player){
SoundEffect::PlaySFX("Wisp Hit",player->GetPos());
game->AddEffect(std::make_unique<Effect>(player->GetPos(),0,"splash_effect.png",upperLevel,player->GetSizeMult(),0.25,vf2d{},"MonsterStrategy.Ursule.Phase 2.Wisp Color"_Pixel));
fadeOutTime="MonsterStrategy.Ursule.Phase 2.Wisp Fadeout Time"_F;
return false;
return BulletDestroyState::KEEP_ALIVE;
}
bool Wisp::MonsterHit(Monster&monster){
deactivated=true;
BulletDestroyState Wisp::MonsterHit(Monster&monster){
SoundEffect::PlaySFX("Wisp Hit",monster.GetPos());
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),0.25,vf2d{},"MonsterStrategy.Ursule.Phase 2.Wisp Color"_Pixel));
fadeOutTime="MonsterStrategy.Ursule.Phase 2.Wisp Fadeout Time"_F;
return false;
return BulletDestroyState::KEEP_ALIVE;
}

@ -99,7 +99,6 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
std::for_each(BULLET_LIST.begin(),BULLET_LIST.end(),[](const std::unique_ptr<Bullet>&bullet){
if(!bullet->friendly){ //Forces all bullets at the end of a fight for the boss to be completely nullified.
bullet->fadeOutTime=0.5f;
bullet->deactivated=true;
}
});
return true;
@ -332,14 +331,29 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
if(m.ReachedTargetPos()){
m.F(A::TARGET_FLYING_HEIGHT)=0.f;
if(m.GetZ()==0.f)m.phase=HALFHEALTH_PHASE;
const bool HasLandedOnGround=m.GetZ()==0.f;
if(HasLandedOnGround){
m.phase=HALFHEALTH_PHASE;
CreateBullet(LargeTornado)(ConfigVec("Mid Phase.Large Tornado Position"),ConfigPixels("Mid Phase.Large Tornado Suction"),ConfigFloat("Mid Phase.Large Tornado Knockup Duration"),ConfigFloat("Mid Phase.Large Tornado Knockback Amount"),ConfigInt("Mid Phase.Large Tornado Damage"),ConfigFloat("Mid Phase.Large Tornado Radius"),INFINITY,m.OnUpperLevel())EndBullet;
BULLET_LIST.back()->SetFadeinTime(1.0f);
}
}
}break;
case HALFHEALTH_PHASE:{
m.ApplyIframes(1.f);
m.UpdateFacingDirection(Direction::SOUTH);
m.PerformAnimation("ATTACK");
if(game->BossEncounterMobCount()==1)m.phase=IDLE;
if(game->BossEncounterMobCount()==1){
m.phase=IDLE;
std::for_each(BULLET_LIST.begin(),BULLET_LIST.end(),[](std::unique_ptr<Bullet>&bullet){
if(bullet->GetBulletType()==BulletType::LARGE_TORNADO){
bullet->fadeOutTime=1.f;
}
});
game->SetWindSpeed({});
}
}break;
}
}

@ -870,6 +870,15 @@ MonsterStrategy
Adds Spawn Y Range = 1344, 1344
Basic Hawk Spawn Count = 4
Major Hawk Spawn Count = 2
Large Tornado Radius = 12
Large Tornado Position = 2040, 1680
Large Tornado Damage = 48
Large Tornado Suction = 100units/s
Large Tornado Knockup Duration = 0.75s
Large Tornado Knockback Amount = 300
}
}
}

@ -103,6 +103,7 @@ Images
GFX_Wind1 = wind1.png
GFX_Wind2 = wind2.png
GFX_WindObjects = commercial_assets/wind_solid_objects.png
GFX_LargeTornado = large_tornado.png
# Ability Icons
GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Loading…
Cancel
Save