Added unit tests for new strip leading color and final render color engine rendering functions. Fixed rendering bug with text elements of different HTML color codes but with same content not having rendering with the correct color code. Fix potential memory leak errors occurring with rendering Font versions of shadow strings that would have different pulsating colors w/same text content. Added an admin console and added the ability to give the player items and accessories with enchants. Also added a help command. Change version number to appear red while in admin mode (as a visual). Unlock all button tied to admin mode. Release Build 10604.

mac-build
sigonasr2 4 months ago
parent f6731463da
commit 23673ec2af
  1. 4
      Adventures in Lestoria Tests/Adventures in Lestoria Tests.vcxproj
  2. 3
      Adventures in Lestoria Tests/Adventures in Lestoria Tests.vcxproj.filters
  3. 90
      Adventures in Lestoria Tests/EngineTests.cpp
  4. 1
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  5. 6
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  6. 72
      Adventures in Lestoria/AdventuresInLestoria.cpp
  7. 2
      Adventures in Lestoria/AdventuresInLestoria.h
  8. 10
      Adventures in Lestoria/ConsoleCommands.txt
  9. 1
      Adventures in Lestoria/DEFINES.h
  10. 12
      Adventures in Lestoria/Item.cpp
  11. 2
      Adventures in Lestoria/Item.h
  12. 27
      Adventures in Lestoria/ItemEnchant.cpp
  13. 1
      Adventures in Lestoria/ItemEnchant.h
  14. 2
      Adventures in Lestoria/Pixel.cpp
  15. 2
      Adventures in Lestoria/Pixel.h
  16. 6
      Adventures in Lestoria/RowItemDisplay.h
  17. 19
      Adventures in Lestoria/SaveFile.cpp
  18. 8
      Adventures in Lestoria/SettingsWindow.cpp
  19. 2
      Adventures in Lestoria/TODO.txt
  20. 2
      Adventures in Lestoria/Version.h
  21. 18
      Adventures in Lestoria/assets/config/items/ItemEnchants.txt
  22. 125
      Adventures in Lestoria/olcPGEX_ViewPort.h
  23. 193
      Adventures in Lestoria/olcPixelGameEngine.h
  24. 1
      Adventures in Lestoria/pixelGameEngine.cpp
  25. 8
      Adventures in Lestoria/util.cpp
  26. 1
      Adventures in Lestoria/util.h
  27. BIN
      x64/Release/Adventures in Lestoria.exe

@ -105,6 +105,10 @@
<SubType> <SubType>
</SubType> </SubType>
</ClCompile> </ClCompile>
<ClCompile Include="EngineTests.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="GeometryTests.cpp" /> <ClCompile Include="GeometryTests.cpp" />
<ClCompile Include="ItemTests.cpp"> <ClCompile Include="ItemTests.cpp">
<SubType> <SubType>

@ -72,5 +72,8 @@
<ClCompile Include="EnchantTests.cpp"> <ClCompile Include="EnchantTests.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="EngineTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -0,0 +1,90 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.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.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "CppUnitTest.h"
#include "AdventuresInLestoria.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace EngineTests
{
TEST_CLASS(EngineTest)
{
public:
std::unique_ptr<AiL>testGame;
TEST_METHOD_INITIALIZE(PlayerInitialize){
testGame.reset(new AiL(true));
}
TEST_METHOD(StripLeadingColorTest){
std::string noColCode{"Hello World!"};
Assert::AreEqual("Hello World!"s,testGame->stripLeadingCol(noColCode),L"Function should not strip out any text when there's no color codes.");
std::string leadingColCode{"#FFFFFFHello World!"};
Assert::AreEqual("Hello World!"s,testGame->stripLeadingCol(leadingColCode),L"Function should strip out color code at beginning of text.");
std::string extraColCodes{"#FFFFFFHello #00FF00World!"};
Assert::AreEqual("Hello #00FF00World!"s,testGame->stripLeadingCol(extraColCodes),L"Function should only strip out color code at beginning of text.");
std::u32string u32noColCode{noColCode.begin(),noColCode.end()};
std::u32string u32noColCodeResult{testGame->stripLeadingCol(u32noColCode)};
Assert::AreEqual("Hello World!"s,std::string{u32noColCodeResult.begin(),u32noColCodeResult.end()},L"Function should not strip out any text when there's no color codes.");
std::u32string u32leadingColCode{noColCode.begin(),noColCode.end()};
std::u32string u32leadingColCodeResult{testGame->stripLeadingCol(u32leadingColCode)};
Assert::AreEqual("Hello World!"s,std::string{u32leadingColCodeResult.begin(),u32leadingColCodeResult.end()},L"Function should strip out color code at beginning of text.");
std::u32string u32extraColCodes{extraColCodes.begin(),extraColCodes.end()};
std::u32string u32extraColCodesResult{testGame->stripLeadingCol(u32extraColCodes)};
Assert::AreEqual("Hello #00FF00World!"s,std::string{u32extraColCodesResult.begin(),u32extraColCodesResult.end()},L"Function should only strip out color code at beginning of text.");
}
TEST_METHOD(GetFinalRenderColorTest){
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,"Hello World!").n,L"Should use source color as there's no HTML color code.");
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,"#0000FFHello World!").n,L"Should use color in string since it has a leading HTML color code.");
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,"#0000FFHello #00FF00World!").n,L"Should use color in string since it has a leading HTML color code.");
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,"Hello #00FF00World!").n,L"Should use color in string since it has a leading HTML color code.");
std::string testStr{"Hello World!"};
std::u32string u32testStr{testStr.begin(),testStr.end()};
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,testStr).n,L"Should use source color as there's no HTML color code.");
std::string colorCodeStr{"#0000FFHello World!"};
std::u32string u32colorCodeStr{colorCodeStr.begin(),colorCodeStr.end()};
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,colorCodeStr).n,L"Should use color in string since it has a leading HTML color code.");
std::string extraColorCodeStr{"#0000FFHello #00FF00World!"};
std::u32string u32extraColorCodeStr{extraColorCodeStr.begin(),extraColorCodeStr.end()};
Assert::AreEqual(BLUE.n,testGame->GetFinalRenderColor(WHITE,extraColorCodeStr).n,L"Should use color in string since it has a leading HTML color code.");
std::string middleColorCodeStr{"Hello #00FF00World!"};
std::u32string u32middleColorCodeStr{middleColorCodeStr.begin(),middleColorCodeStr.end()};
Assert::AreEqual(WHITE.n,testGame->GetFinalRenderColor(WHITE,middleColorCodeStr).n,L"Should use color in string since it has a leading HTML color code.");
}
};
}

@ -1214,6 +1214,7 @@
<Text Include="Chapter_2_Monsters.txt" /> <Text Include="Chapter_2_Monsters.txt" />
<Text Include="Chapter_3_Monsters.txt" /> <Text Include="Chapter_3_Monsters.txt" />
<Text Include="characters.txt" /> <Text Include="characters.txt" />
<Text Include="ConsoleCommands.txt" />
<Text Include="Crawler_2_Bonus_Boss.txt" /> <Text Include="Crawler_2_Bonus_Boss.txt" />
<Text Include="Crawler_Artificer.txt" /> <Text Include="Crawler_Artificer.txt" />
<Text Include="Crawler_System_Overview.txt" /> <Text Include="Crawler_System_Overview.txt" />

