Fix crash when hovering over lock/unlock buttons in the accessories menu on the Merchant menu. Fix size of icons in merchant menus. Enable/Disable increase/decrease buttons on shermans's consumable crafting menu as appropriate. Labels in merchant window and inventory window properly update their item descriptions with flashing / changing colors as needed. Text rendering system no longer eats away at memory for text strings that are equivalent but have different HTML color codes. Release Build 8066.

mac-build
sigonasr2 9 months ago
parent cfdc2c9a8c
commit f588de19d0
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 7
      Adventures in Lestoria/AdventuresInLestoria.cpp
  4. 2
      Adventures in Lestoria/BuyItemWindow.cpp
  5. 14
      Adventures in Lestoria/ConsumableCraftItemWindow.cpp
  6. 1
      Adventures in Lestoria/ConsumableCraftingWindow.cpp
  7. 12
      Adventures in Lestoria/EquipSlotButton.h
  8. 6
      Adventures in Lestoria/InventoryWindow.cpp
  9. 2
      Adventures in Lestoria/MenuItemItemButton.h
  10. 75
      Adventures in Lestoria/MenuItemLabel.h
  11. 14
      Adventures in Lestoria/MerchantWindow.cpp
  12. 3
      Adventures in Lestoria/PauseMenu.cpp
  13. 4
      Adventures in Lestoria/TODO.txt
  14. 2
      Adventures in Lestoria/Version.h
  15. 230
      Adventures in Lestoria/olcPGEX_ViewPort.h
  16. 231
      Adventures in Lestoria/olcPixelGameEngine.h
  17. BIN
      x64/Release/Adventures in Lestoria.exe

@ -397,6 +397,10 @@
</SubType> </SubType>
</ClInclude> </ClInclude>
<ClInclude Include="MenuDefinitions.h" /> <ClInclude Include="MenuDefinitions.h" />
<ClInclude Include="MenuItemLabel.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="MenuType.h"> <ClInclude Include="MenuType.h">
<SubType> <SubType>
</SubType> </SubType>

@ -480,6 +480,9 @@
<ClInclude Include="AccessoryRowItemDisplay.h"> <ClInclude Include="AccessoryRowItemDisplay.h">
<Filter>Header Files\Interface</Filter> <Filter>Header Files\Interface</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="MenuItemLabel.h">
<Filter>Header Files\Interface</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="Player.cpp"> <ClCompile Include="Player.cpp">

@ -341,6 +341,8 @@ bool AiL::OnUserUpdate(float fElapsedTime){
}else lastMouseMovement+=fElapsedTime; }else lastMouseMovement+=fElapsedTime;
if(!GamePaused()){ if(!GamePaused()){
GameState::STATE->OnUserUpdate(this); GameState::STATE->OnUserUpdate(this);
}else{
ClearTimedOutGarbage();
} }
LoadingScreen::Update(); LoadingScreen::Update();
InputListener::Update(); InputListener::Update();
@ -2361,6 +2363,11 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
Audio::UpdateBGMVolume(); Audio::UpdateBGMVolume();
return true; return true;
}); });
LoadingScreen::AddPhase([&](){
ClearGarbage();
return true;
});
} }
} }

