Implement Poison Bounce enchant. Fix Poison Bounce physics when dealing with lower frame rates. Release Build 11180.
This commit is contained in:
parent
e8ead7e07b
commit
88dea6fa84
@ -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,26 +71,38 @@ 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)};
|
||||
for(const auto&[targetPtr,wasHit]:hurtList){
|
||||
if(wasHit){
|
||||
Monster*monsterPtr{std::get<Monster*>(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<Monster*>(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;
|
||||
|
@ -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
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user