@ -97,6 +97,9 @@
<Filter Include="Header Files\Engine"> <Filter Include="Header Files\Engine">
<UniqueIdentifier>{aaa148fb-5e34-4c35-a5bf-65ee8f2c0fb1}</UniqueIdentifier> <UniqueIdentifier>{aaa148fb-5e34-4c35-a5bf-65ee8f2c0fb1}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Documentation\Admin">
<UniqueIdentifier>{e565fb16-43e6-4d18-a450-af7474df70b9}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="olcPixelGameEngine.h"> <ClInclude Include="olcPixelGameEngine.h">
@ -1388,6 +1391,9 @@
<Text Include="assets\config\items\ItemEnchants.txt"> <Text Include="assets\config\items\ItemEnchants.txt">
<Filter>Configurations\Items</Filter> <Filter>Configurations\Items</Filter>
</Text> </Text>
<Text Include="ConsoleCommands.txt">
<Filter>Documentation\Admin</Filter>
</Text>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="assets\heart.ico"> <Image Include="assets\heart.ico">

@ -90,6 +90,7 @@ INCLUDE_FOREDROP_DATA
INCLUDE_MONSTER_DATA INCLUDE_MONSTER_DATA
INCLUDE_PACK_KEY INCLUDE_PACK_KEY
bool ADMIN_MODE = true; //Enables the Unlock All button and admin console.
bool _DEBUG_MAP_LOAD_INFO = false; bool _DEBUG_MAP_LOAD_INFO = false;
//360x240 //360x240
vi2d WINDOW_SIZE={24*15,24*10}; vi2d WINDOW_SIZE={24*15,24*10};
@ -380,12 +381,75 @@ bool AiL::OnUserCreate(){
return true; return true;
} }
void AiL::AdminConsole(){
if(ADMIN_MODE){
if(GetKey(F12).bPressed)ConsoleShow(F12);
}
}
bool AiL::OnConsoleCommand(const std::string& sCommand){
std::vector<std::string>args;
size_t marker{};
std::string acc{};
bool inQuotationMark{false};
while(marker<sCommand.length()){
const char character{sCommand[marker]};
if(character==' '&&!inQuotationMark){
args.emplace_back(acc);
acc.clear();
}else
if(character=='"')inQuotationMark=!inQuotationMark;
else{
acc+=character;
}
marker++;
}
if(acc.length()>0)args.emplace_back(acc);
if(args.size()>0){
try{
if(args[0]=="/help"){
std::ifstream helpFile("ConsoleCommands.txt");
while(helpFile.good())ConsoleOut()<<char(helpFile.get());
ConsoleOut()<<std::endl;
}else
if(args[0]=="/give"){
if(args.size()<2)ConsoleOut()<<"Usage: /give <Item Name> [Amount]"<<std::endl;
uint8_t itemAmt{1};
if(args.size()>=3)itemAmt=std::stoi(args[2]);
Inventory::AddItem(args[1],itemAmt);
ConsoleOut()<<"Added x"<<int(itemAmt)<<" "<<args[1]<<" to player's inventory."<<std::endl;
}else
if(args[0]=="/accessory"){
if(args.size()<2)ConsoleOut()<<"Usage: /accessory <Accessory Name> [Enchant Name]"<<std::endl;
std::weak_ptr<Item>accessory{Inventory::AddItem(args[1])};
if(args.size()>=3)accessory.lock()->EnchantItem(args[2]);
ConsoleOut()<<"Added "<<args[1]<<" to player's inventory."<<std::endl;
}else{
ConsoleOut()<<"Invalid command! Use /help to see available commands."<<std::endl;
}
}catch(std::exception&e){
ConsoleOut()<<"An exception has occurred: "<<e.what()<<std::endl;
}
}else{
ConsoleOut()<<"Invalid command!"<<std::endl;
}
return true;
}
bool AiL::OnUserUpdate(float fElapsedTime){ bool AiL::OnUserUpdate(float fElapsedTime){
AdminConsole();
Audio::Update();
if(!IsConsoleShowing()){
GlobalGameUpdates(); GlobalGameUpdates();
LoadingScreen::Update(); LoadingScreen::Update();
InputListener::Update(); InputListener::Update();
Tutorial::Update(); Tutorial::Update();
Audio::Update(); }
if(!game->TestingModeEnabled()){ if(!game->TestingModeEnabled()){
GameState::STATE->Draw(this); GameState::STATE->Draw(this);
RenderMenu(); RenderMenu();
@ -404,6 +468,8 @@ bool AiL::OnUserUpdate(float fElapsedTime){
} }
#endif #endif
} }
skipGameProcess:
return !gameEnd; return !gameEnd;
} }
@ -3550,7 +3616,7 @@ void AiL::RenderMenu(){
if(Menu::alreadyClicked){ //Do not run a click event for this round. if(Menu::alreadyClicked){ //Do not run a click event for this round.
Menu::alreadyClicked=false; Menu::alreadyClicked=false;
}else{ }else{
if(!MenuClicksDeactivated()){ if(!MenuClicksDeactivated()&&!IsConsoleShowing()){
Menu::stack.back()->Update(this); Menu::stack.back()->Update(this);
} }
} }
@ -3765,7 +3831,7 @@ void AiL::RenderVersionInfo(){
} }
std::string versionStr("v" + std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + "." + std::to_string(VERSION_PATCH) + "." + std::to_string(VERSION_BUILD)); std::string versionStr("v" + std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + "." + std::to_string(VERSION_PATCH) + "." + std::to_string(VERSION_BUILD));
DrawShadowStringDecal(vf2d{ GetScreenSize() } - vf2d{ GetTextSize(versionStr) }*0.4f,versionStr,WHITE,BLACK,{0.4f,0.4f},std::numeric_limits<float>::max(),0.4f); DrawShadowStringDecal(vf2d{ GetScreenSize() } - vf2d{ GetTextSize(versionStr) }*0.4f,versionStr,ADMIN_MODE?RED:WHITE,BLACK,{0.4f,0.4f},std::numeric_limits<float>::max(),0.4f);
} }
int AiL::GetCurrentChapter(){ int AiL::GetCurrentChapter(){

@ -227,6 +227,8 @@ private:
float zoomAdjustSpeed{0.1f}; float zoomAdjustSpeed{0.1f};
std::vector<std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>>lockOnTargets; std::vector<std::tuple<std::weak_ptr<Monster>,StackCount,MarkTime>>lockOnTargets;
float lastLockOnTargetTime{}; float lastLockOnTargetTime{};
void AdminConsole();
virtual bool OnConsoleCommand(const std::string& sCommand)override final;
public: public:
AiL(bool testingMode=false); AiL(bool testingMode=false);
bool OnUserCreate() override; bool OnUserCreate() override;

@ -0,0 +1,10 @@
To open the admin console, press F12 in-game. The admin console is turned on via the ADMIN_MODE variable located in AdventuresInLestoria.cpp
Required arguments are in <>. Optional arguments are in [].
Items with spaces must be surrounded in quotation marks "".
Valid Commands are:
/help - Shows this help file.
/give <Item Name> [Amount] - Gives an item of type <Item Name> into the player's inventory. Optionally adds [Amount] amount of items to the player's inventory.
/accessory <Accessory Name> [Enchant Name] - Gives an accessory of type <Accessory Name> into the player's inventory. Optionally adds an enchant of type [Enchant Name].

@ -61,6 +61,7 @@ using MonsterSpawnerID=int;
#define DO_NOTHING [](MenuFuncData data){return true;} #define DO_NOTHING [](MenuFuncData data){return true;}
#define INCLUDE_WINDOW_SIZE extern vi2d WINDOW_SIZE; #define INCLUDE_WINDOW_SIZE extern vi2d WINDOW_SIZE;
#define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS; #define INCLUDE_ITEM_CONVERSIONS extern safemap<std::string,IT>ITEM_CONVERSIONS;
#define INCLUDE_ADMIN_MODE extern bool ADMIN_MODE;
#define INCLUDE_PACK_KEY extern std::string PACK_KEY; #define INCLUDE_PACK_KEY extern std::string PACK_KEY;

@ -700,7 +700,7 @@ const std::string Item::DisplayName()const{
if(IsEquippable()&&EnhancementLevel()>0){ if(IsEquippable()&&EnhancementLevel()>0){
name+=" [#00FF00+"+std::to_string(EnhancementLevel())+"#FFFFFF]"; name+=" [#00FF00+"+std::to_string(EnhancementLevel())+"#FFFFFF]";
} }
return name; return GetDisplayNameColor().toHTMLColorCode()+name;
} }
const bool ItemInfo::IsEquippable()const{ const bool ItemInfo::IsEquippable()const{
return slot!=EquipSlot::NONE&&(category=="Equipment"||category=="Accessories"); return slot!=EquipSlot::NONE&&(category=="Equipment"||category=="Accessories");
@ -768,6 +768,9 @@ const std::string Item::Description(CompactText compact)const{
} }
} }
} }
if(HasEnchant()){
description+=std::format("\n#D48EFF{}\n#E0E0E0{}#FFFFFF",GetEnchant().value().Name(),GetEnchant().value().Description());
}
return description; return description;
} }
const ITCategory Item::Category()const{ const ITCategory Item::Category()const{
@ -1071,7 +1074,7 @@ const std::string Stats::GetStatsString(const Stats&maxStats,CompactText compact
std::string col=""; std::string col="";
if(maxStats.attributes.count(attr)&&int(val)>=int(maxStats.attributes.at(attr))){ if(maxStats.attributes.count(attr)&&int(val)>=int(maxStats.attributes.at(attr))){
Pixel shimmeringCol=PixelLerp({255,196,60},{254,217,133},sin((70*game->GetRunTime())/2.f+0.5f)); Pixel shimmeringCol=PixelLerp({255,196,60},{254,217,133},sin((70*game->GetRunTime())/2.f+0.5f));
col=util::PixelToHTMLColorCode(shimmeringCol); col=shimmeringCol.toHTMLColorCode();
} }
description+=col+std::string(attr.Name())+": "+statNumber+(attr.DisplayAsPercent()?"%":"")+"#FFFFFF"; description+=col+std::string(attr.Name())+": "+statNumber+(attr.DisplayAsPercent()?"%":"")+"#FFFFFF";
@ -1454,3 +1457,8 @@ std::optional<ItemEnchant>Item::GetEnchant()const{
const bool Item::HasEnchant()const{ const bool Item::HasEnchant()const{
return enchant.has_value(); return enchant.has_value();
} }
const Pixel Item::GetDisplayNameColor()const{
Pixel col{WHITE};
if(HasEnchant())col=0xD8BDFF;
return col;
}

@ -188,6 +188,7 @@ private:
Stats randomizedStats; Stats randomizedStats;
bool locked=false; bool locked=false;
std::optional<ItemEnchant>enchant; std::optional<ItemEnchant>enchant;
Pixel displayNameColor{WHITE};
void SetAmt(uint32_t newAmt); void SetAmt(uint32_t newAmt);
static ItemEnhancementFunctionPrimingData enhanceFunctionPrimed; static ItemEnhancementFunctionPrimingData enhanceFunctionPrimed;
@ -258,6 +259,7 @@ public:
friend const bool operator==(std::weak_ptr<Item>lhs,const IT&rhs){return !lhs.expired()&&lhs.lock()->ActualName()==const_cast<IT&>(rhs);}; friend const bool operator==(std::weak_ptr<Item>lhs,const IT&rhs){return !lhs.expired()&&lhs.lock()->ActualName()==const_cast<IT&>(rhs);};
friend const bool operator==(const IT&lhs,std::weak_ptr<Item>rhs){return operator==(rhs,lhs);}; friend const bool operator==(const IT&lhs,std::weak_ptr<Item>rhs){return operator==(rhs,lhs);};
friend const bool operator==(const IT&lhs,std::shared_ptr<Item>rhs){return operator==(rhs,lhs);}; friend const bool operator==(const IT&lhs,std::shared_ptr<Item>rhs){return operator==(rhs,lhs);};
const Pixel GetDisplayNameColor()const;
}; };
class Inventory{ class Inventory{

@ -103,13 +103,6 @@ void ItemEnchantInfo::Initialize(){
if(!affectSlots.count(affectStr))ERR(std::format("WARNING! Could not find translate ability affect slot name {} to a valid slot! Valid slot names are: \"Auto Attack, Right Click Ability, Ability 1, Ability 2, Ability 3\"",affectStr)); if(!affectSlots.count(affectStr))ERR(std::format("WARNING! Could not find translate ability affect slot name {} to a valid slot! Valid slot names are: \"Auto Attack, Right Click Ability, Ability 1, Ability 2, Ability 3\"",affectStr));
newEnchant.abilitySlot=affectSlots.at(affectStr); newEnchant.abilitySlot=affectSlots.at(affectStr);
} }
size_t statModifierInd{};
while(enchant.HasProperty(std::format("Stat Modifier[{}]",statModifierInd))){
const datafile&stat{enchant[std::format("Stat Modifier[{}]",statModifierInd)]};
newEnchant.minStatModifiers.A(stat.GetString(0))=stat.GetReal(1);
newEnchant.maxStatModifiers.A(stat.GetString(0))=stat.GetReal(2);
statModifierInd++;
}
auto IsRequiredKey=[](const std::string_view key){return key=="Description"||key=="Affects"||key.starts_with("Stat Modifier[");}; auto IsRequiredKey=[](const std::string_view key){return key=="Description"||key=="Affects"||key.starts_with("Stat Modifier[");};
@ -128,6 +121,14 @@ void ItemEnchantInfo::Initialize(){
enchantDescription=enchantDescription.replace(configValInd,wrappedConfigStr.length(),formattedFloat); enchantDescription=enchantDescription.replace(configValInd,wrappedConfigStr.length(),formattedFloat);
} }
size_t statModifierInd{};
while(enchant.HasProperty(std::format("Stat Modifier[{}]",statModifierInd))){
const datafile&stat{enchant[std::format("Stat Modifier[{}]",statModifierInd)]};
newEnchant.minStatModifiers.A(stat.GetString(0))=stat.GetReal(1);
newEnchant.maxStatModifiers.A(stat.GetString(0))=stat.GetReal(2);
statModifierInd++;
}
newEnchant.description=enchantDescription; newEnchant.description=enchantDescription;
return newEnchant; return newEnchant;
@ -155,7 +156,7 @@ void ItemEnchantInfo::Initialize(){
} }
ItemEnchant::ItemEnchant(const std::string_view enchantName) ItemEnchant::ItemEnchant(const std::string_view enchantName)
:enchantName(std::string(enchantName)){ :enchantName(std::string(enchantName)),description(ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).Description()){
for(const auto&[attr,val]:ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).minStatModifiers){ for(const auto&[attr,val]:ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).minStatModifiers){
float minVal=ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).minStatModifiers.A_Read(attr); float minVal=ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).minStatModifiers.A_Read(attr);
float maxVal=ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).maxStatModifiers.A_Read(attr); float maxVal=ItemEnchantInfo::ENCHANT_LIST.at(this->enchantName).maxStatModifiers.A_Read(attr);
@ -164,6 +165,12 @@ ItemEnchant::ItemEnchant(const std::string_view enchantName)
const auto&randRange{std::ranges::iota_view(int(minVal),int(maxVal))}; const auto&randRange{std::ranges::iota_view(int(minVal),int(maxVal))};
A(attr)=randRange[util::random()%randRange.size()]; A(attr)=randRange[util::random()%randRange.size()];
} }
const std::string wrappedConfigStr{std::vformat("{{{}}}",std::make_format_args(attr.ActualName()))};
size_t configValInd{description.find(wrappedConfigStr)};
if(configValInd==std::string::npos)continue;
std::string formattedFloat{std::format("{}",val)};
description=description.replace(configValInd,wrappedConfigStr.length(),formattedFloat);
} }
} }
@ -178,7 +185,7 @@ const std::string ItemEnchantInfo::Name(TextStyle style)const{
return name; return name;
}break; }break;
case COLOR_CODES:{ case COLOR_CODES:{
return ENCHANT_CATEGORIES.at(category).displayCol.toHexString()+name; return ENCHANT_CATEGORIES.at(category).displayCol.toHTMLColorCode()+name;
}break; }break;
} }
return{}; return{};
@ -203,7 +210,7 @@ const std::string ItemEnchant::Name(ItemEnchantInfo::TextStyle style)const{
return GetEnchantInfo().Name(style); return GetEnchantInfo().Name(style);
} }
const std::string_view ItemEnchant::Description()const{ const std::string_view ItemEnchant::Description()const{
return GetEnchantInfo().Description(); return description;
} }
const ItemEnchantInfo::ItemEnchantCategory&ItemEnchant::Category()const{ const ItemEnchantInfo::ItemEnchantCategory&ItemEnchant::Category()const{
return GetEnchantInfo().Category(); return GetEnchantInfo().Category();

@ -104,4 +104,5 @@ public:
private: private:
const ItemEnchantInfo&GetEnchantInfo()const; const ItemEnchantInfo&GetEnchantInfo()const;
std::string enchantName; std::string enchantName;
std::string description;
}; };

@ -168,7 +168,7 @@ namespace olc{
return Pixel(nR, nG, nB, a); return Pixel(nR, nG, nB, a);
} }
std::string Pixel::toHexString()const{ std::string Pixel::toHTMLColorCode()const{
return std::format("#{:2X}{:2X}{:2X}",r,g,b); return std::format("#{:2X}{:2X}{:2X}",r,g,b);
} }

@ -90,7 +90,7 @@ namespace olc{
Pixel& operator *=(const Pixel& p); Pixel& operator *=(const Pixel& p);
bool operator < (const Pixel& p)const; bool operator < (const Pixel& p)const;
Pixel inv() const; Pixel inv() const;
std::string toHexString()const; std::string toHTMLColorCode()const;
}; };
Pixel PixelF(float red, float green, float blue, float alpha = 1.0f); Pixel PixelF(float red, float green, float blue, float alpha = 1.0f);

@ -102,13 +102,13 @@ public:
bool missingRequirements=!canEnhance&&fadeOutIfMissingRequirements; bool missingRequirements=!canEnhance&&fadeOutIfMissingRequirements;
Pixel itemNameTextCol=WHITE;
if(locked&&hideLabelWhileLocked)std::for_each(itemName.begin(),itemName.end(),[](char&c){if(c>='0'&&c<='z'){c='?';}});
Pixel itemNameTextCol{itemRef.lock()->GetDisplayNameColor()};
if(missingRequirements){ if(missingRequirements){
itemNameTextCol=DARK_GREY; itemNameTextCol=DARK_GREY;
} }
if(locked&&hideLabelWhileLocked)std::for_each(itemName.begin(),itemName.end(),[](char&c){if(c>='0'&&c<='z'){c='?';}});
window.DrawShadowStringPropDecal(rect.pos+vf2d{4,4}+vf2d{iconSize.x,iconSize.y/2-4},itemName,itemNameTextCol,BLACK,scaledSize); window.DrawShadowStringPropDecal(rect.pos+vf2d{4,4}+vf2d{iconSize.x,iconSize.y/2-4},itemName,itemNameTextCol,BLACK,scaledSize);
#pragma endregion #pragma endregion

