Generalized FallingStone into FallingBullet to supply a custom image. Prepare graphics and add extra map items and basic AI structure for Chapter 3 boss. Release Build 11936.

master
sigonasr2 2 weeks ago
parent e893d3a54e
commit 9732c57983
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 5
      Adventures in Lestoria/BulletTypes.h
  4. 2
      Adventures in Lestoria/Effect.h
  5. 13
      Adventures in Lestoria/FallingStone.cpp
  6. 116
      Adventures in Lestoria/GhostOfPirateCaptain.cpp
  7. 6
      Adventures in Lestoria/GiantOctopus.cpp
  8. 1
      Adventures in Lestoria/Monster.h
  9. 3
      Adventures in Lestoria/MonsterAttribute.h
  10. 1
      Adventures in Lestoria/RUN_STRATEGY.cpp
  11. 2
      Adventures in Lestoria/StoneGolem.cpp
  12. 2
      Adventures in Lestoria/Version.h
  13. 32
      Adventures in Lestoria/assets/Campaigns/Boss_3.tmx
  14. 10
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  15. 50
      Adventures in Lestoria/assets/config/Monsters.txt
  16. BIN
      Adventures in Lestoria/assets/gamepack.pak
  17. BIN
      x64/Release/Adventures in Lestoria.exe

@ -852,6 +852,10 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="GhostOfPirateCaptain.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="GiantCrab.cpp">
<SubType>
</SubType>

@ -1298,6 +1298,9 @@
<ClCompile Include="ItemHubLoadout.cpp">
<Filter>Source Files\Interface</Filter>
</ClCompile>
<ClCompile Include="GhostOfPirateCaptain.cpp">
<Filter>Source Files\Monster Strategies</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

