diff --git a/Adventures in Lestoria Tests/EnchantTests.cpp b/Adventures in Lestoria Tests/EnchantTests.cpp index 2580a977..2a77579f 100644 --- a/Adventures in Lestoria Tests/EnchantTests.cpp +++ b/Adventures in Lestoria Tests/EnchantTests.cpp @@ -538,7 +538,20 @@ namespace EnchantTests nullRing.lock()->EnchantItem("Stealthy Retreat"); player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); Assert::AreEqual("Ranger.Right Click Ability.RetreatTime"_F+"Stealthy Retreat"_ENC["INVULNERABILITY INCREASE"],player->GetIframeTime(),L"Ranger's retreat iframe time is much greater."); - + } + TEST_METHOD(PoisonousArrowCheck){ + game->ChangePlayerClass(RANGER); + player=game->GetPlayer(); + Assert::AreEqual(false,player->PoisonArrowAutoAttackReady(),L"Poison arrow auto attack should not be ready immediately."); + std::weak_ptrnullRing{Inventory::AddItem("Null Ring"s)}; + Inventory::EquipItem(nullRing,EquipSlot::RING1); + nullRing.lock()->EnchantItem("Poisonous Arrow"); + Assert::AreEqual(true,player->PoisonArrowAutoAttackReady(),L"Poison arrow auto attack should now be ready."); + player->AutoAttack(); + Assert::AreEqual(false,player->PoisonArrowAutoAttackReady(),L"Poison arrow auto attack should now be on cooldown."); + game->SetElapsedTime("Poisonous Arrow"_ENC["POISON ARROW RESET FREQUENCY"]); + game->OnUserUpdate("Poisonous Arrow"_ENC["POISON ARROW RESET FREQUENCY"]); + Assert::AreEqual(true,player->PoisonArrowAutoAttackReady(),L"Poison arrow auto attack should be ready again."); } }; } \ No newline at end of file diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 695d392e..eef639ee 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -1127,6 +1127,13 @@ void AiL::RenderWorld(float fElapsedTime){ if(player->GetState()==State::BLOCK){ view.DrawDecal(player->GetPos()+vf2d{0,-player->GetZ()*(std::signbit(scale.y)?-1:1)}-vf2d{12,12},GFX["block.png"].Decal()); } + + if(player->PoisonArrowAutoAttackReady()&&player->poisonArrowLastParticleTimer==0.f){ + const float particleSize{fmod(game->GetRunTime(),0.4f)<0.2f?1.f:1.5f}; + game->AddEffect(std::make_unique(player->GetPos()-vf2d{0,4.f}-player->GetFacingDirVector()*6.f,0.2f,"energy_particle.png",player->OnUpperLevel(),vf2d{particleSize,particleSize}*1.2f,0.05f,vf2d{},Pixel{0,255,255,120},util::random(2*PI),0.f,true)); + game->AddEffect(std::make_unique(player->GetPos()-vf2d{0,4.f}-player->GetFacingDirVector()*6.f,0.2f,"energy_particle.png",player->OnUpperLevel(),vf2d{particleSize,particleSize},0.05f,vf2d{},DARK_GREEN,util::random(2*PI))); + player->poisonArrowLastParticleTimer=0.15f; + } }; auto RenderZone=[&](geom2d::rect&zone){ diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h index f281ee59..ead47619 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.h +++ b/Adventures in Lestoria/AdventuresInLestoria.h @@ -63,9 +63,11 @@ All rights reserved. class SteamKeyboardCallbackHandler; class SteamStatsReceivedHandler; -#define CreateBullet(type) INCLUDE_BULLET_LIST \ -BULLET_LIST.push_back(std::make_unique(type -#define EndBullet )); +INCLUDE_BULLET_LIST + +#define CreateBullet(type) \ + *(type*const)(BULLET_LIST.emplace_back(std::make_unique(type +#define EndBullet )).get()) using HurtReturnValue=bool; using HurtList=std::vector,HurtReturnValue>>; diff --git a/Adventures in Lestoria/Arrow.cpp b/Adventures in Lestoria/Arrow.cpp index fb36c430..3b06c5bb 100644 --- a/Adventures in Lestoria/Arrow.cpp +++ b/Adventures in Lestoria/Arrow.cpp @@ -43,6 +43,7 @@ All rights reserved. #include "olcUTIL_Geometry2D.h" INCLUDE_game +INCLUDE_GFX Arrow::Arrow(vf2d pos,vf2d targetPos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col) :finalDistance(geom2d::line(pos,targetPos).length()*1.2f),acc(PI/2*250),targetPos(targetPos), @@ -94,6 +95,7 @@ BulletDestroyState Arrow::PlayerHit(Player*player) { fadeOutTime=0.2f; game->AddEffect(std::make_unique(player->GetPos(),0,"splash_effect.png",upperLevel,player->GetSizeMult(),0.25)); + if(poisonArrow)player->AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,"Poisonous Arrow"_ENC["POISON DURATION"],damage*("Poisonous Arrow"_ENC["POISON TICK DAMAGE"]/100.f),1.f); return BulletDestroyState::KEEP_ALIVE; } @@ -101,9 +103,21 @@ BulletDestroyState Arrow::MonsterHit(Monster&monster,const uint8_t markStacksBef { fadeOutTime=0.2f; game->AddEffect(std::make_unique(monster.GetPos(),0,"splash_effect.png",upperLevel,monster.GetSizeMult(),0.25)); + if(poisonArrow)monster.AddBuff(BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_DAMAGE_OVER_TIME,"Poisonous Arrow"_ENC["POISON DURATION"],damage*("Poisonous Arrow"_ENC["POISON TICK DAMAGE"]/100.f),1.f); return BulletDestroyState::KEEP_ALIVE; } void Arrow::ModifyOutgoingDamageData(HurtDamageInfo&data){ if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY; +} + +void Arrow::Draw(const Pixel blendCol)const{ + if(poisonArrow){ + game->SetDecalMode(DecalMode::ADDITIVE); + game->view.DrawRotatedDecal(pos,GFX["glow.png"].Decal(),image_angle,GFX["glow.png"].Sprite()->Size()/2,scale,{255,255,0,120}); + game->SetDecalMode(DecalMode::NORMAL); + Bullet::Draw({128,192,0,blendCol.a}); + }else{ + Bullet::Draw(blendCol); + } } \ No newline at end of file diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h index bcefe572..3ad8a3da 100644 --- a/Adventures in Lestoria/BulletTypes.h +++ b/Adventures in Lestoria/BulletTypes.h @@ -72,6 +72,7 @@ struct Arrow:public Bullet{ float finalDistance=0; float acc=PI/2*250; vf2d targetPos; + bool poisonArrow{false}; Arrow(vf2d pos,vf2d targetPos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE); Arrow(vf2d pos,vf2d targetPos,vf2d vel,const std::string_view gfx,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE); void Update(float fElapsedTime)override; @@ -81,6 +82,7 @@ struct Arrow:public Bullet{ BulletDestroyState PlayerHit(Player*player)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _PlayerHit()!! BulletDestroyState MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit)override;//DO NOT CALL THIS DIRECTLY! INSTEAD USE _MonsterHit()!! void ModifyOutgoingDamageData(HurtDamageInfo&data); + void Draw(const Pixel blendCol)const override; }; struct ChargedArrow:public Bullet{ diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 8fc4b466..39190c71 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -348,6 +348,8 @@ void Player::Update(float fElapsedTime){ lastDotTimer=std::max(0.f,lastDotTimer-fElapsedTime); lastPathfindingCooldown=std::max(0.f,lastPathfindingCooldown-fElapsedTime); lowHealthSoundPlayedTimer=std::max(0.f,lowHealthSoundPlayedTimer-fElapsedTime); + poisonArrowReadyTimer=std::max(0.f,poisonArrowReadyTimer-fElapsedTime); + poisonArrowLastParticleTimer=std::max(0.f,poisonArrowLastParticleTimer-fElapsedTime); if(hurtRumbleTime>0.f){ hurtRumbleTime=std::max(0.f,hurtRumbleTime-fElapsedTime); if(hurtRumbleTime==0.f){ @@ -983,7 +985,7 @@ void Player::SetFacingDirection(Key direction){ facingDirection=direction; } -Key Player::GetFacingDirection(){ +Key Player::GetFacingDirection()const{ return facingDirection; } @@ -1958,4 +1960,29 @@ void Player::ReduceAutoAttackTimer(const float reduceAmt){ const float&Player::GetAutoAttackTimer()const{ return attack_cooldown_timer; +} + +const bool Player::PoisonArrowAutoAttackReady()const{ + return HasEnchant("Poisonous Arrow")&&poisonArrowReadyTimer==0.f; +} + +const vf2d Player::GetFacingDirVector()const{ + switch(GetFacingDirection()){ + case UP:{ + return {0,-1}; + }break; + case DOWN:{ + return {0,1}; + }break; + case LEFT:{ + return {-1,0}; + }break; + case RIGHT:{ + return {1,0}; + }break; + default:{ + ERR(std::format("WARNING! Somehow got an invalid facing direction: {}. THIS SHOULD NOT BE HAPPENING!",int(GetFacingDirection()))); + return {}; + } + } } \ No newline at end of file diff --git a/Adventures in Lestoria/Player.h b/Adventures in Lestoria/Player.h index baf14e4f..f3f9a2a6 100644 --- a/Adventures in Lestoria/Player.h +++ b/Adventures in Lestoria/Player.h @@ -144,7 +144,7 @@ public: float GetAttackRangeMult(); float GetSpinAngle(); State::State GetState(); - Key GetFacingDirection(); + Key GetFacingDirection()const; vf2d GetVelocity(); bool HasIframes(); void Update(float fElapsedTime); @@ -305,6 +305,8 @@ public: void CheckAndPerformAbility(Ability&ability,InputGroup key); void ReduceAutoAttackTimer(const float reduceAmt); const float&GetAutoAttackTimer()const; + const vf2d GetFacingDirVector()const; //Returns a normalized vector based on the facing direction of the character. Ex. {0,-1} for north and {1,0} for east. + const bool PoisonArrowAutoAttackReady()const; //NOTE: Also checks to make sure we have the enchant built-in... private: int hp="Warrior.BaseHealth"_I; int mana="Player.BaseMana"_I; @@ -384,6 +386,7 @@ private: std::unordered_setenchantList; void OnAbilityUse(const Ability&ability); //Callback when an ability successfully is used and has gone on cooldown. const bool LastReserveEnchantConditionsMet()const; + float poisonArrowLastParticleTimer{}; protected: const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; @@ -436,6 +439,7 @@ protected: float leapTimer{}; float totalLeapTime{}; vf2d leapStartingPos{}; + float poisonArrowReadyTimer{}; }; #pragma region Warrior diff --git a/Adventures in Lestoria/Ranger.cpp b/Adventures in Lestoria/Ranger.cpp index 24342558..9458a8d8 100644 --- a/Adventures in Lestoria/Ranger.cpp +++ b/Adventures in Lestoria/Ranger.cpp @@ -71,7 +71,11 @@ bool Ranger::AutoAttack(){ vf2d extendedLine=pointTowardsCursor.upoint(1.1f); float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x); attack_cooldown_timer=ARROW_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); - CreateBullet(Arrow)(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)EndBullet; + Arrow&arrow{CreateBullet(Arrow)(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)EndBullet}; + if(PoisonArrowAutoAttackReady()){ + arrow.poisonArrow=true; + poisonArrowReadyTimer="Poisonous Arrow"_ENC["POISON ARROW RESET FREQUENCY"]; + } BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile(); SetState(State::SHOOT_ARROW); SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor); diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index c5ce78e1..3cf86ebe 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 3 -#define VERSION_BUILD 10803 +#define VERSION_BUILD 10813 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt index e788a670..d273b76e 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -121,6 +121,7 @@ Images GFX_Fragment = items/Fragment.png GFX_MonsterSoul = monstersoul.png GFX_MonsterSoulGlow = monstersoulglow.png + GFX_Glow = glow.png GFX_Thief_Sheet = nico-thief.png GFX_Trapper_Sheet = nico-trapper.png diff --git a/Adventures in Lestoria/assets/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak index 0e2b4176..138da3aa 100644 Binary files a/Adventures in Lestoria/assets/gamepack.pak and b/Adventures in Lestoria/assets/gamepack.pak differ diff --git a/Adventures in Lestoria/assets/glow.png b/Adventures in Lestoria/assets/glow.png new file mode 100644 index 00000000..e5cae714 Binary files /dev/null and b/Adventures in Lestoria/assets/glow.png differ diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 9d677d72..259ada57 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