Implemented util::Tokenize and appropriate unit test. Refactored ability description parsing function slightly to handle future delimiters. Release Build 13626.
Some checks failed
Emscripten Build / Build_and_Deploy_Web_Build (push) Failing after 1m23s
Emscripten Build / UnitTesting (push) Failing after 1m25s

This commit is contained in:
AMay 2026-05-13 01:59:43 -05:00
parent 4862652593
commit c9db9209d7
7 changed files with 90 additions and 49 deletions

View File

@ -37,6 +37,7 @@ All rights reserved.
#pragma endregion
#include "Ability.h"
#include "AdventuresInLestoria.h"
#include<ranges>
INCLUDE_game
@ -113,38 +114,68 @@ const std::string Ability::GetDescriptionWithPlayerModifiers()const{
};
const Pixel specialValCol{69,249,255};
const auto FindAndParse=[&specialPrefixes,&specialValCol](datafile&data,mutable std::string&finalText,mutable bool&bracesFound,mutable std::string&variableName)->bool{
if(data.HasProperty(variableName)){
finalText+=specialValCol.toHTMLColorCode()+data.GetProperty(variableName).GetFullString()+WHITE.toHTMLColorCode();
bracesFound=false;
variableName="";
return true;
}else {
enum SymbolIndicator{
MULTIPLICATION, //*
KEY_VALUE_PAIR, //:
NOT_FOUND,
};
const auto AddToDescription=[&finalText,&bracesFound,&variableName](const std::string&descriptionTextAddon){
finalText+=descriptionTextAddon;
bracesFound=false;
variableName="";
};
SymbolIndicator indicator{NOT_FOUND};
if(variableName.find(':')!=std::string::npos)indicator=KEY_VALUE_PAIR;
else if(variableName.find('*')!=std::string::npos)indicator=MULTIPLICATION;
switch(indicator){
case MULTIPLICATION:{
std::vector<std::string>tokens{util::Tokenize(variableName,'*')};
std::optional<float>numb{};
for(const std::string&token:tokens){
if(!numb)numb=std::stof(data.GetProperty(token).GetString());
else *numb*=std::stof(data.GetProperty(token).GetString());
}
if(!numb)ERR("WARNING! Somehow did not populate a number. Variable: "<<variableName);
AddToDescription(specialValCol.toHTMLColorCode()+std::format("{:.2}",*numb)+WHITE.toHTMLColorCode());
return true;
}break;
case KEY_VALUE_PAIR:{
std::vector<std::string>tokens{util::Tokenize(variableName,':')};
if(tokens.size()!=2)ERR(": variable syntax only allows 2 arguments. Ex. Key:Value. Arguments found: "<<tokens.size());
std::string separatorKey{tokens.at(0)};
std::string separatorVal{tokens.at(1)};
if(!specialPrefixes.count(separatorKey))ERR(std::format("Could not find translation function for key {} inside special prefixes map!",separatorKey))
else {
if(!data.HasProperty(separatorVal))return false; //Could not be found in this section, will need to seek in another location.
AddToDescription(specialValCol.toHTMLColorCode()+specialPrefixes[separatorKey](data,separatorVal)+WHITE.toHTMLColorCode());
return true;
}
}break;
}
}
return false;
};
const auto ParseVariables=[&](const std::string_view abilityText)->std::string{
std::string finalText{};
size_t marker{};
std::string variableName{};
bool bracesFound{false};
const auto FindAndParse=[&finalText,&bracesFound,&variableName,&specialPrefixes,&specialValCol](datafile&data)->bool{
if(data.HasProperty(variableName)){
finalText+=specialValCol.toHTMLColorCode()+data.GetProperty(variableName).GetFullString()+WHITE.toHTMLColorCode();
bracesFound=false;
variableName="";
return true;
}else if(size_t separatorMarker{std::string::npos};(separatorMarker=variableName.find(':'))!=std::string::npos){
std::string separatorKey{variableName.substr(0,separatorMarker)};
std::string separatorVal{variableName.substr(separatorMarker+1)};
if(!specialPrefixes.count(separatorKey))ERR(std::format("Could not find translation function for key {} inside special prefixes map!",separatorKey))
else {
if(!data.HasProperty(separatorVal))return false; //Could not be found in this section, will need to seek in another location.
finalText+=specialValCol.toHTMLColorCode()+specialPrefixes[separatorKey](data,separatorVal)+WHITE.toHTMLColorCode();
bracesFound=false;
variableName="";
return true;
}
}
return false;
};
while(marker<abilityText.length()){
const char c{abilityText[marker]};
if(!bracesFound&&c=='{')bracesFound=true;
else if(bracesFound&&c=='}'){ //Parse the variable.
if(!FindAndParse((*abilityConfig).get()))ERR(std::format("Could not find variable {} for Ability {}",variableName,GetName()));
if(!FindAndParse((*abilityConfig).get(),finalText,bracesFound,variableName))ERR(std::format("Could not find variable {} for Ability {}",variableName,GetName()));
}else if(bracesFound)variableName+=c;
else finalText+=c;
marker++;
@ -170,34 +201,16 @@ const std::string Ability::GetDescriptionWithPlayerModifiers()const{
std::string variableName{};
bool bracesFound{false};
const auto FindAndParse=[&finalText,&bracesFound,&variableName,&specialPrefixes,&specialValCol](datafile&data)->bool{
if(data.HasProperty(variableName)){
finalText+=specialValCol.toHTMLColorCode()+data.GetProperty(variableName).GetFullString()+WHITE.toHTMLColorCode();
bracesFound=false;
variableName="";
return true;
}else if(size_t separatorMarker{std::string::npos};(separatorMarker=variableName.find(':'))!=std::string::npos){
std::string separatorKey{variableName.substr(0,separatorMarker)};
std::string separatorVal{variableName.substr(separatorMarker+1)};
if(!specialPrefixes.count(separatorKey))ERR(std::format("Could not find translation function for key {} inside special prefixes map!",separatorKey))
else {
if(!data.HasProperty(separatorVal))return false; //Could not be found in this section, will need to seek in another location.
finalText+=specialValCol.toHTMLColorCode()+specialPrefixes[separatorKey](data,separatorVal)+WHITE.toHTMLColorCode();
bracesFound=false;
return true;
}
}
return false;
};
while(marker<abilityText.length()){
const char c{abilityText[marker]};
if(!bracesFound&&c=='{')bracesFound=true;
else if(bracesFound&&c=='}'){ //Parse the variable.
if(!FindAndParse(enchant.get().GetData()["Ability Settings"]))
if(!FindAndParse(enchant.get().GetData()))
if(!FindAndParse((*(*enchant.get().GetAbility())->abilityConfig).get()))
ERR(std::format("Could not find variable {} for enchant Ability Settings of enchantment {}",variableName,enchant.get().Name()));
const bool FoundVariable{
FindAndParse(enchant.get().GetData()["Ability Settings"],finalText,bracesFound,variableName)||
FindAndParse(enchant.get().GetData(),finalText,bracesFound,variableName)||
FindAndParse((*(*enchant.get().GetAbility())->abilityConfig).get(),finalText,bracesFound,variableName)
};
if(!FoundVariable)ERR(std::format("Could not find variable {} for enchant Ability Settings of enchantment {}",variableName,enchant.get().Name()));
}else if(bracesFound)variableName+=c;
else finalText+=c;
marker++;

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 3
#define VERSION_PATCH 0
#define VERSION_BUILD 13607
#define VERSION_BUILD 13626
#define stringify(a) stringify_(a)
#define stringify_(a) #a

View File

@ -60,7 +60,7 @@ Thief
{
Name = Hidden Dagger
Short Name = DAGGER
Description = Jump backwards while throwing a dagger.
Description = Jump backwards while throwing a dagger. The dagger deals {DamageMult:Damage}
Icon = hidden_dagger.png
Cooldown = 8
Mana Cost = 20

View File

@ -241,7 +241,7 @@ Wizard
FireRingDamageMult = 1
# How often the fire ring deals damage to enemies inside of it.
FireRingDamageFreq = 1
FireRingLifetime = 4
FireRingLifetime = 4.1000
WorldShakeTime = 2

View File

@ -188,3 +188,13 @@ TEST(EngineTests,"ClassUtilTest"){
REQUIRE("Witch"==classutils::ClassToString(Class::WITCH));
REQUIRE("Trapper"==classutils::ClassToString(Class::TRAPPER));
}
TEST(EngineTests,"TokenizeFunctionTest"){
REQUIRE_THROWS_AS(util::Tokenize("",','),std::runtime_error);
REQUIRE_THAT(util::Tokenize("1,2,3",','),Catch::Matchers::Equals(std::vector<std::string>{"1","2","3"}));
REQUIRE_THROWS_AS(util::Tokenize("1,2,,3",','),std::runtime_error);
REQUIRE_THROWS_AS(util::Tokenize("1,2,3,",','),std::runtime_error);
REQUIRE_THROWS_AS(util::Tokenize(",",','),std::runtime_error);
REQUIRE_THAT(util::Tokenize("1,2:3",':'),Catch::Matchers::Equals(std::vector<std::string>{"1,2","3"}));
REQUIRE_THAT(util::Tokenize("1,2:3:4:5:6:7:AB:;:1 0:11,12",':'),Catch::Matchers::Equals(std::vector<std::string>{"1,2","3","4","5","6","7","AB",";","1 0","11,12",}));
}

View File

@ -255,4 +255,20 @@ const std::string util::GetCaseInsensitiveFilename(const std::string&filename){
}
ERR(std::format("WARNING! Could not find or match case for filename {}! THIS SHOULD NOT BE HAPPENING!",filename));
std::unreachable();
}
const std::vector<std::string>util::Tokenize(const std::string&str,const char separatorChar){
std::vector<std::string>tokens;
size_t marker{};
size_t lastTokenInd{};
while((marker=str.find_first_of(separatorChar,lastTokenInd))!=std::string::npos){
const std::string token{str.substr(lastTokenInd,marker-lastTokenInd)};
lastTokenInd=marker+1;
if(token.length()==0)ERR("WARNING! A blank token was found! THIS SHOULD NOT BE HAPPENING! Input string:"<<str)
tokens.emplace_back(token);
}
const std::string token{str.substr(lastTokenInd)};
if(token.length()==0)ERR("WARNING! A blank token was found! THIS SHOULD NOT BE HAPPENING! Input string:"<<str)
tokens.emplace_back(token);
return tokens;
}

View File

@ -122,6 +122,8 @@ namespace olc::util{
const std::string toLower(const std::string oldStr);
const std::string GetCaseInsensitiveFilename(const std::string&filename);
const std::vector<std::string>Tokenize(const std::string&str,const char separatorChar);
}
template<class TL, class TR>