From c83f84f29c1d9fe5299e64bc1ad7209dbddf34d0 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Fri, 3 May 2024 16:58:56 -0500 Subject: [PATCH] Setup framework for the goblin boar rider. --- .../Adventures in Lestoria.vcxproj | 4 ++ Adventures in Lestoria/Animation.cpp | 21 +++++++ Adventures in Lestoria/Monster.cpp | 12 ++++ Adventures in Lestoria/Monster.h | 25 +++++++- Adventures in Lestoria/MonsterAttribute.h | 1 + Adventures in Lestoria/MountMonster.cpp | 57 +++++++++++++++++++ Adventures in Lestoria/RUN_STRATEGY.cpp | 1 + .../assets/config/MonsterStrategies.txt | 8 +++ .../assets/config/gfx/gfx.txt | 1 + 9 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 Adventures in Lestoria/MountMonster.cpp diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index f9203f87..27a6cba5 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -712,6 +712,10 @@ + + + + diff --git a/Adventures in Lestoria/Animation.cpp b/Adventures in Lestoria/Animation.cpp index 106ebbe3..08812225 100644 --- a/Adventures in Lestoria/Animation.cpp +++ b/Adventures in Lestoria/Animation.cpp @@ -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)){ diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 754afa42..742a24e2 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -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::vectorshieldBuffs=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::optionalMonster::GetMountedFrame()const{ + if(!HasMountedMonster())return {}; + else return mounted_animation.value().GetFrame(internal_mounted_animState.value()); } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index d4bd079a..f7787eee 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -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;iSpawnMonster(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::optionalGetMountedFrame()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::Animationanimation; Animate2D::AnimationState internal_animState; + std::optional>mounted_animation; + std::optionalinternal_mounted_animState; float randomFrameOffset=0.f; float deathTimer=0.f; float monsterHurtSoundCooldown=0.f; @@ -213,7 +235,7 @@ private: std::mapSpawnDrops(); 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::vectordeathData; //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. diff --git a/Adventures in Lestoria/MonsterAttribute.h b/Adventures in Lestoria/MonsterAttribute.h index 863999f7..6a9eaa67 100644 --- a/Adventures in Lestoria/MonsterAttribute.h +++ b/Adventures in Lestoria/MonsterAttribute.h @@ -112,4 +112,5 @@ enum class Attribute{ RANDOM_DIRECTION, RANDOM_RANGE, PERCEPTION_LEVEL, + INITIALIZED_MOUNTED_MONSTER, }; \ No newline at end of file diff --git a/Adventures in Lestoria/MountMonster.cpp b/Adventures in Lestoria/MountMonster.cpp new file mode 100644 index 00000000..f8158512 --- /dev/null +++ b/Adventures in Lestoria/MountMonster.cpp @@ -0,0 +1,57 @@ +#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 "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{}; + for(const std::string&animation:Config("Imposed Monster Animations").GetValues()){ + m.mounted_animation.value().AddState(animation,ANIMATION_DATA.at(animation)); + } + } +} \ No newline at end of file diff --git a/Adventures in Lestoria/RUN_STRATEGY.cpp b/Adventures in Lestoria/RUN_STRATEGY.cpp index b5a5e788..b0ccdfdd 100644 --- a/Adventures in Lestoria/RUN_STRATEGY.cpp +++ b/Adventures in Lestoria/RUN_STRATEGY.cpp @@ -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(); } diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index a36dfd34..078760fb 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -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, + } } \ 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 f1291bcd..f0cccf41 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -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