diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 293f1191..57752ca7 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -1994,7 +1994,7 @@ void AiL::RenderHud(){ std::stringstream castTimeDisplay; castTimeDisplay<::max(),2.f); + DrawShadowStringPropDecal(vf2d{ScreenWidth()/2.f-GetTextSizeProp(castText).x*2/2,ScreenHeight()-64.f},castText,WHITE,BLACK,{2,3},{1.8f,2.6f},std::numeric_limits::max()); }; if(GetPlayer()->GetCastInfo().castTimer>0){ @@ -2020,8 +2020,8 @@ void AiL::RenderHud(){ std::string text=player->GetHealth()>0?std::to_string(healthCounter.GetDisplayValue()):"X"; std::string text_mana=std::to_string(manaCounter.GetDisplayValue()); - DrawShadowStringPropDecal({20,3},text,healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},INFINITE); - DrawShadowStringPropDecal({24,23},text_mana,manaCounter.GetDisplayColor(),BLACK,{1.5f,1.5f},INFINITE); + DrawShadowStringPropDecal({20,3},text,healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},{1.8f,1.8f},INFINITE); + DrawShadowStringPropDecal({24,23},text_mana,manaCounter.GetDisplayColor(),BLACK,{1.5f,1.5f},{1.45f,1.45f},INFINITE); #pragma region Show Max Health/Max Mana if(GameSettings::ShowMaxHealth()){ @@ -2029,14 +2029,14 @@ void AiL::RenderHud(){ std::string maxHealthText="/"+std::to_string(int(player->GetMaxHealth())); float maxHealthTextHeight=GetTextSizeProp(maxHealthText).y; - DrawShadowStringPropDecal(vf2d{20,3}+healthTextSize+vf2d{1.f,-maxHealthTextHeight},maxHealthText,{200,200,200,255},healthOutlineCol,{1.f,1.f},INFINITE); + DrawShadowStringPropDecal(vf2d{20,3}+healthTextSize+vf2d{1.f,-maxHealthTextHeight},maxHealthText,{200,200,200,255},healthOutlineCol,{1.f,1.f},{1.f,1.f},INFINITE); } if(GameSettings::ShowMaxMana()){ vf2d manaTextSize=GetTextSizeProp(text_mana)*vf2d{1.5f,1.5f}; std::string maxManaText="/"+std::to_string(player->GetMaxMana()); float maxManaTextHeight=GetTextSizeProp(maxManaText).y; - DrawShadowStringPropDecal(vf2d{24,23}+manaTextSize+vf2d{1.f,-maxManaTextHeight},maxManaText,{200,200,255,255},BLACK,{1.f,1.f},INFINITE); + DrawShadowStringPropDecal(vf2d{24,23}+manaTextSize+vf2d{1.f,-maxManaTextHeight},maxManaText,{200,200,255,255},BLACK,{1.f,1.f},{1.f,1.f},INFINITE); } #pragma endregion @@ -2108,7 +2108,7 @@ void AiL::RenderCooldowns(){ } std::stringstream cooldownTimeDisplay; cooldownTimeDisplay<0){ std::string amtString="x"+std::to_string(itemAmt); vf2d qtySize=vf2d{GetTextSize(amtString)}*vf2d{0.5f,0.75f}; - DrawShadowStringDecal(pos+vf2d{20,20}-qtySize/2,amtString,WHITE,BLACK,{0.5f,0.75f}); + DrawShadowStringDecal(pos+vf2d{20,20}-qtySize/2,amtString,WHITE,BLACK,{0.5f,0.75f},{0.5f,0.75f}); }else{ DrawDecal(pos,GFX["square_skill_overlay_icon_empty.png"].Decal(),{1,1},DARK_RED); shortNameCol=RED; @@ -2160,11 +2160,11 @@ void AiL::RenderCooldowns(){ if(a.manaCost>0){ vf2d manaCostSize=vf2d{GetTextSize(std::to_string(a.manaCost))}*vf2d{0.5f,0.75f}; - DrawShadowStringDecal(pos+vf2d{20,4}-manaCostSize/2,std::to_string(a.manaCost),{192,192,255},manaCostShadowCol,{0.5f,0.75f}); + DrawShadowStringDecal(pos+vf2d{20,4}-manaCostSize/2,std::to_string(a.manaCost),{192,192,255},manaCostShadowCol,{0.5f,0.75f},{0.5f,0.75f}); } vf2d shortNameSize=vf2d{GetTextSize(a.shortName)}*vf2d{0.5f,0.75f}; - DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f}); + DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f},{0.5f,0.75f}); } }; @@ -3594,7 +3594,7 @@ void AiL::DisplayBossEncounterInfo(){ } vf2d textScale={3,5}; textScale.x=std::min(3.f,float(ScreenWidth())/GetTextSizeProp(displayText).x); - DrawShadowStringPropDecal(vf2d{float(ScreenWidth()/2),float(ScreenHeight()/2)}-vf2d{GetTextSizeProp(displayText)}*textScale/2,displayText,{252, 186, 3, alpha},{128,0,0,alpha},textScale,std::numeric_limits::max(),2); + DrawShadowStringPropDecal(vf2d{float(ScreenWidth()/2),float(ScreenHeight()/2)}-vf2d{GetTextSizeProp(displayText)}*textScale/2,displayText,{252, 186, 3, alpha},{128,0,0,alpha},textScale,textScale*0.9f,std::numeric_limits::max()); } if(InBossEncounter()){ Pixel displayCol=totalBossEncounterMobs==0?Pixel{224, 133, 29}:WHITE; @@ -3747,7 +3747,7 @@ void AiL::InitializeGraphics(){ } LOG(VisualNovel::graphicsToLoad.size()<<" images for visual novel engine have been loaded."); - SetFontSprite("GFX_Prefix"_S+"font.png",&gamepack,"GENERATE_GAMEPACK"_B); + SetFontSprite("GFX_Prefix"_S+"font.png","GFX_Prefix"_S+"font_shadow.png",&gamepack,"GENERATE_GAMEPACK"_B); LOG("Custom font loaded."); Menu::themes.SetInitialized(); @@ -3842,7 +3842,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)); - DrawShadowStringDecal(vf2d{ GetScreenSize() } - vf2d{ GetTextSize(versionStr) }*0.4f,versionStr,ADMIN_MODE?RED:WHITE,BLACK,{0.4f,0.4f},std::numeric_limits::max(),0.4f); + DrawShadowStringDecal(vf2d{ GetScreenSize() } - vf2d{ GetTextSize(versionStr) }*0.4f,versionStr,ADMIN_MODE?RED:WHITE,BLACK,{0.4f,0.4f},{0.4f,0.4f},std::numeric_limits::max()); } int AiL::GetCurrentChapter(){ diff --git a/Adventures in Lestoria/CharacterAbilityPreviewComponent.h b/Adventures in Lestoria/CharacterAbilityPreviewComponent.h index dfaa9596..33e86776 100644 --- a/Adventures in Lestoria/CharacterAbilityPreviewComponent.h +++ b/Adventures in Lestoria/CharacterAbilityPreviewComponent.h @@ -68,8 +68,8 @@ protected: vi2d descriptionPos=iconPos+vi2d{int(rect.size.y)-2,9}; - window.DrawShadowStringPropDecal(descriptionPos-vf2d{0,10},ability->name,{0xFF,0xAF,0x56},BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); - window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); + window.DrawShadowStringPropDecal(descriptionPos-vf2d{0,10},ability->name,{0xFF,0xAF,0x56},BLACK,{0.8f,1.f},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); + window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); InputType controlType=KEY; if(Input::UsingGamepad())controlType=CONTROLLER; diff --git a/Adventures in Lestoria/EnhancementStatsLabel.h b/Adventures in Lestoria/EnhancementStatsLabel.h index 19618fef..8e24db54 100644 --- a/Adventures in Lestoria/EnhancementStatsLabel.h +++ b/Adventures in Lestoria/EnhancementStatsLabel.h @@ -80,7 +80,7 @@ protected: for(float yOffset=0;const auto&[attr,value]:itemRef.lock()->GetEnhancementInfo()[itemRef.lock()->EnhancementLevel()].stats){ std::string attributeLabel=std::format("{}",attr.Name()); float attributeLabelWidth=game->GetTextSizeProp(attributeLabel).x; - window.DrawShadowStringPropDecal(drawPos+vf2d{maxAttributeLabelSize/2+24,yOffset}-vf2d{attributeLabelWidth/2,0},attributeLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x,1.0f); + window.DrawShadowStringPropDecal(drawPos+vf2d{maxAttributeLabelSize/2+24,yOffset}-vf2d{attributeLabelWidth/2,0},attributeLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x); uint8_t nextEnhanceLevel=itemRef.lock()->EnhancementLevel()+1; float nextStageValue=0; if(itemRef.lock()->GetEnhancementInfo().size()>nextEnhanceLevel){ @@ -92,21 +92,21 @@ protected: if(Inventory::GetItemCount(itemRef.lock()->ActualName())==0){ //This item hasn't been created yet, so just show that we are developing the item first. window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:<5}",( (attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign)))), - WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x,1.0f); + WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x); }else{ //This item is getting enhanced to the next level. window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:>5} ->#00AA00 {:<5}", attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign), (nextEnhanceLevel<="Item.Item Max Enhancement Level"_I&&itemRef.lock()->GetEnhancementInfo()[nextEnhanceLevel].chapterAvailable<=game->GetCurrentChapter())? attr.ShowAsDecimal()?std::format("{:<4.2f}{}",nextStageValue,percentageSign):std::format("{}{}",nextStageValue,percentageSign) :"MAX"), - WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x,1.0f); + WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x); } yOffset+=16; } }else{ std::string text="Cannot be enhanced."; float textWidth=game->GetTextSizeProp(text).x; - window.DrawShadowStringPropDecal(rect.middle()-vf2d{textWidth,0}/2,text,RED,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x,1.0f); + window.DrawShadowStringPropDecal(rect.middle()-vf2d{textWidth,0}/2,text,RED,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x); } } }; \ No newline at end of file diff --git a/Adventures in Lestoria/MenuItemItemButton.h b/Adventures in Lestoria/MenuItemItemButton.h index f70d1835..e21d141a 100644 --- a/Adventures in Lestoria/MenuItemItemButton.h +++ b/Adventures in Lestoria/MenuItemItemButton.h @@ -160,7 +160,7 @@ protected: vf2d textSize=vf2d(game->GetTextSizeProp(quantityText))*quantityTextScale; vf2d drawPos=rect.pos+rect.size-textSize; if(itemRef.lock()->Amt()!=INFINITE){ - window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale); + window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale,quantityTextScale); } } } diff --git a/Adventures in Lestoria/MenuLabel.h b/Adventures in Lestoria/MenuLabel.h index 842723f9..9d1a4467 100644 --- a/Adventures in Lestoria/MenuLabel.h +++ b/Adventures in Lestoria/MenuLabel.h @@ -138,15 +138,15 @@ protected: }else{ if(shadow){ if(proportional){ - window.DrawShadowStringPropDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4,1.0f); + window.DrawShadowStringPropDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4); }else{ - window.DrawShadowStringDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4,1.0f); + window.DrawShadowStringDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4); } }else{ if(proportional){ - window.DrawStringPropDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4,1.0f); + window.DrawStringPropDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4); }else{ - window.DrawStringDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4,1.0f); + window.DrawStringDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits::max():rect.size.x-4); } } } diff --git a/Adventures in Lestoria/PopupMenuLabel.h b/Adventures in Lestoria/PopupMenuLabel.h index b04224a6..deea7a59 100644 --- a/Adventures in Lestoria/PopupMenuLabel.h +++ b/Adventures in Lestoria/PopupMenuLabel.h @@ -73,7 +73,7 @@ protected: window.DrawStringPropDecal(rect.pos+rect.size/2-game->GetWrappedTextSizeProp(GetLabel(),int(rect.size.x-1),scale)/2,GetLabel(),WHITE,{1,1},int(rect.size.x-1)); } if(shadow){ - window.DrawShadowStringPropDecal(drawPos,GetLabel(),WHITE,BLACK,scale,int(rect.size.x-1)); + window.DrawShadowStringPropDecal(drawPos,GetLabel(),WHITE,BLACK,scale,scale,int(rect.size.x-1)); }else{ window.DrawStringPropDecal(drawPos,GetLabel(),WHITE,scale,int(rect.size.x-1)); } diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 0edd2ddf..16a19bd8 100644 --- a/Adventures in Lestoria/Version.h +++ b/Adventures in Lestoria/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_PATCH 3 -#define VERSION_BUILD 10737 +#define VERSION_BUILD 10791 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt index 39c5bbbc..e788a670 100644 --- a/Adventures in Lestoria/assets/config/gfx/gfx.txt +++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt @@ -84,6 +84,7 @@ Images GFX_Unlock = unlock.png GFX_SwordSlash = swordslash.png GFX_CustomFont = font.png + GFX_CustomFontShadow = font_shadow.png GFX_Vignette = vignette.png GFX_Checkmark = checkmark.png GFX_DaggerStab = dagger_stab.png diff --git a/Adventures in Lestoria/assets/font_shadow.png b/Adventures in Lestoria/assets/font_shadow.png new file mode 100644 index 00000000..a2399c60 Binary files /dev/null and b/Adventures in Lestoria/assets/font_shadow.png differ diff --git a/Adventures in Lestoria/assets/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak index 9fee2fc9..0e2b4176 100644 Binary files a/Adventures in Lestoria/assets/gamepack.pak and b/Adventures in Lestoria/assets/gamepack.pak differ diff --git a/Adventures in Lestoria/olcPGEX_TransformedView.h b/Adventures in Lestoria/olcPGEX_TransformedView.h index b5dea462..57357645 100644 --- a/Adventures in Lestoria/olcPGEX_TransformedView.h +++ b/Adventures in Lestoria/olcPGEX_TransformedView.h @@ -182,8 +182,10 @@ namespace olc // Draws a multiline string as a decal, with tiniting and scaling void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max()); void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max()); - void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1); - void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1); + void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f }); + void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width=std::numeric_limits::max()); + void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f }); + void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width=std::numeric_limits::max()); // Draws a single shaded filled rectangle as a decal void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); @@ -644,12 +646,20 @@ namespace olc pge->DrawStringPropDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel,width); } - void TransformedView::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){ - pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,width,shadowSizeFactor); + void TransformedView::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ + pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,scale); } - void TransformedView::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){ - pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,width,shadowSizeFactor); + void TransformedView::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width){ + pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,shadowScale,width); + } + + void TransformedView::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ + pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,scale); + } + + void TransformedView::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width){ + pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,shadowScale,width); } void TransformedView::FillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel col) diff --git a/Adventures in Lestoria/olcPGEX_ViewPort.h b/Adventures in Lestoria/olcPGEX_ViewPort.h index 8c685e9f..44ad6371 100644 --- a/Adventures in Lestoria/olcPGEX_ViewPort.h +++ b/Adventures in Lestoria/olcPGEX_ViewPort.h @@ -116,11 +116,13 @@ namespace olc { const vf2d &pos2, Pixel p = WHITE) const; // Draws a multiline string as a decal, with tinting and scaling - void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const bool disableDynamicScaling=false); + void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max()); void DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); - void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }, const float width=std::numeric_limits::max(),const bool disableDynamicScaling=false); - void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1,const bool disableDynamicScaling=false); - void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1,const bool disableDynamicScaling=false); + void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }, const float width=std::numeric_limits::max()); + void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col=WHITE, const Pixel shadowCol=BLACK, const olc::vf2d& scale={1.f,1.f}); + void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width=std::numeric_limits::max()); + void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col=WHITE, const Pixel shadowCol=BLACK, const olc::vf2d& scale={1.f,1.f}); + void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale, const olc::vf2d& shadowScale,const float width=std::numeric_limits::max()); void DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float shadowSizeFactor=1); void DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f }); @@ -634,36 +636,116 @@ float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) { - (point.x - lineA.x) * (lineB.y - lineA.y); } -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; - std::string originalKey{pge->stripCol(sText)}; - std::string renderStr{pge->stripLeadingCol(sText)}; - std::string key{"DSD_"+originalKey}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; - 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. - vf2d imageSize=pge->GetWrappedTextSize(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - pge->garbageCollector[key].decal=newDecal; - }else{ - newDecal=pge->garbageCollector[key].decal; - } - pge->garbageCollector[key].originalStr=originalKey; - pge->SetDrawTarget(newDecal->sprite); - pge->Clear(BLANK); - pge->DrawString({0,0},renderStr,WHITE,1U,width/scale.x); - pge->SetDrawTarget(nullptr); - newDecal->Update(); +void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width){ + Pixel textCol=col; + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += 8.0f * scale.x; + planningMarker.x += 8.0f * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105) + { + textCol={PixelGameEngine::charToColor[c].r,PixelGameEngine::charToColor[c].g,PixelGameEngine::charToColor[c].b,col.a}; + } + else if (c==PixelGameEngine::Reset[0]) + { + textCol=col; + } + else if (c=='#') + { + skip=6; + textCol=BLACK; + for(int i=1;i<7;i++){ + if(i<3){ + textCol.r*=16; + textCol.r+=hexToNumber(sText[index+i]); + }else + if(i<5){ + textCol.g*=16; + textCol.g+=hexToNumber(sText[index+i]); + }else{ + textCol.b*=16; + textCol.b+=hexToNumber(sText[index+i]); + } + } + if(textCol==WHITE)textCol=col; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += 8.0f * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += 8.0f * scale.x; + } + } + letters.emplace_back(c,vf2d{ float(ox) * 8.0f, float(oy) * 8.0f }, vf2d{ 8.0f, 8.0f },textCol); + } + } + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += 8.0f * scale.x; } - pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; - 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){ @@ -682,140 +764,306 @@ void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std:: 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){ - if(sText.length()==0)return; - std::string originalKey{pge->stripCol(sText)}; - std::string renderStr{pge->stripLeadingCol(sText)}; - std::string key{"DSPD"+originalKey}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; - 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. - vf2d imageSize=pge->GetWrappedTextSizeProp(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - pge->garbageCollector[key].decal=newDecal; - }else{ - newDecal=pge->garbageCollector[key].decal; - } - pge->garbageCollector[key].originalStr=originalKey; - pge->SetDrawTarget(newDecal->sprite); - pge->Clear(BLANK); - pge->DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x); - pge->SetDrawTarget(nullptr); - newDecal->Update(); +void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width){ + Pixel textCol=col; + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x; + planningMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105) + { + textCol={PixelGameEngine::charToColor[c].r,PixelGameEngine::charToColor[c].g,PixelGameEngine::charToColor[c].b,col.a}; + } + else if (c==PixelGameEngine::Reset[0]) + { + textCol=col; + } + else if (c=='#') + { + skip=6; + textCol=BLACK; + for(int i=1;i<7;i++){ + if(i<3){ + textCol.r*=16; + textCol.r+=hexToNumber(sText[index+i]); + }else + if(i<5){ + textCol.g*=16; + textCol.g+=hexToNumber(sText[index+i]); + }else{ + textCol.b*=16; + textCol.b+=hexToNumber(sText[index+i]); + } + } + if(textCol==WHITE)textCol=col; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += float(pge->vFontSpacing[c - 32].y) * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=float(pge->vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + } + letters.emplace_back(c,vf2d{ float(ox) * 8.0f + float(pge->vFontSpacing[c - 32].x), float(oy) * 8.0f }, vf2d{ float(pge->vFontSpacing[c - 32].y), 8.0f },textCol); + } + } + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; } - pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; - 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){ - if(sText.length()==0)return; - std::string originalKey{pge->stripCol(sText)}; - std::string renderStr{pge->stripLeadingCol(sText)}; - std::string key{"DSSD_"+originalKey}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; - 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. - vf2d imageSize=pge->GetWrappedTextSize(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - pge->garbageCollector[key].decal=newDecal; - }else{ - newDecal=pge->garbageCollector[key].decal; - } - pge->garbageCollector[key].originalStr=originalKey; - pge->SetDrawTarget(newDecal->sprite); - pge->Clear(BLANK); - pge->DrawString({0,0},renderStr,WHITE,1U,width/scale.x); - newDecal->Update(); - vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; - Decal*newShadowDecal=nullptr; - if(!ShadowRerenderRequired){ - newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2)); - pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; - }else{ - newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal; - } - pge->SetDrawTarget(newShadowDecal->sprite); - pge->Clear(BLANK); - 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){ - if(x!=0||y!=0){ - pge->DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4); +void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ + DrawShadowStringDecal(pos,sText,col,shadowCol,scale,scale); +} + +void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d& shadowScale,const float width){ + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += 8.0f * scale.x; + planningMarker.x += 8.0f * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105){} + else if (c==PixelGameEngine::Reset[0]){} + else if (c=='#') + { + skip=6; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += 8.0f * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += 8.0f * scale.x; } } + letters.emplace_back(c,vf2d{ float(ox) * 10.0f, float(oy) * 10.0f }, vf2d{ 10.0f, 10.0f },shadowCol); } - pge->SetDrawTarget(nullptr); - newShadowDecal->Update(); } - pge->garbageCollector[key].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,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText)); + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + DrawStringDecal(pos,sText,col,scale,width); } -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; - std::string originalKey{pge->stripCol(sText)}; - std::string renderStr{pge->stripLeadingCol(sText)}; - std::string key{"DSSPD"+originalKey}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; - 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. - vf2d imageSize=pge->GetWrappedTextSizeProp(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - pge->garbageCollector[key].decal=newDecal; - }else{ - newDecal=pge->garbageCollector[key].decal; - } - pge->garbageCollector[key].originalStr=originalKey; - pge->SetDrawTarget(newDecal->sprite); - pge->Clear(BLANK); - pge->DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x); - newDecal->Update(); - vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; - Decal*newShadowDecal=nullptr; - if(!ShadowRerenderRequired){ - newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2)); - pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; - }else{ - newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal; - } - pge->SetDrawTarget(newShadowDecal->sprite); - pge->Clear(BLANK); - 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){ - if(x!=0||y!=0){ - pge->DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4); +void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ + DrawShadowStringPropDecal(pos,sText,col,shadowCol,scale,scale); +} + +void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d& shadowScale,const float width){ + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x; + planningMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105){} + else if (c==PixelGameEngine::Reset[0]){} + else if (c=='#') + { + skip=6; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += float(pge->vFontSpacing[c - 32].y) * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; } } + letters.emplace_back(c,vf2d{ float(ox) * 10.0f + float(pge->vFontSpacing[c - 32].x), float(oy) * 10.0f }, vf2d{ float(pge->vFontSpacing[c - 32].y)+2, 10.0f },shadowCol); } - pge->SetDrawTarget(nullptr); - newShadowDecal->Update(); } - pge->garbageCollector[key].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,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText)); + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; + } + DrawStringPropDecal(pos,sText,col,scale,width); } 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){ @@ -828,7 +1076,7 @@ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const 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; pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); - pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); + pge->garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end()); } pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; std::erase_if(pge->garbageCollector,[&](auto&key){ @@ -858,7 +1106,7 @@ void olc::ViewPort::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, c 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; pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); - pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); + pge->garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end()); } pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol); diff --git a/Adventures in Lestoria/olcPixelGameEngine.h b/Adventures in Lestoria/olcPixelGameEngine.h index e1bab1c6..5f984010 100644 --- a/Adventures in Lestoria/olcPixelGameEngine.h +++ b/Adventures in Lestoria/olcPixelGameEngine.h @@ -1148,12 +1148,14 @@ namespace olc void DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE); void DrawPartialSquishedRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::vf2d& squishScale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE); // Draws a multiline string as a decal, with tiniting and scaling - void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const bool disableDynamicScaling=false); + void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max()); void DrawOGStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); void DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); - void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }, const float width=std::numeric_limits::max(),const bool disableDynamicScaling=false); - void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1,const bool disableDynamicScaling=false); - void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits::max(),const float shadowSizeFactor=1,const bool disableDynamicScaling=false); + void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }, const float width=std::numeric_limits::max()); + void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col=WHITE, const Pixel shadowCol=BLACK, const olc::vf2d& scale={1.f,1.f}); + void DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d&shadowScale,const float width=std::numeric_limits::max()); + void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col=WHITE, const Pixel shadowCol=BLACK, const olc::vf2d& scale={1.f,1.f}); + void DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d&shadowScale,const float width=std::numeric_limits::max()); void DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f },const float shadowSizeFactor=1); void DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f }); // Draws a single shaded filled rectangle as a decal @@ -1182,7 +1184,7 @@ namespace olc olc::Sprite* GetFontSprite(); // Returns the font image olc::Decal* GetFontDecal(); - void SetFontSprite(std::string_view filename,ResourcePack*pack,bool generateGamePack); + void SetFontSprite(std::string_view filename,std::string_view shadowFilename,ResourcePack*pack,bool generateGamePack); // Clip a line segment to visible area bool ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2); @@ -1311,7 +1313,7 @@ namespace olc float fLastElapsed = 0.0f; int nFrameCount = 0; bool bSuspendTextureTransfer = false; - Renderable fontRenderable; + Renderable fontRenderable,fontRenderableShadow; std::vector vLayers; uint8_t nTargetLayer = 0; uint32_t nLastFPS = 0; @@ -2474,16 +2476,20 @@ namespace olc { return fontRenderable.Sprite(); } olc::Decal* PixelGameEngine::GetFontDecal() { return fontRenderable.Decal(); } - void PixelGameEngine::SetFontSprite(std::string_view filename,ResourcePack*pack,bool generateGamePack) + void PixelGameEngine::SetFontSprite(std::string_view filename,std::string_view shadowFilename,ResourcePack*pack,bool generateGamePack) { if(pack->Loaded()){ fontRenderable.Load(std::string(filename),pack); + fontRenderableShadow.Load(std::string(shadowFilename),pack); }else{ fontRenderable.Load(std::string(filename),nullptr); + fontRenderableShadow.Load(std::string(shadowFilename),nullptr); if(generateGamePack){ pack->AddFile(std::string(filename)); + pack->AddFile(std::string(shadowFilename)); } } + //Generate shadow version. } bool PixelGameEngine::ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2) @@ -3483,120 +3489,325 @@ 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) { - if(sText.length()==0)return; - std::string originalKey{stripCol(sText)}; - std::string renderStr{stripLeadingCol(sText)}; - std::string key{"DSD"+std::string(originalKey)}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].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. - vf2d imageSize=GetWrappedTextSize(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - garbageCollector[key].decal=newDecal; - }else{ - newDecal=garbageCollector[key].decal; + Pixel textCol=col; + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += 8.0f * scale.x; + planningMarker.x += 8.0f * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105) + { + textCol={PixelGameEngine::charToColor[c].r,PixelGameEngine::charToColor[c].g,PixelGameEngine::charToColor[c].b,col.a}; + } + else if (c==PixelGameEngine::Reset[0]) + { + textCol=col; + } + else if (c=='#') + { + skip=6; + textCol=BLACK; + for(int i=1;i<7;i++){ + if(i<3){ + textCol.r*=16; + textCol.r+=hexToNumber(sText[index+i]); + }else + if(i<5){ + textCol.g*=16; + textCol.g+=hexToNumber(sText[index+i]); + }else{ + textCol.b*=16; + textCol.b+=hexToNumber(sText[index+i]); + } + } + if(textCol==WHITE)textCol=col; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += 8.0f * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=float(vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += 8.0f * scale.x; + } + } + letters.emplace_back(c,vf2d{ float(ox) * 8.0f, float(oy) * 8.0f }, vf2d{ 8.0f, 8.0f },textCol); } - garbageCollector[key].originalStr=originalKey; - SetDrawTarget(newDecal->sprite); - Clear(BLANK); - DrawString({0,0},renderStr,WHITE,1U,width/scale.x); - SetDrawTarget(nullptr); - newDecal->Update(); } - garbageCollector[key].expireTime=GetRunTime()+120.0f; - DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += 8.0f * scale.x; + } } - 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) { - if(sText.length()==0)return; - std::string originalKey{stripCol(sText)}; - std::string renderStr{stripLeadingCol(sText)}; - std::string key{"DSPD_"+std::string(originalKey)}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].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. - vf2d imageSize=GetWrappedTextSizeProp(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - garbageCollector[key].decal=newDecal; - }else{ - newDecal=garbageCollector[key].decal; + Pixel textCol=col; + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += float(vFontSpacing[' ' - 32].y) * scale.x; + planningMarker.x += float(vFontSpacing[' ' - 32].y) * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105) + { + textCol={PixelGameEngine::charToColor[c].r,PixelGameEngine::charToColor[c].g,PixelGameEngine::charToColor[c].b,col.a}; + } + else if (c==PixelGameEngine::Reset[0]) + { + textCol=col; + } + else if (c=='#') + { + skip=6; + textCol=BLACK; + for(int i=1;i<7;i++){ + if(i<3){ + textCol.r*=16; + textCol.r+=hexToNumber(sText[index+i]); + }else + if(i<5){ + textCol.g*=16; + textCol.g+=hexToNumber(sText[index+i]); + }else{ + textCol.b*=16; + textCol.b+=hexToNumber(sText[index+i]); + } + } + if(textCol==WHITE)textCol=col; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += float(vFontSpacing[c - 32].y) * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=float(vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + } + letters.emplace_back(c,vf2d{ float(ox) * 8.0f + float(vFontSpacing[c - 32].x), float(oy) * 8.0f }, vf2d{ float(vFontSpacing[c - 32].y), 8.0f },textCol); } - garbageCollector[key].originalStr=originalKey; - SetDrawTarget(newDecal->sprite); - Clear(BLANK); - DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x); - SetDrawTarget(nullptr); - newDecal->Update(); } - garbageCollector[key].expireTime=GetRunTime()+120.0f; - DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } } - 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; - std::string originalKey{stripCol(sText)}; - std::string renderStr{stripLeadingCol(sText)}; - std::string key{"DSSD_"+std::string(originalKey)}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr; - 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. - vf2d imageSize=GetWrappedTextSize(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - garbageCollector[key].decal=newDecal; - }else{ - newDecal=garbageCollector[key].decal; - } - garbageCollector[key].originalStr=originalKey; - SetDrawTarget(newDecal->sprite); - Clear(BLANK); - DrawString({0,0},renderStr,WHITE,1U,width/scale.x); - newDecal->Update(); - vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; - Decal*newShadowDecal=nullptr; - if(!ShadowRerenderRequired){ - newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2)); - garbageCollector[key+"_SHADOW"].decal=newShadowDecal; - }else{ - newShadowDecal=garbageCollector[key+"_SHADOW"].decal; - } - SetDrawTarget(newShadowDecal->sprite); - Clear(BLANK); - 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){ - if(x!=0||y!=0){ - DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4); + void PixelGameEngine::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ + DrawShadowStringDecal(pos,sText,col,shadowCol,scale,scale); + } + + void PixelGameEngine::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d& shadowScale,const float width){ + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += 8.0f * scale.x; + planningMarker.x += 8.0f * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105){} + else if (c==PixelGameEngine::Reset[0]){} + else if (c=='#') + { + skip=6; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += 8.0f * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=float(vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += 8.0f * scale.x; } } + letters.emplace_back(c,vf2d{ float(ox) * 10.0f, float(oy) * 10.0f }, vf2d{ 10.0f, 10.0f },shadowCol); } - SetDrawTarget(nullptr); - newShadowDecal->Update(); } - garbageCollector[key].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,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += 8.0f * scale.x; + } + DrawStringDecal(pos,sText,col,scale,width); } void PixelGameEngine::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){ @@ -3609,7 +3820,7 @@ namespace olc 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; garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); - garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); + garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end()); } garbageCollector[key].expireTime=GetRunTime()+120.0f; DrawDecal(pos,garbageCollector[key].decal,scale/4,GetFinalRenderColor(col,sText)); @@ -3625,7 +3836,7 @@ namespace olc 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; garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); - garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); + garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end()); } garbageCollector[key].expireTime=GetRunTime()+120.0f; for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ @@ -3648,7 +3859,7 @@ namespace olc 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; garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); - garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); + garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end()); } garbageCollector[key].expireTime=GetRunTime()+120.0f; DrawDecal(pos+vf2d{0,0.5f},garbageCollector[key].decal,scale/4,shadowCol); @@ -3657,56 +3868,99 @@ namespace olc 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){ - if(sText.length()==0)return; - std::string originalKey{stripCol(sText)}; - std::string renderStr{stripLeadingCol(sText)}; - std::string key{"DSSPD_"+originalKey}; - key+=std::to_string(width); - if(!disableDynamicScaling){ - key+=scale.str(); - } - const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr; - 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. - vf2d imageSize=GetWrappedTextSizeProp(originalKey,width,scale); - if(imageSize.x<1||imageSize.y<1)return; - Decal*newDecal=nullptr; - if(!RerenderRequired){ - newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); - garbageCollector[key].decal=newDecal; - }else{ - newDecal=garbageCollector[key].decal; - } - garbageCollector[key].originalStr=originalKey; - SetDrawTarget(newDecal->sprite); - Clear(BLANK); - DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x); - newDecal->Update(); - vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; - Decal*newShadowDecal=nullptr; - if(!ShadowRerenderRequired){ - newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2)); - garbageCollector[key+"_SHADOW"].decal=newShadowDecal; - }else{ - newShadowDecal=garbageCollector[key+"_SHADOW"].decal; - } - SetDrawTarget(newShadowDecal->sprite); - Clear(BLANK); - 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){ - if(x!=0||y!=0){ - DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4); + void PixelGameEngine::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){ + DrawShadowStringPropDecal(pos,sText,col,shadowCol,scale,scale); + } + + void PixelGameEngine::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const olc::vf2d& shadowScale,const float width){ + static std::vectorletters; + letters.clear(); + bool wrappingOccurred=false; + olc::vf2d planningMarker = { 0.0f, 0.0f }; + olc::vf2d drawingMarker = { 0.0f, 0.0f }; + const auto hexToNumber=[](char c){ + if(c<='9')return c-'0'; + return (c-'A')+10; + }; + for (int skip=0,index=-1;auto c : sText) + { + index++; + if(skip){ + skip--; + continue; + } + if(c==' '||c=='\t'||c=='\n'){ + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + wrappingOccurred=false; + } + if(wrappingOccurred){ + if(c!=' '&&c!='\t'&&c!='\n'){ + wrappingOccurred=false; + }else{ + continue; + } + } + if(c=='\r')continue; //Ignore carriage returns. Stupid Linux. + if (c == '\n') + { + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y; + } + else if (c == ' ') + { + drawingMarker.x += float(vFontSpacing[' ' - 32].y) * scale.x; + planningMarker.x += float(vFontSpacing[' ' - 32].y) * scale.x; + } + else if (c == '\t') + { + drawingMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + planningMarker.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else if (c>=-128&&c<-105){} + else if (c==PixelGameEngine::Reset[0]){} + else if (c=='#') + { + skip=6; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + planningMarker.x += float(vFontSpacing[c - 32].y) * scale.x; + if(planningMarker.x>width){ + if(drawingMarker.x==0){ //The text has overflowed a full line, so we have to dump the line. + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + letters.clear(); + }else{ + drawingMarker.x-=float(vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping. + } + planningMarker.x = 0; planningMarker.y += 8.0f * scale.y; + drawingMarker=planningMarker; + wrappingOccurred=true; + for(PixelGameEngine::StringDecalData&letter:letters){ + planningMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; } } + letters.emplace_back(c,vf2d{ float(ox) * 10.0f + float(vFontSpacing[c - 32].x), float(oy) * 10.0f }, vf2d{ float(vFontSpacing[c - 32].y)+2, 10.0f },shadowCol); } - SetDrawTarget(nullptr); - newShadowDecal->Update(); } - garbageCollector[key].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,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); + for(PixelGameEngine::StringDecalData&letter:letters){ + DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol); + drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; + } + DrawStringPropDecal(pos,sText,col,scale,width); } 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){ diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index 4892a060..1c328cfa 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