diff --git a/Adventures in Lestoria/Adventures in Lestoria.tiled-project b/Adventures in Lestoria/Adventures in Lestoria.tiled-project index 63c6b50d..d10f46b4 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.tiled-project +++ b/Adventures in Lestoria/Adventures in Lestoria.tiled-project @@ -455,13 +455,7 @@ "type": "class", "useAs": [ "property", - "map", - "layer", - "object", - "tile", - "tileset", - "wangcolor", - "wangset" + "object" ] }, { diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index 8fea8156..b1586470 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -975,6 +975,7 @@ + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 9f6e55f9..c165d467 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -1226,6 +1226,9 @@ Configurations + + Documentation\Mechanics + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 40aa8d4b..653a2988 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -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())); 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; } diff --git a/Adventures in Lestoria/Crawler_2_Bonus_Boss.txt b/Adventures in Lestoria/Crawler_2_Bonus_Boss.txt new file mode 100644 index 00000000..212060f1 --- /dev/null +++ b/Adventures in Lestoria/Crawler_2_Bonus_Boss.txt @@ -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) diff --git a/Adventures in Lestoria/Merchant.cpp b/Adventures in Lestoria/Merchant.cpp index b36cb23f..e7db5c2e 100644 --- a/Adventures in Lestoria/Merchant.cpp +++ b/Adventures in Lestoria/Merchant.cpp @@ -150,14 +150,14 @@ bool Merchant::CanPurchaseItem(IT item,uint32_t amt)const{ itemAvailable&& !foundItem.expired()&& game->GetPlayer()->GetMoney()>=foundItem.lock()->BuyValue()*amt; -}; +} bool Merchant::CanSellItem(std::weak_ptritem,uint32_t amt)const{ sellFunctionPrimed.amt=amt; sellFunctionPrimed.item=item.lock()->ActualName(); return sellFunctionPrimed= item.lock()->CanBeSold()&& item.lock()->Amt()>=amt; -}; +} void Merchant::PurchaseItem(IT item,uint32_t amt){ purchaseFunctionPrimed.Validate(item,amt); @@ -185,7 +185,7 @@ void Merchant::PurchaseItem(IT item,uint32_t amt){ Inventory::AddItem(item,amt); game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost); -}; +} void Merchant::SellItem(std::weak_ptritem,uint32_t amt){ sellFunctionPrimed.Validate(item.lock()->ActualName(),amt); @@ -230,14 +230,14 @@ void Merchant::SellItem(std::weak_ptritem,uint32_t amt){ } sellFunctionPrimed=false; -}; +} void Merchant::RandomizeTravelingMerchant(){ SetTravelingMerchant(const_cast(GetRandomMerchant(game->GetCurrentChapter())).GetKeyName()); -}; +} Merchant&Merchant::GetCurrentTravelingMerchant(){ return travelingMerchant; -}; +} std::string_view Merchant::GetKeyName()const{ return keyName; diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp index e15a66de..4d784d2d 100644 --- a/Adventures in Lestoria/Monster.cpp +++ b/Adventures in Lestoria/Monster.cpp @@ -62,7 +62,7 @@ INCLUDE_DATA INCLUDE_GFX safemap>STRATEGY_DATA; -std::mapMonsterData::imgs; +std::unordered_mapMonsterData::imgs; 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()){ @@ -1056,4 +1056,8 @@ const bool Monster::IsDead()const{ void Monster::SetIframes(const float iframeTime){ iframe_timer=iframeTime; +} + +const std::string_view Monster::GetDisplayName()const{ + return MONSTER_DATA.at(GetName()).GetDisplayName(); } \ No newline at end of file diff --git a/Adventures in Lestoria/Monster.h b/Adventures in Lestoria/Monster.h index e5f5346c..36b3a217 100644 --- a/Adventures in Lestoria/Monster.h +++ b/Adventures in Lestoria/Monster.h @@ -174,6 +174,7 @@ public: //If an object has a collision radius, returns it. const float GetCollisionRadius()const; const bool IsDead()const; + const std::string_view GetDisplayName()const; private: //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. diff --git a/Adventures in Lestoria/MonsterData.cpp b/Adventures in Lestoria/MonsterData.cpp index bab356ef..17abbbb9 100644 --- a/Adventures in Lestoria/MonsterData.cpp +++ b/Adventures in Lestoria/MonsterData.cpp @@ -51,12 +51,14 @@ std::mapMONSTER_DATA; MonsterData::MonsterData() :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::vectordrops,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){} +MonsterData::MonsterData(std::string name,std::string displayName,int hp,int atk,const uint32_t xp,std::vectordrops,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 "<Load("assets/monsters/commercial_assets/"+MonsterName+".png"); - if(imgLoadResult!=OK)ERR(std::format("WARNING! Image loading for Monster {} failed with result {}",MonsterName,int(imgLoadResult))); + 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=""; @@ -83,6 +91,10 @@ void MonsterData::InitializeMonsterData(){ } 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;idrops,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::vectordrops,float moveSpd=1.0f,float size=1.0f,std::string strategy="Run Towards",int collisionDmg=0); int GetHealth(); int GetAttack(); const uint32_t GetXP()const; @@ -85,10 +85,10 @@ public: return animations; } const std::vector&GetDropData(); - std::string GetDisplayName(); + const std::string&GetDisplayName()const; static void InitializeMonsterData(); static void InitializeNPCData(); - static std::mapimgs; + static std::unordered_mapimgs; const bool HasFourWaySprites()const; void SetUsesFourWaySprites(); const bool HasMountedAnimation()const; @@ -109,6 +109,7 @@ private: float moveSpd;//1.0=100% float size; std::unordered_setanimations; + 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 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) diff --git a/Adventures in Lestoria/NPC.cpp b/Adventures in Lestoria/NPC.cpp index d07495be..d4769740 100644 --- a/Adventures in Lestoria/NPC.cpp +++ b/Adventures in Lestoria/NPC.cpp @@ -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){ 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"))); - 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); }); } diff --git a/Adventures in Lestoria/SpawnEncounterLabel.h b/Adventures in Lestoria/SpawnEncounterLabel.h index 96d220e3..e7b3dc4d 100644 --- a/Adventures in Lestoria/SpawnEncounterLabel.h +++ b/Adventures in Lestoria/SpawnEncounterLabel.h @@ -54,7 +54,7 @@ class SpawnEncounterLabel:public MenuLabel{ public: inline SpawnEncounterLabel(geom2d::rectrect,std::string label,std::string 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.UpdateState(state,util::random(1)); if(MONSTER_DATA.at(monsterName).HasMountedAnimation()){ diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index f2980d8f..6d55270c 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 9353 +#define VERSION_BUILD 9362 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx b/Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx index 39527109..6f13e53b 100644 --- a/Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx +++ b/Adventures in Lestoria/assets/Campaigns/Boss_2_B.tmx @@ -1,5 +1,5 @@ - + @@ -8,9 +8,10 @@ + -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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, @@ -601,6 +602,53 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Adventures in Lestoria/assets/config/Monsters.txt b/Adventures in Lestoria/assets/config/Monsters.txt index 063b7082..46f0361a 100644 --- a/Adventures in Lestoria/assets/config/Monsters.txt +++ b/Adventures in Lestoria/assets/config/Monsters.txt @@ -925,6 +925,51 @@ Monsters Death Sound = Slime Dead 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[0] = Ring of the Bear,100%,1,1 } diff --git a/Adventures in Lestoria/assets/maps/Monsters/Hawk_NOXP.tx b/Adventures in Lestoria/assets/maps/Monsters/Hawk_NOXP.tx new file mode 100644 index 00000000..ba5704fe --- /dev/null +++ b/Adventures in Lestoria/assets/maps/Monsters/Hawk_NOXP.tx @@ -0,0 +1,5 @@ + + diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 25a6284b..d13cb4db 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