Fix bug with multi-hit bullets not adding hit targets to the hit list before monster hit / player hit bullet callbacks. Implemented Piercing Bolt enchant. 199/199 unit tests passing. Release Build 11200.
This commit is contained in:
parent
6d4de7940b
commit
0288ccbd0c
@ -97,7 +97,7 @@ namespace EnchantTests
|
|||||||
|
|
||||||
#pragma region Setup a fake test map and test monster
|
#pragma region Setup a fake test map and test monster
|
||||||
game->MAP_DATA["CAMPAIGN_1_1"];
|
game->MAP_DATA["CAMPAIGN_1_1"];
|
||||||
game->MAP_DATA["CAMPAIGN_1_1"]._SetMapData(MapTag{50,50,24,24});
|
game->MAP_DATA["CAMPAIGN_1_1"]._SetMapData(MapTag{24*24,24*24,24,24});
|
||||||
ItemDrop::ClearDrops();
|
ItemDrop::ClearDrops();
|
||||||
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
MonsterData testMonsterData{"TestName","Test Monster",1000,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f};
|
||||||
MONSTER_DATA["TestName"]=testMonsterData;
|
MONSTER_DATA["TestName"]=testMonsterData;
|
||||||
@ -577,6 +577,7 @@ namespace EnchantTests
|
|||||||
}
|
}
|
||||||
TEST_METHOD(TripleTossCheck){
|
TEST_METHOD(TripleTossCheck){
|
||||||
Game::ChangeClass(player,THIEF);
|
Game::ChangeClass(player,THIEF);
|
||||||
|
player->SetTestScreenAimingLocation(player->GetPos()+vf2d{30.f,0.f});
|
||||||
player->CheckAndPerformAbility(player->GetAbility1(),testKeyboardInput);
|
player->CheckAndPerformAbility(player->GetAbility1(),testKeyboardInput);
|
||||||
Game::Update(0.5f);
|
Game::Update(0.5f);
|
||||||
Assert::AreEqual(size_t(1),BULLET_LIST.size(),L"Only 1 dagger has spawned without the Triple Toss enchant.");
|
Assert::AreEqual(size_t(1),BULLET_LIST.size(),L"Only 1 dagger has spawned without the Triple Toss enchant.");
|
||||||
@ -839,6 +840,7 @@ namespace EnchantTests
|
|||||||
Assert::AreEqual(size_t(1),BULLET_LIST.size(),L"An Explosive Trap still exists (in the process of detonating a few times)");
|
Assert::AreEqual(size_t(1),BULLET_LIST.size(),L"An Explosive Trap still exists (in the process of detonating a few times)");
|
||||||
Assert::AreEqual(721,newMonster.GetHealth(),L"Monster takes 104 + 104 damage from Concussive Trap bonus (Collision+Collateral Explosion).");
|
Assert::AreEqual(721,newMonster.GetHealth(),L"Monster takes 104 + 104 damage from Concussive Trap bonus (Collision+Collateral Explosion).");
|
||||||
Game::Update(5.5f);
|
Game::Update(5.5f);
|
||||||
|
Game::Update(5.5f);
|
||||||
Assert::AreEqual(size_t(0),BULLET_LIST.size(),L"Explosive Trap detonates later.");
|
Assert::AreEqual(size_t(0),BULLET_LIST.size(),L"Explosive Trap detonates later.");
|
||||||
}
|
}
|
||||||
TEST_METHOD(SwordEnchantmentNoEnchantCheck){
|
TEST_METHOD(SwordEnchantmentNoEnchantCheck){
|
||||||
@ -966,12 +968,13 @@ namespace EnchantTests
|
|||||||
}
|
}
|
||||||
TEST_METHOD(BouncingOrbEnchantCheck){
|
TEST_METHOD(BouncingOrbEnchantCheck){
|
||||||
Game::ChangeClass(player,WITCH);
|
Game::ChangeClass(player,WITCH);
|
||||||
player->ForceSetPos({50.f,0.f});
|
player->ForceSetPos({20.f,50.f});
|
||||||
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
Monster&newMonster{game->SpawnMonster({30.f,50.f},MONSTER_DATA["TestName"])};
|
||||||
Monster&newMonster2{game->SpawnMonster({50.f,0.f},MONSTER_DATA["TestName"])};
|
Monster&newMonster2{game->SpawnMonster({50.f,50.f},MONSTER_DATA["TestName"])};
|
||||||
Game::GiveAndEquipEnchantedRing("Bouncing Orb");
|
Game::GiveAndEquipEnchantedRing("Bouncing Orb");
|
||||||
Assert::AreEqual(1000,newMonster.GetHealth(),L"Monster 1 is at 1000 HP.");
|
Assert::AreEqual(1000,newMonster.GetHealth(),L"Monster 1 is at 1000 HP.");
|
||||||
Assert::AreEqual(1000,newMonster2.GetHealth(),L"Monster 2 is at 1000 HP.");
|
Assert::AreEqual(1000,newMonster2.GetHealth(),L"Monster 2 is at 1000 HP.");
|
||||||
|
player->SetTestScreenAimingLocation(player->GetPos()+vf2d{30.f,0.f});
|
||||||
player->AutoAttack();
|
player->AutoAttack();
|
||||||
for(int i:std::ranges::iota_view(0,50)){
|
for(int i:std::ranges::iota_view(0,50)){
|
||||||
game->SetElapsedTime(0.1f);
|
game->SetElapsedTime(0.1f);
|
||||||
@ -1150,5 +1153,36 @@ namespace EnchantTests
|
|||||||
Assert::AreEqual(newMonster3.GetMaxHealth()-500,newMonster3.GetHealth(),L"Monster 3 takes collateral damage from Curse of Doom enchant based on the amount of damage dealt during Curse of Death debuff.");
|
Assert::AreEqual(newMonster3.GetMaxHealth()-500,newMonster3.GetHealth(),L"Monster 3 takes collateral damage from Curse of Doom enchant based on the amount of damage dealt during Curse of Death debuff.");
|
||||||
Assert::AreEqual(newMonster4.GetMaxHealth()-500,newMonster4.GetHealth(),L"Monster 4 takes collateral damage from Curse of Doom enchant based on the amount of damage dealt during Curse of Death debuff.");
|
Assert::AreEqual(newMonster4.GetMaxHealth()-500,newMonster4.GetHealth(),L"Monster 4 takes collateral damage from Curse of Doom enchant based on the amount of damage dealt during Curse of Death debuff.");
|
||||||
}
|
}
|
||||||
|
TEST_METHOD(PiercingBoltNoEnchantCheck){
|
||||||
|
Game::ChangeClass(player,WIZARD);
|
||||||
|
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
Monster&newMonster2{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
Monster&newMonster3{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
Monster&newMonster4{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
player->SetTestScreenAimingLocation(player->GetPos()+vf2d{30.f,0.f});
|
||||||
|
Game::Update(0.f);
|
||||||
|
player->AutoAttack();
|
||||||
|
Game::Update(0.5f);
|
||||||
|
Assert::AreEqual(985,newMonster.GetHealth(),L"Monster 1 takes normal damage from Wizard auto attack.");
|
||||||
|
Assert::AreEqual(1000,newMonster2.GetHealth(),L"Monster 2 should still be full health.");
|
||||||
|
Assert::AreEqual(1000,newMonster3.GetHealth(),L"Monster 3 should still be full health.");
|
||||||
|
Assert::AreEqual(1000,newMonster4.GetHealth(),L"Monster 4 should still be full health.");
|
||||||
|
}
|
||||||
|
TEST_METHOD(PiercingBoltEnchantCheck){
|
||||||
|
Game::ChangeClass(player,WIZARD);
|
||||||
|
Game::GiveAndEquipEnchantedRing("Piercing Bolt");
|
||||||
|
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
Monster&newMonster2{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
Monster&newMonster3{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
Monster&newMonster4{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
|
||||||
|
player->SetTestScreenAimingLocation(player->GetPos()+vf2d{30.f,0.f});
|
||||||
|
Game::Update(0.f);
|
||||||
|
player->AutoAttack();
|
||||||
|
Game::Update(0.5f);
|
||||||
|
Assert::AreEqual(985,newMonster.GetHealth(),L"Monster 1 takes normal damage from Wizard auto attack.");
|
||||||
|
Assert::AreEqual(992,newMonster2.GetHealth(),L"Monster 2 should take half the damage the first target does with the Percing Bolt Enchant.");
|
||||||
|
Assert::AreEqual(992,newMonster3.GetHealth(),L"Monster 3 should take half the damage the first target does with the Percing Bolt Enchant");
|
||||||
|
Assert::AreEqual(992,newMonster4.GetHealth(),L"Monster 4 should take half the damage the first target does with the Percing Bolt Enchant");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -50,8 +50,8 @@ namespace Game{
|
|||||||
game->SetElapsedTime(fElapsedTime);
|
game->SetElapsedTime(fElapsedTime);
|
||||||
game->OnUserUpdate(fElapsedTime);
|
game->OnUserUpdate(fElapsedTime);
|
||||||
}
|
}
|
||||||
inline void CastAbilityAtLocation(Ability&ability,const vf2d&screenLoc,const CastWaitProperty castWaitTime=CastWaitProperty::WAIT_FOR_CAST_TIME){ //NOTE: screenLoc is the actual screen coordinates, NOT the world coordinates! You are defining the mouse position essentially.
|
inline void CastAbilityAtLocation(Ability&ability,const vf2d&worldLoc,const CastWaitProperty castWaitTime=CastWaitProperty::WAIT_FOR_CAST_TIME){ //NOTE: screenLoc is the actual screen coordinates, NOT the world coordinates! You are defining the mouse position essentially.
|
||||||
game->GetPlayer()->SetTestScreenAimingLocation(screenLoc);
|
game->GetPlayer()->SetTestScreenAimingLocation(worldLoc);
|
||||||
game->GetPlayer()->PrepareCast(ability);
|
game->GetPlayer()->PrepareCast(ability);
|
||||||
game->GetPlayer()->CastSpell(ability);
|
game->GetPlayer()->CastSpell(ability);
|
||||||
Game::Update(ability.precastInfo.castTime);
|
Game::Update(ability.precastInfo.castTime);
|
||||||
|
@ -45,7 +45,7 @@ INCLUDE_game
|
|||||||
|
|
||||||
EnergyBolt::EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
EnergyBolt::EnergyBolt(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col)
|
||||||
:Bullet(pos,vel,radius,damage,
|
:Bullet(pos,vel,radius,damage,
|
||||||
"energy_bolt.png",upperLevel,false,INFINITE,true,friendly,col){}
|
"energy_bolt.png",upperLevel,game->GetPlayer()->HasEnchant("Piercing Bolt")?true:false,INFINITE,true,friendly,col){}
|
||||||
|
|
||||||
void EnergyBolt::Update(float fElapsedTime){
|
void EnergyBolt::Update(float fElapsedTime){
|
||||||
lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime);
|
lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime);
|
||||||
@ -67,8 +67,9 @@ BulletDestroyState EnergyBolt::PlayerHit(Player*player)
|
|||||||
|
|
||||||
BulletDestroyState EnergyBolt::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)
|
BulletDestroyState EnergyBolt::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)
|
||||||
{
|
{
|
||||||
fadeOutTime="Wizard.Auto Attack.BulletHitFadeoutTime"_F;
|
if(!game->GetPlayer()->HasEnchant("Piercing Bolt"))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));
|
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),"Wizard.Auto Attack.SplashEffectFadeoutTime"_F));
|
||||||
|
if(hitList.size()==1)damage=ceil(float(damage)/2);
|
||||||
return BulletDestroyState::KEEP_ALIVE;
|
return BulletDestroyState::KEEP_ALIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +96,10 @@ void ExplosiveTrap::Detonate(){
|
|||||||
for(const auto&[targetPtr,wasHit]:list){
|
for(const auto&[targetPtr,wasHit]:list){
|
||||||
if(wasHit){
|
if(wasHit){
|
||||||
std::get<Monster*>(targetPtr)->ApplyMark("Trapper.Ability 3.Explosion Mark Stack Time"_F,"Trapper.Ability 3.Explosion Mark Stack Increase"_I);
|
std::get<Monster*>(targetPtr)->ApplyMark("Trapper.Ability 3.Explosion Mark Stack Time"_F,"Trapper.Ability 3.Explosion Mark Stack Increase"_I);
|
||||||
std::get<Monster*>(targetPtr)->ProximityKnockback(pos,"Trapper.Ability 3.Explosion Knockback Amount"_F);
|
if(explosionCount==1)std::get<Monster*>(targetPtr)->ProximityKnockback(pos,"Trapper.Ability 3.Explosion Knockback Amount"_F);
|
||||||
std::get<Monster*>(targetPtr)->Knockup("Trapper.Ability 3.Explosion Knockup Amount"_F);
|
else std::get<Monster*>(targetPtr)->ProximityKnockback(pos,20.f);
|
||||||
|
if(explosionCount==1)std::get<Monster*>(targetPtr)->Knockup("Trapper.Ability 3.Explosion Knockup Amount"_F);
|
||||||
|
else std::get<Monster*>(targetPtr)->Knockup(0.1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,9 +121,6 @@ BulletDestroyState ExplosiveTrap::MonsterHit(Monster&monster,const uint8_t markS
|
|||||||
monster.Hurt(0,monster.OnUpperLevel(),monster.GetZ(),HurtFlag::PLAYER_ABILITY);//Triggers mark multiple times after the first mark.
|
monster.Hurt(0,monster.OnUpperLevel(),monster.GetZ(),HurtFlag::PLAYER_ABILITY);//Triggers mark multiple times after the first mark.
|
||||||
}
|
}
|
||||||
|
|
||||||
monster.ProximityKnockback(pos,"Trapper.Ability 3.Explosion Knockback Amount"_F);
|
|
||||||
monster.Knockup("Trapper.Ability 3.Explosion Knockup Amount"_F);
|
|
||||||
|
|
||||||
Detonate();
|
Detonate();
|
||||||
|
|
||||||
return BulletDestroyState::KEEP_ALIVE;
|
return BulletDestroyState::KEEP_ALIVE;
|
||||||
|
@ -106,13 +106,13 @@ void IBullet::_Update(const float fElapsedTime){
|
|||||||
ModifyOutgoingDamageData(damageData);
|
ModifyOutgoingDamageData(damageData);
|
||||||
uint8_t markStacksBeforeHit{m->GetMarkStacks()};
|
uint8_t markStacksBeforeHit{m->GetMarkStacks()};
|
||||||
if(m->Hurt(damageData)){
|
if(m->Hurt(damageData)){
|
||||||
|
hitList.insert(&*m);
|
||||||
if(!hitsMultiple){
|
if(!hitsMultiple){
|
||||||
if(_MonsterHit(*m,markStacksBeforeHit)==BulletDestroyState::DESTROY){
|
if(_MonsterHit(*m,markStacksBeforeHit)==BulletDestroyState::DESTROY){
|
||||||
dead=true;
|
dead=true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}else _MonsterHit(*m,markStacksBeforeHit);
|
}else _MonsterHit(*m,markStacksBeforeHit);
|
||||||
hitList.insert(&*m);
|
|
||||||
if(!CollisionCheckRequired())return false;
|
if(!CollisionCheckRequired())return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,13 +125,13 @@ void IBullet::_Update(const float fElapsedTime){
|
|||||||
//NOTE: OnHurt() will potentially change the Damage Data, passing it along to bullet children that might need to modify the flags!
|
//NOTE: OnHurt() will potentially change the Damage Data, passing it along to bullet children that might need to modify the flags!
|
||||||
ModifyOutgoingDamageData(damageData);
|
ModifyOutgoingDamageData(damageData);
|
||||||
if(game->GetPlayer()->Hurt(damageData)){
|
if(game->GetPlayer()->Hurt(damageData)){
|
||||||
|
hitList.insert(game->GetPlayer());
|
||||||
if(!hitsMultiple){
|
if(!hitsMultiple){
|
||||||
if(_PlayerHit(&*game->GetPlayer())==BulletDestroyState::DESTROY){
|
if(_PlayerHit(&*game->GetPlayer())==BulletDestroyState::DESTROY){
|
||||||
dead=true;
|
dead=true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}else _PlayerHit(&*game->GetPlayer());
|
}else _PlayerHit(&*game->GetPlayer());
|
||||||
hitList.insert(game->GetPlayer());
|
|
||||||
if(!CollisionCheckRequired())return false;
|
if(!CollisionCheckRequired())return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1760,7 +1760,9 @@ void Player::Knockup(float duration){
|
|||||||
}
|
}
|
||||||
|
|
||||||
const vf2d Player::GetWorldAimingLocation(bool useWalkDir,bool invert){
|
const vf2d Player::GetWorldAimingLocation(bool useWalkDir,bool invert){
|
||||||
const vf2d screenAimingLoc{game->view.ScreenToWorld(GetAimingLocation(useWalkDir,invert))};
|
vf2d screenAimingLoc{game->view.ScreenToWorld(GetAimingLocation(useWalkDir,invert))};
|
||||||
|
|
||||||
|
if(game->TestingModeEnabled())screenAimingLoc=testAimingLoc.value_or(GetPos()+vf2d{});
|
||||||
|
|
||||||
if(screenAimingLoc==GetPos())return GetPos()+vf2d{0,-1.f};
|
if(screenAimingLoc==GetPos())return GetPos()+vf2d{0,-1.f};
|
||||||
else return screenAimingLoc;
|
else return screenAimingLoc;
|
||||||
|
@ -104,6 +104,7 @@ BulletDestroyState PurpleEnergyBall::MonsterHit(Monster&monster,const uint8_t ma
|
|||||||
bounceCount--;
|
bounceCount--;
|
||||||
hitList.clear();
|
hitList.clear();
|
||||||
lastHitTarget=monster.GetWeakPointer();
|
lastHitTarget=monster.GetWeakPointer();
|
||||||
|
hitList.emplace(&monster);
|
||||||
if(bounceCount<=0)fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
|
if(bounceCount<=0)fadeOutTime="Witch.Auto Attack.BulletHitFadeoutTime"_F;
|
||||||
else lastHitTimer=0.1f;
|
else lastHitTimer=0.1f;
|
||||||
Deactivate();
|
Deactivate();
|
||||||
|
@ -39,7 +39,7 @@ All rights reserved.
|
|||||||
#define VERSION_MAJOR 1
|
#define VERSION_MAJOR 1
|
||||||
#define VERSION_MINOR 2
|
#define VERSION_MINOR 2
|
||||||
#define VERSION_PATCH 5
|
#define VERSION_PATCH 5
|
||||||
#define VERSION_BUILD 11188
|
#define VERSION_BUILD 11200
|
||||||
|
|
||||||
#define stringify(a) stringify_(a)
|
#define stringify(a) stringify_(a)
|
||||||
#define stringify_(a) #a
|
#define stringify_(a) #a
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user