Implement Poison Bounce enchant. Fix Poison Bounce physics when dealing with lower frame rates. Release Build 11180.

pull/65/head
sigonasr2 3 months ago
parent e8ead7e07b
commit 88dea6fa84
  1. 25
      Adventures in Lestoria Tests/EnchantTests.cpp
  2. 15
      Adventures in Lestoria Tests/GameHelper.h
  3. 4
      Adventures in Lestoria/BulletTypes.h
  4. 29
      Adventures in Lestoria/PoisonBottle.cpp
  5. 2
      Adventures in Lestoria/Version.h
  6. 7
      Adventures in Lestoria/Witch.cpp
  7. 1
      Adventures in Lestoria/assets/config/items/ItemEnchants.txt
  8. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1021,7 +1021,6 @@ namespace EnchantTests
Monster&newMonster4{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName2"])};
Game::Update(0.f);
Game::CastAbilityAtLocation(player->GetAbility1(),player->GetPos()+vf2d{30.f,0.f});
Game::Update(0.f);
Assert::IsTrue(newMonster.GetBuffs(BuffType::CURSE_OF_PAIN).size()>0,L"The first monster should have been targeted with Curse of Pain.");
Game::Update(3.f);
Assert::IsTrue(newMonster.IsDead(),L"The first monster has died to Curse of Pain's tick.");
@ -1046,7 +1045,6 @@ namespace EnchantTests
Assert::AreEqual(size_t(0),newMonster3.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"If a target without Curse of Pain dies, it should not spread onto other targets.");
Assert::AreEqual(size_t(0),newMonster4.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"If a target without Curse of Pain dies, it should not spread onto other targets.");
Game::CastAbilityAtLocation(player->GetAbility1(),player->GetPos()+vf2d{30.f,0.f});
Game::Update(0.f);
Assert::AreEqual(size_t(1),newMonster.GetBuffs(BuffType::CURSE_OF_PAIN).size(),L"The first monster should have been targeted with Curse of Pain.");
Game::Update(3.f);
Assert::IsTrue(newMonster.IsDead(),L"The first monster has died to Curse of Pain's tick.");
@ -1068,7 +1066,6 @@ namespace EnchantTests
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
Game::Update(0.f);
Game::CastAbilityAtLocation(player->GetAbility2(),player->GetPos()+vf2d{30.f,0.f});
Game::Update(0.f);
while(BULLET_LIST.size()>0){
Game::Update(1/30.f);
}
@ -1082,7 +1079,6 @@ namespace EnchantTests
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
Game::Update(0.f);
Game::CastAbilityAtLocation(player->GetAbility2(),player->GetPos()+vf2d{30.f,0.f});
Game::Update(0.f);
while(BULLET_LIST.size()>0){
Game::Update(1/30.f);
}
@ -1090,5 +1086,26 @@ namespace EnchantTests
Game::Update(2.f);
Assert::AreEqual(910,newMonster.GetHealth(),L"Monster should have lost an additional 15 health from lingering Pooling Poison.");
}
TEST_METHOD(PoisonBounceNoEnchantCheck){
Game::ChangeClass(player,WITCH);
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
Game::Update(0.f);
Game::CastAbilityAtLocation(player->GetAbility2(),player->GetPos()+vf2d{30.f,0.f});
while(BULLET_LIST.size()>0){
Game::Update(1/30.f);
}
Assert::AreEqual(925,newMonster.GetHealth(),L"Monster should have lost 75 health from Poison Pool.");
}
TEST_METHOD(PoisonBounceEnchantCheck){
Game::ChangeClass(player,WITCH);
Game::GiveAndEquipEnchantedRing("Poison Bounce");
Monster&newMonster{game->SpawnMonster({30.f,0.f},MONSTER_DATA["TestName"])};
Game::Update(0.f);
Game::CastAbilityAtLocation(player->GetAbility2(),player->GetPos()+vf2d{30.f,0.f});
while(BULLET_LIST.size()>0){
Game::Update(1/30.f);
}
Assert::AreEqual(895,newMonster.GetHealth(),L"Monster should have lost 105 health from Poison Pool.");
}
};
}

