Implemented Death Defiance enchant. Correct shadow render strings being square (not using proper Y sizes to create cached text). Move game configuration initialization out to global scope to allow config variables to be used when initializing the AiL class. Release Build 10670.

This commit is contained in:
sigonasr2 2024-08-06 04:39:36 -05:00
parent 62086cbd92
commit cfd73ab036
17 changed files with 112 additions and 41 deletions

View File

@ -50,6 +50,7 @@ using namespace olc::utils;
INCLUDE_GFX
INCLUDE_ITEM_DATA
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_INITIALIZEGAMECONFIGURATIONS
extern std::mt19937 rng;
@ -71,6 +72,7 @@ namespace EnchantTests
Player*player;
const HWButton*testKey;
TEST_METHOD_INITIALIZE(PlayerInitialize){
InitializeGameConfigurations();
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
testGame.reset(new AiL(true));
ItemAttribute::Initialize();
@ -260,5 +262,34 @@ namespace EnchantTests
Assert::AreEqual(4.5_Pct+0.5_Pct*i,player->GetHP4RecoveryPct(),0.1_Pct,L"HP Recovery/4 Pct is increasing by 0.5% per 10% missing health.");
}
}
TEST_METHOD(DeathDefianceCheck){
for(int i:std::ranges::iota_view(0,10)){
player->Heal(100);
player->Hurt(1000,player->OnUpperLevel(),player->GetZ());
if(player->IsAlive()){Assert::Fail(L"Player survived while not having the Death Defiance Enchant! THIS SHOULD NOT BE HAPPENING!");}
player->_SetIframes(0.f);
}
std::weak_ptr<Item>nullRing{Inventory::AddItem("Null Ring"s)};
Inventory::EquipItem(nullRing,EquipSlot::RING1);
nullRing.lock()->EnchantItem("Death Defiance");
player->Heal(100);
for(int i:std::ranges::iota_view(0,10)){
const int prevPlayerHP{player->GetHealth()};
player->Hurt(1,player->OnUpperLevel(),player->GetZ());
Assert::AreNotEqual(prevPlayerHP,player->GetHealth(),L"Death Defiance triggered even though the player did not take lethal damage!");
player->_SetIframes(0.f);
}
bool survivedAtLeastOnce{false};
for(int i:std::ranges::iota_view(0,10)){
player->Heal(100);
player->Hurt(1000,player->OnUpperLevel(),player->GetZ());
if(player->IsAlive()){
survivedAtLeastOnce=true;
break;
}
player->_SetIframes(0.f);
}
Assert::AreEqual(true,survivedAtLeastOnce,L"Player should have survived at least one time with Death Defiance.");
}
};
}

View File

