Skeleton Captain and flag mechanics implemented. Release Build 12382.
All checks were successful
Emscripten Build / Build_and_Deploy_Web_Build (push) Successful in 7m4s

This commit is contained in:
sigonasr2 2026-01-29 17:56:42 -06:00
parent ef04956414
commit 9d38fbeada
18 changed files with 337 additions and 11 deletions

View File

@ -1148,6 +1148,8 @@
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="SkeletonCaptain.cpp" />
<ClCompile Include="SkeletonCaptainFlag.cpp" />
<ClCompile Include="State_Arena.cpp" />
<ClCompile Include="ThrownProjectile.cpp">
<SubType>

View File

@ -1331,6 +1331,12 @@
<ClCompile Include="Do_Nothing.cpp">
<Filter>Source Files\Monster Strategies</Filter>
</ClCompile>
<ClCompile Include="SkeletonCaptain.cpp">
<Filter>Source Files\Monster Strategies</Filter>
</ClCompile>
<ClCompile Include="SkeletonCaptainFlag.cpp">
<Filter>Source Files\Monster Strategies</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

View File

@ -401,6 +401,8 @@ private:
static void PIRATES_COIN(Monster&m,float fElapsedTime,std::string strategy);
static void SKELETON_BARBARIAN(Monster&m,float fElapsedTime,std::string strategy);
static void _RUN_RIGHT(Monster&m,float fElapsedTime,std::string strategy);
static void SKELETON_CAPTAIN(Monster&m,float fElapsedTime,std::string strategy);
static void SKELETON_CAPTAIN_FLAG(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.

View File

@ -181,4 +181,6 @@ enum class Attribute{
LAST_COLLISION_TIMER,
HIDING_POS,
SHRAPNEL_CANNON_TIMER,
FLAG_PTR,
WAIT_ONE_TICK,
};

View File

@ -35,6 +35,9 @@ Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include"MonsterAttribute.h"
#pragma once
#define ConfigInt(param) _GetInt(m,param,strategy)
#define ConfigFloat(param) _GetFloat(m,param,strategy)
@ -52,4 +55,6 @@ All rights reserved.
#define ConfigVecArr(param,ind) _GetVec(m,param,strategy,ind)
#define PHASE() m.GetPhase(strategy)
#define SETPHASE(phase) m.SetPhase(strategy,phase)
#define SETPHASE(phase) m.SetPhase(strategy,phase)
using A=Attribute;

View File

@ -82,6 +82,8 @@ void Monster::InitializeStrategies(){
STRATEGY_DATA.insert("Pirate's Coin",Monster::STRATEGY::PIRATES_COIN);
STRATEGY_DATA.insert("Skeleton Barbarian",Monster::STRATEGY::SKELETON_BARBARIAN);
STRATEGY_DATA.insert("[TEST]Run Right",Monster::STRATEGY::_RUN_RIGHT);
STRATEGY_DATA.insert("Skeleton Captain",Monster::STRATEGY::SKELETON_CAPTAIN);
STRATEGY_DATA.insert("Skeleton Captain Flag",Monster::STRATEGY::SKELETON_CAPTAIN_FLAG);
STRATEGY_DATA.SetInitialized();
}

View File

@ -0,0 +1,148 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy 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 "Monster.h"
#include "AdventuresInLestoria.h"
#include"MonsterStrategyHelpers.h"
#include<ranges>
INCLUDE_game
INCLUDE_MONSTER_LIST
INCLUDE_MONSTER_DATA
void Monster::STRATEGY::SKELETON_CAPTAIN(Monster&m,float fElapsedTime,std::string strategy){
enum Phase{
INITIAL,
RESET,
CHARGE,
RECOVERY,
TURNAROUND,
};
if(PHASE()!=INITIAL){
m.F(A::ATTACK_COOLDOWN)=std::max(0.f,m.F(A::ATTACK_COOLDOWN)-fElapsedTime);
const Monster*flag=any_cast<Monster*>(m.ANY(A::FLAG_PTR));
if(m.F(A::ATTACK_COOLDOWN)<=0.f&&(!flag||flag->IsDead())){
for(std::shared_ptr<Monster>&flagTarget:MONSTER_LIST|std::views::filter([&m,&strategy](std::shared_ptr<Monster>&monster){return m.GetDistanceFrom(monster->GetPos())<=ConfigFloat("Drop Flag Range")/100.f*24;})){
m.F(A::ATTACK_COOLDOWN)=ConfigFloat("Flag Cooldown Time");
m.ANY(A::FLAG_PTR)=&game->SpawnMonster(flagTarget->GetPos(),MONSTER_DATA.at("Skeleton Captain Flag"),m.OnUpperLevel());
break;
}
}
}
switch(PHASE()){
case INITIAL:{
Monster*blank{nullptr};
m.ANY(A::FLAG_PTR)=blank;
SETPHASE(RESET);
}break;
case RESET:{
m.F(A::CHASE_TIMER)=0.f;
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
const float distToPlayer{geom2d::line<float>(m.GetPos(),m.V(A::LOCKON_POS)).length()};
m.V(A::LOCKON_POS)=geom2d::line<float>(m.GetPos(),m.V(A::LOCKON_POS)).rpoint(64.f+distToPlayer);
m.F(A::TARGET_TIMER)=3.0f;
m.PerformShootAnimation();
SETPHASE(CHARGE);
}break;
case CHARGE:{
m.F(A::CHASE_TIMER)+=fElapsedTime;
m.RemoveBuff(BuffType::LOCKON_SPEEDBOOST);
m.AddBuff(BuffType::LOCKON_SPEEDBOOST,INFINITE,ConfigFloat("Jousting Movespeed Buff")/100*(std::min(m.F(A::CHASE_TIMER),ConfigFloat("Jousting Speedup Time"))/ConfigFloat("Jousting Speedup Time")));
m.F(A::TARGET_TIMER)=std::max(0.f,m.F(A::TARGET_TIMER)-fElapsedTime);
float distToTarget=geom2d::line<float>(m.GetPos(),m.V(A::LOCKON_POS)).length();
if(distToTarget<=12.f||m.F(A::TARGET_TIMER)==0.f){
SETPHASE(RECOVERY);
m.F(A::RECOVERY_TIME)=ConfigFloat("Jousting Slowdown Time");
m.PerformJumpAnimation();
}else{
m.target=m.V(A::LOCKON_POS);
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
}
}break;
case RECOVERY:{
m.F(A::RECOVERY_TIME)=std::max(0.f,m.F(A::RECOVERY_TIME)-fElapsedTime);
m.RemoveBuff(BuffType::LOCKON_SPEEDBOOST);
m.AddBuff(BuffType::LOCKON_SPEEDBOOST,INFINITE,ConfigFloat("Jousting Movespeed Buff")/100*(m.F(A::RECOVERY_TIME)/ConfigFloat("Jousting Slowdown Time")));
if(m.F(A::RECOVERY_TIME)==0.f){
m.RemoveBuff(BuffType::LOCKON_SPEEDBOOST);
std::vector<vf2d>disengagePoints;
for(float angle=0.f;angle<360.f;angle+=22.5f){
float range=50;
vf2d checkLoc=vf2d{0,util::degToRad(angle)}.cart();
while(range<=ConfigFloat("Disengage Range")){
checkLoc=game->GetPlayer()->GetPos()+vf2d{range/100*24.f,util::degToRad(angle)}.cart();
if(game->GetTileCollision(game->GetCurrentMapName(),checkLoc,m.OnUpperLevel())!=game->NO_COLLISION){
break;
}
range+=50;
}
disengagePoints.push_back(checkLoc);
}
m.path.Initialize(disengagePoints);
if(disengagePoints.size()!=16)ERR(std::format("Disengage Points size was not equal to 16! Size: {}",disengagePoints.size()));
if(m.path.points.size()!=16)ERR(std::format("Path Points size was not equal to 16! Size: {}",m.path.points.size()));
m.I(A::PATH_DIR)=util::random()%2;
if(m.I(A::PATH_DIR)==0)m.I(A::PATH_DIR)=-1;
m.pathIndex=util::random()%disengagePoints.size();
SETPHASE(TURNAROUND);
m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Disengage Duration");
m.PerformJumpAnimation();
}
}break;
case TURNAROUND:{
m.F(A::RUN_AWAY_TIMER)=std::max(0.f,m.F(A::RUN_AWAY_TIMER)-fElapsedTime);
m.pathIndex+=m.I(A::PATH_DIR)*fElapsedTime*1.25f;
m.pathIndex=fmod(m.pathIndex,m.path.points.size());
if(m.pathIndex<0){ //When the modulus is negative, adding the total number of elements gets us to the correct index.
m.pathIndex=m.pathIndex+m.path.points.size();
}
if(m.F(A::RUN_AWAY_TIMER)==0.f){
SETPHASE(RESET);
}
m.target=m.path.GetSplinePoint(m.pathIndex).pos;
geom2d::line<float>moveTowardsLine=geom2d::line(m.pos,m.path.GetSplinePoint(m.pathIndex).pos);
if(moveTowardsLine.length()>1.f){
m.UpdateFacingDirection(moveTowardsLine.end);
m.SetPos(m.pos+moveTowardsLine.vector().norm()*100*fElapsedTime*m.GetMoveSpdMult());
}
}break;
}
}

View File

@ -0,0 +1,59 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2026 Amy 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 "Monster.h"
#include"MonsterStrategyHelpers.h"
#include<ranges>
INCLUDE_MONSTER_LIST
using A=Attribute;
void Monster::STRATEGY::SKELETON_CAPTAIN_FLAG(Monster&m,float fElapsedTime,std::string strategy){
const float HASTE_REAPPLY_TIME=1.f;
if(m.GetZ()>0)m.SetZ(std::max(0.f,m.GetZ()-60.f*fElapsedTime));
m.F(A::ATTACK_COOLDOWN)-=fElapsedTime;
if(m.F(A::ATTACK_COOLDOWN)<=0.f){
m.F(A::ATTACK_COOLDOWN)=HASTE_REAPPLY_TIME;
for(std::shared_ptr<Monster>&target:MONSTER_LIST|std::views::filter([](std::shared_ptr<Monster>&m){return m->IsAlive()&&!m->isBoss&&m->GetStrategyName()!="Skeleton Captain";})){
if(target->GetDistanceFrom(m.GetPos())<=ConfigFloat("Haste Buff Range")/100.f*24)target->AddBuff(BuffType::HASTEN,HASTE_REAPPLY_TIME,ConfigFloat("Haste Buff")/100.f);
}
}
}

View File

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

View File

@ -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="315" height="238" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="13">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="315" height="238" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="15">
<properties>
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
</properties>
@ -989,17 +989,12 @@
<object id="6" name="Monster Spawn Zone" type="SpawnGroup" x="282" y="4776" width="216" height="246">
<ellipse/>
</object>
<object id="10" template="../maps/Monsters/Skeleton Archer.tx" x="486" y="5046">
<object id="13" template="../maps/Monsters/Skeleton Captain.tx" x="534" y="4950">
<properties>
<property name="spawner" type="object" value="6"/>
</properties>
</object>
<object id="11" template="../maps/Monsters/Skeleton Archer.tx" x="570" y="5040">
<properties>
<property name="spawner" type="object" value="6"/>
</properties>
</object>
<object id="12" template="../maps/Monsters/Skeleton Archer.tx" x="570" y="4974">
<object id="14" template="../maps/Monsters/Skeleton Barbarian.tx" x="522" y="4794">
<properties>
<property name="spawner" type="object" value="6"/>
</properties>

View File

@ -1407,4 +1407,27 @@ MonsterStrategy
# Amount the hasten buff speeds up the actions of the Skeleton Barbarian.
Hasten Buff Potency = 30%
}
Skeleton Captain
{
Jousting Speedup Time = 1s
Jousting Movespeed Buff = 50%
Jousting Slowdown Time = 2s
Drop Flag Range = 150
Flag Cooldown Time = 20s
# The speed boost percentage to increase by and the duration while disengaging.
Disengage Speed Boost = 30%, 3s
# The distance to disengage the player.
Disengage Range = 200
# The amount of time to spend disengaged.
Disengage Duration = 2.0s
}
Skeleton Captain Flag
{
Haste Buff Range = 600
Haste Buff = 30%
}
}