@ -51,6 +51,7 @@ All rights reserved.
#include "Tutorial.h" #include "Tutorial.h"
INCLUDE_game INCLUDE_game
INCLUDE_ADMIN_MODE
size_t SaveFile::saveFileID=0; size_t SaveFile::saveFileID=0;
std::string SaveFile::saveFileName=""; std::string SaveFile::saveFileName="";
@ -104,6 +105,13 @@ const void SaveFile::SaveGame(){
for(const auto&[attr,val]:item->RandomStats()){ for(const auto&[attr,val]:item->RandomStats()){
saveFile["Items"][std::format("Item[{}]",itemCount)]["Attributes"][std::string(attr.ActualName())].SetReal(val); saveFile["Items"][std::format("Item[{}]",itemCount)]["Attributes"][std::string(attr.ActualName())].SetReal(val);
} }
if(item->HasEnchant()){
saveFile["Items"][std::format("Item[{}]",itemCount)]["Enchant"]["Name"].SetString(item->GetEnchant().value().Name());
ItemEnchant enchant{item->GetEnchant().value()};
for(const auto&[attr,val]:enchant){
saveFile["Items"][std::format("Item[{}]",itemCount)]["Enchant"]["Attributes"][std::string(attr.ActualName())].SetReal(val);
}
}
itemCount++; itemCount++;
} }
saveFile["Player"]["Class"].SetString(game->GetPlayer()->GetClassName()); saveFile["Player"]["Class"].SetString(game->GetPlayer()->GetClassName());
@ -331,7 +339,7 @@ void SaveFile::LoadFile(){
std::string fileHash=util::GetHash("save_file_path"_S+std::format("save.{:04}",saveFileID)); std::string fileHash=util::GetHash("save_file_path"_S+std::format("save.{:04}",saveFileID));
trim(fileHash); //It's possible the expected file hash has a space at the end/beginning that gets stripped out. We want to trim and match that string. trim(fileHash); //It's possible the expected file hash has a space at the end/beginning that gets stripped out. We want to trim and match that string.
if(expectedFileHash!=fileHash){ if(!ADMIN_MODE&&expectedFileHash!=fileHash){
LOG(std::format("WARNING! Filehash for file {} was not identified as proper! Will not load this file!","save_file_path"_S+std::format("save.{:04}",saveFileID))); LOG(std::format("WARNING! Filehash for file {} was not identified as proper! Will not load this file!","save_file_path"_S+std::format("save.{:04}",saveFileID)));
return; return;
} }
@ -367,6 +375,15 @@ void SaveFile::LoadFile(){
newItem.lock()->Lock(); newItem.lock()->Lock();
} }
} }
if(data.HasProperty("Enchant")){
newItem.lock()->enchant=ItemEnchant{data["Enchant"]["Name"].GetString()};
if(loadFile.GetProperty(std::format("Items.{}.Enchant",key)).HasProperty("Attributes")){
for(auto&[attr,data]:loadFile.GetProperty(std::format("Items.{}.Enchant.Attributes",key)).GetOrderedKeys()){
newItem.lock()->enchant.value().A(attr)=data.GetReal();
}
}
}
} }
game->ChangePlayerClass(classutils::StringToClass(loadFile["Player"]["Class"].GetString())); game->ChangePlayerClass(classutils::StringToClass(loadFile["Player"]["Class"].GetString()));
game->GetPlayer()->SetLevel(loadFile["Player"]["Level"].GetInt()); game->GetPlayer()->SetLevel(loadFile["Player"]["Level"].GetInt());

