diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 5547dcf8..2ea9fc1c 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -839,6 +839,10 @@ + + + + @@ -852,6 +856,10 @@ + + + + @@ -1114,7 +1122,11 @@ - + + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 0bdaca48..9dcc64d0 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -1304,9 +1304,18 @@ Source Files\Bullet Types - + Source Files\Monster Strategies + + Source Files\Monster Strategies + + + Source Files\Effects + + + Source Files\Effects + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 8dfbf4be..33f7ea41 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -1069,7 +1069,7 @@ void AiL::RenderWorld(float fElapsedTime){ 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}; + const bool displayCoinSymbol{player->GetBuffs(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_COIN).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)))}; @@ -1098,7 +1098,7 @@ void AiL::RenderWorld(float fElapsedTime){ } 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}); + view.DrawRotatedDecal(pos+vf2d{0,(-player->GetZ()-16.f-sinf(PI*GetRunTime())*2.f)*(std::signbit(scale.y)?-1:1)},GFX["coin.png"].Decal(),0.f,GFX["coin.png"].Sprite()->Size()/2,{0.5f,0.5f}); } }; @@ -4490,6 +4490,9 @@ void AiL::ComputeModeColors(TilesetData&tileset){ void AiL::UpdateEntities(){ UpdateEffects(GetElapsedTime()); + for(std::shared_ptr&m:MONSTER_LIST){ + m->B(Attribute::COLLIDED_WITH_PLAYER)=false; + } GetPlayer()->Update(GetElapsedTime()); UpdateMonsters(); diff --git a/Adventures in Lestoria/Buff.h b/Adventures in Lestoria/Buff.h index fb28536b..39d4b859 100644 --- a/Adventures in Lestoria/Buff.h +++ b/Adventures in Lestoria/Buff.h @@ -66,7 +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_PRECURSE, //A coin icon appears above the player's head. + PIRATE_GHOST_CAPTAIN_CURSE_COIN, //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{ diff --git a/Adventures in Lestoria/CollectedCoinEffect.cpp b/Adventures in Lestoria/CollectedCoinEffect.cpp new file mode 100644 index 00000000..2398f140 --- /dev/null +++ b/Adventures in Lestoria/CollectedCoinEffect.cpp @@ -0,0 +1,57 @@ +#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 "Effect.h" +#include "AdventuresInLestoria.h" +#include "DEFINES.h" +#include "util.h" + +INCLUDE_game + +CollectCoinEffect::CollectCoinEffect(const Entity attachedTarget,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) + :Effect(attachedTarget.GetPos(),lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending),attachedTarget(attachedTarget),sizeFlipper(-1.f,1.f,lifetime*2.f),zRiser(0.f,coinArcRiseZAmt,1/lifetime){ +} +bool CollectCoinEffect::Update(float fElapsedTime){ + zRiser.Update(fElapsedTime); + pos=attachedTarget.GetPos()-vf2d{0,zRiser.get()}; //A hack, instead of using the built-in z (which would display a shadow, we instead manipulate the final position based on the zRiser output). + size.x=sizeFlipper.Update(fElapsedTime); + return Effect::Update(fElapsedTime); +} +void CollectCoinEffect::Draw(const Pixel blendCol)const{ + Effect::Draw(blendCol); +} \ No newline at end of file diff --git a/Adventures in Lestoria/Effect.cpp b/Adventures in Lestoria/Effect.cpp index 8788dd74..b37f9235 100644 --- a/Adventures in Lestoria/Effect.cpp +++ b/Adventures in Lestoria/Effect.cpp @@ -107,6 +107,10 @@ void Effect::_Draw()const{ } void Effect::Draw(const Pixel blendCol)const{ + if(GetZ()>0){ + vf2d shadowScale=vf2d{8*size.x/3.f,1}/std::max(1.f,GetZ()/24); + game->view.DrawDecal(pos-vf2d{3,3}*shadowScale/2+vf2d{0,6*size.x},GFX["circle.png"].Decal(),shadowScale,BLACK); + } game->view.DrawPartialRotatedDecal(pos-vf2d{0,GetZ()},GetFrame().GetSourceImage()->Decal(),rotation,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,size,blendCol); } diff --git a/Adventures in Lestoria/Effect.h b/Adventures in Lestoria/Effect.h index 1eea2bc6..5b2e9e54 100644 --- a/Adventures in Lestoria/Effect.h +++ b/Adventures in Lestoria/Effect.h @@ -41,6 +41,7 @@ All rights reserved. #include #include #include "Oscillator.h" +#include "Entity.h" class Monster; class Player; using HitList=std::unordered_set>; @@ -224,4 +225,24 @@ private: const float radius; const float inkSlowdownDuration; const float inkSlowdownAmount; +}; + +struct FlipCoinEffect:Effect{ + //cycleSpd is how long it takes to get from fully opaque to fully transparent, and back to fully opaque + FlipCoinEffect(OscillatorstartEndPos,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false); + virtual bool Update(float fElapsedTime)override; + virtual void Draw(const Pixel blendCol)const override; + OscillatorstartEndPos; + OscillatorsizeFlipper; + OscillatorzRiser; +}; + +struct CollectCoinEffect:Effect{ + //cycleSpd is how long it takes to get from fully opaque to fully transparent, and back to fully opaque + CollectCoinEffect(const Entity attachedTarget,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size=1.0f,float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false); + virtual bool Update(float fElapsedTime)override; + virtual void Draw(const Pixel blendCol)const override; + const Entity attachedTarget; + OscillatorsizeFlipper; + OscillatorzRiser; }; \ No newline at end of file diff --git a/Adventures in Lestoria/FlipCoinEffect.cpp b/Adventures in Lestoria/FlipCoinEffect.cpp new file mode 100644 index 00000000..b1c6caa9 --- /dev/null +++ b/Adventures in Lestoria/FlipCoinEffect.cpp @@ -0,0 +1,58 @@ +#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 "Effect.h" +#include "AdventuresInLestoria.h" +#include "DEFINES.h" +#include "util.h" + +INCLUDE_game + +FlipCoinEffect::FlipCoinEffect(OscillatorstartEndPos,float coinArcRiseZAmt,float lifetime,const std::string&imgFile,bool upperLevel,float size,float fadeout,vf2d spd,Pixel col,float rotation,float rotationSpd,bool additiveBlending) + :Effect(startEndPos.first,lifetime,imgFile,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending),startEndPos(startEndPos),sizeFlipper(-1.f,1.f,lifetime*2.f),zRiser(0.f,coinArcRiseZAmt,1/lifetime){ +} +bool FlipCoinEffect::Update(float fElapsedTime){ + zRiser.Update(fElapsedTime); + pos=startEndPos.Update(fElapsedTime); + size.y=sizeFlipper.Update(fElapsedTime); + z=zRiser.get(); + return Effect::Update(fElapsedTime); +} +void FlipCoinEffect::Draw(const Pixel blendCol)const{ + Effect::Draw(blendCol); +} \ No newline at end of file diff --git a/Adventures in Lestoria/GhostOfPirateCaptain.cpp b/Adventures in Lestoria/GhostOfPirateCaptain.cpp index b7b801d6..c3535a61 100644 --- a/Adventures in Lestoria/GhostOfPirateCaptain.cpp +++ b/Adventures in Lestoria/GhostOfPirateCaptain.cpp @@ -100,15 +100,25 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std } m.F(A::GHOST_SABER_TIMER)-=fElapsedTime; + m.F(A::LAST_COLLISION_TIMER)-=fElapsedTime; if(m.B(A::FIRST_WAVE_COMPLETE)){ - m.UpdateFacingDirection(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); - m.SetVelocity(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*100.f*m.GetMoveSpdMult()); - const float distToPlayer{util::distance(m.GetPos(),game->GetPlayer()->GetPos())}; - if(m.F(A::GHOST_SABER_TIMER)<=0.f&&distToPlayer<=ConfigPixels("Ghost Saber Activation Range")){ - m.F(A::GHOST_SABER_TIMER)=ConfigFloat("Ghost Saber Cooldown"); - const float playerToMonsterAngle{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y}; - CreateBullet(GhostSaber)(m.GetPos(),m.GetWeakPointer(),ConfigFloat("Ghost Saber Lifetime"),ConfigFloat("Ghost Saber Distance"),ConfigFloat("Ghost Saber Knockback Amt"),playerToMonsterAngle,ConfigFloat("Ghost Saber Radius"),ConfigFloat("Ghost Saber Expand Spd"),ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")))EndBullet; + if(m.F(A::LAST_COLLISION_TIMER)<=0.f){ + m.UpdateFacingDirection(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos())); + m.SetVelocity(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*100.f*m.GetMoveSpdMult()); + const float distToPlayer{util::distance(m.GetPos(),game->GetPlayer()->GetPos())}; + if(m.F(A::GHOST_SABER_TIMER)<=0.f&&distToPlayer<=ConfigPixels("Ghost Saber Activation Range")){ + m.F(A::GHOST_SABER_TIMER)=ConfigFloat("Ghost Saber Cooldown"); + const float playerToMonsterAngle{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y}; + CreateBullet(GhostSaber)(m.GetPos(),m.GetWeakPointer(),ConfigFloat("Ghost Saber Lifetime"),ConfigFloat("Ghost Saber Distance"),ConfigFloat("Ghost Saber Knockback Amt"),playerToMonsterAngle,ConfigFloat("Ghost Saber Radius"),ConfigFloat("Ghost Saber Expand Spd"),ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")))EndBullet; + } + } + if(m.B(A::COLLIDED_WITH_PLAYER)){ + m.PerformAnimation("SLASHING"); + m.F(A::GHOST_SABER_SLASH_ANIMATION_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration(); + m.F(A::LAST_COLLISION_TIMER)=ConfigFloat("Collision Recovery Time"); + m.I(A::PREVIOUS_PHASE)=PHASE(); + SETPHASE(GHOSTSABER_SLASH); } } @@ -119,6 +129,12 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std SHRAPNEL_SHOT, }; case INIT:{ + m.SetStrategyDeathFunction([](GameEvent&ev,Monster&m,const std::string&strategy){ + for(std::shared_ptr&m:MONSTER_LIST){ + if(m->IsAlive())m->_DealTrueDamage(m->GetHealth(),HurtFlag::NONE); + } + return false; + }); for(int i:std::ranges::iota_view(0U,PHASE_COUNT)){ const int cannonCycle{DATA.GetProperty("MonsterStrategy.Ghost of Pirate Captain.Cannon Cycle").GetInt(i)}; m.VEC(A::CANNON_PHASES).emplace_back(cannonCycle); @@ -183,8 +199,11 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std } 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.GetHealthRatio()<=ConfigFloatArr("Curse Thresholds",m.I(A::CURSE_THRESHOLD_ARRAY_IND))/100.f){ m.I(A::CURSE_THRESHOLD_ARRAY_IND)++; + m.F(A::TOSS_COIN_WAIT_TIMER)=ConfigFloat("Coin Toss Pause Time"); + m.V(A::TOSS_COIN_TARGET)=game->GetPlayer()->GetPos(); + game->AddEffect(std::make_unique(Oscillator{m.GetPos(),m.V(A::TOSS_COIN_TARGET),1.f/m.F(A::TOSS_COIN_WAIT_TIMER)/2.f},ConfigFloat("Coin Toss Rise Amount"),m.F(A::TOSS_COIN_WAIT_TIMER),"coin.png",m.OnUpperLevel(),3.f)); SETPHASE(TOSS_COIN); } }break; @@ -203,9 +222,15 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std } }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); + m.F(A::TOSS_COIN_WAIT_TIMER)-=fElapsedTime; + if(m.F(A::TOSS_COIN_WAIT_TIMER)<=0.f){ + const float curseDmgPctOverTime{ConfigFloat("Curse Damage")}; + game->GetPlayer()->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_PRECURSE,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); + }); + game->SpawnMonster(m.V(A::TOSS_COIN_TARGET),MONSTER_DATA["Pirate's Coin"],m.OnUpperLevel()); + SETPHASE(NORMAL); + } }break; } } \ No newline at end of file diff --git a/Adventures in Lestoria/HurtDamageInfo.h b/Adventures in Lestoria/HurtDamageInfo.h index d2f444fb..df0613b7 100644 --- a/Adventures in Lestoria/HurtDamageInfo.h +++ b/Adventures in Lestoria/HurtDamageInfo.h @@ -39,9 +39,10 @@ All rights reserved. namespace HurtFlag{ enum HurtFlag{ - NONE = 0b00, - PLAYER_ABILITY = 0b01, //Specifically use this flag for player auto attacks or Abilities only! (Identifies these attacks for the Trapper class) - DOT = 0b10, //Damage over time ability. + NONE = 0b000, + PLAYER_ABILITY = 0b001, //Specifically use this flag for player auto attacks or Abilities only! (Identifies these attacks for the Trapper class) + DOT = 0b010, //Damage over time ability. + NO_DAMAGE_NUMBER = 0b100, //This flag ensures no damage number appears. }; } diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 35681627..6f0567b3 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -787,8 +787,7 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da const Buff specialMarkBuff{GetBuffs(BuffType::SPECIAL_MARK)[0]}; if(game->GetPlayer()->HasEnchant("Opportunity Shot")&&specialMarkBuff.originalDuration-specialMarkBuff.duration<="Opportunity Shot"_ENC["MARK TRIGGER TIME"])Trapper::ability1.cooldown-="Opportunity Shot"_ENC["MARK COOLDOWN REDUCE"]; RemoveSpecialMarkStack(); - }else - if(GetMarkStacks()>0){ + }else if(GetMarkStacks()>0){ ApplyMarkEffects(); RemoveMarkStack(); } @@ -811,33 +810,35 @@ bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag da if(GetBuffs(BuffType::CURSE_OF_DEATH).size()>0)accumulatedCurseOfDeathDamage+=mod_dmg; - if(IsDOT){ - if(lastDotTimer>0){ - dotNumberPtr.get()->AddDamage(int(mod_dmg)); - }else{ - dotNumberPtr=std::make_shared(pos+vf2d{0,8.f},int(mod_dmg),false,DamageNumberType::DOT); - DAMAGENUMBER_LIST.push_back(dotNumberPtr); - } - lastDotTimer=0.05f; - }else{ - if(lastHitTimer>0){ - damageNumberPtr.get()->AddDamage(int(mod_dmg)); - damageNumberPtr.get()->SetPauseTimer(0.4f); - }else{ - damageNumberPtr=std::make_shared(pos,int(mod_dmg)); - DAMAGENUMBER_LIST.push_back(damageNumberPtr); - } - #pragma region Change Label based on bonus conditions (Crit/Backstab) - if(crit){ - damageNumberPtr.get()->SetType(DamageNumberType::CRIT); + if(!(hurtFlags&HurtFlag::NO_DAMAGE_NUMBER)){ + if(IsDOT){ + if(lastDotTimer>0){ + dotNumberPtr.get()->AddDamage(int(mod_dmg)); + }else{ + dotNumberPtr=std::make_shared(pos+vf2d{0,8.f},int(mod_dmg),false,DamageNumberType::DOT); + DAMAGENUMBER_LIST.push_back(dotNumberPtr); } - if(damageNumberPtr.get()->GetType()==DamageNumberType::CRIT)goto doneChangingDamageNumberColors; //Crit number priority. Other colors should not apply if the crit has applied. - if(backstabOccurred){ - damageNumberPtr.get()->SetType(DamageNumberType::BACKSTAB); + lastDotTimer=0.05f; + }else{ + if(lastHitTimer>0){ + damageNumberPtr.get()->AddDamage(int(mod_dmg)); + damageNumberPtr.get()->SetPauseTimer(0.4f); + }else{ + damageNumberPtr=std::make_shared(pos,int(mod_dmg)); + DAMAGENUMBER_LIST.push_back(damageNumberPtr); } - doneChangingDamageNumberColors: - #pragma endregion - lastHitTimer=0.05f; + #pragma region Change Label based on bonus conditions (Crit/Backstab) + if(crit){ + damageNumberPtr.get()->SetType(DamageNumberType::CRIT); + } + if(damageNumberPtr.get()->GetType()==DamageNumberType::CRIT)goto doneChangingDamageNumberColors; //Crit number priority. Other colors should not apply if the crit has applied. + if(backstabOccurred){ + damageNumberPtr.get()->SetType(DamageNumberType::BACKSTAB); + } + doneChangingDamageNumberColors: + #pragma endregion + lastHitTimer=0.05f; + } } attackedByPlayer=true; diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index faf26430..e2a1dec9 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -393,6 +393,7 @@ private: 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); + static void PIRATES_COIN(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 bc389698..46d2e4ad 100644 --- a/Adventures in Lestoria/MonsterAttribute.h +++ b/Adventures in Lestoria/MonsterAttribute.h @@ -176,4 +176,8 @@ enum class Attribute{ 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? + TOSS_COIN_WAIT_TIMER, + TOSS_COIN_TARGET, + LAST_COLLISION_TIMER, + }; \ No newline at end of file diff --git a/Adventures in Lestoria/Oscillator.h b/Adventures in Lestoria/Oscillator.h index e65fbbfb..9d10a9c0 100644 --- a/Adventures in Lestoria/Oscillator.h +++ b/Adventures in Lestoria/Oscillator.h @@ -43,12 +43,12 @@ template class Oscillator{ friend struct ThrownProjectile; public: - inline Oscillator() - :Oscillator({},{},0.f){} + inline Oscillator():Oscillator({},{},0.f){} + inline Oscillator(std::pairvals,float cycleSpd):Oscillator(vals.first,vals.second,cycleSpd){} inline Oscillator(T val1,T val2,float cycleSpd) :val1(val1),val2(val2),first(this->val1),second(this->val2),cycleSpd(cycleSpd),currentVal(val1){} inline const T&Update(const float fElapsedTime){ - currentVal=util::lerp(val1,val2,sin(PI*timer*cycleSpd)/2+0.5f); + currentVal=util::lerp(val1,val2,sin(2*PI*timer*cycleSpd-PI/2)/2+0.5); timer+=fElapsedTime; return get(); } diff --git a/Adventures in Lestoria/Pirates_Coin.cpp b/Adventures in Lestoria/Pirates_Coin.cpp new file mode 100644 index 00000000..001f8b56 --- /dev/null +++ b/Adventures in Lestoria/Pirates_Coin.cpp @@ -0,0 +1,55 @@ +#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 "Entity.h" + +using A=Attribute; + +INCLUDE_game + +void Monster::STRATEGY::PIRATES_COIN(Monster&m,float fElapsedTime,std::string strategy){ + if(m.B(Attribute::COLLIDED_WITH_PLAYER)){ + game->AddEffect(std::make_unique(game->GetPlayer(),ConfigFloat("Coin Collect Rise Amount"),ConfigFloat("Coin Rise Timer"),"coin.png",m.OnUpperLevel()),true); + game->GetPlayer()->AddBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_COIN,INFINITY,1); + m._DealTrueDamage(m.GetHealth(),HurtFlag::NO_DAMAGE_NUMBER); + m.SetLifetime(0.f); + m.SetSize(0.f,false); + } +} \ No newline at end of file diff --git a/Adventures in Lestoria/PiratesTreasure.cpp b/Adventures in Lestoria/Pirates_Treasure.cpp similarity index 91% rename from Adventures in Lestoria/PiratesTreasure.cpp rename to Adventures in Lestoria/Pirates_Treasure.cpp index 9333e164..39179a23 100644 --- a/Adventures in Lestoria/PiratesTreasure.cpp +++ b/Adventures in Lestoria/Pirates_Treasure.cpp @@ -45,8 +45,9 @@ void Monster::STRATEGY::PIRATES_TREASURE(Monster&m,float fElapsedTime,std::strin 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); + if(m.B(Attribute::COLLIDED_WITH_PLAYER)&&game->GetPlayer()->HasBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_COIN)){ + game->GetPlayer()->RemoveBuff(BuffType::PIRATE_GHOST_CAPTAIN_PRECURSE); + game->GetPlayer()->RemoveBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_COIN); game->GetPlayer()->RemoveBuff(BuffType::PIRATE_GHOST_CAPTAIN_CURSE_DOT); } } \ No newline at end of file diff --git a/Adventures in Lestoria/RUN_STRATEGY.cpp b/Adventures in Lestoria/RUN_STRATEGY.cpp index fef25d79..c5a0a3fb 100644 --- a/Adventures in Lestoria/RUN_STRATEGY.cpp +++ b/Adventures in Lestoria/RUN_STRATEGY.cpp @@ -79,6 +79,7 @@ void Monster::InitializeStrategies(){ 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.insert("Pirate's Coin",Monster::STRATEGY::PIRATES_COIN); STRATEGY_DATA.SetInitialized(); } diff --git a/Adventures in Lestoria/Ursule.cpp b/Adventures in Lestoria/Ursule.cpp index bf2085ea..9bbb00d7 100644 --- a/Adventures in Lestoria/Ursule.cpp +++ b/Adventures in Lestoria/Ursule.cpp @@ -305,7 +305,6 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy m.F(A::CASTING_TIMER)=ConfigFloat("Phase 3.Charge Cast Time"); m.UpdateFacingDirection(geom2d::line(m.GetPos(),game->GetPlayer()->GetPos()).vector()); m.PerformAnimation("CHARGE"); - m.B(A::COLLIDED_WITH_PLAYER)=false; } } if(m.F(A::CASTING_TIMER)>0.f){ diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index e0de2e5c..87e53357 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 12049 +#define VERSION_BUILD 12094 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 6f141e61..0c974b58 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -1304,6 +1304,9 @@ MonsterStrategy # Repeats at end of cycle. Cannon Cycle = 0,0,0,0,0,0,0,0,1,2,1 + # How much time to stand still before moving towards the player again. + Collision Recovery Time = 3s + Cannon Shot Delay = 0.4s Cannon Shot Impact Time = 2.5s Silence Time = 2s @@ -1340,7 +1343,7 @@ MonsterStrategy Ghost Saber Distance = 48px Ghost Saber Radius = 8px Ghost Saber Damage = 75 - Ghost Saber Rotation Spd = 210deg/s + Ghost Saber Rotation Spd = 140deg/s Ghost Saber Knockback Amt = 100 # Amount of pixels/sec the ghost saber circle expands outwards. Ghost Saber Expand Spd = 14px @@ -1351,9 +1354,20 @@ MonsterStrategy Curse Damage Wait Time = 10s # How much % damage the curse does to the player every second. Curse Damage = 1%/sec + # How much time to pause before moving again. + Coin Toss Pause Time = 2s + # Highest Z position the coin is tossed up. + Coin Toss Rise Amount = 72px } Pirate's Treasure { Open Distance = 64px } + Pirate's Coin + { + # How much time the coin rises. + Coin Rise Timer = 2s + # Highest Z position the collected coin icon appears at. + Coin Collect Rise Amount = 36px + } } \ 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 b3458575..db3b4ed2 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -1906,6 +1906,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 Coin + { + Health = 1 + Attack = 80 + + CollisionDmg = 0 + + Immovable = True + Invulnerable = True + + Fadeout = False + + Collision Radius = 64 + + MoveSpd = 0% + Size = 300% + + XP = 0 + + Strategy = Pirate's Coin + + #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/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak index 811e645d..6ee32347 100644 Binary files a/Adventures in Lestoria/assets/gamepack.pak and b/Adventures in Lestoria/assets/gamepack.pak differ diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 3fdc4706..72952115 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