Fix bug with multi-hit bullets not allowing their collision checks to be disabled mid-sequence. Add capability for derived bullet types to re-activate themselves. 181/181 unit tests passing. Release Build 11106.

pull/65/head
sigonasr2 5 months ago
parent 220bf9d655
commit 976cc7a0b7
  1. 36
      Adventures in Lestoria Tests/EnchantTests.cpp
  2. 5
      Adventures in Lestoria/BulletTypes.h
  3. 10
      Adventures in Lestoria/IBullet.cpp
  4. 1
      Adventures in Lestoria/IBullet.h
  5. 3
      Adventures in Lestoria/Monster.cpp
  6. 2
      Adventures in Lestoria/Monster.h
  7. 30
      Adventures in Lestoria/PurpleEnergyBall.cpp
  8. 2
      Adventures in Lestoria/Version.h
  9. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1161,5 +1161,41 @@ namespace EnchantTests
testGame->OnUserUpdate(0.5f);
Assert::AreEqual(910,newMonster.GetHealth(),L"Monster now takes 90 health with the Sonic Upgrade (10% of player's Max HP).");
}
TEST_METHOD(BouncingOrbNoEnchantCheck){
testKey->bHeld=true; //Force the key to be held down for testing purposes.
game->ChangePlayerClass(WITCH);
player=game->GetPlayer();
Monster&newMonster{game->SpawnMonster({},MONSTER_DATA["TestName"])};
Monster&newMonster2{game->SpawnMonster({},MONSTER_DATA["TestName"])};
Assert::AreEqual(1000,newMonster.GetHealth(),L"Monster 1 is at 1000 HP.");
Assert::AreEqual(1000,newMonster2.GetHealth(),L"Monster 2 is at 1000 HP.");
player->AutoAttack();
for(int i:std::ranges::iota_view(0,50)){
game->SetElapsedTime(0.1f);
game->OnUserUpdate(0.1f);
}
Assert::AreEqual(985,newMonster.GetHealth(),L"Monster 1 should have been hit.");
Assert::AreEqual(1000,newMonster2.GetHealth(),L"Monster 2 should not have been hit. Target limit is 1.");
}
TEST_METHOD(BouncingOrbEnchantCheck){
testKey->bHeld=true; //Force the key to be held down for testing purposes.
game->ChangePlayerClass(WITCH);
player=game->GetPlayer();
player->ForceSetPos({50.f,0.f});
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
Monster&newMonster2{game->SpawnMonster({50.f,0.f},MONSTER_DATA["TestName"])};
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,EquipSlot::RING1);
nullRing.lock()->EnchantItem("Bouncing Orb");
Assert::AreEqual(1000,newMonster.GetHealth(),L"Monster 1 is at 1000 HP.");
Assert::AreEqual(1000,newMonster2.GetHealth(),L"Monster 2 is at 1000 HP.");
player->AutoAttack();
for(int i:std::ranges::iota_view(0,50)){
game->SetElapsedTime(0.1f);
game->OnUserUpdate(0.1f);
}
Assert::AreEqual(970,newMonster.GetHealth(),L"Monster 1 should have been hit. Since we can have 4 bounces, this got hit twice.");
Assert::AreEqual(970,newMonster2.GetHealth(),L"Monster 2 should have been hit. Since we can have 4 bounces, this got hit twice.");
}
};
}

@ -324,7 +324,7 @@ private:
};
struct PurpleEnergyBall:public Bullet{
PurpleEnergyBall(vf2d pos,float radius,float homingRadius,int damage,bool upperLevel,vf2d speed,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1});
PurpleEnergyBall(vf2d pos,float radius,float homingRadius,int damage,bool upperLevel,vf2d speed,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1});
void Update(float fElapsedTime)override;
void Draw(const Pixel blendCol)const override;
BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!!
@ -332,6 +332,9 @@ struct PurpleEnergyBall:public Bullet{
private:
const vf2d initialScale;
const float homingRadius;
int bounceCount{1};
float lastHitTimer{0.f};
std::optional<std::weak_ptr<Monster>>lastHitTarget;
std::optional<std::weak_ptr<Monster>>homingTarget;
};