@ -64,7 +64,7 @@ void Menu::InitializeBuyItemWindow(){
if(qty==99||pricePerItem*(qty+1)>game->GetPlayer()->GetMoney()){ if(qty==99||pricePerItem*(qty+1)>game->GetPlayer()->GetMoney()){
Component<MenuComponent>(BUY_ITEM,"Increase buy amount Button")->SetGrayedOut(true); Component<MenuComponent>(BUY_ITEM,"Increase buy amount Button")->SetGrayedOut(true);
Menu::menus[BUY_ITEM]->SetSelection(static_cast<std::weak_ptr<MenuComponent>>(Component<MenuComponent>(BUY_ITEM,"Decrease buy amount Button"))); Menu::menus[BUY_ITEM]->SetSelection("Decrease buy amount Button"sv);
}else{ }else{
Component<MenuComponent>(BUY_ITEM,"Increase buy amount Button")->SetGrayedOut(false); Component<MenuComponent>(BUY_ITEM,"Increase buy amount Button")->SetGrayedOut(false);
} }

@ -59,9 +59,23 @@ void Menu::InitializeConsumableCraftItemWindow(){
const std::string&item=Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->GetString(A::ITEM_NAME); const std::string&item=Component<MenuLabel>(CONSUMABLE_CRAFT_ITEM,"Item Name Header")->GetString(A::ITEM_NAME);
bool canCraft=Item(qty,item).CanEnhanceItem(qty); bool canCraft=Item(qty,item).CanEnhanceItem(qty);
bool canCraftOneHigher=Item(qty+1,item).CanEnhanceItem(qty+1);
std::string colorCode=""; std::string colorCode="";
if(!canCraft)colorCode="#FF0000"; if(!canCraft)colorCode="#FF0000";
if(qty==99||!canCraftOneHigher){
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Increase Craft amount Button")->SetGrayedOut(true);
Menu::menus[CONSUMABLE_CRAFT_ITEM]->SetSelection("Decrease Craft amount Button"sv);
}else{
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Increase Craft amount Button")->SetGrayedOut(false);
}
if(qty<=1){
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Decrease Craft amount Button")->SetGrayedOut(true);
Menu::menus[CONSUMABLE_CRAFT_ITEM]->SetSelection("Increase Craft amount Button"sv);
}else{
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Decrease Craft amount Button")->SetGrayedOut(false);
}
Component<RequiredMaterialsList>(CONSUMABLE_CRAFT_ITEM,"Required Materials List")->SetQuantity(qty); Component<RequiredMaterialsList>(CONSUMABLE_CRAFT_ITEM,"Required Materials List")->SetQuantity(qty);
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Craft Button")->SetGrayedOut(!canCraft); Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Craft Button")->SetGrayedOut(!canCraft);
}; };

@ -69,6 +69,7 @@ void Menu::InitializeConsumableCraftingWindow(){
Component<RequiredMaterialsList>(CONSUMABLE_CRAFT_ITEM,"Required Materials List")->SetItem(item); Component<RequiredMaterialsList>(CONSUMABLE_CRAFT_ITEM,"Required Materials List")->SetItem(item);
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Craft Button")->SetGrayedOut(!item.lock()->CanEnhanceItem()); Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Craft Button")->SetGrayedOut(!item.lock()->CanEnhanceItem());
if(item.lock()->GetEnhancementInfo()[0].chapterAvailable<=game->GetCurrentChapter()){ if(item.lock()->GetEnhancementInfo()[0].chapterAvailable<=game->GetCurrentChapter()){
Component<MenuComponent>(CONSUMABLE_CRAFT_ITEM,"Decrease Craft amount Button")->SetGrayedOut(true);
Menu::OpenMenu(CONSUMABLE_CRAFT_ITEM); Menu::OpenMenu(CONSUMABLE_CRAFT_ITEM);
}else{ }else{
SoundEffect::PlaySFX("Locked Item",SoundEffect::CENTERED); SoundEffect::PlaySFX("Locked Item",SoundEffect::CENTERED);

@ -59,6 +59,18 @@ public:
itemRef=Item::BLANK; itemRef=Item::BLANK;
} }
} }
virtual inline void Update(AiL*game)override{
MenuIconButton::Update(game);
valid=!ISBLANK(itemRef);
if(!valid){
icon=nullptr;
}
if(hovered){
UpdateLabel();
}
}
inline const EquipSlot GetSlot()const{ inline const EquipSlot GetSlot()const{
return slot; return slot;
} }

