diff --git a/CHANGELOG.md b/CHANGELOG.md index 577646ff..d7c33fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Unreleased ### Added + - `--theme-menu` starts the game directly on the theme selection screen (Configure -> Themes) + ### Removed ### Changed diff --git a/man/blockattack.man b/man/blockattack.man index 00426beb..258075f4 100644 --- a/man/blockattack.man +++ b/man/blockattack.man @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. -.TH BLOCKATTACK "6" "October 2025" "blockattack 2.11.0-SNAPSHOT" "Games" +.TH BLOCKATTACK "6" "September 2026" "blockattack 2.11.0-SNAPSHOT" "Games" .SH NAME blockattack \- a puzzle game inspired by Tetris Attack .SH SYNOPSIS @@ -52,6 +52,9 @@ Start the sprite editor/browser \fB\-\-puzzle\-editor\fR Start the build in puzzle editor .TP +\fB\-\-theme\-menu\fR +Go directly to the theme selection screen +.TP \fB\-\-puzzle\-level\-file\fR arg Sets the default puzzle file to load .TP diff --git a/source/code/BlockGame.cpp b/source/code/BlockGame.cpp index 87c173ab..4e22dfae 100644 --- a/source/code/BlockGame.cpp +++ b/source/code/BlockGame.cpp @@ -519,9 +519,10 @@ void BlockGame::putSampleBlocks() { for (size_t j = 6; j < 9; j++) { board[i][j] = GARBAGE+1; } - for (size_t j = 9; j < 12; j++) { + for (size_t j = 9; j < 11; j++) { board[i][j] = GARBAGE+2; } + board[i][11] = GARBAGE_GRAY*GARBAGE; } } diff --git a/source/code/BlockGameSdl.hpp b/source/code/BlockGameSdl.hpp index 67e105e7..795a1a64 100644 --- a/source/code/BlockGameSdl.hpp +++ b/source/code/BlockGameSdl.hpp @@ -238,6 +238,8 @@ class BlockGameSdl : public BlockGame { //Draws all the bricks to the board (including garbage) void PaintBricks() const { + const GarbageData& garbageSet = globalData.theme.garbage; + const sago::SagoSpriteHolder& sprites = *globalData.spriteHolder; for (int i=0; i<13; ++i) { for (int j=0; j<6; ++j) { int basicBrick = board[j][i]%10; //The basic brick, stored on the least significant digit @@ -279,51 +281,51 @@ class BlockGameSdl : public BlockGame { under = board[j][i-1]; } if ((left == number)&&(right == number)&&(over == number)&&(under == number)) { - DrawImgBoardBounded(globalData.garbageFill, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.fill), j*bsize, bsize*12-i*bsize-pixels); } if ((left != number)&&(right == number)&&(over == number)&&(under == number)) { - DrawImgBoardBounded(globalData.garbageL, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.l), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right != number)&&(over == number)&&(under == number)) { - DrawImgBoardBounded(globalData.garbageR, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.r), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right == number)&&(over != number)&&(under == number)) { - DrawImgBoardBounded(globalData.garbageT, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.t), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right == number)&&(over == number)&&(under != number)) { - DrawImgBoardBounded(globalData.garbageB, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.b), j*bsize, bsize*12-i*bsize-pixels); } if ((left != number)&&(right == number)&&(over != number)&&(under == number)) { - DrawImgBoardBounded(globalData.garbageTL, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.tl), j*bsize, bsize*12-i*bsize-pixels); } if ((left != number)&&(right == number)&&(over == number)&&(under != number)) { - DrawImgBoardBounded(globalData.garbageBL, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.bl), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right != number)&&(over != number)&&(under == number)) { - DrawImgBoardBounded(globalData.garbageTR, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.tr), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right != number)&&(over == number)&&(under != number)) { - DrawImgBoardBounded(globalData.garbageBR, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.br), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right != number)&&(over != number)&&(under != number)) { - DrawImgBoardBounded(globalData.garbageMR, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.mr), j*bsize, bsize*12-i*bsize-pixels); } if ((left == number)&&(right == number)&&(over != number)&&(under != number)) { - DrawImgBoardBounded(globalData.garbageM, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.m), j*bsize, bsize*12-i*bsize-pixels); } if ((left != number)&&(right == number)&&(over != number)&&(under != number)) { - DrawImgBoardBounded(globalData.garbageML, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.ml), j*bsize, bsize*12-i*bsize-pixels); } } if ((board[j][i]/1000000)%10==2) { if (j==0) { - DrawImgBoardBounded(globalData.garbageGML, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.gml), j*bsize, bsize*12-i*bsize-pixels); } else if (j==5) { - DrawImgBoardBounded(globalData.garbageGMR, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.gmr), j*bsize, bsize*12-i*bsize-pixels); } else { - DrawImgBoardBounded(globalData.garbageGM, j*bsize, bsize*12-i*bsize-pixels); + DrawImgBoardBounded(sprites.GetSprite(garbageSet.gm), j*bsize, bsize*12-i*bsize-pixels); } } } diff --git a/source/code/global.hpp b/source/code/global.hpp index 0c07caf8..cc41b963 100644 --- a/source/code/global.hpp +++ b/source/code/global.hpp @@ -124,21 +124,6 @@ struct GlobalData { sago::SoundHandler counterChunk; //When counting down sago::SoundHandler counterFinalChunk; - sago::SagoSprite garbageTL; //the Garbage Blocks - sago::SagoSprite garbageT; - sago::SagoSprite garbageTR; - sago::SagoSprite garbageR; - sago::SagoSprite garbageBR; - sago::SagoSprite garbageB; - sago::SagoSprite garbageBL; - sago::SagoSprite garbageL; - sago::SagoSprite garbageFill; - sago::SagoSprite garbageM; - sago::SagoSprite garbageML; - sago::SagoSprite garbageMR; - sago::SagoSprite garbageGM; - sago::SagoSprite garbageGML; - sago::SagoSprite garbageGMR; sago::SagoSprite transCover; //The transperant block, covers the upcomming sago::SagoSprite bSkip; sago::SagoSprite bRetry; diff --git a/source/code/main.cpp b/source/code/main.cpp index fa346767..af79211d 100644 --- a/source/code/main.cpp +++ b/source/code/main.cpp @@ -73,7 +73,7 @@ Source information and contacts persons can be found at #include "editor/SagoTextureSelector.hpp" #include "puzzle_editor/PuzzleEditorState.hpp" -#include "theme_editor/ThemeEditorState.hpp" +#include "menudef_themes.hpp" #include "SagoImGui.hpp" /******************************************************************************* @@ -172,21 +172,6 @@ static int InitImages(sago::SagoSpriteHolder& holder) { globalData.iLevelCheckBoxMarked = holder.GetSprite("i_level_check_box_marked"); globalData.iCheckBoxArea = holder.GetSprite("i_check_box_area"); globalData.boardBackBack = holder.GetSprite("board_back_back"); - globalData.garbageTL = holder.GetSprite("garbage_tl"); - globalData.garbageT = holder.GetSprite("garbage_t"); - globalData.garbageTR = holder.GetSprite("garbage_tr"); - globalData.garbageR = holder.GetSprite("garbage_r"); - globalData.garbageBR = holder.GetSprite("garbage_br"); - globalData.garbageB = holder.GetSprite("garbage_b"); - globalData.garbageBL = holder.GetSprite("garbage_bl"); - globalData.garbageL = holder.GetSprite("garbage_l"); - globalData.garbageFill = holder.GetSprite("garbage_fill"); - globalData.garbageML = holder.GetSprite("garbage_ml"); - globalData.garbageM = holder.GetSprite("garbage_m"); - globalData.garbageMR = holder.GetSprite("garbage_mr"); - globalData.garbageGM = holder.GetSprite("garbage_gm"); - globalData.garbageGML = holder.GetSprite("garbage_gml"); - globalData.garbageGMR = holder.GetSprite("garbage_gmr"); globalData.transCover = holder.GetSprite("trans_cover"); bExit = holder.GetSprite("b_exit"); globalData.bSkip = holder.GetSprite("b_blank"); @@ -948,7 +933,7 @@ static void ParseArguments(int argc, char* argv[], globalConfig& conf) { ("always-sixteen-nine", "Use 16:9 format even in Window mode") ("editor", "Start the sprite editor/browser") ("puzzle-editor", "Start the build in puzzle editor") - ("theme-editor", "Start the theme editor") + ("theme-menu", "Go directly to the theme selection screen") ("puzzle-level-file", boost::program_options::value(), "Sets the default puzzle file to load") ("puzzle-single-level", boost::program_options::value(), "Start the specific puzzle level directly") #ifdef REPLAY_IMPLEMENTED @@ -1050,8 +1035,8 @@ static void ParseArguments(int argc, char* argv[], globalConfig& conf) { if (vm.count("puzzle-editor")) { puzzleEditor = true; } - if (vm.count("theme-editor")) { - themeEditor = true; + if (vm.count("theme-menu")) { + themeMenu = true; } if (vm.count("puzzle-level-file")) { conf.puzzleName = vm["puzzle-level-file"].as(); @@ -1358,17 +1343,6 @@ int main(int argc, char* argv[]) { RunImGuiGameState(s); ImGui::SaveIniSettingsToDisk(imgui_inifile.c_str()); } - else if (themeEditor) { - InitImGui(sdlWindow, renderer, globalData.xsize, globalData.ysize); - ImGuiIO& io = ImGui::GetIO(); - io.IniFilename = nullptr; - std::string imgui_inifile = getPathToSaveFiles() + "/imgui_theme_editor.ini"; - ImGui::LoadIniSettingsFromDisk(imgui_inifile.c_str()); - ThemeEditorState s; - s.Init(); - RunImGuiGameState(s); - ImGui::SaveIniSettingsToDisk(imgui_inifile.c_str()); - } else if (globalData.replayArgument.length()) { ReplayPlayer rp; RunGameState(rp); @@ -1377,6 +1351,9 @@ int main(int argc, char* argv[]) { if (!gameShutdownProperly) { SafeModeMenu(); } + if (themeMenu) { + OpenThemesMenu(); + } //game loop MainMenu(); } diff --git a/source/code/mainVars.inc b/source/code/mainVars.inc index 34f768e8..ce394d73 100644 --- a/source/code/mainVars.inc +++ b/source/code/mainVars.inc @@ -92,7 +92,7 @@ bool twoPlayers; //True if two playerImGui_ImplSDL2_ProcessEvent(&event);s ar static bool singlePuzzle = false; static bool editor = false; static bool puzzleEditor = false; -static bool themeEditor = false; +static bool themeMenu = false; static int singlePuzzleNr = 0; static std::string singlePuzzleFile; diff --git a/source/code/menudef_themes.cpp b/source/code/menudef_themes.cpp index 7521511d..cff84002 100644 --- a/source/code/menudef_themes.cpp +++ b/source/code/menudef_themes.cpp @@ -96,6 +96,11 @@ static void themesEditSwitchBorder() { globalData.theme = themeToEdit; } +static void themesEditSwitchGarbage() { + themeToEdit.garbage = ThemesGetNextGarbage(themeToEdit.garbage.name); + globalData.theme = themeToEdit; +} + static void themesEditSlot(int slot) { ThemesMenu tem(globalData.screen, fmt::format(fmt::runtime(_("Edit custom theme {}")), slot), true); size_t theme_index = ThemesGetNumber(fmt::format("custom_slot_{}", slot)); @@ -115,6 +120,10 @@ static void themesEditSlot(int slot) { bSwitchBorder.setLabel(_("Switch border")); bSwitchBorder.setAction(&themesEditSwitchBorder); tem.addButton(&bSwitchBorder); + Button bSwitchGarbage; + bSwitchGarbage.setLabel(_("Switch garbage")); + bSwitchGarbage.setAction(&themesEditSwitchGarbage); + tem.addButton(&bSwitchGarbage); themeBackup = globalData.theme; themeToEdit = ThemesGet(theme_index); globalData.theme = themeToEdit; diff --git a/source/code/theme_editor/ThemeEditorState.cpp b/source/code/theme_editor/ThemeEditorState.cpp deleted file mode 100644 index 0d5aef23..00000000 --- a/source/code/theme_editor/ThemeEditorState.cpp +++ /dev/null @@ -1,619 +0,0 @@ -/* -=========================================================================== -blockattack - Block Attack - Rise of the Blocks -Copyright (C) 2005-2026 Poul Sander - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see http://www.gnu.org/licenses/ - -Source information and contacts persons can be found at -https://blockattack.net -=========================================================================== -*/ - -#include "ThemeEditorState.hpp" - -#include -#include - -#include "imgui.h" -#include "imgui_internal.h" -#include "backends/imgui_impl_sdl2.h" -#include "backends/imgui_impl_sdlrenderer2.h" -#include "../sago/SagoMisc.hpp" -#include "../BlockGameSdl.hpp" - -ThemeEditorState::ThemeEditorState() { - current_border.name = "standard"; - current_border.border_sprite = "board_back_back"; - current_border.border_sprite_offset = {-60, -68}; - current_border.score_label_offset = {310, 80}; - current_border.time_label_offset = {310, 129}; - current_border.chain_label_offset = {310, 178}; - current_border.speed_label_offset = {310, 227}; - current_border.score_field_offset = {310, 102}; - current_border.time_field_offset = {310, 151}; - current_border.chain_field_offset = {310, 200}; - current_border.speed_field_offset = {310, 249}; - - current_background.name = "standard"; - current_background.background_sprite = "background"; - current_background.background_sprite_16x9 = "background_sixteen_nine"; - current_background.background_scale = ImgScale::Stretch; -} - -ThemeEditorState::~ThemeEditorState() { - // Restore the original theme when editor exits - globalData.theme = saved_theme; -} - -bool ThemeEditorState::IsActive() { - return isActive; -} - -void ThemeEditorState::ProcessInput(const SDL_Event& event, bool& processed) { - (void)event; - (void)processed; - // Handle any custom input processing here if needed -} - -void ThemeEditorState::Init() { - // Initialize the theme system - ThemesInit(); - - // Populate lists - border_names = ThemesGetBorderNames(); - background_names = ThemesGetBackgroundNames(); - decoration_names = ThemesGetDecorationNames(); - - // Save current theme and set up preview - saved_theme = globalData.theme; - preview_theme = ThemesGet(0); - - // Create preview game instance positioned in the right 40% of screen - // Editor takes 60% of width, so preview starts at 60% + some padding - int preview_x = static_cast(globalData.xsize * 0.62f); - preview_game = std::make_shared(preview_x, 100, &globalData.spriteHolder->GetDataHolder()); - preview_game->name = "Preview"; - preview_game->putSampleBlocks(); - - // Initialize preview with current border/background - preview_theme.border = current_border; - preview_theme.background = current_background; - globalData.theme = preview_theme; -} - -void ThemeEditorState::Update() { - // Update preview if needed - if (preview_needs_update) { - preview_needs_update = false; - UpdatePreview(); - } -} - -void ThemeEditorState::UpdatePreview() { - // Update preview theme based on current tab - switch (current_tab) { - case 0: // Border Editor - preview_theme.border = current_border; - break; - case 1: // Background Editor - preview_theme.background = current_background; - break; - case 2: // Theme Composer - // Combine selected components - if (static_cast(selected_border_index) < border_names.size()) { - preview_theme.border = ThemesGetBorder(border_names[selected_border_index]); - } - if (static_cast(selected_background_index) < background_names.size()) { - preview_theme.background = ThemesGetBackground(background_names[selected_background_index]); - } - preview_theme.back_board = board_backgrounds[selected_board_background]; - break; - } - - // Apply theme to global for preview rendering - globalData.theme = preview_theme; -} - -void ThemeEditorState::Draw(SDL_Renderer* target) { - // Get actual physical renderer size and keep logicalResize in sync every frame - int display_w = 1; - int display_h = 1; - SDL_GetRendererOutputSize(target, &display_w, &display_h); - globalData.logicalResize.SetPhysicalSize(display_w, display_h); - - // Main window - take up left 60% of screen to leave room for preview - const float editor_width = display_w * 0.5f; - ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(editor_width, (float)display_h), ImGuiCond_Always); - - ImGui::Begin("Theme Editor", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - - // Tab bar - if (ImGui::BeginTabBar("EditorTabs")) { - if (ImGui::BeginTabItem("Border Editor")) { - current_tab = 0; - DrawBorderEditor(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Background Editor")) { - current_tab = 1; - DrawBackgroundEditor(); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Theme Composer")) { - current_tab = 2; - DrawThemeComposer(); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); - } - - ImGui::Separator(); - if (ImGui::Button("Exit Editor")) { - isActive = false; - } - - ImGui::End(); - - // Draw the preview: convert physical position (ImGui space) to logical space for DoPaintJob - if (preview_game) { - const int preview_physical_x = static_cast(editor_width) + 250; - const int preview_physical_y = 150; - int preview_logical_x = 0; - int preview_logical_y = 0; - globalData.logicalResize.PhysicalToLogical(preview_physical_x, preview_physical_y, preview_logical_x, preview_logical_y); - preview_game->SetTopXY(preview_logical_x, preview_logical_y); - preview_game->DoPaintJob(); - } -} - -void ThemeEditorState::DrawBorderEditor() { - ImGui::Text("Create and edit custom borders"); - ImGui::Separator(); - - // Load existing border - ImGui::Text("Load existing border:"); - if (ImGui::BeginCombo("##BorderLoad", border_names.empty() ? "No borders" : current_border.name.c_str())) { - for (const auto& name : border_names) { - bool is_selected = (current_border.name == name); - if (ImGui::Selectable(name.c_str(), is_selected)) { - LoadBorder(name); - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::Separator(); - ImGui::Text("Border Settings:"); - - // Border name - ImGui::InputText("Border Name", border_name_buffer, sizeof(border_name_buffer)); - - // Border sprite - if (ImGui::InputText("Border Sprite", border_sprite_buffer, sizeof(border_sprite_buffer))) { - current_border.border_sprite = border_sprite_buffer; - preview_needs_update = true; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Examples: board_back_back, board_back_back_mirror"); - } - - // Validate sprite button - ImGui::SameLine(); - if (ImGui::Button("Validate##border")) { - if (ValidateSprite(border_sprite_buffer)) { - ImGui::OpenPopup("Valid Sprite"); - } else { - ImGui::OpenPopup("Invalid Sprite"); - } - } - - if (ImGui::BeginPopup("Valid Sprite")) { - ImGui::Text("Sprite exists!"); - ImGui::EndPopup(); - } - if (ImGui::BeginPopup("Invalid Sprite")) { - ImGui::Text("Warning: Sprite does not exist!"); - ImGui::EndPopup(); - } - - // Border sprite offset - if (ImGui::DragInt2("Border Sprite Offset", &border_offset_x)) { - current_border.border_sprite_offset = {border_offset_x, border_offset_y}; - preview_needs_update = true; - } - - // Label offsets - ImGui::Separator(); - ImGui::Text("Label Positions:"); - if (ImGui::DragInt2("Score Label Offset", &score_offset_x)) { - current_border.score_label_offset = {score_offset_x, score_offset_y}; - preview_needs_update = true; - } - if (ImGui::DragInt2("Time Label Offset", &time_offset_x)) { - current_border.time_label_offset = {time_offset_x, time_offset_y}; - preview_needs_update = true; - } - if (ImGui::DragInt2("Chain Label Offset", &chain_offset_x)) { - current_border.chain_label_offset = {chain_offset_x, chain_offset_y}; - preview_needs_update = true; - } - if (ImGui::DragInt2("Speed Label Offset", &speed_offset_x)) { - current_border.speed_label_offset = {speed_offset_x, speed_offset_y}; - preview_needs_update = true; - } - - // Field offsets - ImGui::Separator(); - ImGui::Text("Field Positions:"); - if (ImGui::DragInt2("Score Field Offset", &score_field_x)) { - current_border.score_field_offset = {score_field_x, score_field_y}; - preview_needs_update = true; - } - if (ImGui::DragInt2("Time Field Offset", &time_field_x)) { - current_border.time_field_offset = {time_field_x, time_field_y}; - preview_needs_update = true; - } - if (ImGui::DragInt2("Chain Field Offset", &chain_field_x)) { - current_border.chain_field_offset = {chain_field_x, chain_field_y}; - preview_needs_update = true; - } - if (ImGui::DragInt2("Speed Field Offset", &speed_field_x)) { - current_border.speed_field_offset = {speed_field_x, speed_field_y}; - preview_needs_update = true; - } - - ImGui::Separator(); - if (ImGui::Button("Save Border")) { - SaveCurrentBorder(); - } - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Saved to: borders/.json"); -} - -void ThemeEditorState::DrawBackgroundEditor() { - ImGui::Text("Create and edit custom backgrounds"); - ImGui::Separator(); - - // Load existing background - ImGui::Text("Load existing background:"); - if (ImGui::BeginCombo("##BackgroundLoad", background_names.empty() ? "No backgrounds" : current_background.name.c_str())) { - for (const auto& name : background_names) { - bool is_selected = (current_background.name == name); - if (ImGui::Selectable(name.c_str(), is_selected)) { - LoadBackground(name); - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::Separator(); - ImGui::Text("Background Settings:"); - - // Background name - ImGui::InputText("Background Name", background_name_buffer, sizeof(background_name_buffer)); - - // Background sprite - if (ImGui::InputText("Background Sprite", background_sprite_buffer, sizeof(background_sprite_buffer))) { - current_background.background_sprite = background_sprite_buffer; - preview_needs_update = true; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Examples: background, background_autumn, custom_background_"); - } - - // Validate sprite button - ImGui::SameLine(); - if (ImGui::Button("Validate##background")) { - if (ValidateSprite(background_sprite_buffer)) { - ImGui::OpenPopup("Valid BG Sprite"); - } else { - ImGui::OpenPopup("Invalid BG Sprite"); - } - } - - if (ImGui::BeginPopup("Valid BG Sprite")) { - ImGui::Text("Sprite exists!"); - ImGui::EndPopup(); - } - if (ImGui::BeginPopup("Invalid BG Sprite")) { - ImGui::Text("Warning: Sprite does not exist!"); - ImGui::EndPopup(); - } - - // 16:9 background sprite (optional) - if (ImGui::InputText("Background Sprite 16:9 (optional)", background_sprite_16x9_buffer, sizeof(background_sprite_16x9_buffer))) { - current_background.background_sprite_16x9 = background_sprite_16x9_buffer; - preview_needs_update = true; - } - - // Scaling mode - ImGui::Separator(); - ImGui::Text("Scaling Mode:"); - const char* scale_modes[] = { "Stretch", "Tile", "Resize", "Cut" }; - if (ImGui::Combo("##ScaleMode", &background_scale_mode, scale_modes, 4)) { - switch (background_scale_mode) { - case 0: current_background.background_scale = ImgScale::Stretch; break; - case 1: current_background.background_scale = ImgScale::Tile; break; - case 2: current_background.background_scale = ImgScale::Resize; break; - case 3: current_background.background_scale = ImgScale::Cut; break; - } - preview_needs_update = true; - } - - // Tile animation speed - ImGui::Separator(); - ImGui::Text("Tile Animation (0 = disabled):"); - if (ImGui::DragInt("Move Speed X (ms)", &tile_move_speed_x, 1.0f, 0, 10000)) { - current_background.tileMoveSpeedX = tile_move_speed_x; - preview_needs_update = true; - } - if (ImGui::DragInt("Move Speed Y (ms)", &tile_move_speed_y, 1.0f, 0, 10000)) { - current_background.tileMoveSpeedY = tile_move_speed_y; - preview_needs_update = true; - } - - ImGui::Separator(); - if (ImGui::Button("Save Background")) { - SaveCurrentBackground(); - } - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Saved to: backgrounds/.json"); -} - -void ThemeEditorState::DrawThemeComposer() { - ImGui::Text("Combine borders and backgrounds into a complete theme"); - ImGui::Separator(); - - // Theme name - ImGui::InputText("Theme Name", theme_name_buffer, sizeof(theme_name_buffer)); - - // Border selection - ImGui::Separator(); - ImGui::Text("Select Border:"); - if (ImGui::BeginCombo("##BorderSelect", static_cast(selected_border_index) < border_names.size() ? border_names[selected_border_index].c_str() : "")) { - for (size_t i = 0; i < border_names.size(); i++) { - bool is_selected = (static_cast(selected_border_index) == i); - if (ImGui::Selectable(border_names[i].c_str(), is_selected)) { - selected_border_index = i; - preview_needs_update = true; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - // Background selection - ImGui::Text("Select Background:"); - if (ImGui::BeginCombo("##BackgroundSelect", static_cast(selected_background_index) < background_names.size() ? background_names[selected_background_index].c_str() : "")) { - for (size_t i = 0; i < background_names.size(); i++) { - bool is_selected = (static_cast(selected_background_index) == i); - if (ImGui::Selectable(background_names[i].c_str(), is_selected)) { - selected_background_index = i; - preview_needs_update = true; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - // Decoration selection - ImGui::Text("Select Decoration:"); - if (ImGui::BeginCombo("##DecorationSelect", static_cast(selected_decoration_index) < decoration_names.size() ? decoration_names[selected_decoration_index].c_str() : "")) { - for (size_t i = 0; i < decoration_names.size(); i++) { - bool is_selected = (static_cast(selected_decoration_index) == i); - if (ImGui::Selectable(decoration_names[i].c_str(), is_selected)) { - selected_decoration_index = i; - preview_needs_update = true; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - // Board background selection - ImGui::Text("Select Board Background:"); - if (ImGui::BeginCombo("##BoardBGSelect", board_backgrounds[selected_board_background].c_str())) { - for (size_t i = 0; i < board_backgrounds.size(); i++) { - bool is_selected = (static_cast(selected_board_background) == i); - if (ImGui::Selectable(board_backgrounds[i].c_str(), is_selected)) { - selected_board_background = i; - preview_needs_update = true; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::Separator(); - if (ImGui::Button("Save Theme")) { - SaveCurrentTheme(); - } - ImGui::SameLine(); - ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Saved to: custom_themes.json"); -} - -void ThemeEditorState::DrawPreview(SDL_Renderer* target) { - (void)target; - // TODO: Implement preview rendering using BlockGameSdl -} - -void ThemeEditorState::SaveCurrentBorder() { - // Update current_border from UI fields - std::string name = border_name_buffer; - if (name.empty()) { - std::cerr << "Error: Border name cannot be empty\n"; - return; - } - - current_border.name = name; - current_border.border_sprite = border_sprite_buffer; - current_border.border_sprite_offset = {border_offset_x, border_offset_y}; - current_border.score_label_offset = {score_offset_x, score_offset_y}; - current_border.time_label_offset = {time_offset_x, time_offset_y}; - current_border.chain_label_offset = {chain_offset_x, chain_offset_y}; - current_border.speed_label_offset = {speed_offset_x, speed_offset_y}; - current_border.score_field_offset = {score_field_x, score_field_y}; - current_border.time_field_offset = {time_field_x, time_field_y}; - current_border.chain_field_offset = {chain_field_x, chain_field_y}; - current_border.speed_field_offset = {speed_field_x, speed_field_y}; - - // Validate sprite - if (!ValidateSprite(current_border.border_sprite)) { - std::cerr << "Warning: Saving border with non-existent sprite: " << current_border.border_sprite << "\n"; - } - - // Save to file - ThemesSaveBorderData(name, current_border); - std::cout << "Saved border: " << name << "\n"; - - // Refresh border list - border_names = ThemesGetBorderNames(); -} - -void ThemeEditorState::SaveCurrentBackground() { - // Update current_background from UI fields - std::string name = background_name_buffer; - if (name.empty()) { - std::cerr << "Error: Background name cannot be empty\n"; - return; - } - - current_background.name = name; - current_background.background_sprite = background_sprite_buffer; - current_background.background_sprite_16x9 = background_sprite_16x9_buffer; - - // Convert scale mode - switch (background_scale_mode) { - case 0: current_background.background_scale = ImgScale::Stretch; break; - case 1: current_background.background_scale = ImgScale::Tile; break; - case 2: current_background.background_scale = ImgScale::Resize; break; - case 3: current_background.background_scale = ImgScale::Cut; break; - } - - current_background.tileMoveSpeedX = tile_move_speed_x; - current_background.tileMoveSpeedY = tile_move_speed_y; - - // Validate sprite - if (!ValidateSprite(current_background.background_sprite)) { - std::cerr << "Warning: Saving background with non-existent sprite: " << current_background.background_sprite << "\n"; - } - - // Save to file - ThemesSaveBackgroundData(name, current_background); - std::cout << "Saved background: " << name << "\n"; - - // Refresh background list - background_names = ThemesGetBackgroundNames(); -} - -void ThemeEditorState::SaveCurrentTheme() { - std::string name = theme_name_buffer; - if (name.empty()) { - std::cerr << "Error: Theme name cannot be empty\n"; - return; - } - - Theme theme; - theme.theme_name = name; - - // Set components - if (static_cast(selected_border_index) < border_names.size()) { - theme.border = ThemesGetBorder(border_names[selected_border_index]); - } - if (static_cast(selected_background_index) < background_names.size()) { - theme.background = ThemesGetBackground(background_names[selected_background_index]); - } - theme.back_board = board_backgrounds[selected_board_background]; - - // Add or replace the theme - ThemesAddOrReplace(theme); - - // Save to custom themes file - ThemesSaveCustomSlots(); - - std::cout << "Saved theme: " << name << "\n"; -} - -void ThemeEditorState::LoadBorder(const std::string& name) { - current_border = ThemesGetBorder(name); - - // Update UI fields - strncpy(border_name_buffer, current_border.name.c_str(), sizeof(border_name_buffer) - 1); - strncpy(border_sprite_buffer, current_border.border_sprite.c_str(), sizeof(border_sprite_buffer) - 1); - border_offset_x = current_border.border_sprite_offset.first; - border_offset_y = current_border.border_sprite_offset.second; - score_offset_x = current_border.score_label_offset.first; - score_offset_y = current_border.score_label_offset.second; - time_offset_x = current_border.time_label_offset.first; - time_offset_y = current_border.time_label_offset.second; - chain_offset_x = current_border.chain_label_offset.first; - chain_offset_y = current_border.chain_label_offset.second; - speed_offset_x = current_border.speed_label_offset.first; - speed_offset_y = current_border.speed_label_offset.second; - score_field_x = current_border.score_field_offset.first; - score_field_y = current_border.score_field_offset.second; - time_field_x = current_border.time_field_offset.first; - time_field_y = current_border.time_field_offset.second; - chain_field_x = current_border.chain_field_offset.first; - chain_field_y = current_border.chain_field_offset.second; - speed_field_x = current_border.speed_field_offset.first; - speed_field_y = current_border.speed_field_offset.second; - preview_needs_update = true; -} - -void ThemeEditorState::LoadBackground(const std::string& name) { - current_background = ThemesGetBackground(name); - - // Update UI fields - strncpy(background_name_buffer, current_background.name.c_str(), sizeof(background_name_buffer) - 1); - strncpy(background_sprite_buffer, current_background.background_sprite.c_str(), sizeof(background_sprite_buffer) - 1); - strncpy(background_sprite_16x9_buffer, current_background.background_sprite_16x9.c_str(), sizeof(background_sprite_16x9_buffer) - 1); - - // Convert scale mode - switch (current_background.background_scale) { - case ImgScale::Stretch: background_scale_mode = 0; break; - case ImgScale::Tile: background_scale_mode = 1; break; - case ImgScale::Resize: background_scale_mode = 2; break; - case ImgScale::Cut: background_scale_mode = 3; break; - } - - tile_move_speed_x = current_background.tileMoveSpeedX; - tile_move_speed_y = current_background.tileMoveSpeedY; - preview_needs_update = true; -} - -bool ThemeEditorState::ValidateSprite(const std::string& sprite_name) { - return ThemesValidateSpriteName(sprite_name); -} - -std::vector ThemeEditorState::GetAvailableSprites() { - // TODO: Implement getting available sprite names from globalData.spriteHolder - return std::vector(); -} diff --git a/source/code/theme_editor/ThemeEditorState.hpp b/source/code/theme_editor/ThemeEditorState.hpp deleted file mode 100644 index 9152dda4..00000000 --- a/source/code/theme_editor/ThemeEditorState.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -=========================================================================== -blockattack - Block Attack - Rise of the Blocks -Copyright (C) 2005-2026 Poul Sander - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see http://www.gnu.org/licenses/ - -Source information and contacts persons can be found at -https://blockattack.net -=========================================================================== -*/ - -#pragma once - -#include "../global.hpp" -#include "../sago/GameStateInterface.hpp" -#include "../themes.hpp" -#include -#include -#include - -class BlockGameSdl; - -class ThemeEditorState : public sago::GameStateInterface { -public: - ThemeEditorState(); - ThemeEditorState(const ThemeEditorState& orig) = delete; - virtual ~ThemeEditorState(); - - void Init(); - - bool IsActive() override; - void ProcessInput(const SDL_Event& event, bool& processed) override; - void Draw(SDL_Renderer* target) override; - void Update() override; - -private: - void DrawBorderEditor(); - void DrawBackgroundEditor(); - void DrawThemeComposer(); - void DrawPreview(SDL_Renderer* target); - - void SaveCurrentBorder(); - void SaveCurrentBackground(); - void SaveCurrentTheme(); - - void LoadBorder(const std::string& name); - void LoadBackground(const std::string& name); - - bool ValidateSprite(const std::string& sprite_name); - std::vector GetAvailableSprites(); - - bool isActive = true; - int current_tab = 0; // 0=Border, 1=Background, 2=Theme - - // Border editor state - ThemeBorderData current_border; - char border_name_buffer[256] = ""; - char border_sprite_buffer[256] = "board_back_back"; - int border_offset_x = -60; - int border_offset_y = -68; - int score_offset_x = 310; - int score_offset_y = 80; - int time_offset_x = 310; - int time_offset_y = 129; - int chain_offset_x = 310; - int chain_offset_y = 178; - int speed_offset_x = 310; - int speed_offset_y = 227; - int score_field_x = 310; - int score_field_y = 102; - int time_field_x = 310; - int time_field_y = 151; - int chain_field_x = 310; - int chain_field_y = 200; - int speed_field_x = 310; - int speed_field_y = 249; - - // Background editor state - BackGroundData current_background; - char background_name_buffer[256] = ""; - char background_sprite_buffer[256] = "background"; - char background_sprite_16x9_buffer[256] = "background_sixteen_nine"; - int background_scale_mode = 0; // 0=Stretch, 1=Tile, 2=Resize, 3=Cut - int tile_move_speed_x = 0; - int tile_move_speed_y = 0; - - // Theme composer state - char theme_name_buffer[256] = "custom_theme"; - int selected_border_index = 0; - int selected_background_index = 0; - int selected_decoration_index = 0; - int selected_board_background = 0; // 0=back_board, 1=back_board_sample_snow, 2=trans_cover - std::vector border_names; - std::vector background_names; - std::vector decoration_names; - const std::vector board_backgrounds = {"back_board", "back_board_sample_snow", "trans_cover"}; - - // Preview - std::shared_ptr preview_game; - Theme preview_theme; - Theme saved_theme; // Backup of the global theme - bool preview_needs_update = true; - void UpdatePreview(); -}; diff --git a/source/code/themes.cpp b/source/code/themes.cpp index e8cce004..8637a75a 100644 --- a/source/code/themes.cpp +++ b/source/code/themes.cpp @@ -24,7 +24,6 @@ Source information and contacts persons can be found at #include "themes.hpp" #include "sago/SagoMisc.hpp" -#include "os.hpp" #include #include #include @@ -99,15 +98,26 @@ void to_json(json& j, const ThemeBorderData& p) { } void to_json(json& j, const Theme& p) { - j = json{ {"theme_name", p.theme_name}, {"back_board", p.back_board}, {"background_name", p.background.name}, {"decoration_name", p.decoration.name} }; + j = json{ {"theme_name", p.theme_name}, {"back_board", p.back_board}, {"background_name", p.background.name}, {"decoration_name", p.decoration.name}, {"border_name", p.border.name}, {"garbage_name", p.garbage.name} }; } void to_json(json& j, const DecorationData& p) { j = json{ {"name", p.name}, {"decoration_sprites", p.decoration_sprites} }; } +void to_json(json& j, const GarbageData& p) { + j = json{ + {"garbage_name", p.name}, + {"tl", p.tl}, {"t", p.t}, {"tr", p.tr}, + {"l", p.l}, {"fill", p.fill}, {"r", p.r}, + {"bl", p.bl}, {"b", p.b}, {"br", p.br}, + {"ml", p.ml}, {"m", p.m}, {"mr", p.mr}, + {"gml", p.gml}, {"gm", p.gm}, {"gmr", p.gmr} + }; +} + void to_json(json& j, const ThemeFileData& p) { - j = json{ {"background_data", p.background_data}, {"decoration_data", p.decoration_data}, {"border_data", p.border_data}, {"themes", p.themes} }; + j = json{ {"background_data", p.background_data}, {"decoration_data", p.decoration_data}, {"border_data", p.border_data}, {"garbage_data", p.garbage_data}, {"themes", p.themes} }; } void from_json(const json& j, BackGroundData& p) { @@ -165,6 +175,12 @@ void from_json(const json& j, Theme& p) { j.at("back_board").get_to(p.back_board); j.at("background_name").get_to(p.background.name); j.at("decoration_name").get_to(p.decoration.name); + if (j.contains("border_name")) { + j.at("border_name").get_to(p.border.name); + } + if (j.contains("garbage_name")) { + j.at("garbage_name").get_to(p.garbage.name); + } } void from_json(const json& j, DecorationData& p) { @@ -172,6 +188,22 @@ void from_json(const json& j, DecorationData& p) { j.at("decoration_sprites").get_to(p.decoration_sprites); } +void from_json(const json& j, GarbageData& p) { + j.at("garbage_name").get_to(p.name); + const std::pair sprites[] = { + {"tl", &p.tl}, {"t", &p.t}, {"tr", &p.tr}, + {"l", &p.l}, {"fill", &p.fill}, {"r", &p.r}, + {"bl", &p.bl}, {"b", &p.b}, {"br", &p.br}, + {"ml", &p.ml}, {"m", &p.m}, {"mr", &p.mr}, + {"gml", &p.gml}, {"gm", &p.gm}, {"gmr", &p.gmr} + }; + for (const auto& sprite : sprites) { + if (j.contains(sprite.first)) { + j.at(sprite.first).get_to(*sprite.second); + } + } +} + void from_json(const json& j, ThemeFileData& p) { if (j.contains("background_data")) { j.at("background_data").get_to(p.background_data); @@ -182,6 +214,9 @@ void from_json(const json& j, ThemeFileData& p) { if (j.contains("border_data")) { j.at("border_data").get_to(p.border_data); } + if (j.contains("garbage_data")) { + j.at("garbage_data").get_to(p.garbage_data); + } if (j.contains("themes")) { j.at("themes").get_to(p.themes); } @@ -193,6 +228,7 @@ static std::vector themes(1); static std::map background_data; static std::map decoration_data; static std::map border_data; +static std::map garbage_data; static bool initialized = false; static size_t current_theme = 0; @@ -203,6 +239,21 @@ void ThemesFillMissingFields(Theme& theme) { theme.background.name = "standard"; } theme.background = background_data[theme.background.name]; + if (border_data.find(theme.border.name) == border_data.end()) { + //If the theme does not define a border then use the standard. + theme.border.name = "standard"; + } + theme.border = border_data[theme.border.name]; + if (decoration_data.find(theme.decoration.name) == decoration_data.end()) { + //If the theme does not define a decoration then use the standard smilies. + theme.decoration.name = "smilies"; + } + theme.decoration = decoration_data[theme.decoration.name]; + if (garbage_data.find(theme.garbage.name) == garbage_data.end()) { + //If the theme does not define a garbage set then use the standard. + theme.garbage.name = "standard"; + } + theme.garbage = garbage_data[theme.garbage.name]; } static void ThemesReadDataFromFile(const std::string& filename) { @@ -225,6 +276,9 @@ static void ThemesReadDataFromFile(const std::string& filename) { for (const auto& border : tfd.border_data) { border_data[border.name] = border; } + for (const auto& garbage : tfd.garbage_data) { + garbage_data[garbage.name] = garbage; + } for (const Theme& theme : tfd.themes) { if (globalData.verboseLevel) { std::cout << "Adding theme " << theme.theme_name << "\n"; @@ -267,6 +321,11 @@ static void ThemesInitBorderData() { border_data[mirror.name] = mirror; } +static void ThemesInitGarbageData() { + GarbageData standard; + garbage_data[standard.name] = standard; +} + static bool str_startswith(const std::string& s, const char* prefix) { return (s.rfind(prefix, 0) == 0); } @@ -283,6 +342,9 @@ static void ThemesDumpData() { for (auto& pair : border_data) { tfd.border_data.push_back(pair.second); } + for (auto& pair : garbage_data) { + tfd.garbage_data.push_back(pair.second); + } json j = tfd; std::string s = j.dump(4); sago::WriteFileContent("themes_dump.json", s); @@ -322,6 +384,7 @@ void ThemesInit() { ThemesInitBackGroundData(); ThemesInitCustomBackgrounds(); ThemesInitBorderData(); + ThemesInitGarbageData(); ThemesLoadCustomBorders(); ThemesLoadCustomBackgrounds(); themes.resize(1); //Add the default theme @@ -434,6 +497,20 @@ ThemeBorderData ThemesGetNextBorder(const std::string& current) { return ret; } +GarbageData ThemesGetNextGarbage(const std::string& current) { + ThemesInit(); + GarbageData ret = garbage_data["standard"]; + auto it = garbage_data.find(current); + if (it != garbage_data.end()) { + ++it; + if (it == garbage_data.end()) { + it = garbage_data.begin(); + } + ret = it->second; + } + return ret; +} + void ThemesInitCustomBackgrounds() { std::vector custom_backgrounds = sago::GetFileList("textures/backgrounds"); std::stringstream sprite_stream; @@ -469,34 +546,6 @@ void ThemesInitCustomBackgrounds() { sago::WriteFileContent("sprites/custom_backgrounds.sprite", sprite_stream.str()); } -void ThemesSaveBorderData(const std::string& name, const ThemeBorderData& data) { - ThemesInit(); - // Create borders directory if it doesn't exist - OsCreateFolder("borders"); - - json j = data; - std::string s = j.dump(4); - std::string filename = fmt::format("borders/{}.json", name); - sago::WriteFileContent(filename.c_str(), s); - - // Add to the border_data map - border_data[name] = data; -} - -void ThemesSaveBackgroundData(const std::string& name, const BackGroundData& data) { - ThemesInit(); - // Create backgrounds directory if it doesn't exist - OsCreateFolder("backgrounds"); - - json j = data; - std::string s = j.dump(4); - std::string filename = fmt::format("backgrounds/{}.json", name); - sago::WriteFileContent(filename.c_str(), s); - - // Add to the background_data map - background_data[name] = data; -} - void ThemesLoadCustomBorders() { const std::vector& border_files = sago::GetFileList("borders"); for (const std::string& filename : border_files) { @@ -544,50 +593,3 @@ void ThemesLoadCustomBackgrounds() { } } } - -bool ThemesValidateSpriteName(const std::string& sprite_name) { - ThemesInit(); - if (sprite_name.empty()) { - return false; - } - // Note: GetSprite returns a default sprite if not found, so we can't easily validate - // The sprite will be validated when actually used in the game - // For now, just check that it's not empty - return true; -} - -std::vector ThemesGetBorderNames() { - ThemesInit(); - std::vector names; - for (const auto& pair : border_data) { - names.push_back(pair.first); - } - return names; -} - -std::vector ThemesGetBackgroundNames() { - ThemesInit(); - std::vector names; - for (const auto& pair : background_data) { - names.push_back(pair.first); - } - return names; -} - -std::vector ThemesGetDecorationNames() { - ThemesInit(); - std::vector names; - for (const auto& pair : decoration_data) { - names.push_back(pair.first); - } - return names; -} - -BackGroundData ThemesGetBackground(const std::string& name) { - ThemesInit(); - auto it = background_data.find(name); - if (it == background_data.end()) { - return background_data["standard"]; - } - return it->second; -} \ No newline at end of file diff --git a/source/code/themes.hpp b/source/code/themes.hpp index f2601b73..6b24de90 100644 --- a/source/code/themes.hpp +++ b/source/code/themes.hpp @@ -63,18 +63,39 @@ struct DecorationData { std::vector decoration_sprites = {"smileys0", "smileys1", "smileys2", "smileys3"}; }; +struct GarbageData { + std::string name = "standard"; + std::string tl = "garbage_tl"; + std::string t = "garbage_t"; + std::string tr = "garbage_tr"; + std::string l = "garbage_l"; + std::string fill = "garbage_fill"; + std::string r = "garbage_r"; + std::string bl = "garbage_bl"; + std::string b = "garbage_b"; + std::string br = "garbage_br"; + std::string ml = "garbage_ml"; + std::string m = "garbage_m"; + std::string mr = "garbage_mr"; + std::string gml = "garbage_gml"; + std::string gm = "garbage_gm"; + std::string gmr = "garbage_gmr"; +}; + struct Theme { std::string theme_name = "standard"; std::string back_board = "back_board"; // Can also be "back_board_sample_snow" or "trans_cover" BackGroundData background; //Serialized as background.background_name DecorationData decoration; ThemeBorderData border; + GarbageData garbage; }; struct ThemeFileData { std::vector background_data; std::vector decoration_data; std::vector border_data; + std::vector garbage_data; std::vector themes; }; @@ -123,21 +144,9 @@ ThemeBorderData ThemesGetBorder(const std::string& name); ThemeBorderData ThemesGetNextBorder(const std::string& current); -void ThemesInitCustomBackgrounds(); - -/** - * @brief Saves a border to the borders/ directory - * @param name The name of the border - * @param data The border data to save - */ -void ThemesSaveBorderData(const std::string& name, const ThemeBorderData& data); +GarbageData ThemesGetNextGarbage(const std::string& current); -/** - * @brief Saves a background to the backgrounds/ directory - * @param name The name of the background - * @param data The background data to save - */ -void ThemesSaveBackgroundData(const std::string& name, const BackGroundData& data); +void ThemesInitCustomBackgrounds(); /** * @brief Loads custom borders from the borders/ directory @@ -148,35 +157,3 @@ void ThemesLoadCustomBorders(); * @brief Loads custom backgrounds from the backgrounds/ directory */ void ThemesLoadCustomBackgrounds(); - -/** - * @brief Validates that a sprite name exists in the sprite holder - * @param sprite_name The sprite name to validate - * @return true if the sprite exists, false otherwise - */ -bool ThemesValidateSpriteName(const std::string& sprite_name); - -/** - * @brief Gets all available border names - * @return Vector of border names - */ -std::vector ThemesGetBorderNames(); - -/** - * @brief Gets all available background names - * @return Vector of background names - */ -std::vector ThemesGetBackgroundNames(); - -/** - * @brief Gets all available decoration names - * @return Vector of decoration names - */ -std::vector ThemesGetDecorationNames(); - -/** - * @brief Gets a background by name - * @param name The background name - * @return The background data - */ -BackGroundData ThemesGetBackground(const std::string& name); \ No newline at end of file