@ -281,9 +281,10 @@ private:
const float knockbackAmt;
};
struct FallingStone:public Bullet{
struct FallingBullet:public Bullet{
//The position for this bullet represents where the falling stone should land.
FallingStone(vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple=false,float knockbackAmt=0.f,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f,float spellCircleRotation=0.f,float spellCircleRotationSpd=0.f,Pixel insigniaCol=WHITE,float insigniaRotation=0.f,float insigniaRotationSpd=0.f);
FallingBullet(vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple=false,float knockbackAmt=0.f,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f,float spellCircleRotation=0.f,float spellCircleRotationSpd=0.f,Pixel insigniaCol=WHITE,float insigniaRotation=0.f,float insigniaRotationSpd=0.f);
FallingBullet(const std::string&imgName,vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple=false,float knockbackAmt=0.f,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f,float spellCircleRotation=0.f,float spellCircleRotationSpd=0.f,Pixel insigniaCol=WHITE,float insigniaRotation=0.f,float insigniaRotationSpd=0.f);
protected:
void Update(float fElapsedTime)override;
void Draw(const Pixel blendCol)const override;

@ -55,7 +55,7 @@ enum class EffectType{
struct Effect{
friend class AiL;
friend struct FallingStone;
friend struct FallingBullet;
vf2d pos={0,0};
float lifetime=0;
float fadeout=0;

@ -45,14 +45,17 @@ All rights reserved.
INCLUDE_game
FallingStone::FallingStone(vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple,float knockbackAmt,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle,float spellCircleRotation,float spellCircleRotationSpd,Pixel insigniaCol,float insigniaRotation,float insigniaRotationSpd)
:Bullet(targetPos,vel,radius,damage,"rock.png",upperLevel,false,lifetime+0.1f,false,friendly,col,scale,image_angle),targetPos(targetPos),zVel(zVel),indicatorDisplayTime(indicatorDisplayTime),knockbackAmt(knockbackAmt),
FallingBullet::FallingBullet(const std::string&imgName,vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple,float knockbackAmt,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle,float spellCircleRotation,float spellCircleRotationSpd,Pixel insigniaCol,float insigniaRotation,float insigniaRotationSpd)
:Bullet(targetPos,vel,radius,damage,imgName,upperLevel,false,lifetime+0.1f,false,friendly,col,scale,image_angle),targetPos(targetPos),zVel(zVel),indicatorDisplayTime(indicatorDisplayTime),knockbackAmt(knockbackAmt),
indicator(targetPos,lifetime+0.1f,"range_indicator.png","spell_insignia.png",upperLevel,radius/12.f,0.5f,{},col,spellCircleRotation,spellCircleRotationSpd,false,radius/12.f,0.f,{},insigniaCol,insigniaRotation,insigniaRotationSpd,false){
pos+=-vel*lifetime;
z=-zVel*lifetime;
}
void FallingStone::Update(float fElapsedTime){
FallingBullet::FallingBullet(vf2d targetPos,vf2d vel,float zVel,float indicatorDisplayTime,float radius,int damage,bool upperLevel,bool hitsMultiple,float knockbackAmt,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle,float spellCircleRotation,float spellCircleRotationSpd,Pixel insigniaCol,float insigniaRotation,float insigniaRotationSpd)
:FallingBullet("rock.png",targetPos,vel,zVel,indicatorDisplayTime,radius,damage,upperLevel,hitsMultiple,knockbackAmt,lifetime,friendly,col,scale,image_angle,spellCircleRotation,spellCircleRotationSpd,insigniaCol,insigniaRotation,insigniaRotationSpd){}
void FallingBullet::Update(float fElapsedTime){
z+=zVel*fElapsedTime;
lastTrailEffect-=fElapsedTime;
if(z<=0.f){
@ -82,11 +85,11 @@ void FallingStone::Update(float fElapsedTime){
}
indicator.Update(fElapsedTime);
}
void FallingStone::Draw(const Pixel blendCol)const{
void FallingBullet::Draw(const Pixel blendCol)const{
if(lifetime<=indicatorDisplayTime){
indicator._Draw();
}
Bullet::Draw(blendCol);
}
void FallingStone::ModifyOutgoingDamageData(HurtDamageInfo&data){}
void FallingBullet::ModifyOutgoingDamageData(HurtDamageInfo&data){}

@ -0,0 +1,116 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
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 "MonsterStrategyHelpers.h"
#include "util.h"
#include "AdventuresInLestoria.h"
#include "SoundEffect.h"
#include "BulletTypes.h"
using A=Attribute;
INCLUDE_game
INCLUDE_MONSTER_LIST
void Monster::STRATEGY::GHOST_OF_PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string strategy){
enum PhaseName{
INIT,
NORMAL,
};
static const uint8_t PHASE_COUNT{DATA.GetProperty("MonsterStrategy.Ghost of Pirate Captain").GetValueCount()};
static std::vector<uint8_t>PHASES{};
const auto AdvanceCannonPhase{[&m,&strategy](){
m.GetFloat(A::CANNON_TIMER)=0.f;
if(m.GetInt(A::CANNON_PHASE)+1>=PHASE_COUNT)m.GetInt(A::CANNON_SHOT_TYPE)=util::random()%5;
m.GetInt(A::CANNON_PHASE)=(m.GetInt(A::CANNON_PHASE)+1)%PHASE_COUNT;
}};
switch(PHASE()){
case INIT:{
for(int i:std::ranges::iota_view(PHASE_COUNT)){
PHASES.emplace_back(DATA.GetProperty("MonsterStrategy.Ghost of Pirate Captain").GetInt(i));
}
m.I(A::CANNON_SHOT_TYPE)=util::random()%5;
}break;
case NORMAL:{
m.F(A::CANNON_TIMER)+=fElapsedTime;
switch(PHASES[m.I(A::CANNON_PHASE)]){
enum CannonPhaseType{
CANNON_SHOT,
SILENCE,
SHRAPNEL_SHOT,
};
case CANNON_SHOT:{//Normal Cannon Shot. Takes on one of five varieties.
enum CannonShotType{
BOMBARDMENT,//every shot is at a random location within 900 Range of the player. (This is also the move during hide and seek.)
PRECISE_BOMBARDMENT,//same as before but within 700 range of the player.
LINE,//the 4th or 5th hit would hit the player if the player doesnt move at all.
SHARPSHOOTER,//aiming directly at the player, skipping every 2nd shot. (only 4 instead of 8 shots)
PREDICTION,//shoots in the direction of the current or last players movement and predicts where the player would be in 2.8 seconds. (slightly further then where the player would be at impact. thats why 2.8 instead of 2.5 seconds)
};
switch(m.I(A::CANNON_SHOT_TYPE)){
case BOMBARDMENT:{
}break;
case PRECISE_BOMBARDMENT:{
}break;
case LINE:{
}break;
case SHARPSHOOTER:{
}break;
case PREDICTION:{
}break;
}
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Cannon Shot Delay"))AdvanceCannonPhase();
}break;
case SILENCE:{
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Silence Time"))AdvanceCannonPhase();
}break;
case SHRAPNEL_SHOT:{
if(m.F(A::CANNON_TIMER)>=ConfigFloat("Shrapnel Shot Delay"))AdvanceCannonPhase();
}break;
}
}break;
}
}

@ -53,6 +53,7 @@ void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string s
IDENTIFY_ARMS,
NORMAL,
HURT_ANIMATION,
DEAD,
};
if(!m.B(A::ARM_SPEEDS_INCREASED)&&m.GetHealth()<=ConfigInt("Arm Speedup Health Threshold")){
@ -75,9 +76,7 @@ void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string s
if(!GFX.count(takoyakiImgDir))ERR(std::format("WARNING! Could not find item image {}",takoyakiImgDir));
game->AddEffect(std::make_unique<Effect>(m.GetPos(),INFINITE,"item_img_directory"_S+"Takoyaki.png",m.OnUpperLevel(),1.f,0.f,vf2d{4.f,4.f},vf2d{},WHITE));
m.SetLifetime(4.f);
m.F(A::SHOOT_TIMER)=ConfigFloat("Shoot Frequency");
m.F(A::SHOOT_ANIMATION_TIME)=m.GetAnimation("ATTACK").GetTotalAnimationDuration();
m.I(A::ATTACK_COUNT)=0;
SETPHASE(DEAD);
return false;
});
SETPHASE(IDENTIFY_ARMS);
@ -175,5 +174,6 @@ void Monster::STRATEGY::GIANT_OCTOPUS(Monster&m,float fElapsedTime,std::string s
case HURT_ANIMATION:{
}break;
case DEAD:{}break;
}
}

@ -380,6 +380,7 @@ private:
static void GIANT_CRAB(Monster&m,float fElapsedTime,std::string strategy);
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);
};
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.

@ -163,4 +163,7 @@ enum class Attribute{
LAST_SHOOT_TIMER,
BULLET_COUNT_AFTER_INK_ATTACK,
LAST_INK_SHOOT_TIMER,
CANNON_TIMER,
CANNON_PHASE,
CANNON_SHOT_TYPE,
};

@ -77,6 +77,7 @@ void Monster::InitializeStrategies(){
STRATEGY_DATA.insert("Giant Crab",Monster::STRATEGY::GIANT_CRAB);
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.SetInitialized();
}

@ -330,7 +330,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
m.PerformAnimation("CAST");
for(int i:std::ranges::iota_view(0,ConfigInt("Stone Rain.Stone Count"))){
CreateBullet(FallingStone)(game->camera.GetViewPosition()+vf2d{util::random(game->ScreenWidth()),util::random(game->ScreenHeight())},ConfigVec("Stone Rain.Stone Vel"),ConfigFloatArr("Stone Rain.Stone Vel",2),ConfigFloat("Stone Rain.Indicator Time"),ConfigPixels("Stone Rain.Stone Radius"),ConfigInt("Stone Rain.Stone Damage"),m.OnUpperLevel(),false,ConfigFloat("Stone Rain.Stone Knockback Amt"),util::random_range(ConfigFloatArr("Stone Rain.Stone Fall Delay",0),ConfigFloatArr("Stone Rain.Stone Fall Delay",1)),false,ConfigPixel("Stone Rain.Stone Spell Circle Color"),vf2d{ConfigFloat("Stone Rain.Stone Radius")/100.f*2.f,ConfigFloat("Stone Rain.Stone Radius")/100.f*2.f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Stone Rain.Stone Spell Circle Rotation Spd")),ConfigPixel("Stone Rain.Stone Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Rain.Stone Spell Insignia Rotation Spd")))EndBullet;
CreateBullet(FallingBullet)(game->camera.GetViewPosition()+vf2d{util::random(game->ScreenWidth()),util::random(game->ScreenHeight())},ConfigVec("Stone Rain.Stone Vel"),ConfigFloatArr("Stone Rain.Stone Vel",2),ConfigFloat("Stone Rain.Indicator Time"),ConfigPixels("Stone Rain.Stone Radius"),ConfigInt("Stone Rain.Stone Damage"),m.OnUpperLevel(),false,ConfigFloat("Stone Rain.Stone Knockback Amt"),util::random_range(ConfigFloatArr("Stone Rain.Stone Fall Delay",0),ConfigFloatArr("Stone Rain.Stone Fall Delay",1)),false,ConfigPixel("Stone Rain.Stone Spell Circle Color"),vf2d{ConfigFloat("Stone Rain.Stone Radius")/100.f*2.f,ConfigFloat("Stone Rain.Stone Radius")/100.f*2.f},util::random(2*PI),util::random(2*PI),util::degToRad(ConfigFloat("Stone Rain.Stone Spell Circle Rotation Spd")),ConfigPixel("Stone Rain.Stone Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Rain.Stone Spell Insignia Rotation Spd")))EndBullet;
}
}
}

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 11935
#define VERSION_BUILD 11936
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="76" height="85" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="16">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="76" height="85" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="30">
<properties>
<property name="Background Music" propertytype="BGM" value="beach_boss"/>
<property name="Level Type" type="int" propertytype="LevelType" value="1"/>
@ -534,5 +534,35 @@
</properties>
<point/>
</object>
<object id="16" name="Hiding Spot" x="642" y="1410">
<point/>
</object>
<object id="18" name="Hiding Spot" x="906" y="1320">
<point/>
</object>
<object id="19" name="Hiding Spot" x="714" y="1200">
<point/>
</object>
<object id="20" name="Hiding Spot" x="522" y="1104">
<point/>
</object>
<object id="21" name="Hiding Spot" x="690" y="912">
<point/>
</object>
<object id="22" name="Hiding Spot" x="570" y="744">
<point/>
</object>
<object id="25" name="Hiding Spot" x="906" y="744">
<point/>
</object>
<object id="26" name="Hiding Spot" x="906" y="1008">
<point/>
</object>
<object id="27" name="Hiding Spot" x="1146" y="888">
<point/>
</object>
<object id="28" name="Hiding Spot" x="1146" y="1176">
<point/>
</object>
</objectgroup>
</map>