@ -88,8 +88,8 @@ void IBullet::_Update(const float fElapsedTime){
UpdateFadeTime(fElapsedTime);
Update(fElapsedTime);
animation.UpdateState(internal_animState,fElapsedTime);
const bool CollisionCheckRequired=IsActivated()&&fadeInTimer==fadeInTime&&radius!=0.f;
if(CollisionCheckRequired){
const auto CollisionCheckRequired=[this](){return 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;
@ -113,6 +113,7 @@ void IBullet::_Update(const float fElapsedTime){
return false;
}else _MonsterHit(*m,markStacksBeforeHit);
hitList.insert(&*m);
if(!CollisionCheckRequired())return false;
}
}
}
@ -131,6 +132,7 @@ void IBullet::_Update(const float fElapsedTime){
return false;
}else _PlayerHit(&*game->GetPlayer());
hitList.insert(game->GetPlayer());
if(!CollisionCheckRequired())return false;
}
}
}
@ -274,6 +276,10 @@ void IBullet::Deactivate(){
deactivated=true;
}
void IBullet::Activate(){
deactivated=false;
}
const double IBullet::GetTimeAlive()const{
return aliveTime;
}

@ -76,6 +76,7 @@ protected:
float fadeInTime=0; //Setting the fade in time causes the bullet to be disabled and the bullet's alpha will fade in from zero to the actual alpha of the bullet. When the fade in timer reaches the fade in time automatically, the bullet will be enabled.
virtual void Update(float fElapsedTime);
void Deactivate();
void Activate();
private:
float fadeOutTimer=0;
float fadeInTimer=0;

@ -1430,10 +1430,11 @@ void Monster::ApplySpecialMark(float time,uint8_t stackCount){
specialMarkApplicationTimer=0.5f;
}
std::optional<std::weak_ptr<Monster>>Monster::GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z){
std::optional<std::weak_ptr<Monster>>Monster::GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z,const std::optional<std::weak_ptr<Monster>>excludedTarget){
std::optional<std::weak_ptr<Monster>>closestMonster;
std::optional<std::weak_ptr<Monster>>closestGenericMonster;
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
if(excludedTarget&&!excludedTarget.value().expired()&&&*excludedTarget.value().lock()==&*m)continue;
geom2d::line<float>aimingLine=geom2d::line<float>(point,m->GetPos());
float distToMonster=aimingLine.length();
float distToClosestPoint,distToClosestGenericPoint;

@ -209,7 +209,7 @@ public:
void ApplyMark(float time,uint8_t stackCount); //Adds stackCount mark stacks to the target, refreshing the buff to time time.
void ApplySpecialMark(float time,uint8_t stackCount); //Adds stackCount special mark stacks to the target, refreshing the buff to time time.
//Gets the nearest target that can be immediately targeted
static std::optional<std::weak_ptr<Monster>>GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z);
static std::optional<std::weak_ptr<Monster>>GetNearestMonster(const vf2d point,const float maxDistance,const bool onUpperLevel,const float z,const std::optional<std::weak_ptr<Monster>>excludedTarget={});
const bool InUndamageableState(const bool onUpperLevel,const float z)const;
const bool CanMove()const;
const std::weak_ptr<Monster>GetWeakPointer()const;

