Change monster data image storing to use an unordered_map instead. Create an optional display name for monsters to use when monster data is being generated to allow for name overrides while retaining unique monster types. Added No XP variant of Hawks. Setup Chapter 2 Bonus Boss spawn. Release Build 9362.

mac-build
sigonasr2 6 months ago
parent d6409f4604
commit a39551c356
  1. 8
      Adventures in Lestoria/Adventures in Lestoria.tiled-project
  2. 1
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  3. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  4. 2
      Adventures in Lestoria/AdventuresInLestoria.cpp
  5. 56
      Adventures in Lestoria/Crawler_2_Bonus_Boss.txt
  6. 12
      Adventures in Lestoria/Merchant.cpp
  7. 6
      Adventures in Lestoria/Monster.cpp
  8. 1
      Adventures in Lestoria/Monster.h
  9. 35
      Adventures in Lestoria/MonsterData.cpp
  10. 7
      Adventures in Lestoria/MonsterData.h
  11. 2
      Adventures in Lestoria/NPC.cpp
  12. 2
      Adventures in Lestoria/SpawnEncounterLabel.h
  13. 2
      Adventures in Lestoria/Version.h
  14. 54
      Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx
  15. 45
      Adventures in Lestoria/assets/config/Monsters.txt
  16. 5
      Adventures in Lestoria/assets/maps/Monsters/Hawk_NOXP.tx
  17. BIN
      x64/Release/Adventures in Lestoria.exe

