Setup framework for the goblin boar rider.

pull/57/head
sigonasr2 7 months ago
parent 3051422383
commit c83f84f29c
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 21
      Adventures in Lestoria/Animation.cpp
  3. 12
      Adventures in Lestoria/Monster.cpp
  4. 25
      Adventures in Lestoria/Monster.h
  5. 1
      Adventures in Lestoria/MonsterAttribute.h
  6. 57
      Adventures in Lestoria/MountMonster.cpp
  7. 1
      Adventures in Lestoria/RUN_STRATEGY.cpp
  8. 8
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  9. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt

@ -712,6 +712,10 @@
</SubType>
</ClCompile>
<ClCompile Include="GameState.cpp" />
<ClCompile Include="MountMonster.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="Goblin_Bow.cpp">
<SubType>
</SubType>

@ -249,6 +249,27 @@ void sig::Animation::InitializeAnimations(){
CreateStillAnimation("laser.png",{5,1});
CreateStillAnimation("range_indicator.png",{24,24});
Animate2D::FrameSequence goblin_bow_mount_n,goblin_bow_mount_e,goblin_bow_mount_s,goblin_bow_mount_w;
Animate2D::FrameSequence goblin_bow_attack_n,goblin_bow_attack_e,goblin_bow_attack_s,goblin_bow_attack_w;
//Idle sequences for the mounted boar bow goblin.
for(int animationIndex=0;animationIndex<4;animationIndex++){
Animate2D::FrameSequence mountAnimation;
for(int i=0;i<2;i++){
mountAnimation.AddFrame(Animate2D::Frame{&GFX["monsters/commercial_assets/Goblin (Bow)_foreground.png"],{{i*32,animationIndex*32},{32,32}}});
}
ANIMATION_DATA[std::format("GOBLIN_BOW_MOUNTED_{}",animationIndex)]=mountAnimation;
animationIndex++;
}
//Shooting sequences for the mounted boar bow goblin.
for(int animationIndex=0;animationIndex<4;animationIndex++){
Animate2D::FrameSequence mountShootAnimation;
for(int i=0;i<4;i++){
mountShootAnimation.AddFrame(Animate2D::Frame{&GFX["monsters/commercial_assets/Goblin (Bow)_foreground.png"],{{i*32,index*32+4*32},{32,32}}});
}
ANIMATION_DATA[std::format("GOBLIN_BOW_ATTACK_{}",animationIndex)]=mountShootAnimation;
animationIndex++;
}
for(auto&dat:GFX){
std::string imgFile=dat.first;
if(!ANIMATION_DATA.count(imgFile)){

@ -379,6 +379,7 @@ void Monster::UpdateFacingDirection(vf2d facingTargetPoint){
}
}
animation.ModifyDisplaySprite(internal_animState,std::format("{}_{}",animation.currentStateName.substr(0,animation.currentStateName.length()-2),int(facingDirection)));
if(HasMountedMonster())mounted_animation.value().ModifyDisplaySprite(internal_mounted_animState.value(),std::format("{}_{}",mounted_animation.value().currentStateName.substr(0,mounted_animation.value().currentStateName.length()-2),int(facingDirection)));
}else{
if(diff.x>0){
facingDirection=Direction::EAST;
@ -398,6 +399,7 @@ void Monster::Draw()const{
if(overlaySprite.length()!=0){
game->view.DrawPartialRotatedDecal(GetPos()-vf2d{0,GetZ()},GFX[overlaySprite].Decal(),spriteRot,GetFrame().GetSourceRect().size/2,GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::WEST?-1:1),GetSizeMult()),{blendCol.r,blendCol.g,blendCol.b,overlaySpriteTransparency});
}
if(HasMountedMonster())game->view.DrawPartialRotatedDecal(GetPos()-vf2d{0,GetZ()},GetMountedFrame().GetSourceImage()->Decal(),spriteRot,GetMountedFrame().GetSourceRect().size/2,GetMountedFrame().GetSourceRect().pos,GetMountedFrame().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult()),blendCol);
std::vector<Buff>shieldBuffs=GetBuffs(BARRIER_DAMAGE_REDUCTION);
if(shieldBuffs.size()>0){
@ -942,4 +944,14 @@ const Direction Monster::GetFacingDirectionToTarget(vf2d target)const{
const bool Monster::HasFourWaySprites()const{
return MONSTER_DATA.at(name).HasFourWaySprites();
}
const bool Monster::HasMountedMonster()const{
if(internal_mounted_animState.has_value()^mounted_animation.has_value())ERR("WARNING! The internal mounted animation state and the mounted animation variables are not matching! They should both either be on or both be off! THIS SHOULD NOT BE HAPPENING!");
return true;
}
const std::optional<const Animate2D::Frame&>Monster::GetMountedFrame()const{
if(!HasMountedMonster())return {};
else return mounted_animation.value().GetFrame(internal_mounted_animState.value());
}

@ -50,6 +50,8 @@ All rights reserved.
#include "Direction.h"
INCLUDE_ITEM_DATA
INCLUDE_MONSTER_DATA
INCLUDE_game
struct DamageNumber;
class AiL;
@ -58,6 +60,22 @@ enum class Attribute;
class GameEvent;
class DeathSpawnInfo{
std::string monsterSpawnName;
uint8_t spawnAmt;
vf2d spawnLocOffset;
public:
DeathSpawnInfo(const std::string_view monsterName,const uint8_t spawnAmt,const vf2d spawnOffset={})
:monsterSpawnName(monsterName),spawnAmt(spawnAmt),spawnLocOffset(spawnOffset){
if(!MONSTER_DATA.count(std::string(monsterName)))ERR(std::format("WARNING! Monster {} specified in DeathSpawnInfo does not exist! Please provide a proper monster name.",monsterName));
}
void Spawn(const vf2d monsterDeathPos,const bool onUpperLevel){
for(uint8_t i=0;i<spawnAmt;i++){
game->SpawnMonster(monsterDeathPos+spawnLocOffset,MONSTER_DATA.at(monsterSpawnName),onUpperLevel);
}
}
};
class Monster:IAttributable{
friend struct STRATEGY;
friend class AiL;
@ -74,6 +92,7 @@ public:
//Obtains the size multiplier (from 0.f-1.f).
float GetSizeMult()const;
Animate2D::Frame GetFrame()const;
const std::optional<const Animate2D::Frame&>GetMountedFrame()const;
bool Update(float fElapsedTime);
//Returns true when damage is actually dealt. Provide whether or not the attack is on the upper level or not. Monsters must be on the same level to get hit by it. (there is a death check and level check here.)
//If you need to hurt multiple enemies try AiL::HurtEnemies()
@ -148,6 +167,7 @@ public:
const float GetDistanceFrom(vf2d target)const;
const Direction GetFacingDirectionToTarget(vf2d target)const;
const bool HasFourWaySprites()const;
const bool HasMountedMonster()const;
private:
std::string name;
vf2d pos;
@ -170,6 +190,8 @@ private:
uint8_t overlaySpriteTransparency=0U;
Animate2D::Animation<std::string>animation;
Animate2D::AnimationState internal_animState;
std::optional<Animate2D::Animation<std::string>>mounted_animation;
std::optional<Animate2D::AnimationState>internal_mounted_animState;
float randomFrameOffset=0.f;
float deathTimer=0.f;
float monsterHurtSoundCooldown=0.f;
@ -213,7 +235,7 @@ private:
std::map<ItemInfo*,uint16_t>SpawnDrops();
float prevFacingDirectionAngle=0.f; //Keeps track of the angle of the previous target to ensure four-way sprite facing changes don't happen too early or spastically.
float lastFacingDirectionChange=0.f; //How much time has passed since the last facing direction change. Used to ensure another facing direction change doesn't happen too quickly. Probably allowing one every quarter second is good enough.
std::vector<DeathSpawnInfo>deathData; //Data that contains further actions and information when this monster dies. Mostly used to spawn sub-monsters from a defeated monster.
private:
struct STRATEGY{
static int _GetInt(Monster&m,std::string param,std::string strategy,int index=0);
@ -238,6 +260,7 @@ private:
static void BOAR(Monster&m,float fElapsedTime,std::string strategy);
static void GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string strategy);
static void GOBLIN_BOW(Monster&m,float fElapsedTime,std::string strategy);
static void GOBLIN_BOAR_RIDER(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.

@ -112,4 +112,5 @@ enum class Attribute{
RANDOM_DIRECTION,
RANDOM_RANGE,
PERCEPTION_LEVEL,
INITIALIZED_MOUNTED_MONSTER,
};

@ -0,0 +1,57 @@
#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 "AdventuresInLestoria.h"
#include "DEFINES.h"
#include "Monster.h"
#include "MonsterStrategyHelpers.h"
INCLUDE_ANIMATION_DATA
using A=Attribute;
void Monster::STRATEGY::GOBLIN_BOAR_RIDER(Monster&m,float fElapsedTime,std::string strategy){
//We have access to GOBLIN_BOW_MOUNTED_X and GOBLIN_BOW_ATTACK_X
if(!m.B(A::INITIALIZED_MOUNTED_MONSTER)){
m.B(A::INITIALIZED_MOUNTED_MONSTER)=true;
m.internal_mounted_animState=Animate2D::AnimationState{};
m.mounted_animation=Animate2D::Animation<std::string>{};
for(const std::string&animation:Config("Imposed Monster Animations").GetValues()){
m.mounted_animation.value().AddState(animation,ANIMATION_DATA.at(animation));
}
}
}

@ -56,6 +56,7 @@ void Monster::InitializeStrategies(){
STRATEGY_DATA.insert("Boar",Monster::STRATEGY::BOAR);
STRATEGY_DATA.insert("Goblin Dagger",Monster::STRATEGY::GOBLIN_DAGGER);
STRATEGY_DATA.insert("Goblin Bow",Monster::STRATEGY::GOBLIN_BOW);
STRATEGY_DATA.insert("Goblin Boar Rider",Monster::STRATEGY::GOBLIN_BOAR_RIDER);
STRATEGY_DATA.SetInitialized();
}

@ -613,4 +613,12 @@ MonsterStrategy
Perception Level Increase = 2.5
Maximum Perception Level = 45
}
Goblin Boar Rider
{
# Which monster spawns on death of the boar.
Spawned Monster = Goblin (Bow)
Imposed Monster Spritesheet = Goblin (Bow)_foreground.png
Imposed Monster Offset = 0,-10
Imposed Monster Animations = GOBLIN_BOW_MOUNTED_0,GOBLIN_BOW_MOUNTED_1,GOBLIN_BOW_MOUNTED_2,GOBLIN_BOW_MOUNTED_3,GOBLIN_BOW_ATTACK_0,GOBLIN_BOW_ATTACK_1,GOBLIN_BOW_ATTACK_2,GOBLIN_BOW_ATTACK_3,
}
}

@ -89,6 +89,7 @@ Images
GFX_DaggerStab = dagger_stab.png
GFX_GoblinSwordSlash = goblin_sword_slash.png
GFX_GoblinArrow = goblin_arrow.png
GFX_GoblinBowForeground = monsters/commercial_assets/Goblin (Bow)_foreground.png
# Ability Icons
GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png

Loading…
Cancel
Save