View File

@ -2092,6 +2092,82 @@ Monsters
Death Sound = Slime Dead
Walk Sound = Slime Walk
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
#DROP[0] = Broken Bow,30%,1,1
}
Skeleton Captain
{
Health = 1900
Attack = 84
CollisionDmg = 84
MoveSpd = 130%
Size = 180%
XP = 101
Strategy = Skeleton Captain
#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 = 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 = 1, 0.6, Repeat
WALK = 4, 0.2, Repeat
CHARGE = 3, 0.1, Repeat
DEATH = 3, 0.15, OneShot
}
Hurt Sound = Monster Hurt
Death Sound = Slime Dead
Walk Sound = Slime Walk
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
#DROP[0] = Broken Bow,30%,1,1
}
Skeleton Captain Flag
{
Health = 280
Attack = 0
CollisionDmg = 0
MoveSpd = 0%
Size = 100%
XP = 0
Strategy = Skeleton Captain Flag
#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 = 4, 0.6, Repeat
WALK = 1, 0.2, Repeat
ATTACK = 1, 0.1, Repeat
DEATH = 3, 0.15, OneShot
}
Hurt Sound = Monster Hurt
Death Sound = Slime Dead
Walk Sound = Slime Walk
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
#DROP[0] = Broken Bow,30%,1,1
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="24" tileheight="24" infinite="1" nextlayerid="3" nextobjectid="51">
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="24" tileheight="24" infinite="1" nextlayerid="3" nextobjectid="52">
<tileset firstgid="1" source="Monsters.tsx"/>
<layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv"/>
@ -43,5 +43,6 @@
<object id="46" template="Monsters/Pirate's Treasure.tx" type="Monster" x="324" y="174" width="144" height="144"/>
<object id="48" template="Monsters/Skeleton Barbarian.tx" type="Monster" x="-240" y="360"/>
<object id="50" template="Monsters/Skeleton Archer.tx" type="Monster" x="354" y="564"/>
<object id="51" template="Monsters/Skeleton Captain.tx" type="Monster" x="360" y="684"/>
</objectgroup>
</map>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<tileset firstgid="1" source="../Monsters.tsx"/>
<object name="Skeleton Captain" type="Monster" gid="38" width="86.4" height="86.4"/>
</template>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB