Graphics configuration file and initial configuration layout setup.
This commit is contained in:
parent
649e7f62f4
commit
c17ff42a23
@ -42,6 +42,14 @@ Crawler::Crawler()
|
|||||||
|
|
||||||
bool Crawler::OnUserCreate(){
|
bool Crawler::OnUserCreate(){
|
||||||
|
|
||||||
|
utils::datafile::Read(DATA,"assets/config/configuration.txt");
|
||||||
|
|
||||||
|
std::string CONFIG_PATH = GetString("config_path");
|
||||||
|
|
||||||
|
std::string GFX_CONFIG = CONFIG_PATH + GetString("gfx_config");
|
||||||
|
|
||||||
|
utils::datafile::Read(DATA,GFX_CONFIG);
|
||||||
|
|
||||||
InitializeLevel("assets/Campaigns/1_1.tmx",CAMPAIGN_1_1);
|
InitializeLevel("assets/Campaigns/1_1.tmx",CAMPAIGN_1_1);
|
||||||
|
|
||||||
player=std::make_unique<Warrior>();
|
player=std::make_unique<Warrior>();
|
||||||
@ -53,35 +61,41 @@ bool Crawler::OnUserCreate(){
|
|||||||
camera.SetWorldBoundary({0,0},WORLD_SIZE*24);
|
camera.SetWorldBoundary({0,0},WORLD_SIZE*24);
|
||||||
camera.EnableWorldBoundary(false);
|
camera.EnableWorldBoundary(false);
|
||||||
|
|
||||||
|
#undef LoadImage //Dumb Windows.
|
||||||
|
auto LoadImage=[&](Renderable&r,std::string key){
|
||||||
|
r.Load(GetString("GFX_Prefix")+GetString(key));
|
||||||
|
};
|
||||||
|
#define LOADIMG(name) LoadImage(name,"Images."#name);
|
||||||
|
|
||||||
//Graphics
|
//Graphics
|
||||||
GFX_Warrior_Sheet.Load("assets/nico-warrior.png");
|
LOADIMG(GFX_Warrior_Sheet)
|
||||||
GFX_Slime_Sheet.Load("assets/slime.png");
|
LOADIMG(GFX_Slime_Sheet)
|
||||||
GFX_Circle.Load("assets/circle.png");
|
LOADIMG(GFX_Circle)
|
||||||
GFX_Effect_GroundSlam_Back.Load("assets/ground-slam-attack-back.png");
|
LOADIMG(GFX_Effect_GroundSlam_Back)
|
||||||
GFX_Effect_GroundSlam_Front.Load("assets/ground-slam-attack-front.png");
|
LOADIMG(GFX_Effect_GroundSlam_Front)
|
||||||
GFX_Heart.Load("assets/heart.png");
|
LOADIMG(GFX_Heart)
|
||||||
GFX_BLOCK_BUBBLE.Load("assets/block.png");
|
LOADIMG(GFX_BLOCK_BUBBLE)
|
||||||
GFX_Ranger_Sheet.Load("assets/nico-ranger.png");
|
LOADIMG(GFX_Ranger_Sheet)
|
||||||
GFX_Wizard_Sheet.Load("assets/nico-wizard.png");
|
LOADIMG(GFX_Wizard_Sheet)
|
||||||
GFX_Battlecry_Effect.Load("assets/battlecry_effect.png");
|
LOADIMG(GFX_Battlecry_Effect)
|
||||||
GFX_Mana.Load("assets/mana.png");
|
LOADIMG(GFX_Mana)
|
||||||
GFX_SonicSlash.Load("assets/sonicslash.png");
|
LOADIMG(GFX_SonicSlash)
|
||||||
GFX_BulletCircle.Load("assets/circle.png");
|
LOADIMG(GFX_BulletCircle)
|
||||||
GFX_BulletCircleOutline.Load("assets/circle_outline.png");
|
LOADIMG(GFX_BulletCircleOutline)
|
||||||
GFX_EnergyBolt.Load("assets/energy_bolt.png");
|
LOADIMG(GFX_EnergyBolt)
|
||||||
GFX_EnergyParticle.Load("assets/energy_particle.png");
|
LOADIMG(GFX_EnergyParticle)
|
||||||
GFX_Splash_Effect.Load("assets/splash_effect.png");
|
LOADIMG(GFX_Splash_Effect)
|
||||||
GFX_LightningBolt.Load("assets/lightning_bolt.png");
|
LOADIMG(GFX_LightningBolt)
|
||||||
GFX_LightningBoltParticle1.Load("assets/lightning_bolt_part1.png");
|
LOADIMG(GFX_LightningBoltParticle1)
|
||||||
GFX_LightningBoltParticle2.Load("assets/lightning_bolt_part2.png");
|
LOADIMG(GFX_LightningBoltParticle2)
|
||||||
GFX_LightningBoltParticle3.Load("assets/lightning_bolt_part3.png");
|
LOADIMG(GFX_LightningBoltParticle3)
|
||||||
GFX_LightningBoltParticle4.Load("assets/lightning_bolt_part4.png");
|
LOADIMG(GFX_LightningBoltParticle4)
|
||||||
GFX_ChainLightning.Load("assets/chain_lightning.png");
|
LOADIMG(GFX_ChainLightning)
|
||||||
GFX_LightningSplash.Load("assets/lightning_splash_effect.png");
|
LOADIMG(GFX_LightningSplash)
|
||||||
GFX_Meteor.Load("assets/meteor.png");
|
LOADIMG(GFX_Meteor)
|
||||||
GFX_Arrow.Load("assets/arrow.png");
|
LOADIMG(GFX_Arrow)
|
||||||
GFX_Laser.Load("assets/laser.png");
|
LOADIMG(GFX_Laser)
|
||||||
GFX_ChargedArrow.Load("assets/charged_shot_arrow.png");
|
LOADIMG(GFX_ChargedArrow)
|
||||||
|
|
||||||
//Animations
|
//Animations
|
||||||
sig::Animation::InitializeAnimations();
|
sig::Animation::InitializeAnimations();
|
||||||
@ -1082,6 +1096,22 @@ void Crawler::InitializeClassAbilities(){
|
|||||||
Witch::InitializeClassAbilities();
|
Witch::InitializeClassAbilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Crawler::GetString(std::string key){
|
||||||
|
return DATA.GetProperty(key).GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Crawler::GetInt(std::string key){
|
||||||
|
return DATA.GetProperty(key).GetInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Crawler::GetFloat(std::string key){
|
||||||
|
return DATA.GetProperty(key).GetReal();
|
||||||
|
}
|
||||||
|
|
||||||
|
double Crawler::GetDouble(std::string key){
|
||||||
|
return DATA.GetProperty(key).GetReal();
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
Crawler demo;
|
Crawler demo;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "Effect.h"
|
#include "Effect.h"
|
||||||
#include "Map.h"
|
#include "Map.h"
|
||||||
#include "TMXParser.h"
|
#include "TMXParser.h"
|
||||||
|
#include "olcUTIL_DataFile.h"
|
||||||
|
|
||||||
struct TilesheetData{
|
struct TilesheetData{
|
||||||
TilesetData&tileset;
|
TilesetData&tileset;
|
||||||
@ -51,6 +52,7 @@ private:
|
|||||||
int bridgeLayerIndex=-1;
|
int bridgeLayerIndex=-1;
|
||||||
float bridgeFadeFactor=0.f;
|
float bridgeFadeFactor=0.f;
|
||||||
void InitializeClassAbilities();
|
void InitializeClassAbilities();
|
||||||
|
utils::datafile DATA;
|
||||||
public:
|
public:
|
||||||
Crawler();
|
Crawler();
|
||||||
bool OnUserCreate() override;
|
bool OnUserCreate() override;
|
||||||
@ -98,4 +100,8 @@ public:
|
|||||||
std::map<std::string,std::vector<geom2d::rect<int>>>&GetZoneData(MapName map);
|
std::map<std::string,std::vector<geom2d::rect<int>>>&GetZoneData(MapName map);
|
||||||
void PopulateRenderLists(std::vector<Monster*>&monstersBeforeLower,std::vector<Monster*>&monstersBeforeUpper,std::vector<Monster*>&monstersAfterLower,std::vector<Monster*>&monstersAfterUpper,std::vector<Bullet*>&bulletsLower,std::vector<Bullet*>&bulletsUpper,std::vector<Effect*>&backgroundEffectsLower,std::vector<Effect*>&backgroundEffectsUpper,std::vector<Effect*>&foregroundEffectsLower,std::vector<Effect*>&foregroundEffectsUpper);
|
void PopulateRenderLists(std::vector<Monster*>&monstersBeforeLower,std::vector<Monster*>&monstersBeforeUpper,std::vector<Monster*>&monstersAfterLower,std::vector<Monster*>&monstersAfterUpper,std::vector<Bullet*>&bulletsLower,std::vector<Bullet*>&bulletsUpper,std::vector<Effect*>&backgroundEffectsLower,std::vector<Effect*>&backgroundEffectsUpper,std::vector<Effect*>&foregroundEffectsLower,std::vector<Effect*>&foregroundEffectsUpper);
|
||||||
void ChangePlayerClass(Class cl);
|
void ChangePlayerClass(Class cl);
|
||||||
|
std::string GetString(std::string key);
|
||||||
|
int GetInt(std::string key);
|
||||||
|
float GetFloat(std::string key);
|
||||||
|
double GetDouble(std::string key);
|
||||||
};
|
};
|
@ -189,6 +189,7 @@
|
|||||||
<ClInclude Include="olcPixelGameEngine.h" />
|
<ClInclude Include="olcPixelGameEngine.h" />
|
||||||
<ClInclude Include="olcUTIL_Animate2D.h" />
|
<ClInclude Include="olcUTIL_Animate2D.h" />
|
||||||
<ClInclude Include="olcUTIL_Camera2D.h" />
|
<ClInclude Include="olcUTIL_Camera2D.h" />
|
||||||
|
<ClInclude Include="olcUTIL_DataFile.h" />
|
||||||
<ClInclude Include="olcUTIL_Geometry2D.h" />
|
<ClInclude Include="olcUTIL_Geometry2D.h" />
|
||||||
<ClInclude Include="Pathfinding.h" />
|
<ClInclude Include="Pathfinding.h" />
|
||||||
<ClInclude Include="Player.h" />
|
<ClInclude Include="Player.h" />
|
||||||
@ -234,8 +235,11 @@
|
|||||||
<None Include="cpp.hint" />
|
<None Include="cpp.hint" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Text Include="assets\config\configuration.txt" />
|
||||||
|
<Text Include="assets\config\gfx\gfx.txt" />
|
||||||
<Text Include="NewClasses.txt" />
|
<Text Include="NewClasses.txt" />
|
||||||
<Text Include="InitialConcept.txt" />
|
<Text Include="InitialConcept.txt" />
|
||||||
|
<Text Include="Slime_King_Encounter.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="assets\heart.ico" />
|
<Image Include="assets\heart.ico" />
|
||||||
|
@ -28,6 +28,12 @@
|
|||||||
<Filter Include="Source Files\Player Classes">
|
<Filter Include="Source Files\Player Classes">
|
||||||
<UniqueIdentifier>{715c64c5-956a-4ed3-9205-64110409fbea}</UniqueIdentifier>
|
<UniqueIdentifier>{715c64c5-956a-4ed3-9205-64110409fbea}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="Configurations">
|
||||||
|
<UniqueIdentifier>{44d00400-febd-4904-89a4-3725e6bc6cc7}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Configurations\GFX">
|
||||||
|
<UniqueIdentifier>{153059cd-0714-4c62-b01c-342f3bcd393d}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="olcPixelGameEngine.h">
|
<ClInclude Include="olcPixelGameEngine.h">
|
||||||
@ -111,6 +117,9 @@
|
|||||||
<ClInclude Include="Emitter.h">
|
<ClInclude Include="Emitter.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="olcUTIL_DataFile.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Player.cpp">
|
<ClCompile Include="Player.cpp">
|
||||||
@ -208,6 +217,13 @@
|
|||||||
<Text Include="NewClasses.txt">
|
<Text Include="NewClasses.txt">
|
||||||
<Filter>Documentation</Filter>
|
<Filter>Documentation</Filter>
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text Include="Slime_King_Encounter.txt" />
|
||||||
|
<Text Include="assets\config\configuration.txt">
|
||||||
|
<Filter>Configurations</Filter>
|
||||||
|
</Text>
|
||||||
|
<Text Include="assets\config\gfx\gfx.txt">
|
||||||
|
<Filter>Configurations\GFX</Filter>
|
||||||
|
</Text>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="assets\heart.ico">
|
<Image Include="assets\heart.ico">
|
||||||
|
@ -76,6 +76,7 @@ void Ranger::InitializeClassAbilities(){
|
|||||||
vf2d arrowVelocity=util::pointTo(p->GetPos(),game->GetWorldMousePos());
|
vf2d arrowVelocity=util::pointTo(p->GetPos(),game->GetWorldMousePos());
|
||||||
BULLET_LIST.push_back(std::make_unique<ChargedArrow>(p->GetPos(),arrowVelocity*600,20,p->GetAttack()*2.5,p->OnUpperLevel(),true));
|
BULLET_LIST.push_back(std::make_unique<ChargedArrow>(p->GetPos(),arrowVelocity*600,20,p->GetAttack()*2.5,p->OnUpperLevel(),true));
|
||||||
p->SetAnimationBasedOnTargetingDirection(atan2(arrowVelocity.y,arrowVelocity.x));
|
p->SetAnimationBasedOnTargetingDirection(atan2(arrowVelocity.y,arrowVelocity.x));
|
||||||
|
game->SetupWorldShake(0.3);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
47
Crawler/Slime_King_Encounter.txt
Normal file
47
Crawler/Slime_King_Encounter.txt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
Test Attributes, to be able to try encounter with the concept Warrior:
|
||||||
|
HP: 1200
|
||||||
|
Projectile Attack dmg: 10
|
||||||
|
Jump Attack dmg: 20
|
||||||
|
|
||||||
|
- Jumps need a telegraph on the ground.
|
||||||
|
|
||||||
|
Slime King
|
||||||
|
Base Size 800%
|
||||||
|
|
||||||
|
100%
|
||||||
|
Every 4 Seconds Shoots a ring of projectiles and another 2 rings with each 0.2 seconds delayed and slightly shifted.
|
||||||
|
after 4 repeats of this pattern, jump in the air and target location player currently at. Lands after 3 seconds.
|
||||||
|
After landing creats 1 ring of projectiles and takes 2 extra seconds to recover.
|
||||||
|
|
||||||
|
repeats behaviour until 75% is reached.
|
||||||
|
|
||||||
|
75%:
|
||||||
|
Size changes to 600%
|
||||||
|
Spawns 2 Slime adds (Monster C from Concept for now)
|
||||||
|
|
||||||
|
shoots every second with 3 projectiles (shoots out the projectiles in same moment with 45 degree shiftet flight location) in players direction.
|
||||||
|
after 5 shoots it charges for 5 seconds after that the king does 3 rapid jumps aiming for the player.
|
||||||
|
first should be dodgeable. quite easily.
|
||||||
|
second needs to be blocked with movement skill or iframe (unless high movespeed, 110% shall not be enough to dodge this, 110% can be achived without any special gear as bard permanently)
|
||||||
|
third jump is a little slower that should allow a player with 100% movespeed to barely run out of it.
|
||||||
|
|
||||||
|
50%:
|
||||||
|
Size changes to 400%
|
||||||
|
Spawns 2 Slime adds (Monster C from Concept for now)
|
||||||
|
|
||||||
|
1 fast Jump towards player. 3 projectiles attacks with 0.5 seconds between attack.
|
||||||
|
2 seconds recover.
|
||||||
|
repeat.
|
||||||
|
|
||||||
|
|
||||||
|
25%:
|
||||||
|
Size changes to 200%
|
||||||
|
Spawns 2 Slime adds (Monster B from Concept for now)
|
||||||
|
Movespeed 50%
|
||||||
|
|
||||||
|
King Shoots 5 projectiles, 0.1 sec delay Boss doesnt aim for the player directly instead shoots randomly in the general direction. runs away for 2.5 seconds. stands still for 1 second and shoot again.
|
||||||
|
Repeat 5 times then instead of running jump up to 1000 Range away from player and repeat.
|
||||||
|
|
||||||
|
0%:
|
||||||
|
instead of dying Slime king Looses 50% size on every hit taken (1 sec iframe after taking dmg) until reaching 0% size and dies.
|
||||||
|
Slime King only runs away with 50% move speed.
|
@ -2,7 +2,7 @@
|
|||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 2
|
#define VERSION_MINOR 2
|
||||||
#define VERSION_PATCH 0
|
#define VERSION_PATCH 0
|
||||||
#define VERSION_BUILD 854
|
#define VERSION_BUILD 861
|
||||||
|
|
||||||
#define stringify(a) stringify_(a)
|
#define stringify(a) stringify_(a)
|
||||||
#define stringify_(a) #a
|
#define stringify_(a) #a
|
||||||
|
2
Crawler/assets/config/configuration.txt
Normal file
2
Crawler/assets/config/configuration.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
config_path = assets/config/
|
||||||
|
gfx_config = gfx/gfx.txt
|
33
Crawler/assets/config/gfx/gfx.txt
Normal file
33
Crawler/assets/config/gfx/gfx.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
GFX_Prefix = assets/
|
||||||
|
|
||||||
|
Images
|
||||||
|
{
|
||||||
|
GFX_Warrior_Sheet = nico-warrior.png
|
||||||
|
GFX_Slime_Sheet = slime.png
|
||||||
|
GFX_Circle = circle.png
|
||||||
|
GFX_Effect_GroundSlam_Back = ground-slam-attack-back.png
|
||||||
|
GFX_Effect_GroundSlam_Front = ground-slam-attack-front.png
|
||||||
|
GFX_Heart = heart.png
|
||||||
|
GFX_BLOCK_BUBBLE = block.png
|
||||||
|
GFX_Ranger_Sheet = nico-ranger.png
|
||||||
|
GFX_Wizard_Sheet = nico-wizard.png
|
||||||
|
GFX_Battlecry_Effect = battlecry_effect.png
|
||||||
|
GFX_Mana = mana.png
|
||||||
|
GFX_SonicSlash = sonicslash.png
|
||||||
|
GFX_BulletCircle = circle.png
|
||||||
|
GFX_BulletCircleOutline = circle_outline.png
|
||||||
|
GFX_EnergyBolt = energy_bolt.png
|
||||||
|
GFX_EnergyParticle = energy_particle.png
|
||||||
|
GFX_Splash_Effect = splash_effect.png
|
||||||
|
GFX_LightningBolt = lightning_bolt.png
|
||||||
|
GFX_LightningBoltParticle1 = lightning_bolt_part1.png
|
||||||
|
GFX_LightningBoltParticle2 = lightning_bolt_part2.png
|
||||||
|
GFX_LightningBoltParticle3 = lightning_bolt_part3.png
|
||||||
|
GFX_LightningBoltParticle4 = lightning_bolt_part4.png
|
||||||
|
GFX_ChainLightning = chain_lightning.png
|
||||||
|
GFX_LightningSplash = lightning_splash_effect.png
|
||||||
|
GFX_Meteor = meteor.png
|
||||||
|
GFX_Arrow = arrow.png
|
||||||
|
GFX_Laser = laser.png
|
||||||
|
GFX_ChargedArrow = charged_shot_arrow.png
|
||||||
|
}
|
435
Crawler/olcUTIL_DataFile.h
Normal file
435
Crawler/olcUTIL_DataFile.h
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
/*
|
||||||
|
OneLoneCoder - DataFile v1.00
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
An "easy to use" serialisation/deserialisation class that yields
|
||||||
|
human readable hierachical files.
|
||||||
|
|
||||||
|
License (OLC-3)
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2018 - 2022 OneLoneCoder.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.
|
||||||
|
|
||||||
|
Links
|
||||||
|
~~~~~
|
||||||
|
YouTube: https://www.youtube.com/javidx9
|
||||||
|
Discord: https://discord.gg/WhwHUMV
|
||||||
|
Twitter: https://www.twitter.com/javidx9
|
||||||
|
Twitch: https://www.twitch.tv/javidx9
|
||||||
|
GitHub: https://www.github.com/onelonecoder
|
||||||
|
Homepage: https://www.onelonecoder.com
|
||||||
|
|
||||||
|
Author
|
||||||
|
~~~~~~
|
||||||
|
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2019, 2020, 2021, 2022
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stack>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace olc::utils
|
||||||
|
{
|
||||||
|
class datafile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline datafile() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Sets the String Value of a Property (for a given index)
|
||||||
|
inline void SetString(const std::string& sString, const size_t nItem = 0)
|
||||||
|
{
|
||||||
|
if (nItem >= m_vContent.size())
|
||||||
|
m_vContent.resize(nItem + 1);
|
||||||
|
|
||||||
|
m_vContent[nItem] = sString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the String Value of a Property (for a given index) or ""
|
||||||
|
inline const std::string GetString(const size_t nItem = 0) const
|
||||||
|
{
|
||||||
|
if (nItem >= m_vContent.size())
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return m_vContent[nItem];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the Real Value of a Property (for a given index) or 0.0
|
||||||
|
inline const double GetReal(const size_t nItem = 0) const
|
||||||
|
{
|
||||||
|
return std::atof(GetString(nItem).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the Real Value of a Property (for a given index)
|
||||||
|
inline void SetReal(const double d, const size_t nItem = 0)
|
||||||
|
{
|
||||||
|
SetString(std::to_string(d), nItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieves the Integer Value of a Property (for a given index) or 0
|
||||||
|
inline const int32_t GetInt(const size_t nItem = 0) const
|
||||||
|
{
|
||||||
|
return std::atoi(GetString(nItem).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the Integer Value of a Property (for a given index)
|
||||||
|
inline void SetInt(const int32_t n, const size_t nItem = 0)
|
||||||
|
{
|
||||||
|
SetString(std::to_string(n), nItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the number of Values a property consists of
|
||||||
|
inline size_t GetValueCount() const
|
||||||
|
{
|
||||||
|
return m_vContent.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a property exists - useful to avoid creating properties
|
||||||
|
// via reading them, though non-essential
|
||||||
|
inline bool HasProperty(const std::string& sName) const
|
||||||
|
{
|
||||||
|
return m_mapObjects.count(sName) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access a datafile via a convenient name - "root.node.something.property"
|
||||||
|
inline datafile& GetProperty(const std::string& name)
|
||||||
|
{
|
||||||
|
size_t x = name.find_first_of('.');
|
||||||
|
if (x != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string sProperty = name.substr(0, x);
|
||||||
|
if (HasProperty(sProperty))
|
||||||
|
return operator[](sProperty).GetProperty(name.substr(x + 1, name.size()));
|
||||||
|
else
|
||||||
|
return operator[](sProperty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return operator[](name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access a numbered element - "node[23]", or "root[56].node"
|
||||||
|
inline datafile& GetIndexedProperty(const std::string& name, const size_t nIndex)
|
||||||
|
{
|
||||||
|
return GetProperty(name + "[" + std::to_string(nIndex) + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Writes a "datafile" node (and all of its child nodes and properties) recursively
|
||||||
|
// to a file.
|
||||||
|
inline static bool Write(const datafile& n, const std::string& sFileName, const std::string& sIndent = "\t", const char sListSep = ',')
|
||||||
|
{
|
||||||
|
// Cache indentation level
|
||||||
|
size_t nIndentCount = 0;
|
||||||
|
// Cache sperator string for convenience
|
||||||
|
std::string sSeperator = std::string(1, sListSep) + " ";
|
||||||
|
|
||||||
|
// Fully specified lambda, because this lambda is recursive!
|
||||||
|
std::function<void(const datafile&, std::ofstream&)> write = [&](const datafile& n, std::ofstream& file)
|
||||||
|
{
|
||||||
|
// Lambda creates string given indentation preferences
|
||||||
|
auto indent = [&](const std::string& sString, const size_t nCount)
|
||||||
|
{
|
||||||
|
std::string sOut;
|
||||||
|
for (size_t n = 0; n < nCount; n++) sOut += sString;
|
||||||
|
return sOut;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate through each property of this node
|
||||||
|
for (auto const& property : n.m_vecObjects)
|
||||||
|
{
|
||||||
|
// Does property contain any sub objects?
|
||||||
|
if (property.second.m_vecObjects.empty())
|
||||||
|
{
|
||||||
|
// No, so it's an assigned field and should just be written. If the property
|
||||||
|
// is flagged as comment, it has no assignment potential. First write the
|
||||||
|
// property name
|
||||||
|
file << indent(sIndent, nIndentCount) << property.first << (property.second.m_bIsComment ? "" : " = ");
|
||||||
|
|
||||||
|
// Second, write the property value (or values, seperated by provided
|
||||||
|
// separation charater
|
||||||
|
size_t nItems = property.second.GetValueCount();
|
||||||
|
for (size_t i = 0; i < property.second.GetValueCount(); i++)
|
||||||
|
{
|
||||||
|
// If the Value being written, in string form, contains the separation
|
||||||
|
// character, then the value must be written inside quotation marks. Note,
|
||||||
|
// that if the Value is the last of a list of Values for a property, it is
|
||||||
|
// not suffixed with the separator
|
||||||
|
size_t x = property.second.GetString(i).find_first_of(sListSep);
|
||||||
|
if (x != std::string::npos)
|
||||||
|
{
|
||||||
|
// Value contains separator, so wrap in quotes
|
||||||
|
file << "\"" << property.second.GetString(i) << "\"" << ((nItems > 1) ? sSeperator : "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Value does not contain separator, so just write out
|
||||||
|
file << property.second.GetString(i) << ((nItems > 1) ? sSeperator : "");
|
||||||
|
}
|
||||||
|
nItems--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property written, move to next line
|
||||||
|
file << "\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Yes, property has properties of its own, so it's a node
|
||||||
|
// Force a new line and write out the node's name
|
||||||
|
file << "\n" << indent(sIndent, nIndentCount) << property.first << "\n";
|
||||||
|
// Open braces, and update indentation
|
||||||
|
file << indent(sIndent, nIndentCount) << "{\n";
|
||||||
|
nIndentCount++;
|
||||||
|
// Recursively write that node
|
||||||
|
write(property.second, file);
|
||||||
|
// Node written, so close braces
|
||||||
|
file << indent(sIndent, nIndentCount) << "}\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've finished writing out a node, regardless of state, our indentation
|
||||||
|
// must decrease, unless we're top level
|
||||||
|
if (nIndentCount > 0) nIndentCount--;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start Here! Open the file for writing
|
||||||
|
std::ofstream file(sFileName);
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
// Write the file starting form the supplied node
|
||||||
|
write(n, file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',')
|
||||||
|
{
|
||||||
|
// Open the file!
|
||||||
|
std::ifstream file(sFileName);
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
// These variables are outside of the read loop, as we will
|
||||||
|
// need to refer to previous iteration values in certain conditions
|
||||||
|
std::string sPropName = "";
|
||||||
|
std::string sPropValue = "";
|
||||||
|
|
||||||
|
// The file is fundamentally structured as a stack, so we will read it
|
||||||
|
// in a such, but note the data structure in memory is not explicitly
|
||||||
|
// stored in a stack, but one is constructed implicitly via the nodes
|
||||||
|
// owning other nodes (aka a tree)
|
||||||
|
|
||||||
|
// I dont want to accidentally create copies all over the place, nor do
|
||||||
|
// I want to use pointer syntax, so being a bit different and stupidly
|
||||||
|
// using std::reference_wrapper, so I can store references to datafile
|
||||||
|
// nodes in a std::container.
|
||||||
|
std::stack<std::reference_wrapper<datafile>> stkPath;
|
||||||
|
stkPath.push(n);
|
||||||
|
|
||||||
|
|
||||||
|
// Read file line by line and process
|
||||||
|
while (!file.eof())
|
||||||
|
{
|
||||||
|
// Read line
|
||||||
|
std::string line;
|
||||||
|
std::getline(file, line);
|
||||||
|
|
||||||
|
// This little lambda removes whitespace from
|
||||||
|
// beginning and end of supplied string
|
||||||
|
auto trim = [](std::string& s)
|
||||||
|
{
|
||||||
|
s.erase(0, s.find_first_not_of(" \t\n\r\f\v"));
|
||||||
|
s.erase(s.find_last_not_of(" \t\n\r\f\v") + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
trim(line);
|
||||||
|
|
||||||
|
// If line has content
|
||||||
|
if (!line.empty())
|
||||||
|
{
|
||||||
|
// Test if its a comment...
|
||||||
|
if (line[0] == '#')
|
||||||
|
{
|
||||||
|
// ...it is a comment, so ignore
|
||||||
|
datafile comment;
|
||||||
|
comment.m_bIsComment = true;
|
||||||
|
stkPath.top().get().m_vecObjects.push_back({ line, comment });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ...it is content, so parse. Firstly, find if the line
|
||||||
|
// contains an assignment. If it does then it's a property...
|
||||||
|
size_t x = line.find_first_of('=');
|
||||||
|
if (x != std::string::npos)
|
||||||
|
{
|
||||||
|
// ...so split up the property into a name, and its values!
|
||||||
|
|
||||||
|
// Extract the property name, which is all characters up to
|
||||||
|
// first assignment, trim any whitespace from ends
|
||||||
|
sPropName = line.substr(0, x);
|
||||||
|
trim(sPropName);
|
||||||
|
|
||||||
|
// Extract the property value, which is all characters after
|
||||||
|
// the first assignment operator, trim any whitespace from ends
|
||||||
|
sPropValue = line.substr(x + 1, line.size());
|
||||||
|
trim(sPropValue);
|
||||||
|
|
||||||
|
// The value may be in list form: a, b, c, d, e, f etc and some of those
|
||||||
|
// elements may exist in quotes a, b, c, "d, e", f. So we need to iterate
|
||||||
|
// character by character and break up the value
|
||||||
|
bool bInQuotes = false;
|
||||||
|
std::string sToken;
|
||||||
|
size_t nTokenCount = 0;
|
||||||
|
for (const auto c : sPropValue)
|
||||||
|
{
|
||||||
|
// Is character a quote...
|
||||||
|
if (c == '\"')
|
||||||
|
{
|
||||||
|
// ...yes, so toggle quote state
|
||||||
|
bInQuotes = !bInQuotes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ...no, so proceed creating token. If we are in quote state
|
||||||
|
// then just append characters until we exit quote state.
|
||||||
|
if (bInQuotes)
|
||||||
|
{
|
||||||
|
sToken.append(1, c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Is the character our seperator? If it is
|
||||||
|
if (c == sListSep)
|
||||||
|
{
|
||||||
|
// Clean up the token
|
||||||
|
trim(sToken);
|
||||||
|
// Add it to the vector of values for this property
|
||||||
|
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||||
|
// Reset our token state
|
||||||
|
sToken.clear();
|
||||||
|
nTokenCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It isnt, so just append to token
|
||||||
|
sToken.append(1, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any residual characters at this point just make up the final token,
|
||||||
|
// so clean it up and add it to the vector of values
|
||||||
|
if (!sToken.empty())
|
||||||
|
{
|
||||||
|
trim(sToken);
|
||||||
|
stkPath.top().get()[sPropName].SetString(sToken, nTokenCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ...but if it doesnt, then it's something structural
|
||||||
|
if (line[0] == '{')
|
||||||
|
{
|
||||||
|
// Open brace, so push this node to stack, subsequent properties
|
||||||
|
// will belong to the new node
|
||||||
|
stkPath.push(stkPath.top().get()[sPropName]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (line[0] == '}')
|
||||||
|
{
|
||||||
|
// Close brace, so this node has been defined, pop it from the
|
||||||
|
// stack
|
||||||
|
stkPath.pop();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Line is a property with no assignment. Who knows whether this is useful,
|
||||||
|
// but we can simply add it as a valueless property...
|
||||||
|
sPropName = line;
|
||||||
|
// ...actually it is useful, as valuless properties are typically
|
||||||
|
// going to be the names of new datafile nodes on the next iteration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close and exit!
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// File not found, so fail
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline datafile& operator[](const std::string& name)
|
||||||
|
{
|
||||||
|
// Check if this "node"'s map already contains an object with this name...
|
||||||
|
if (m_mapObjects.count(name) == 0)
|
||||||
|
{
|
||||||
|
// ...it did not! So create this object in the map. First get a vector id
|
||||||
|
// and link it with the name in the unordered_map
|
||||||
|
m_mapObjects[name] = m_vecObjects.size();
|
||||||
|
// then creating the new, blank object in the vector of objects
|
||||||
|
m_vecObjects.push_back({ name, datafile() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...it exists! so return the object, by getting its index from the map, and using that
|
||||||
|
// index to look up a vector element.
|
||||||
|
return m_vecObjects[m_mapObjects[name]].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The "list of strings" that make up a property value
|
||||||
|
std::vector<std::string> m_vContent;
|
||||||
|
|
||||||
|
// Linkage to create "ordered" unordered_map. We have a vector of
|
||||||
|
// "properties", and the index to a specific element is mapped.
|
||||||
|
std::vector<std::pair<std::string, datafile>> m_vecObjects;
|
||||||
|
std::unordered_map<std::string, size_t> m_mapObjects;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Used to identify if a property is a comment or not, not user facing
|
||||||
|
bool m_bIsComment = false;
|
||||||
|
};
|
||||||
|
}
|
@ -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.1" orientation="orthogonal" renderorder="right-down" width="192" height="203" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="156">
|
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="192" height="203" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="158">
|
||||||
<tileset firstgid="1" source="../maps/grass_tiles_24x24.tsx"/>
|
<tileset firstgid="1" source="../maps/grass_tiles_24x24.tsx"/>
|
||||||
<tileset firstgid="784" source="../maps/grass_tiles_modded.tsx"/>
|
<tileset firstgid="784" source="../maps/grass_tiles_modded.tsx"/>
|
||||||
<layer id="5" name="Collision Layer" width="192" height="203">
|
<layer id="5" name="Collision Layer" width="192" height="203">
|
||||||
@ -7414,6 +7414,9 @@
|
|||||||
</map>
|
</map>
|
||||||
‰PNG
|
‰PNG
|
||||||
|
|
||||||
|
|