2023-11-20 23:25:36 -06:00
# pragma region License
2023-11-14 18:11:32 -06:00
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
2024-01-02 00:46:32 -06:00
Copyright 2024 Joshua Sigona < sigonasr2 @ gmail . com >
2023-11-14 18:11:32 -06:00
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 .
2023-11-29 00:50:00 -06:00
2024-01-30 14:48:49 +00:00
Portions of this software are copyright © 2024 The FreeType
2023-11-29 00:50:00 -06:00
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
2023-11-14 18:11:32 -06:00
*/
2023-11-20 23:25:36 -06:00
# pragma endregion
2023-06-11 20:03:30 -05:00
# include "Monster.h"
2023-06-11 21:19:45 -05:00
# include "Animation.h"
2023-08-06 19:00:09 -05:00
# include "config.h"
# include "DEFINES.h"
# include "safemap.h"
2023-11-22 01:12:09 -06:00
# include "Item.h"
2024-09-17 13:28:01 -05:00
# include "AdventuresInLestoria.h"
2023-06-11 20:03:30 -05:00
2023-08-06 19:00:09 -05:00
INCLUDE_DATA
INCLUDE_STRATEGY_DATA
2023-08-13 02:12:19 -05:00
INCLUDE_ANIMATION_DATA
2023-11-22 01:12:09 -06:00
INCLUDE_ITEM_DATA
2024-09-17 13:28:01 -05:00
INCLUDE_game
2023-07-01 20:47:18 -07:00
2023-12-19 00:15:47 -06:00
std : : map < std : : string , MonsterData > MONSTER_DATA ;
2023-07-01 20:47:18 -07:00
2023-09-06 23:07:15 -05:00
MonsterData : : MonsterData ( )
2023-12-19 00:15:47 -06:00
: atk ( 0 ) , collisionDmg ( 0 ) , hp ( 0 ) , moveSpd ( 0 ) , size ( 0 ) , strategy ( " Run Towards " ) { }
2024-05-20 17:34:16 -05:00
MonsterData : : MonsterData ( std : : string name , std : : string displayName , int hp , int atk , const uint32_t xp , std : : vector < MonsterDropData > drops , float moveSpd , float size , std : : string strategy , int collisionDmg ) :
2024-08-15 01:56:54 -05:00
name ( name ) , displayName ( displayName ) , hp ( hp ) , atk ( atk ) , xp ( xp ) , moveSpd ( moveSpd ) , size ( size ) , strategy ( strategy ) , dropData ( drops ) , collisionDmg ( collisionDmg ) , collisionRadius ( 8.f * this - > size ) { }
2023-08-13 02:12:19 -05:00
2023-08-06 19:00:09 -05:00
void MonsterData : : InitializeMonsterData ( ) {
2024-10-08 23:20:12 -05:00
MONSTER_DATA . clear ( ) ;
2023-12-19 00:15:47 -06:00
for ( auto & [ key , size ] : DATA [ " Monsters " ] . GetKeys ( ) ) {
std : : string MonsterName = key ;
2024-05-20 17:34:16 -05:00
std : : string MonsterImgName = MonsterName ;
std : : string MonsterDisplayName = MonsterName ;
2023-12-19 00:15:47 -06:00
if ( MONSTER_DATA . count ( key ) ) {
ERR ( " WARNING! A monster with the name " < < key < < " already exists in the database! Duplicates are not allowed. " )
}
2024-04-28 18:00:13 -05:00
std : : vector < std : : string > animations ;
2023-08-06 19:00:09 -05:00
2024-04-28 21:31:18 -05:00
bool hasFourWaySpriteSheet = false ;
2024-05-20 17:34:16 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Base Image Name " ) ) {
MonsterImgName = DATA [ " Monsters " ] [ MonsterName ] [ " Base Image Name " ] . GetString ( ) ;
}
if ( ! MonsterData : : imgs . count ( MonsterImgName ) ) {
MonsterData : : imgs [ MonsterImgName ] = NEW Renderable ( ) ;
2024-09-17 13:28:01 -05:00
const rcode imgLoadResult { game - > LoadResource ( * MonsterData : : imgs [ MonsterImgName ] , " assets/monsters/commercial_assets/ " + MonsterImgName + " .png " ) } ;
2024-05-20 17:34:16 -05:00
if ( imgLoadResult ! = OK ) ERR ( std : : format ( " WARNING! Image loading for Monster {} failed with result {} " , MonsterImgName , int ( imgLoadResult ) ) ) ;
}
2023-08-13 02:12:19 -05:00
2024-01-08 07:42:44 -06:00
EventName hurtSound = " " ;
EventName deathSound = " " ;
EventName walkSound = " " ;
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Hurt Sound " ) ) {
hurtSound = DATA [ " Monsters " ] [ MonsterName ] [ " Hurt Sound " ] . GetString ( ) ;
}
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Death Sound " ) ) {
deathSound = DATA [ " Monsters " ] [ MonsterName ] [ " Death Sound " ] . GetString ( ) ;
}
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Walk Sound " ) ) {
walkSound = DATA [ " Monsters " ] [ MonsterName ] [ " Walk Sound " ] . GetString ( ) ;
}
2024-01-15 01:30:06 -06:00
auto CreateHorizontalAnimationSequence = [ & ] ( Renderable & img , int frameCount , vf2d size , std : : string state , int row , AnimationData data = { } ) {
2024-05-20 17:34:16 -05:00
if ( ANIMATION_DATA . count ( state ) ) {
LOG ( std : : format ( " Animation sequence for {} with state {} at row {} already exists. Skipping. " , MonsterName , state , row ) ) ;
return ;
}
2024-01-15 01:30:06 -06:00
Animate2D : : FrameSequence anim ( data . frameDuration , data . style ) ;
for ( int i = 0 ; i < frameCount ; i + + ) {
anim . AddFrame ( { & img , { { int ( i * size . x ) , int ( row * size . y ) } , size } } ) ;
}
ANIMATION_DATA [ state ] = anim ;
} ;
2024-04-28 18:00:13 -05:00
if ( ! DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Animations " ) ) ERR ( std : : format ( " WARNING! Could not find any animations to load for monster {}! Please check the Monsters.txt configuration file! " , MonsterName ) ) ;
if ( DATA [ " Monsters " ] [ MonsterName ] [ " Animations " ] . GetKeys ( ) . size ( ) < 4 ) ERR ( std : : format ( " WARNING! Monster {} does not have at least 4 animations. The animations should be defined in this order: a standing, walking, attack, and death animation " , MonsterName ) ) ;
2024-04-28 19:31:06 -05:00
for ( size_t animationRow = 0 ; auto & [ animationName , data ] : DATA [ " Monsters " ] [ MonsterName ] [ " Animations " ] . GetOrderedKeys ( ) ) {
2023-08-13 02:12:19 -05:00
Animate2D : : Style style = Animate2D : : Style : : Repeat ;
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " Repeat " ) {
2023-08-13 02:12:19 -05:00
style = Animate2D : : Style : : Repeat ;
2024-01-15 01:30:06 -06:00
} else
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " OneShot " ) {
2023-08-13 02:12:19 -05:00
style = Animate2D : : Style : : OneShot ;
2024-01-15 01:30:06 -06:00
} else
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " PingPong " ) {
2023-08-13 02:12:19 -05:00
style = Animate2D : : Style : : PingPong ;
2024-01-15 01:30:06 -06:00
} else
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " Reverse " ) {
2023-08-13 02:12:19 -05:00
style = Animate2D : : Style : : Reverse ;
2024-07-14 07:59:13 -05:00
} else
if ( data . GetString ( 2 ) = = " ReverseOneShot " ) {
style = Animate2D : : Style : : ReverseOneShot ;
2024-01-15 01:30:06 -06:00
} else {
ERR ( std : : format ( " WARNING! Invalid Animation Style specified: {} " , int ( style ) ) ) ;
2023-08-13 02:12:19 -05:00
}
2024-04-28 19:31:06 -05:00
int frameCount = data . GetInt ( 0 ) ;
2024-01-15 01:30:06 -06:00
vf2d frameSize = vf2d { float ( DATA [ " Monsters " ] [ MonsterName ] [ " SheetFrameSize " ] . GetInt ( 0 ) ) , float ( DATA [ " Monsters " ] [ MonsterName ] [ " SheetFrameSize " ] . GetInt ( 1 ) ) } ;
2024-04-28 18:00:13 -05:00
2024-04-28 21:31:18 -05:00
if ( ! DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " 4-Way Spritesheet " ) ) ERR ( std : : format ( " WARNING! Monster {} does not have the property '4-Way Spritesheet' set " , MonsterName ) ) ;
if ( DATA [ " Monsters " ] [ MonsterName ] [ " 4-Way Spritesheet " ] . GetBool ( ) ) {
hasFourWaySpriteSheet = true ;
for ( int direction = 0 ; direction < 4 ; direction + + ) {
2024-07-02 05:38:01 -05:00
CreateHorizontalAnimationSequence ( * MonsterData : : imgs [ MonsterImgName ] , frameCount , frameSize , std : : format ( " {}_{}_{} " , MonsterName , animationName , direction ) , animationRow * 4 + direction , AnimationData { float ( data . GetReal ( 1 ) ) , style } ) ;
2024-04-28 21:31:18 -05:00
animations . push_back ( std : : format ( " {}_{} " , animationName , direction ) ) ;
}
} else {
2024-07-02 05:38:01 -05:00
CreateHorizontalAnimationSequence ( * MonsterData : : imgs [ MonsterImgName ] , frameCount , frameSize , std : : format ( " {}_{} " , MonsterName , animationName ) , animationRow , AnimationData { float ( data . GetReal ( 1 ) ) , style } ) ;
2024-04-28 21:31:18 -05:00
animations . push_back ( animationName ) ;
}
2023-08-13 02:12:19 -05:00
2024-04-28 18:00:13 -05:00
animationRow + + ;
2023-08-06 19:00:09 -05:00
}
2023-11-22 01:12:09 -06:00
std : : vector < MonsterDropData > drops ;
//Add drop items to monster data from the config.
int dropDataCounter = 0 ;
2023-12-19 00:15:47 -06:00
while ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " DROP[ " + std : : to_string ( dropDataCounter ) + " ] " ) ) {
datafile drop = DATA [ " Monsters " ] [ MonsterName ] [ " DROP[ " + std : : to_string ( dropDataCounter ) + " ] " ] ;
2023-11-22 01:12:09 -06:00
if ( ! ITEM_DATA . count ( drop . GetString ( 0 ) ) ) {
ERR ( " Could not add drop " < < drop . GetString ( 0 ) < < " to " < < MonsterName < < " 's drop table! Item does not exist! " ) ;
}
drops . push_back ( MonsterDropData { drop . GetString ( 0 ) , float ( drop . GetReal ( 1 ) ) , drop . GetInt ( 2 ) , drop . GetInt ( 3 ) } ) ;
dropDataCounter + + ;
}
2023-12-19 00:15:47 -06:00
const std : : string & strategyName = DATA [ " Monsters " ] [ MonsterName ] [ " Strategy " ] . GetString ( ) ;
2023-12-18 15:40:36 -06:00
if ( ! STRATEGY_DATA . count ( strategyName ) ) {
ERR ( " WARNING! Strategy for " < < MonsterName < < " does not exist in strategy database! " ) ;
}
2024-05-20 17:34:16 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Display Name " ) ) MonsterDisplayName = DATA [ " Monsters " ] [ MonsterName ] [ " Display Name " ] . GetString ( ) ;
2023-08-06 19:00:09 -05:00
MonsterData monster (
2023-08-13 02:12:19 -05:00
MonsterName ,
2024-05-20 17:34:16 -05:00
MonsterDisplayName ,
2023-12-19 00:15:47 -06:00
DATA [ " Monsters " ] [ MonsterName ] [ " Health " ] . GetInt ( ) ,
DATA [ " Monsters " ] [ MonsterName ] [ " Attack " ] . GetInt ( ) ,
2023-12-24 22:50:25 -06:00
DATA [ " Monsters " ] [ MonsterName ] [ " XP " ] . GetInt ( ) ,
2023-11-22 01:12:09 -06:00
drops ,
2024-01-14 12:53:40 -06:00
float ( DATA [ " Monsters " ] [ MonsterName ] [ " MoveSpd " ] . GetReal ( ) ) ,
2023-12-19 00:15:47 -06:00
float ( DATA [ " Monsters " ] [ MonsterName ] [ " Size " ] . GetReal ( ) ) / 100 ,
2023-12-18 15:40:36 -06:00
strategyName ,
2023-12-19 00:15:47 -06:00
DATA [ " Monsters " ] [ MonsterName ] [ " CollisionDmg " ] . GetInt ( )
2023-08-06 19:00:09 -05:00
) ;
2024-05-10 01:38:19 -05:00
2024-08-28 18:12:53 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Immovable " ) ) {
monster . immovable = DATA [ " Monsters " ] [ MonsterName ] [ " Immovable " ] . GetBool ( ) ;
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Fadeout " ) ) monster . fadeout = DATA [ " Monsters " ] [ MonsterName ] [ " Fadeout " ] . GetBool ( ) ;
else monster . fadeout = true ;
}
2024-08-28 17:38:43 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Fadeout " ) ) monster . fadeout = DATA [ " Monsters " ] [ MonsterName ] [ " Fadeout " ] . GetBool ( ) ;
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " No Facing " ) ) monster . noFacing = DATA [ " Monsters " ] [ MonsterName ] [ " No Facing " ] . GetBool ( ) ;
2024-05-10 01:38:19 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Ignore Collisions " ) ) monster . ignoresCollision = DATA [ " Monsters " ] [ MonsterName ] [ " Ignore Collisions " ] . GetBool ( ) ;
2024-05-04 18:19:47 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Mounted Animation " ) ) monster . mountedAnimName = DATA [ " Monsters " ] [ MonsterName ] [ " Mounted Animation " ] . GetString ( ) ;
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Mounted Animation Offset " ) ) {
if ( DATA [ " Monsters " ] [ MonsterName ] [ " Mounted Animation Offset " ] . GetValueCount ( ) = = 2 ) monster . mountedAnimationOffset = { DATA [ " Monsters " ] [ MonsterName ] [ " Mounted Animation Offset " ] . GetReal ( 0 ) , DATA [ " Monsters " ] [ MonsterName ] [ " Mounted Animation Offset " ] . GetReal ( 1 ) } ;
else ERR ( std : : format ( " WARNING! Monster {} containing a mounted animation offset has {} for reading in a vector, when vectors are supposed to only have two values! Please check the \" Mounted Animation Offset \" configuration value for {} " , MonsterName , DATA [ " Monsters " ] [ MonsterName ] [ " Mounted Animation Offset " ] . GetValueCount ( ) , MonsterName ) ) ;
}
2024-05-13 22:02:54 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Invulnerable " ) ) monster . invulnerable = DATA [ " Monsters " ] [ MonsterName ] [ " Invulnerable " ] . GetBool ( ) ;
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Lifetime " ) ) monster . lifetime = DATA [ " Monsters " ] [ MonsterName ] [ " Lifetime " ] . GetReal ( ) ;
2024-05-16 01:09:44 -05:00
2024-07-21 16:11:41 -05:00
monster . collisionRadius = 8 * ( std : : min ( DATA [ " Monsters " ] [ MonsterName ] [ " SheetFrameSize " ] . GetInt ( 0 ) , DATA [ " Monsters " ] [ MonsterName ] [ " SheetFrameSize " ] . GetInt ( 1 ) ) / 24.f ) ;
2024-05-18 05:04:52 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Collision Radius " ) ) monster . collisionRadius = DATA [ " Monsters " ] [ MonsterName ] [ " Collision Radius " ] . GetReal ( ) ;
2024-05-22 09:12:35 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " ShowBossIndicator " ) ) monster . hasArrowIndicator = DATA [ " Monsters " ] [ MonsterName ] [ " ShowBossIndicator " ] . GetBool ( ) ;
2024-10-01 23:00:02 -07:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Unconscious Time " ) ) monster . totalUnconsciousTime = DATA [ " Monsters " ] [ MonsterName ] [ " Unconscious Time " ] . GetReal ( ) ;
2025-02-14 00:22:19 -06:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Floating " ) ) monster . floating = DATA [ " Monsters " ] [ MonsterName ] [ " Floating " ] . GetBool ( ) ;
2024-05-04 18:19:47 -05:00
2024-07-11 08:03:37 -05:00
if ( DATA [ " Monsters " ] [ MonsterName ] . HasProperty ( " Rectangle Collision " ) ) {
const datafile & rectData { DATA [ " Monsters " ] [ MonsterName ] [ " Rectangle Collision " ] } ;
monster . rectCollision = { { rectData . GetReal ( 0 ) , rectData . GetReal ( 1 ) } , { rectData . GetReal ( 2 ) , rectData . GetReal ( 3 ) } } ;
}
2024-04-28 21:31:18 -05:00
if ( hasFourWaySpriteSheet ) monster . SetUsesFourWaySprites ( ) ;
2024-04-28 18:00:13 -05:00
for ( size_t animationRow = 0 ; const std : : string & animationName : animations ) {
if ( ! monster . animations . insert ( animationName ) . second ) ERR ( std : : format ( " WARNING! The Animation {} for Monster {} already exists! Animations should have unique names! " , animationName , MonsterName ) ) ;
2024-04-28 21:31:18 -05:00
if ( monster . HasFourWaySprites ( ) ) {
switch ( animationRow ) {
case 0 * 4 : monster . idleAnimation = animationName . substr ( 0 , animationName . length ( ) - 2 ) ; break ;
case 1 * 4 : monster . jumpAnimation = animationName . substr ( 0 , animationName . length ( ) - 2 ) ; break ;
case 2 * 4 : monster . shootAnimation = animationName . substr ( 0 , animationName . length ( ) - 2 ) ; break ;
case 3 * 4 : monster . deathAnimation = animationName . substr ( 0 , animationName . length ( ) - 2 ) ; break ;
}
} else {
switch ( animationRow ) {
case 0 : monster . idleAnimation = animationName ; break ;
case 1 : monster . jumpAnimation = animationName ; break ;
case 2 : monster . shootAnimation = animationName ; break ;
case 3 : monster . deathAnimation = animationName ; break ;
}
2024-04-28 18:00:13 -05:00
}
animationRow + + ;
}
2024-01-08 07:42:44 -06:00
monster . hurtSound = hurtSound ;
monster . deathSound = deathSound ;
monster . walkSound = walkSound ;
2023-12-19 00:15:47 -06:00
MONSTER_DATA [ MonsterName ] = monster ;
2023-08-06 19:00:09 -05:00
}
}
2024-01-29 00:27:16 -06:00
void MonsterData : : InitializeNPCData ( ) {
2024-01-29 18:27:08 +00:00
for ( auto & [ key , dataSize ] : DATA [ " NPCs " ] . GetKeys ( ) ) {
2024-01-29 00:27:16 -06:00
std : : string NPCName = key ;
if ( MONSTER_DATA . count ( key ) ) {
ERR ( " WARNING! A monster with the name " < < key < < " already exists in the database! Duplicates are not allowed. " )
}
2024-04-28 18:58:33 -05:00
std : : vector < std : : string > animations ;
2024-01-29 00:27:16 -06:00
MonsterData : : imgs [ NPCName ] = NEW Renderable ( ) ;
const rcode imgLoadResult = MonsterData : : imgs [ NPCName ] - > Load ( " assets/npcs/ " + NPCName + " .png " ) ;
if ( imgLoadResult ! = OK ) ERR ( std : : format ( " WARNING! Image loading for NPC {} failed with result {} " , NPCName , int ( imgLoadResult ) ) ) ;
EventName hurtSound = " " ;
EventName deathSound = " " ;
EventName walkSound = " " ;
int health = 100 ;
int attack = 0 ;
int xp = 0 ;
float moveSpd = 100.f ;
float size = 100.f ;
int collisionDmg = 0 ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Health " ) ) health = DATA [ " NPCs " ] [ NPCName ] [ " Health " ] . GetInt ( ) ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Attack " ) ) attack = DATA [ " NPCs " ] [ NPCName ] [ " Attack " ] . GetInt ( ) ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " XP " ) ) xp = DATA [ " NPCs " ] [ NPCName ] [ " XP " ] . GetInt ( ) ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " MoveSpd " ) ) moveSpd = DATA [ " NPCs " ] [ NPCName ] [ " MoveSpd " ] . GetReal ( ) ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Size " ) ) moveSpd = DATA [ " NPCs " ] [ NPCName ] [ " Size " ] . GetReal ( ) ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " CollisionDmg " ) ) collisionDmg = DATA [ " NPCs " ] [ NPCName ] [ " CollisionDmg " ] . GetInt ( ) ;
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Hurt Sound " ) ) {
hurtSound = DATA [ " NPCs " ] [ NPCName ] [ " Hurt Sound " ] . GetString ( ) ;
}
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Death Sound " ) ) {
deathSound = DATA [ " NPCs " ] [ NPCName ] [ " Death Sound " ] . GetString ( ) ;
}
if ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Walk Sound " ) ) {
walkSound = DATA [ " NPCs " ] [ NPCName ] [ " Walk Sound " ] . GetString ( ) ;
}
auto CreateHorizontalAnimationSequence = [ & ] ( Renderable & img , int frameCount , vf2d size , std : : string state , int row , AnimationData data = { } ) {
Animate2D : : FrameSequence anim ( data . frameDuration , data . style ) ;
for ( int i = 0 ; i < frameCount ; i + + ) {
anim . AddFrame ( { & img , { { int ( i * size . x ) , int ( row * size . y ) } , size } } ) ;
}
ANIMATION_DATA [ state ] = anim ;
} ;
2024-04-28 18:00:13 -05:00
if ( ! DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " Animations " ) ) ERR ( std : : format ( " WARNING! Could not find any animations to load for monster {}! Please check the Monsters.txt configuration file! " , NPCName ) ) ;
if ( DATA [ " NPCs " ] [ NPCName ] [ " Animations " ] . GetKeys ( ) . size ( ) < 4 ) ERR ( std : : format ( " WARNING! Monster {} does not have at least 4 animations. The animations should be defined in this order: a standing, walking, attack, and death animation " , NPCName ) ) ;
2024-04-28 19:31:06 -05:00
for ( size_t animationRow = 0 ; auto & [ animationName , data ] : DATA [ " NPCs " ] [ NPCName ] [ " Animations " ] . GetOrderedKeys ( ) ) {
2024-01-29 00:27:16 -06:00
Animate2D : : Style style = Animate2D : : Style : : Repeat ;
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " Repeat " ) {
2024-01-29 00:27:16 -06:00
style = Animate2D : : Style : : Repeat ;
} else
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " OneShot " ) {
2024-01-29 00:27:16 -06:00
style = Animate2D : : Style : : OneShot ;
} else
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " PingPong " ) {
2024-01-29 00:27:16 -06:00
style = Animate2D : : Style : : PingPong ;
} else
2024-04-28 19:31:06 -05:00
if ( data . GetString ( 2 ) = = " Reverse " ) {
2024-01-29 00:27:16 -06:00
style = Animate2D : : Style : : Reverse ;
2024-07-14 07:59:13 -05:00
} else
if ( data . GetString ( 2 ) = = " ReverseOneShot " ) {
style = Animate2D : : Style : : ReverseOneShot ;
2024-01-29 00:27:16 -06:00
} else {
ERR ( std : : format ( " WARNING! Invalid Animation Style specified: {} " , int ( style ) ) ) ;
}
2024-04-28 19:31:06 -05:00
int frameCount = data . GetInt ( 0 ) ;
2024-01-29 00:27:16 -06:00
vf2d frameSize = vf2d { float ( DATA [ " NPCs " ] [ NPCName ] [ " SheetFrameSize " ] . GetInt ( 0 ) ) , float ( DATA [ " NPCs " ] [ NPCName ] [ " SheetFrameSize " ] . GetInt ( 1 ) ) } ;
2024-04-28 19:31:06 -05:00
CreateHorizontalAnimationSequence ( * MonsterData : : imgs [ NPCName ] , frameCount , frameSize , std : : format ( " {}_{} " , NPCName , animationName ) , animationRow , AnimationData { float ( data . GetReal ( 1 ) ) , style } ) ;
2024-01-29 00:27:16 -06:00
2024-04-28 18:00:13 -05:00
animations . push_back ( animationName ) ;
animationRow + + ;
2024-01-29 00:27:16 -06:00
}
std : : vector < MonsterDropData > drops ;
//Add drop items to monster data from the config.
int dropDataCounter = 0 ;
while ( DATA [ " NPCs " ] [ NPCName ] . HasProperty ( " DROP[ " + std : : to_string ( dropDataCounter ) + " ] " ) ) {
datafile drop = DATA [ " NPCs " ] [ NPCName ] [ " DROP[ " + std : : to_string ( dropDataCounter ) + " ] " ] ;
if ( ! ITEM_DATA . count ( drop . GetString ( 0 ) ) ) {
ERR ( " Could not add drop " < < drop . GetString ( 0 ) < < " to " < < NPCName < < " 's drop table! Item does not exist! " ) ;
}
drops . push_back ( MonsterDropData { drop . GetString ( 0 ) , float ( drop . GetReal ( 1 ) ) , drop . GetInt ( 2 ) , drop . GetInt ( 3 ) } ) ;
dropDataCounter + + ;
}
const std : : string & strategyName = DATA [ " NPCs " ] [ NPCName ] [ " Strategy " ] . GetString ( ) ;
if ( ! STRATEGY_DATA . count ( strategyName ) ) {
ERR ( " WARNING! Strategy for " < < NPCName < < " does not exist in strategy database! " ) ;
}
2024-05-20 17:34:16 -05:00
MonsterData monster ( NPCName , NPCName , health , attack , xp , drops , moveSpd , size / 100 , strategyName , collisionDmg ) ;
2024-04-28 18:00:13 -05:00
for ( size_t animationRow = 0 ; const std : : string & animationName : animations ) {
2024-04-28 18:58:33 -05:00
if ( ! monster . animations . insert ( animationName ) . second ) ERR ( std : : format ( " WARNING! The Animation {} for Monster {} already exists! Animations should have unique names! " , animationName , NPCName ) ) ;
2024-04-28 18:00:13 -05:00
switch ( animationRow ) {
case 0 : monster . idleAnimation = animationName ; break ;
case 1 : monster . jumpAnimation = animationName ; break ;
case 2 : monster . shootAnimation = animationName ; break ;
case 3 : monster . deathAnimation = animationName ; break ;
}
animationRow + + ;
}
2024-01-29 00:27:16 -06:00
monster . hurtSound = hurtSound ;
monster . deathSound = deathSound ;
monster . walkSound = walkSound ;
2024-01-29 18:27:08 +00:00
monster . isNPC = true ; //If we read any data from this config file, it's definitely considered an NPC.
2024-01-29 00:27:16 -06:00
MONSTER_DATA [ NPCName ] = monster ;
}
}
2023-07-01 20:47:18 -07:00
int MonsterData : : GetHealth ( ) {
return hp ;
}
int MonsterData : : GetAttack ( ) {
return atk ;
}
float MonsterData : : GetMoveSpdMult ( ) {
return moveSpd ;
}
2024-05-18 05:04:52 -05:00
float MonsterData : : GetSizeMult ( ) const {
2023-07-01 20:47:18 -07:00
return size ;
}
int MonsterData : : GetCollisionDmg ( ) {
return collisionDmg ;
}
2024-01-26 00:48:32 -06:00
const std : : string & MonsterData : : GetAIStrategy ( ) const {
2023-07-01 20:47:18 -07:00
return strategy ;
}
2024-06-22 09:45:53 -05:00
const std : : string & MonsterData : : GetInternalName ( ) const {
return name ;
}
2024-05-20 17:34:16 -05:00
const std : : string & MonsterData : : GetDisplayName ( ) const {
return displayName ;
2023-11-11 17:31:53 -06:00
}
2023-07-01 20:47:18 -07:00
2024-04-28 21:31:18 -05:00
const std : : string MonsterData : : GetIdleAnimation ( const Direction & dir ) const {
if ( HasFourWaySprites ( ) ) return std : : format ( " {}_{} " , idleAnimation , int ( dir ) ) ;
else return idleAnimation ;
2023-08-13 02:12:19 -05:00
}
2024-04-28 21:31:18 -05:00
const std : : string MonsterData : : GetJumpAnimation ( const Direction & dir ) const {
if ( HasFourWaySprites ( ) ) return std : : format ( " {}_{} " , jumpAnimation , int ( dir ) ) ;
else return jumpAnimation ;
2023-07-01 20:47:18 -07:00
}
2024-04-28 21:31:18 -05:00
const std : : string MonsterData : : GetShootAnimation ( const Direction & dir ) const {
if ( HasFourWaySprites ( ) ) return std : : format ( " {}_{} " , shootAnimation , int ( dir ) ) ;
else return shootAnimation ;
2023-07-05 02:18:58 -05:00
}
2024-04-28 21:31:18 -05:00
const std : : string MonsterData : : GetDeathAnimation ( const Direction & dir ) const {
if ( HasFourWaySprites ( ) ) return std : : format ( " {}_{} " , deathAnimation , int ( dir ) ) ;
else return deathAnimation ;
2023-07-05 02:18:58 -05:00
}
2023-11-22 01:12:09 -06:00
const std : : vector < MonsterDropData > & MonsterData : : GetDropData ( ) {
return dropData ;
2024-01-08 07:42:44 -06:00
}
const EventName & MonsterData : : GetHurtSound ( ) {
return hurtSound ;
}
const EventName & MonsterData : : GetDeathSound ( ) {
return deathSound ;
}
const EventName & MonsterData : : GetWalkSound ( ) {
return walkSound ;
2024-04-28 21:31:18 -05:00
}
const bool MonsterData : : HasFourWaySprites ( ) const {
return fourWayDirectionalSprites ;
}
void MonsterData : : SetUsesFourWaySprites ( ) {
fourWayDirectionalSprites = true ;
}
const std : : string MonsterData : : GetDefaultIdleAnimation ( ) const {
return GetIdleAnimation ( Direction : : SOUTH ) ;
}
const std : : string MonsterData : : GetDefaultJumpAnimation ( ) const {
return GetJumpAnimation ( Direction : : SOUTH ) ;
}
const std : : string MonsterData : : GetDefaultShootAnimation ( ) const {
return GetShootAnimation ( Direction : : SOUTH ) ;
}
const std : : string MonsterData : : GetDefaultDeathAnimation ( ) const {
return GetDeathAnimation ( Direction : : SOUTH ) ;
}
2024-05-04 18:19:47 -05:00
const bool MonsterData : : HasMountedAnimation ( ) const {
return mountedAnimName . has_value ( ) ;
}
const std : : optional < const std : : string > MonsterData : : GetMountedAnimation ( ) const {
if ( ! HasMountedAnimation ( ) ) ERR ( " WARNING! Trying to get a mounted animation for a monster that doesn't have a mounted animation to begin with! " ) ;
return mountedAnimName . value ( ) ;
}
const vf2d & MonsterData : : GetMountedAnimationOffset ( ) const {
if ( ! HasMountedAnimation ( ) ) ERR ( " WARNING! Trying to get a mounted animation offset for a monster that doesn't have a mounted animation to begin with! " ) ;
return mountedAnimationOffset ;
2024-05-10 01:38:19 -05:00
}
const bool MonsterData : : IgnoresTerrainCollision ( ) const {
return ignoresCollision ;
2024-05-13 22:02:54 -05:00
}
const bool MonsterData : : Immovable ( ) const {
return immovable ;
}
const bool MonsterData : : Invulnerable ( ) const {
return invulnerable ;
}
const std : : optional < float > MonsterData : : GetLifetime ( ) const {
return lifetime ;
}
2024-05-16 01:09:44 -05:00
const float MonsterData : : GetCollisionRadius ( ) const {
2024-05-24 13:48:39 -05:00
return collisionRadius * 0.6f ;
2024-05-22 09:12:35 -05:00
}
const bool MonsterData : : HasArrowIndicator ( ) const {
return hasArrowIndicator ;
2024-07-11 08:03:37 -05:00
}
const std : : optional < geom2d : : rect < float > > & MonsterData : : GetRectangleCollision ( ) const {
return rectCollision ;
2024-08-28 17:38:43 -05:00
}
const bool MonsterData : : FadeoutWhenStandingBehind ( ) const {
return fadeout ;
}
const bool MonsterData : : FaceTarget ( ) const {
return ! noFacing ;
2024-10-01 23:00:02 -07:00
}
const float MonsterData : : UnconsciousTime ( ) const {
return totalUnconsciousTime ;
2025-02-14 00:22:19 -06:00
}
const bool MonsterData : : IsFloating ( ) const {
return floating ;
2024-05-04 18:19:47 -05:00
}