@ -42,15 +42,20 @@ All rights reserved.
INCLUDE_game
namespace Game{
inline void CastAbilityAtLocation(Ability&ability,const vf2d&screenLoc){ //NOTE: screenLoc is the actual screen coordinates, NOT the world coordinates! You are defining the mouse position essentially.
game->GetPlayer()->SetTestScreenAimingLocation(screenLoc);
game->GetPlayer()->PrepareCast(ability);
game->GetPlayer()->CastSpell(ability);
}
enum class CastWaitProperty{
WAIT_FOR_CAST_TIME,
NO_WAIT,
};
inline void Update(const float fElapsedTime){
game->SetElapsedTime(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.
game->GetPlayer()->SetTestScreenAimingLocation(screenLoc);
game->GetPlayer()->PrepareCast(ability);
game->GetPlayer()->CastSpell(ability);
Game::Update(ability.precastInfo.castTime);
}
inline void ChangeClass(Player*&player_in,const Class&cl){
game->ChangePlayerClass(cl);
player_in=game->GetPlayer();

@ -339,7 +339,7 @@ private:
};
struct PoisonBottle:public Bullet{
PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
void Update(float fElapsedTime)override;
void ModifyOutgoingDamageData(HurtDamageInfo&data);
private:
@ -351,4 +351,6 @@ private:
const float initialZ;
const float totalRiseZAmt;
const float explodeRadius;
const float bounceExplodeRadius;
int additionalBounceCount;
};

@ -44,8 +44,8 @@ All rights reserved.
INCLUDE_game
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle)
:Bullet(pos,util::pointTo(pos,targetPos)*util::distance(pos,targetPos)/totalFallTime,0.f,damage,"poison_bottle.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,image_angle),initialZ(z),explodeRadius(explodeRadius),totalRiseZAmt(totalRiseZAmt),totalFallTime(totalFallTime),startingPos(pos),targetPos(targetPos),originalRisingTime(totalFallTime*0.25f),risingTime(originalRisingTime),originalFallingTime(totalFallTime*0.75f),fallingTime(originalFallingTime){
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float bounceExplodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,int additionalBounceCount,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle)
:Bullet(pos,util::pointTo(pos,targetPos)*util::distance(pos,targetPos)/totalFallTime,0.f,damage,"poison_bottle.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,image_angle),initialZ(z),explodeRadius(explodeRadius),bounceExplodeRadius(bounceExplodeRadius),totalRiseZAmt(totalRiseZAmt),totalFallTime(totalFallTime),startingPos(pos),targetPos(targetPos),originalRisingTime(totalFallTime*0.25f/(additionalBounceCount+1)),risingTime(originalRisingTime),originalFallingTime(totalFallTime*0.75f/(additionalBounceCount+1)),fallingTime(originalFallingTime),additionalBounceCount(additionalBounceCount){
this->z=z;
}
@ -58,7 +58,12 @@ void PoisonBottle::Update(float fElapsedTime){
z=0.f;
SoundEffect::PlaySFX("Glass Break",pos);
SoundEffect::PlaySFX("Poison Pool",pos);
const float poisonCircleScale{"Witch.Ability 2.Casting Size"_F/100.f};
float poisonCircleScale{"Witch.Ability 2.Casting Size"_F/100.f};
if(additionalBounceCount>0)poisonCircleScale="Poison Bounce"_ENC["BOUNCE EXPLODE RADIUS"]/100.f;
float currentExplodeRadius{explodeRadius};
if(additionalBounceCount>0)currentExplodeRadius=bounceExplodeRadius;
game->AddEffect(std::make_unique<Effect>(pos,0.f,"poison_pool.png",game->GetPlayer()->OnUpperLevel(),0.5f,1.2f,vf2d{poisonCircleScale,poisonCircleScale},vf2d{},WHITE,0.f,0.f,false),true);
for(int i:std::ranges::iota_view(0,200)){
float size{util::random_range(0.4f,0.8f)};
@ -66,13 +71,18 @@ void PoisonBottle::Update(float fElapsedTime){
col.a=util::random_range(60,200);
game->AddEffect(std::make_unique<Effect>(pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col));
}
if(game->GetPlayer()->HasEnchant("Pooling Poison")){
game->AddEffect(std::make_unique<PoisonPool>(pos,"poison_pool.png",explodeRadius,game->GetPlayer()->GetAttack()*"Pooling Poison"_ENC["POISON POOL DAMAGE PCT"]/100.f,"Pooling Poison"_ENC["POISON POOL DAMAGE FREQUENCY"],friendly?HurtType::MONSTER:HurtType::PLAYER,"Pooling Poison"_ENC["SPLASH LINGER TIME"],2.f,OnUpperLevel(),poisonCircleScale,vf2d{},WHITE,0.f,0.f,false,0.05f,[pos=pos,col=col,poisonCircleScale](){
if(additionalBounceCount==0&&game->GetPlayer()->HasEnchant("Pooling Poison")){
game->AddEffect(std::make_unique<PoisonPool>(pos,"poison_pool.png",bounceExplodeRadius,game->GetPlayer()->GetAttack()*"Pooling Poison"_ENC["POISON POOL DAMAGE PCT"]/100.f,"Pooling Poison"_ENC["POISON POOL DAMAGE FREQUENCY"],friendly?HurtType::MONSTER:HurtType::PLAYER,"Pooling Poison"_ENC["SPLASH LINGER TIME"],0.5f,OnUpperLevel(),poisonCircleScale,vf2d{},WHITE,0.f,0.f,false,0.05f,[pos=pos,col=col,poisonCircleScale](){
float size{util::random_range(0.4f,0.8f)};
return Effect{pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col};
}),true);
}
const HurtList hurtList{game->Hurt(pos,explodeRadius,damage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
int poolDamage{damage};
if(additionalBounceCount>0)poolDamage=game->GetPlayer()->GetAttack()*"Poison Bounce"_ENC["BOUNCE DAMAGE"]/100.f;
const HurtList hurtList{game->Hurt(pos,bounceExplodeRadius,poolDamage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
if(additionalBounceCount==0){
for(const auto&[targetPtr,wasHit]:hurtList){
if(wasHit){
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
@ -83,9 +93,16 @@ void PoisonBottle::Update(float fElapsedTime){
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
}
}
}
if(additionalBounceCount>0){
additionalBounceCount--;
risingTime=originalRisingTime;
fallingTime=originalFallingTime;
}else{
vel={};
fadeOutTime=0.25f;
Deactivate();
}
}else{
if(risingTime>0.f){
risingTime-=fElapsedTime;

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 5
#define VERSION_BUILD 11173
#define VERSION_BUILD 11180
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -166,9 +166,10 @@ void Witch::InitializeClassAbilities(){
#pragma region Witch Ability 2 (Throw Poison)
Witch::ability2.action=
[](Player*p,vf2d pos={}){
const float totalFallTime{util::lerp(0.f,0.3f,util::distance(p->GetPos(),pos)/("Witch.Ability 2.Casting Range"_F/100.f*24))};
CreateBullet(PoisonBottle)(p->GetPos(),pos,"Witch.Ability 2.Casting Size"_F/100.f*24,12.f,totalFallTime,"Witch.Ability 2.Toss Max Z"_F,p->GetAttack()*"Witch.Ability 2.Damage Mult"_F,p->OnUpperLevel(),false,INFINITE,true,WHITE,vf2d{1.f,1.f},util::random(2*PI))EndBullet;
int additionalBounceCount{0};
if(p->HasEnchant("Poison Bounce"))additionalBounceCount+="Poison Bounce"_ENC["BOUNCE COUNT"];
const float totalFallTime{util::lerp(1/30.f*(additionalBounceCount+1),0.3f,util::distance(p->GetPos(),pos)/("Witch.Ability 2.Casting Range"_F/100.f*24))};
CreateBullet(PoisonBottle)(p->GetPos(),pos,"Witch.Ability 2.Casting Size"_F/100.f*24,"Poison Bounce"_ENC["BOUNCE EXPLODE RADIUS"]/100.f*24,12.f,totalFallTime,"Witch.Ability 2.Toss Max Z"_F,p->GetAttack()*"Witch.Ability 2.Damage Mult"_F,additionalBounceCount,p->OnUpperLevel(),false,INFINITE,true,WHITE,vf2d{1.f,1.f},util::random(2*PI))EndBullet;
return true;
};
#pragma endregion

@ -438,6 +438,7 @@ Item Enchants
BOUNCE COUNT = 2
BOUNCE DAMAGE = 100%
BOUNCE EXPLODE RADIUS = 175
# Stat, Lowest, Highest Value
# Stat Modifier[0] = ..., 0, 0

Loading…
Cancel
Save