diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index f2fc73c0..fbc3dcfc 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -408,6 +408,10 @@ + + + + @@ -836,6 +840,10 @@ + + + + @@ -1086,6 +1094,10 @@ + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 5f4a2ef0..a477eff6 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -711,6 +711,9 @@ Header Files\Utils + + Header Files\Utils + @@ -1274,6 +1277,12 @@ Source Files\Bullet Types + + Source Files\Utils + + + Source Files\Bullet Types + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 8fa6ab7c..17fd5ae4 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -100,7 +100,7 @@ safemapANIMATION_DATA; std::vector>MONSTER_LIST; std::unordered_mapSPAWNER_LIST; std::vector>DAMAGENUMBER_LIST; -std::vector>BULLET_LIST; +std::vector>BULLET_LIST{MAX_BULLETS}; std::optional>SPAWNER_CONTROLLER; safemapGFX; utils::datafile DATA; @@ -816,6 +816,7 @@ void AiL::UpdateBullets(float fElapsedTime){ IBullet*b=(*it).get(); b->_Update(fElapsedTime); } + if(BULLET_LIST.size()>MAX_BULLETS)ERR(std::format("WARNING! Bullet list reached greater than maximum expected size: {}. Consider expanding!!!",MAX_BULLETS)); std::erase_if(BULLET_LIST,[](std::unique_ptr&b){return b->IsDead();}); } diff --git a/Adventures in Lestoria/Animation.cpp b/Adventures in Lestoria/Animation.cpp index 2b51a013..47b2181c 100644 --- a/Adventures in Lestoria/Animation.cpp +++ b/Adventures in Lestoria/Animation.cpp @@ -465,6 +465,8 @@ void sig::Animation::InitializeAnimations(){ CreateHorizontalAnimationSequence("portal.png",8,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}}); + CreateHorizontalAnimationSequence("burstbullet.png",4,{24,24},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::OneShot}}); + //!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //DO NOT CREATE MORE ANIMATION SEQUENCES UNDERNEATH HERE AS THE NEXT BLOCK WILL CREATE DEFAULT ANIMATIONS //FOR ALL NON-SPECIFIED ANIMATION SEQUENCES! diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h index c61c0249..b40f7fa4 100644 --- a/Adventures in Lestoria/BulletTypes.h +++ b/Adventures in Lestoria/BulletTypes.h @@ -40,6 +40,8 @@ All rights reserved. #include "Direction.h" #include "Effect.h" #include "TrailEffect.h" +#include "Entity.h" +#include struct EnergyBolt:public Bullet{ float lastParticleSpawn=0; @@ -383,11 +385,33 @@ private: }; struct BurstBullet:public Bullet{ - BurstBullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col); + BurstBullet(vf2d pos,vf2d vel,const Entity target,const float explodeDist,const int extraBulletCount,const float extraBulletHeadingAngleChange,const float extraBulletRadius,const vf2d extraBulletScale,const float extraBulletStartSpeed,const float extraBulletAcc,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale); + void Update(float fElapsedTime)override; 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); private: - const float bounceExplodeRadius; - int additionalBounceCount; + void Explode(); + const Entity target; + const float explodeDist; + OscillatorscaleOscillator; + bool activated{false}; + float explodeTimer{0.f}; + const int extraBulletCount; + const float extraBulletHeadingAngleChange; //In radians. + const float extraBulletRadius; + const vf2d extraBulletScale; + const float extraBulletAcc; + const float extraBulletStartSpeed; +}; + +struct RotateBullet:public Bullet{ + //headingAngleChange determines how the startingAngle adjusts over time (in radians). + RotateBullet(vf2d pos,float startingAng,float startSpeed,float acc,float headingAngleChange,float radius,int damage,bool upperLevel,bool friendly,Pixel col); + void Update(float fElapsedTime)override; + 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); +private: + const float headingAngleChange; }; \ No newline at end of file diff --git a/Adventures in Lestoria/BurstBullet.cpp b/Adventures in Lestoria/BurstBullet.cpp index 01c16283..d336ba78 100644 --- a/Adventures in Lestoria/BurstBullet.cpp +++ b/Adventures in Lestoria/BurstBullet.cpp @@ -42,69 +42,61 @@ All rights reserved. #include "util.h" #include "SoundEffect.h" #include "TrailEffect.h" +#include INCLUDE_game INCLUDE_MONSTER_LIST +INCLUDE_ANIMATION_DATA -BurstBullet::BurstBullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col) - :Bullet(pos,vel,radius,damage,"energy_bolt.png",upperLevel,false,INFINITE,true,friendly,col){ - if(game->GetPlayer()->HasEnchant("Trail of Fire"))flameTrail=dynamic_cast(game->AddEffect(std::make_unique(pos,"Trail of Fire"_ENC["TRAIL DURATION"],"FlamesTexture.png","Trail of Fire"_ENC["TRAIL DAMAGE"]/100.f*game->GetPlayer()->GetAttack(),"Trail of Fire"_ENC["TRAIL TICK FREQUENCY"],upperLevel,1.f,vf2d{1.f,2.f},30000.f,Oscillator{{255,0,0,128},Pixel(0xE74F30),2.f},EffectType::TRAIL_OF_FIRE,true),true)); +BurstBullet::BurstBullet(vf2d pos,vf2d vel,const Entity target,const float explodeDist,const int extraBulletCount,const float extraBulletHeadingAngleChange,const float extraBulletRadius,const vf2d extraBulletScale,const float extraBulletStartSpeed,const float extraBulletAcc,float radius,int damage,bool upperLevel,bool friendly,Pixel col,vf2d scale) + :Bullet(pos,vel,radius,damage,"burstrotatebullet.png",upperLevel,false,INFINITE,true,friendly,col,scale),extraBulletStartSpeed(extraBulletStartSpeed),extraBulletAcc(extraBulletAcc),extraBulletRadius(extraBulletRadius),extraBulletScale(extraBulletScale),extraBulletHeadingAngleChange(extraBulletHeadingAngleChange),extraBulletCount(extraBulletCount),target(target),explodeDist(explodeDist),scaleOscillator({0.85f,0.85f},{1.f,1.f},0.3f){ + animation.mult=0.f; //Prevent animating. } -void BurstBullet::Update(float fElapsedTime){ - lastParticleSpawn=std::max(0.f,lastParticleSpawn-fElapsedTime); - if(lastParticleSpawn==0){ - lastParticleSpawn="Wizard.Ability 1.ParticleFrequency"_F; - game->AddEffect(std::make_unique(pos,"Wizard.Ability 1.ParticleLifetimeRange"_FRange,"energy_particle.png",upperLevel,"Wizard.Ability 1.ParticleSizeRange"_FRange,"Wizard.Ability 1.ParticleFadeoutTime"_F,vf2d{"Wizard.Ability 1.ParticleXSpeedRange"_FRange,"Wizard.Ability 1.ParticleYSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.ParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.ParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.ParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.ParticleAlphaRange"_FRange)})); - } - if(distanceTraveled>"Wizard.Ability 1.Max Range"_F&&IsActivated()){ - fadeOutTime="Wizard.Ability 1.BulletHitFadeoutTime"_F; - for(int i=0;i<"Wizard.Ability 1.BulletHitExplosionParticleCount"_I;i++){ - game->AddEffect(std::make_unique(pos,"Wizard.Ability 1.BulletHitExplosionParticleLifetimeRange"_FRange,"circle.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionParticleSizeRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleFadeoutTimeRange"_FRange,vf2d{"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.BulletHitExplosionParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleAlphaRange"_FRange)})); +void BurstBullet::Explode(){ + fadeOutTime=0.1f; + HurtType targets{friendly?HurtType::MONSTER:HurtType::PLAYER}; + for(const auto&[target,isHurt]:game->Hurt(pos,radius,damage,OnUpperLevel(),GetZ(),targets)){ + if(isHurt){ + Entity ent{target}; + ent.SetIframeTime(0.3f); } - game->SetupWorldShake("Wizard.Ability 1.WorldShakeTime"_F); - if(friendly){ - game->Hurt(pos,"Wizard.Ability 1.BulletHitExplosionRange"_F/100*12,int("Wizard.Ability 1.BulletHitExplosionDamageMult"_F*game->GetPlayer()->GetAttack()),OnUpperLevel(),0,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY); - }else{ - if(geom2d::overlaps(geom2d::circle{pos,"Wizard.Ability 1.BulletHitExplosionRange"_F/100*12},geom2d::circle{game->GetPlayer()->GetPos(),12.f})){ - game->GetPlayer()->Hurt(damage,OnUpperLevel(),0.f); + } + SoundEffect::PlaySFX("Burst Bullet Explode",pos); + for(int i:std::ranges::iota_view(0,extraBulletCount)){ + const float angle{(360.f/extraBulletCount)*i}; + const vf2d newPos{pos+vf2d{radius/2.f,angle}.cart()}; + CreateBullet(RotateBullet)(newPos,angle,extraBulletStartSpeed,extraBulletAcc,extraBulletHeadingAngleChange,extraBulletRadius,damage,OnUpperLevel(),friendly,WHITE)EndBullet; + } + Deactivate(); +} + +void BurstBullet::Update(float fElapsedTime){ + if(IsActivated()){ + if(!activated){ + scaleOscillator.Update(fElapsedTime); + if(util::distance(target.GetPos(),pos)<=explodeDist){ + activated=true; + animation.mult=1.f; + explodeTimer=ANIMATION_DATA[animation.currentStateName].GetTotalAnimationDuration(); } + }else if(explodeTimer>0.f){ + explodeTimer-=fElapsedTime; + if(explodeTimer<=0.f)Explode(); } - game->AddEffect(std::make_unique(pos,0,"splash_effect.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionRange"_F/100*2,"Wizard.Ability 1.BulletHitExplosionFadeoutTime"_F,vf2d{},"Wizard.Ability 1.BulletHitExplosionColor"_Pixel)); - - SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos); } - - if(flameTrail)flameTrail.value().get().SetEndPos(pos); } -BulletDestroyState BurstBullet::PlayerHit(Player*player) -{ - fadeOutTime="Wizard.Ability 1.BulletHitFadeoutTime"_F; - for(int i=0;i<"Wizard.Ability 1.BulletHitExplosionParticleCount"_I;i++){ - game->AddEffect(std::make_unique(player->GetPos(),"Wizard.Ability 1.BulletHitExplosionParticleLifetimeRange"_FRange,"circle.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionParticleSizeRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleFadeoutTimeRange"_FRange,vf2d{"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.BulletHitExplosionParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleAlphaRange"_FRange)})); - - game->SetupWorldShake("Wizard.Ability 1.WorldShakeTime"_F);} - game->AddEffect(std::make_unique(player->GetPos(),0,"splash_effect.png",upperLevel,5,0.25,vf2d{},Pixel{240,120,60})); - - SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos); +BulletDestroyState BurstBullet::PlayerHit(Player*player){ + Explode(); return BulletDestroyState::KEEP_ALIVE; } -BulletDestroyState BurstBullet::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit) -{ - fadeOutTime="Wizard.Ability 1.BulletHitFadeoutTime"_F; - for(int i=0;i<"Wizard.Ability 1.BulletHitExplosionParticleCount"_I;i++){ - game->AddEffect(std::make_unique(monster.GetPos(),"Wizard.Ability 1.BulletHitExplosionParticleLifetimeRange"_FRange,"circle.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionParticleSizeRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleFadeoutTimeRange"_FRange,vf2d{"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange,"Wizard.Ability 1.BulletHitExplosionParticleSpeedRange"_FRange},Pixel{uint8_t("Wizard.Ability 1.BulletHitExplosionParticleRedRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleGreenRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleBlueRange"_FRange),uint8_t("Wizard.Ability 1.BulletHitExplosionParticleAlphaRange"_FRange)})); - } - game->SetupWorldShake("Wizard.Ability 1.WorldShakeTime"_F); - game->Hurt(monster.GetPos(),"Wizard.Ability 1.BulletHitExplosionRange"_F/100*12,int("Wizard.Ability 1.BulletHitExplosionDamageMult"_F*game->GetPlayer()->GetAttack()),OnUpperLevel(),0,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY); - game->AddEffect(std::make_unique(monster.GetPos(),0,"splash_effect.png",upperLevel,"Wizard.Ability 1.BulletHitExplosionRange"_F/100*2,"Wizard.Ability 1.BulletHitExplosionFadeoutTime"_F,vf2d{},"Wizard.Ability 1.BulletHitExplosionColor"_Pixel)); - - SoundEffect::PlaySFX("Wizard Fire Bolt Hit",pos); +BulletDestroyState BurstBullet::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ + Explode(); return BulletDestroyState::KEEP_ALIVE; } void BurstBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){ - if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY; + data.damage=0; //Nullify the damage because proximity damage will occur instead. } \ No newline at end of file diff --git a/Adventures in Lestoria/DEFINES.h b/Adventures in Lestoria/DEFINES.h index 273591ac..f11f5ec1 100644 --- a/Adventures in Lestoria/DEFINES.h +++ b/Adventures in Lestoria/DEFINES.h @@ -81,6 +81,8 @@ using MonsterSpawnerID=int; #undef INFINITE #define INFINITE 999999 +#define MAX_BULLETS size_t(512) + #define SETUP_CLASS(class) \ class::class() \ :Player::Player(){} \ diff --git a/Adventures in Lestoria/Entity.cpp b/Adventures in Lestoria/Entity.cpp new file mode 100644 index 00000000..42af04b7 --- /dev/null +++ b/Adventures in Lestoria/Entity.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 "Entity.h" +#include "Player.h" + +#define is(type) std::holds_alternative(entity) +#define get(type) std::get(entity) + +Entity::Entity(Player*player):entity(player){} +Entity::Entity(Monster*monster):entity(monster){} +Entity::Entity(const std::variantent):entity(ent){} + +const vf2d Entity::GetPos()const{ + if(is(Player*))return get(Player*)->GetPos(); + if(is(Monster*))return get(Monster*)->GetPos(); +} + +void Entity::SetIframeTime(const float iframeTime){ + if(is(Player*))get(Player*)->ApplyIframes(iframeTime); + else if(is(Monster*))get(Monster*)->ApplyIframes(iframeTime); +} \ No newline at end of file diff --git a/Adventures in Lestoria/Entity.h b/Adventures in Lestoria/Entity.h new file mode 100644 index 00000000..afe5e0b3 --- /dev/null +++ b/Adventures in Lestoria/Entity.h @@ -0,0 +1,56 @@ +#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 +#pragma once + +#include "olcUTIL_Geometry2D.h" +#include + +class Player; +class Monster; + +//A helper class to connect multiple entity types based on their commonalities without using inheritance shenanigans. +class Entity{ +public: + Entity(Player*player); + Entity(Monster*monster); + Entity(const std::variantent); + const vf2d GetPos()const; + void SetIframeTime(const float iframeTime); +private: + const std::variantentity; +}; \ No newline at end of file diff --git a/Adventures in Lestoria/GiantOctopus.cpp b/Adventures in Lestoria/GiantOctopus.cpp index 3721e335..ea37e076 100644 --- a/Adventures in Lestoria/GiantOctopus.cpp +++ b/Adventures in Lestoria/GiantOctopus.cpp @@ -135,12 +135,15 @@ void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string s } if(m.F(A::SHOOT_TIMER)<=0.f){ if(m.I(A::ATTACK_COUNT)>=ConfigInt("Big Bullet Frequency")-1){ - CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Big Bullet Speed"),ConfigFloat("Big Bullet Radius")*1.5f,ConfigFloat("Big Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Big Bullet Color"),{ConfigFloat("Big Bullet Radius"),ConfigFloat("Big Bullet Radius")})EndBullet; + CreateBullet(BurstBullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Big Bullet Speed"),game->GetPlayer(),ConfigPixels("Big Bullet Detection Radius"),ConfigInt("Big Bullet Extra Bullet Count"),util::degToRad(ConfigFloat("Big Bullet Extra Bullet Rotate Speed")),ConfigFloat("Big Bullet Extra Bullet Radius"),vf2d{ConfigFloatArr("Big Bullet Extra Bullet Image Scale",0),ConfigFloatArr("Big Bullet Extra Bullet Image Scale",1)},ConfigFloat("Big Bullet Extra Bullet Speed"),ConfigFloat("Big Bullet Extra Bullet Acceleration"),ConfigFloat("Big Bullet Radius"),ConfigInt("Big Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Big Bullet Color"),vf2d{ConfigFloat("Big Bullet Image Scale"),ConfigFloat("Big Bullet Image Scale")})EndBullet; + m.F(A::SHOOT_TIMER)=ConfigFloat("Big Bullet Boss Rest Time"); m.I(A::ATTACK_COUNT)=-1; - }else CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Bullet Color"),{ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Radius")})EndBullet; + }else{ + m.F(A::SHOOT_TIMER)=ConfigFloat("Shoot Frequency"); + CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),game->GetPlayer()->GetPos())*ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Damage"),m.OnUpperLevel(),false,ConfigPixel("Bullet Color"),{ConfigFloat("Bullet Radius"),ConfigFloat("Bullet Radius")})EndBullet; + } m.PerformShootAnimation(); - m.F(A::SHOOT_TIMER)=ConfigFloat("Shoot Frequency"); m.I(A::ATTACK_COUNT)++; } }break; diff --git a/Adventures in Lestoria/IBullet.cpp b/Adventures in Lestoria/IBullet.cpp index 2d84da86..317a3736 100644 --- a/Adventures in Lestoria/IBullet.cpp +++ b/Adventures in Lestoria/IBullet.cpp @@ -143,6 +143,7 @@ void IBullet::_Update(const float fElapsedTime){ while(iterations>0){ iterations--; if(IsPlayerAutoAttackProjectile()){pos+=(game->GetWindSpeed()*game->GetElapsedTime())/float(totalIterations);} + vel=vf2d{vel.polar().x+(acc*fElapsedTime/float(totalIterations)),vel.polar().y}.cart(); pos+=(vel*fElapsedTime)/float(totalIterations); if(!CollisionCheck()){ goto DeadBulletCheck; @@ -154,6 +155,7 @@ void IBullet::_Update(const float fElapsedTime){ } }else{ if(IsPlayerAutoAttackProjectile()){pos+=game->GetWindSpeed()*game->GetElapsedTime();} + vel=vf2d{vel.polar().x+acc*fElapsedTime,vel.polar().y}.cart(); pos+=vel*fElapsedTime; } diff --git a/Adventures in Lestoria/IBullet.h b/Adventures in Lestoria/IBullet.h index 36428477..1da0e480 100644 --- a/Adventures in Lestoria/IBullet.h +++ b/Adventures in Lestoria/IBullet.h @@ -60,6 +60,7 @@ enum class BulletDestroyState{ struct IBullet{ friend class AiL; + float acc{}; vf2d vel; vf2d pos; float radius; diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 42681564..885b033f 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -708,6 +708,7 @@ const bool Monster::AttackAvoided(const float attackZ)const{ } bool Monster::Hurt(HurtDamageInfo damageData){ + if(damageData.damage==0)return false; //Cancel the hit. return _Hurt(damageData.damage,damageData.onUpperLevel,damageData.z,damageData.damageRule,damageData.hurtFlags); } diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index 65b8bb0d..eb18fc1c 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -892,6 +892,7 @@ bool Player::HasIframes(){ } bool Player::Hurt(HurtDamageInfo damageData){ + if(damageData.damage==0)return false; //Cancel the hit. return Hurt(damageData.damage,damageData.onUpperLevel,damageData.z,damageData.damageRule,damageData.hurtFlags); } diff --git a/Adventures in Lestoria/RotateBullet.cpp b/Adventures in Lestoria/RotateBullet.cpp new file mode 100644 index 00000000..3972abfe --- /dev/null +++ b/Adventures in Lestoria/RotateBullet.cpp @@ -0,0 +1,65 @@ +#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 "BulletTypes.h" + + +RotateBullet::RotateBullet(vf2d pos,float startingAng,float startSpeed,float acc,float headingAngleChange,float radius,int damage,bool upperLevel,bool friendly,Pixel col) + :Bullet(pos,vf2d{startSpeed,startingAng}.cart(),radius,damage,"burstrotatebullet.png",upperLevel,false,INFINITE,true,friendly,col),headingAngleChange(headingAngleChange){ + this->acc=acc; +} + +void RotateBullet::Update(float fElapsedTime){ + const float currentAng{vel.polar().y}; + const float newAng{currentAng+headingAngleChange*fElapsedTime}; + vel=vf2d{vel.polar().x,newAng}.cart(); +} + +BulletDestroyState RotateBullet::PlayerHit(Player*player){ + fadeOutTime=0.2f; + Deactivate(); + return BulletDestroyState::KEEP_ALIVE; +} + +BulletDestroyState RotateBullet::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){ + fadeOutTime=0.2f; + Deactivate(); + return BulletDestroyState::KEEP_ALIVE; +} + +void RotateBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){} \ No newline at end of file diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 2ac6ed55..e776c225 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 11871 +#define VERSION_BUILD 11878 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/burstbullet.png b/Adventures in Lestoria/assets/burstbullet.png new file mode 100644 index 00000000..3d1ebe1b Binary files /dev/null and b/Adventures in Lestoria/assets/burstbullet.png differ diff --git a/Adventures in Lestoria/assets/burstrotatebullet.png b/Adventures in Lestoria/assets/burstrotatebullet.png new file mode 100644 index 00000000..74beb97f Binary files /dev/null and b/Adventures in Lestoria/assets/burstrotatebullet.png differ diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 3ebdd2f9..b51f53ee 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -1262,9 +1262,17 @@ MonsterStrategy # Number of shots when a big bullet comes out instead. Big Bullet Frequency = 6shots Big Bullet Speed = 250 - Big Bullet Radius = 12px + Big Bullet Radius = 18px + Big Bullet Detection Radius = 200 Big Bullet Damage = 90 + # xscale, yscale + Big Bullet Image Scale = 2,2 Big Bullet Extra Bullet Count = 10 + Big Bullet Extra Bullet Radius = 8px + Big Bullet Extra Bullet Rotate Speed = 40deg/sec + Big Bullet Extra Bullet Image Scale = 1,1 + Big Bullet Extra Bullet Speed = 120 + Big Bullet Extra Bullet Acceleration = 50 Big Bullet Boss Rest Time = 2s Big Bullet Color = 255r,0g,0b,255a } diff --git a/Adventures in Lestoria/assets/config/audio/events.txt b/Adventures in Lestoria/assets/config/audio/events.txt index 821ffafe..7b2d6b5c 100644 --- a/Adventures in Lestoria/assets/config/audio/events.txt +++ b/Adventures in Lestoria/assets/config/audio/events.txt @@ -69,6 +69,11 @@ Events # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) File[0] = burning.ogg, 100% } + Burst Bullet Explode + { + # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) + File[0] = burstexplode.ogg, 100% + } Button Click { # Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%) diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt index a7f8fcbe..38f2fa70 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -141,6 +141,8 @@ Images GFX_SandSuction = sand_suction.png GFX_Bomb = bomb.png GFX_Molotov = molotov.png + GFX_BurstBullet = burstbullet.png + GFX_BurstRotateBullet = burstrotatebullet.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 92996dc2..688f9bce 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/sounds/burstexplode.ogg b/Adventures in Lestoria/assets/sounds/burstexplode.ogg new file mode 100644 index 00000000..3faf7e10 Binary files /dev/null and b/Adventures in Lestoria/assets/sounds/burstexplode.ogg differ diff --git a/Adventures in Lestoria/olcUTIL_Animate2D.h b/Adventures in Lestoria/olcUTIL_Animate2D.h index de8bbb49..6452d11e 100644 --- a/Adventures in Lestoria/olcUTIL_Animate2D.h +++ b/Adventures in Lestoria/olcUTIL_Animate2D.h @@ -225,7 +225,7 @@ namespace olc::utils::Animate2D { public: Animation() = default; - float mult = 1.f; + float mult = 1.f; //Speed multiplier of the animation. Adjust to change the speed the animation plays at. StatesEnum currentStateName; inline bool ChangeState(AnimationState& state, const StatesEnum& sStateName, const float frameMult) diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 9ab82037..b5730341 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