diff --git a/Adventures in Lestoria Tests/EnchantTests.cpp b/Adventures in Lestoria Tests/EnchantTests.cpp index 00eae922..7fc7b63e 100644 --- a/Adventures in Lestoria Tests/EnchantTests.cpp +++ b/Adventures in Lestoria Tests/EnchantTests.cpp @@ -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."); + } }; } diff --git a/Adventures in Lestoria Tests/GameHelper.h b/Adventures in Lestoria Tests/GameHelper.h index 0b67717f..e1db6eb2 100644 --- a/Adventures in Lestoria Tests/GameHelper.h +++ b/Adventures in Lestoria Tests/GameHelper.h @@ -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(); diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h index 878fce62..614fb0c9 100644 --- a/Adventures in Lestoria/BulletTypes.h +++ b/Adventures in Lestoria/BulletTypes.h @@ -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; }; \ No newline at end of file diff --git a/Adventures in Lestoria/PoisonBottle.cpp b/Adventures in Lestoria/PoisonBottle.cpp index 144ee8f6..9be0b0ad 100644 --- a/Adventures in Lestoria/PoisonBottle.cpp +++ b/Adventures in Lestoria/PoisonBottle.cpp @@ -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(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,26 +71,38 @@ void PoisonBottle::Update(float fElapsedTime){ col.a=util::random_range(60,200); game->AddEffect(std::make_unique(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(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(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)}; - for(const auto&[targetPtr,wasHit]:hurtList){ - if(wasHit){ - Monster*monsterPtr{std::get(targetPtr)}; - const int buffDamage{int(game->GetPlayer()->GetAttack()*"Witch.Ability 2.Poison Debuff"_f[0])}; - const float buffDuration{"Witch.Ability 2.Poison Debuff"_f[2]}; - const float buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]}; - monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n); - monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks); + + 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(targetPtr)}; + const int buffDamage{int(game->GetPlayer()->GetAttack()*"Witch.Ability 2.Poison Debuff"_f[0])}; + const float buffDuration{"Witch.Ability 2.Poison Debuff"_f[2]}; + const float buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]}; + monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n); + monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks); + } } } - vel={}; - fadeOutTime=0.25f; - Deactivate(); + if(additionalBounceCount>0){ + additionalBounceCount--; + risingTime=originalRisingTime; + fallingTime=originalFallingTime; + }else{ + vel={}; + fadeOutTime=0.25f; + Deactivate(); + } }else{ if(risingTime>0.f){ risingTime-=fElapsedTime; diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index a35ad104..977433d9 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 2 #define VERSION_PATCH 5 -#define VERSION_BUILD 11173 +#define VERSION_BUILD 11180 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/Witch.cpp b/Adventures in Lestoria/Witch.cpp index 22100d41..f690dc57 100644 --- a/Adventures in Lestoria/Witch.cpp +++ b/Adventures in Lestoria/Witch.cpp @@ -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 diff --git a/Adventures in Lestoria/assets/config/items/ItemEnchants.txt b/Adventures in Lestoria/assets/config/items/ItemEnchants.txt index a6119b1b..2310df34 100644 --- a/Adventures in Lestoria/assets/config/items/ItemEnchants.txt +++ b/Adventures in Lestoria/assets/config/items/ItemEnchants.txt @@ -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 diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 95c61067..cae97b5a 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