@ -455,13 +455,7 @@
"type": "class", "type": "class",
"useAs": [ "useAs": [
"property", "property",
"map", "object"
"layer",
"object",
"tile",
"tileset",
"wangcolor",
"wangset"
] ]
}, },
{ {

@ -975,6 +975,7 @@
<Text Include="Chapter_1_Creatures_Part_2.txt" /> <Text Include="Chapter_1_Creatures_Part_2.txt" />
<Text Include="Chapter_2_Monsters.txt" /> <Text Include="Chapter_2_Monsters.txt" />
<Text Include="characters.txt" /> <Text Include="characters.txt" />
<Text Include="Crawler_2_Bonus_Boss.txt" />
<Text Include="Crawler_System_Overview.txt" /> <Text Include="Crawler_System_Overview.txt" />
<Text Include="Merchant%27s Items.txt" /> <Text Include="Merchant%27s Items.txt" />
<Text Include="NewClasses.txt" /> <Text Include="NewClasses.txt" />

@ -1226,6 +1226,9 @@
<Filter>Configurations</Filter> <Filter>Configurations</Filter>
</Text> </Text>
<Text Include="Chapter_2_Monsters.txt" /> <Text Include="Chapter_2_Monsters.txt" />
<Text Include="Crawler_2_Bonus_Boss.txt">
<Filter>Documentation\Mechanics</Filter>
</Text>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="assets\heart.ico"> <Image Include="assets\heart.ico">

@ -3279,7 +3279,7 @@ void AiL::InitializeLevels(){
if(MAP_DATA[cp.map].GetMapType()=="Boss"&&MAP_DATA[cp.map].GetZones().at("BossArena").size()==0)ERR(std::format("WARNING! Map {} doesn't have a boss arena region defined! Add an object of class BossArena to the map.",MAP_DATA[cp.map].GetMapDisplayName())); if(MAP_DATA[cp.map].GetMapType()=="Boss"&&MAP_DATA[cp.map].GetZones().at("BossArena").size()==0)ERR(std::format("WARNING! Map {} doesn't have a boss arena region defined! Add an object of class BossArena to the map.",MAP_DATA[cp.map].GetMapDisplayName()));
for(std::string spawn:MAP_DATA[cp.map].spawns){ for(std::string spawn:MAP_DATA[cp.map].spawns){
cp.spawns.push_back(spawn); cp.spawns.push_back(MONSTER_DATA.at(spawn).GetDisplayName());
} }
cp.levelDataExists=true; cp.levelDataExists=true;
} }

@ -0,0 +1,56 @@
Eagle Boss - King of Birds, Zephy
On entering boss arena:
8 basic Hawks appear, no exp/dropp
after defeat:
2 Major Hawks appear.
Major Hawks
Hp: 520
Attack: 31
Size: 120%
Move-Spd: 220%
no exp or dropps
As long both are active normal Hawk Strategie:
Attack Strategie: Fly circles at the edge of the Players Screen. every 8 seconds charge on players location to the other corner of the screen.
If only 1 is active instead of every 8 seconds charge every 3.
Once those are defeated the boss spawns.
Boss
Hp: 7000
Attack: 48
Size: 400%
Move-Spd: 180%
From now on every 10 seconds a basic Hawk spawns.
It uses the following attack moves in random order:
- Flys from left to right or right to left in the top of the Screen and shits down. (25 dmg on hit)
- Lands in the middle of the boss arena (in the area the player is at) and create small Tornados around himself which move in a circle (size: ~80% spd: 90%, )
- Flys to the Central top of the arena and does a Fan of feathers. Feathers fly until the end of the arena. you cant outrange it. there are a few gaps inbetween to dodge though.
(8 dmg each, can be hit from multiple at once)
Repeats this move once.
- lands on the left or right side of the arena and starts to create alot of wind with his wings. moving towards the boss slows the movespeed running away increases the move speed.
starts with +/- 10% and increases by another 10% every second until it reaches 60%.
On full strength: Projectiles of auto attacks get blown away. Objects appear that deal damage on collision. (Bushes & stones from forest tileset. if possible spinning)
Collision dmg 40.
Full strength lasts for 12 Seconds.
50%-Phase
- Boss lands on the Pillar in the top Middle of the arena and creates and tornado. Player gets constantly slightly sucked towards it.
- The Tornado shoots feather projectiles in random directions.
(10 dmg, amount of feathers needs a bit of testing.
it needs to be enough that it is some kind of threat but not to much
that you are just overwhelmed by them while fighting the other birds.)
- the 10 second Hawk spawner is disabled until boss reappears.
- 2 Major hawks spawn + 4 Basic Hawks
- after every Hawk is defeated the fight goes back to normal mode. (which are the 6 spawned + any bird that still spawned in the fight before and didnt got killed yet)

@ -150,14 +150,14 @@ bool Merchant::CanPurchaseItem(IT item,uint32_t amt)const{
itemAvailable&& itemAvailable&&
!foundItem.expired()&& !foundItem.expired()&&
game->GetPlayer()->GetMoney()>=foundItem.lock()->BuyValue()*amt; game->GetPlayer()->GetMoney()>=foundItem.lock()->BuyValue()*amt;
}; }
bool Merchant::CanSellItem(std::weak_ptr<Item>item,uint32_t amt)const{ bool Merchant::CanSellItem(std::weak_ptr<Item>item,uint32_t amt)const{
sellFunctionPrimed.amt=amt; sellFunctionPrimed.amt=amt;
sellFunctionPrimed.item=item.lock()->ActualName(); sellFunctionPrimed.item=item.lock()->ActualName();
return sellFunctionPrimed= return sellFunctionPrimed=
item.lock()->CanBeSold()&& item.lock()->CanBeSold()&&
item.lock()->Amt()>=amt; item.lock()->Amt()>=amt;
}; }
void Merchant::PurchaseItem(IT item,uint32_t amt){ void Merchant::PurchaseItem(IT item,uint32_t amt){
purchaseFunctionPrimed.Validate(item,amt); purchaseFunctionPrimed.Validate(item,amt);
@ -185,7 +185,7 @@ void Merchant::PurchaseItem(IT item,uint32_t amt){
Inventory::AddItem(item,amt); Inventory::AddItem(item,amt);
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost); game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost);
}; }
void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){ void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){
sellFunctionPrimed.Validate(item.lock()->ActualName(),amt); sellFunctionPrimed.Validate(item.lock()->ActualName(),amt);
@ -230,14 +230,14 @@ void Merchant::SellItem(std::weak_ptr<Item>item,uint32_t amt){
} }
sellFunctionPrimed=false; sellFunctionPrimed=false;
}; }
void Merchant::RandomizeTravelingMerchant(){ void Merchant::RandomizeTravelingMerchant(){
SetTravelingMerchant(const_cast<Merchant&>(GetRandomMerchant(game->GetCurrentChapter())).GetKeyName()); SetTravelingMerchant(const_cast<Merchant&>(GetRandomMerchant(game->GetCurrentChapter())).GetKeyName());
}; }
Merchant&Merchant::GetCurrentTravelingMerchant(){ Merchant&Merchant::GetCurrentTravelingMerchant(){
return travelingMerchant; return travelingMerchant;
}; }
std::string_view Merchant::GetKeyName()const{ std::string_view Merchant::GetKeyName()const{
return keyName; return keyName;

@ -62,7 +62,7 @@ INCLUDE_DATA
INCLUDE_GFX INCLUDE_GFX
safemap<std::string,std::function<void(Monster&,float,std::string)>>STRATEGY_DATA; safemap<std::string,std::function<void(Monster&,float,std::string)>>STRATEGY_DATA;
std::map<std::string,Renderable*>MonsterData::imgs; std::unordered_map<std::string,Renderable*>MonsterData::imgs;
Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob): Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob):
pos(pos),spawnPos(pos),hp(data.GetHealth()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(Direction::SOUTH),lifetime(GetTotalLifetime()){ pos(pos),spawnPos(pos),hp(data.GetHealth()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(Direction::SOUTH),lifetime(GetTotalLifetime()){
@ -1056,4 +1056,8 @@ const bool Monster::IsDead()const{
void Monster::SetIframes(const float iframeTime){ void Monster::SetIframes(const float iframeTime){
iframe_timer=iframeTime; iframe_timer=iframeTime;
}
const std::string_view Monster::GetDisplayName()const{
return MONSTER_DATA.at(GetName()).GetDisplayName();
} }

@ -174,6 +174,7 @@ public:
//If an object has a collision radius, returns it. //If an object has a collision radius, returns it.
const float GetCollisionRadius()const; const float GetCollisionRadius()const;
const bool IsDead()const; const bool IsDead()const;
const std::string_view GetDisplayName()const;
private: private:
//NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!! //NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!!
// The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop. // The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop.

@ -51,12 +51,14 @@ std::map<std::string,MonsterData>MONSTER_DATA;
MonsterData::MonsterData() MonsterData::MonsterData()
:atk(0),collisionDmg(0),hp(0),moveSpd(0),size(0),strategy("Run Towards"){} :atk(0),collisionDmg(0),hp(0),moveSpd(0),size(0),strategy("Run Towards"){}
MonsterData::MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vector<MonsterDropData>drops,float moveSpd,float size,std::string strategy,int collisionDmg): 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),hp(hp),atk(atk),xp(xp),moveSpd(moveSpd),size(size),strategy(strategy),dropData(drops),collisionDmg(collisionDmg){} name(name),displayName(displayName),hp(hp),atk(atk),xp(xp),moveSpd(moveSpd),size(size),strategy(strategy),dropData(drops),collisionDmg(collisionDmg){}
void MonsterData::InitializeMonsterData(){ void MonsterData::InitializeMonsterData(){
for(auto&[key,size]:DATA["Monsters"].GetKeys()){ for(auto&[key,size]:DATA["Monsters"].GetKeys()){
std::string MonsterName=key; std::string MonsterName=key;
std::string MonsterImgName=MonsterName;
std::string MonsterDisplayName=MonsterName;
if(MONSTER_DATA.count(key)){ if(MONSTER_DATA.count(key)){
ERR("WARNING! A monster with the name "<<key<<" already exists in the database! Duplicates are not allowed.") ERR("WARNING! A monster with the name "<<key<<" already exists in the database! Duplicates are not allowed.")
} }
@ -64,9 +66,15 @@ void MonsterData::InitializeMonsterData(){
bool hasFourWaySpriteSheet=false; bool hasFourWaySpriteSheet=false;
MonsterData::imgs[MonsterName]=NEW Renderable(); if(DATA["Monsters"][MonsterName].HasProperty("Base Image Name")){
const rcode imgLoadResult=MonsterData::imgs[MonsterName]->Load("assets/monsters/commercial_assets/"+MonsterName+".png"); MonsterImgName=DATA["Monsters"][MonsterName]["Base Image Name"].GetString();
if(imgLoadResult!=OK)ERR(std::format("WARNING! Image loading for Monster {} failed with result {}",MonsterName,int(imgLoadResult))); }
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 hurtSound="";
EventName deathSound=""; EventName deathSound="";
@ -83,6 +91,10 @@ void MonsterData::InitializeMonsterData(){
} }
auto CreateHorizontalAnimationSequence=[&](Renderable&img,int frameCount,vf2d size,std::string state,int row,AnimationData data={}){ 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); Animate2D::FrameSequence anim(data.frameDuration,data.style);
for(int i=0;i<frameCount;i++){ for(int i=0;i<frameCount;i++){
anim.AddFrame({&img,{{int(i*size.x),int(row*size.y)},size}}); anim.AddFrame({&img,{{int(i*size.x),int(row*size.y)},size}});
@ -117,12 +129,12 @@ void MonsterData::InitializeMonsterData(){
if(DATA["Monsters"][MonsterName]["4-Way Spritesheet"].GetBool()){ if(DATA["Monsters"][MonsterName]["4-Way Spritesheet"].GetBool()){
hasFourWaySpriteSheet=true; hasFourWaySpriteSheet=true;
for(int direction=0;direction<4;direction++){ for(int direction=0;direction<4;direction++){
CreateHorizontalAnimationSequence(*MonsterData::imgs[MonsterName],frameCount,frameSize,std::format("{}_{}_{}",MonsterName,animationName,direction),animationRow*4+direction,AnimationData{float(data.GetReal(1)),style}); 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)); animations.push_back(std::format("{}_{}",animationName,direction));
} }
}else{ }else{
CreateHorizontalAnimationSequence(*MonsterData::imgs[MonsterName],frameCount,frameSize,std::format("{}_{}",MonsterName,animationName),animationRow,AnimationData{float(data.GetReal(1)),style}); CreateHorizontalAnimationSequence(*MonsterData::imgs[MonsterImgName],frameCount,frameSize,std::format("{}_{}",MonsterImgName,animationName),animationRow,AnimationData{float(data.GetReal(1)),style});
animations.push_back(animationName); animations.push_back(animationName);
} }
@ -148,8 +160,11 @@ void MonsterData::InitializeMonsterData(){
ERR("WARNING! Strategy for "<<MonsterName<<" does not exist in strategy database!"); 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( MonsterData monster(
MonsterName, MonsterName,
MonsterDisplayName,
DATA["Monsters"][MonsterName]["Health"].GetInt(), DATA["Monsters"][MonsterName]["Health"].GetInt(),
DATA["Monsters"][MonsterName]["Attack"].GetInt(), DATA["Monsters"][MonsterName]["Attack"].GetInt(),
DATA["Monsters"][MonsterName]["XP"].GetInt(), DATA["Monsters"][MonsterName]["XP"].GetInt(),
@ -299,7 +314,7 @@ void MonsterData::InitializeNPCData(){
ERR("WARNING! Strategy for "<<NPCName<<" does not exist in strategy database!"); ERR("WARNING! Strategy for "<<NPCName<<" does not exist in strategy database!");
} }
MonsterData monster(NPCName,health,attack,xp,drops,moveSpd,size/100,strategyName,collisionDmg); MonsterData monster(NPCName,NPCName,health,attack,xp,drops,moveSpd,size/100,strategyName,collisionDmg);
for(size_t animationRow=0;const std::string&animationName:animations){ 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)); if(!monster.animations.insert(animationName).second)ERR(std::format("WARNING! The Animation {} for Monster {} already exists! Animations should have unique names!",animationName,NPCName));
@ -340,8 +355,8 @@ int MonsterData::GetCollisionDmg(){
const std::string&MonsterData::GetAIStrategy()const{ const std::string&MonsterData::GetAIStrategy()const{
return strategy; return strategy;
} }
std::string MonsterData::GetDisplayName(){ const std::string&MonsterData::GetDisplayName()const{
return name; return displayName;
} }
const std::string MonsterData::GetIdleAnimation(const Direction&dir)const{ const std::string MonsterData::GetIdleAnimation(const Direction&dir)const{

@ -61,7 +61,7 @@ struct MonsterDropData{
struct MonsterData{ struct MonsterData{
public: public:
MonsterData(); MonsterData();
MonsterData(std::string name,int hp,int atk,const uint32_t xp,std::vector<MonsterDropData>drops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0); MonsterData(std::string name,std::string displayName,int hp,int atk,const uint32_t xp,std::vector<MonsterDropData>drops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0);
int GetHealth(); int GetHealth();
int GetAttack(); int GetAttack();
const uint32_t GetXP()const; const uint32_t GetXP()const;
@ -85,10 +85,10 @@ public:
return animations; return animations;
} }
const std::vector<MonsterDropData>&GetDropData(); const std::vector<MonsterDropData>&GetDropData();
std::string GetDisplayName(); const std::string&GetDisplayName()const;
static void InitializeMonsterData(); static void InitializeMonsterData();
static void InitializeNPCData(); static void InitializeNPCData();
static std::map<std::string,Renderable*>imgs; static std::unordered_map<std::string,Renderable*>imgs;
const bool HasFourWaySprites()const; const bool HasFourWaySprites()const;
void SetUsesFourWaySprites(); void SetUsesFourWaySprites();
const bool HasMountedAnimation()const; const bool HasMountedAnimation()const;
@ -109,6 +109,7 @@ private:
float moveSpd;//1.0=100% float moveSpd;//1.0=100%
float size; float size;
std::unordered_set<std::string>animations; std::unordered_set<std::string>animations;
std::string displayName;
std::string idleAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_IDLE, but IDLE) std::string idleAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_IDLE, but IDLE)
std::string jumpAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_JUMP, but JUMP) std::string jumpAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_JUMP, but JUMP)
std::string shootAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_SHOOT, but SHOOT) std::string shootAnimation="WARRIOR_IDLE_S"; //Represents the basic animation name, not the full animation name that ANIMATION_DATA indexes into!! (Ex. Not GREEN_SLIME_SHOOT, but SHOOT)

@ -57,7 +57,7 @@ void Monster::STRATEGY::NPC(Monster&m,float fElapsedTime,std::string strategy){
m.SetStrategyDrawOverlayFunction([](AiL*game,Monster&m,const std::string&strategy){ m.SetStrategyDrawOverlayFunction([](AiL*game,Monster&m,const std::string&strategy){
vf2d nameTextSize=game->GetTextSizeProp(m.GetName()); vf2d nameTextSize=game->GetTextSizeProp(m.GetName());
uint8_t alpha=uint8_t(util::lerp(0.f,255.f,m.F(A::TARGET_TIMER)/ConfigFloat("Interaction Display Ease in Timer"))); uint8_t alpha=uint8_t(util::lerp(0.f,255.f,m.F(A::TARGET_TIMER)/ConfigFloat("Interaction Display Ease in Timer")));
game->view.DrawShadowStringPropDecal(m.GetPos()-vf2d{0,12}-nameTextSize/2.f,m.GetName(),{255,255,0},{0,0,0}); game->view.DrawShadowStringPropDecal(m.GetPos()-vf2d{0,12}-nameTextSize/2.f,m.GetDisplayName(),{255,255,0},{0,0,0});
game->KEY_CONFIRM.DrawPrimaryInput(&game->view,m.GetPos()+vf2d{ConfigFloatArr("Interaction Display Offset",0),ConfigFloatArr("Interaction Display Offset",1)},"Interact",alpha); game->KEY_CONFIRM.DrawPrimaryInput(&game->view,m.GetPos()+vf2d{ConfigFloatArr("Interaction Display Offset",0),ConfigFloatArr("Interaction Display Offset",1)},"Interact",alpha);
}); });
} }

@ -54,7 +54,7 @@ class SpawnEncounterLabel:public MenuLabel{
public: public:
inline SpawnEncounterLabel(geom2d::rect<float>rect,std::string label,std::string monsterName) inline SpawnEncounterLabel(geom2d::rect<float>rect,std::string label,std::string monsterName)
:MenuLabel(rect,label),monsterName(monsterName){ :MenuLabel(rect,label),monsterName(monsterName){
anim.AddState("IDLE",ANIMATION_DATA.at(std::format("{}_{}",monsterName,MONSTER_DATA.at(monsterName).GetDefaultIdleAnimation()))); anim.AddState("IDLE",ANIMATION_DATA.at(std::format("{}_{}",MONSTER_DATA.at(monsterName).GetDisplayName(),MONSTER_DATA.at(monsterName).GetDefaultIdleAnimation())));
anim.ChangeState(state,"IDLE"); anim.ChangeState(state,"IDLE");
anim.UpdateState(state,util::random(1)); anim.UpdateState(state,util::random(1));
if(MONSTER_DATA.at(monsterName).HasMountedAnimation()){ if(MONSTER_DATA.at(monsterName).HasMountedAnimation()){

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 2 #define VERSION_MINOR 2
#define VERSION_PATCH 0 #define VERSION_PATCH 0
#define VERSION_BUILD 9353 #define VERSION_BUILD 9362
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="160" height="144" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="3"> <map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="160" height="144" tilewidth="24" tileheight="24" infinite="0" nextlayerid="6" nextobjectid="14">
<properties> <properties>
<property name="Backdrop" propertytype="Backdrop" value="mountain_night"/> <property name="Backdrop" propertytype="Backdrop" value="mountain_night"/>
<property name="Background Music" propertytype="BGM" value="foresty_boss"/> <property name="Background Music" propertytype="BGM" value="foresty_boss"/>
@ -8,9 +8,10 @@
<tileset firstgid="1" source="../maps/Tilesheet_No_Shadow24x24.tsx"/> <tileset firstgid="1" source="../maps/Tilesheet_No_Shadow24x24.tsx"/>
<tileset firstgid="2913" source="../maps/objects.tsx"/> <tileset firstgid="2913" source="../maps/objects.tsx"/>
<tileset firstgid="5020" source="../maps/Decorations_c1_No_Shadow24x24.tsx"/> <tileset firstgid="5020" source="../maps/Decorations_c1_No_Shadow24x24.tsx"/>
<tileset firstgid="7964" source="../maps/Monsters.tsx"/>
<layer id="1" name="Layer 1" width="160" height="144"> <layer id="1" name="Layer 1" width="160" height="144">
<data encoding="csv"> <data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -601,6 +602,53 @@
</data> </data>
</layer> </layer>
<objectgroup id="5" name="Spawn Zones"> <objectgroup id="5" name="Spawn Zones">
<object id="1" name="Boss Arena" type="BossArena" x="1294" y="1195" width="1448" height="1206"/> <object id="1" name="Boss Arena" type="BossArena" x="1302" y="1122" width="1448" height="1206"/>
<object id="3" name="Boss Fight Start Zone" type="SpawnGroup" x="1561" y="1320" width="964" height="498">
<properties>
<property name="Boss Title Display" value="Zephy, King of Birds"/>
</properties>
<ellipse/>
</object>
<object id="5" template="../maps/Monsters/Hawk_NOXP.tx" x="1884" y="1382">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="6" template="../maps/Monsters/Hawk_NOXP.tx" x="2129" y="1386">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="7" template="../maps/Monsters/Hawk_NOXP.tx" x="1894" y="1292">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="8" template="../maps/Monsters/Hawk_NOXP.tx" x="1962" y="1245">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="9" template="../maps/Monsters/Hawk_NOXP.tx" x="1848" y="1202">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="10" template="../maps/Monsters/Hawk_NOXP.tx" x="2127" y="1243">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="11" template="../maps/Monsters/Hawk_NOXP.tx" x="2200" y="1288">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="12" template="../maps/Monsters/Hawk_NOXP.tx" x="2230" y="1182">
<properties>
<property name="spawner" type="object" value="3"/>
</properties>
</object>
<object id="13" name="Player Spawn" type="PlayerSpawnLocation" x="2028" y="1872" width="24" height="24"/>
</objectgroup> </objectgroup>
</map> </map>

@ -925,6 +925,51 @@ Monsters
Death Sound = Slime Dead Death Sound = Slime Dead
Walk Sound = Wing Flap Walk Sound = Wing Flap
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
# DROP[0] = Ring of the Bear,100%,1,1
}
Hawk_NOXP
{ # A versions of the Hawk that does not provide any XP. All other features remain identical.
# Which monster base image this monster should be based off of.
Base Image Name = "Hawk"
Display Name = "Hawk"
Health = 10
Attack = 22
CollisionDmg = 22
MoveSpd = 180%
Size = 50%
XP = 0
Strategy = Hawk
#Size of each animation frame
SheetFrameSize = 32,32
# Setting this to true means every four rows indicates one animation, the ordering of the directions is: NORTH, EAST, SOUTH, WEST
4-Way Spritesheet = True
Animations
{
# Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse)
# 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 = 4, 0.2, Repeat
ATTACKING = 2, 0.2, Repeat
DEATH = 3, 0.15, OneShot
ATTACK = 4, 0.1, Repeat
}
Ignore Collisions = True
Hurt Sound = Monster Hurt
Death Sound = Slime Dead
Walk Sound = Wing Flap
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity # Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
# DROP[0] = Ring of the Bear,100%,1,1 # DROP[0] = Ring of the Bear,100%,1,1
} }

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<tileset firstgid="1" source="../Monsters.tsx"/>
<object name="Hawk_NOXP" type="Monster" gid="12" width="24" height="24"/>
</template>
Loading…
Cancel
Save