@ -1298,4 +1298,14 @@ MonsterStrategy
Homing Bullet Color = 110r,255g,110b,255a
}
}
Ghost of Pirate Captain
{
# 0 = Normal Cannon Shot, 1 = Silence, 2 = Shrapnel Shot
# Repeats at end of cycle.
Cannon Cycle = 0,0,0,0,0,0,0,0,1,2,1
Cannon Shot Delay = 2.5s
Silence Time = 2s
Shrapnel Shot Delay = 2.5s
}
}

@ -1808,6 +1808,56 @@ Monsters
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
#DROP[0] = Broken Dagger,30%,1,1
Hurt Sound = Monster Hurt
Death Sound = Slime Dead
Walk Sound = Slime Walk
}
Ghost of Pirate Captain
{
Health = 60000
Attack = 80
CollisionDmg = 50
MoveSpd = 50%
Size = 600%
XP = 0
# A flag to show an arrow indicator when the boss is off-screen.
ShowBossIndicator = True
Ignore Collisions = True
Strategy = Ghost of Pirate Captain
#Size of each animation frame
SheetFrameSize = 36,36
# Setting this to true means every four rows indicates one animation, the ordering of the directions is: NORTH, EAST, SOUTH, WEST
4-Way Spritesheet = True
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 = 4, 0.6, Repeat
WALK = 4, 0.15, Repeat
SLASHING = 4, 0.1, OneShot
DEATH = 4, 0.25, OneShot
STABBING = 3, 0.1, OneShot
SLASH = 1, 0.1, OneShot
STAB = 1, 0.1, OneShot
SHOOTING = 3, 0.2, OneShot
SHOOT = 1, 0.1, OneShot
# Drink is approximately 2 seconds long.
DRINK = 2, 0.65, PingPong
}
# 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

Loading…
Cancel
Save