diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
index cd62f091..99d0e85b 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
@@ -852,6 +852,10 @@
+
+
+
+
diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
index 2f30ab42..ae6acb9c 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
@@ -1301,6 +1301,9 @@
Source Files\Monster Strategies
+
+ Source Files\Bullet Types
+
diff --git a/Adventures in Lestoria/Animation.cpp b/Adventures in Lestoria/Animation.cpp
index 3bfeaeba..36779669 100644
--- a/Adventures in Lestoria/Animation.cpp
+++ b/Adventures in Lestoria/Animation.cpp
@@ -403,6 +403,7 @@ void sig::Animation::InitializeAnimations(){
CreateHorizontalAnimationSequence("large_tornado.png",4,{72,144},AnimationData{.frameDuration{0.1f},.style{Animate2D::Style::Repeat}});
CreateHorizontalAnimationSequence("sand_suction.png",4,{72,72},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::Repeat}});
CreateHorizontalAnimationSequence("bomb.png",4,{24,24},AnimationData{.frameDuration{0.2f},.style{Animate2D::Style::OneShot}});
+ CreateHorizontalAnimationSequence("ghost_dagger.png",3,{24,24},{0.1f,Animate2D::Style::PingPong});
CreateStillAnimation("meteor.png",{192,192});
diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h
index c1497bdb..d8bfb817 100644
--- a/Adventures in Lestoria/BulletTypes.h
+++ b/Adventures in Lestoria/BulletTypes.h
@@ -42,6 +42,7 @@ All rights reserved.
#include "TrailEffect.h"
#include "Entity.h"
#include
+#include
struct EnergyBolt:public Bullet{
float lastParticleSpawn=0;
@@ -441,4 +442,21 @@ private:
const Entity target;
const float rotateTowardsSpeed;
const float rotateTowardsSpeedCoveredInInk;
+};
+
+struct GhostSaber:public Bullet{
+ GhostSaber(const vf2d pos,const std::weak_ptrtarget,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const int damage,const bool upperLevel,const float rotSpd,const bool friendly=false,const Pixel col=WHITE,const vf2d scale={1,1},const float image_angle=0.f);
+ 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 std::weak_ptrattachedMonster;
+ const float rotSpd;
+ const float distFromTarget;
+ float rot;
+ const float knockbackAmt;
+ float particleTimer{};
+ float aliveTime{};
+ OscillatoralphaOscillator{128U,255U,0.6f};
};
\ No newline at end of file
diff --git a/Adventures in Lestoria/GhostOfPirateCaptain.cpp b/Adventures in Lestoria/GhostOfPirateCaptain.cpp
index f9108b81..e73942bd 100644
--- a/Adventures in Lestoria/GhostOfPirateCaptain.cpp
+++ b/Adventures in Lestoria/GhostOfPirateCaptain.cpp
@@ -77,6 +77,7 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
m.B(A::FIRST_WAVE_COMPLETE)=true;
m.ForceSetPos(m.spawnPos);
m.SetupAfterImage();
+ m.afterImagePos=m.GetPos();
m.SetCollisionRadius(0.f);
m.F(A::CASTING_TIMER)=1.f;
SETPHASE(AFTERIMAGE_FADEIN);
@@ -96,6 +97,17 @@ void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std
}
}
+ m.F(A::GHOST_SABER_TIMER)-=fElapsedTime;
+
+ if(m.B(A::FIRST_WAVE_COMPLETE)){
+ m.SetVelocity(util::pointTo(m.GetPos(),game->GetPlayer()->GetPos()));
+ if(m.F(A::GHOST_SABER_TIMER)<=0.f){
+ 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"),ConfigInt("Ghost Saber Damage"),m.OnUpperLevel(),util::degToRad(ConfigFloat("Ghost Saber Rotation Spd")))EndBullet;
+ }
+ }
+
switch(PHASE()){
enum CannonPhaseType{
CANNON_SHOT,
diff --git a/Adventures in Lestoria/GhostSaber.cpp b/Adventures in Lestoria/GhostSaber.cpp
new file mode 100644
index 00000000..20bee8df
--- /dev/null
+++ b/Adventures in Lestoria/GhostSaber.cpp
@@ -0,0 +1,75 @@
+#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"
+
+GhostSaber::GhostSaber(const vf2d pos,const std::weak_ptrtarget,const float lifetime,const float distFromTarget,const float knockbackAmt,const float initialRot,const float radius,const int damage,const bool upperLevel,const float rotSpd,const bool friendly,const Pixel col,const vf2d scale,const float image_angle)
+:Bullet(target.lock()->GetPos()+vf2d{distFromTarget,initialRot}.cart(),{},radius,damage,"ghost_dagger.png",upperLevel,false,INFINITE,false,friendly,col,scale,image_angle),attachedMonster(target),rotSpd(rotSpd),distFromTarget(distFromTarget),rot(initialRot),aliveTime(lifetime),knockbackAmt(knockbackAmt){}
+void GhostSaber::Update(float fElapsedTime){
+ alphaOscillator.Update(fElapsedTime);
+ particleTimer-=fElapsedTime;
+ aliveTime-=fElapsedTime;
+ if(particleTimer<=0.f){
+ particleTimer+=0.05f;
+ game->AddEffect(std::make_unique(pos,0.1f,0.1f,"pixel.png",2.f,vf2d{},Pixel{239,215,98,192},util::random(2*PI),0.f,true));
+ }
+ rot+=rotSpd*fElapsedTime;
+ if(!attachedMonster.expired()){
+ pos=attachedMonster.lock()->GetPos()+vf2d{distFromTarget,rot}.cart();
+ }
+ if(aliveTime<=0.f&&!IsDeactivated()){
+ Deactivate();
+ fadeOutTime=0.5f;
+ }
+ col.a=alphaOscillator.get();
+ image_angle=-rot*2;
+};
+
+BulletDestroyState GhostSaber::PlayerHit(Player*player){
+ player->ApplyIframes(0.2f);
+ player->Knockback(vf2d{knockbackAmt,rot}.cart());
+ return BulletDestroyState::KEEP_ALIVE;
+}
+
+BulletDestroyState GhostSaber::MonsterHit(Monster&monster,const uint8_t markStacksBeforeHit){
+ monster.ApplyIframes(0.2f);
+ monster.Knockback(vf2d{knockbackAmt,rot}.cart());
+ return BulletDestroyState::KEEP_ALIVE;
+}
+
+void GhostSaber::ModifyOutgoingDamageData(HurtDamageInfo&data){}
\ No newline at end of file
diff --git a/Adventures in Lestoria/MonsterAttribute.h b/Adventures in Lestoria/MonsterAttribute.h
index f25fe2ec..34d3d26d 100644
--- a/Adventures in Lestoria/MonsterAttribute.h
+++ b/Adventures in Lestoria/MonsterAttribute.h
@@ -173,4 +173,5 @@ enum class Attribute{
LINE_SHOT_ANG,
LAST_PLAYER_POS,
FIRST_WAVE_COMPLETE,
+ GHOST_SABER_TIMER,
};
\ No newline at end of file
diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt
index 7f1d8cd0..9b158eab 100644
--- a/Adventures in Lestoria/TODO.txt
+++ b/Adventures in Lestoria/TODO.txt
@@ -1,9 +1,10 @@
Ghost of Pirate Captain Boss Graphics
Ghost of Pirate Captain Boss Implementation
-Fix up Octopus Boss Facing Animations
Add attack sound effects for Octopus boss Fight
+On death and choosing to Try Again, the player should be given the Item Loadout screen in order to change strategies if they want to.
+
Arena
Chapter 5 wont have a bonus boss instead there will be the endless arena. During the campaign there will be a 5 or 10 waves easy introduction after chapter 5 is cleared the endless mode unlocks.
diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h
index 330e0cd7..779d31cf 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 12003
+#define VERSION_BUILD 12016
#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 b4ebf316..863fff02 100644
--- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt
+++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt
@@ -1332,5 +1332,14 @@ MonsterStrategy
Cannon Spell Circle Rotation Spd = -30
# Degrees/sec. Positive is CW, Negative is CCW.
Cannon Spell Insignia Rotation Spd = 50
+
+ Ghost Saber Cooldown = 4s
+ Ghost Saber Lifetime = 4s
+ # Distance from the boss the ghost sabers should spin around.
+ Ghost Saber Distance = 56px
+ Ghost Saber Radius = 8px
+ Ghost Saber Damage = 75
+ Ghost Saber Rotation Spd = 180deg/s
+ Ghost Saber Knockback Amt = 100
}
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt
index 2ed86532..74db792a 100644
--- a/Adventures in Lestoria/assets/config/gfx/gfx.txt
+++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt
@@ -147,6 +147,7 @@ Images
GFX_InkBubbleExplode = inkbubble_explode.png
GFX_Ink = ink.png
GFX_Cannonball = cannonball.png
+ GFX_GhostDagger = ghost_dagger.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 51632fc6..1086782a 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/ghost_dagger.png b/Adventures in Lestoria/assets/ghost_dagger.png
new file mode 100644
index 00000000..11a04089
Binary files /dev/null and b/Adventures in Lestoria/assets/ghost_dagger.png differ
diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe
index a30b7d61..47e26443 100644
Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