diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
index bd120a85..f2fc73c0 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
@@ -834,6 +834,7 @@
+
diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
index ecf5023c..5f4a2ef0 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
@@ -1271,6 +1271,9 @@
Source Files\Utils
+
+ Source Files\Bullet Types
+
diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h
index 022bfd13..c61c0249 100644
--- a/Adventures in Lestoria/BulletTypes.h
+++ b/Adventures in Lestoria/BulletTypes.h
@@ -380,4 +380,14 @@ struct PoisonBottle:public ThrownProjectile{
private:
const float bounceExplodeRadius;
int additionalBounceCount;
+};
+
+struct BurstBullet:public Bullet{
+ BurstBullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly,Pixel col);
+ 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;
};
\ No newline at end of file
diff --git a/Adventures in Lestoria/BurstBullet.cpp b/Adventures in Lestoria/BurstBullet.cpp
new file mode 100644
index 00000000..01c16283
--- /dev/null
+++ b/Adventures in Lestoria/BurstBullet.cpp
@@ -0,0 +1,110 @@
+#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"
+#include "Effect.h"
+#include "AdventuresInLestoria.h"
+#include "DEFINES.h"
+#include "util.h"
+#include "SoundEffect.h"
+#include "TrailEffect.h"
+
+INCLUDE_game
+INCLUDE_MONSTER_LIST
+
+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));
+}
+
+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)}));
+ }
+ 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);
+ }
+ }
+ 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);
+ 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);
+ return BulletDestroyState::KEEP_ALIVE;
+}
+
+void BurstBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){
+ if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY;
+}
\ No newline at end of file
diff --git a/Adventures in Lestoria/GiantOctopus.cpp b/Adventures in Lestoria/GiantOctopus.cpp
index 1d683971..3721e335 100644
--- a/Adventures in Lestoria/GiantOctopus.cpp
+++ b/Adventures in Lestoria/GiantOctopus.cpp
@@ -134,7 +134,7 @@ void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string s
m.F(A::CASTING_TIMER)=util::random_range(ConfigFloatArr("Arm Move Timer",0),ConfigFloatArr("Arm Move Timer",1));
}
if(m.F(A::SHOOT_TIMER)<=0.f){
- if(m.I(A::ATTACK_COUNT)>=ConfigInt("Big Bullet Frequency")){
+ 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;
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;