@ -55,18 +55,16 @@ All rights reserved.
INCLUDE_DATA INCLUDE_DATA
INCLUDE_game INCLUDE_game
INCLUDE_WINDOW_SIZE INCLUDE_WINDOW_SIZE
INCLUDE_ADMIN_MODE
using A=Attribute; using A=Attribute;
#define UNLOCK_ALL_BUTTON
//#undef UNLOCK_ALL_BUTTON //Comment out to enable unlock all button.
void Menu::InitializeSettingsWindow(){ void Menu::InitializeSettingsWindow(){
vf2d windowSize=WINDOW_SIZE-vf2d{28,28}; vf2d windowSize=WINDOW_SIZE-vf2d{28,28};
Menu*settingsWindow=CreateMenu(SETTINGS,CENTERED,windowSize); Menu*settingsWindow=CreateMenu(SETTINGS,CENTERED,windowSize);
#pragma region Unlock All Button #pragma region Unlock All Button
#ifdef UNLOCK_ALL_BUTTON if(ADMIN_MODE){
settingsWindow->ADD("Unlock All Button",MenuComponent)(geom2d::rect<float>{{4,windowSize.y-20},{72,12}},"Unlock All",[](MenuFuncData data){ settingsWindow->ADD("Unlock All Button",MenuComponent)(geom2d::rect<float>{{4,windowSize.y-20},{72,12}},"Unlock All",[](MenuFuncData data){
if(Menu::IsCurrentlyActive(SETTINGS)){ if(Menu::IsCurrentlyActive(SETTINGS)){
for(auto&cp:State_OverworldMap::connections){ for(auto&cp:State_OverworldMap::connections){
@ -76,7 +74,7 @@ void Menu::InitializeSettingsWindow(){
} }
return true; return true;
})END; })END;
#endif }
#pragma endregion #pragma endregion
settingsWindow->ADD("Settings Label",MenuLabel)(geom2d::rect<float>{{4,4},vf2d{windowSize.x-8,24}},"Game Settings",2.f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END; settingsWindow->ADD("Settings Label",MenuLabel)(geom2d::rect<float>{{4,4},vf2d{windowSize.x-8,24}},"Game Settings",2.f,ComponentAttr::BACKGROUND|ComponentAttr::OUTLINE|ComponentAttr::SHADOW)END;

@ -23,6 +23,8 @@ DEMO
==== ====
Add Unit Test for Attack Speed Reduction stat. Add Unit Test for Attack Speed Reduction stat.
Move censoredTextEntry calculation in MenuLabel draw somewhere else perhaps?
PGETinker notes PGETinker notes
=============== ===============
Changing zoom size does not affect the indentation breadcumb immediately (requires scrolling to change the view) Changing zoom size does not affect the indentation breadcumb immediately (requires scrolling to change the view)

@ -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 3 #define VERSION_PATCH 3
#define VERSION_BUILD 10538 #define VERSION_BUILD 10604
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -8,49 +8,49 @@ Item Enchants
Health Boost Health Boost
{ {
Description = "Increases Maximum Health %." Description = "Increases Maximum Health by {Health %}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Health %, 3, 5 Stat Modifier[0] = Health %, 3, 5
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
} }
Attack Boost Attack Boost
{ {
Description = "Increases Attack %." Description = "Increases Attack {Attack %}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Attack %, 3, 5 Stat Modifier[0] = Attack %, 3, 5
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
} }
Movement Boost Movement Boost
{ {
Description = "Increases Move Spd %." Description = "Increases Move Spd by {Move Spd %}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Move Spd %, 3, 5 Stat Modifier[0] = Move Spd %, 3, 5
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
} }
Ability Haste Ability Haste
{ {
Description = "Decreases cooldown time of abilities." Description = "Decreases cooldown time of abilities by {CDR}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = CDR, 3, 5 Stat Modifier[0] = CDR, 3, 5
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
} }
Crit Rate Crit Rate
{ {
Description = "Increases critical hit rate." Description = "Increases critical hit rate by {Crit Rate}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Crit Rate, 3, 5 Stat Modifier[0] = Crit Rate, 3, 5
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
} }
Crit Damage Crit Damage
{ {
Description = "Increases damage from critical hits." Description = "Increases damage from critical hits by {Crit Dmg}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Crit Dmg, 7, 10 Stat Modifier[0] = Crit Dmg, 7, 10
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
} }
Stoneskin Stoneskin
{ {
Description = "Reduces damage taken." Description = "Reduces damage taken by {Damage Reduction}%"
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Damage Reduction, 3, 5 Stat Modifier[0] = Damage Reduction, 3, 5
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
@ -58,7 +58,7 @@ Item Enchants
Mana Pool Mana Pool
{ {
Description = "Increases maximum mana." Description = "Increases maximum mana by {Mana}."
# Stat, Lowest, Highest Value # Stat, Lowest, Highest Value
Stat Modifier[0] = Mana, 7, 12 Stat Modifier[0] = Mana, 7, 12
# Stat Modifier[1] = ... # Stat Modifier[1] = ...
@ -161,7 +161,7 @@ Item Enchants
} }
Tumble Tumble
{ {
Description = "Roll range, invulnerability time, and Movespeed boost are all increased by {BOOST PERCENTAGE}%." Description = "Roll range, invulnerability time, and Movespeed boost are all increased by {BOOST PERCENTAGE}%"
Affects = Right Click Ability Affects = Right Click Ability
BOOST PERCENTAGE = 30% BOOST PERCENTAGE = 30%

@ -636,100 +636,113 @@ float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) {
void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling){ void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling){
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSD_"+std::string(pge->stripCol(sText))}; std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSD_"+originalKey};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale); const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr;
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=pge->GetWrappedTextSize(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!pge->garbageCollector.count(key)){ 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.x));
pge->garbageCollector[key].decal=newDecal; pge->garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=pge->garbageCollector[key].decal; newDecal=pge->garbageCollector[key].decal;
} }
pge->garbageCollector[key].originalStr=sText; pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite); pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK); pge->Clear(BLANK);
pge->DrawString({0,0},sText,WHITE,1U,width/scale.x); pge->DrawString({0,0},renderStr,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr); pge->SetDrawTarget(nullptr);
newDecal->Update(); newDecal->Update();
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){ void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){
if(sText.length()==0)return; if(sText.length()==0)return;
std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+pge->stripCol(sText); std::u32string originalKey{pge->stripCol(sText)};
std::u32string renderStr{pge->stripLeadingCol(sText)};
std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end()); std::string key=std::string(Ukey.begin(),Ukey.end());
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(pge->garbageCollector.count(key)){ if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal; delete pge->garbageCollector[key].decal;
} pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling){ void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling){
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSPD"+std::string(pge->stripCol(sText))}; std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSPD"+originalKey};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale); const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr;
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=pge->GetWrappedTextSizeProp(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!pge->garbageCollector.count(key)){ 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.x));
pge->garbageCollector[key].decal=newDecal; pge->garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=pge->garbageCollector[key].decal; newDecal=pge->garbageCollector[key].decal;
} }
pge->garbageCollector[key].originalStr=sText; pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite); pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK); pge->Clear(BLANK);
pge->DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); pge->DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr); pge->SetDrawTarget(nullptr);
newDecal->Update(); newDecal->Update();
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSSD_"+std::string(pge->stripCol(sText))}; std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSSD_"+originalKey};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale); const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr;
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=pge->GetWrappedTextSize(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!pge->garbageCollector.count(key)){ 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.x/scale.x));
pge->garbageCollector[key].decal=newDecal; pge->garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=pge->garbageCollector[key].decal; newDecal=pge->garbageCollector[key].decal;
} }
pge->garbageCollector[key].originalStr=sText; pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite); pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK); pge->Clear(BLANK);
pge->DrawString({0,0},sText,WHITE,1U,width/scale.x); pge->DrawString({0,0},renderStr,WHITE,1U,width/scale.x);
newDecal->Update(); newDecal->Update();
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr; Decal*newShadowDecal=nullptr;
if(!pge->garbageCollector.count(key+"_SHADOW")){ 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.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{ }else{
@ -740,7 +753,7 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
pge->DrawString(vf2d{x,y}+adjustedShadowSizeFactor, sText, WHITE,4U,width/scale.x*4); pge->DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
} }
@ -750,34 +763,38 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSSPD"+std::string(pge->stripCol(sText))}; std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSSPD"+originalKey};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale); const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr;
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=pge->GetWrappedTextSizeProp(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!pge->garbageCollector.count(key)){ 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.x/scale.x));
pge->garbageCollector[key].decal=newDecal; pge->garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=pge->garbageCollector[key].decal; newDecal=pge->garbageCollector[key].decal;
} }
pge->garbageCollector[key].originalStr=sText; pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite); pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK); pge->Clear(BLANK);
pge->DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); pge->DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x);
newDecal->Update(); newDecal->Update();
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr; Decal*newShadowDecal=nullptr;
if(!pge->garbageCollector.count(key+"_SHADOW")){ 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.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{ }else{
@ -788,7 +805,7 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
pge->DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor, sText, WHITE,4U,width/scale.x*4); pge->DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
} }
@ -798,19 +815,20 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){
if(sText.length()==0)return; if(sText.length()==0)return;
std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+pge->stripCol(sText); std::u32string originalKey{pge->stripCol(sText)};
std::u32string renderStr{pge->stripLeadingCol(sText)};
std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end()); std::string key=std::string(Ukey.begin(),Ukey.end());
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(pge->garbageCollector.count(key)){ if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal; delete pge->garbageCollector[key].decal;
} pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
std::erase_if(pge->garbageCollector,[&](auto&key){ std::erase_if(pge->garbageCollector,[&](auto&key){
@ -827,25 +845,26 @@ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const
} }
} }
} }
DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ void olc::ViewPort::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
if(sText.length()==0)return; if(sText.length()==0)return;
std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+pge->stripCol(sText); std::u32string originalKey{pge->stripCol(sText)};
std::u32string renderStr{pge->stripLeadingCol(sText)};
std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end()); std::string key=std::string(Ukey.begin(),Ukey.end());
if(!pge->garbageCollector.count(key)||pge->garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(pge->garbageCollector.count(key)){ if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal; delete pge->garbageCollector[key].decal;
} pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0.5f,0},pge->garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0.5f,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col)const{ void olc::ViewPort::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col)const{

@ -931,6 +931,10 @@ namespace olc
static std::unique_ptr<Platform> platform; static std::unique_ptr<Platform> platform;
static std::map<size_t, uint8_t> mapKeys; static std::map<size_t, uint8_t> mapKeys;
namespace EngineTests{
class EngineTest;
}
// O------------------------------------------------------------------------------O // O------------------------------------------------------------------------------O
// | olc::PixelGameEngine - The main BASE class for your application | // | olc::PixelGameEngine - The main BASE class for your application |
// O------------------------------------------------------------------------------O // O------------------------------------------------------------------------------O
@ -1204,7 +1208,6 @@ namespace olc
void SetMosaicEffect(uint8_t effectLevel); void SetMosaicEffect(uint8_t effectLevel);
HWButton*const GetKeyboardState(uint8_t key); HWButton*const GetKeyboardState(uint8_t key);
public: public:
static std::map<char,Pixel> charToColor; static std::map<char,Pixel> charToColor;
static std::string Grey; static std::string Grey;
@ -1273,6 +1276,14 @@ namespace olc
friend class ViewPort; friend class ViewPort;
std::string stripLeadingCol(std::string_view originalText);
std::u32string stripLeadingCol(std::u32string_view originalText);
std::string stripCol(std::string_view originalText);
std::u32string stripCol(std::u32string_view originalText);
//If WHITE is provided as the blend color (as is the default), then we need to check the source string for a leading HTML color code. If we find one, return that as the final color we want to render the string as. Otherwise, return the original color.
const Pixel GetFinalRenderColor(const Pixel initialCol,std::string_view sourceStr);
const Pixel GetFinalRenderColor(const Pixel initialCol,std::u32string_view sourceStr);
private: // Inner mysterious workings private: // Inner mysterious workings
olc::Sprite* pDrawTarget = nullptr; olc::Sprite* pDrawTarget = nullptr;
Pixel::Mode nPixelMode = Pixel::NORMAL; Pixel::Mode nPixelMode = Pixel::NORMAL;
@ -1338,9 +1349,6 @@ namespace olc
std::list<std::string> sCommandHistory; std::list<std::string> sCommandHistory;
std::list<std::string>::iterator sCommandHistoryIt; std::list<std::string>::iterator sCommandHistoryIt;
std::string stripCol(std::string_view originalText);
std::u32string stripCol(std::u32string_view originalText);
// Text Entry Specific // Text Entry Specific
bool bTextEntryEnable = false; bool bTextEntryEnable = false;
std::string sTextEntryString = ""; std::string sTextEntryString = "";
@ -2125,6 +2133,16 @@ namespace olc
}); });
} }
std::string PixelGameEngine::stripLeadingCol(std::string_view originalText){
if(originalText.length()>0&&originalText[0]=='#')return std::string(originalText.substr(7));
return std::string(originalText);
}
std::u32string PixelGameEngine::stripLeadingCol(std::u32string_view originalText){
if(originalText.length()>0&&originalText[0]=='#')return std::u32string(originalText.substr(7));
return std::u32string(originalText);
}
std::string PixelGameEngine::stripCol(std::string_view originalText){ std::string PixelGameEngine::stripCol(std::string_view originalText){
std::string newStr{""}; std::string newStr{""};
for(int i=0;i<originalText.length();i++){ for(int i=0;i<originalText.length();i++){
@ -2149,6 +2167,15 @@ namespace olc
return newStr; return newStr;
} }
const Pixel PixelGameEngine::GetFinalRenderColor(const Pixel initialCol,std::string_view sourceStr){
if(sourceStr.length()>0&&initialCol==WHITE&&sourceStr[0]=='#')return Pixel(std::stoi(std::string(sourceStr.substr(1,6)),nullptr,16));
return initialCol;
}
const Pixel PixelGameEngine::GetFinalRenderColor(const Pixel initialCol,std::u32string_view sourceStr){
if(sourceStr.length()>0&&initialCol==WHITE&&sourceStr[0]=='#')return Pixel(std::stoi(std::string(sourceStr.substr(1,6).begin(),sourceStr.substr(1,6).end()),nullptr,16));
return initialCol;
}
bool PixelGameEngine::IsFocused() const bool PixelGameEngine::IsFocused() const
{ return bHasInputFocus; } { return bHasInputFocus; }
@ -3459,86 +3486,96 @@ namespace olc
void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling) void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling)
{ {
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSD"+std::string(stripCol(sText))}; std::string originalKey{stripCol(sText)};
std::string renderStr{stripLeadingCol(sText)};
std::string key{"DSD"+std::string(originalKey)};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=GetWrappedTextSize(sText,width,scale); if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=GetWrappedTextSize(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){ 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.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=garbageCollector[key].decal; newDecal=garbageCollector[key].decal;
} }
garbageCollector[key].originalStr=sText; garbageCollector[key].originalStr=originalKey;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawString({0,0},sText,WHITE,1U,width/scale.x); DrawString({0,0},renderStr,WHITE,1U,width/scale.x);
SetDrawTarget(nullptr); SetDrawTarget(nullptr);
newDecal->Update(); newDecal->Update();
} }
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale,col); DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling) void PixelGameEngine::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling)
{ {
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSPD_"+std::string(stripCol(sText))}; std::string originalKey{stripCol(sText)};
std::string renderStr{stripLeadingCol(sText)};
std::string key{"DSPD_"+std::string(originalKey)};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale); if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=GetWrappedTextSizeProp(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){ 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.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=garbageCollector[key].decal; newDecal=garbageCollector[key].decal;
} }
garbageCollector[key].originalStr=sText; garbageCollector[key].originalStr=originalKey;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x);
SetDrawTarget(nullptr); SetDrawTarget(nullptr);
newDecal->Update(); newDecal->Update();
} }
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale,col); DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){ void PixelGameEngine::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSSD_"+std::string(stripCol(sText))}; std::string originalKey{stripCol(sText)};
std::string renderStr{stripLeadingCol(sText)};
std::string key{"DSSD_"+std::string(originalKey)};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=GetWrappedTextSize(sText,width,scale); const bool ShadowRerenderRequired=garbageCollector.count(key+"_SHADOW")&&garbageCollector[key+"_SHADOW"].originalStr!=renderStr;
if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=GetWrappedTextSize(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){ 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.x/scale.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=garbageCollector[key].decal; newDecal=garbageCollector[key].decal;
} }
garbageCollector[key].originalStr=sText; garbageCollector[key].originalStr=originalKey;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawString({0,0},sText,WHITE,1U,width/scale.x); DrawString({0,0},renderStr,WHITE,1U,width/scale.x);
newDecal->Update(); newDecal->Update();
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr; Decal*newShadowDecal=nullptr;
if(!garbageCollector.count(key+"_SHADOW")){ 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.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
garbageCollector[key+"_SHADOW"].decal=newShadowDecal; garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{ }else{
@ -3549,7 +3586,7 @@ namespace olc
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawString(vf2d{x,y}+adjustedShadowSizeFactor, sText, WHITE,4U,width/scale.x*4); DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
} }
@ -3559,34 +3596,36 @@ namespace olc
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f; garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale,col); DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){ void PixelGameEngine::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){
if(sText.length()==0)return; if(sText.length()==0)return;
std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+stripCol(sText); std::u32string originalKey{stripCol(sText)};
std::u32string renderStr{stripLeadingCol(sText)};
std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end()); std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(garbageCollector.count(key)){ if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete garbageCollector[key].decal; delete garbageCollector[key].decal;
} garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale/4,col); DrawDecal(pos,garbageCollector[key].decal,scale/4,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){ void PixelGameEngine::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){
if(sText.length()==0)return; if(sText.length()==0)return;
std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+stripCol(sText); std::u32string originalKey{stripCol(sText)};
std::u32string renderStr{stripLeadingCol(sText)};
std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end()); std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(garbageCollector.count(key)){ if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete garbageCollector[key].decal; delete garbageCollector[key].decal;
} garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
@ -3596,52 +3635,57 @@ namespace olc
} }
} }
} }
DrawDecal(pos,garbageCollector[key].decal,scale/4,col); DrawDecal(pos,garbageCollector[key].decal,scale/4,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ void PixelGameEngine::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
if(sText.length()==0)return; if(sText.length()==0)return;
std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+stripCol(sText); std::u32string originalKey{stripCol(sText)};
std::u32string renderStr{stripLeadingCol(sText)};
std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+originalKey;
std::string key=std::string(Ukey.begin(),Ukey.end()); std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=std::string(sText.begin(),sText.end())){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=std::string(renderStr.begin(),renderStr.end());
if(garbageCollector.count(key)){ if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete garbageCollector[key].decal; delete garbageCollector[key].decal;
} garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end());
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos+vf2d{0,0.5f},garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0,0.5f},garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0},garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0.5f,0},garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0.5f},garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0.5f,0.5f},garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale/4,col); DrawDecal(pos,garbageCollector[key].decal,scale/4,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){ void PixelGameEngine::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor,const bool disableDynamicScaling){
if(sText.length()==0)return; if(sText.length()==0)return;
std::string key{"DSSPD_"+std::string(stripCol(sText))}; std::string originalKey{stripCol(sText)};
std::string renderStr{stripLeadingCol(sText)};
std::string key{"DSSPD_"+originalKey};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)||garbageCollector[key].originalStr!=sText){ //If the text key already exists, don't have to recreate the decal, just update the expire time. const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr;
vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale); const bool ShadowRerenderRequired=garbageCollector.count(key+"_SHADOW")&&garbageCollector[key+"_SHADOW"].originalStr!=renderStr;
if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=GetWrappedTextSizeProp(originalKey,width,scale);
if(imageSize.x<1||imageSize.y<1)return; if(imageSize.x<1||imageSize.y<1)return;
Decal*newDecal=nullptr; Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){ 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.x/scale.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{ }else{
newDecal=garbageCollector[key].decal; newDecal=garbageCollector[key].decal;
} }
garbageCollector[key].originalStr=sText; garbageCollector[key].originalStr=originalKey;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x);
newDecal->Update(); newDecal->Update();
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale;
Decal*newShadowDecal=nullptr; Decal*newShadowDecal=nullptr;
if(!garbageCollector.count(key+"_SHADOW")){ 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.x/scale.x*4)+adjustedShadowSizeFactor.y*2));
garbageCollector[key+"_SHADOW"].decal=newShadowDecal; garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{ }else{
@ -3652,7 +3696,7 @@ namespace olc
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor, sText, WHITE,4U,width/scale.x*4); DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
} }
@ -3662,7 +3706,7 @@ namespace olc
garbageCollector[key].expireTime=GetRunTime()+120.0f; garbageCollector[key].expireTime=GetRunTime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f; garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale,col); DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText));
} }
void PixelGameEngine::DrawShadowString(const olc::vi2d& pos, std::string_view sText, Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor){ void PixelGameEngine::DrawShadowString(const olc::vi2d& pos, std::string_view sText, Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float width,const float shadowSizeFactor){
@ -4423,7 +4467,7 @@ namespace olc
if (GetKey(olc::Key::DEL).bPressed && nTextEntryCursor < sTextEntryString.size()) if (GetKey(olc::Key::DEL).bPressed && nTextEntryCursor < sTextEntryString.size())
sTextEntryString.erase(nTextEntryCursor, 1); sTextEntryString.erase(nTextEntryCursor, 1);
/*
if (GetKey(olc::Key::UP).bPressed) if (GetKey(olc::Key::UP).bPressed)
{ {
if (!sCommandHistory.empty()) if (!sCommandHistory.empty())
@ -4474,7 +4518,7 @@ namespace olc
OnTextEntryComplete(sTextEntryString); OnTextEntryComplete(sTextEntryString);
TextEntryEnable(false); TextEntryEnable(false);
} }
}*/ }
} }
// User must override these functions as required. I have not made // User must override these functions as required. I have not made
@ -4726,12 +4770,13 @@ namespace olc
// Our time per frame coefficient // Our time per frame coefficient
float actualElapsedTime = elapsedTime.count(); float actualElapsedTime = elapsedTime.count();
float fElapsedTime = std::clamp(elapsedTime.count(),0.f,1/30.f); //HACK fix. We can't have a negative time. Although using a more precise system clock should make this never occur. Also make sure if the game is too slow we advance by only 1/30th of a second. float fElapsedTime = std::clamp(elapsedTime.count(),0.f,1/30.f); //HACK fix. We can't have a negative time. Although using a more precise system clock should make this never occur. Also make sure if the game is too slow we advance by only 1/30th of a second.
fLastElapsed = fElapsedTime;
dRunTime += fElapsedTime;
if (bConsoleSuspendTime) if (bConsoleSuspendTime)
fElapsedTime = 0.0f; fElapsedTime = 0.0f;
fLastElapsed = fElapsedTime;
dRunTime += fElapsedTime;
// Some platforms will need to check for events // Some platforms will need to check for events
platform->HandleSystemEvent(); platform->HandleSystemEvent();
@ -4944,6 +4989,32 @@ namespace olc
// {olc::Key::TAB, "\t", "\t"} // {olc::Key::TAB, "\t", "\t"}
}; };
#endif #endif
#ifdef OLC_KEYBOARD_US
vKeyboardMap =
{
{olc::Key::A, "a", "A"}, {olc::Key::B, "b", "B"}, {olc::Key::C, "c", "C"}, {olc::Key::D, "d", "D"}, {olc::Key::E, "e", "E"},
{olc::Key::F, "f", "F"}, {olc::Key::G, "g", "G"}, {olc::Key::H, "h", "H"}, {olc::Key::I, "i", "I"}, {olc::Key::J, "j", "J"},
{olc::Key::K, "k", "K"}, {olc::Key::L, "l", "L"}, {olc::Key::M, "m", "M"}, {olc::Key::N, "n", "N"}, {olc::Key::O, "o", "O"},
{olc::Key::P, "p", "P"}, {olc::Key::Q, "q", "Q"}, {olc::Key::R, "r", "R"}, {olc::Key::S, "s", "S"}, {olc::Key::T, "t", "T"},
{olc::Key::U, "u", "U"}, {olc::Key::V, "v", "V"}, {olc::Key::W, "w", "W"}, {olc::Key::X, "x", "X"}, {olc::Key::Y, "y", "Y"},
{olc::Key::Z, "z", "Z"},
{olc::Key::K0, "0", ")"}, {olc::Key::K1, "1", "!"}, {olc::Key::K2, "2", "@"}, {olc::Key::K3, "3", "#"}, {olc::Key::K4, "4", "$"},
{olc::Key::K5, "5", "%"}, {olc::Key::K6, "6", "^"}, {olc::Key::K7, "7", "&"}, {olc::Key::K8, "8", "*"}, {olc::Key::K9, "9", "("},
{olc::Key::NP0, "0", "0"}, {olc::Key::NP1, "1", "1"}, {olc::Key::NP2, "2", "2"}, {olc::Key::NP3, "3", "3"}, {olc::Key::NP4, "4", "4"},
{olc::Key::NP5, "5", "5"}, {olc::Key::NP6, "6", "6"}, {olc::Key::NP7, "7", "7"}, {olc::Key::NP8, "8", "8"}, {olc::Key::NP9, "9", "9"},
{olc::Key::NP_MUL, "*", "*"}, {olc::Key::NP_DIV, "/", "/"}, {olc::Key::NP_ADD, "+", "+"}, {olc::Key::NP_SUB, "-", "-"}, {olc::Key::NP_DECIMAL, ".", "."},
{olc::Key::PERIOD, ".", ">"}, {olc::Key::EQUALS, "=", "+"}, {olc::Key::COMMA, ",", "<"}, {olc::Key::MINUS, "-", "_"}, {olc::Key::SPACE, " ", " "},
{olc::Key::OEM_1, ";", ":"}, {olc::Key::OEM_2, "/", "?"}, {olc::Key::OEM_3, "`", "~"}, {olc::Key::OEM_4, "[", "{"},
{olc::Key::OEM_5, "\\", "|"}, {olc::Key::OEM_6, "]", "}"}, {olc::Key::OEM_7, "\'", "\""},
// {olc::Key::TAB, "\t", "\t"}
};
#endif
} }
void PixelGameEngine::pgex_Register(olc::PGEX* pgex) void PixelGameEngine::pgex_Register(olc::PGEX* pgex)