@ -41,6 +41,7 @@ All rights reserved.
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
INCLUDE_INITIALIZEGAMECONFIGURATIONS
namespace EngineTests
{
@ -49,6 +50,7 @@ namespace EngineTests
public:
std::unique_ptr<AiL>testGame;
TEST_METHOD_INITIALIZE(PlayerInitialize){
InitializeGameConfigurations();
testGame.reset(new AiL(true));
}
TEST_METHOD(StripColorTest){

View File

@ -48,6 +48,7 @@ using namespace olc::utils;
INCLUDE_GFX
INCLUDE_ITEM_DATA
INCLUDE_INITIALIZEGAMECONFIGURATIONS
extern std::mt19937 rng;
@ -61,6 +62,7 @@ namespace ItemTests
Player*player;
HWButton*testKey;
TEST_METHOD_INITIALIZE(ItemInitialize){
InitializeGameConfigurations();
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
testGame.reset(new AiL(true));
ItemAttribute::Initialize();

View File

@ -50,6 +50,7 @@ INCLUDE_game
INCLUDE_GFX
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_MONSTER_LIST
INCLUDE_INITIALIZEGAMECONFIGURATIONS
TEST_MODULE_INITIALIZE(AiLTestSuite)
{
@ -65,6 +66,7 @@ namespace MonsterTests
#pragma region Setup Functions
//Makes MONSTER_DATA["TestName"] available.
void SetupTestMonster(){
InitializeGameConfigurations();
testGame.reset(new AiL(true));
ItemAttribute::Initialize();
ItemInfo::InitializeItems();

View File

@ -49,6 +49,7 @@ using namespace olc::utils;
INCLUDE_GFX
INCLUDE_ITEM_DATA
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_INITIALIZEGAMECONFIGURATIONS
extern std::mt19937 rng;
@ -62,6 +63,7 @@ namespace PlayerTests
Player*player;
HWButton*testKey;
TEST_METHOD_INITIALIZE(PlayerInitialize){
InitializeGameConfigurations();
rng=std::mt19937{57189U};//Establish a fixed random seed on setup so the exact same results are generated every test run.
testGame.reset(new AiL(true));
ItemAttribute::Initialize();

View File

@ -161,15 +161,33 @@ float AiL::SIZE_CHANGE_SPEED=1;
AiL::AiL(bool testingMode){
olc_SetTestingMode(testingMode);
GFX.Reset();
DATA.Reset();
MONSTER_LIST.clear();
InitializeGameConfigurations();
#pragma region Extra Config Initializations
if(TestingModeEnabled()){ //Unit Test-specific custom configurations.
std::string CONFIG_PATH = "config_path"_S;
std::string ITEM_CONFIG = CONFIG_PATH + "item_config"_S;
std::string ITEM_SET_CONFIG = CONFIG_PATH + "item_set_config"_S;
ITEM_CONFIG = CONFIG_PATH + "item-test_config"_S;
ITEM_SET_CONFIG = CONFIG_PATH + "item_set-test_config"_S;
utils::datafile::Read(DATA,ITEM_CONFIG,',',datafile::OverwriteMode::OVERWRITE);
utils::datafile::Read(DATA,ITEM_SET_CONFIG,',',datafile::OverwriteMode::OVERWRITE);
auto keys=DATA.GetProperty("ItemConfiguration");
for(auto&[key,value]:keys){
std::string config=DATA["ItemConfiguration"][key].GetString();
utils::datafile::Read(DATA,CONFIG_PATH + "item_directory"_S + config,',',datafile::OverwriteMode::OVERWRITE);
}
}
DEBUG_PATHFINDING="debug_pathfinding"_I;
#pragma endregion
sAppName="GAME_NAME"_S;
game=this;
gameStarted=time(NULL);
}
void AiL::InitializeGameConfigurations(){
void InitializeGameConfigurations(){
DATA.Reset();
utils::datafile::Read(DATA,"assets/config/configuration.txt");
std::filesystem::create_directories("save_file_path"_S);
@ -205,11 +223,6 @@ void AiL::InitializeGameConfigurations(){
std::string ITEM_CONFIG = CONFIG_PATH + "item_config"_S;
std::string ITEM_SET_CONFIG = CONFIG_PATH + "item_set_config"_S;
if(TestingModeEnabled()){ //Unit Test-specific custom configurations.
ITEM_CONFIG = CONFIG_PATH + "item-test_config"_S;
ITEM_SET_CONFIG = CONFIG_PATH + "item_set-test_config"_S;
}
utils::datafile::Read(DATA,ITEM_CONFIG);
utils::datafile::Read(DATA,ITEM_SET_CONFIG);
}
@ -229,8 +242,6 @@ void AiL::InitializeGameConfigurations(){
utils::datafile::Read(DATA,CONFIG_PATH + "item_directory"_S + config);
}
DEBUG_PATHFINDING="debug_pathfinding"_I;
std::vector<std::string>values=DATA.GetProperty("class_list").GetValues();
for(const std::string&cl:values){
LOG(cl);
@ -2060,10 +2071,9 @@ void AiL::RenderHud(){
hudOverlay.Draw();
Pixel vignetteOverlayColor="Interface.Vignette Color"_Pixel;
const float vignetteTotalDisplayTime="Interface.Vignette Appearance Time"_F+"Interface.Vignette Fadeout Time"_F;
if(vignetteDisplayTime<"Interface.Vignette Fadeout Time"_F)vignetteOverlayColor.a=util::lerp(0,255,vignetteDisplayTime/"Interface.Vignette Fadeout Time"_F);
DrawDecal({0,0},GFX["vignette.png"].Decal(),{1.f,1.f},vignetteOverlayColor);
if(vignetteDisplayTime<"Interface.Vignette Fadeout Time"_F)vignetteOverlayCol.a=util::lerp(0,255,vignetteDisplayTime/"Interface.Vignette Fadeout Time"_F);
DrawDecal({0,0},GFX["vignette.png"].Decal(),{1.f,1.f},vignetteOverlayCol);
}
void AiL::RenderCooldowns(){
@ -3080,6 +3090,7 @@ int main(const int argn,char**args)
}
}
{
InitializeGameConfigurations();
AiL demo;
demo.UsingSteamAPI(usingSteam);
@ -4334,8 +4345,9 @@ const float AiL::GetEncounterDuration()const{
return encounterDuration;
}
void AiL::ShowDamageVignetteOverlay(){
void AiL::ShowDamageVignetteOverlay(const Pixel col){
vignetteDisplayTime="Interface.Vignette Appearance Time"_F+"Interface.Vignette Fadeout Time"_F;
vignetteOverlayCol=col;
}
void AiL::GlobalGameUpdates(){

View File

@ -229,6 +229,7 @@ private:
float lastLockOnTargetTime{};
void AdminConsole();
virtual bool OnConsoleCommand(const std::string& sCommand)override final;
Pixel vignetteOverlayCol{"Interface.Vignette Color"_Pixel};
public:
AiL(bool testingMode=false);
bool OnUserCreate() override;
@ -362,7 +363,7 @@ public:
void UpdateMonsters();
void ActivateActionSetForAllControllers(InputActionSetHandle_t actionSetHandle);
const float GetEncounterDuration()const;
void ShowDamageVignetteOverlay();
void ShowDamageVignetteOverlay(const Pixel="Interface.Vignette Color"_Pixel);
void GlobalGameUpdates();
const bool QuitRequested()const;
void SetQuitAllowed(bool quittingAllowed); //Locks the game from quitting during sensitive operations such as file saving/loading.
@ -377,7 +378,6 @@ public:
Overlay&GetOverlay();
void SetWindSpeed(vf2d newWindSpd);
const vf2d&GetWindSpeed()const;
void InitializeGameConfigurations();
void InitializePlayer();
void SetWorldZoom(float newZoomScale);
//Plays the correct footstep sound based on player's current tile.

View File

@ -73,6 +73,8 @@ using MonsterSpawnerID=int;
#define ACCESS_PLAYER Player*p=game->GetPlayer();
#define INCLUDE_INITIALIZEGAMECONFIGURATIONS extern void InitializeGameConfigurations();
#define VARIANTS float,int,std::string,bool,vf2d,std::vector<std::any>,size_t
#undef INFINITE
#define INFINITE 999999

View File

@ -92,15 +92,15 @@ protected:
vf2d adjustedScale={scale,scale};
vf2d labelTextSize=
proportional?
vf2d(game->GetWrappedTextSizeProp(finalLabel,rect.size.x-2,adjustedScale)):
vf2d(game->GetWrappedTextSize(finalLabel,rect.size.x-2,adjustedScale));
vf2d(game->GetWrappedTextSizeProp(finalLabel,rect.size.x-4,adjustedScale)):
vf2d(game->GetWrappedTextSize(finalLabel,rect.size.x-4,adjustedScale));
if(fitToLabel){
labelTextSize=
proportional?
vf2d(game->GetTextSizeProp(finalLabel)*adjustedScale):
vf2d(game->GetTextSize(finalLabel)*adjustedScale);
float sizeRatio=(labelTextSize.x)/(rect.size.x-2);
float sizeRatio=(labelTextSize.x)/(rect.size.x-4);
if(sizeRatio>1){
adjustedScale.x/=sizeRatio;
labelTextSize.x/=sizeRatio;
@ -110,7 +110,7 @@ protected:
vf2d drawPos=vf2d{-1,0}+rect.middle()-vf2d{labelTextSize}/2; //Assume centered.
if(!centered){
if(rightAlign){
drawPos=vf2d{rect.pos.x+rect.size.x-labelTextSize.x-2,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here.
drawPos=vf2d{rect.pos.x+rect.size.x-labelTextSize.x-4,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here.
}else{ //Left Align
drawPos=vf2d{rect.pos.x+2,rect.middle().y-labelTextSize.y/2}; //We should at least vertically align here.
}
@ -138,15 +138,15 @@ protected:
}else{
if(shadow){
if(proportional){
window.DrawShadowStringPropDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-2,1.0f);
window.DrawShadowStringPropDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f);
}else{
window.DrawShadowStringDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-2,1.0f);
window.DrawShadowStringDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f);
}
}else{
if(proportional){
window.DrawStringPropDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-2,1.0f);
window.DrawStringPropDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f);
}else{
window.DrawStringDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-2,1.0f);
window.DrawStringDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f);
}
}
}

View File

@ -904,9 +904,18 @@ bool Player::Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag dama
if(Menu::IsMenuOpen()&&mod_dmg>0)Menu::CloseAllMenus();
if(!IsDOT&&mod_dmg>0)game->ShowDamageVignetteOverlay();
const bool tookLethalDamage{int(mod_dmg)>=hp};
bool survivedHitDueToDefiance{false};
if(tookLethalDamage&&game->GetPlayer()->HasEnchant("Death Defiance"))survivedHitDueToDefiance=util::random(1.f)<="Death Defiance"_ENC["LETHAL DAMAGE AVOID CHANCE"]/100.f;
hp=std::max(0,hp-int(mod_dmg));
if(!IsDOT&&mod_dmg>0){
Pixel vignetteOverlayCol{"Interface.Vignette Color"_Pixel};
if(tookLethalDamage&&survivedHitDueToDefiance)vignetteOverlayCol="Interface.Vignette Death Defiance Color"_Pixel;
game->ShowDamageVignetteOverlay(vignetteOverlayCol);
}
if(tookLethalDamage&&survivedHitDueToDefiance)ApplyIframes("Death Defiance"_ENC["LETHAL DAMAGE SURVIVE IFRAME TIME"]);
else hp=std::max(0,hp-int(mod_dmg));
if(!IsAlive()&&GameState::STATE!=GameState::states[States::DEATH])GameState::ChangeState(States::DEATH);

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_BUILD 10653
#define VERSION_BUILD 10670
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -30,6 +30,9 @@ Interface
# Damage Vignette Overlay Color
Vignette Color = 218, 44, 143, 255
# Damage Vignette Death Defiance Overlay Color
Vignette Death Defiance Color = 79, 255, 255, 255
# The original health display color.
HUD Health Display Color = 255,255,255,255

View File

@ -608,6 +608,8 @@ Item Enchants
Description = "{LETHAL DAMAGE AVOID CHANCE}% chance to avoid taking lethal damage."
LETHAL DAMAGE AVOID CHANCE = 30%
# If the player took lethal damage, they are given iframes to survive longer.
LETHAL DAMAGE SURVIVE IFRAME TIME = 2.0s
# Stat, Lowest, Highest Value
# Stat Modifier[0] = ..., 0, 0

View File

@ -650,7 +650,7 @@ void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
@ -698,7 +698,7 @@ void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view s
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
@ -730,7 +730,7 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
@ -743,7 +743,7 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr;
if(!ShadowRerenderRequired){
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2));
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal;
@ -782,7 +782,7 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
@ -795,7 +795,7 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr;
if(!ShadowRerenderRequired){
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2));
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal;

View File

@ -3499,7 +3499,7 @@ namespace olc
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
@ -3531,7 +3531,7 @@ namespace olc
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
@ -3563,7 +3563,7 @@ namespace olc
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
@ -3576,7 +3576,7 @@ namespace olc
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr;
if(!ShadowRerenderRequired){
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2));
garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{
newShadowDecal=garbageCollector[key+"_SHADOW"].decal;
@ -3673,7 +3673,7 @@ namespace olc
if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr;
if(!RerenderRequired){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x));
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y));
garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
@ -3686,7 +3686,7 @@ namespace olc
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr;
if(!ShadowRerenderRequired){
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2));
garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{
newShadowDecal=garbageCollector[key+"_SHADOW"].decal;

View File

@ -73,6 +73,10 @@ namespace olc::utils
class datafile
{
public:
enum class OverwriteMode{
NO_OVERWRITE,
OVERWRITE,
};
inline datafile() = default;
inline static bool DEBUG_ACCESS_OPTIONS=false;
inline static bool INITIAL_SETUP_COMPLETE=false;
@ -325,7 +329,7 @@ namespace olc::utils
return false;
}
inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',')
inline static bool Read(datafile& n, const std::string& sFileName, const char sListSep = ',', const OverwriteMode mode=OverwriteMode::NO_OVERWRITE)
{
bool previousSetupState=INITIAL_SETUP_COMPLETE;
INITIAL_SETUP_COMPLETE=false;
@ -393,7 +397,7 @@ namespace olc::utils
sPropName = line.substr(0, x);
trim(sPropName);
auto&top=stkPath.top().get();
if(stkPath.top().get().HasProperty(sPropName))ERR(std::format("WARNING! Duplicate key found! Key {} already exists! Duplicate line: {}",sPropName,line));
if(mode==OverwriteMode::NO_OVERWRITE&&stkPath.top().get().HasProperty(sPropName))ERR(std::format("WARNING! Duplicate key found! Key {} already exists! Duplicate line: {}",sPropName,line));
// Extract the property value, which is all characters after
// the first assignment operator, trim any whitespace from ends