diff --git a/Adventures in Lestoria Tests/BuffTests.cpp b/Adventures in Lestoria Tests/BuffTests.cpp index abd4e938..def9a10b 100644 --- a/Adventures in Lestoria Tests/BuffTests.cpp +++ b/Adventures in Lestoria Tests/BuffTests.cpp @@ -120,6 +120,7 @@ namespace BuffTests TEST_METHOD(AddBuffPlayerCallbackExpireFunctionTest){ Assert::AreEqual(size_t(0),game->GetPlayer()->GetBuffs(BuffType::AFFECTED_BY_LIGHTNING_BOLT).size(),L"Player does not have any lightning bolt affected buffs when spawning."); game->GetPlayer()->AddBuff(BuffType::AFFECTED_BY_LIGHTNING_BOLT,3.f,1,[](Player*attachedTarget,Buff&b){attachedTarget->Hurt(5,attachedTarget->OnUpperLevel(),attachedTarget->GetZ());}); + Game::Update(0.f); Assert::AreEqual(size_t(1),game->GetPlayer()->GetBuffs(BuffType::AFFECTED_BY_LIGHTNING_BOLT).size(),L"Player is now affected By Lightning Bolt buff. Should get hurt for 5 damage in 3 seconds..."); Game::Update(0.f); Game::Update(3.f); @@ -146,20 +147,27 @@ namespace BuffTests } TEST_METHOD(PlayerHasBuffFunctionTest){ Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should not have a speedboost buff before being given one."); - game->GetPlayer()->AddBuff(BuffType::ADRENALINE_RUSH,1.f,1.f); + Game::AddBuffToPlayer(BuffType::ADRENALINE_RUSH,1.f,1.f); Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should not have a speedboost buff when given an unrelated buff."); - game->GetPlayer()->AddBuff(BuffType::SPEEDBOOST,1.f,1.f); + Game::AddBuffToPlayer(BuffType::SPEEDBOOST,1.f,1.f); Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should now have a speedboost buff."); - game->GetPlayer()->AddBuff(BuffType::SPEEDBOOST,2.f,1.f); + Game::AddBuffToPlayer(BuffType::SPEEDBOOST,2.f,1.f); Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should still report having a speedboost buff."); Game::Update(0.f); Game::Update(1.f); Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should still have one speedboost buff."); Game::Update(1.f); Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should no longer have a speedboost buff."); - game->GetPlayer()->AddBuff(BuffType::SPEEDBOOST,1.f,1.f); + Game::AddBuffToPlayer(BuffType::SPEEDBOOST,1.f,1.f); game->GetPlayer()->RemoveBuff(BuffType::SPEEDBOOST); Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should no longer have a speedboost buff."); } + TEST_METHOD(PlayerDoesNotImmediatelyReceiveBuffTest){ + Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should not have a speedboost buff before being given one."); + game->GetPlayer()->AddBuff(BuffType::SPEEDBOOST,1.f,1.f); + Assert::AreEqual(false,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should still not have a speedboost buff since it is in the add queue."); + Game::Update(0.f); + Assert::AreEqual(true,game->GetPlayer()->HasBuff(BuffType::SPEEDBOOST),L"Player should now have a speedboost buff."); + } }; } \ No newline at end of file diff --git a/Adventures in Lestoria Tests/EnchantTests.cpp b/Adventures in Lestoria Tests/EnchantTests.cpp index 9f97b30f..877165f0 100644 --- a/Adventures in Lestoria Tests/EnchantTests.cpp +++ b/Adventures in Lestoria Tests/EnchantTests.cpp @@ -275,6 +275,7 @@ namespace EnchantTests Assert::AreEqual(size_t(0),player->GetBuffs(BuffType::LETHAL_TEMPO).size(),L"Lethal Tempo does not stack up without the enchant."); Game::GiveAndEquipEnchantedRing("Lethal Tempo"); testMonster.Hurt(0,testMonster.OnUpperLevel(),testMonster.GetZ(),HurtFlag::PLAYER_ABILITY); + Game::Update(0.f); Assert::AreEqual(size_t(1),player->GetBuffs(BuffType::LETHAL_TEMPO).size(),L"Lethal Tempo buff is active after attacking with the enchant.."); Assert::AreEqual(0.0175f,player->GetAttackRecoveryRateReduction(),L"Lethal Tempo buff reduced attack Recovery Rate by 0.0175 (5% of 0.35)."); for(int i:std::ranges::iota_view(0,10)){ @@ -561,6 +562,7 @@ namespace EnchantTests TEST_METHOD(TumbleCheck){ Game::ChangeClass(player,THIEF); player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); + Game::Update(0.f); const float originalIframeTime{"Thief.Right Click Ability.Iframe Time"_F}; Assert::AreEqual(originalIframeTime,player->GetIframeTime(),L"Iframe time should be normal."); const float originalMovespdIntensity{"Thief.Right Click Ability.Movespeed Buff"_f[0]/100.f}; @@ -569,6 +571,7 @@ namespace EnchantTests player->GetRightClickAbility().charges=1; Game::GiveAndEquipEnchantedRing("Tumble"); player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); + Game::Update(0.f); Assert::AreEqual(originalIframeTime+"Thief.Right Click Ability.Iframe Time"_F*"Tumble"_ENC["BOOST PERCENTAGE"]/100.f,player->GetIframeTime(),L"Iframe time should be longer."); Assert::AreEqual(originalMovespdIntensity+originalMovespdIntensity*"Tumble"_ENC["BOOST PERCENTAGE"]/100.f,player->GetBuffs(BuffType::SPEEDBOOST)[0].intensity,L"Player should have a movespeed buff with greater intensity."); } @@ -618,6 +621,7 @@ namespace EnchantTests TEST_METHOD(AdrenalineStimCheck){ Game::ChangeClass(player,THIEF); player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Game::Update(0.f); Assert::AreEqual("Thief.Ability 3.Duration"_F,player->GetBuffs(BuffType::ADRENALINE_RUSH)[0].duration,L"Adrenaline Rush buff duration is normal."); Assert::AreEqual(100,player->GetHealth(),L"Adrenaline Rush does not reduce health."); Assert::AreEqual("Thief.Auto Attack.Cooldown"_F*"Thief.Ability 3.Attack Speed Increase"_F/100.f,player->GetAttackRecoveryRateReduction(),L"Adrenaline Rush boosts attack rate normally."); @@ -627,6 +631,7 @@ namespace EnchantTests player->RemoveAllBuffs(); Game::GiveAndEquipEnchantedRing("Adrenaline Stim"); player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Game::Update(0.f); Assert::AreEqual("Adrenaline Stim"_ENC["NEW ADRENALINE RUSH DURATION"],player->GetBuffs(BuffType::ADRENALINE_RUSH)[0].duration,L"Adrenaline Stim enchant boosts the duration of Adrenaline Rush."); Assert::AreEqual(80,player->GetHealth(),L"Adrenaline Stim reduces health."); Assert::AreEqual("Thief.Auto Attack.Cooldown"_F*"Thief.Ability 3.Attack Speed Increase"_F/100.f,player->GetAttackRecoveryRateReduction(),L"Adrenaline Stim still boosts attack rate normally."); @@ -636,6 +641,7 @@ namespace EnchantTests Game::ChangeClass(player,THIEF); player->SetBaseStat("Attack",100); player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Game::Update(0.f); MonsterData testMonsterData{"TestName","Test Monster",30,10,5,{MonsterDropData{"Health Potion",100.f,1,1}},200.f}; MONSTER_DATA["TestName"]=testMonsterData; for(int i:std::ranges::iota_view(0,10)){ @@ -649,6 +655,7 @@ namespace EnchantTests Thief::ability3.charges=1; Game::GiveAndEquipEnchantedRing("Bloodlust"); player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Game::Update(0.f); for(int i:std::ranges::iota_view(0,30)){ Monster testMonster{{},MONSTER_DATA["TestName"]}; testMonster.Hurt(1000,testMonster.OnUpperLevel(),testMonster.GetZ()); @@ -663,6 +670,7 @@ namespace EnchantTests player->GetRightClickAbility().charges=1; Game::GiveAndEquipEnchantedRing("Evasive Movement"); player->CheckAndPerformAbility(player->GetRightClickAbility(),testKeyboardInput); + Game::Update(0.f); Assert::AreEqual(size_t(1),player->GetBuffs(BuffType::DAMAGE_REDUCTION).size(),L"Roll gives a damage reduction buff."); Assert::AreEqual("Evasive Movement"_ENC["DAMAGE REDUCTION PCT"]/100.f,player->GetDamageReductionFromBuffs(),L"Evasive Movement provides 50% damage reduction."); } @@ -848,6 +856,7 @@ namespace EnchantTests TEST_METHOD(SwordEnchantmentEnchantCheck){ Game::GiveAndEquipEnchantedRing("Sword Enchantment"); player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Game::Update(0.f); Assert::AreEqual(300.f,player->GetAttackRange(),L"Attack range of Warrior doubled."); Game::Update(0.f); //Wait an extra tick for the buff to begin going down. Game::Update(8.f); diff --git a/Adventures in Lestoria Tests/GameHelper.h b/Adventures in Lestoria Tests/GameHelper.h index 4997c9a3..1bee13e9 100644 --- a/Adventures in Lestoria Tests/GameHelper.h +++ b/Adventures in Lestoria Tests/GameHelper.h @@ -69,6 +69,21 @@ namespace Game{ nullRing.lock()->_EnchantItem(enchantName); return nullRing; } + //Adds the buff directly to the player instead of the buffs added list. (By calling an update tick.) + inline void AddBuffToPlayer(BuffType type,float duration,float intensity){ + game->GetPlayer()->AddBuff(type,duration,intensity); + Update(0.f); + } + //NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!! + inline void AddBuffToPlayer(BuffType type,float duration,float intensity,std::setattr){ + game->GetPlayer()->AddBuff(type,duration,intensity,attr); + Update(0.f); + } + //NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!! + inline void AddBuffToPlayer(BuffType type,float duration,float intensity,std::setattr){ + game->GetPlayer()->AddBuff(type,duration,intensity,attr); + Update(0.f); + } } namespace Test diff --git a/Adventures in Lestoria Tests/PlayerTests.cpp b/Adventures in Lestoria Tests/PlayerTests.cpp index e5deb961..02abbddb 100644 --- a/Adventures in Lestoria Tests/PlayerTests.cpp +++ b/Adventures in Lestoria Tests/PlayerTests.cpp @@ -43,6 +43,7 @@ All rights reserved. #include "ItemDrop.h" #include "DamageNumber.h" #include +#include "GameHelper.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace olc::utils; @@ -321,11 +322,11 @@ namespace PlayerTests Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100."); } TEST_METHOD(HealthStatUpBuffCheck){ - player->AddBuff(BuffType::STAT_UP,5.f,1000.f,{"Health"}); + Game::AddBuffToPlayer(BuffType::STAT_UP,5.f,1000.f,{"Health"}); Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100."); } TEST_METHOD(HealthPctStatUpBuffCheck){ - player->AddBuff(BuffType::STAT_UP,5.f,1000.0_Pct,{"Health %"}); + Game::AddBuffToPlayer(BuffType::STAT_UP,5.f,1000.0_Pct,{"Health %"}); Assert::AreEqual(1000+player->GetBaseStat("Health"),float(player->GetMaxHealth()),L"Max Health stat should be increased from 100 to 1100."); } TEST_METHOD(IllegalCritRateStatUpBuffCheck){ @@ -593,6 +594,7 @@ namespace PlayerTests Assert::AreEqual(0.f,player->GetAttackRecoveryRateReduction(),L"Attack rate cooldown starts with being reduced by 0 seconds."); testKey->bHeld=true; //Force the key to be held down for testing purposes. player->CheckAndPerformAbility(player->GetAbility3(),testKeyboardInput); + Game::Update(0.f); Assert::IsTrue(player->GetBuffs(BuffType::ADRENALINE_RUSH).size()>0,L"After using Adrenaline Rush, player has the Adrenaline Rush buff."); Assert::AreEqual(1.1f,player->GetMoveSpdMult(),L"Move Speed Multiplier increased by 10% to x1.1"); Assert::AreEqual(0.105f,player->GetAttackRecoveryRateReduction(),L"Attack Recovery Rate reduced by 30%, or 0.105s"); diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 99d0e85b..5547dcf8 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -1114,6 +1114,10 @@ + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index ae6acb9c..0bdaca48 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -1304,6 +1304,9 @@ Source Files\Bullet Types + + Source Files\Monster Strategies + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 670e2386..8dfbf4be 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -1068,12 +1068,15 @@ void AiL::RenderWorld(float fElapsedTime){ const std::vectoradrenalineRushBuffs{player->GetBuffs(BuffType::ADRENALINE_RUSH)}; const std::vectordamageReductionBuffs{player->GetBuffs(BuffType::DAMAGE_REDUCTION)}; const std::vectorinkSlowdownDebuff{player->GetBuffs(BuffType::INK_SLOWDOWN)}; + const std::vectorcurseDebuff{player->GetBuffs(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_DOT)}; + const bool displayCoinSymbol{player->GetBuffs(BuffType::PIRATE_GHOST_CAPTAIN_CURSE).size()>0}; Pixel playerCol{WHITE}; if(attackBuffs.size()>0)playerCol={255,uint8_t(255*abs(sin(1.4f*attackBuffs[0].duration))),uint8_t(255*abs(sin(1.4f*attackBuffs[0].duration)))}; else if(adrenalineRushBuffs.size()>0)playerCol={uint8_t(255*abs(sin(6.f*adrenalineRushBuffs[0].duration))),255,uint8_t(255*abs(sin(6.f*adrenalineRushBuffs[0].duration)))}; else if(movespeedBuffs.size()>0)playerCol={uint8_t(255*abs(sin(2.f*movespeedBuffs[0].duration))),255,uint8_t(255*abs(sin(2.f*movespeedBuffs[0].duration)))}; else if(inkSlowdownDebuff.size()>0)playerCol={uint8_t(255*abs(sin(2.f*inkSlowdownDebuff[0].duration))),uint8_t(255*abs(sin(2.f*inkSlowdownDebuff[0].duration))),uint8_t(255*abs(sin(2.f*inkSlowdownDebuff[0].duration)))}; + else if(curseDebuff.size()>0)playerCol={uint8_t(128*abs(sin(2.f*GetRunTime()))+127),uint8_t(128*abs(sin(2.f*GetRunTime()))),uint8_t(128*abs(sin(2.f*GetRunTime()))+127)}; if(player->HasIframes())playerCol.a*=0.62f; @@ -1093,6 +1096,10 @@ void AiL::RenderWorld(float fElapsedTime){ 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; } + + if(displayCoinSymbol){ + view.DrawRotatedDecal(pos+vf2d{0,(-player->GetZ()-24.f-sinf(PI*GetRunTime())*4.f)*(std::signbit(scale.y)?-1:1)},GFX["coin.png"].Decal(),0.f,GFX["coin.png"].Sprite()->Size()/2,{0.5f,0.5f}); + } }; auto RenderZone=[&](geom2d::rect&zone){ diff --git a/Adventures in Lestoria/Buff.h b/Adventures in Lestoria/Buff.h index 18d38c8f..fb28536b 100644 --- a/Adventures in Lestoria/Buff.h +++ b/Adventures in Lestoria/Buff.h @@ -66,6 +66,8 @@ enum BuffType{ CURSE_OF_DEATH, AFFECTED_BY_LIGHTNING_BOLT, //Intensity indicates number of repeats remaining. INK_SLOWDOWN, //Intensity indicates % movespd slowdown. + PIRATE_GHOST_CAPTAIN_CURSE, //A coin icon appears above the player's head. + PIRATE_GHOST_CAPTAIN_CURSE_DOT, //The same as above, but now is a damage over time as well. }; enum class BuffRestorationType{ ONE_OFF, //This is used as a hack fix for the RestoreDuringCast Item script since they require us to restore 1 tick immediately. Over time buffs do not apply a tick immediately. @@ -75,11 +77,11 @@ enum class BuffRestorationType{ namespace BuffOverTimeType{ enum BuffOverTimeType{ HP_RESTORATION, - HP_PCT_RESTORATION, + HP_PCT_RESTORATION, //Percentage should be the raw percentage, NOT % form! (So 1 for 1%, not 0.01) MP_RESTORATION, - MP_PCT_RESTORATION, + MP_PCT_RESTORATION, //Percentage should be the raw percentage, NOT % form! (So 1 for 1%, not 0.01) HP_DAMAGE_OVER_TIME, - HP_PCT_DAMAGE_OVER_TIME, + HP_PCT_DAMAGE_OVER_TIME, //Percentage should be the raw percentage, NOT % form! (So 1 for 1%, not 0.01) }; }; diff --git a/Adventures in Lestoria/GhostOfPirateCaptain.cpp b/Adventures in Lestoria/GhostOfPirateCaptain.cpp index 1bd134b1..b7b801d6 100644 --- a/Adventures in Lestoria/GhostOfPirateCaptain.cpp +++ b/Adventures in Lestoria/GhostOfPirateCaptain.cpp @@ -54,6 +54,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std NORMAL, AFTERIMAGE_FADEIN, GHOSTSABER_SLASH=999, + TOSS_COIN, }; enum CannonShotType{ @@ -180,6 +181,12 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std } }break; } + + if(Config("Curse Thresholds").GetValueCount()>m.I(A::CURSE_THRESHOLD_ARRAY_IND)&& + m.GetHealthRatio()<=ConfigFloatArr("Curse Thresholds",m.I(A::CURSE_THRESHOLD_ARRAY_IND))){ + m.I(A::CURSE_THRESHOLD_ARRAY_IND)++; + SETPHASE(TOSS_COIN); + } }break; case AFTERIMAGE_FADEIN:{ m.F(A::CASTING_TIMER)-=fElapsedTime; @@ -195,5 +202,10 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std SETPHASE(m.I(A::PREVIOUS_PHASE)); } }break; + case TOSS_COIN:{ + const float curseDmgPctOverTime{ConfigFloat("Curse Damage")}; + game->GetPlayer()->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE,ConfigFloat("Curse Damage Wait Time"),ceil(game->GetPlayer()->GetMaxHealth()*ConfigFloat("Curse Damage")/100.f)+1,[curseDmgPctOverTime](Player*attachedTarget,Buff&b){attachedTarget->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_DOT,BuffRestorationType::OVER_TIME,BuffOverTimeType::HP_PCT_DAMAGE_OVER_TIME,INFINITY,curseDmgPctOverTime,1.f);}); + SETPHASE(NORMAL); + }break; } } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 5f67e2f2..35681627 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -82,8 +82,10 @@ Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob): monsterWalkSoundTimer=util::random(1.f); UpdateFacingDirection(game->GetPlayer()->GetPos()); animation.UpdateState(internal_animState,randomFrameOffset); - const vf2d¤tRect{GetFrame().GetSourceRect().size}; - afterImage.Create(currentRect.x,currentRect.y); + [[likely]]if(!game->TestingModeEnabled()){ + const vf2d¤tRect{GetFrame().GetSourceRect().size}; + afterImage.Create(currentRect.x,currentRect.y); + } } const vf2d&Monster::GetPos()const{ return pos; @@ -411,23 +413,25 @@ void Monster::Update(const float fElapsedTime){ attackedByPlayer=false; #pragma region Afterimage Handling - const auto RemoveScanLine=[&](uint8_t scanLine){ - for(int x:std::ranges::iota_view(0,afterImage.Sprite()->width)){ - afterImage.Sprite()->SetPixel({x,scanLine},BLANK); - } - afterImage.Decal()->Update(); - }; - - //Scan Line goes through 1-(height-1) (odd numbers) first, then 0-22. - const bool ScanLineFinished{scanLine==afterImage.Sprite()->height}; - if(!ScanLineFinished){ - removeLineTimer-=fElapsedTime; - if(removeLineTimer<=0.f){ - removeLineTimer=TIME_BETWEEN_LINE_REMOVALS; - RemoveScanLine(scanLine); - scanLine+=2; - if(scanLine>afterImage.Sprite()->height-1&&scanLine%2==1){ - scanLine=0; + [[likely]]if(!game->TestingModeEnabled()){ + const auto RemoveScanLine=[&](uint8_t scanLine){ + for(int x:std::ranges::iota_view(0,afterImage.Sprite()->width)){ + afterImage.Sprite()->SetPixel({x,scanLine},BLANK); + } + afterImage.Decal()->Update(); + }; + + //Scan Line goes through 1-(height-1) (odd numbers) first, then 0-22. + const bool ScanLineFinished{scanLine==afterImage.Sprite()->height}; + if(!ScanLineFinished){ + removeLineTimer-=fElapsedTime; + if(removeLineTimer<=0.f){ + removeLineTimer=TIME_BETWEEN_LINE_REMOVALS; + RemoveScanLine(scanLine); + scanLine+=2; + if(scanLine>afterImage.Sprite()->height-1&&scanLine%2==1){ + scanLine=0; + } } } } diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index 1c240b86..faf26430 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -392,6 +392,7 @@ private: static void GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string strategy); static void OCTOPUS_ARM(Monster&m,float fElapsedTime,std::string strategy); static void GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string strategy); + static void PIRATES_TREASURE(Monster&m,float fElapsedTime,std::string strategy); }; bool bumpedIntoTerrain=false; //Gets set to true before a strategy executes if the monster runs into some terrain on this frame. bool attackedByPlayer=false; //Gets set to true before a strategy executes if the monster has been attacked by the player. diff --git a/Adventures in Lestoria/MonsterAttribute.h b/Adventures in Lestoria/MonsterAttribute.h index acb4aa79..bc389698 100644 --- a/Adventures in Lestoria/MonsterAttribute.h +++ b/Adventures in Lestoria/MonsterAttribute.h @@ -175,4 +175,5 @@ enum class Attribute{ FIRST_WAVE_COMPLETE, GHOST_SABER_TIMER, GHOST_SABER_SLASH_ANIMATION_TIMER, + CURSE_THRESHOLD_ARRAY_IND, //Which array index in the curse threshold strategy property we are currently at? }; \ No newline at end of file diff --git a/Adventures in Lestoria/PiratesTreasure.cpp b/Adventures in Lestoria/PiratesTreasure.cpp new file mode 100644 index 00000000..9333e164 --- /dev/null +++ b/Adventures in Lestoria/PiratesTreasure.cpp @@ -0,0 +1,52 @@ +#pragma region License +/* +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2024 Joshua Sigona + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions or derivations of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce the above +copyright notice. This list of conditions and the following disclaimer must be +reproduced in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may +be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Portions of this software are copyright © 2024 The FreeType +Project (www.freetype.org). Please see LICENSE_FT.txt for more information. +All rights reserved. +*/ +#pragma endregion + +#include "AdventuresInLestoria.h" +#include "MonsterStrategyHelpers.h" + +INCLUDE_game + +void Monster::STRATEGY::PIRATES_TREASURE(Monster&m,float fElapsedTime,std::string strategy){ + const float distToPlayer{util::distance(game->GetPlayer()->GetPos(),m.GetPos())}; + if(distToPlayer<=ConfigFloat("Open Distance"))m.PerformAnimation("OPEN"); + else m.PerformIdleAnimation(); + if(m.B(Attribute::COLLIDED_WITH_PLAYER)){ + game->GetPlayer()->RemoveBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE); + game->GetPlayer()->RemoveBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_DOT); + } +} \ No newline at end of file diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 4eb93cae..ea90a3f8 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -441,6 +441,9 @@ void Player::Update(float fElapsedTime){ } return false; }); + std::for_each(buffsToBeAdded.begin(),buffsToBeAdded.end(),[&](Buff&b){b.Update(game,fElapsedTime);}); + std::move(buffsToBeAdded.begin(),buffsToBeAdded.end(),std::back_inserter(buffList)); + buffsToBeAdded.clear(); //Class-specific update events. OnUpdate(fElapsedTime); switch(state){ @@ -1143,39 +1146,31 @@ void Player::UpdateIdleAnimation(Key direction){ animation.ChangeState(internal_catAnimState,std::format("WITCH_CAT_WALK_{}",anim[anim.length()-1]),0.f); } -void Player::AddBuff(BuffType type,float duration,float intensity){ - Buff&newBuff{buffList.emplace_back(this,type,duration,intensity)}; - OnBuffAdd(newBuff); +Buff&Player::AddBuff(BuffType type,float duration,float intensity){ + return buffsToBeAdded.emplace_back(this,type,duration,intensity); } -void Player::AddBuff(BuffType type,float duration,float intensity,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){ - Buff&newBuff{buffList.emplace_back(this,type,duration,intensity,expireCallbackFunc)}; - OnBuffAdd(newBuff); +Buff&Player::AddBuff(BuffType type,float duration,float intensity,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){ + return buffsToBeAdded.emplace_back(this,type,duration,intensity,expireCallbackFunc); } -void Player::AddBuff(BuffType type,float duration,float intensity,std::setattr){ +Buff&Player::AddBuff(BuffType type,float duration,float intensity,std::setattr){ if(type==STAT_UP)std::for_each(attr.begin(),attr.end(),[](const ItemAttribute&attr){if(attr.ActualName()!="Health"&&attr.ActualName()!="Health %"&&attr.ActualName()!="Attack"&&attr.ActualName()!="Attack %"&&attr.ActualName()!="Defense"&&attr.ActualName()!="Defense %"&&attr.ActualName()!="CDR"&&attr.ActualName()!="Move Spd %")ERR(std::format("WARNING! Stat Up Attribute type {} is NOT IMPLEMENTED!",attr.ActualName()));}); - Buff&newBuff{buffList.emplace_back(this,type,duration,intensity,attr)}; - OnBuffAdd(newBuff); + return buffsToBeAdded.emplace_back(this,type,duration,intensity,attr); } -void Player::AddBuff(BuffType type,float duration,float intensity,std::setattr){ +Buff&Player::AddBuff(BuffType type,float duration,float intensity,std::setattr){ if(type==STAT_UP)std::for_each(attr.begin(),attr.end(),[](const std::string&attr){if(attr!="Health"&&attr!="Health %"&&attr!="Attack"&&attr!="Attack %"&&attr!="Defense"&&attr!="Defense %"&&attr!="CDR"&&attr!="Move Spd %")ERR(std::format("WARNING! Stat Up Attribute type {} is NOT IMPLEMENTED!",attr));}); - Buff&newBuff{buffList.emplace_back(this,type,duration,intensity,attr)}; - OnBuffAdd(newBuff); + return buffsToBeAdded.emplace_back(this,type,duration,intensity,attr); } -void Player::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks){ - Buff&newBuff{buffList.emplace_back(this,type,overTimeType,duration,intensity,timeBetweenTicks)}; - OnBuffAdd(newBuff); +Buff&Player::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks){ + return buffsToBeAdded.emplace_back(this,type,overTimeType,duration,intensity,timeBetweenTicks); } -void Player::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){ - Buff&newBuff{buffList.emplace_back(this,type,overTimeType,duration,intensity,timeBetweenTicks,expireCallbackFunc)}; - OnBuffAdd(newBuff); +Buff&Player::AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){ + return buffsToBeAdded.emplace_back(this,type,overTimeType,duration,intensity,timeBetweenTicks,expireCallbackFunc); } -void Player::AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks){ - Buff&newBuff{buffList.emplace_back(this,type,restorationType,overTimeType,duration,intensity,timeBetweenTicks)}; - OnBuffAdd(newBuff); +Buff&Player::AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks){ + return buffsToBeAdded.emplace_back(this,type,restorationType,overTimeType,duration,intensity,timeBetweenTicks); } -void Player::AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){ - Buff&newBuff{buffList.emplace_back(this,type,restorationType,overTimeType,duration,intensity,timeBetweenTicks,expireCallbackFunc)}; - OnBuffAdd(newBuff); +Buff&Player::AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc){ + return buffsToBeAdded.emplace_back(this,type,restorationType,overTimeType,duration,intensity,timeBetweenTicks,expireCallbackFunc); } bool Player::OnUpperLevel(){ @@ -1200,7 +1195,7 @@ void Player::RemoveBuff(BuffType buff){ Buff&Player::GetOrAddBuff(BuffType buff,std::pairnewBuff){ if(GetBuffs(buff).size()>0)return EditBuff(buff,0); - else return buffList.emplace_back(this,buff,newBuff.first,newBuff.second); + else return AddBuff(buff,newBuff.first,newBuff.second); } Buff&Player::EditBuff(BuffType buff,size_t buffInd){ diff --git a/Adventures in Lestoria/Player.h b/Adventures in Lestoria/Player.h index e09c77b0..99e80c96 100644 --- a/Adventures in Lestoria/Player.h +++ b/Adventures in Lestoria/Player.h @@ -174,16 +174,16 @@ public: void ForceSetPos(vf2d pos); void SetState(State::State newState); - void AddBuff(BuffType type,float duration,float intensity); + Buff&AddBuff(BuffType type,float duration,float intensity); //NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!! - void AddBuff(BuffType type,float duration,float intensity,std::setattr); + Buff&AddBuff(BuffType type,float duration,float intensity,std::setattr); //NOTE: If adding a % increase stat, please use the percentage version! 100% = 1!! - void AddBuff(BuffType type,float duration,float intensity,std::setattr); - void AddBuff(BuffType type,float duration,float intensity,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); - void AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks); - void AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); - void AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks); - void AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); + Buff&AddBuff(BuffType type,float duration,float intensity,std::setattr); + Buff&AddBuff(BuffType type,float duration,float intensity,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); + Buff&AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks); + Buff&AddBuff(BuffRestorationType type,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); + Buff&AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks); + Buff&AddBuff(BuffType type,BuffRestorationType restorationType,BuffOverTimeType::BuffOverTimeType overTimeType,float duration,float intensity,float timeBetweenTicks,Buff::PlayerBuffExpireCallbackFunction expireCallbackFunc); const bool HasBuff(BuffType buff)const; const std::vectorGetBuffs(BuffType buff)const; const std::vectorGetStatBuffs(const std::vector&attr)const; @@ -435,6 +435,7 @@ private: std::optionaltestAimingLoc{}; vf2d addedVel{}; vf2d previousPos{pos}; + std::vectorbuffsToBeAdded{}; protected: const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; diff --git a/Adventures in Lestoria/RUN_STRATEGY.cpp b/Adventures in Lestoria/RUN_STRATEGY.cpp index 86b30834..fef25d79 100644 --- a/Adventures in Lestoria/RUN_STRATEGY.cpp +++ b/Adventures in Lestoria/RUN_STRATEGY.cpp @@ -78,6 +78,7 @@ void Monster::InitializeStrategies(){ STRATEGY_DATA.insert("Giant Octopus",Monster::STRATEGY::GIANT_OCTOPUS); STRATEGY_DATA.insert("Octopus Arm",Monster::STRATEGY::OCTOPUS_ARM); STRATEGY_DATA.insert("Ghost of Pirate Captain",Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN); + STRATEGY_DATA.insert("Pirate's Treasure",Monster::STRATEGY::PIRATES_TREASURE); STRATEGY_DATA.SetInitialized(); } diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index fcc7c031..e0de2e5c 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 3 #define VERSION_PATCH 0 -#define VERSION_BUILD 12036 +#define VERSION_BUILD 12049 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/Campaigns/Boss_3.tmx b/Adventures in Lestoria/assets/Campaigns/Boss_3.tmx index e95eb140..ec205b8c 100644 --- a/Adventures in Lestoria/assets/Campaigns/Boss_3.tmx +++ b/Adventures in Lestoria/assets/Campaigns/Boss_3.tmx @@ -1,5 +1,5 @@ - + @@ -575,5 +575,10 @@ + + + + + diff --git a/Adventures in Lestoria/assets/coin.png b/Adventures in Lestoria/assets/coin.png new file mode 100644 index 00000000..6d31dc8a Binary files /dev/null and b/Adventures in Lestoria/assets/coin.png differ diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index edb9e0a0..6f141e61 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -1344,5 +1344,16 @@ MonsterStrategy Ghost Saber Knockback Amt = 100 # Amount of pixels/sec the ghost saber circle expands outwards. Ghost Saber Expand Spd = 14px + + # What HP % the boss throws a coin at the player, applying a curse, and hiding from the player. + Curse Thresholds = 70%, 40%, 10% + # How much time before the curse starts dealing damage to the player + Curse Damage Wait Time = 10s + # How much % damage the curse does to the player every second. + Curse Damage = 1%/sec + } + Pirate's Treasure + { + Open Distance = 64px } } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/Monsters.txt b/Adventures in Lestoria/assets/config/Monsters.txt index 3f55a5a3..b3458575 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -1861,6 +1861,51 @@ Monsters # Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity # DROP[0] = Octopus Ring,100%,1,1 + Hurt Sound = Monster Hurt + Death Sound = Slime Dead + Walk Sound = Slime Walk + } + Pirate's Treasure + { + Health = 1 + Attack = 80 + + CollisionDmg = 0 + + Immovable = True + Invulnerable = True + + Fadeout = False + + Collision Radius = 64 + + MoveSpd = 0% + Size = 300% + + XP = 0 + + Strategy = Pirate's Treasure + + #Size of each animation frame + SheetFrameSize = 24,24 + + # Setting this to true means every four rows indicates one animation, the ordering of the directions is: NORTH, EAST, SOUTH, WEST + 4-Way Spritesheet = False + + Animations + { + # Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse,ReverseOneShot) + # Animations must be defined in the same order as they are in their sprite sheets + # The First Four animations must represent a standing, walking, attack, and death animation. Their names are up to the creator. + IDLE = 1, 1.0, OneShot + WALK = 1, 1.0, OneShot + SLASHING = 1, 1.0, OneShot + OPEN = 1, 1.0, OneShot + } + + # Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity + # DROP[0] = Octopus Ring,100%,1,1 + Hurt Sound = Monster Hurt Death Sound = Slime Dead Walk Sound = Slime Walk diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt index 74db792a..9db6a613 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -148,6 +148,7 @@ Images GFX_Ink = ink.png GFX_Cannonball = cannonball.png GFX_GhostDagger = ghost_dagger.png + GFX_Coin = coin.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 1086782a..811e645d 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/maps/Monster_Presets.tmx b/Adventures in Lestoria/assets/maps/Monster_Presets.tmx index 153dc7cd..84c9c95a 100644 --- a/Adventures in Lestoria/assets/maps/Monster_Presets.tmx +++ b/Adventures in Lestoria/assets/maps/Monster_Presets.tmx @@ -1,5 +1,5 @@ - + @@ -40,5 +40,6 @@ + diff --git a/Adventures in Lestoria/assets/maps/Monsters/Pirate's Treasure.tx b/Adventures in Lestoria/assets/maps/Monsters/Pirate's Treasure.tx new file mode 100644 index 00000000..bf90739e --- /dev/null +++ b/Adventures in Lestoria/assets/maps/Monsters/Pirate's Treasure.tx @@ -0,0 +1,5 @@ + + diff --git a/Adventures in Lestoria/assets/maps/monsters-tileset.png b/Adventures in Lestoria/assets/maps/monsters-tileset.png index 52164951..41c858a8 100644 Binary files a/Adventures in Lestoria/assets/maps/monsters-tileset.png and b/Adventures in Lestoria/assets/maps/monsters-tileset.png differ diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index e2d404a2..3fdc4706 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