@ -206,7 +206,8 @@ void Menu::InitializeInventoryWindow(){
{game->KEY_CONFIRM,{[](MenuFuncData data){ {game->KEY_CONFIRM,{[](MenuFuncData data){
if(!data.menu.GetSelection().expired()&& if(!data.menu.GetSelection().expired()&&
!data.menu.GetSelection().lock()->parentComponent.expired()&& !data.menu.GetSelection().lock()->parentComponent.expired()&&
data.menu.GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"){ data.menu.GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"
&&data.component.lock()->GetSubcomponentParent().expired()){
if(DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.menu.GetSelection().lock())->GetItem().lock()->IsLocked()){ if(DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.menu.GetSelection().lock())->GetItem().lock()->IsLocked()){
return "Unlock"; return "Unlock";
}else{ }else{
@ -225,7 +226,8 @@ void Menu::InitializeInventoryWindow(){
{game->KEY_SELECT,{[](MenuFuncData data){ {game->KEY_SELECT,{[](MenuFuncData data){
if(!data.menu.GetSelection().expired()&& if(!data.menu.GetSelection().expired()&&
!data.menu.GetSelection().lock()->parentComponent.expired()&& !data.menu.GetSelection().lock()->parentComponent.expired()&&
data.menu.GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"){ data.menu.GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"
&&data.component.lock()->GetSubcomponentParent().expired()){
if(DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.menu.GetSelection().lock())->GetItem().lock()->IsLocked()){ if(DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.menu.GetSelection().lock())->GetItem().lock()->IsLocked()){
return "Unlock"; return "Unlock";
}else{ }else{

@ -149,10 +149,8 @@ protected:
if(!valid){ if(!valid){
icon=nullptr; icon=nullptr;
} }
if(hovered){
UpdateLabel(); UpdateLabel();
} }
}
virtual inline void DrawDecal(ViewPort&window,bool focused)override{ virtual inline void DrawDecal(ViewPort&window,bool focused)override{
MenuIconButton::DrawDecal(window,focused); MenuIconButton::DrawDecal(window,focused);
if(valid&&!hideQty){ if(valid&&!hideQty){

@ -0,0 +1,75 @@
#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
#pragma once
#include "MenuLabel.h"
#include "Item.h"
namespace ItemLabelDescriptionType{
enum ItemDescriptionType{
ITEM_DESCRIPTION,
ITEM_NAME
};
};
using namespace ItemLabelDescriptionType;
class MenuItemLabel:public MenuLabel{
std::weak_ptr<Item>itemRef;
ItemDescriptionType labelType;
public:
inline MenuItemLabel(geom2d::rect<float>rect,std::weak_ptr<Item>itemRef,ItemDescriptionType labelType=ITEM_DESCRIPTION,float scale=1,ComponentAttr attributes=ComponentAttr::NONE)
:MenuLabel(rect,"",scale,attributes),itemRef(itemRef),labelType(labelType){}
inline virtual void Update(AiL*game)override{
MenuLabel::Update(game);
if(!itemRef.expired()){
switch(labelType){
case ITEM_DESCRIPTION:{
SetLabel(itemRef.lock()->Description(NON_COMPACT));
}break;
case ITEM_NAME:{
SetLabel(itemRef.lock()->DisplayName());
}break;
}
}
}
inline virtual void SetItem(std::weak_ptr<Item>itemRef){
this->itemRef=itemRef;
}
};

@ -109,6 +109,7 @@ void Menu::InitializeMerchantWindow(){
auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", auto inventoryDisplay=merchantWindow->ADD("Merchant Inventory Display",RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{2,28},{220,merchantWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
if(!data.component.lock()->GetSubcomponentParent().expired())return true; //We do not do anything if it's not the actual item.
std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock()); std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
int qty=1; int qty=1;
@ -138,6 +139,7 @@ void Menu::InitializeMerchantWindow(){
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
if(!data.component.lock()->GetSubcomponentParent().expired())return true; //We do not do anything if it's not the actual item.
Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem()); Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem());
data.menu.I(A::MERCHANT_ITEM_SLOT)=data.parentComponent.lock()->GetComponentIndex(data.component); data.menu.I(A::MERCHANT_ITEM_SLOT)=data.parentComponent.lock()->GetComponentIndex(data.component);
return true; return true;
@ -189,6 +191,7 @@ void Menu::InitializeMerchantWindow(){
auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{72,28},{150,merchantWindow->size.y-44}},"Item Name Label","Item Description Label", auto inventoryDisplay=merchantWindow->ADD("Inventory Display - "+category,RowInventoryScrollableWindowComponent)(geom2d::rect<float>{{72,28},{150,merchantWindow->size.y-44}},"Item Name Label","Item Description Label",
[](MenuFuncData data){ [](MenuFuncData data){
if(!data.component.lock()->GetSubcomponentParent().expired())return true; //We do not do anything if it's not the actual item.
std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock()); std::weak_ptr<RowItemDisplay>item=DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock());
if(item.lock()->GetItem().lock()->IsLocked()){ if(item.lock()->GetItem().lock()->IsLocked()){
SoundEffect::PlaySFX("Locked Item",SoundEffect::CENTERED); SoundEffect::PlaySFX("Locked Item",SoundEffect::CENTERED);
@ -221,6 +224,7 @@ void Menu::InitializeMerchantWindow(){
return true; return true;
}, },
[](MenuFuncData data){ [](MenuFuncData data){
if(!data.component.lock()->GetSubcomponentParent().expired())return true; //We do not do anything if it's not the actual item.
Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem()); Component<MenuItemItemButton>(data.menu.GetType(),"Item Icon")->SetItem(DYNAMIC_POINTER_CAST<RowItemDisplay>(data.component.lock())->GetItem());
data.menu.I(A::ITEM_SLOT)=data.parentComponent.lock()->GetComponentIndex(data.component); data.menu.I(A::ITEM_SLOT)=data.parentComponent.lock()->GetComponentIndex(data.component);
return true; return true;
@ -252,7 +256,8 @@ void Menu::InitializeMerchantWindow(){
#pragma region Inventory Description #pragma region Inventory Description
float inventoryDescriptionWidth=merchantWindow->pos.x+merchantWindow->size.x-26-224; float inventoryDescriptionWidth=merchantWindow->pos.x+merchantWindow->size.x-26-224;
merchantWindow->ADD("Item Description Outline",MenuLabel)(geom2d::rect<float>{{224,28},{inventoryDescriptionWidth,merchantWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END; merchantWindow->ADD("Item Description Outline",MenuLabel)(geom2d::rect<float>{{224,28},{inventoryDescriptionWidth,merchantWindow->size.y-44}},"",1,ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND)END;
merchantWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,DO_NOTHING,"","",IconButtonAttr::NOT_SELECTABLE)END; merchantWindow->ADD("Item Icon",MenuItemItemButton)(geom2d::rect<float>{{226+inventoryDescriptionWidth/2-24,30},{48,48}},Item::BLANK,DO_NOTHING,"Item Name Label","Item Description Label",IconButtonAttr::NOT_SELECTABLE)END
->SetIconScale({2.f,2.f});
merchantWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; merchantWindow->ADD("Item Name Label",MenuLabel)(geom2d::rect<float>{{226,84},{inventoryDescriptionWidth-6,12}},"",0.75f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
merchantWindow->ADD("Item Description Label",MenuLabel)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,merchantWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END; merchantWindow->ADD("Item Description Label",MenuLabel)(geom2d::rect<float>{{226,94},{inventoryDescriptionWidth-6,merchantWindow->size.y-44-66}},"",0.5f,ComponentAttr::LEFT_ALIGN|ComponentAttr::SHADOW)END;
#pragma endregion #pragma endregion
@ -358,7 +363,8 @@ void Menu::InitializeMerchantWindow(){
{game->KEY_SELECT,{[](MenuFuncData data){ {game->KEY_SELECT,{[](MenuFuncData data){
if(!data.menu.GetSelection().expired()&& if(!data.menu.GetSelection().expired()&&
!data.menu.GetSelection().lock()->parentComponent.expired()&& !data.menu.GetSelection().lock()->parentComponent.expired()&&
data.menu.GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"){ data.menu.GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"&&
data.menu.GetSelection().lock()->GetSubcomponentParent().expired()){
if(DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.menu.GetSelection().lock())->GetItem().lock()->IsLocked()){ if(DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(data.menu.GetSelection().lock())->GetItem().lock()->IsLocked()){
return "Unlock"; return "Unlock";
}else{ }else{
@ -369,8 +375,8 @@ void Menu::InitializeMerchantWindow(){
},[](MenuType type){ },[](MenuType type){
if(!Menu::menus[type]->GetSelection().expired()&& if(!Menu::menus[type]->GetSelection().expired()&&
!Menu::menus[type]->GetSelection().lock()->parentComponent.expired()&& !Menu::menus[type]->GetSelection().lock()->parentComponent.expired()&&
Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories" Menu::menus[type]->GetSelection().lock()->parentComponent.lock()->GetName()=="Inventory Display - Accessories"&&
&&Menu::menus[type]->GetSelection().lock()->GetSubcomponentParent().expired()){ Menu::menus[type]->GetSelection().lock()->GetSubcomponentParent().expired()){
DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(Menu::menus[type]->GetSelection().lock())->GetLockButton().lock()->Click(); DYNAMIC_POINTER_CAST<AccessoryRowItemDisplay>(Menu::menus[type]->GetSelection().lock())->GetLockButton().lock()->Click();
} }
}}}, }}},

@ -51,7 +51,8 @@ INCLUDE_GFX
void Menu::InitializePauseWindow(){ void Menu::InitializePauseWindow(){
Menu*pauseWindow=CreateMenu(MenuType::PAUSE,CENTERED,vi2d{96,140}); Menu*pauseWindow=CreateMenu(MenuType::PAUSE,CENTERED,vi2d{96,140});
pauseWindow->ADD("Resume Button",MenuComponent)(geom2d::rect<float>{{6.f,0.f},{84.f,24.f}},"Resume",[](MenuFuncData data){ pauseWindow->ADD("Resume Button",MenuComponent)(geom2d::rect<float>{{6.f,0.f},{84.f,24.f}},"Resume",[](MenuFuncData data){void ClearGarbage();
game->ClearGarbage();
Menu::CloseMenu(); Menu::CloseMenu();
return true; return true;
},ButtonAttr::FIT_TO_LABEL)END; },ButtonAttr::FIT_TO_LABEL)END;

@ -11,3 +11,7 @@ should gemstones dropp from boss stages aswell? (Maybe lower droprate?)
Funny text colors with text color override on blacksmith menu Funny text colors with text color override on blacksmith menu
Toggle for displaying error messages Toggle for displaying error messages
Fix size of icons in merchant menus.
Sherman's consumable crafting menu needs enabling/disabling of +/- buttons
Label doesn't constantly update in the merchant window.

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 5 #define VERSION_MINOR 5
#define VERSION_PATCH 1 #define VERSION_PATCH 1
#define VERSION_BUILD 8038 #define VERSION_BUILD 8066
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -628,116 +628,103 @@ 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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSD_"+std::string(pge->stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale); vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x)); Decal*newDecal=nullptr;
garbageCollector[key].decal=newDecal; if(!pge->garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].originalStr=sText;
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},sText,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr); pge->SetDrawTarget(nullptr);
newDecal->Update(); newDecal->Update();
} }
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){ DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
if(key.second.expireTime<pge->GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::u32string,DecalData>garbageCollector; std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+pge->stripCol(sText);
std::u32string key=font.GetFontName()+U"_"+sText; std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); if(pge->garbageCollector.count(key)){
delete pge->garbageCollector[key].decal;
} }
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
std::erase_if(garbageCollector,[&](auto&key){ pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
if(key.second.expireTime<pge->GetRuntime()){
delete key.second.decal;
return true;
} }
return false; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
}); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col);
DrawDecal(pos,garbageCollector[key].decal,scale/4,col);
} }
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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSPD"+std::string(pge->stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale); vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x)); Decal*newDecal=nullptr;
garbageCollector[key].decal=newDecal; if(!pge->garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].originalStr=sText;
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},sText,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr); pge->SetDrawTarget(nullptr);
newDecal->Update(); newDecal->Update();
} }
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){ DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
if(key.second.expireTime<pge->GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSSD_"+std::string(pge->stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale); vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x)); Decal*newDecal=nullptr;
garbageCollector[key].decal=newDecal; if(!pge->garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].originalStr=sText;
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},sText,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=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2)); Decal*newShadowDecal=nullptr;
garbageCollector[key+"_SHADOW"].decal=newShadowDecal; if(!pge->garbageCollector.count(key+"_SHADOW")){
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;
}else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal;
}
pge->SetDrawTarget(newShadowDecal->sprite); pge->SetDrawTarget(newShadowDecal->sprite);
pge->Clear(BLANK); pge->Clear(BLANK);
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){
@ -750,42 +737,41 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view
pge->SetDrawTarget(nullptr); pge->SetDrawTarget(nullptr);
newShadowDecal->Update(); newShadowDecal->Update();
} }
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){ DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
if(key.second.expireTime<pge->GetRuntime()){ DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSSPD"+std::string(pge->stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale); vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x)); Decal*newDecal=nullptr;
garbageCollector[key].decal=newDecal; if(!pge->garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].originalStr=sText;
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},sText,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=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2)); Decal*newShadowDecal=nullptr;
garbageCollector[key+"_SHADOW"].decal=newShadowDecal; if(!pge->garbageCollector.count(key+"_SHADOW")){
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;
}else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal;
}
pge->SetDrawTarget(newShadowDecal->sprite); pge->SetDrawTarget(newShadowDecal->sprite);
pge->Clear(BLANK); pge->Clear(BLANK);
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){
@ -798,32 +784,25 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
pge->SetDrawTarget(nullptr); pge->SetDrawTarget(nullptr);
newShadowDecal->Update(); newShadowDecal->Update();
} }
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){ DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
if(key.second.expireTime<pge->GetRuntime()){ DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::u32string,DecalData>garbageCollector; std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+pge->stripCol(sText);
std::u32string key=font.GetFontName()+U"_"+sText; std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); if(pge->garbageCollector.count(key)){
delete pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){ std::erase_if(pge->garbageCollector,[&](auto&key){
if(key.second.expireTime<pge->GetRuntime()){ if(key.second.expireTime<pge->GetRuntime()){
delete key.second.decal; delete key.second.decal;
return true; return true;
@ -833,36 +812,29 @@ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawDecal(pos+vf2d{x,y},garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{x,y},pge->garbageCollector[key].decal,scale/4,shadowCol);
} }
} }
} }
DrawDecal(pos,garbageCollector[key].decal,scale/4,col); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col);
} }
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){
struct DecalData{
Decal*decal;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::u32string,DecalData>garbageCollector; std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+pge->stripCol(sText);
std::u32string key=font.GetFontName()+U"_"+sText; std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); if(pge->garbageCollector.count(key)){
} delete pge->garbageCollector[key].decal;
garbageCollector[key].expireTime=pge->GetRuntime()+120.0f; }
std::erase_if(garbageCollector,[&](auto&key){ pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
if(key.second.expireTime<pge->GetRuntime()){ pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
delete key.second.decal; }
return true; pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
} DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol);
return false; 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,0.5f},garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col);
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,garbageCollector[key].decal,scale/4,col);
} }
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{

@ -981,6 +981,7 @@ namespace olc
// O------------------------------------------------------------------------------O // O------------------------------------------------------------------------------O
class PixelGameEngine class PixelGameEngine
{ {
friend class ViewPort;
struct StringDecalData{ struct StringDecalData{
char c; char c;
vf2d sourcePos; vf2d sourcePos;
@ -1021,6 +1022,9 @@ namespace olc
public: // Hardware Interfaces public: // Hardware Interfaces
void ClearGarbage();
void ClearTimedOutGarbage();
// Returns true if window is currently in focus // Returns true if window is currently in focus
bool IsFocused() const; bool IsFocused() const;
// Get the state of a specific keyboard button // Get the state of a specific keyboard button
@ -1349,6 +1353,13 @@ namespace olc
olc::vi2d vDroppedFilesPointCache; olc::vi2d vDroppedFilesPointCache;
uint8_t mosaic=1; uint8_t mosaic=1;
uint8_t nTextEntryCharLimit=std::numeric_limits<uint8_t>::max(); uint8_t nTextEntryCharLimit=std::numeric_limits<uint8_t>::max();
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
std::string originalStr;
};
std::unordered_map<std::string,DecalData>garbageCollector;
bool requestClear=false;
// Command Console Specific // Command Console Specific
bool bConsoleShow = false; bool bConsoleShow = false;
@ -1363,6 +1374,9 @@ 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 = "";
@ -2240,6 +2254,42 @@ namespace olc
uint32_t PixelGameEngine::GetFPS() const uint32_t PixelGameEngine::GetFPS() const
{ return nLastFPS; } { return nLastFPS; }
void PixelGameEngine::ClearGarbage(){
requestClear=true;
}
void PixelGameEngine::ClearTimedOutGarbage(){
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
}
std::string PixelGameEngine::stripCol(std::string_view originalText){
std::string newStr{""};
for(int i=0;i<originalText.length();i++){
if(originalText[i]=='#'){
i+=6;
continue;
}
newStr+=originalText[i];
}
return newStr;
}
std::u32string PixelGameEngine::stripCol(std::u32string_view originalText){
std::u32string newStr{U""};
for(int i=0;i<originalText.length();i++){
if(originalText[i]=='#'){
i+=6;
continue;
}
newStr+=originalText[i];
}
return newStr;
}
bool PixelGameEngine::IsFocused() const bool PixelGameEngine::IsFocused() const
{ return bHasInputFocus; } { return bHasInputFocus; }
@ -3437,21 +3487,22 @@ 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)
{ {
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSD"+std::string(stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=GetWrappedTextSize(sText,width,scale); vi2d imageSize=GetWrappedTextSize(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x)); Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
}
garbageCollector[key].originalStr=sText;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawString({0,0},sText,WHITE,1U,width/scale.x); DrawString({0,0},sText,WHITE,1U,width/scale.x);
@ -3459,33 +3510,27 @@ namespace olc
newDecal->Update(); newDecal->Update();
} }
garbageCollector[key].expireTime=GetRuntime()+120.0f; garbageCollector[key].expireTime=GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col); DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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)
{ {
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSPD_"+std::string(stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale); vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x)); Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
}
garbageCollector[key].originalStr=sText;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); DrawStringProp({0,0},sText,WHITE,1U,width/scale.x);
@ -3493,39 +3538,38 @@ namespace olc
newDecal->Update(); newDecal->Update();
} }
garbageCollector[key].expireTime=GetRuntime()+120.0f; garbageCollector[key].expireTime=GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col); DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSSD_"+std::string(stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=GetWrappedTextSize(sText,width,scale); vi2d imageSize=GetWrappedTextSize(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x)); Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
}
garbageCollector[key].originalStr=sText;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawString({0,0},sText,WHITE,1U,width/scale.x); DrawString({0,0},sText,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=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2)); Decal*newShadowDecal=nullptr;
if(!garbageCollector.count(key+"_SHADOW")){
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{
newShadowDecal=garbageCollector[key+"_SHADOW"].decal;
}
SetDrawTarget(newShadowDecal->sprite); SetDrawTarget(newShadowDecal->sprite);
Clear(BLANK); Clear(BLANK);
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){
@ -3540,58 +3584,37 @@ 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;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
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,col);
} }
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){
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::u32string,DecalData>garbageCollector; std::u32string Ukey=U"DSD_"+font.GetFontName()+U"_"+stripCol(sText);
std::u32string key=font.GetFontName()+U"_"+sText; std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
if(garbageCollector.count(key)){
delete garbageCollector[key].decal;
}
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=GetRuntime()+120.0f; garbageCollector[key].expireTime=GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale/4,col); DrawDecal(pos,garbageCollector[key].decal,scale/4,col);
} }
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){
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::u32string,DecalData>garbageCollector; std::u32string Ukey=U"DSSD_"+font.GetFontName()+U"_"+stripCol(sText);
std::u32string key=font.GetFontName()+U"_"+sText; std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
if(garbageCollector.count(key)){
delete garbageCollector[key].decal;
}
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=GetRuntime()+120.0f; garbageCollector[key].expireTime=GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
@ -3603,24 +3626,17 @@ namespace olc
} }
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){
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::u32string,DecalData>garbageCollector; std::u32string Ukey=U"DDSSD_"+font.GetFontName()+U"_"+stripCol(sText);
std::u32string key=font.GetFontName()+U"_"+sText; std::string key=std::string(Ukey.begin(),Ukey.end());
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
if(garbageCollector.count(key)){
delete garbageCollector[key].decal;
}
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE); garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
} }
garbageCollector[key].expireTime=GetRuntime()+120.0f; garbageCollector[key].expireTime=GetRuntime()+120.0f;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
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);
@ -3628,28 +3644,34 @@ namespace olc
} }
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){
struct DecalData{
Decal*decal=nullptr;
float expireTime=0.0f;
};
if(sText.length()==0)return; if(sText.length()==0)return;
static std::map<std::string,DecalData>garbageCollector; std::string key{"DSSPD_"+std::string(stripCol(sText))};
std::string key{sText};
key+=std::to_string(width); key+=std::to_string(width);
if(!disableDynamicScaling){ if(!disableDynamicScaling){
key+=scale.str(); key+=scale.str();
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time. 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.
vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale); vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.x/scale.x)); Decal*newDecal=nullptr;
if(!garbageCollector.count(key)){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal; garbageCollector[key].decal=newDecal;
}else{
newDecal=garbageCollector[key].decal;
}
garbageCollector[key].originalStr=sText;
SetDrawTarget(newDecal->sprite); SetDrawTarget(newDecal->sprite);
Clear(BLANK); Clear(BLANK);
DrawStringProp({0,0},sText,WHITE,1U,width/scale.x); DrawStringProp({0,0},sText,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=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.x/scale.x*4)+adjustedShadowSizeFactor.y*2)); Decal*newShadowDecal=nullptr;
if(!garbageCollector.count(key+"_SHADOW")){
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{
newShadowDecal=garbageCollector[key+"_SHADOW"].decal;
}
SetDrawTarget(newShadowDecal->sprite); SetDrawTarget(newShadowDecal->sprite);
Clear(BLANK); Clear(BLANK);
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){
@ -3664,13 +3686,6 @@ 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;
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
}
return false;
});
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,col);
} }
@ -4827,7 +4842,13 @@ namespace olc
} }
} }
if(requestClear||garbageCollector.size()>5000){
std::for_each(garbageCollector.begin(),garbageCollector.end(),[&](auto&key){
delete key.second.decal;
});
garbageCollector.clear();
requestClear=false;
}
// Present Graphics to screen // Present Graphics to screen
renderer->DisplayFrame(); renderer->DisplayFrame();

Loading…
Cancel
Save