diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 27a6cba5..a50a4b17 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -712,7 +712,7 @@ - + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 0d47230f..13c67911 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -3016,11 +3016,12 @@ void AiL::InitializeLevels(){ Test::RunMapTests(); } -void AiL::SpawnMonster(vf2d pos,MonsterData&data,bool upperLevel,bool isBossSpawn){ +Monster&AiL::SpawnMonster(vf2d pos,MonsterData&data,bool upperLevel,bool isBossSpawn){ monstersToBeSpawned.push_back(Monster(pos,data,upperLevel,isBossSpawn)); if(isBossSpawn){ totalBossEncounterMobs++; } + return monstersToBeSpawned.back(); } void AiL::DrawPie(vf2d center,float radius,float degreesCut,Pixel col){ diff --git a/Adventures in Lestoria/AdventuresInLestoria.h b/Adventures in Lestoria/AdventuresInLestoria.h index 001a0999..34ee26cf 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.h +++ b/Adventures in Lestoria/AdventuresInLestoria.h @@ -277,7 +277,7 @@ public: void RenderTile(vi2d pos,TilesheetData tileSheet,int tileSheetIndex,vi2d tileSheetPos); void RenderTile(TileRenderData&tileSheet,Pixel col); bool IsReflectiveTile(TilesheetData tileSheet,int tileID); - void SpawnMonster(vf2d pos,MonsterData&data,bool upperLevel=false,bool isBossSpawn=false); //Queues a monster for spawning on the next frame. + Monster&SpawnMonster(vf2d pos,MonsterData&data,bool upperLevel=false,bool isBossSpawn=false); //Queues a monster for spawning on the next frame. void DrawPie(vf2d center,float radius,float degreesCut,Pixel col); void DrawSquarePie(vf2d center,float radius,float degreesCut,Pixel col); void RenderCooldowns(); diff --git a/Adventures in Lestoria/Animation.cpp b/Adventures in Lestoria/Animation.cpp index 08812225..883c309c 100644 --- a/Adventures in Lestoria/Animation.cpp +++ b/Adventures in Lestoria/Animation.cpp @@ -253,21 +253,19 @@ void sig::Animation::InitializeAnimations(){ 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; + Animate2D::FrameSequence mountAnimation{0.6f}; 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 animationIndex=0;animationIndex<4;animationIndex++){ + Animate2D::FrameSequence mountShootAnimation{0.06f,Animate2D::Style::OneShot}; 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}}}); + mountShootAnimation.AddFrame(Animate2D::Frame{&GFX["monsters/commercial_assets/Goblin (Bow)_foreground.png"],{{i*32,animationIndex*32+4*32},{32,32}}}); } ANIMATION_DATA[std::format("GOBLIN_BOW_ATTACK_{}",animationIndex)]=mountShootAnimation; - animationIndex++; } for(auto&dat:GFX){ diff --git a/Adventures in Lestoria/MountMonster.cpp b/Adventures in Lestoria/Goblin_Boar_Rider.cpp similarity index 57% rename from Adventures in Lestoria/MountMonster.cpp rename to Adventures in Lestoria/Goblin_Boar_Rider.cpp index f8158512..2bb42537 100644 --- a/Adventures in Lestoria/MountMonster.cpp +++ b/Adventures in Lestoria/Goblin_Boar_Rider.cpp @@ -39,8 +39,11 @@ All rights reserved. #include "DEFINES.h" #include "Monster.h" #include "MonsterStrategyHelpers.h" +#include "BulletTypes.h" INCLUDE_ANIMATION_DATA +INCLUDE_BULLET_LIST +INCLUDE_game using A=Attribute; @@ -48,10 +51,39 @@ void Monster::STRATEGY::GOBLIN_BOAR_RIDER(Monster&m,float fElapsedTime,std::stri //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.F(A::PERCEPTION_LEVEL)=ConfigFloat("Starting Perception Level"); m.internal_mounted_animState=Animate2D::AnimationState{}; m.mounted_animation=Animate2D::Animation{}; - for(const std::string&animation:Config("Imposed Monster Animations").GetValues()){ + + for(bool firstAnimation=true;const std::string&animation:Config("Imposed Monster Animations").GetValues()){ m.mounted_animation.value().AddState(animation,ANIMATION_DATA.at(animation)); + + if(firstAnimation)m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),animation); + firstAnimation=false; } + m.mountedSprOffset=ConfigVec("Imposed Monster Offset"); + m.deathData.push_back(DeathSpawnInfo{ConfigString("Spawned Monster"),1U}); + } + + BOAR(m,fElapsedTime,"Boar"); + + m.F(A::ATTACK_COOLDOWN)+=fElapsedTime; + if(m.F(A::SHOOT_TIMER)>0.f){ + m.F(A::SHOOT_TIMER)-=fElapsedTime; + if(m.F(A::SHOOT_TIMER)<=0.f){ + geom2d::line pointTowardsPlayer(m.GetPos(),game->GetPlayer()->GetPos()); + vf2d extendedLine=pointTowardsPlayer.upoint(1.1f); + CreateBullet(Arrow)(m.GetPos(),extendedLine,pointTowardsPlayer.vector().norm()*ConfigFloat("Arrow Spd"),"goblin_arrow.png",ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet; + Arrow&arrow=static_cast(*BULLET_LIST.back()); + arrow.PointToBestTargetPath(m.F(A::PERCEPTION_LEVEL)); + + m.F(A::ATTACK_COOLDOWN)=0.f; + m.F(A::PERCEPTION_LEVEL)=std::min(ConfigFloat("Maximum Perception Level"),m.F(A::PERCEPTION_LEVEL)+ConfigFloat("Perception Level Increase")); + m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),std::format("GOBLIN_BOW_MOUNTED_{}",int(m.GetFacingDirection()))); + } + }else + if(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Reload Time")){ + m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time"); + m.mounted_animation.value().ChangeState(m.internal_mounted_animState.value(),std::format("GOBLIN_BOW_ATTACK_{}",int(m.GetFacingDirection()))); } } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index 742a24e2..d9b0cd33 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -399,7 +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); + if(HasMountedMonster())game->view.DrawPartialRotatedDecal(GetPos()-vf2d{0,GetZ()}+mountedSprOffset,GetMountedFrame().value().GetSourceImage()->Decal(),spriteRot,GetMountedFrame().value().GetSourceRect().size/2,GetMountedFrame().value().GetSourceRect().pos,GetMountedFrame().value().GetSourceRect().size,vf2d(GetSizeMult()*(!HasFourWaySprites()&&GetFacingDirection()==Direction::EAST?-1:1),GetSizeMult()),blendCol); std::vectorshieldBuffs=GetBuffs(BARRIER_DAMAGE_REDUCTION); if(shieldBuffs.size()>0){ @@ -768,6 +768,15 @@ std::mapMonster::SpawnDrops(){ void Monster::OnDeath(){ animation.ChangeState(internal_animState,GetDeathAnimationName()); + + if(HasMountedMonster()){ + for(DeathSpawnInfo&deathInfo:deathData){ + deathInfo.Spawn(GetPos(),OnUpperLevel()); + } + mounted_animation={}; + internal_mounted_animState={}; + } + if(isBoss){ game->ReduceBossEncounterMobCount(); if(game->BossEncounterMobCount()==0){ @@ -948,10 +957,21 @@ const bool Monster::HasFourWaySprites()const{ 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; + return internal_mounted_animState.has_value()&&mounted_animation.has_value(); } -const std::optionalMonster::GetMountedFrame()const{ +const std::optionalMonster::GetMountedFrame()const{ if(!HasMountedMonster())return {}; else return mounted_animation.value().GetFrame(internal_mounted_animState.value()); +} + +DeathSpawnInfo::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 DeathSpawnInfo::Spawn(const vf2d monsterDeathPos,const bool onUpperLevel){ + for(uint8_t i=0;iSpawnMonster(monsterDeathPos+spawnLocOffset,MONSTER_DATA.at(monsterSpawnName),onUpperLevel).iframe_timer=0.25f; + } } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index f7787eee..ba64b368 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -51,7 +51,6 @@ All rights reserved. INCLUDE_ITEM_DATA INCLUDE_MONSTER_DATA -INCLUDE_game struct DamageNumber; class AiL; @@ -65,21 +64,15 @@ class DeathSpawnInfo{ 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); - } - } + DeathSpawnInfo(const std::string_view monsterName,const uint8_t spawnAmt,const vf2d spawnOffset={}); + void Spawn(const vf2d monsterDeathPos,const bool onUpperLevel); }; class Monster:IAttributable{ friend struct STRATEGY; friend class AiL; friend class InventoryCreator; + friend class DeathSpawnInfo; public: Monster()=delete; Monster(vf2d pos,MonsterData data,bool upperLevel=false,bool bossMob=false); @@ -92,7 +85,7 @@ public: //Obtains the size multiplier (from 0.f-1.f). float GetSizeMult()const; Animate2D::Frame GetFrame()const; - const std::optionalGetMountedFrame()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() @@ -236,6 +229,7 @@ private: 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. + vf2d mountedSprOffset{}; private: struct STRATEGY{ static int _GetInt(Monster&m,std::string param,std::string strategy,int index=0); @@ -245,7 +239,7 @@ private: static double _GetPixels(Monster&m,std::string param,std::string strategy,int index=0); static vf2d _GetVec(Monster&m,std::string param,std::string strategy,int index=0); static const std::string&_GetString(Monster&m,std::string param,std::string strategy,int index=0); - static datafile _Get(Monster&m,std::string param,std::string strategy); + static const datafile&_Get(Monster&m,std::string param,std::string strategy); static void RUN_STRATEGY(Monster&m,float fElapsedTime); static void RUN_TOWARDS(Monster&m,float fElapsedTime,std::string strategy); static void SHOOT_AFAR(Monster&m,float fElapsedTime,std::string strategy); diff --git a/Adventures in Lestoria/RUN_STRATEGY.cpp b/Adventures in Lestoria/RUN_STRATEGY.cpp index b0ccdfdd..32332584 100644 --- a/Adventures in Lestoria/RUN_STRATEGY.cpp +++ b/Adventures in Lestoria/RUN_STRATEGY.cpp @@ -101,7 +101,7 @@ vf2d Monster::STRATEGY::_GetVec(Monster&m,std::string param,std::string strategy return {DATA["MonsterStrategy"][strategy].GetProperty(param).GetReal(index),DATA["MonsterStrategy"][strategy].GetProperty(param).GetReal(index+1)}; } } -datafile Monster::STRATEGY::_Get(Monster&m,std::string param,std::string strategy){ +const datafile&Monster::STRATEGY::_Get(Monster&m,std::string param,std::string strategy){ if(m.IsNPC()&&DATA["NPCs"][m.name].HasProperty(param)){ return DATA["NPCs"][m.name].GetProperty(param); }else diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 676d5b09..b9bae47b 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 2 #define VERSION_PATCH 0 -#define VERSION_BUILD 9180 +#define VERSION_BUILD 9199 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/Campaigns/2_1.tmx b/Adventures in Lestoria/assets/Campaigns/2_1.tmx index 7b1f0856..a38be711 100644 --- a/Adventures in Lestoria/assets/Campaigns/2_1.tmx +++ b/Adventures in Lestoria/assets/Campaigns/2_1.tmx @@ -1,5 +1,5 @@ - + @@ -1882,17 +1882,7 @@ - - - - - - - - - - - + diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index 078760fb..9b7b2fb8 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -617,8 +617,57 @@ MonsterStrategy { # 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, + + ################### + # Goblin Bow Stuff + ################### + + Attack Reload Time = 2.0s + + # How long it takes to prepare the attack once an attack is queued. + Attack Windup Time = 1.0s + + Arrow Spd = 350 + + Arrow Hitbox Radius = 8 + + # The perception level indicates how accurate the bow user's shots become over time. Perception can be between 0-90. A perception level of 90 should never miss. This doesn't necessarily mean lower numbers will miss, just that it doesn't auto-correct for error as much. + Starting Perception Level = 0 + # Every shot taken, the bow user's perception level will increase by this amount. + Perception Level Increase = 2.5 + Maximum Perception Level = 45 + + ####################### + # END Goblin Bow Stuff + ####################### + + ################### + ### Boar Stuff + ################### + + Closein Range = 700 + + Backpedal Range = 400 + + # Number of times the boar scratches the ground before charging. + # The amount of time this takes is also dependent on the animation speed (extra animation 0) + Ground Scratch Count = 2 + + Charge Movespeed = 130% + + Charge Distance = 900 + + # Amount of time to wait after charging before returning to Move Phase. + Charge Recovery Time = 0.3s + + Backpedal Movespeed = 50% + + Charge Knockback Amount = 140 + + ################### + ### End Boar Stuff + ################### } } \ No newline at end of file diff --git a/Adventures in Lestoria/assets/config/Monsters.txt b/Adventures in Lestoria/assets/config/Monsters.txt index 2d3a3e76..97dcfb99 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -652,7 +652,7 @@ Monsters XP = 21 - Strategy = Run Towards + Strategy = Goblin Boar Rider #Size of each animation frame SheetFrameSize = 32,32 @@ -666,9 +666,9 @@ Monsters # 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 - JUMP = 1, 0.2, Repeat - SHOOT = 1, 0.2, OneShot - DEATH = 1, 0.15, OneShot + WALK = 4, 0.2, Repeat + SCRATCH = 5, 0.1, Repeat + DEATH = 4, 0.15, OneShot } Hurt Sound = Monster Hurt diff --git a/Adventures in Lestoria/assets/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak index 481122fd..fc0b839c 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/olcUTIL_DataFile.h b/Adventures in Lestoria/olcUTIL_DataFile.h index 22b01417..f0fbd72a 100644 --- a/Adventures in Lestoria/olcUTIL_DataFile.h +++ b/Adventures in Lestoria/olcUTIL_DataFile.h @@ -157,12 +157,12 @@ namespace olc::utils return m_vContent.size(); } - inline const std::vector&GetValues() + inline const std::vector&GetValues()const { return m_vContent; } - inline const std::unordered_map&GetKeys(){ + inline const std::unordered_map&GetKeys()const{ return m_mapObjects; } diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index a0c37167..b9fc84bd 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