The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
440 lines
19 KiB
440 lines
19 KiB
#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 "Monster.h"
|
|
#include "Animation.h"
|
|
#include "config.h"
|
|
#include "DEFINES.h"
|
|
#include "safemap.h"
|
|
#include "Item.h"
|
|
|
|
INCLUDE_DATA
|
|
INCLUDE_STRATEGY_DATA
|
|
INCLUDE_ANIMATION_DATA
|
|
INCLUDE_ITEM_DATA
|
|
|
|
std::map<std::string,MonsterData>MONSTER_DATA;
|
|
|
|
MonsterData::MonsterData()
|
|
:atk(0),collisionDmg(0),hp(0),moveSpd(0),size(0),strategy("Run Towards"){}
|
|
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):
|
|
name(name),displayName(displayName),hp(hp),atk(atk),xp(xp),moveSpd(moveSpd),size(size),strategy(strategy),dropData(drops),collisionDmg(collisionDmg){}
|
|
|
|
void MonsterData::InitializeMonsterData(){
|
|
for(auto&[key,size]:DATA["Monsters"].GetKeys()){
|
|
std::string MonsterName=key;
|
|
std::string MonsterImgName=MonsterName;
|
|
std::string MonsterDisplayName=MonsterName;
|
|
if(MONSTER_DATA.count(key)){
|
|
ERR("WARNING! A monster with the name "<<key<<" already exists in the database! Duplicates are not allowed.")
|
|
}
|
|
std::vector<std::string>animations;
|
|
|
|
bool hasFourWaySpriteSheet=false;
|
|
|
|
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();
|
|
const rcode imgLoadResult=MonsterData::imgs[MonsterImgName]->Load("assets/monsters/commercial_assets/"+MonsterImgName+".png");
|
|
if(imgLoadResult!=OK)ERR(std::format("WARNING! Image loading for Monster {} failed with result {}",MonsterImgName,int(imgLoadResult)));
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
auto CreateHorizontalAnimationSequence=[&](Renderable&img,int frameCount,vf2d size,std::string state,int row,AnimationData data={}){
|
|
if(ANIMATION_DATA.count(state)){
|
|
LOG(std::format("Animation sequence for {} with state {} at row {} already exists. Skipping.",MonsterName,state,row));
|
|
return;
|
|
}
|
|
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;
|
|
};
|
|
|
|
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));
|
|
for(size_t animationRow=0;auto&[animationName,data]:DATA["Monsters"][MonsterName]["Animations"].GetOrderedKeys()){
|
|
Animate2D::Style style=Animate2D::Style::Repeat;
|
|
if(data.GetString(2)=="Repeat"){
|
|
style=Animate2D::Style::Repeat;
|
|
}else
|
|
if(data.GetString(2)=="OneShot"){
|
|
style=Animate2D::Style::OneShot;
|
|
}else
|
|
if(data.GetString(2)=="PingPong"){
|
|
style=Animate2D::Style::PingPong;
|
|
}else
|
|
if(data.GetString(2)=="Reverse"){
|
|
style=Animate2D::Style::Reverse;
|
|
}else{
|
|
ERR(std::format("WARNING! Invalid Animation Style specified: {}",int(style)));
|
|
}
|
|
|
|
int frameCount=data.GetInt(0);
|
|
vf2d frameSize=vf2d{float(DATA["Monsters"][MonsterName]["SheetFrameSize"].GetInt(0)),float(DATA["Monsters"][MonsterName]["SheetFrameSize"].GetInt(1))};
|
|
|
|
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++){
|
|
CreateHorizontalAnimationSequence(*MonsterData::imgs[MonsterImgName],frameCount,frameSize,std::format("{}_{}_{}",MonsterImgName,animationName,direction),animationRow*4+direction,AnimationData{float(data.GetReal(1)),style});
|
|
|
|
animations.push_back(std::format("{}_{}",animationName,direction));
|
|
}
|
|
}else{
|
|
CreateHorizontalAnimationSequence(*MonsterData::imgs[MonsterImgName],frameCount,frameSize,std::format("{}_{}",MonsterImgName,animationName),animationRow,AnimationData{float(data.GetReal(1)),style});
|
|
|
|
animations.push_back(animationName);
|
|
}
|
|
|
|
animationRow++;
|
|
}
|
|
|
|
std::vector<MonsterDropData>drops;
|
|
|
|
//Add drop items to monster data from the config.
|
|
int dropDataCounter=0;
|
|
while(DATA["Monsters"][MonsterName].HasProperty("DROP["+std::to_string(dropDataCounter)+"]")){
|
|
datafile drop=DATA["Monsters"][MonsterName]["DROP["+std::to_string(dropDataCounter)+"]"];
|
|
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++;
|
|
}
|
|
|
|
const std::string&strategyName=DATA["Monsters"][MonsterName]["Strategy"].GetString();
|
|
if(!STRATEGY_DATA.count(strategyName)){
|
|
ERR("WARNING! Strategy for "<<MonsterName<<" does not exist in strategy database!");
|
|
}
|
|
|
|
if(DATA["Monsters"][MonsterName].HasProperty("Display Name"))MonsterDisplayName=DATA["Monsters"][MonsterName]["Display Name"].GetString();
|
|
|
|
MonsterData monster(
|
|
MonsterName,
|
|
MonsterDisplayName,
|
|
DATA["Monsters"][MonsterName]["Health"].GetInt(),
|
|
DATA["Monsters"][MonsterName]["Attack"].GetInt(),
|
|
DATA["Monsters"][MonsterName]["XP"].GetInt(),
|
|
drops,
|
|
float(DATA["Monsters"][MonsterName]["MoveSpd"].GetReal()),
|
|
float(DATA["Monsters"][MonsterName]["Size"].GetReal())/100,
|
|
strategyName,
|
|
DATA["Monsters"][MonsterName]["CollisionDmg"].GetInt()
|
|
);
|
|
|
|
if(DATA["Monsters"][MonsterName].HasProperty("Ignore Collisions"))monster.ignoresCollision=DATA["Monsters"][MonsterName]["Ignore Collisions"].GetBool();
|
|
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));
|
|
}
|
|
if(DATA["Monsters"][MonsterName].HasProperty("Immovable"))monster.immovable=DATA["Monsters"][MonsterName]["Immovable"].GetBool();
|
|
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();
|
|
|
|
monster.collisionRadius=8;
|
|
if(DATA["Monsters"][MonsterName].HasProperty("Collision Radius"))monster.collisionRadius=DATA["Monsters"][MonsterName]["Collision Radius"].GetReal();
|
|
|
|
if(hasFourWaySpriteSheet)monster.SetUsesFourWaySprites();
|
|
|
|
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));
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
animationRow++;
|
|
}
|
|
|
|
monster.hurtSound=hurtSound;
|
|
monster.deathSound=deathSound;
|
|
monster.walkSound=walkSound;
|
|
|
|
MONSTER_DATA[MonsterName]=monster;
|
|
}
|
|
}
|
|
void MonsterData::InitializeNPCData(){
|
|
for(auto&[key,dataSize]:DATA["NPCs"].GetKeys()){
|
|
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.")
|
|
}
|
|
std::vector<std::string>animations;
|
|
|
|
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;
|
|
};
|
|
|
|
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));
|
|
for(size_t animationRow=0;auto&[animationName,data]:DATA["NPCs"][NPCName]["Animations"].GetOrderedKeys()){
|
|
Animate2D::Style style=Animate2D::Style::Repeat;
|
|
if(data.GetString(2)=="Repeat"){
|
|
style=Animate2D::Style::Repeat;
|
|
}else
|
|
if(data.GetString(2)=="OneShot"){
|
|
style=Animate2D::Style::OneShot;
|
|
}else
|
|
if(data.GetString(2)=="PingPong"){
|
|
style=Animate2D::Style::PingPong;
|
|
}else
|
|
if(data.GetString(2)=="Reverse"){
|
|
style=Animate2D::Style::Reverse;
|
|
}else{
|
|
ERR(std::format("WARNING! Invalid Animation Style specified: {}",int(style)));
|
|
}
|
|
|
|
int frameCount=data.GetInt(0);
|
|
vf2d frameSize=vf2d{float(DATA["NPCs"][NPCName]["SheetFrameSize"].GetInt(0)),float(DATA["NPCs"][NPCName]["SheetFrameSize"].GetInt(1))};
|
|
CreateHorizontalAnimationSequence(*MonsterData::imgs[NPCName],frameCount,frameSize,std::format("{}_{}",NPCName,animationName),animationRow,AnimationData{float(data.GetReal(1)),style});
|
|
|
|
animations.push_back(animationName);
|
|
|
|
animationRow++;
|
|
}
|
|
|
|
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!");
|
|
}
|
|
|
|
MonsterData monster(NPCName,NPCName,health,attack,xp,drops,moveSpd,size/100,strategyName,collisionDmg);
|
|
|
|
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,NPCName));
|
|
|
|
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++;
|
|
}
|
|
|
|
monster.hurtSound=hurtSound;
|
|
monster.deathSound=deathSound;
|
|
monster.walkSound=walkSound;
|
|
monster.isNPC=true; //If we read any data from this config file, it's definitely considered an NPC.
|
|
|
|
MONSTER_DATA[NPCName]=monster;
|
|
}
|
|
}
|
|
int MonsterData::GetHealth(){
|
|
return hp;
|
|
}
|
|
int MonsterData::GetAttack(){
|
|
return atk;
|
|
}
|
|
float MonsterData::GetMoveSpdMult(){
|
|
return moveSpd;
|
|
}
|
|
float MonsterData::GetSizeMult()const{
|
|
return size;
|
|
}
|
|
int MonsterData::GetCollisionDmg(){
|
|
return collisionDmg;
|
|
}
|
|
const std::string&MonsterData::GetAIStrategy()const{
|
|
return strategy;
|
|
}
|
|
const std::string&MonsterData::GetDisplayName()const{
|
|
return displayName;
|
|
}
|
|
|
|
const std::string MonsterData::GetIdleAnimation(const Direction&dir)const{
|
|
if(HasFourWaySprites())return std::format("{}_{}",idleAnimation,int(dir));
|
|
else return idleAnimation;
|
|
}
|
|
const std::string MonsterData::GetJumpAnimation(const Direction&dir)const{
|
|
if(HasFourWaySprites())return std::format("{}_{}",jumpAnimation,int(dir));
|
|
else return jumpAnimation;
|
|
}
|
|
const std::string MonsterData::GetShootAnimation(const Direction&dir)const{
|
|
if(HasFourWaySprites())return std::format("{}_{}",shootAnimation,int(dir));
|
|
else return shootAnimation;
|
|
}
|
|
const std::string MonsterData::GetDeathAnimation(const Direction&dir)const{
|
|
if(HasFourWaySprites())return std::format("{}_{}",deathAnimation,int(dir));
|
|
else return deathAnimation;
|
|
}
|
|
const std::vector<MonsterDropData>&MonsterData::GetDropData(){
|
|
return dropData;
|
|
}
|
|
|
|
|
|
const EventName&MonsterData::GetHurtSound(){
|
|
return hurtSound;
|
|
}
|
|
const EventName&MonsterData::GetDeathSound(){
|
|
return deathSound;
|
|
}
|
|
const EventName&MonsterData::GetWalkSound(){
|
|
return walkSound;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const bool MonsterData::IgnoresTerrainCollision()const{
|
|
return ignoresCollision;
|
|
}
|
|
|
|
const bool MonsterData::Immovable()const{
|
|
return immovable;
|
|
}
|
|
const bool MonsterData::Invulnerable()const{
|
|
return invulnerable;
|
|
}
|
|
const std::optional<float>MonsterData::GetLifetime()const{
|
|
return lifetime;
|
|
}
|
|
const float MonsterData::GetCollisionRadius()const{
|
|
return collisionRadius;
|
|
} |