@ -43,6 +43,7 @@ All rights reserved.
#include "olcPGEX_ViewPort.h" #include "olcPGEX_ViewPort.h"
#include "olcUTIL_Geometry2D.h" #include "olcUTIL_Geometry2D.h"
#define OLC_PGE_APPLICATION #define OLC_PGE_APPLICATION
#define OLC_KEYBOARD_US
#include "olcPixelGameEngine.h" #include "olcPixelGameEngine.h"
#define OLC_PGEX_TRANSFORMEDVIEW #define OLC_PGEX_TRANSFORMEDVIEW
#include "olcPGEX_TransformedView.h" #include "olcPGEX_TransformedView.h"

@ -76,14 +76,6 @@ float util::lerp(float n1,float n2,double t){
return float(n1*(1-t)+n2*t); return float(n1*(1-t)+n2*t);
} }
std::string util::PixelToHTMLColorCode(const Pixel&col){
std::string hexCode=std::format("#{:2x}{:2x}{:2x}",col.r,col.g,col.b);
for(char&c:hexCode){
c=toupper(c);
}
return hexCode;
}
std::string util::timerStr(float time){ std::string util::timerStr(float time){
int seconds=int(time); int seconds=int(time);
int hours=seconds/3600; int hours=seconds/3600;

@ -55,7 +55,6 @@ namespace olc::util{
float degToRad(float deg); float degToRad(float deg);
float radToDeg(float rad); float radToDeg(float rad);
float lerp(float n1,float n2,double t); float lerp(float n1,float n2,double t);
std::string PixelToHTMLColorCode(const Pixel&col);
std::string timerStr(float time); std::string timerStr(float time);
std::string WrapText(PixelGameEngine*pge,std::string str,int width,bool proportional,vd2d scale); std::string WrapText(PixelGameEngine*pge,std::string str,int width,bool proportional,vd2d scale);
std::u32string WrapText(PixelGameEngine*pge,std::u32string str,int width,Font&font,vd2d scale); std::u32string WrapText(PixelGameEngine*pge,std::u32string str,int width,Font&font,vd2d scale);

Loading…
Cancel
Save