@ -42,15 +42,25 @@ All rights reserved.
INCLUDE_game
PurpleEnergyBall::PurpleEnergyBall(vf2d pos,float radius,float homingRadius,int damage,bool upperLevel,vf2d speed,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale)
:initialScale(scale),homingRadius(homingRadius),Bullet(pos,speed,radius,damage,"mark_trail.png",upperLevel,hitsMultiple,INFINITE,false,friendly,col,scale/2.f,0.f){}
PurpleEnergyBall::PurpleEnergyBall(vf2d pos,float radius,float homingRadius,int damage,bool upperLevel,vf2d speed,float lifetime,bool friendly,Pixel col,vf2d scale)
:initialScale(scale),homingRadius(homingRadius),bounceCount(game->GetPlayer()->HasEnchant("Bouncing Orb")?"Bouncing Orb"_ENC["BOUNCE COUNT"]:1),Bullet(pos,speed,radius,damage,"mark_trail.png",upperLevel,true,INFINITE,false,friendly,col,scale/2.f,0.f){}
void PurpleEnergyBall::Update(float fElapsedTime){
if(IsDeactivated()){
if(lastHitTimer>0.f){
lastHitTimer-=fElapsedTime;
if(lastHitTimer<=0.f){
lastHitTimer=0.f;
Activate();
}
}else return;
}
if(homingTarget.has_value()){
if(!homingTarget.value().expired()){
const bool TargetInRange{util::distance(pos,homingTarget.value().lock()->GetPos())<="Witch.Auto Attack.Homing Range"_F};
const bool TargetIsAlive{homingTarget.value().lock()->IsAlive()};
if(TargetIsAlive&&TargetInRange){
if(TargetIsAlive&&TargetInRange&&(!lastHitTarget||lastHitTarget&&!lastHitTarget.value().expired()&&&*homingTarget.value().lock()!=&*lastHitTarget.value().lock())){
const float targetAngle{util::angleTo(pos,homingTarget.value().lock()->GetPos())};
float currentAngle{vel.polar().y};
util::turn_towards_direction(currentAngle,targetAngle,util::degToRad("Witch.Auto Attack.Homing Turning Radius"_F)*fElapsedTime);
@ -61,7 +71,7 @@ void PurpleEnergyBall::Update(float fElapsedTime){
}else{
homingTarget.reset();
}
}else homingTarget=Monster::GetNearestMonster(pos,"Witch.Auto Attack.Homing Range"_F,OnUpperLevel(),GetZ());
}else homingTarget=Monster::GetNearestMonster(pos,"Witch.Auto Attack.Homing Range"_F,OnUpperLevel(),GetZ(),lastHitTarget);
if(distanceTraveled>"Witch.Auto Attack.Range"_F&&IsActivated()){
fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
}
@ -79,7 +89,10 @@ void PurpleEnergyBall::Draw(const Pixel blendCol)const{
BulletDestroyState PurpleEnergyBall::PlayerHit(Player*player)
{
fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
bounceCount--;
if(bounceCount<=0)fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
else lastHitTimer=0.1f;
Deactivate();
std::unique_ptr<Effect>hitEffect{std::make_unique<Effect>(player->GetPos(),0,"purpleenergyball_hit.png",upperLevel,player->GetSizeMult(),"Witch.Auto Attack.SplashEffectFadeoutTime"_F,vf2d{},WHITE,util::random(2*PI),5*PI)};
hitEffect->scaleSpd={-0.3f,-0.3f};
game->AddEffect(std::move(hitEffect));
@ -88,7 +101,12 @@ BulletDestroyState PurpleEnergyBall::PlayerHit(Player*player)
BulletDestroyState PurpleEnergyBall::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)
{
fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
bounceCount--;
hitList.clear();
lastHitTarget=monster.GetWeakPointer();
if(bounceCount<=0)fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
else lastHitTimer=0.1f;
Deactivate();
std::unique_ptr<Effect>hitEffect{std::make_unique<Effect>(monster.GetPos(),0,"purpleenergyball_hit.png",upperLevel,monster.GetSizeMult(),"Witch.Auto Attack.SplashEffectFadeoutTime"_F,vf2d{},WHITE,util::random(2*PI),5*PI)};
hitEffect->scaleSpd={-0.3f,-0.3f};
game->AddEffect(std::move(hitEffect));

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

Loading…
Cancel
Save