From d1d72c49f5c986b8ae1a716e8ff03a2df601c819 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 14 Mar 2025 04:38:56 -0500 Subject: [PATCH 01/36] Add gui elements --- GUI.cpp | 144 ++++++++++++++++++++++++++++++++++++++++++++++ GUI.h | 9 +++ StonesenseState.h | 2 + UserInput.cpp | 36 +++++++----- UserInput.h | 1 + main.cpp | 9 +++ 6 files changed, 186 insertions(+), 15 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 697cf530..e22315a7 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "common.h" #include "Tile.h" @@ -23,6 +24,8 @@ #include "GameConfiguration.h" #include "StonesenseState.h" +#include "UserInput.h" + #include "modules/Screen.h" #include "modules/Units.h" #include "DataDefs.h" @@ -164,6 +167,135 @@ class ImageFileList } IMGFileList; +// Base GUI Element Class +class GUIElement { +public: + int x, y, w, h; // Position and size of the element + bool hovered; // Tracks if the mouse is over the element + std::unordered_set visibleStates; + + GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) + : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) { + } + + virtual void onClick() {} + + virtual void onHover() {} + + virtual void onHoverExit() {} + + virtual void onScroll(int deltaY) {} + + virtual void draw() {} + + void update() { + al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); + if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { + draw(); + } + } + + bool isMouseOver(int mouseX, int mouseY) { + return mouseX >= x && mouseX <= x + w && + mouseY >= y && mouseY <= y + h; + } + + void updateHoverState(int mouseX, int mouseY) { + bool mouseOver = isMouseOver(mouseX, mouseY); + if (mouseOver && !hovered) { + onHover(); + } + else if (!mouseOver && hovered) { + onHoverExit(); + } + hovered = mouseOver; + } + +}; + +// Function pointer type +typedef void (*OnClickCallback)(uint32_t); + +class Button : public GUIElement { +public: + int32_t borderColor; + int32_t bgColor; + OnClickCallback onClickCallback; // Function pointer for the callback + + Button(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback clickFn, std::unordered_set visibleStates) + : GUIElement(x, y, w, h, visibleStates), borderColor(borderColor), bgColor(bgColor), onClickCallback(clickFn) { + } + + void onClick() override { + if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { + if (onClickCallback) { + onClickCallback(getKeyMods(&stonesenseState.keyboard)); // Pass a uint32_t argument when the button is clicked + } + } + } + + void draw() override { + al_draw_filled_rectangle(x, y, x + w, y + h, uiColor(bgColor, hovered)); + al_draw_rectangle(x, y, x + w, y + h, uiColor(borderColor, hovered), 2); + } +}; + + +// Global list of GUI elements +std::vector elements; + +void updateAll() { + for (auto* elem : elements) { + elem->update(); + } +} + +// Handle mouse click events +void handleMouseClick(int mouseX, int mouseY) { + for (auto* elem : elements) { + if (elem->isMouseOver(mouseX, mouseY)) { + elem->onClick(); + break; + } + } +} + +// Handle mouse release events +void handleMouseRelease() { + for (auto* elem : elements) { + + } +} + +// Handle mouse drag events +void handleMouseDrag(int mouseX, int mouseY) { + for (auto* elem : elements) { + + } +} + +// Handle mouse movement for hover state +void handleMouseMove(int mouseX, int mouseY) { + for (auto* elem : elements) { + elem->updateHoverState(mouseX, mouseY); + } +} + +// Handle mouse wheel scrolling +void handleMouseWheel(int mouseX, int mouseY, int deltaY) { + for (auto* elem : elements) { + if (elem->isMouseOver(mouseX, mouseY)) { + + } + } +} + +void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates) { + elements.push_back(new Button(x, y, w, h, borderColor, bgColor, onClickCallback, visibleStates)); +} + + + namespace { void ScreenToPoint(int inx, int iny, int& x1, int& y1, int& z1, int segSizeX, int segSizeY, int segSizeZ, int ScreenW, int ScreenH) @@ -933,12 +1065,15 @@ void paintboard() stonesenseState.stoneSenseTimers.frame_total.update(donetime - stonesenseState.stoneSenseTimers.prev_frame_time); stonesenseState.stoneSenseTimers.prev_frame_time = donetime; + stonesenseState.UIState = "DEFAULT"; if (ssConfig.show_announcements) { + stonesenseState.UIState = "INFO_PANEL/ANNOUNCEMENTS"; al_hold_bitmap_drawing(true); draw_announcements(font, ssState.ScreenW, ssState.ScreenH - 10 - al_get_font_line_height(font), ALLEGRO_ALIGN_RIGHT, df::global::world->status.announcements); al_hold_bitmap_drawing(false); } if(ssConfig.show_keybinds){ + stonesenseState.UIState = "INFO_PANEL/KEYBINDS"; std::string *keyname, *actionname; keyname = actionname = NULL; int line = 1; @@ -955,7 +1090,9 @@ void paintboard() } al_hold_bitmap_drawing(false); } else if (ssConfig.config.show_osd) { + stonesenseState.UIState = "OSD"; al_hold_bitmap_drawing(true); + draw_textf_border(font, uiColor(dfColors::white), 10,fontHeight, 0, "%i,%i,%i, r%i, z%i", ssState.Position.x,ssState.Position.y,ssState.Position.z, ssState.Rotation, ssConfig.zoom); drawSelectionCursor(segment); @@ -965,6 +1102,7 @@ void paintboard() drawAdvmodeMenuTalk(font, 5, ssState.ScreenH - 5); if(ssConfig.config.debug_mode) { + stonesenseState.UIState = "DEBUG"; auto& contentLoader = stonesenseState.contentLoader; draw_textf_border(font, uiColor(dfColors::white), 10, 3*fontHeight, 0, "Map Read Time: %.2fms", clockToMs(stonesenseState.stoneSenseTimers.read_time)); draw_textf_border(font, uiColor(dfColors::white), 10, 4*fontHeight, 0, "Map Beautification Time: %.2fms", clockToMs(stonesenseState.stoneSenseTimers.beautify_time)); @@ -996,9 +1134,15 @@ void paintboard() top += fontHeight; draw_textf_border(font, uiColor(dfColors::white), ssState.ScreenW/2,top, ALLEGRO_ALIGN_CENTRE, "Reloading every %0.1fs", (float)ssConfig.config.automatic_reload_time/1000); } + al_hold_bitmap_drawing(false); DrawMinimap(segment); } + + al_hold_bitmap_drawing(true); + updateAll(); + al_hold_bitmap_drawing(false); + stonesenseState.map_segment.unlockDraw(); } diff --git a/GUI.h b/GUI.h index d1f65494..6e1a1dc7 100644 --- a/GUI.h +++ b/GUI.h @@ -2,6 +2,15 @@ #include "common.h" #include +#include + +// GUI Element stuffs +void handleMouseClick(int mouseX, int mouseY); +void handleMouseMove(int mouseX, int mouseY); +void handleMouseWheel(int mouseX, int mouseY, int deltaY); +typedef void (*OnClickCallback)(uint32_t); +void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates); + void ScreenToPoint(int x,int y,int &x1, int &y1, int &z1); void pointToScreen(int *inx, int *iny, int inz); diff --git a/StonesenseState.h b/StonesenseState.h index 2b5c4672..a162b224 100644 --- a/StonesenseState.h +++ b/StonesenseState.h @@ -37,6 +37,8 @@ class StonesenseState ALLEGRO_MOUSE_STATE mouse; int randomCube[RANDOM_CUBE][RANDOM_CUBE][RANDOM_CUBE]; + std::string UIState; + std::unique_ptr currentMap; std::unique_ptr contentLoader; diff --git a/UserInput.cpp b/UserInput.cpp index 77bcda81..73c2f354 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -135,23 +135,28 @@ void doMouse() //mouse_callback = mouseProc; static int last_mouse_z; auto& mouse = stonesenseState.mouse; - if(mouse.z < last_mouse_z) { - if(ssConfig.config.invert_mouse_z) { - action_incrZ(keymod); + handleMouseMove(mouse.x, mouse.y); + if (mouse.z != last_mouse_z) { + auto deltaY = last_mouse_z - mouse.z; + if(mouse.z < last_mouse_z) { + if(ssConfig.config.invert_mouse_z) { + action_incrZ(keymod); + } + else { + action_decrZ(keymod); + } + last_mouse_z = mouse.z; } - else { - action_decrZ(keymod); + if(mouse.z > last_mouse_z) { + if(ssConfig.config.invert_mouse_z) { + action_decrZ(keymod); + } + else { + action_incrZ(keymod); + } + last_mouse_z = mouse.z; } - last_mouse_z = mouse.z; - } - if(mouse.z > last_mouse_z) { - if(ssConfig.config.invert_mouse_z) { - action_decrZ(keymod); - } - else { - action_incrZ(keymod); - } - last_mouse_z = mouse.z; + handleMouseWheel(mouse.x, mouse.y, deltaY); } if( mouse.buttons & 2 ) { ssConfig.config.track_mode = Config::TRACKING_NONE; @@ -175,6 +180,7 @@ void doMouse() int x, y; x = mouse.x;//pos >> 16; y = mouse.y; //pos & 0x0000ffff; + handleMouseClick(x, y); if(x >= stonesenseState.MiniMapTopLeftX && x <= stonesenseState.MiniMapBottomRightX && y >= stonesenseState.MiniMapTopLeftY && diff --git a/UserInput.h b/UserInput.h index 5a5e3cfe..055af66e 100644 --- a/UserInput.h +++ b/UserInput.h @@ -44,3 +44,4 @@ void action_incrZ(uint32_t keymod); extern bool isRepeatable(int32_t keycode); extern bool doKey(int32_t keycode, uint32_t keymodcode); +extern int32_t getKeyMods(ALLEGRO_KEYBOARD_STATE* keyboardstate); diff --git a/main.cpp b/main.cpp index c5d1802c..8e5e9666 100644 --- a/main.cpp +++ b/main.cpp @@ -19,6 +19,9 @@ #include "GroundMaterialConfiguration.h" #include "ContentLoader.h" #include "OcclusionTest.h" + +#include "UserInput.h" + #include "GameConfiguration.h" #include "GameState.h" #include "StonesenseState.h" @@ -208,6 +211,10 @@ void drawcredits() // Make the backbuffer visible } +void addElems() { + addButton(0, 0, 20, 20, dfColors::blue, dfColors::green, action_togglekeybinds, { "DEFAULT", "INFO_PANEL/ANNOUNCEMENTS" }); +} + /* main_loop: * The main loop of the program. Here we wait for events to come in from * any one of the event sources and react to each one accordingly. While @@ -220,6 +227,8 @@ static void main_loop(ALLEGRO_DISPLAY * display, ALLEGRO_EVENT_QUEUE *queue, ALL auto& ssConfig = stonesenseState.ssConfig; ALLEGRO_EVENT event; + + addElems(); while (!al_get_thread_should_stop(main_thread)) { if (redraw && al_event_queue_is_empty(queue)) { From 87b5e4ac2f386985eb1018963b9cea04b1939a56 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 14 Mar 2025 04:40:05 -0500 Subject: [PATCH 02/36] create info panel --- Config.h | 1 + GUI.cpp | 154 +++++++++++++++++++++++++++++++++++++++++----- GameState.h | 11 ++++ StonesenseState.h | 10 +++ UserInput.cpp | 15 ++++- main.cpp | 7 +++ 6 files changed, 182 insertions(+), 16 deletions(-) diff --git a/Config.h b/Config.h index 3c1a219a..fbea6fda 100644 --- a/Config.h +++ b/Config.h @@ -46,6 +46,7 @@ class Config { bool names_use_nick = true; bool names_use_species = true; bool show_osd = false; + bool show_info_panel = true; bool cache_images = false; bool show_stockpiles = true; bool show_zones = true; diff --git a/GUI.cpp b/GUI.cpp index 697cf530..5698d9e5 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -353,24 +355,84 @@ void draw_color_ustr_border(const ALLEGRO_FONT* font, ALLEGRO_COLOR text_color, draw_color_border(font, text_color, border_color, x, y, flags, ustr); } -namespace -{ - void draw_report_border(const ALLEGRO_FONT* font, float x, float y, int flags, const df::report* report) - { - auto& ssConfig = stonesenseState.ssConfig; +std::string fitTextToWidth(const std::string& input, ALLEGRO_FONT* font, int width) { + std::string allCurrentChars; // The part of the string that fits so far - ALLEGRO_COLOR color = ssConfig.config.colors.getDfColor(report->color, ssConfig.config.useDfColors); - draw_text_border(font, color, x, y, flags, DF2UTF(report->text).c_str()); + for (char nextChar : input) { + std::string testString = allCurrentChars + nextChar + "."; // Test adding the next char plus a dot + + if (al_get_text_width(font, testString.c_str()) > width) { + return allCurrentChars + "."; // If too wide, return what we have with a dot + } + + allCurrentChars += nextChar; // Append the character if it fits } + return allCurrentChars; // If we reached the end, return the full string +} + + +std::vector splitLinesToWidth(const std::string& input,const ALLEGRO_FONT* font, int width) { + std::vector splits; // To store each part of the split string + std::string allCurrentChars; // The part of the string that fits so far + + // Split input into words + std::istringstream stream(input); + std::string word; + + while (stream >> word) { + // Test if adding this word to the current string would exceed the width + std::string testString = allCurrentChars + (allCurrentChars.empty() ? "" : " ") + word; + + if (al_get_text_width(font, testString.c_str()) > width) { + // If it's too wide, stop and push the current string into the vector + if (!allCurrentChars.empty()) { + splits.push_back(allCurrentChars); + } + allCurrentChars = word; // Start a new line with the current word + } + else { + // If it fits, append the word + if (!allCurrentChars.empty()) { + allCurrentChars += " "; + } + allCurrentChars += word; + } + } + + // Add any remaining text to the vector + if (!allCurrentChars.empty()) { + splits.push_back(allCurrentChars); + } + + return splits; +} + + +namespace +{ void draw_announcements(const ALLEGRO_FONT* font, float x, float y, int flags, std::vector& announcements) { + auto& ssConfig = stonesenseState.ssConfig; + const int numAnnouncements = (int)announcements.size(); const int maxAnnouncements = std::min(10, numAnnouncements); - for (int i = numAnnouncements - 1; i >= (numAnnouncements - maxAnnouncements) && announcements[i]->duration > 0; i--) + int line = 0; + int offset = 0; + + int fontHeight = al_get_font_line_height(font); + int tabHeight = fontHeight + 10; // Padding for visuals + + for (int i = numAnnouncements - 1; /*i >= (numAnnouncements - maxAnnouncements) && */announcements[i]->duration > 0; i--) { - int offset = ((numAnnouncements - 1) - i) * al_get_font_line_height(font); - draw_report_border(font, x, y - offset, flags, announcements[i]); + ALLEGRO_COLOR color = ssConfig.config.colors.getDfColor(announcements[i]->color, ssConfig.config.useDfColors); + auto reportStr = DF2UTF(announcements[i]->text).c_str(); + auto splits = splitLinesToWidth(reportStr, font, stonesenseState.ssState.InfoW - 20); + for (auto split : splits) { + offset = (line * fontHeight); + draw_text_border(font, color, x, y + offset, flags, split.c_str()); + line++; + } } } } @@ -815,6 +877,51 @@ ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteShe return al_create_sub_bitmap(spriteSheet, sheetx * SPRITEWIDTH, sheety * SPRITEHEIGHT, SPRITEWIDTH, SPRITEHEIGHT); } +void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex) { + int numTabs = 2; + int tabWidth = stonesenseState.ssState.InfoW / numTabs; + int fontHeight = al_get_font_line_height(font); + int tabHeight = fontHeight + 10; // Padding for visuals + int panelX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW; // Left edge of info panel + int tabX = panelX + (tabIndex * tabWidth); // Position inside the panel + int tabY = stonesenseState.ssState.ScreenH - tabHeight; // Flush with bottom + + // Determine colors based on selection + bool isSelected = (stonesenseState.ssState.selectedTab == tabIndex); + ALLEGRO_COLOR bgColor = uiColor(dfColors::black, isSelected); // White if selected, black otherwise + ALLEGRO_COLOR textColor = uiColor(dfColors::lgray, isSelected); // Black text if selected, white otherwise + + // Truncate label to fit inside tab + std::string truncatedLabel = fitTextToWidth(label, font, tabWidth - 20); + + // Draw filled rounded rectangle (tab background) + al_draw_filled_rounded_rectangle(tabX, tabY, tabX + tabWidth, tabY + tabHeight, 10, 10, bgColor); + + // Draw tab border - always yellow + al_draw_rounded_rectangle(tabX, tabY, tabX + tabWidth, tabY + tabHeight, 10, 10, uiColor(dfColors::yellow), 2); + + // Draw text centered inside tab + al_draw_text(font, textColor, tabX + tabWidth / 2, tabY + (tabHeight - fontHeight) / 2, ALLEGRO_ALIGN_CENTER, truncatedLabel.c_str()); +} + + +void drawInfoPanel() { + auto font = stonesenseState.font; + auto fontHeight = al_get_font_line_height(font); + int tabWidth = (stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW) / 2; + int halftabHeight = (fontHeight + 10)/2; + int bottomY = stonesenseState.ssState.ScreenH - 2; + + //draw panel + al_draw_filled_rectangle(stonesenseState.ssState.ScreenW, 0, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::black)); + al_draw_rectangle(stonesenseState.ssState.ScreenW, 2, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::yellow), 2); + + //draw tabs + drawTab(font, "Announcements",0); + drawTab(font, "Keybinds", 1); +} + + void DrawSpriteIndexOverlay(int imageIndex) { auto currentImage = IMGFileList.lookup(imageIndex); @@ -933,9 +1040,12 @@ void paintboard() stonesenseState.stoneSenseTimers.frame_total.update(donetime - stonesenseState.stoneSenseTimers.prev_frame_time); stonesenseState.stoneSenseTimers.prev_frame_time = donetime; + if (ssConfig.config.show_info_panel) { + drawInfoPanel(); + } if (ssConfig.show_announcements) { al_hold_bitmap_drawing(true); - draw_announcements(font, ssState.ScreenW, ssState.ScreenH - 10 - al_get_font_line_height(font), ALLEGRO_ALIGN_RIGHT, df::global::world->status.announcements); + draw_announcements(font, ssState.ScreenW - 10, 10/*ssState.ScreenH - 10 - al_get_font_line_height(font)*/, ALLEGRO_ALIGN_RIGHT, df::global::world->status.announcements); al_hold_bitmap_drawing(false); } if(ssConfig.show_keybinds){ @@ -946,8 +1056,22 @@ void paintboard() for(int32_t i=1; true; i++){ if(getKeyStrings(i, keyname, actionname)){ - draw_textf_border(font, uiColor(dfColors::white), 10, line*fontHeight, 0, "%s: %s%s", keyname->c_str(), actionname->c_str(), isRepeatable(i) ? " (repeats)" : ""); - line++; + std::ostringstream oss; + oss << *keyname << ": " << *actionname; + if (isRepeatable(i)) { + oss << " (repeats)"; + } + std::string keyStr = oss.str(); + + int maxWidth = std::max(0, stonesenseState.ssState.InfoW - 20); + std::vector splitLines = splitLinesToWidth(keyStr, font, maxWidth); + + for (const auto& lineText : splitLines) { + draw_textf_border(font, uiColor(dfColors::white), ssState.ScreenW - 10, line * fontHeight, ALLEGRO_ALIGN_RIGHT, lineText.c_str()); + line++; + } + + } if(keyname == NULL) { break; @@ -997,7 +1121,9 @@ void paintboard() draw_textf_border(font, uiColor(dfColors::white), ssState.ScreenW/2,top, ALLEGRO_ALIGN_CENTRE, "Reloading every %0.1fs", (float)ssConfig.config.automatic_reload_time/1000); } al_hold_bitmap_drawing(false); - DrawMinimap(segment); + if (!ssConfig.config.show_info_panel) { + DrawMinimap(segment); + } } stonesenseState.map_segment.unlockDraw(); } diff --git a/GameState.h b/GameState.h index ca3178b3..a39ef440 100644 --- a/GameState.h +++ b/GameState.h @@ -22,4 +22,15 @@ struct GameState{ //the width and height of the stonesense window int ScreenW; int ScreenH; + + //info panel + int InfoW; + int InfoH; + int selectedTab = 0; + + enum tabs { + announcements, + keybinds, + }; + }; diff --git a/StonesenseState.h b/StonesenseState.h index 2b5c4672..a4da9fa2 100644 --- a/StonesenseState.h +++ b/StonesenseState.h @@ -43,6 +43,16 @@ class StonesenseState GameConfiguration ssConfig; GameState ssState; + int KeybindsTabTopLeftX; + int KeybindsTabTopLeftY; + int KeybindsTabBottomRightX; + int KeybindsTabBottomRightY; + + int AnnouncementsTabTopLeftX; + int AnnouncementsTabTopLeftY; + int AnnouncementsTabBottomRightX; + int AnnouncementsTabBottomRightY; + int MiniMapTopLeftX; int MiniMapTopLeftY; int MiniMapBottomRightX; diff --git a/UserInput.cpp b/UserInput.cpp index 77bcda81..7285fb2a 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -175,13 +175,18 @@ void doMouse() int x, y; x = mouse.x;//pos >> 16; y = mouse.y; //pos & 0x0000ffff; - if(x >= stonesenseState.MiniMapTopLeftX && + if (x >= stonesenseState.MiniMapTopLeftX && x <= stonesenseState.MiniMapBottomRightX && y >= stonesenseState.MiniMapTopLeftY && y <= stonesenseState.MiniMapBottomRightY) { // in minimap ssState.Position.x = (x- stonesenseState.MiniMapTopLeftX- stonesenseState.MiniMapSegmentWidth/2)/ stonesenseState.oneTileInPixels; ssState.Position.y = (y- stonesenseState.MiniMapTopLeftY- stonesenseState.MiniMapSegmentHeight/2)/ stonesenseState.oneTileInPixels; - } else { + }/*else if (x >= stonesenseState.KeybindsTabTopLeftX && + x <= stonesenseState.KeybindsTabBottomRightX && + y >= stonesenseState.KeybindsTabTopLeftY && + y <= stonesenseState.KeybindsTabBottomRightY) { + + }*/ else { int tilex,tiley,tilez; //get the point in the segment @@ -442,11 +447,17 @@ void action_toggleosd(uint32_t keymod) void action_togglekeybinds(uint32_t keymod){ auto& ssConfig = stonesenseState.ssConfig; ssConfig.show_keybinds = !ssConfig.show_keybinds; + ssConfig.config.show_info_panel = ssConfig.show_keybinds; + ssConfig.show_announcements = false; + stonesenseState.ssState.selectedTab = GameState::tabs::keybinds; } void action_toggleannouncements(uint32_t keymod) { auto& ssConfig = stonesenseState.ssConfig; ssConfig.show_announcements = !ssConfig.show_announcements; + ssConfig.config.show_info_panel = ssConfig.show_announcements; + ssConfig.show_keybinds = false; + stonesenseState.ssState.selectedTab = GameState::tabs::announcements; } void action_toggledebug(uint32_t keymod) diff --git a/main.cpp b/main.cpp index c5d1802c..26d81dac 100644 --- a/main.cpp +++ b/main.cpp @@ -167,6 +167,10 @@ void animUpdateProc() } } +int getInfoWidth() { + return int(stonesenseState.ssState.ScreenH / 2.25); +} + void drawcredits() { auto color_black = uiColor(dfColors::black); @@ -310,6 +314,7 @@ static void main_loop(ALLEGRO_DISPLAY * display, ALLEGRO_EVENT_QUEUE *queue, ALL redraw = true; stonesenseState.ssState.ScreenH = event.display.height; stonesenseState.ssState.ScreenW = event.display.width; + stonesenseState.ssState.InfoW = getInfoWidth(); if (!al_acknowledge_resize(event.display.source)) { con.printerr("Failed to resize diplay"); return; @@ -368,6 +373,7 @@ static void* stonesense_thread(ALLEGRO_THREAD* main_thread, void* parms) stonesenseState.ssState.ScreenH = stonesenseState.ssConfig.config.defaultScreenHeight; stonesenseState.ssState.ScreenW = stonesenseState.ssConfig.config.defaultScreenWidth; + stonesenseState.ssState.InfoW = getInfoWidth(); stonesenseState.ssState.Size = { DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE_Z }; stonesenseState.timeToReloadConfig = true; stonesenseState.contentLoader = std::make_unique(); @@ -424,6 +430,7 @@ static void* stonesense_thread(ALLEGRO_THREAD* main_thread, void* parms) // a resize event for us. stonesenseState.ssState.ScreenW = al_get_display_width(display); stonesenseState.ssState.ScreenH = al_get_display_height(display); + stonesenseState.ssState.InfoW = getInfoWidth(); if(!al_is_keyboard_installed()) { if (!al_install_keyboard()) { From ef6e66a68dcd7f2fa57b13068debe43c0899fccd Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 02:38:54 -0500 Subject: [PATCH 03/36] finish info panel rewrite --- GUI.cpp | 387 ++++++++++++++++++++++++++++------------------ GUI.h | 3 +- StonesenseState.h | 1 + UserInput.cpp | 20 +-- UserInput.h | 1 + main.cpp | 4 +- 6 files changed, 258 insertions(+), 158 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index f5dea23a..1d846473 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -169,135 +169,6 @@ class ImageFileList } IMGFileList; -// Base GUI Element Class -class GUIElement { -public: - int x, y, w, h; // Position and size of the element - bool hovered; // Tracks if the mouse is over the element - std::unordered_set visibleStates; - - GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) - : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) { - } - - virtual void onClick() {} - - virtual void onHover() {} - - virtual void onHoverExit() {} - - virtual void onScroll(int deltaY) {} - - virtual void draw() {} - - void update() { - al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); - if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { - draw(); - } - } - - bool isMouseOver(int mouseX, int mouseY) { - return mouseX >= x && mouseX <= x + w && - mouseY >= y && mouseY <= y + h; - } - - void updateHoverState(int mouseX, int mouseY) { - bool mouseOver = isMouseOver(mouseX, mouseY); - if (mouseOver && !hovered) { - onHover(); - } - else if (!mouseOver && hovered) { - onHoverExit(); - } - hovered = mouseOver; - } - -}; - -// Function pointer type -typedef void (*OnClickCallback)(uint32_t); - -class Button : public GUIElement { -public: - int32_t borderColor; - int32_t bgColor; - OnClickCallback onClickCallback; // Function pointer for the callback - - Button(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback clickFn, std::unordered_set visibleStates) - : GUIElement(x, y, w, h, visibleStates), borderColor(borderColor), bgColor(bgColor), onClickCallback(clickFn) { - } - - void onClick() override { - if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { - if (onClickCallback) { - onClickCallback(getKeyMods(&stonesenseState.keyboard)); // Pass a uint32_t argument when the button is clicked - } - } - } - - void draw() override { - al_draw_filled_rectangle(x, y, x + w, y + h, uiColor(bgColor, hovered)); - al_draw_rectangle(x, y, x + w, y + h, uiColor(borderColor, hovered), 2); - } -}; - - -// Global list of GUI elements -std::vector elements; - -void updateAll() { - for (auto* elem : elements) { - elem->update(); - } -} - -// Handle mouse click events -void handleMouseClick(int mouseX, int mouseY) { - for (auto* elem : elements) { - if (elem->isMouseOver(mouseX, mouseY)) { - elem->onClick(); - break; - } - } -} - -// Handle mouse release events -void handleMouseRelease() { - for (auto* elem : elements) { - - } -} - -// Handle mouse drag events -void handleMouseDrag(int mouseX, int mouseY) { - for (auto* elem : elements) { - - } -} - -// Handle mouse movement for hover state -void handleMouseMove(int mouseX, int mouseY) { - for (auto* elem : elements) { - elem->updateHoverState(mouseX, mouseY); - } -} - -// Handle mouse wheel scrolling -void handleMouseWheel(int mouseX, int mouseY, int deltaY) { - for (auto* elem : elements) { - if (elem->isMouseOver(mouseX, mouseY)) { - - } - } -} - -void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates) { - elements.push_back(new Button(x, y, w, h, borderColor, bgColor, onClickCallback, visibleStates)); -} - - - namespace { void ScreenToPoint(int inx, int iny, int& x1, int& y1, int& z1, int segSizeX, int segSizeY, int segSizeZ, int ScreenW, int ScreenH) @@ -540,6 +411,234 @@ std::vector splitLinesToWidth(const std::string& input,const ALLEGR return splits; } +// Base GUI Element Class +class GUIElement { +public: + int x, y, w, h; // Position and size of the element + bool hovered; // Tracks if the mouse is over the element + std::unordered_set visibleStates; + + GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) + : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) { + } + + virtual void onClick() {} + + virtual void onRelease() {} + + virtual void onHover() {} + + virtual void onHoverExit() { onRelease(); } + + virtual void onScroll(int deltaY) {} + + virtual void draw() {} + + void update() { + al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); + if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { + draw(); + } + } + + bool isMouseOver(int mouseX, int mouseY) { + return mouseX >= x && mouseX <= x + w && + mouseY >= y && mouseY <= y + h; + } + + void updateHoverState(int mouseX, int mouseY) { + bool mouseOver = isMouseOver(mouseX, mouseY); + + if (mouseOver && !hovered) { + onHover(); + } + else if (!mouseOver && hovered) { + onHoverExit(); + } + hovered = mouseOver; + } + +}; + +// Function pointer type +typedef void (*OnClickCallback)(uint32_t); + +class Button : public GUIElement { +public: + int32_t borderColor; + int32_t bgColor; + OnClickCallback onClickCallback; // Function pointer for the callback + bool held = false; + + Button(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback clickFn, std::unordered_set visibleStates) + : GUIElement(x, y, w, h, visibleStates), borderColor(borderColor), bgColor(bgColor), onClickCallback(clickFn) { + } + + void onClick() override { + if (!held) { + held = true; + if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { + if (onClickCallback) { + onClickCallback(getKeyMods(&stonesenseState.keyboard)); // Pass a uint32_t argument when the button is clicked + } + } + } + } + + void onRelease() override { + held = false; + } + + void draw() override { + al_draw_filled_rectangle(x, y, x + w, y + h, uiColor(bgColor, hovered)); + al_draw_rectangle(x, y, x + w, y + h, uiColor(borderColor, hovered), 2); + } +}; + +#include + +class LabeledButton : public Button { +public: + std::string label; + ALLEGRO_FONT* font; + + LabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, + const std::string& label, ALLEGRO_FONT* font, + OnClickCallback clickFn, std::unordered_set visibleStates) + : Button(x, y, w, h, borderColor, bgColor, clickFn, visibleStates), + label(label), font(font) { + } + + void draw() override { + Button::draw(); // Draw base button + + // Draw label text centered + if (font) { + int textWidth = al_get_text_width(font, label.c_str()); + int textHeight = al_get_font_line_height(font); + al_draw_text(font, uiColor(dfColors::white), x + (w / 2), y + (h - textHeight) / 2, + ALLEGRO_ALIGN_CENTER, label.c_str()); + } + } +}; + + + +class Tab : public Button { +public: + int tabIndex; + std::string label; + ALLEGRO_FONT* font; + + Tab(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, int tabIndex, const std::string& label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates) + : Button(x, y, w, h, borderColor, bgColor, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), font(font) { + } + + void draw() override { + bool isSelected = (stonesenseState.ssState.selectedTab == tabIndex); + ALLEGRO_COLOR bg = uiColor(bgColor, isSelected); + ALLEGRO_COLOR textColor = uiColor(dfColors::lgray, isSelected); + + al_draw_filled_rounded_rectangle(x, y, x + w, y + h, 10, 10, bg); + al_draw_rounded_rectangle(x, y, x + w, y + h, 10, 10, uiColor(borderColor), 2); + + std::string truncatedLabel = fitTextToWidth(label, font, w - 20); + al_draw_text(font, textColor, x + w / 2, y + (h - al_get_font_line_height(font)) / 2, ALLEGRO_ALIGN_CENTER, truncatedLabel.c_str()); + } + + void onClick() override { + stonesenseState.ssState.selectedTab = tabIndex; // Update selected tab + Button::onClick(); // Call the base class onClick() to execute any extra behavior + } +}; + + +// Global list of GUI elements +std::vector elements; + +void updateAll() { + for (auto* elem : elements) { + elem->update(); + } +} + +// Handle mouse click events +void handleMouseClick(int mouseX, int mouseY) { + for (auto* elem : elements) { + if (elem->isMouseOver(mouseX, mouseY)) { + elem->onClick(); + break; + } + } +} + +// Handle mouse release events +void handleMouseRelease() { + for (auto* elem : elements) { + elem->onRelease(); + } + stonesenseState.mouseHeld = false; +} + +// Handle mouse drag events +void handleMouseDrag(int mouseX, int mouseY) { + for (auto* elem : elements) { + + } +} + +// Handle mouse movement for hover state +void handleMouseMove(int mouseX, int mouseY) { + for (auto* elem : elements) { + elem->updateHoverState(mouseX, mouseY); + } +} + +// Handle mouse wheel scrolling +void handleMouseWheel(int mouseX, int mouseY, int deltaY) { + for (auto* elem : elements) { + if (elem->isMouseOver(mouseX, mouseY)) { + elem->onScroll(deltaY); + } + } +} + +bool elementExists(int x, int y, int w, int h) { + for (GUIElement* elem : elements) { + if (elem->x == x && elem->y == y && elem->w == w && elem->h == h) { + return true; // Found an element with the same position and size + } + } + return false; +} + +void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates) { + if (!elementExists(x, y, w, h)) { + elements.push_back(new Button(x, y, w, h, borderColor, bgColor, onClickCallback, visibleStates)); + } +} + +void addLabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, + const std::string& label, ALLEGRO_FONT* font, + OnClickCallback onClickCallback, std::unordered_set visibleStates) { + if (!elementExists(x, y, w, h)) { + elements.push_back(new LabeledButton(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, visibleStates)); + } +} + + +void addTab(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, int tabIndex, const std::string& label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates) { + if (!elementExists(x, y, w, h)) { + elements.push_back(new Tab(x, y, w, h, borderColor, bgColor, tabIndex, label, font, onClickCallback, visibleStates)); + } +} + +void clearElements() { + for (auto* element : elements) { + delete element; // Free allocated memory + } + elements.clear(); // Remove all pointers from the vector +} namespace { @@ -1009,7 +1108,7 @@ ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteShe return al_create_sub_bitmap(spriteSheet, sheetx * SPRITEWIDTH, sheety * SPRITEHEIGHT, SPRITEWIDTH, SPRITEHEIGHT); } -void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex) { +void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClickCallback onClickCallback) { int numTabs = 2; int tabWidth = stonesenseState.ssState.InfoW / numTabs; int fontHeight = al_get_font_line_height(font); @@ -1018,22 +1117,10 @@ void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex) { int tabX = panelX + (tabIndex * tabWidth); // Position inside the panel int tabY = stonesenseState.ssState.ScreenH - tabHeight; // Flush with bottom - // Determine colors based on selection - bool isSelected = (stonesenseState.ssState.selectedTab == tabIndex); - ALLEGRO_COLOR bgColor = uiColor(dfColors::black, isSelected); // White if selected, black otherwise - ALLEGRO_COLOR textColor = uiColor(dfColors::lgray, isSelected); // Black text if selected, white otherwise - // Truncate label to fit inside tab std::string truncatedLabel = fitTextToWidth(label, font, tabWidth - 20); - // Draw filled rounded rectangle (tab background) - al_draw_filled_rounded_rectangle(tabX, tabY, tabX + tabWidth, tabY + tabHeight, 10, 10, bgColor); - - // Draw tab border - always yellow - al_draw_rounded_rectangle(tabX, tabY, tabX + tabWidth, tabY + tabHeight, 10, 10, uiColor(dfColors::yellow), 2); - - // Draw text centered inside tab - al_draw_text(font, textColor, tabX + tabWidth / 2, tabY + (tabHeight - fontHeight) / 2, ALLEGRO_ALIGN_CENTER, truncatedLabel.c_str()); + addTab(tabX, tabY, tabWidth, tabHeight, dfColors::yellow, dfColors::black, tabIndex, truncatedLabel, font, onClickCallback, { "INFO_PANEL", "INFO_PANEL/ANNOUNCEMENTS","INFO_PANEL/KEYBINDS" }); } @@ -1049,8 +1136,8 @@ void drawInfoPanel() { al_draw_rectangle(stonesenseState.ssState.ScreenW, 2, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::yellow), 2); //draw tabs - drawTab(font, "Announcements",0); - drawTab(font, "Keybinds", 1); + drawTab(font, "Announcements", GameState::tabs::announcements, action_toggleannouncements); + drawTab(font, "Keybinds", GameState::tabs::keybinds, action_togglekeybinds); } @@ -1173,16 +1260,22 @@ void paintboard() stonesenseState.stoneSenseTimers.prev_frame_time = donetime; stonesenseState.UIState = "DEFAULT"; + + auto buttonW = 30; + auto buttonH = buttonW; + addLabeledButton(stonesenseState.ssState.ScreenW - buttonW, 0, buttonW, buttonH, dfColors::yellow, dfColors::blue, "i", stonesenseState.font, action_toggleinfopanel, { "DEFAULT" }); + addLabeledButton(stonesenseState.ssState.ScreenW - buttonW - stonesenseState.ssState.InfoW, 0, buttonW, buttonH, dfColors::yellow, dfColors::red, "x", stonesenseState.font, action_toggleinfopanel, { "INFO_PANEL","INFO_PANEL/ANNOUNCEMENTS","INFO_PANEL/KEYBINDS" }); if (ssConfig.config.show_info_panel) { + stonesenseState.UIState = "INFO_PANEL"; drawInfoPanel(); } - if (ssConfig.show_announcements) { + if (stonesenseState.ssState.selectedTab == GameState::tabs::announcements) { stonesenseState.UIState = "INFO_PANEL/ANNOUNCEMENTS"; al_hold_bitmap_drawing(true); draw_announcements(font, ssState.ScreenW - 10, 10/*ssState.ScreenH - 10 - al_get_font_line_height(font)*/, ALLEGRO_ALIGN_RIGHT, df::global::world->status.announcements); al_hold_bitmap_drawing(false); } - if(ssConfig.show_keybinds){ + if(stonesenseState.ssState.selectedTab == GameState::tabs::keybinds){ stonesenseState.UIState = "INFO_PANEL/KEYBINDS"; std::string *keyname, *actionname; keyname = actionname = NULL; diff --git a/GUI.h b/GUI.h index 6e1a1dc7..ae88eda2 100644 --- a/GUI.h +++ b/GUI.h @@ -8,9 +8,10 @@ void handleMouseClick(int mouseX, int mouseY); void handleMouseMove(int mouseX, int mouseY); void handleMouseWheel(int mouseX, int mouseY, int deltaY); +void handleMouseRelease(); typedef void (*OnClickCallback)(uint32_t); void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates); - +void clearElements(); void ScreenToPoint(int x,int y,int &x1, int &y1, int &z1); void pointToScreen(int *inx, int *iny, int inz); diff --git a/StonesenseState.h b/StonesenseState.h index dc349f0a..5176c4c6 100644 --- a/StonesenseState.h +++ b/StonesenseState.h @@ -79,6 +79,7 @@ class StonesenseState int mouse_y; int mouse_z; unsigned int mouse_b; + bool mouseHeld; SegmentWrap map_segment; diff --git a/UserInput.cpp b/UserInput.cpp index 14c19817..d203821a 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -175,12 +175,16 @@ void doMouse() stonesenseState.timeToReloadSegment = true; //rest(50); } + if (stonesenseState.mouseHeld && !(mouse.buttons & 1)) { handleMouseRelease(); } if( mouse.buttons & 1 ) { ssConfig.config.follow_DFcursor = false; int x, y; x = mouse.x;//pos >> 16; y = mouse.y; //pos & 0x0000ffff; - handleMouseClick(x, y); + if (!stonesenseState.mouseHeld) { + stonesenseState.mouseHeld = true; + handleMouseClick(x, y); + } if( x >= stonesenseState.MiniMapTopLeftX && x <= stonesenseState.MiniMapBottomRightX && y >= stonesenseState.MiniMapTopLeftY && @@ -451,21 +455,19 @@ void action_toggleosd(uint32_t keymod) } void action_togglekeybinds(uint32_t keymod){ - auto& ssConfig = stonesenseState.ssConfig; - ssConfig.show_keybinds = !ssConfig.show_keybinds; - ssConfig.config.show_info_panel = ssConfig.show_keybinds; - ssConfig.show_announcements = false; stonesenseState.ssState.selectedTab = GameState::tabs::keybinds; } void action_toggleannouncements(uint32_t keymod) { - auto& ssConfig = stonesenseState.ssConfig; - ssConfig.show_announcements = !ssConfig.show_announcements; - ssConfig.config.show_info_panel = ssConfig.show_announcements; - ssConfig.show_keybinds = false; stonesenseState.ssState.selectedTab = GameState::tabs::announcements; } +void action_toggleinfopanel(uint32_t keymod) { + auto& ssConfig = stonesenseState.ssConfig; + ssConfig.config.show_info_panel = !ssConfig.config.show_info_panel; + stonesenseState.ssState.selectedTab = ssConfig.config.show_info_panel ? GameState::tabs::announcements : -1; +} + void action_toggledebug(uint32_t keymod) { auto& ssConfig = stonesenseState.ssConfig; diff --git a/UserInput.h b/UserInput.h index 055af66e..6f364952 100644 --- a/UserInput.h +++ b/UserInput.h @@ -28,6 +28,7 @@ void action_toggleosd(uint32_t keymod); void action_toggledebug(uint32_t keymod); void action_togglekeybinds(uint32_t keymod); void action_toggleannouncements(uint32_t keymod); +void action_toggleinfopanel(uint32_t keymod); void action_incrzoom(uint32_t keymod); void action_decrzoom(uint32_t keymod); void action_screenshot(uint32_t keymod); diff --git a/main.cpp b/main.cpp index 66b7bfb9..13bb5aa6 100644 --- a/main.cpp +++ b/main.cpp @@ -216,7 +216,7 @@ void drawcredits() } void addElems() { - addButton(0, 0, 20, 20, dfColors::blue, dfColors::green, action_togglekeybinds, { "DEFAULT", "INFO_PANEL/ANNOUNCEMENTS" }); + //addButton(0, 0, 20, 20, dfColors::blue, dfColors::green, action_togglekeybinds, { "DEFAULT", "INFO_PANEL/ANNOUNCEMENTS" }); } /* main_loop: @@ -316,6 +316,8 @@ static void main_loop(ALLEGRO_DISPLAY * display, ALLEGRO_EVENT_QUEUE *queue, ALL if(in_time) { switch (event.type) { case ALLEGRO_EVENT_DISPLAY_RESIZE: + clearElements(); + addElems(); if (ssConfig.overlay_mode) { break; } From 71e399cad3b9e6afa55ad6b02e30cad921e9b97b Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 02:42:52 -0500 Subject: [PATCH 04/36] Remove unneeded code --- StonesenseState.h | 10 ---------- UserInput.cpp | 7 +------ 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/StonesenseState.h b/StonesenseState.h index 5176c4c6..01336e66 100644 --- a/StonesenseState.h +++ b/StonesenseState.h @@ -45,16 +45,6 @@ class StonesenseState GameConfiguration ssConfig; GameState ssState; - int KeybindsTabTopLeftX; - int KeybindsTabTopLeftY; - int KeybindsTabBottomRightX; - int KeybindsTabBottomRightY; - - int AnnouncementsTabTopLeftX; - int AnnouncementsTabTopLeftY; - int AnnouncementsTabBottomRightX; - int AnnouncementsTabBottomRightY; - int MiniMapTopLeftX; int MiniMapTopLeftY; int MiniMapBottomRightX; diff --git a/UserInput.cpp b/UserInput.cpp index d203821a..64f43aa4 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -191,12 +191,7 @@ void doMouse() y <= stonesenseState.MiniMapBottomRightY) { // in minimap ssState.Position.x = (x- stonesenseState.MiniMapTopLeftX- stonesenseState.MiniMapSegmentWidth/2)/ stonesenseState.oneTileInPixels; ssState.Position.y = (y- stonesenseState.MiniMapTopLeftY- stonesenseState.MiniMapSegmentHeight/2)/ stonesenseState.oneTileInPixels; - }/*else if (x >= stonesenseState.KeybindsTabTopLeftX && - x <= stonesenseState.KeybindsTabBottomRightX && - y >= stonesenseState.KeybindsTabTopLeftY && - y <= stonesenseState.KeybindsTabBottomRightY) { - - }*/ else { + } else { int tilex,tiley,tilez; //get the point in the segment From fac4a7270c36e00d1ba98c2b11e4040e7edcdbb7 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 02:51:39 -0500 Subject: [PATCH 05/36] update configs --- Config.h | 2 +- GameConfiguration.h | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Config.h b/Config.h index fbea6fda..475a33c3 100644 --- a/Config.h +++ b/Config.h @@ -46,7 +46,7 @@ class Config { bool names_use_nick = true; bool names_use_species = true; bool show_osd = false; - bool show_info_panel = true; + bool show_info_panel = false; bool cache_images = false; bool show_stockpiles = true; bool show_zones = true; diff --git a/GameConfiguration.h b/GameConfiguration.h index 64f1c633..bb1ff8ab 100644 --- a/GameConfiguration.h +++ b/GameConfiguration.h @@ -15,8 +15,6 @@ struct GameConfiguration { // items not configurable via the config file bool overlay_mode; bool show_designations = true; - bool show_announcements = false; - bool show_keybinds = false; bool single_layer_view; bool shade_hidden_tiles = true; bool show_hidden_tiles = false; @@ -68,12 +66,11 @@ struct GameConfiguration { { config = {}; - show_announcements = true; + show_info_panel = false; hide_outer_tiles = false; shade_hidden_tiles = true; load_ground_materials = true; show_designations = true; - show_keybinds = false; track_screen_center = true; creditScreen = true; bloodcutoff = 100; From aaf58287fcf62429208a84d1035f7759c67caf84 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 02:52:55 -0500 Subject: [PATCH 06/36] didnt mean to put that here --- GameConfiguration.h | 1 - 1 file changed, 1 deletion(-) diff --git a/GameConfiguration.h b/GameConfiguration.h index bb1ff8ab..02b0bf38 100644 --- a/GameConfiguration.h +++ b/GameConfiguration.h @@ -66,7 +66,6 @@ struct GameConfiguration { { config = {}; - show_info_panel = false; hide_outer_tiles = false; shade_hidden_tiles = true; load_ground_materials = true; From b988bdf57e0396726a89a277b016fba34b6fc38c Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 02:59:55 -0500 Subject: [PATCH 07/36] Fix on linux --- GUI.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 1d846473..560c4e3d 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -422,6 +422,8 @@ class GUIElement { : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) { } + virtual ~GUIElement() {} + virtual void onClick() {} virtual void onRelease() {} @@ -514,7 +516,6 @@ class LabeledButton : public Button { // Draw label text centered if (font) { - int textWidth = al_get_text_width(font, label.c_str()); int textHeight = al_get_font_line_height(font); al_draw_text(font, uiColor(dfColors::white), x + (w / 2), y + (h - textHeight) / 2, ALLEGRO_ALIGN_CENTER, label.c_str()); @@ -580,13 +581,6 @@ void handleMouseRelease() { stonesenseState.mouseHeld = false; } -// Handle mouse drag events -void handleMouseDrag(int mouseX, int mouseY) { - for (auto* elem : elements) { - - } -} - // Handle mouse movement for hover state void handleMouseMove(int mouseX, int mouseY) { for (auto* elem : elements) { @@ -647,12 +641,12 @@ namespace auto& ssConfig = stonesenseState.ssConfig; const int numAnnouncements = (int)announcements.size(); - const int maxAnnouncements = std::min(10, numAnnouncements); + //const int maxAnnouncements = std::min(10, numAnnouncements); int line = 0; int offset = 0; int fontHeight = al_get_font_line_height(font); - int tabHeight = fontHeight + 10; // Padding for visuals + //int tabHeight = fontHeight + 10; // Padding for visuals for (int i = numAnnouncements - 1; /*i >= (numAnnouncements - maxAnnouncements) && */announcements[i]->duration > 0; i--) { @@ -1127,9 +1121,7 @@ void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClick void drawInfoPanel() { auto font = stonesenseState.font; auto fontHeight = al_get_font_line_height(font); - int tabWidth = (stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW) / 2; int halftabHeight = (fontHeight + 10)/2; - int bottomY = stonesenseState.ssState.ScreenH - 2; //draw panel al_draw_filled_rectangle(stonesenseState.ssState.ScreenW, 0, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::black)); From b6b0530fbe84d52f2905f1bec9fabd420d9210a9 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 03:01:59 -0500 Subject: [PATCH 08/36] fix the default tab state --- GameState.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GameState.h b/GameState.h index a39ef440..34bc4f3a 100644 --- a/GameState.h +++ b/GameState.h @@ -26,7 +26,7 @@ struct GameState{ //info panel int InfoW; int InfoH; - int selectedTab = 0; + int selectedTab = -1; enum tabs { announcements, From 665a9a20148e8c7d498b985d5d0beda1da774d73 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 15 Mar 2025 14:52:41 -0500 Subject: [PATCH 09/36] fix the info panel outline not being lined up --- GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI.cpp b/GUI.cpp index 560c4e3d..13f72c7f 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -1125,7 +1125,7 @@ void drawInfoPanel() { //draw panel al_draw_filled_rectangle(stonesenseState.ssState.ScreenW, 0, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::black)); - al_draw_rectangle(stonesenseState.ssState.ScreenW, 2, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::yellow), 2); + al_draw_rectangle(stonesenseState.ssState.ScreenW, 0, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::yellow), 2); //draw tabs drawTab(font, "Announcements", GameState::tabs::announcements, action_toggleannouncements); From 6f6a9f0687b6675dac4356aec1413370a207e9ea Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 23 Mar 2025 10:31:56 -0500 Subject: [PATCH 10/36] always do the day/night shade --- SpriteColors.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/SpriteColors.cpp b/SpriteColors.cpp index 66f3d235..d04d284d 100644 --- a/SpriteColors.cpp +++ b/SpriteColors.cpp @@ -206,9 +206,6 @@ ALLEGRO_COLOR premultiply(ALLEGRO_COLOR input) ALLEGRO_COLOR shadeAdventureMode(ALLEGRO_COLOR color, bool foggy, bool outside) { auto& contentLoader = stonesenseState.contentLoader; - if(contentLoader->gameMode.g_mode != GAMEMODE_ADVENTURE) { - return color; - } if(foggy && stonesenseState.ssConfig.config.fog_of_war) { color.r *= 0.25f; @@ -216,14 +213,12 @@ ALLEGRO_COLOR shadeAdventureMode(ALLEGRO_COLOR color, bool foggy, bool outside) color.b *= 0.25f; } - if(stonesenseState.ssConfig.config.dayNightCycle) { - if(outside) { - color = color*getDayShade(contentLoader->currentHour, contentLoader->currentTickRel); - } else { - color.r *= 0.5f; - color.g *= 0.5f; - color.b *= 0.5f; - } + if (outside) { + color = color * getDayShade(contentLoader->currentHour, contentLoader->currentTickRel); + } else { + color.r *= 0.75f; + color.g *= 0.75f; + color.b *= 0.75f; } return color; From 628382ced043311e313597dd7ab2de5a67feabc6 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sun, 23 Mar 2025 11:37:08 -0500 Subject: [PATCH 11/36] Always do fogOwar but only in adventure mode --- SpriteColors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpriteColors.cpp b/SpriteColors.cpp index d04d284d..f52a2b3d 100644 --- a/SpriteColors.cpp +++ b/SpriteColors.cpp @@ -207,7 +207,7 @@ ALLEGRO_COLOR shadeAdventureMode(ALLEGRO_COLOR color, bool foggy, bool outside) { auto& contentLoader = stonesenseState.contentLoader; - if(foggy && stonesenseState.ssConfig.config.fog_of_war) { + if(foggy && contentLoader->gameMode.g_mode == GAMEMODE_ADVENTURE) { color.r *= 0.25f; color.g *= 0.25f; color.b *= 0.25f; From bbf4ce1f3841a3ffbb3a83cac5122c388557def7 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 16:59:13 -0500 Subject: [PATCH 12/36] Greatly rework the guipanel and make it all function --- GUI.cpp | 614 +++++++++++++++++++++++------- GUI.h | 1 - GameState.h | 1 + Keybinds.cpp | 28 +- UserInput.cpp | 4 + UserInput.h | 1 + main.cpp | 6 - resources/GUI/control-buttons.png | Bin 0 -> 854 bytes resources/GUI/simple-buttons.png | Bin 0 -> 554 bytes resources/GUI/tabs.png | Bin 0 -> 662 bytes resources/GUI/text.png | Bin 0 -> 4839 bytes 11 files changed, 504 insertions(+), 151 deletions(-) create mode 100644 resources/GUI/control-buttons.png create mode 100644 resources/GUI/simple-buttons.png create mode 100644 resources/GUI/tabs.png create mode 100644 resources/GUI/text.png diff --git a/GUI.cpp b/GUI.cpp index 13f72c7f..431ea3f5 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -73,6 +73,9 @@ ALLEGRO_BITMAP* bigFile = 0; GLhandleARB tinter; GLhandleARB tinter_shader; +const int TILE_WIDTH = 8; +const int TILE_HEIGHT = 12; + class ImageCache { private: @@ -375,7 +378,7 @@ std::string fitTextToWidth(const std::string& input, ALLEGRO_FONT* font, int wid } -std::vector splitLinesToWidth(const std::string& input,const ALLEGRO_FONT* font, int width) { +std::vector splitLinesToWidth(const std::string& input, int width) { std::vector splits; // To store each part of the split string std::string allCurrentChars; // The part of the string that fits so far @@ -387,7 +390,7 @@ std::vector splitLinesToWidth(const std::string& input,const ALLEGR // Test if adding this word to the current string would exceed the width std::string testString = allCurrentChars + (allCurrentChars.empty() ? "" : " ") + word; - if (al_get_text_width(font, testString.c_str()) > width) { + if ((strlen(testString.c_str())*TILE_WIDTH) > width) { // If it's too wide, stop and push the current string into the vector if (!allCurrentChars.empty()) { splits.push_back(allCurrentChars); @@ -413,6 +416,9 @@ std::vector splitLinesToWidth(const std::string& input,const ALLEGR // Base GUI Element Class class GUIElement { +protected: + static std::vector tiles; + static std::vector letterTiles; public: int x, y, w, h; // Position and size of the element bool hovered; // Tracks if the mouse is over the element @@ -420,17 +426,19 @@ class GUIElement { GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) { + if (tiles.empty()) { + tiles = loadTileset("hack/data/art/border-window.png", al_map_rgb(255, 0, 255)); + } + if (letterTiles.empty()) { + letterTiles = loadTileset("stonesense/GUI/text.png", al_map_rgb(255, 0, 255)); + } } virtual ~GUIElement() {} - virtual void onClick() {} - - virtual void onRelease() {} - virtual void onHover() {} - virtual void onHoverExit() { onRelease(); } + virtual void onHoverExit() {} virtual void onScroll(int deltaY) {} @@ -460,96 +468,358 @@ class GUIElement { hovered = mouseOver; } + void setPosSize(int newX, int newY, int newW, int newH) { + x = newX; + y = newY; + w = newW; + h = newH; + } + + std::vector loadTileset(const char* tilesetPath, ALLEGRO_COLOR alphaMask) { + std::vector allTiles; // Store extracted tiles + + auto tileset = al_load_bitmap(tilesetPath); + if (!tileset) { + printf("Failed to load tileset!\n"); + return {}; + } + + int tileset_width = al_get_bitmap_width(tileset); + int tileset_height = al_get_bitmap_height(tileset); + int tiles_per_row = tileset_width / TILE_WIDTH; + int tiles_per_col = tileset_height / TILE_HEIGHT; + + for (int y = 0; y < tiles_per_col; ++y) { + for (int x = 0; x < tiles_per_row; ++x) { + ALLEGRO_BITMAP* tile = al_create_sub_bitmap(tileset, x * TILE_WIDTH, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); + al_convert_mask_to_alpha(tile, alphaMask); + allTiles.push_back(tile); + } + } + return allTiles; + } + + static int get_cp437_tile_index(char c) { + return (unsigned char)c; // CP437 codes directly map to tile indices + } + + + static void draw_tile(int tile_index, int x, int y, std::vector tileset, int flip_flag = 0) { + if (tile_index >= 0 && tile_index < tileset.size() && tileset[tile_index]) { + al_draw_bitmap(tileset[tile_index], x, y, flip_flag); + } + } + + + static void draw_tinted_tile(int tile_index, int x, int y, std::vector tileset, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { + + // Draw solid background first + al_draw_filled_rectangle(x, y, x + TILE_WIDTH, y + TILE_HEIGHT, bg); + + // Draw the tile with foreground tint + if (tile_index >= 0 && tile_index < tileset.size() && tileset[tile_index]) { + al_draw_tinted_bitmap(tileset[tile_index], fg, x, y, 0); + } + } + + enum TextAlign { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT + }; + + static void draw_text_with_tiles(int x, int y, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, + const char* text, TextAlign align = ALIGN_LEFT) { + if (!text) return; + + int label_length = strlen(text); + int start_x = x; + + // Handle text alignment + if (align == ALIGN_CENTER) { + start_x -= (label_length * TILE_WIDTH) / 2; // Center by shifting left half the width + } + else if (align == ALIGN_RIGHT) { + start_x -= label_length * TILE_WIDTH; // Shift left by full text width + } + + // Draw each character using CP437 tiles + for (int i = 0; i < label_length; i++) { + int tile_index = get_cp437_tile_index(text[i]); // Get CP437 tile index + draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, letterTiles, fg, bg); + } + } + + +}; + +std::vector GUIElement::tiles; +std::vector GUIElement::letterTiles; + +class textElement : public GUIElement { +public: + std::string text; // Store the actual text, not just a pointer + ALLEGRO_COLOR fg; + ALLEGRO_COLOR bg; + TextAlign align; + + textElement(int x, int y, int w, int h, std::unordered_set visibleStates, const char* text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, TextAlign align = ALIGN_LEFT) + : GUIElement(x, y, w, h, visibleStates), text(text), fg(fg), bg(bg), align(align) { + } + + void setText(const char* newText) { text = newText; } // Copy new text safely + + void draw() override { + GUIElement::draw_text_with_tiles(x, y, fg, bg, text.c_str(), align); + } +}; + + +class WindowPanel : public GUIElement { +public: + const char* label; + + WindowPanel(int x, int y, int w, int h, std::unordered_set visibleStates, const char* label) :GUIElement(x, y, w, h, visibleStates), label(label) {} + + + void draw_window(float x, float y, int width, int height, const char* label, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { + if (width < 3) width = 3; + if (height < 3) height = 3; + + // Draw corners + draw_tile(0, x, y, tiles); // Top-left + draw_tile(2, x + (width - 1) * TILE_WIDTH, y, tiles); // Top-right + draw_tile(14, x, y + (height - 1) * TILE_HEIGHT, tiles); // Bottom-left + draw_tile(16, x + (width - 1) * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT, tiles); // Bottom-right + + // Draw top & bottom edges + for (int col = 1; col < width - 1; ++col) { + draw_tile(1, x + col * TILE_WIDTH, y, tiles); // Top + draw_tile(15, x + col * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT, tiles); // Bottom + } + + // Draw left & right edges + for (int row = 1; row < height - 1; ++row) { + draw_tile(7, x, y + row * TILE_HEIGHT, tiles); // Left + draw_tile(9, x + (width - 1) * TILE_WIDTH, y + row * TILE_HEIGHT, tiles); // Right + } + + // Fill center area + for (int row = 1; row < height - 1; ++row) { + for (int col = 1; col < width - 1; ++col) { + draw_tile(8, x + col * TILE_WIDTH, y + row * TILE_HEIGHT, tiles); // Center + } + } + + // Draw the centered title using CP437 letter tiles + if (label) { + int label_length = strlen(label); + int start_x = x + (width * TILE_WIDTH - label_length * TILE_WIDTH) / 2; // Center horizontally + + for (int i = 0; i < label_length; i++) { + int tile_index = get_cp437_tile_index(label[i]); + draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, letterTiles, fg, bg); + } + } + } + + void draw() override { + draw_window(x, y, w, h, label, uiColor(dfColors::black), uiColor(dfColors::lgray)); + } + }; // Function pointer type typedef void (*OnClickCallback)(uint32_t); -class Button : public GUIElement { +class clickElement : public GUIElement { public: - int32_t borderColor; - int32_t bgColor; - OnClickCallback onClickCallback; // Function pointer for the callback + OnClickCallback clickFn; // Function pointer for the callback bool held = false; - Button(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback clickFn, std::unordered_set visibleStates) - : GUIElement(x, y, w, h, visibleStates), borderColor(borderColor), bgColor(bgColor), onClickCallback(clickFn) { + clickElement(int x, int y, int w, int h, OnClickCallback clickFn, std::unordered_set visibleStates) + : GUIElement(x, y, w, h, visibleStates), clickFn(clickFn) { } - void onClick() override { + virtual void onClick() { if (!held) { held = true; if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { - if (onClickCallback) { - onClickCallback(getKeyMods(&stonesenseState.keyboard)); // Pass a uint32_t argument when the button is clicked + if (clickFn) { + clickFn(getKeyMods(&stonesenseState.keyboard)); // Pass a uint32_t argument when the button is clicked } } } } - void onRelease() override { + virtual void onRelease() { held = false; } - - void draw() override { - al_draw_filled_rectangle(x, y, x + w, y + h, uiColor(bgColor, hovered)); - al_draw_rectangle(x, y, x + w, y + h, uiColor(borderColor, hovered), 2); - } }; -#include - -class LabeledButton : public Button { +class LabeledButton : public clickElement { public: - std::string label; + const char* label; ALLEGRO_FONT* font; + int32_t borderColor; + int32_t bgColor; LabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, - const std::string& label, ALLEGRO_FONT* font, + const char* label, ALLEGRO_FONT* font, OnClickCallback clickFn, std::unordered_set visibleStates) - : Button(x, y, w, h, borderColor, bgColor, clickFn, visibleStates), - label(label), font(font) { + : clickElement(x, y, w, h, clickFn, visibleStates), + label(label), font(font), borderColor(borderColor), bgColor(bgColor) { } void draw() override { - Button::draw(); // Draw base button + ALLEGRO_COLOR bg = uiColor(bgColor, hovered); + ALLEGRO_COLOR textColor = uiColor(dfColors::lgray, hovered); + al_draw_filled_rectangle(x, y, x + w, y + h, bg); // Draw label text centered if (font) { int textHeight = al_get_font_line_height(font); - al_draw_text(font, uiColor(dfColors::white), x + (w / 2), y + (h - textHeight) / 2, - ALLEGRO_ALIGN_CENTER, label.c_str()); + al_draw_text(font, textColor, x + (w / 2), y + (h - textHeight) / 2, + ALLEGRO_ALIGN_CENTER, label); } } }; +class simpleButton : public clickElement { +public: + std::string icon; + std::vector simpleButtons; + int colorFlag; + + simpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, std::unordered_set visibleStates) + : clickElement(x, y, w, h, onClickCallback, visibleStates), icon(icon), colorFlag(colorFlag) { + simpleButtons = loadTileset("stonesense/GUI/simple-buttons.png", al_map_rgb(255, 0, 255)); + } + + void draw_simple_button(float x, float y, const char* text) { + int colorOffset = 3; + int rowOffset = 12; + + // Draw corners + for (int i = 0; i < 3; i++) { + draw_tile(0 + (colorFlag * colorOffset) + (i * rowOffset), x, y + TILE_HEIGHT * i, simpleButtons); // Left + draw_tile(1 + (colorFlag * colorOffset) + (i * rowOffset), x + TILE_WIDTH, y + TILE_HEIGHT * i, simpleButtons); // Middle + draw_tile(2 + (colorFlag * colorOffset) + (i * rowOffset), x + TILE_WIDTH * 2, y + TILE_HEIGHT * i, simpleButtons); // Right + } + + // Draw the centered title using CP437 letter tiles + if (text) { + int tile_index = get_cp437_tile_index(text[0]); + draw_tinted_tile(tile_index, x + TILE_WIDTH, y + TILE_HEIGHT, letterTiles, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0)); + } + } -class Tab : public Button { + void draw() override { + draw_simple_button(x, y, icon.c_str()); + } + +}; + + +class controlButton : public clickElement { +public: + std::vector controlButtons; + bool thick; + bool& enabledVar; + + controlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, std::unordered_set visibleStates) + : clickElement(x, y, w, h, nullptr, visibleStates), thick(thick), enabledVar(enabledVar) { + controlButtons = loadTileset("stonesense/GUI/control-buttons.png", al_map_rgb(28,28,28)); + } + + void draw_control_button(float x, float y) { + int enabledOffset = 3; + int rowOffset = 15; + + + draw_tile(0 + (enabledVar * enabledOffset) + (thick * rowOffset), x, y, controlButtons); // Left + draw_tile(1 + (enabledVar * enabledOffset) + (thick * rowOffset), x + TILE_WIDTH, y, controlButtons); // Middle + draw_tile(2 + (enabledVar * enabledOffset) + (thick * rowOffset), x + TILE_WIDTH * 2, y, controlButtons); // Right + + } + + void draw() override { + draw_control_button(x, y); + } + + void onClick() override { + enabledVar = !enabledVar; // Update selected tab + clickElement::onClick(); // Call the base class onClick() to execute any extra behavior + } + +}; + + +class Tab : public clickElement { public: int tabIndex; std::string label; - ALLEGRO_FONT* font; + std::vector tabSet; + bool upside_down; + + Tab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) + : clickElement(x, y, w, h, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), upside_down(upside_down) { + tabSet = loadTileset("stonesense/GUI/tabs.png", al_map_rgb(28,28,28)); + } + + void draw_tab(float x, float y, int width, bool is_enabled, bool is_upside_down, ALLEGRO_COLOR fg, const char* text) { + // Flip flag (use ALLEGRO_FLIP_VERTICAL for upside-down tabs) + int flip_flag = is_upside_down ? ALLEGRO_FLIP_VERTICAL : 0; + + + // Draw corners + int tileShift = is_upside_down ? 10 : 0; + draw_tile(tileShift + 0 + (is_enabled ? 5 : 0), x + TILE_WIDTH, y, tabSet, flip_flag); // Top-left + draw_tile(tileShift + 1 + (is_enabled ? 5 : 0), x + TILE_WIDTH + TILE_WIDTH, y, tabSet, flip_flag); + + draw_tile(tileShift + 3 + (is_enabled ? 5 : 0), x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y, tabSet, flip_flag); // Top-right + draw_tile(tileShift + 4 + (is_enabled ? 5 : 0), x + ((width / TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y, tabSet, flip_flag); + + draw_tile(-tileShift + 10 + (is_enabled ? 5 : 0), x + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-left + draw_tile(-tileShift + 11 + (is_enabled ? 5 : 0), x + TILE_WIDTH + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); + + draw_tile(-tileShift + 13 + (is_enabled ? 5 : 0), x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y + TILE_HEIGHT, tabSet, flip_flag); + draw_tile(-tileShift + 14 + (is_enabled ? 5 : 0), x + ((width/TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-right + + //// Draw top & bottom edges + for (int col = 3; col < (width / TILE_WIDTH) - 1; ++col) { + draw_tile(tileShift + 2 + (is_enabled ? 5 : 0), x + col * TILE_WIDTH, y, tabSet, flip_flag); // Top + draw_tile(-tileShift + 12 + (is_enabled ? 5 : 0), x + col * TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom + } + + // Draw the centered title using CP437 letter tiles + if (text) { + int label_length = strlen(text); + //int usable_width = (width / TILE_WIDTH) - 4; // Account for non-writable tiles + //int start_tile = (usable_width - label_length) / 2 + 2; // Centering formula + //int start_x = x + start_tile * TILE_WIDTH; // Convert to pixels + int start_x = x + TILE_WIDTH * 3; + + for (int i = 0; i < label_length; i++) { + int tile_index = get_cp437_tile_index(text[i]); + draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y + 7, letterTiles, fg, al_map_rgba(0, 0, 0, 0)); + } + } - Tab(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, int tabIndex, const std::string& label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates) - : Button(x, y, w, h, borderColor, bgColor, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), font(font) { } void draw() override { bool isSelected = (stonesenseState.ssState.selectedTab == tabIndex); - ALLEGRO_COLOR bg = uiColor(bgColor, isSelected); - ALLEGRO_COLOR textColor = uiColor(dfColors::lgray, isSelected); - - al_draw_filled_rounded_rectangle(x, y, x + w, y + h, 10, 10, bg); - al_draw_rounded_rectangle(x, y, x + w, y + h, 10, 10, uiColor(borderColor), 2); + ALLEGRO_COLOR textColor = isSelected ? uiColor(dfColors::black) : uiColor(dfColors::white); - std::string truncatedLabel = fitTextToWidth(label, font, w - 20); - al_draw_text(font, textColor, x + w / 2, y + (h - al_get_font_line_height(font)) / 2, ALLEGRO_ALIGN_CENTER, truncatedLabel.c_str()); + std::string temp = fitTextToWidth(label, stonesenseState.font, w - 20); + draw_tab(x, y, w, isSelected, upside_down, textColor, temp.c_str()); } void onClick() override { stonesenseState.ssState.selectedTab = tabIndex; // Update selected tab - Button::onClick(); // Call the base class onClick() to execute any extra behavior + clickElement::onClick(); } }; @@ -566,9 +836,11 @@ void updateAll() { // Handle mouse click events void handleMouseClick(int mouseX, int mouseY) { for (auto* elem : elements) { - if (elem->isMouseOver(mouseX, mouseY)) { - elem->onClick(); - break; + if (auto* clickElem = dynamic_cast(elem)) { + if (clickElem->isMouseOver(mouseX, mouseY)) { + clickElem->onClick(); + break; + } } } } @@ -576,11 +848,14 @@ void handleMouseClick(int mouseX, int mouseY) { // Handle mouse release events void handleMouseRelease() { for (auto* elem : elements) { - elem->onRelease(); + if (auto* clickElem = dynamic_cast(elem)) { + clickElem->onRelease(); + } } stonesenseState.mouseHeld = false; } + // Handle mouse movement for hover state void handleMouseMove(int mouseX, int mouseY) { for (auto* elem : elements) { @@ -608,22 +883,45 @@ bool elementExists(int x, int y, int w, int h) { void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates) { if (!elementExists(x, y, w, h)) { - elements.push_back(new Button(x, y, w, h, borderColor, bgColor, onClickCallback, visibleStates)); + elements.push_back(new clickElement(x, y, w, h, onClickCallback, visibleStates)); } } void addLabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, - const std::string& label, ALLEGRO_FONT* font, + const char* label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates) { if (!elementExists(x, y, w, h)) { elements.push_back(new LabeledButton(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, visibleStates)); } } +void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, std::unordered_set visibleStates){ + if (!elementExists(x, y, w, h)) { + elements.push_back(new simpleButton(x, y, w, h, icon, colorFlag, onClickCallback, visibleStates)); + } +} + +void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, std::unordered_set visibleStates) { + if (!elementExists(x, y, w, h)) { + elements.push_back(new controlButton(x, y, w, h, thick, enabledVar, visibleStates)); + } +} + +void addTab(int x, int y, int w, int h, int tabIndex, const char* label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) { + if (!elementExists(x, y, w, h)) { + elements.push_back(new Tab(x, y, w, h, tabIndex, label, onClickCallback, visibleStates, upside_down)); + } +} -void addTab(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, int tabIndex, const std::string& label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates) { +void addPanel(int x, int y, int w, int h, std::unordered_set visibleStates, const char* label) { if (!elementExists(x, y, w, h)) { - elements.push_back(new Tab(x, y, w, h, borderColor, bgColor, tabIndex, label, font, onClickCallback, visibleStates)); + elements.push_back(new WindowPanel(x, y, w, h, visibleStates, label)); + } +} + +void addText(int x, int y, int w, int h, std::unordered_set visibleStates, const char* text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, GUIElement::TextAlign align) { + if (!elementExists(x, y, w, h)) { + elements.push_back(new textElement(x, y, w, h, visibleStates, text, fg, bg, align)); } } @@ -636,30 +934,36 @@ void clearElements() { namespace { - void draw_announcements(const ALLEGRO_FONT* font, float x, float y, int flags, std::vector& announcements) - { + void drawAnnouncemntLine(int& rowNum, const std::string& text, ALLEGRO_COLOR reportColor) { + + int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; + const char* report = text.c_str(); + + // Use text.length() * TILE_WIDTH instead of strlen(text) + GUIElement::draw_text_with_tiles(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), reportColor, + al_map_rgba(0, 0, 0, 0), report, GUIElement::TextAlign::ALIGN_LEFT); + + rowNum++; + } + + void draw_announcements(std::vector& announcements) { auto& ssConfig = stonesenseState.ssConfig; const int numAnnouncements = (int)announcements.size(); - //const int maxAnnouncements = std::min(10, numAnnouncements); - int line = 0; - int offset = 0; - - int fontHeight = al_get_font_line_height(font); - //int tabHeight = fontHeight + 10; // Padding for visuals + const int maxAnnouncements = std::min(10, numAnnouncements); + int line = 1; - for (int i = numAnnouncements - 1; /*i >= (numAnnouncements - maxAnnouncements) && */announcements[i]->duration > 0; i--) - { + for (int i = numAnnouncements - 1; i >= (numAnnouncements - maxAnnouncements) && announcements[i]->duration > 0; i--) { ALLEGRO_COLOR color = ssConfig.config.colors.getDfColor(announcements[i]->color, ssConfig.config.useDfColors); - auto reportStr = DF2UTF(announcements[i]->text).c_str(); - auto splits = splitLinesToWidth(reportStr, font, stonesenseState.ssState.InfoW - 20); - for (auto split : splits) { - offset = (line * fontHeight); - draw_text_border(font, color, x, y + offset, flags, split.c_str()); - line++; + std::string reportStr = announcements[i]->text; + std::vector splits = splitLinesToWidth(reportStr, stonesenseState.ssState.InfoW - 20); + + for (const std::string& split : splits) { + drawAnnouncemntLine(line, split, color); } } } + } void draw_loading_message(const char *format, ...) @@ -1102,34 +1406,91 @@ ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteShe return al_create_sub_bitmap(spriteSheet, sheetx * SPRITEWIDTH, sheety * SPRITEHEIGHT, SPRITEWIDTH, SPRITEHEIGHT); } -void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClickCallback onClickCallback) { - int numTabs = 2; - int tabWidth = stonesenseState.ssState.InfoW / numTabs; - int fontHeight = al_get_font_line_height(font); - int tabHeight = fontHeight + 10; // Padding for visuals +void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClickCallback onClickCallback, std::unordered_set visibleStates) { + int numTabs = 3; + int tabWidth = (stonesenseState.ssState.InfoW - 16) / numTabs; + int tabHeight = 24; int panelX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW; // Left edge of info panel int tabX = panelX + (tabIndex * tabWidth); // Position inside the panel - int tabY = stonesenseState.ssState.ScreenH - tabHeight; // Flush with bottom + int tabY = stonesenseState.ssState.ScreenH - tabHeight-4; // Flush with bottom // Truncate label to fit inside tab - std::string truncatedLabel = fitTextToWidth(label, font, tabWidth - 20); + auto temp = fitTextToWidth(label, font, tabWidth - 20); + const char* truncatedLabel = temp.c_str(); + - addTab(tabX, tabY, tabWidth, tabHeight, dfColors::yellow, dfColors::black, tabIndex, truncatedLabel, font, onClickCallback, { "INFO_PANEL", "INFO_PANEL/ANNOUNCEMENTS","INFO_PANEL/KEYBINDS" }); + addTab(tabX, tabY, tabWidth, tabHeight, tabIndex, truncatedLabel, font, onClickCallback, visibleStates, true); } -void drawInfoPanel() { +void drawInfoPanel(std::unordered_set infoStates) { auto font = stonesenseState.font; auto fontHeight = al_get_font_line_height(font); int halftabHeight = (fontHeight + 10)/2; + auto panelWidth = int(stonesenseState.ssState.InfoW / 8); + auto panelHeight = int((stonesenseState.ssState.ScreenH - fontHeight) / 12); + const char* label = " Info Panel "; //draw panel - al_draw_filled_rectangle(stonesenseState.ssState.ScreenW, 0, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::black)); - al_draw_rectangle(stonesenseState.ssState.ScreenW, 0, stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, stonesenseState.ssState.ScreenH - halftabHeight, uiColor(dfColors::yellow), 2); + addPanel(stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, 0, panelWidth, panelHeight, infoStates, label); //draw tabs - drawTab(font, "Announcements", GameState::tabs::announcements, action_toggleannouncements); - drawTab(font, "Keybinds", GameState::tabs::keybinds, action_togglekeybinds); + drawTab(font, "Announcements", GameState::tabs::announcements, action_toggleannouncements, infoStates); + drawTab(font, "Keybinds", GameState::tabs::keybinds, action_togglekeybinds, infoStates); + drawTab(font, "Settings", GameState::tabs::settings, action_togglesettings, infoStates); + +} + +void addSettingLine(int& rowNum, bool& settingVar, const char* text) { + int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; + addControlButton(startX, TILE_HEIGHT+(rowNum*TILE_HEIGHT), 3 * TILE_WIDTH, TILE_HEIGHT, false, settingVar, {"INFO_PANEL/SETTINGS"}); + addText(startX + TILE_WIDTH * 4, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH * strlen(text), TILE_HEIGHT, { "INFO_PANEL/SETTINGS" }, text, uiColor(dfColors::cyan), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + + rowNum++; +} +void drawSettings() { + auto& ssConfig = stonesenseState.ssConfig; + + int rowNum = 1; + addSettingLine(rowNum, ssConfig.config.show_creature_names, "Show creature names"); + addSettingLine(rowNum, ssConfig.config.names_use_nick, "Use nickname"); + addSettingLine(rowNum, ssConfig.config.names_use_species, "Use species name"); + addSettingLine(rowNum, ssConfig.config.show_creature_jobs, "Show creature jobs"); + addSettingLine(rowNum, ssConfig.config.show_creature_moods, "Show creature moods"); + addSettingLine(rowNum, ssConfig.config.fogenable, "Draw depth fog"); + addSettingLine(rowNum, ssConfig.config.show_stockpiles, "Show stockpiles"); + addSettingLine(rowNum, ssConfig.config.show_zones, "Show zones"); + addSettingLine(rowNum, ssConfig.show_designations, "Show designations"); + addSettingLine(rowNum, ssConfig.show_hidden_tiles, "Show unrevealed tiles (cheat)"); + addSettingLine(rowNum, ssConfig.shade_hidden_tiles, "Show blackboxes"); +} + +void addKeybindLine(int& rowNum, const char* keyname, const char* actionname, bool repeats) { + int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; + + int keyW = TILE_WIDTH * strlen(keyname); + int actionW= TILE_WIDTH * strlen(actionname); + + addText(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), keyW, TILE_HEIGHT, { "INFO_PANEL/KEYBINDS" }, keyname, uiColor(dfColors::lgreen), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + addText(startX+keyW, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH*2, TILE_HEIGHT, { "INFO_PANEL/KEYBINDS" }, ": ", uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + addText(startX+keyW+(2*TILE_WIDTH), TILE_HEIGHT + (rowNum * TILE_HEIGHT), actionW, TILE_HEIGHT, {"INFO_PANEL/KEYBINDS"}, actionname, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + + rowNum++; +} + +void drawKeybinds() { + auto& ssState = stonesenseState.ssState; + std::string* keyname, * actionname; + int line = 1; + + for (int32_t i = 1; true; i++) { + if (getKeyStrings(i, keyname, actionname)) { + addKeybindLine(line,keyname->c_str(),actionname->c_str(),isRepeatable(i)); + } + if (keyname == NULL) { + break; + } + } } @@ -1196,6 +1557,31 @@ float clockToMs(float clockTicks) { return clockTicks / (CLOCKS_PER_SEC/1000); } +void updateUIState() { + auto& ssConfig = stonesenseState.ssConfig; + auto& ssState = stonesenseState.ssState; + + stonesenseState.UIState = "DEFAULT"; + if (ssConfig.config.show_info_panel) { + stonesenseState.UIState = "INFO_PANEL"; + } + if (ssState.selectedTab == GameState::tabs::announcements) { + stonesenseState.UIState = "INFO_PANEL/ANNOUNCEMENTS"; + } + if (ssState.selectedTab == GameState::tabs::keybinds) { + stonesenseState.UIState = "INFO_PANEL/KEYBINDS"; + } + if (ssState.selectedTab == GameState::tabs::settings) { + stonesenseState.UIState = "INFO_PANEL/SETTINGS"; + } + if (ssConfig.config.show_osd) { + stonesenseState.UIState = "OSD"; + } + if (ssConfig.config.debug_mode) { + stonesenseState.UIState = "DEBUG"; + } +} + void paintboard() { DFHack::CoreSuspender suspend; @@ -1251,55 +1637,22 @@ void paintboard() stonesenseState.stoneSenseTimers.frame_total.update(donetime - stonesenseState.stoneSenseTimers.prev_frame_time); stonesenseState.stoneSenseTimers.prev_frame_time = donetime; - stonesenseState.UIState = "DEFAULT"; - - auto buttonW = 30; - auto buttonH = buttonW; - addLabeledButton(stonesenseState.ssState.ScreenW - buttonW, 0, buttonW, buttonH, dfColors::yellow, dfColors::blue, "i", stonesenseState.font, action_toggleinfopanel, { "DEFAULT" }); - addLabeledButton(stonesenseState.ssState.ScreenW - buttonW - stonesenseState.ssState.InfoW, 0, buttonW, buttonH, dfColors::yellow, dfColors::red, "x", stonesenseState.font, action_toggleinfopanel, { "INFO_PANEL","INFO_PANEL/ANNOUNCEMENTS","INFO_PANEL/KEYBINDS" }); - if (ssConfig.config.show_info_panel) { - stonesenseState.UIState = "INFO_PANEL"; - drawInfoPanel(); - } - if (stonesenseState.ssState.selectedTab == GameState::tabs::announcements) { - stonesenseState.UIState = "INFO_PANEL/ANNOUNCEMENTS"; - al_hold_bitmap_drawing(true); - draw_announcements(font, ssState.ScreenW - 10, 10/*ssState.ScreenH - 10 - al_get_font_line_height(font)*/, ALLEGRO_ALIGN_RIGHT, df::global::world->status.announcements); - al_hold_bitmap_drawing(false); - } - if(stonesenseState.ssState.selectedTab == GameState::tabs::keybinds){ - stonesenseState.UIState = "INFO_PANEL/KEYBINDS"; - std::string *keyname, *actionname; - keyname = actionname = NULL; - int line = 1; - al_hold_bitmap_drawing(true); - - for(int32_t i=1; true; i++){ - if(getKeyStrings(i, keyname, actionname)){ - std::ostringstream oss; - oss << *keyname << ": " << *actionname; - if (isRepeatable(i)) { - oss << " (repeats)"; - } - std::string keyStr = oss.str(); - - int maxWidth = std::max(0, stonesenseState.ssState.InfoW - 20); - std::vector splitLines = splitLinesToWidth(keyStr, font, maxWidth); - - for (const auto& lineText : splitLines) { - draw_textf_border(font, uiColor(dfColors::white), ssState.ScreenW - 10, line * fontHeight, ALLEGRO_ALIGN_RIGHT, lineText.c_str()); - line++; - } + updateUIState(); + enum buttonColors { + grey, + red, + green, + blue + }; - } - if(keyname == NULL) { - break; - } - } - al_hold_bitmap_drawing(false); - } else if (ssConfig.config.show_osd) { - stonesenseState.UIState = "OSD"; + std::unordered_set infoStates = { "INFO_PANEL", "INFO_PANEL/ANNOUNCEMENTS","INFO_PANEL/KEYBINDS","INFO_PANEL/SETTINGS" }; + addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH), 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "i", buttonColors::blue, action_toggleinfopanel, { "DEFAULT" }); + addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH) - stonesenseState.ssState.InfoW, 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "x", buttonColors::red, action_toggleinfopanel, infoStates); + drawInfoPanel(infoStates); + drawSettings(); + drawKeybinds(); + if (ssConfig.config.show_osd) { al_hold_bitmap_drawing(true); draw_textf_border(font, uiColor(dfColors::white), 10,fontHeight, 0, "%i,%i,%i, r%i, z%i", ssState.Position.x,ssState.Position.y,ssState.Position.z, ssState.Rotation, ssConfig.zoom); @@ -1311,7 +1664,6 @@ void paintboard() drawAdvmodeMenuTalk(font, 5, ssState.ScreenH - 5); if(ssConfig.config.debug_mode) { - stonesenseState.UIState = "DEBUG"; auto& contentLoader = stonesenseState.contentLoader; draw_textf_border(font, uiColor(dfColors::white), 10, 3*fontHeight, 0, "Map Read Time: %.2fms", clockToMs(stonesenseState.stoneSenseTimers.read_time)); draw_textf_border(font, uiColor(dfColors::white), 10, 4*fontHeight, 0, "Map Beautification Time: %.2fms", clockToMs(stonesenseState.stoneSenseTimers.beautify_time)); @@ -1352,8 +1704,10 @@ void paintboard() al_hold_bitmap_drawing(true); updateAll(); + if (ssState.selectedTab == GameState::tabs::announcements) { + draw_announcements(df::global::world->status.announcements); + } al_hold_bitmap_drawing(false); - stonesenseState.map_segment.unlockDraw(); } diff --git a/GUI.h b/GUI.h index ae88eda2..bfe71be3 100644 --- a/GUI.h +++ b/GUI.h @@ -10,7 +10,6 @@ void handleMouseMove(int mouseX, int mouseY); void handleMouseWheel(int mouseX, int mouseY, int deltaY); void handleMouseRelease(); typedef void (*OnClickCallback)(uint32_t); -void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates); void clearElements(); void ScreenToPoint(int x,int y,int &x1, int &y1, int &z1); diff --git a/GameState.h b/GameState.h index 34bc4f3a..477901c1 100644 --- a/GameState.h +++ b/GameState.h @@ -31,6 +31,7 @@ struct GameState{ enum tabs { announcements, keybinds, + settings }; }; diff --git a/Keybinds.cpp b/Keybinds.cpp index 6b1862b7..3b0e7933 100644 --- a/Keybinds.cpp +++ b/Keybinds.cpp @@ -178,31 +178,31 @@ action_name_mapper actionnamemap[] = { {"ROTATE", action_incrrotation}, {"RELOAD_SEGMENT", action_reloadsegment}, {"PAINT", action_paintboard}, - {"TOGGLE_DESIGNATIONS", action_toggledesignations}, - {"TOGGLE_STOCKS", action_togglestockpiles}, - {"TOGGLE_ZONES", action_togglezones}, + //{"TOGGLE_DESIGNATIONS", action_toggledesignations}, + //{"TOGGLE_STOCKS", action_togglestockpiles}, + //{"TOGGLE_ZONES", action_togglezones}, {"TOGGLE_OCCLUSION", action_toggleocclusion}, - {"TOGGLE_FOG",action_togglefog}, - {"TOGGLE_CREATURE_MOODS", action_togglecreaturemood}, - {"TOGGLE_CREATURE_PROFS", action_togglecreatureprof}, - {"TOGGLE_CREATURE_JOBS", action_togglecreaturejob}, - {"TOGGLE_CREATURE_NAMES", action_togglecreaturenames}, + //{"TOGGLE_FOG",action_togglefog}, + //{"TOGGLE_CREATURE_MOODS", action_togglecreaturemood}, + //{"TOGGLE_CREATURE_PROFS", action_togglecreatureprof}, + //{"TOGGLE_CREATURE_JOBS", action_togglecreaturejob}, + //{"TOGGLE_CREATURE_NAMES", action_togglecreaturenames}, {"CHOP_WALLS", action_chopwall}, {"CYCLE_TRACKING_MODE", action_cycletrackingmode}, {"RESET_VIEW_OFFSET", action_resetscreen}, - {"TOGGLE_SINGLE_LAYER", action_togglesinglelayer}, - {"TOGGLE_SHADE_HIDDEN_TILES", action_toggleshadehidden}, - {"TOGGLE_SHOW_HIDDEN_TILES", action_toggleshowhidden}, + //{"TOGGLE_SINGLE_LAYER", action_togglesinglelayer}, + //{"TOGGLE_SHADE_HIDDEN_TILES", action_toggleshadehidden}, + //{"TOGGLE_SHOW_HIDDEN_TILES", action_toggleshowhidden}, {"TOGGLE_OSD", action_toggleosd}, - {"TOGGLE_KEYBINDS", action_togglekeybinds}, - {"TOGGLE_ANNOUNCEMENTS", action_toggleannouncements}, + //{"TOGGLE_KEYBINDS", action_togglekeybinds}, + //{"TOGGLE_ANNOUNCEMENTS", action_toggleannouncements}, {"TOGGLE_DEBUG", action_toggledebug}, {"INCR_ZOOM", action_incrzoom}, {"DECR_ZOOM", action_decrzoom}, {"SCREENSHOT", action_screenshot}, {"INCR_RELOAD_TIME", action_incrreloadtime}, {"DECR_RELOAD_TIME", action_decrreloadtime}, - {"CREDITS", action_credits}, + //{"CREDITS", action_credits}, {"DECR_SEGMENT_X", action_decrsegmentX}, {"INCR_SEGMENT_X", action_incrsegmentX}, diff --git a/UserInput.cpp b/UserInput.cpp index 64f43aa4..983b8765 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -457,6 +457,10 @@ void action_toggleannouncements(uint32_t keymod) { stonesenseState.ssState.selectedTab = GameState::tabs::announcements; } +void action_togglesettings(uint32_t keymod) { + stonesenseState.ssState.selectedTab = GameState::tabs::settings; +} + void action_toggleinfopanel(uint32_t keymod) { auto& ssConfig = stonesenseState.ssConfig; ssConfig.config.show_info_panel = !ssConfig.config.show_info_panel; diff --git a/UserInput.h b/UserInput.h index 6f364952..9b07d0be 100644 --- a/UserInput.h +++ b/UserInput.h @@ -28,6 +28,7 @@ void action_toggleosd(uint32_t keymod); void action_toggledebug(uint32_t keymod); void action_togglekeybinds(uint32_t keymod); void action_toggleannouncements(uint32_t keymod); +void action_togglesettings(uint32_t keymod); void action_toggleinfopanel(uint32_t keymod); void action_incrzoom(uint32_t keymod); void action_decrzoom(uint32_t keymod); diff --git a/main.cpp b/main.cpp index 13bb5aa6..53093eec 100644 --- a/main.cpp +++ b/main.cpp @@ -215,10 +215,6 @@ void drawcredits() // Make the backbuffer visible } -void addElems() { - //addButton(0, 0, 20, 20, dfColors::blue, dfColors::green, action_togglekeybinds, { "DEFAULT", "INFO_PANEL/ANNOUNCEMENTS" }); -} - /* main_loop: * The main loop of the program. Here we wait for events to come in from * any one of the event sources and react to each one accordingly. While @@ -232,7 +228,6 @@ static void main_loop(ALLEGRO_DISPLAY * display, ALLEGRO_EVENT_QUEUE *queue, ALL ALLEGRO_EVENT event; - addElems(); while (!al_get_thread_should_stop(main_thread)) { if (redraw && al_event_queue_is_empty(queue)) { @@ -317,7 +312,6 @@ static void main_loop(ALLEGRO_DISPLAY * display, ALLEGRO_EVENT_QUEUE *queue, ALL switch (event.type) { case ALLEGRO_EVENT_DISPLAY_RESIZE: clearElements(); - addElems(); if (ssConfig.overlay_mode) { break; } diff --git a/resources/GUI/control-buttons.png b/resources/GUI/control-buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..04957cd0ac4ac405740458f58da4e1f4a635d4c2 GIT binary patch literal 854 zcmV-c1F8IpP)Px&5J^NqRA_}cQ7i$N zeLXGNE%A?xj0M_=4l%0eM-#DL$4T|y33PGg@ooS+hrhCg{G4lC3*_hO+5yQAWdXpK zm-pR*%XVkT=}4w%E#^+-1E5QsAqRjTz6gomcd(BgatypKS8H?gGXS!gTz6NeVUOz- zOqX=cjhW(8A7Tug;%p|zbPy84N$C`HM(WV{VI?}!BRGKXi|-|! zA)%SSnU9U_habKOi7z_j7#)_{g))MRS_k)P)F()9Fyc>5=A~xNEx5NI-q7)N zT8G5<#(<5jI@1iTq!q0rlo6ce3-3?LK4Eob!N1l4qukLlT89m^wT{g|TkD7x+FD0b z_}8?Ks3B1-`PVv#?_cW>{Y!RBT1TWsB>)}C)Y5X%@JNQjH!wNxI-inV_rFG6RMKTZ gClZCu4@*?~39Zki$61m)AOHXW07*qoM6N<$f?WKh%>V!Z literal 0 HcmV?d00001 diff --git a/resources/GUI/simple-buttons.png b/resources/GUI/simple-buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..141ac602274444e107c1ead4aaba9727220edf8c GIT binary patch literal 554 zcmeAS@N?(olHy`uVBq!ia0vp^2|%pE!3HGD0_~lE6k~CayA#8@b22Z1>?fWsjv*P1 zZ)aP3GY1N|_2<>ZdvfOOVll`$OFCtu3GP(|I5I4!k!!>hXWa)Ue{e zGnAf{L}!{_@7Gjn-WwKm_G>cNa(%HavwRA#W!|bSbNTf5)0%Y*8pZQtZp_$x2N?Mbp00i_>zopr E0QzzHy#N3J literal 0 HcmV?d00001 diff --git a/resources/GUI/tabs.png b/resources/GUI/tabs.png new file mode 100644 index 0000000000000000000000000000000000000000..7b9ff14d756b5fca9834b324c8ca5b8f4860ec17 GIT binary patch literal 662 zcmV;H0%`q;P)Px%P)S5VR9J=WnXyajKpe-vl`L6G7b&!T-ReKk2QIGimTd7KkV3#g5OIA|)(+Xk zK=2Qc+uoAhrRZOvV|j&#%Oi_8>5$&#E3t+eafET#4uCQITm{Pv`XPjT2z&X83-&hY7Ra=;z3z#VJZ4{7=Cw0b< z3Sp1~4?ua#6!AHycfjM%Y-bJZWqk4$UWFk3oUQ7#4*-U}KC>_bt?*^@d~5rJ;(ak` z!6wYYGCpAv;U^4!ywh1cK7arJW6N>)1^^&Y@~MRaA2;5`@s0LQAyFcJ#_d$fx3_U5 z2QuvS8!KM^{3J@&ZBi1BFOF{-DISoT7~f9Q38jFKwQ%6rjZ7FSp)Ktq8*}m&3VgI> zfh~>EX-muaHVtes#Gk8K2*CMu+<5Wwk?~mocXgv=>-NzUSt#)F_H8Le78+$Bpk`qk z_R-||EEDXbky(iKa$ENC3^EIiGY}}Yuuc1Dcs|P%`)FhqwrwAc%t9j#1R&gL(3oH! z4dvSd^xvx0O=@9wC&cVCpm^@A3-4&c?ouogB>?n1rqZ31?eq1vL!tzo@p!y@q+cVQ w_lN1OfHh*7))wC_rT>ZSQXFG{#(bB20KEZ2x6bq4(f|Me07*qoM6N<$f_hjzng9R* literal 0 HcmV?d00001 diff --git a/resources/GUI/text.png b/resources/GUI/text.png new file mode 100644 index 0000000000000000000000000000000000000000..a17f51c7f19ae34cc183d050484bb9ee53c60e93 GIT binary patch literal 4839 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 N002ovPDHLkV1j&SK_dVF literal 0 HcmV?d00001 From d0cffc20098f0a4b459de04c84ba5762ac8e825d Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 17:04:24 -0500 Subject: [PATCH 13/36] remove debug code and unused vars --- GUI.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 431ea3f5..24f596fe 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -445,7 +445,7 @@ class GUIElement { virtual void draw() {} void update() { - al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); + //al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { draw(); } @@ -1424,11 +1424,8 @@ void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClick void drawInfoPanel(std::unordered_set infoStates) { - auto font = stonesenseState.font; - auto fontHeight = al_get_font_line_height(font); - int halftabHeight = (fontHeight + 10)/2; auto panelWidth = int(stonesenseState.ssState.InfoW / 8); - auto panelHeight = int((stonesenseState.ssState.ScreenH - fontHeight) / 12); + auto panelHeight = int((stonesenseState.ssState.ScreenH-TILE_HEIGHT) / 12); const char* label = " Info Panel "; //draw panel @@ -1479,7 +1476,6 @@ void addKeybindLine(int& rowNum, const char* keyname, const char* actionname, bo } void drawKeybinds() { - auto& ssState = stonesenseState.ssState; std::string* keyname, * actionname; int line = 1; From 03e9ef428685f9c111a90a9eb2229002e1f0331d Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 18:20:00 -0500 Subject: [PATCH 14/36] Clean up --- GUI.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 24f596fe..23a18bd4 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -361,13 +361,13 @@ void draw_color_ustr_border(const ALLEGRO_FONT* font, ALLEGRO_COLOR text_color, draw_color_border(font, text_color, border_color, x, y, flags, ustr); } -std::string fitTextToWidth(const std::string& input, ALLEGRO_FONT* font, int width) { +std::string fitTextToWidth(const std::string& input, int width) { std::string allCurrentChars; // The part of the string that fits so far for (char nextChar : input) { std::string testString = allCurrentChars + nextChar + "."; // Test adding the next char plus a dot - if (al_get_text_width(font, testString.c_str()) > width) { + if ((strlen(testString.c_str()) * TILE_WIDTH) > width) { return allCurrentChars + "."; // If too wide, return what we have with a dot } @@ -813,7 +813,7 @@ class Tab : public clickElement { bool isSelected = (stonesenseState.ssState.selectedTab == tabIndex); ALLEGRO_COLOR textColor = isSelected ? uiColor(dfColors::black) : uiColor(dfColors::white); - std::string temp = fitTextToWidth(label, stonesenseState.font, w - 20); + std::string temp = fitTextToWidth(label, w - 20); draw_tab(x, y, w, isSelected, upside_down, textColor, temp.c_str()); } @@ -907,7 +907,7 @@ void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, } } -void addTab(int x, int y, int w, int h, int tabIndex, const char* label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) { +void addTab(int x, int y, int w, int h, int tabIndex, const char* label, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) { if (!elementExists(x, y, w, h)) { elements.push_back(new Tab(x, y, w, h, tabIndex, label, onClickCallback, visibleStates, upside_down)); } @@ -950,6 +950,7 @@ namespace auto& ssConfig = stonesenseState.ssConfig; const int numAnnouncements = (int)announcements.size(); + int maxLines = (int((stonesenseState.ssState.ScreenH - (TILE_HEIGHT * 2)) / 12) / TILE_HEIGHT) - 2; const int maxAnnouncements = std::min(10, numAnnouncements); int line = 1; @@ -1406,7 +1407,7 @@ ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteShe return al_create_sub_bitmap(spriteSheet, sheetx * SPRITEWIDTH, sheety * SPRITEHEIGHT, SPRITEWIDTH, SPRITEHEIGHT); } -void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClickCallback onClickCallback, std::unordered_set visibleStates) { +void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCallback, std::unordered_set visibleStates) { int numTabs = 3; int tabWidth = (stonesenseState.ssState.InfoW - 16) / numTabs; int tabHeight = 24; @@ -1415,26 +1416,26 @@ void drawTab(ALLEGRO_FONT* font, const std::string& label, int tabIndex, OnClick int tabY = stonesenseState.ssState.ScreenH - tabHeight-4; // Flush with bottom // Truncate label to fit inside tab - auto temp = fitTextToWidth(label, font, tabWidth - 20); + auto temp = fitTextToWidth(label, tabWidth - 20); const char* truncatedLabel = temp.c_str(); - addTab(tabX, tabY, tabWidth, tabHeight, tabIndex, truncatedLabel, font, onClickCallback, visibleStates, true); + addTab(tabX, tabY, tabWidth, tabHeight, tabIndex, truncatedLabel, onClickCallback, visibleStates, true); } void drawInfoPanel(std::unordered_set infoStates) { auto panelWidth = int(stonesenseState.ssState.InfoW / 8); - auto panelHeight = int((stonesenseState.ssState.ScreenH-TILE_HEIGHT) / 12); + auto panelHeight = int((stonesenseState.ssState.ScreenH-(TILE_HEIGHT*2)) / 12); const char* label = " Info Panel "; //draw panel addPanel(stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, 0, panelWidth, panelHeight, infoStates, label); //draw tabs - drawTab(font, "Announcements", GameState::tabs::announcements, action_toggleannouncements, infoStates); - drawTab(font, "Keybinds", GameState::tabs::keybinds, action_togglekeybinds, infoStates); - drawTab(font, "Settings", GameState::tabs::settings, action_togglesettings, infoStates); + drawTab( "Announcements", GameState::tabs::announcements, action_toggleannouncements, infoStates); + drawTab( "Keybinds", GameState::tabs::keybinds, action_togglekeybinds, infoStates); + drawTab( "Settings", GameState::tabs::settings, action_togglesettings, infoStates); } From f3896c57ef6e4433b299513ebfb727f663ccbe4c Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 18:22:32 -0500 Subject: [PATCH 15/36] Fix signed issues --- GUI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 23a18bd4..5b554d8a 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -505,7 +505,7 @@ class GUIElement { static void draw_tile(int tile_index, int x, int y, std::vector tileset, int flip_flag = 0) { - if (tile_index >= 0 && tile_index < tileset.size() && tileset[tile_index]) { + if (tile_index >= 0 && static_cast(tile_index) < tileset.size() && tileset[tile_index]) { al_draw_bitmap(tileset[tile_index], x, y, flip_flag); } } @@ -517,7 +517,7 @@ class GUIElement { al_draw_filled_rectangle(x, y, x + TILE_WIDTH, y + TILE_HEIGHT, bg); // Draw the tile with foreground tint - if (tile_index >= 0 && tile_index < tileset.size() && tileset[tile_index]) { + if (tile_index >= 0 && static_cast(tile_index) < tileset.size() && tileset[tile_index]) { al_draw_tinted_bitmap(tileset[tile_index], fg, x, y, 0); } } From aadcaa9169a71e978cc90b0d972c28e77c12f012 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 19:25:02 -0500 Subject: [PATCH 16/36] Contain the announcements in the box --- GUI.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 5b554d8a..d91bb745 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -949,9 +949,10 @@ namespace void draw_announcements(std::vector& announcements) { auto& ssConfig = stonesenseState.ssConfig; + int panelHeight = int((stonesenseState.ssState.ScreenH - (TILE_HEIGHT * 2)) / TILE_HEIGHT); + int maxLines = panelHeight - 3; const int numAnnouncements = (int)announcements.size(); - int maxLines = (int((stonesenseState.ssState.ScreenH - (TILE_HEIGHT * 2)) / 12) / TILE_HEIGHT) - 2; - const int maxAnnouncements = std::min(10, numAnnouncements); + const int maxAnnouncements = std::min(100, numAnnouncements); int line = 1; for (int i = numAnnouncements - 1; i >= (numAnnouncements - maxAnnouncements) && announcements[i]->duration > 0; i--) { @@ -961,7 +962,9 @@ namespace for (const std::string& split : splits) { drawAnnouncemntLine(line, split, color); + if (line > maxLines) { break; } } + if (line > maxLines) { break; } } } @@ -1425,8 +1428,8 @@ void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCall void drawInfoPanel(std::unordered_set infoStates) { - auto panelWidth = int(stonesenseState.ssState.InfoW / 8); - auto panelHeight = int((stonesenseState.ssState.ScreenH-(TILE_HEIGHT*2)) / 12); + auto panelWidth = int(stonesenseState.ssState.InfoW / TILE_WIDTH); + auto panelHeight = int((stonesenseState.ssState.ScreenH-(TILE_HEIGHT*2)) / TILE_HEIGHT); const char* label = " Info Panel "; //draw panel From 763552b6e3739d8e0f4baeb5ac64248fa31fb161 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 19:32:59 -0500 Subject: [PATCH 17/36] missed a few casts --- GUI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index d91bb745..a6a4ba9b 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -367,7 +367,7 @@ std::string fitTextToWidth(const std::string& input, int width) { for (char nextChar : input) { std::string testString = allCurrentChars + nextChar + "."; // Test adding the next char plus a dot - if ((strlen(testString.c_str()) * TILE_WIDTH) > width) { + if (static_cast(strlen(testString.c_str()) * TILE_WIDTH) > width) { return allCurrentChars + "."; // If too wide, return what we have with a dot } @@ -390,7 +390,7 @@ std::vector splitLinesToWidth(const std::string& input, int width) // Test if adding this word to the current string would exceed the width std::string testString = allCurrentChars + (allCurrentChars.empty() ? "" : " ") + word; - if ((strlen(testString.c_str())*TILE_WIDTH) > width) { + if (static_cast(strlen(testString.c_str())*TILE_WIDTH) > width) { // If it's too wide, stop and push the current string into the vector if (!allCurrentChars.empty()) { splits.push_back(allCurrentChars); From c9973fc352cd56aeb85421a159b3517d22dc10ad Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 19:45:13 -0500 Subject: [PATCH 18/36] remove mouse rclick to move and add scroll handling --- GUI.cpp | 4 +++- GUI.h | 2 +- UserInput.cpp | 51 ++++++++++++++++++--------------------------------- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index a6a4ba9b..fecbff16 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -864,12 +864,14 @@ void handleMouseMove(int mouseX, int mouseY) { } // Handle mouse wheel scrolling -void handleMouseWheel(int mouseX, int mouseY, int deltaY) { +bool handleMouseWheel(int mouseX, int mouseY, int deltaY) { for (auto* elem : elements) { if (elem->isMouseOver(mouseX, mouseY)) { elem->onScroll(deltaY); + return true; } } + return false; } bool elementExists(int x, int y, int w, int h) { diff --git a/GUI.h b/GUI.h index bfe71be3..263284a4 100644 --- a/GUI.h +++ b/GUI.h @@ -7,7 +7,7 @@ // GUI Element stuffs void handleMouseClick(int mouseX, int mouseY); void handleMouseMove(int mouseX, int mouseY); -void handleMouseWheel(int mouseX, int mouseY, int deltaY); +bool handleMouseWheel(int mouseX, int mouseY, int deltaY); void handleMouseRelease(); typedef void (*OnClickCallback)(uint32_t); void clearElements(); diff --git a/UserInput.cpp b/UserInput.cpp index 983b8765..ced8f906 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -138,42 +138,27 @@ void doMouse() handleMouseMove(mouse.x, mouse.y); if (mouse.z != last_mouse_z) { auto deltaY = last_mouse_z - mouse.z; - if(mouse.z < last_mouse_z) { - if(ssConfig.config.invert_mouse_z) { - action_incrZ(keymod); + bool didScroll = handleMouseWheel(mouse.x, mouse.y, deltaY); + if (!didScroll) { + if(mouse.z < last_mouse_z) { + if(ssConfig.config.invert_mouse_z) { + action_incrZ(keymod); + } + else { + action_decrZ(keymod); + } + last_mouse_z = mouse.z; } - else { - action_decrZ(keymod); + if(mouse.z > last_mouse_z) { + if(ssConfig.config.invert_mouse_z) { + action_decrZ(keymod); + } + else { + action_incrZ(keymod); + } + last_mouse_z = mouse.z; } - last_mouse_z = mouse.z; } - if(mouse.z > last_mouse_z) { - if(ssConfig.config.invert_mouse_z) { - action_decrZ(keymod); - } - else { - action_incrZ(keymod); - } - last_mouse_z = mouse.z; - } - handleMouseWheel(mouse.x, mouse.y, deltaY); - } - if( mouse.buttons & 2 ) { - ssConfig.config.track_mode = Config::TRACKING_NONE; - int x, y; - x = mouse.x; - y = mouse.y; - int tilex,tiley,tilez; - ScreenToPoint(x,y,tilex,tiley,tilez); - int diffx = tilex - ssState.Size.x/2; - int diffy = tiley - ssState.Size.y/2; - /*we use changeRelativeToRotation directly, and not through moveViewRelativeToRotation - because we don't want to move the offset with the mouse. It just feels weird. */ - // changing to +1,+1 which moves the clicked point to one of the 4 surrounding the center of rotation - changeRelativeToRotation(ssState.Position.x, ssState.Position.y, diffx+1, diffy+1 ); - //moveViewRelativeToRotation(diffx+1, diffy+1); - stonesenseState.timeToReloadSegment = true; - //rest(50); } if (stonesenseState.mouseHeld && !(mouse.buttons & 1)) { handleMouseRelease(); } if( mouse.buttons & 1 ) { From ad273332ebec46988c8db15baa0f6bb7309a3a54 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Tue, 25 Mar 2025 22:33:12 -0500 Subject: [PATCH 19/36] Add new settings --- Config.h | 2 ++ GUI.cpp | 4 ++++ MapLoading.cpp | 4 +++- UserInput.cpp | 6 ++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Config.h b/Config.h index 475a33c3..3eb1f07a 100644 --- a/Config.h +++ b/Config.h @@ -37,6 +37,8 @@ class Config { int animation_step = 196; bool verbose_logging = false; trackingmode track_mode = TRACKING_CENTER; + bool force_track = true; + bool track_zoom = true; bool invert_mouse_z = false; bool follow_DFcursor = true; bool show_creature_names = false; diff --git a/GUI.cpp b/GUI.cpp index fecbff16..0e0680e2 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -1460,12 +1460,16 @@ void drawSettings() { addSettingLine(rowNum, ssConfig.config.names_use_species, "Use species name"); addSettingLine(rowNum, ssConfig.config.show_creature_jobs, "Show creature jobs"); addSettingLine(rowNum, ssConfig.config.show_creature_moods, "Show creature moods"); + rowNum++; addSettingLine(rowNum, ssConfig.config.fogenable, "Draw depth fog"); addSettingLine(rowNum, ssConfig.config.show_stockpiles, "Show stockpiles"); addSettingLine(rowNum, ssConfig.config.show_zones, "Show zones"); addSettingLine(rowNum, ssConfig.show_designations, "Show designations"); addSettingLine(rowNum, ssConfig.show_hidden_tiles, "Show unrevealed tiles (cheat)"); addSettingLine(rowNum, ssConfig.shade_hidden_tiles, "Show blackboxes"); + rowNum++; + addSettingLine(rowNum, ssConfig.config.force_track, "Follow DF view"); + addSettingLine(rowNum, ssConfig.config.track_zoom, "Track zoom with view"); } void addKeybindLine(int& rowNum, const char* keyname, const char* actionname, bool repeats) { diff --git a/MapLoading.cpp b/MapLoading.cpp index ce2ad862..38dd5a6e 100644 --- a/MapLoading.cpp +++ b/MapLoading.cpp @@ -956,7 +956,9 @@ void read_segment( void *arg) if (stonesenseState.ssConfig.config.track_mode == Config::TRACKING_CENTER) { followCurrentDFCenter(); } - stonesenseState.ssConfig.zoom = (df::global::gps->viewport_zoom_factor - 64) / 16; + if (stonesenseState.ssConfig.config.track_zoom) { + stonesenseState.ssConfig.zoom = (df::global::gps->viewport_zoom_factor - 64) / 16; + } stonesenseState.ssConfig.recalculateScale(); } segment = stonesenseState.map_segment.getRead(); diff --git a/UserInput.cpp b/UserInput.cpp index ced8f906..2ba19ee0 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -525,6 +525,7 @@ void action_decrY(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); + if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -539,6 +540,7 @@ void action_incrY(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); + if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -553,6 +555,7 @@ void action_decrX(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); + if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -567,6 +570,7 @@ void action_incrX(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); + if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -584,6 +588,7 @@ void action_decrZ(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); + if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -608,6 +613,7 @@ void action_incrZ(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); + if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { ssConfig.config.track_mode = Config::TRACKING_NONE; } From 128fd6bdaf2b74a3c0de7abf0c38cc9a573e0fa3 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 12:18:54 -0500 Subject: [PATCH 20/36] Fix the bright bool --- GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI.cpp b/GUI.cpp index f8869f31..79ab2cf9 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -958,7 +958,7 @@ namespace int line = 1; for (int i = numAnnouncements - 1; i >= (numAnnouncements - maxAnnouncements) && announcements[i]->duration > 0; i--) { - ALLEGRO_COLOR color = ssConfig.config.colors.getDfColor(announcements[i]->color, ssConfig.config.useDfColors); + ALLEGRO_COLOR color = ssConfig.config.colors.getDfColor(announcements[i]->color, true, ssConfig.config.useDfColors); std::string reportStr = announcements[i]->text; std::vector splits = splitLinesToWidth(reportStr, stonesenseState.ssState.InfoW - 20); From 52a4fba76b72ec82941940454caeabe5e479153c Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 12:21:03 -0500 Subject: [PATCH 21/36] fix the cast --- GUI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 79ab2cf9..294d189f 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -367,7 +367,7 @@ std::string fitTextToWidth(const std::string& input, int width) { for (char nextChar : input) { std::string testString = allCurrentChars + nextChar + "."; // Test adding the next char plus a dot - if (static_cast(strlen(testString.c_str()) * TILE_WIDTH) > width) { + if ((strlen(testString.c_str()) * TILE_WIDTH) > static_cast(width)) { return allCurrentChars + "."; // If too wide, return what we have with a dot } @@ -390,7 +390,7 @@ std::vector splitLinesToWidth(const std::string& input, int width) // Test if adding this word to the current string would exceed the width std::string testString = allCurrentChars + (allCurrentChars.empty() ? "" : " ") + word; - if (static_cast(strlen(testString.c_str())*TILE_WIDTH) > width) { + if ((strlen(testString.c_str()) * TILE_WIDTH) > static_cast(width)) { // If it's too wide, stop and push the current string into the vector if (!allCurrentChars.empty()) { splits.push_back(allCurrentChars); From 4841ad6c4ffb694ba4ae4b9e4031256f187c2630 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 13:56:40 -0500 Subject: [PATCH 22/36] Apply some changes from code review --- GUI.cpp | 142 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 70 insertions(+), 72 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 294d189f..1fc7de13 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -73,8 +73,8 @@ ALLEGRO_BITMAP* bigFile = 0; GLhandleARB tinter; GLhandleARB tinter_shader; -const int TILE_WIDTH = 8; -const int TILE_HEIGHT = 12; +constexpr int TILE_WIDTH = 8; +constexpr int TILE_HEIGHT = 12; class ImageCache { @@ -419,9 +419,9 @@ class GUIElement { protected: static std::vector tiles; static std::vector letterTiles; -public: int x, y, w, h; // Position and size of the element bool hovered; // Tracks if the mouse is over the element +public: std::unordered_set visibleStates; GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) @@ -434,15 +434,13 @@ class GUIElement { } } - virtual ~GUIElement() {} + virtual void onHover() = 0; - virtual void onHover() {} + virtual void onHoverExit() = 0; - virtual void onHoverExit() {} + virtual void onScroll(int deltaY) = 0; - virtual void onScroll(int deltaY) {} - - virtual void draw() {} + virtual void draw() = 0; void update() { //al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); @@ -475,10 +473,10 @@ class GUIElement { h = newH; } - std::vector loadTileset(const char* tilesetPath, ALLEGRO_COLOR alphaMask) { + std::vector loadTileset(std::string tilesetPath, ALLEGRO_COLOR alphaMask) { std::vector allTiles; // Store extracted tiles - auto tileset = al_load_bitmap(tilesetPath); + auto tileset = al_load_bitmap(tilesetPath.c_str()); if (!tileset) { printf("Failed to load tileset!\n"); return {}; @@ -529,10 +527,10 @@ class GUIElement { }; static void draw_text_with_tiles(int x, int y, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, - const char* text, TextAlign align = ALIGN_LEFT) { - if (!text) return; + std::string text, TextAlign align = ALIGN_LEFT) { + if (text.empty()) return; - int label_length = strlen(text); + int label_length = text.length(); int start_x = x; // Handle text alignment @@ -563,11 +561,11 @@ class textElement : public GUIElement { ALLEGRO_COLOR bg; TextAlign align; - textElement(int x, int y, int w, int h, std::unordered_set visibleStates, const char* text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, TextAlign align = ALIGN_LEFT) + textElement(int x, int y, int w, int h, std::unordered_set visibleStates, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, TextAlign align = ALIGN_LEFT) : GUIElement(x, y, w, h, visibleStates), text(text), fg(fg), bg(bg), align(align) { } - void setText(const char* newText) { text = newText; } // Copy new text safely + void setText(std::string newText) { text = newText; } // Copy new text safely void draw() override { GUIElement::draw_text_with_tiles(x, y, fg, bg, text.c_str(), align); @@ -577,12 +575,12 @@ class textElement : public GUIElement { class WindowPanel : public GUIElement { public: - const char* label; + std::string label; - WindowPanel(int x, int y, int w, int h, std::unordered_set visibleStates, const char* label) :GUIElement(x, y, w, h, visibleStates), label(label) {} + WindowPanel(int x, int y, int w, int h, std::unordered_set visibleStates, std::string label) :GUIElement(x, y, w, h, visibleStates), label(label) {} - void draw_window(float x, float y, int width, int height, const char* label, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { + void draw_window(float x, float y, int width, int height, std::string label, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { if (width < 3) width = 3; if (height < 3) height = 3; @@ -612,8 +610,8 @@ class WindowPanel : public GUIElement { } // Draw the centered title using CP437 letter tiles - if (label) { - int label_length = strlen(label); + if (!label.empty()) { + int label_length = label.length(); int start_x = x + (width * TILE_WIDTH - label_length * TILE_WIDTH) / 2; // Center horizontally for (int i = 0; i < label_length; i++) { @@ -659,13 +657,13 @@ class clickElement : public GUIElement { class LabeledButton : public clickElement { public: - const char* label; + std::string label; ALLEGRO_FONT* font; int32_t borderColor; int32_t bgColor; LabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, - const char* label, ALLEGRO_FONT* font, + std::string label, ALLEGRO_FONT* font, OnClickCallback clickFn, std::unordered_set visibleStates) : clickElement(x, y, w, h, clickFn, visibleStates), label(label), font(font), borderColor(borderColor), bgColor(bgColor) { @@ -680,7 +678,7 @@ class LabeledButton : public clickElement { if (font) { int textHeight = al_get_font_line_height(font); al_draw_text(font, textColor, x + (w / 2), y + (h - textHeight) / 2, - ALLEGRO_ALIGN_CENTER, label); + ALLEGRO_ALIGN_CENTER, label.c_str()); } } }; @@ -696,7 +694,7 @@ class simpleButton : public clickElement { simpleButtons = loadTileset("stonesense/GUI/simple-buttons.png", al_map_rgb(255, 0, 255)); } - void draw_simple_button(float x, float y, const char* text) { + void draw_simple_button(float x, float y, std::string text) { int colorOffset = 3; int rowOffset = 12; @@ -708,7 +706,7 @@ class simpleButton : public clickElement { } // Draw the centered title using CP437 letter tiles - if (text) { + if (!text.empty()) { int tile_index = get_cp437_tile_index(text[0]); draw_tinted_tile(tile_index, x + TILE_WIDTH, y + TILE_HEIGHT, letterTiles, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0)); } @@ -768,34 +766,35 @@ class Tab : public clickElement { tabSet = loadTileset("stonesense/GUI/tabs.png", al_map_rgb(28,28,28)); } - void draw_tab(float x, float y, int width, bool is_enabled, bool is_upside_down, ALLEGRO_COLOR fg, const char* text) { + void draw_tab(float x, float y, int width, bool is_enabled, bool is_upside_down, ALLEGRO_COLOR fg, std::string text) { // Flip flag (use ALLEGRO_FLIP_VERTICAL for upside-down tabs) int flip_flag = is_upside_down ? ALLEGRO_FLIP_VERTICAL : 0; // Draw corners int tileShift = is_upside_down ? 10 : 0; - draw_tile(tileShift + 0 + (is_enabled ? 5 : 0), x + TILE_WIDTH, y, tabSet, flip_flag); // Top-left - draw_tile(tileShift + 1 + (is_enabled ? 5 : 0), x + TILE_WIDTH + TILE_WIDTH, y, tabSet, flip_flag); + int enabledShift = is_enabled ? 5 : 0; + draw_tile(tileShift + 0 + enabledShift, x + TILE_WIDTH, y, tabSet, flip_flag); // Top-left + draw_tile(tileShift + 1 + enabledShift, x + TILE_WIDTH + TILE_WIDTH, y, tabSet, flip_flag); - draw_tile(tileShift + 3 + (is_enabled ? 5 : 0), x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y, tabSet, flip_flag); // Top-right - draw_tile(tileShift + 4 + (is_enabled ? 5 : 0), x + ((width / TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y, tabSet, flip_flag); + draw_tile(tileShift + 3 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y, tabSet, flip_flag); // Top-right + draw_tile(tileShift + 4 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y, tabSet, flip_flag); - draw_tile(-tileShift + 10 + (is_enabled ? 5 : 0), x + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-left - draw_tile(-tileShift + 11 + (is_enabled ? 5 : 0), x + TILE_WIDTH + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); + draw_tile(-tileShift + 10 + enabledShift, x + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-left + draw_tile(-tileShift + 11 + enabledShift, x + TILE_WIDTH + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); - draw_tile(-tileShift + 13 + (is_enabled ? 5 : 0), x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y + TILE_HEIGHT, tabSet, flip_flag); - draw_tile(-tileShift + 14 + (is_enabled ? 5 : 0), x + ((width/TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-right + draw_tile(-tileShift + 13 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y + TILE_HEIGHT, tabSet, flip_flag); + draw_tile(-tileShift + 14 + enabledShift, x + ((width/TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-right //// Draw top & bottom edges for (int col = 3; col < (width / TILE_WIDTH) - 1; ++col) { - draw_tile(tileShift + 2 + (is_enabled ? 5 : 0), x + col * TILE_WIDTH, y, tabSet, flip_flag); // Top - draw_tile(-tileShift + 12 + (is_enabled ? 5 : 0), x + col * TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom + draw_tile(tileShift + 2 + enabledShift, x + col * TILE_WIDTH, y, tabSet, flip_flag); // Top + draw_tile(-tileShift + 12 + enabledShift, x + col * TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom } // Draw the centered title using CP437 letter tiles - if (text) { - int label_length = strlen(text); + if (!text.empty()) { + int label_length = text.length(); //int usable_width = (width / TILE_WIDTH) - 4; // Account for non-writable tiles //int start_tile = (usable_width - label_length) / 2 + 2; // Centering formula //int start_x = x + start_tile * TILE_WIDTH; // Convert to pixels @@ -825,18 +824,18 @@ class Tab : public clickElement { // Global list of GUI elements -std::vector elements; +std::vector> elements; void updateAll() { - for (auto* elem : elements) { + for (auto& elem : elements) { // Use reference to unique_ptr elem->update(); } } // Handle mouse click events void handleMouseClick(int mouseX, int mouseY) { - for (auto* elem : elements) { - if (auto* clickElem = dynamic_cast(elem)) { + for (auto& elem : elements) { + if (auto* clickElem = dynamic_cast(elem.get())) { // Use .get() to access raw pointer if (clickElem->isMouseOver(mouseX, mouseY)) { clickElem->onClick(); break; @@ -847,25 +846,24 @@ void handleMouseClick(int mouseX, int mouseY) { // Handle mouse release events void handleMouseRelease() { - for (auto* elem : elements) { - if (auto* clickElem = dynamic_cast(elem)) { + for (auto& elem : elements) { + if (auto* clickElem = dynamic_cast(elem.get())) { clickElem->onRelease(); } } stonesenseState.mouseHeld = false; } - // Handle mouse movement for hover state void handleMouseMove(int mouseX, int mouseY) { - for (auto* elem : elements) { + for (auto& elem : elements) { elem->updateHoverState(mouseX, mouseY); } } // Handle mouse wheel scrolling bool handleMouseWheel(int mouseX, int mouseY, int deltaY) { - for (auto* elem : elements) { + for (auto& elem : elements) { if (elem->isMouseOver(mouseX, mouseY)) { elem->onScroll(deltaY); return true; @@ -875,7 +873,7 @@ bool handleMouseWheel(int mouseX, int mouseY, int deltaY) { } bool elementExists(int x, int y, int w, int h) { - for (GUIElement* elem : elements) { + for (auto& elem : elements) { if (elem->x == x && elem->y == y && elem->w == w && elem->h == h) { return true; // Found an element with the same position and size } @@ -883,57 +881,57 @@ bool elementExists(int x, int y, int w, int h) { return false; } + void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates) { if (!elementExists(x, y, w, h)) { - elements.push_back(new clickElement(x, y, w, h, onClickCallback, visibleStates)); + elements.push_back(std::make_unique(x, y, w, h, onClickCallback, visibleStates)); } } void addLabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, - const char* label, ALLEGRO_FONT* font, + std::string label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, std::unordered_set visibleStates) { if (!elementExists(x, y, w, h)) { - elements.push_back(new LabeledButton(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, visibleStates)); + elements.push_back(std::make_unique(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, visibleStates)); } } -void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, std::unordered_set visibleStates){ +void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, std::unordered_set visibleStates) { if (!elementExists(x, y, w, h)) { - elements.push_back(new simpleButton(x, y, w, h, icon, colorFlag, onClickCallback, visibleStates)); + elements.push_back(std::make_unique(x, y, w, h, icon, colorFlag, onClickCallback, visibleStates)); } } void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, std::unordered_set visibleStates) { if (!elementExists(x, y, w, h)) { - elements.push_back(new controlButton(x, y, w, h, thick, enabledVar, visibleStates)); + elements.push_back(std::make_unique(x, y, w, h, thick, enabledVar, visibleStates)); } } -void addTab(int x, int y, int w, int h, int tabIndex, const char* label, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) { +void addTab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) { if (!elementExists(x, y, w, h)) { - elements.push_back(new Tab(x, y, w, h, tabIndex, label, onClickCallback, visibleStates, upside_down)); + elements.push_back(std::make_unique(x, y, w, h, tabIndex, label, onClickCallback, visibleStates, upside_down)); } } -void addPanel(int x, int y, int w, int h, std::unordered_set visibleStates, const char* label) { +void addPanel(int x, int y, int w, int h, std::unordered_set visibleStates, std::string label) { if (!elementExists(x, y, w, h)) { - elements.push_back(new WindowPanel(x, y, w, h, visibleStates, label)); + elements.push_back(std::make_unique(x, y, w, h, visibleStates, label)); } } -void addText(int x, int y, int w, int h, std::unordered_set visibleStates, const char* text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, GUIElement::TextAlign align) { +void addText(int x, int y, int w, int h, std::unordered_set visibleStates, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, GUIElement::TextAlign align) { if (!elementExists(x, y, w, h)) { - elements.push_back(new textElement(x, y, w, h, visibleStates, text, fg, bg, align)); + elements.push_back(std::make_unique(x, y, w, h, visibleStates, text, fg, bg, align)); } } + void clearElements() { - for (auto* element : elements) { - delete element; // Free allocated memory - } - elements.clear(); // Remove all pointers from the vector + elements.clear(); // Automatically deletes all elements } + namespace { void drawAnnouncemntLine(int& rowNum, const std::string& text, ALLEGRO_COLOR reportColor) { @@ -941,7 +939,7 @@ namespace int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; const char* report = text.c_str(); - // Use text.length() * TILE_WIDTH instead of strlen(text) + GUIElement::draw_text_with_tiles(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), reportColor, al_map_rgba(0, 0, 0, 0), report, GUIElement::TextAlign::ALIGN_LEFT); @@ -1432,7 +1430,7 @@ void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCall void drawInfoPanel(std::unordered_set infoStates) { auto panelWidth = int(stonesenseState.ssState.InfoW / TILE_WIDTH); auto panelHeight = int((stonesenseState.ssState.ScreenH-(TILE_HEIGHT*2)) / TILE_HEIGHT); - const char* label = " Info Panel "; + std::string label = " Info Panel "; //draw panel addPanel(stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW, 0, panelWidth, panelHeight, infoStates, label); @@ -1444,10 +1442,10 @@ void drawInfoPanel(std::unordered_set infoStates) { } -void addSettingLine(int& rowNum, bool& settingVar, const char* text) { +void addSettingLine(int& rowNum, bool& settingVar, std::string text) { int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; addControlButton(startX, TILE_HEIGHT+(rowNum*TILE_HEIGHT), 3 * TILE_WIDTH, TILE_HEIGHT, false, settingVar, {"INFO_PANEL/SETTINGS"}); - addText(startX + TILE_WIDTH * 4, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH * strlen(text), TILE_HEIGHT, { "INFO_PANEL/SETTINGS" }, text, uiColor(dfColors::cyan), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + addText(startX + TILE_WIDTH * 4, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH * text.length(), TILE_HEIGHT, { "INFO_PANEL/SETTINGS" }, text, uiColor(dfColors::cyan), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); rowNum++; } @@ -1472,11 +1470,11 @@ void drawSettings() { addSettingLine(rowNum, ssConfig.config.track_zoom, "Track zoom with view"); } -void addKeybindLine(int& rowNum, const char* keyname, const char* actionname, bool repeats) { +void addKeybindLine(int& rowNum, std::string keyname, std::string actionname, bool repeats) { int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; - int keyW = TILE_WIDTH * strlen(keyname); - int actionW= TILE_WIDTH * strlen(actionname); + int keyW = TILE_WIDTH * keyname.length(); + int actionW= TILE_WIDTH * actionname.length(); addText(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), keyW, TILE_HEIGHT, { "INFO_PANEL/KEYBINDS" }, keyname, uiColor(dfColors::lgreen), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); addText(startX+keyW, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH*2, TILE_HEIGHT, { "INFO_PANEL/KEYBINDS" }, ": ", uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); From 936609e718fcfb92e45d32e4b711ac1f2c639100 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 14:11:42 -0500 Subject: [PATCH 23/36] apply more code review stuff --- GUI.cpp | 21 +++++++-------------- GUI.h | 3 +-- Keybinds.cpp | 14 -------------- main.cpp | 2 +- 4 files changed, 9 insertions(+), 31 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 1fc7de13..9b711619 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -361,23 +361,14 @@ void draw_color_ustr_border(const ALLEGRO_FONT* font, ALLEGRO_COLOR text_color, draw_color_border(font, text_color, border_color, x, y, flags, ustr); } -std::string fitTextToWidth(const std::string& input, int width) { - std::string allCurrentChars; // The part of the string that fits so far - - for (char nextChar : input) { - std::string testString = allCurrentChars + nextChar + "."; // Test adding the next char plus a dot - - if ((strlen(testString.c_str()) * TILE_WIDTH) > static_cast(width)) { - return allCurrentChars + "."; // If too wide, return what we have with a dot - } - - allCurrentChars += nextChar; // Append the character if it fits +std::string fitTextToWidth(const std::string input, int width) { + int max_chars = width / TILE_WIDTH; + if (input.length() > max_chars) { + return input.substr(0, max_chars - 1) + "."; } - - return allCurrentChars; // If we reached the end, return the full string + return input; } - std::vector splitLinesToWidth(const std::string& input, int width) { std::vector splits; // To store each part of the split string std::string allCurrentChars; // The part of the string that fits so far @@ -434,6 +425,8 @@ class GUIElement { } } + virtual ~GUIElement() = 0; + virtual void onHover() = 0; virtual void onHoverExit() = 0; diff --git a/GUI.h b/GUI.h index 263284a4..d0ccb3a2 100644 --- a/GUI.h +++ b/GUI.h @@ -2,14 +2,13 @@ #include "common.h" #include -#include // GUI Element stuffs void handleMouseClick(int mouseX, int mouseY); void handleMouseMove(int mouseX, int mouseY); bool handleMouseWheel(int mouseX, int mouseY, int deltaY); void handleMouseRelease(); -typedef void (*OnClickCallback)(uint32_t); +using OnClickCallback = auto (*)(uint32_t) -> void; void clearElements(); void ScreenToPoint(int x,int y,int &x1, int &y1, int &z1); diff --git a/Keybinds.cpp b/Keybinds.cpp index 3b0e7933..e7f26772 100644 --- a/Keybinds.cpp +++ b/Keybinds.cpp @@ -178,31 +178,17 @@ action_name_mapper actionnamemap[] = { {"ROTATE", action_incrrotation}, {"RELOAD_SEGMENT", action_reloadsegment}, {"PAINT", action_paintboard}, - //{"TOGGLE_DESIGNATIONS", action_toggledesignations}, - //{"TOGGLE_STOCKS", action_togglestockpiles}, - //{"TOGGLE_ZONES", action_togglezones}, {"TOGGLE_OCCLUSION", action_toggleocclusion}, - //{"TOGGLE_FOG",action_togglefog}, - //{"TOGGLE_CREATURE_MOODS", action_togglecreaturemood}, - //{"TOGGLE_CREATURE_PROFS", action_togglecreatureprof}, - //{"TOGGLE_CREATURE_JOBS", action_togglecreaturejob}, - //{"TOGGLE_CREATURE_NAMES", action_togglecreaturenames}, {"CHOP_WALLS", action_chopwall}, {"CYCLE_TRACKING_MODE", action_cycletrackingmode}, {"RESET_VIEW_OFFSET", action_resetscreen}, - //{"TOGGLE_SINGLE_LAYER", action_togglesinglelayer}, - //{"TOGGLE_SHADE_HIDDEN_TILES", action_toggleshadehidden}, - //{"TOGGLE_SHOW_HIDDEN_TILES", action_toggleshowhidden}, {"TOGGLE_OSD", action_toggleosd}, - //{"TOGGLE_KEYBINDS", action_togglekeybinds}, - //{"TOGGLE_ANNOUNCEMENTS", action_toggleannouncements}, {"TOGGLE_DEBUG", action_toggledebug}, {"INCR_ZOOM", action_incrzoom}, {"DECR_ZOOM", action_decrzoom}, {"SCREENSHOT", action_screenshot}, {"INCR_RELOAD_TIME", action_incrreloadtime}, {"DECR_RELOAD_TIME", action_decrreloadtime}, - //{"CREDITS", action_credits}, {"DECR_SEGMENT_X", action_decrsegmentX}, {"INCR_SEGMENT_X", action_incrsegmentX}, diff --git a/main.cpp b/main.cpp index 53093eec..09b7777a 100644 --- a/main.cpp +++ b/main.cpp @@ -171,7 +171,7 @@ void animUpdateProc() } int getInfoWidth() { - return int(stonesenseState.ssState.ScreenH / 2.25); + return stonesenseState.ssState.ScreenH * 4 / 9; } void drawcredits() From 52cf67b44c9e3fd15c738f2f599a63fe6c59f0be Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 15:11:32 -0500 Subject: [PATCH 24/36] Code review --- GUI.cpp | 27 +++++++++++---------------- UserInput.cpp | 4 +--- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 9b711619..1526780d 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -410,28 +410,21 @@ class GUIElement { protected: static std::vector tiles; static std::vector letterTiles; +public: int x, y, w, h; // Position and size of the element bool hovered; // Tracks if the mouse is over the element -public: std::unordered_set visibleStates; GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) - : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) { - if (tiles.empty()) { - tiles = loadTileset("hack/data/art/border-window.png", al_map_rgb(255, 0, 255)); - } - if (letterTiles.empty()) { - letterTiles = loadTileset("stonesense/GUI/text.png", al_map_rgb(255, 0, 255)); - } - } + : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) {} - virtual ~GUIElement() = 0; + virtual ~GUIElement() {} - virtual void onHover() = 0; + virtual void onHover() {} - virtual void onHoverExit() = 0; + virtual void onHoverExit() {} - virtual void onScroll(int deltaY) = 0; + virtual void onScroll(int deltaY) {} virtual void draw() = 0; @@ -466,7 +459,7 @@ class GUIElement { h = newH; } - std::vector loadTileset(std::string tilesetPath, ALLEGRO_COLOR alphaMask) { + static std::vector loadTileset(std::string tilesetPath, ALLEGRO_COLOR alphaMask) { std::vector allTiles; // Store extracted tiles auto tileset = al_load_bitmap(tilesetPath.c_str()); @@ -544,8 +537,8 @@ class GUIElement { }; -std::vector GUIElement::tiles; -std::vector GUIElement::letterTiles; +std::vector GUIElement::tiles{ loadTileset("hack/data/art/border-window.png", al_map_rgb(255, 0, 255)) }; +std::vector GUIElement::letterTiles{ loadTileset("stonesense/GUI/text.png", al_map_rgb(255, 0, 255)) }; class textElement : public GUIElement { public: @@ -646,6 +639,8 @@ class clickElement : public GUIElement { virtual void onRelease() { held = false; } + + void draw() override {} }; class LabeledButton : public clickElement { diff --git a/UserInput.cpp b/UserInput.cpp index 2ba19ee0..7d377de6 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -137,9 +137,7 @@ void doMouse() auto& mouse = stonesenseState.mouse; handleMouseMove(mouse.x, mouse.y); if (mouse.z != last_mouse_z) { - auto deltaY = last_mouse_z - mouse.z; - bool didScroll = handleMouseWheel(mouse.x, mouse.y, deltaY); - if (!didScroll) { + if (!handleMouseWheel(mouse.x, mouse.y, (last_mouse_z - mouse.z))) { if(mouse.z < last_mouse_z) { if(ssConfig.config.invert_mouse_z) { action_incrZ(keymod); From 58fbf1fbe85cfcfcd438db52d30778460150152a Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 18:57:28 -0500 Subject: [PATCH 25/36] code review --- GUI.cpp | 391 ++++++++++++++++++++++++++++------------------ StonesenseState.h | 14 +- 2 files changed, 252 insertions(+), 153 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 1526780d..0317fc28 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -405,18 +405,104 @@ std::vector splitLinesToWidth(const std::string& input, int width) return splits; } +#include + +constexpr size_t MAX_UI_STATES = static_cast(StonesenseState::UIState::COUNT); +using UIStateSet = std::bitset; + +class Tileset { +protected: + std::vector tileset; + ALLEGRO_BITMAP* tilesheet; + +public: + Tileset(const std::string& filepath, ALLEGRO_COLOR alphaMask) { + tilesheet = al_load_bitmap(filepath.c_str()); + if (!tilesheet) { + LogError("Failed to load tileset!\n"); + throw std::runtime_error("Failed to load tileset: File not found or invalid format."); + } + + int tileset_width = al_get_bitmap_width(tilesheet); + int tileset_height = al_get_bitmap_height(tilesheet); + int tiles_per_row = tileset_width / TILE_WIDTH; + int tiles_per_col = tileset_height / TILE_HEIGHT; + + for (int y = 0; y < tiles_per_col; ++y) { + for (int x = 0; x < tiles_per_row; ++x) { + ALLEGRO_BITMAP* tile = al_create_sub_bitmap(tilesheet, x * TILE_WIDTH, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); + if (!tile) { + throw std::runtime_error("Failed to split tilesheet"); + } + al_convert_mask_to_alpha(tile, alphaMask); + tileset.push_back(tile); + } + } + } + + ~Tileset() { + for (auto& bitmap : tileset) { + al_destroy_bitmap(bitmap); + } + al_destroy_bitmap(tilesheet); + } + + Tileset(const Tileset&) = delete; + Tileset& operator=(const Tileset&) = delete; + + Tileset(Tileset&& other) noexcept + : tileset(std::move(other.tileset)), tilesheet(other.tilesheet) { + other.tilesheet = nullptr; + } + + Tileset& operator=(Tileset&& other) noexcept { + if (this != &other) { + // Clean up existing bitmaps + for (auto& bitmap : tileset) { + al_destroy_bitmap(bitmap); + } + al_destroy_bitmap(tilesheet); + + tileset = std::move(other.tileset); + tilesheet = other.tilesheet; + other.tilesheet = nullptr; + } + return *this; + } + +public: + void draw_tile(int tile_index, int x, int y, int flip_flag = 0) { + if (tile_index >= 0 && static_cast(tile_index) < tileset.size() && tileset[tile_index]) { + al_draw_bitmap(tileset[tile_index], x, y, flip_flag); + } + } + + void draw_tinted_tile(int tile_index, int x, int y, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { + + // Draw solid background first + al_draw_filled_rectangle(x, y, x + TILE_WIDTH, y + TILE_HEIGHT, bg); + + // Draw the tile with foreground tint + if (tile_index >= 0 && static_cast(tile_index) < tileset.size() && tileset[tile_index]) { + al_draw_tinted_bitmap(tileset[tile_index], fg, x, y, 0); + } + } +}; + + // Base GUI Element Class class GUIElement { protected: - static std::vector tiles; - static std::vector letterTiles; + static Tileset tiles; + static Tileset letterTiles; public: + int x, y, w, h; // Position and size of the element bool hovered; // Tracks if the mouse is over the element - std::unordered_set visibleStates; + UIStateSet visibleStates; - GUIElement(int x, int y, int w, int h, std::unordered_set visibleStates) - : x(x), y(y), w(w), h(h), hovered(false), visibleStates(std::move(visibleStates)) {} + GUIElement(int x, int y, int w, int h, UIStateSet visibleStates) + : x(x), y(y), w(w), h(h), hovered(false), visibleStates(visibleStates) {} virtual ~GUIElement() {} @@ -428,9 +514,12 @@ class GUIElement { virtual void draw() = 0; + bool isVisible(StonesenseState::UIState currentState) const { + return visibleStates.test(static_cast(currentState)); + } void update() { //al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); - if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { + if (isVisible(stonesenseState.currentUIState)) { draw(); } } @@ -458,60 +547,37 @@ class GUIElement { w = newW; h = newH; } +}; - static std::vector loadTileset(std::string tilesetPath, ALLEGRO_COLOR alphaMask) { - std::vector allTiles; // Store extracted tiles - - auto tileset = al_load_bitmap(tilesetPath.c_str()); - if (!tileset) { - printf("Failed to load tileset!\n"); - return {}; - } +Tileset GUIElement::tiles{ "hack/data/art/border-window.png", al_map_rgb(255, 0, 255) }; +Tileset GUIElement::letterTiles{ "stonesense/GUI/text.png", al_map_rgb(255, 0, 255) }; - int tileset_width = al_get_bitmap_width(tileset); - int tileset_height = al_get_bitmap_height(tileset); - int tiles_per_row = tileset_width / TILE_WIDTH; - int tiles_per_col = tileset_height / TILE_HEIGHT; - - for (int y = 0; y < tiles_per_col; ++y) { - for (int x = 0; x < tiles_per_row; ++x) { - ALLEGRO_BITMAP* tile = al_create_sub_bitmap(tileset, x * TILE_WIDTH, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); - al_convert_mask_to_alpha(tile, alphaMask); - allTiles.push_back(tile); - } - } - return allTiles; - } +class textElement : public GUIElement { +public: + enum TextAlign { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT + }; + std::string text; // Store the actual text, not just a pointer + ALLEGRO_COLOR fg; + ALLEGRO_COLOR bg; + TextAlign align; - static int get_cp437_tile_index(char c) { - return (unsigned char)c; // CP437 codes directly map to tile indices + textElement(int x, int y, int w, int h, UIStateSet visibleStates, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, TextAlign align = ALIGN_LEFT) + : GUIElement(x, y, w, h, visibleStates), text(text), fg(fg), bg(bg), align(align) { } + void setText(std::string newText) { text = newText; } // Copy new text safely - static void draw_tile(int tile_index, int x, int y, std::vector tileset, int flip_flag = 0) { - if (tile_index >= 0 && static_cast(tile_index) < tileset.size() && tileset[tile_index]) { - al_draw_bitmap(tileset[tile_index], x, y, flip_flag); - } + void draw() override { + draw_text_with_tiles(x, y, fg, bg, text.c_str(), align); } - - static void draw_tinted_tile(int tile_index, int x, int y, std::vector tileset, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { - - // Draw solid background first - al_draw_filled_rectangle(x, y, x + TILE_WIDTH, y + TILE_HEIGHT, bg); - - // Draw the tile with foreground tint - if (tile_index >= 0 && static_cast(tile_index) < tileset.size() && tileset[tile_index]) { - al_draw_tinted_bitmap(tileset[tile_index], fg, x, y, 0); - } + static int get_cp437_tile_index(char c) { + return (unsigned char)c; // CP437 codes directly map to tile indices } - enum TextAlign { - ALIGN_LEFT, - ALIGN_CENTER, - ALIGN_RIGHT - }; - static void draw_text_with_tiles(int x, int y, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, std::string text, TextAlign align = ALIGN_LEFT) { if (text.empty()) return; @@ -530,32 +596,9 @@ class GUIElement { // Draw each character using CP437 tiles for (int i = 0; i < label_length; i++) { int tile_index = get_cp437_tile_index(text[i]); // Get CP437 tile index - draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, letterTiles, fg, bg); + letterTiles.draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, fg, bg); } } - - -}; - -std::vector GUIElement::tiles{ loadTileset("hack/data/art/border-window.png", al_map_rgb(255, 0, 255)) }; -std::vector GUIElement::letterTiles{ loadTileset("stonesense/GUI/text.png", al_map_rgb(255, 0, 255)) }; - -class textElement : public GUIElement { -public: - std::string text; // Store the actual text, not just a pointer - ALLEGRO_COLOR fg; - ALLEGRO_COLOR bg; - TextAlign align; - - textElement(int x, int y, int w, int h, std::unordered_set visibleStates, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, TextAlign align = ALIGN_LEFT) - : GUIElement(x, y, w, h, visibleStates), text(text), fg(fg), bg(bg), align(align) { - } - - void setText(std::string newText) { text = newText; } // Copy new text safely - - void draw() override { - GUIElement::draw_text_with_tiles(x, y, fg, bg, text.c_str(), align); - } }; @@ -563,7 +606,7 @@ class WindowPanel : public GUIElement { public: std::string label; - WindowPanel(int x, int y, int w, int h, std::unordered_set visibleStates, std::string label) :GUIElement(x, y, w, h, visibleStates), label(label) {} + WindowPanel(int x, int y, int w, int h, UIStateSet visibleStates, std::string label) :GUIElement(x, y, w, h, visibleStates), label(label) {} void draw_window(float x, float y, int width, int height, std::string label, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg) { @@ -571,27 +614,27 @@ class WindowPanel : public GUIElement { if (height < 3) height = 3; // Draw corners - draw_tile(0, x, y, tiles); // Top-left - draw_tile(2, x + (width - 1) * TILE_WIDTH, y, tiles); // Top-right - draw_tile(14, x, y + (height - 1) * TILE_HEIGHT, tiles); // Bottom-left - draw_tile(16, x + (width - 1) * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT, tiles); // Bottom-right + tiles.draw_tile(0, x, y); // Top-left + tiles.draw_tile(2, x + (width - 1) * TILE_WIDTH, y); // Top-right + tiles.draw_tile(14, x, y + (height - 1) * TILE_HEIGHT); // Bottom-left + tiles.draw_tile(16, x + (width - 1) * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT); // Bottom-right // Draw top & bottom edges for (int col = 1; col < width - 1; ++col) { - draw_tile(1, x + col * TILE_WIDTH, y, tiles); // Top - draw_tile(15, x + col * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT, tiles); // Bottom + tiles.draw_tile(1, x + col * TILE_WIDTH, y); // Top + tiles.draw_tile(15, x + col * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT); // Bottom } // Draw left & right edges for (int row = 1; row < height - 1; ++row) { - draw_tile(7, x, y + row * TILE_HEIGHT, tiles); // Left - draw_tile(9, x + (width - 1) * TILE_WIDTH, y + row * TILE_HEIGHT, tiles); // Right + tiles.draw_tile(7, x, y + row * TILE_HEIGHT); // Left + tiles.draw_tile(9, x + (width - 1) * TILE_WIDTH, y + row * TILE_HEIGHT); // Right } // Fill center area for (int row = 1; row < height - 1; ++row) { for (int col = 1; col < width - 1; ++col) { - draw_tile(8, x + col * TILE_WIDTH, y + row * TILE_HEIGHT, tiles); // Center + tiles.draw_tile(8, x + col * TILE_WIDTH, y + row * TILE_HEIGHT); // Center } } @@ -601,8 +644,8 @@ class WindowPanel : public GUIElement { int start_x = x + (width * TILE_WIDTH - label_length * TILE_WIDTH) / 2; // Center horizontally for (int i = 0; i < label_length; i++) { - int tile_index = get_cp437_tile_index(label[i]); - draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, letterTiles, fg, bg); + int tile_index = textElement::get_cp437_tile_index(label[i]); + letterTiles.draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, fg, bg); } } } @@ -621,14 +664,14 @@ class clickElement : public GUIElement { OnClickCallback clickFn; // Function pointer for the callback bool held = false; - clickElement(int x, int y, int w, int h, OnClickCallback clickFn, std::unordered_set visibleStates) + clickElement(int x, int y, int w, int h, OnClickCallback clickFn, UIStateSet visibleStates) : GUIElement(x, y, w, h, visibleStates), clickFn(clickFn) { } virtual void onClick() { if (!held) { held = true; - if (visibleStates.find(stonesenseState.UIState) != visibleStates.end()) { + if (GUIElement::isVisible(stonesenseState.currentUIState)) { if (clickFn) { clickFn(getKeyMods(&stonesenseState.keyboard)); // Pass a uint32_t argument when the button is clicked } @@ -652,7 +695,7 @@ class LabeledButton : public clickElement { LabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, std::string label, ALLEGRO_FONT* font, - OnClickCallback clickFn, std::unordered_set visibleStates) + OnClickCallback clickFn, UIStateSet visibleStates) : clickElement(x, y, w, h, clickFn, visibleStates), label(label), font(font), borderColor(borderColor), bgColor(bgColor) { } @@ -674,13 +717,12 @@ class LabeledButton : public clickElement { class simpleButton : public clickElement { public: std::string icon; - std::vector simpleButtons; int colorFlag; + Tileset simpleButtons; - simpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, std::unordered_set visibleStates) - : clickElement(x, y, w, h, onClickCallback, visibleStates), icon(icon), colorFlag(colorFlag) { - simpleButtons = loadTileset("stonesense/GUI/simple-buttons.png", al_map_rgb(255, 0, 255)); - } + simpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, UIStateSet visibleStates) + : clickElement(x, y, w, h, onClickCallback, visibleStates), icon(icon), colorFlag(colorFlag), simpleButtons("stonesense/GUI/simple-buttons.png", al_map_rgb(255, 0, 255)) { + }; void draw_simple_button(float x, float y, std::string text) { int colorOffset = 3; @@ -688,15 +730,15 @@ class simpleButton : public clickElement { // Draw corners for (int i = 0; i < 3; i++) { - draw_tile(0 + (colorFlag * colorOffset) + (i * rowOffset), x, y + TILE_HEIGHT * i, simpleButtons); // Left - draw_tile(1 + (colorFlag * colorOffset) + (i * rowOffset), x + TILE_WIDTH, y + TILE_HEIGHT * i, simpleButtons); // Middle - draw_tile(2 + (colorFlag * colorOffset) + (i * rowOffset), x + TILE_WIDTH * 2, y + TILE_HEIGHT * i, simpleButtons); // Right + simpleButtons.draw_tile(0 + (colorFlag * colorOffset) + (i * rowOffset), x, y + TILE_HEIGHT * i); // Left + simpleButtons.draw_tile(1 + (colorFlag * colorOffset) + (i * rowOffset), x + TILE_WIDTH, y + TILE_HEIGHT * i); // Middle + simpleButtons.draw_tile(2 + (colorFlag * colorOffset) + (i * rowOffset), x + TILE_WIDTH * 2, y + TILE_HEIGHT * i); // Right } // Draw the centered title using CP437 letter tiles if (!text.empty()) { - int tile_index = get_cp437_tile_index(text[0]); - draw_tinted_tile(tile_index, x + TILE_WIDTH, y + TILE_HEIGHT, letterTiles, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0)); + int tile_index = textElement::get_cp437_tile_index(text[0]); + letterTiles.draw_tinted_tile(tile_index, x + TILE_WIDTH, y + TILE_HEIGHT, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0)); } } @@ -710,23 +752,21 @@ class simpleButton : public clickElement { class controlButton : public clickElement { public: - std::vector controlButtons; bool thick; bool& enabledVar; + Tileset controlButtons; - controlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, std::unordered_set visibleStates) - : clickElement(x, y, w, h, nullptr, visibleStates), thick(thick), enabledVar(enabledVar) { - controlButtons = loadTileset("stonesense/GUI/control-buttons.png", al_map_rgb(28,28,28)); - } + controlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, UIStateSet visibleStates) + : clickElement(x, y, w, h, nullptr, visibleStates), thick(thick), enabledVar(enabledVar), controlButtons("stonesense/GUI/control-buttons.png", al_map_rgb(28, 28, 28)) {} void draw_control_button(float x, float y) { int enabledOffset = 3; int rowOffset = 15; - draw_tile(0 + (enabledVar * enabledOffset) + (thick * rowOffset), x, y, controlButtons); // Left - draw_tile(1 + (enabledVar * enabledOffset) + (thick * rowOffset), x + TILE_WIDTH, y, controlButtons); // Middle - draw_tile(2 + (enabledVar * enabledOffset) + (thick * rowOffset), x + TILE_WIDTH * 2, y, controlButtons); // Right + controlButtons.draw_tile(0 + (enabledVar * enabledOffset) + (thick * rowOffset), x, y); // Left + controlButtons.draw_tile(1 + (enabledVar * enabledOffset) + (thick * rowOffset), x + TILE_WIDTH, y); // Middle + controlButtons.draw_tile(2 + (enabledVar * enabledOffset) + (thick * rowOffset), x + TILE_WIDTH * 2, y); // Right } @@ -746,13 +786,11 @@ class Tab : public clickElement { public: int tabIndex; std::string label; - std::vector tabSet; bool upside_down; + Tileset tabSet; - Tab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) - : clickElement(x, y, w, h, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), upside_down(upside_down) { - tabSet = loadTileset("stonesense/GUI/tabs.png", al_map_rgb(28,28,28)); - } + Tab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, UIStateSet visibleStates, bool upside_down) + : clickElement(x, y, w, h, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), upside_down(upside_down), tabSet("stonesense/GUI/tabs.png", al_map_rgb(28,28,28)){} void draw_tab(float x, float y, int width, bool is_enabled, bool is_upside_down, ALLEGRO_COLOR fg, std::string text) { // Flip flag (use ALLEGRO_FLIP_VERTICAL for upside-down tabs) @@ -762,22 +800,22 @@ class Tab : public clickElement { // Draw corners int tileShift = is_upside_down ? 10 : 0; int enabledShift = is_enabled ? 5 : 0; - draw_tile(tileShift + 0 + enabledShift, x + TILE_WIDTH, y, tabSet, flip_flag); // Top-left - draw_tile(tileShift + 1 + enabledShift, x + TILE_WIDTH + TILE_WIDTH, y, tabSet, flip_flag); + tabSet.draw_tile(tileShift + 0 + enabledShift, x + TILE_WIDTH, y, flip_flag); // Top-left + tabSet.draw_tile(tileShift + 1 + enabledShift, x + TILE_WIDTH + TILE_WIDTH, y, flip_flag); - draw_tile(tileShift + 3 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y, tabSet, flip_flag); // Top-right - draw_tile(tileShift + 4 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y, tabSet, flip_flag); + tabSet.draw_tile(tileShift + 3 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y, flip_flag); // Top-right + tabSet.draw_tile(tileShift + 4 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y, flip_flag); - draw_tile(-tileShift + 10 + enabledShift, x + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-left - draw_tile(-tileShift + 11 + enabledShift, x + TILE_WIDTH + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); + tabSet.draw_tile(-tileShift + 10 + enabledShift, x + TILE_WIDTH, y + TILE_HEIGHT, flip_flag); // Bottom-left + tabSet.draw_tile(-tileShift + 11 + enabledShift, x + TILE_WIDTH + TILE_WIDTH, y + TILE_HEIGHT, flip_flag); - draw_tile(-tileShift + 13 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y + TILE_HEIGHT, tabSet, flip_flag); - draw_tile(-tileShift + 14 + enabledShift, x + ((width/TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom-right + tabSet.draw_tile(-tileShift + 13 + enabledShift, x + ((width / TILE_WIDTH - 1) * TILE_WIDTH), y + TILE_HEIGHT, flip_flag); + tabSet.draw_tile(-tileShift + 14 + enabledShift, x + ((width/TILE_WIDTH - 1) * TILE_WIDTH) + TILE_WIDTH, y + TILE_HEIGHT, flip_flag); // Bottom-right //// Draw top & bottom edges for (int col = 3; col < (width / TILE_WIDTH) - 1; ++col) { - draw_tile(tileShift + 2 + enabledShift, x + col * TILE_WIDTH, y, tabSet, flip_flag); // Top - draw_tile(-tileShift + 12 + enabledShift, x + col * TILE_WIDTH, y + TILE_HEIGHT, tabSet, flip_flag); // Bottom + tabSet.draw_tile(tileShift + 2 + enabledShift, x + col * TILE_WIDTH, y, flip_flag); // Top + tabSet.draw_tile(-tileShift + 12 + enabledShift, x + col * TILE_WIDTH, y + TILE_HEIGHT, flip_flag); // Bottom } // Draw the centered title using CP437 letter tiles @@ -789,8 +827,8 @@ class Tab : public clickElement { int start_x = x + TILE_WIDTH * 3; for (int i = 0; i < label_length; i++) { - int tile_index = get_cp437_tile_index(text[i]); - draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y + 7, letterTiles, fg, al_map_rgba(0, 0, 0, 0)); + int tile_index = textElement::get_cp437_tile_index(text[i]); + letterTiles.draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y + 7, fg, al_map_rgba(0, 0, 0, 0)); } } @@ -869,51 +907,96 @@ bool elementExists(int x, int y, int w, int h) { return false; } - -void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, std::unordered_set visibleStates) { +void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, UIStateSet visibleStates) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, onClickCallback, visibleStates)); } } +void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, StonesenseState::UIState visibleState) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addButton(x, y, w, h, borderColor, bgColor, onClickCallback, temp); +} + void addLabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, std::string label, ALLEGRO_FONT* font, - OnClickCallback onClickCallback, std::unordered_set visibleStates) { + OnClickCallback onClickCallback, UIStateSet visibleStates) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, visibleStates)); } } -void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, std::unordered_set visibleStates) { +void addLabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, + std::string label, ALLEGRO_FONT* font, + OnClickCallback onClickCallback, StonesenseState::UIState visibleState) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addLabeledButton(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, temp); +} + +void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, UIStateSet visibleStates) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, icon, colorFlag, onClickCallback, visibleStates)); } } -void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, std::unordered_set visibleStates) { +void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, StonesenseState::UIState visibleState) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addSimpleButton(x, y, w, h, icon, colorFlag, onClickCallback, temp); +} + +void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, UIStateSet visibleStates) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, thick, enabledVar, visibleStates)); } } -void addTab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, std::unordered_set visibleStates, bool upside_down) { +void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, StonesenseState::UIState visibleState) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addControlButton(x, y, w, h, thick, enabledVar, temp); +} + +void addTab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, UIStateSet visibleStates, bool upside_down) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, tabIndex, label, onClickCallback, visibleStates, upside_down)); } } -void addPanel(int x, int y, int w, int h, std::unordered_set visibleStates, std::string label) { +void addTab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, StonesenseState::UIState visibleState, bool upside_down) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addTab(x, y, w, h, tabIndex, label, onClickCallback, temp, upside_down); +} + +void addPanel(int x, int y, int w, int h, UIStateSet visibleStates, std::string label) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, visibleStates, label)); } } -void addText(int x, int y, int w, int h, std::unordered_set visibleStates, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, GUIElement::TextAlign align) { +void addPanel(int x, int y, int w, int h, StonesenseState::UIState visibleState, std::string label) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addPanel(x, y, w, h, temp, label); +} + +void addText(int x, int y, int w, int h, UIStateSet visibleStates, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, textElement::TextAlign align) { if (!elementExists(x, y, w, h)) { elements.push_back(std::make_unique(x, y, w, h, visibleStates, text, fg, bg, align)); } } +void addText(int x, int y, int w, int h, StonesenseState::UIState visibleState, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, textElement::TextAlign align) { + UIStateSet temp; + temp.set(static_cast(visibleState)); + addText(x, y, w, h, temp, text, fg, bg, align); +} + + + void clearElements() { elements.clear(); // Automatically deletes all elements @@ -928,8 +1011,8 @@ namespace const char* report = text.c_str(); - GUIElement::draw_text_with_tiles(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), reportColor, - al_map_rgba(0, 0, 0, 0), report, GUIElement::TextAlign::ALIGN_LEFT); + textElement::draw_text_with_tiles(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), reportColor, + al_map_rgba(0, 0, 0, 0), report, textElement::TextAlign::ALIGN_LEFT); rowNum++; } @@ -1398,7 +1481,7 @@ ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteShe return al_create_sub_bitmap(spriteSheet, sheetx * SPRITEWIDTH, sheety * SPRITEHEIGHT, SPRITEWIDTH, SPRITEHEIGHT); } -void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCallback, std::unordered_set visibleStates) { +void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCallback, UIStateSet visibleStates) { int numTabs = 3; int tabWidth = (stonesenseState.ssState.InfoW - 16) / numTabs; int tabHeight = 24; @@ -1415,7 +1498,7 @@ void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCall } -void drawInfoPanel(std::unordered_set infoStates) { +void drawInfoPanel(UIStateSet infoStates) { auto panelWidth = int(stonesenseState.ssState.InfoW / TILE_WIDTH); auto panelHeight = int((stonesenseState.ssState.ScreenH-(TILE_HEIGHT*2)) / TILE_HEIGHT); std::string label = " Info Panel "; @@ -1432,8 +1515,8 @@ void drawInfoPanel(std::unordered_set infoStates) { void addSettingLine(int& rowNum, bool& settingVar, std::string text) { int startX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW + TILE_WIDTH; - addControlButton(startX, TILE_HEIGHT+(rowNum*TILE_HEIGHT), 3 * TILE_WIDTH, TILE_HEIGHT, false, settingVar, {"INFO_PANEL/SETTINGS"}); - addText(startX + TILE_WIDTH * 4, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH * text.length(), TILE_HEIGHT, { "INFO_PANEL/SETTINGS" }, text, uiColor(dfColors::cyan), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + addControlButton(startX, TILE_HEIGHT+(rowNum*TILE_HEIGHT), 3 * TILE_WIDTH, TILE_HEIGHT, false, settingVar, StonesenseState::UIState::INFO_PANEL_SETTING); + addText(startX + TILE_WIDTH * 4, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH * text.length(), TILE_HEIGHT, StonesenseState::UIState::INFO_PANEL_SETTING, text, uiColor(dfColors::cyan), al_map_rgba(0, 0, 0, 0), textElement::TextAlign::ALIGN_LEFT); rowNum++; } @@ -1464,9 +1547,9 @@ void addKeybindLine(int& rowNum, std::string keyname, std::string actionname, bo int keyW = TILE_WIDTH * keyname.length(); int actionW= TILE_WIDTH * actionname.length(); - addText(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), keyW, TILE_HEIGHT, { "INFO_PANEL/KEYBINDS" }, keyname, uiColor(dfColors::lgreen), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); - addText(startX+keyW, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH*2, TILE_HEIGHT, { "INFO_PANEL/KEYBINDS" }, ": ", uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); - addText(startX+keyW+(2*TILE_WIDTH), TILE_HEIGHT + (rowNum * TILE_HEIGHT), actionW, TILE_HEIGHT, {"INFO_PANEL/KEYBINDS"}, actionname, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), GUIElement::TextAlign::ALIGN_LEFT); + addText(startX, TILE_HEIGHT + (rowNum * TILE_HEIGHT), keyW, TILE_HEIGHT, StonesenseState::UIState::INFO_PANEL_KEYBINDS, keyname, uiColor(dfColors::lgreen), al_map_rgba(0, 0, 0, 0), textElement::TextAlign::ALIGN_LEFT); + addText(startX+keyW, TILE_HEIGHT + (rowNum * TILE_HEIGHT), TILE_WIDTH*2, TILE_HEIGHT, StonesenseState::UIState::INFO_PANEL_KEYBINDS, ": ", uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), textElement::TextAlign::ALIGN_LEFT); + addText(startX+keyW+(2*TILE_WIDTH), TILE_HEIGHT + (rowNum * TILE_HEIGHT), actionW, TILE_HEIGHT, StonesenseState::UIState::INFO_PANEL_KEYBINDS, actionname, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0), textElement::TextAlign::ALIGN_LEFT); rowNum++; } @@ -1553,24 +1636,24 @@ void updateUIState() { auto& ssConfig = stonesenseState.ssConfig; auto& ssState = stonesenseState.ssState; - stonesenseState.UIState = "DEFAULT"; + stonesenseState.currentUIState = StonesenseState::UIState::DEFAULT; if (ssConfig.config.show_info_panel) { - stonesenseState.UIState = "INFO_PANEL"; + stonesenseState.currentUIState = StonesenseState::UIState::INFO_PANEL; } if (ssState.selectedTab == GameState::tabs::announcements) { - stonesenseState.UIState = "INFO_PANEL/ANNOUNCEMENTS"; + stonesenseState.currentUIState = StonesenseState::UIState::INFO_PANEL_ANNOUNCEMENTS;//annoucements } if (ssState.selectedTab == GameState::tabs::keybinds) { - stonesenseState.UIState = "INFO_PANEL/KEYBINDS"; + stonesenseState.currentUIState = StonesenseState::UIState::INFO_PANEL_KEYBINDS;//keybinds } if (ssState.selectedTab == GameState::tabs::settings) { - stonesenseState.UIState = "INFO_PANEL/SETTINGS"; + stonesenseState.currentUIState = StonesenseState::UIState::INFO_PANEL_SETTING;//settings } if (ssConfig.config.show_osd) { - stonesenseState.UIState = "OSD"; - } - if (ssConfig.config.debug_mode) { - stonesenseState.UIState = "DEBUG"; + stonesenseState.currentUIState = StonesenseState::UIState::OSD; + if (ssConfig.config.debug_mode) { + stonesenseState.currentUIState = StonesenseState::UIState::DEBUG; + } } } @@ -1637,8 +1720,12 @@ void paintboard() blue }; - std::unordered_set infoStates = { "INFO_PANEL", "INFO_PANEL/ANNOUNCEMENTS","INFO_PANEL/KEYBINDS","INFO_PANEL/SETTINGS" }; - addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH), 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "i", buttonColors::blue, action_toggleinfopanel, { "DEFAULT" }); + UIStateSet infoStates; + infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL)); + infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL_ANNOUNCEMENTS)); + infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL_KEYBINDS)); + infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL_SETTING)); + addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH), 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "i", buttonColors::blue, action_toggleinfopanel, StonesenseState::UIState::DEFAULT); addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH) - stonesenseState.ssState.InfoW, 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "x", buttonColors::red, action_toggleinfopanel, infoStates); drawInfoPanel(infoStates); drawSettings(); diff --git a/StonesenseState.h b/StonesenseState.h index 01336e66..d0cfade0 100644 --- a/StonesenseState.h +++ b/StonesenseState.h @@ -37,7 +37,19 @@ class StonesenseState ALLEGRO_MOUSE_STATE mouse; int randomCube[RANDOM_CUBE][RANDOM_CUBE][RANDOM_CUBE]; - std::string UIState; + enum class UIState { + DEFAULT, + OSD, + DEBUG, + INFO_PANEL, + INFO_PANEL_ANNOUNCEMENTS, + INFO_PANEL_KEYBINDS, + INFO_PANEL_SETTING, + COUNT + }; + + UIState currentUIState; + std::unique_ptr currentMap; std::unique_ptr contentLoader; From edc76eb3e61c14cf3ba9517ef049e26271ab4bfb Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 19:06:22 -0500 Subject: [PATCH 26/36] fix missing cast --- GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI.cpp b/GUI.cpp index 0317fc28..3d152378 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -363,7 +363,7 @@ void draw_color_ustr_border(const ALLEGRO_FONT* font, ALLEGRO_COLOR text_color, std::string fitTextToWidth(const std::string input, int width) { int max_chars = width / TILE_WIDTH; - if (input.length() > max_chars) { + if (input.length() > static_cast(max_chars)) { return input.substr(0, max_chars - 1) + "."; } return input; From f100c9db7ba7d0bf756052200c5bcf2d5e33d4e3 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 19:28:51 -0500 Subject: [PATCH 27/36] Update to use safer bitmap load and filepath --- GUI.cpp | 12 ++++-------- GUI.h | 2 +- main.cpp | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 3d152378..88956b90 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -406,6 +406,7 @@ std::vector splitLinesToWidth(const std::string& input, int width) } #include +#include constexpr size_t MAX_UI_STATES = static_cast(StonesenseState::UIState::COUNT); using UIStateSet = std::bitset; @@ -416,9 +417,9 @@ class Tileset { ALLEGRO_BITMAP* tilesheet; public: - Tileset(const std::string& filepath, ALLEGRO_COLOR alphaMask) { - tilesheet = al_load_bitmap(filepath.c_str()); - if (!tilesheet) { + Tileset(std::filesystem::path & filepath, ALLEGRO_COLOR alphaMask) { + tilesheet = load_bitmap_withWarning(filepath.string().c_str(),alphaMask); + if (tilesheet=0) { LogError("Failed to load tileset!\n"); throw std::runtime_error("Failed to load tileset: File not found or invalid format."); } @@ -434,7 +435,6 @@ class Tileset { if (!tile) { throw std::runtime_error("Failed to split tilesheet"); } - al_convert_mask_to_alpha(tile, alphaMask); tileset.push_back(tile); } } @@ -548,10 +548,6 @@ class GUIElement { h = newH; } }; - -Tileset GUIElement::tiles{ "hack/data/art/border-window.png", al_map_rgb(255, 0, 255) }; -Tileset GUIElement::letterTiles{ "stonesense/GUI/text.png", al_map_rgb(255, 0, 255) }; - class textElement : public GUIElement { public: enum TextAlign { diff --git a/GUI.h b/GUI.h index d0ccb3a2..287ecddc 100644 --- a/GUI.h +++ b/GUI.h @@ -29,7 +29,7 @@ void flushImgFiles(); //returns index into getImgFile. Will only create new bitmaps when needed int loadImgFile(std::filesystem::path filename); ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteSheet); -ALLEGRO_BITMAP* load_bitmap_withWarning(std::filesystem::path path); +ALLEGRO_BITMAP* load_bitmap_withWarning(std::filesystem::path path, ALLEGRO_COLOR alphaMask = al_map_rgb(255, 0, 255)); void DrawSpriteIndexOverlay(int i); void DoSpriteIndexOverlay(); void loadGraphicsFromDisk(); diff --git a/main.cpp b/main.cpp index 09b7777a..8f623310 100644 --- a/main.cpp +++ b/main.cpp @@ -67,7 +67,7 @@ ALLEGRO_THREAD *stonesense_event_thread; // the segment wrapper handles concurrency control bool redraw = true; -ALLEGRO_BITMAP* load_bitmap_withWarning(std::filesystem::path path) +ALLEGRO_BITMAP* load_bitmap_withWarning(std::filesystem::path path, ALLEGRO_COLOR alphaMask) { ALLEGRO_BITMAP* img = 0; img = al_load_bitmap(path.string().c_str()); @@ -76,7 +76,7 @@ ALLEGRO_BITMAP* load_bitmap_withWarning(std::filesystem::path path) al_set_thread_should_stop(stonesense_event_thread); return 0; } - al_convert_mask_to_alpha(img, al_map_rgb(255, 0, 255)); + al_convert_mask_to_alpha(img, alphaMask); return img; } From d9a57fbe5b2631521fdc0178e6f159a45b6f2fc1 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 19:34:09 -0500 Subject: [PATCH 28/36] Fix the filesystem usage and put back the removed code (bug) --- GUI.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 88956b90..a92f7463 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -417,7 +417,7 @@ class Tileset { ALLEGRO_BITMAP* tilesheet; public: - Tileset(std::filesystem::path & filepath, ALLEGRO_COLOR alphaMask) { + Tileset(std::filesystem::path filepath, ALLEGRO_COLOR alphaMask) { tilesheet = load_bitmap_withWarning(filepath.string().c_str(),alphaMask); if (tilesheet=0) { LogError("Failed to load tileset!\n"); @@ -548,6 +548,10 @@ class GUIElement { h = newH; } }; + +Tileset GUIElement::tiles{ "hack/data/art/border-window.png", al_map_rgb(255, 0, 255) }; +Tileset GUIElement::letterTiles{ "stonesense/GUI/text.png", al_map_rgb(255, 0, 255) }; + class textElement : public GUIElement { public: enum TextAlign { @@ -717,7 +721,7 @@ class simpleButton : public clickElement { Tileset simpleButtons; simpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, UIStateSet visibleStates) - : clickElement(x, y, w, h, onClickCallback, visibleStates), icon(icon), colorFlag(colorFlag), simpleButtons("stonesense/GUI/simple-buttons.png", al_map_rgb(255, 0, 255)) { + : clickElement(x, y, w, h, onClickCallback, visibleStates), icon(icon), colorFlag(colorFlag), simpleButtons(std::filesystem::path{"stonesense/GUI/simple-buttons.png" }, al_map_rgb(255, 0, 255)) { }; void draw_simple_button(float x, float y, std::string text) { @@ -753,7 +757,8 @@ class controlButton : public clickElement { Tileset controlButtons; controlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, UIStateSet visibleStates) - : clickElement(x, y, w, h, nullptr, visibleStates), thick(thick), enabledVar(enabledVar), controlButtons("stonesense/GUI/control-buttons.png", al_map_rgb(28, 28, 28)) {} + : clickElement(x, y, w, h, nullptr, visibleStates), thick(thick), enabledVar(enabledVar), controlButtons(std::filesystem::path{ "stonesense/GUI/control-buttons.png" }, al_map_rgb(28, 28, 28)) { + } void draw_control_button(float x, float y) { int enabledOffset = 3; @@ -786,7 +791,8 @@ class Tab : public clickElement { Tileset tabSet; Tab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, UIStateSet visibleStates, bool upside_down) - : clickElement(x, y, w, h, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), upside_down(upside_down), tabSet("stonesense/GUI/tabs.png", al_map_rgb(28,28,28)){} + : clickElement(x, y, w, h, onClickCallback, visibleStates), tabIndex(tabIndex), label(label), upside_down(upside_down), tabSet(std::filesystem::path{ "stonesense/GUI/tabs.png" }, al_map_rgb(28, 28, 28)) { + } void draw_tab(float x, float y, int width, bool is_enabled, bool is_upside_down, ALLEGRO_COLOR fg, std::string text) { // Flip flag (use ALLEGRO_FLIP_VERTICAL for upside-down tabs) From 1fb07d497b79c32d8af49fb882a8c3be1003a2c1 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 20:38:41 -0500 Subject: [PATCH 29/36] fix silly bug --- GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI.cpp b/GUI.cpp index a92f7463..7daade8a 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -419,7 +419,7 @@ class Tileset { public: Tileset(std::filesystem::path filepath, ALLEGRO_COLOR alphaMask) { tilesheet = load_bitmap_withWarning(filepath.string().c_str(),alphaMask); - if (tilesheet=0) { + if (tilesheet==0) { LogError("Failed to load tileset!\n"); throw std::runtime_error("Failed to load tileset: File not found or invalid format."); } From c795d65a45c8f2a413f6a5e45a2f4b19394bba84 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 20:39:33 -0500 Subject: [PATCH 30/36] fix it better --- GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI.cpp b/GUI.cpp index 7daade8a..6091c90d 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -419,7 +419,7 @@ class Tileset { public: Tileset(std::filesystem::path filepath, ALLEGRO_COLOR alphaMask) { tilesheet = load_bitmap_withWarning(filepath.string().c_str(),alphaMask); - if (tilesheet==0) { + if (!tilesheet) { LogError("Failed to load tileset!\n"); throw std::runtime_error("Failed to load tileset: File not found or invalid format."); } From b65ecc82643428eebc3d137a5b7949571dcef95f Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Wed, 26 Mar 2025 22:20:27 -0500 Subject: [PATCH 31/36] make it work again --- GUI.cpp | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 6091c90d..7d862a3d 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -414,9 +414,13 @@ using UIStateSet = std::bitset; class Tileset { protected: std::vector tileset; - ALLEGRO_BITMAP* tilesheet; + ALLEGRO_BITMAP* tilesheet = nullptr; public: + + Tileset() : tilesheet(nullptr) {} + + Tileset(std::filesystem::path filepath, ALLEGRO_COLOR alphaMask) { tilesheet = load_bitmap_withWarning(filepath.string().c_str(),alphaMask); if (!tilesheet) { @@ -492,9 +496,9 @@ class Tileset { // Base GUI Element Class class GUIElement { -protected: - static Tileset tiles; - static Tileset letterTiles; +public: + static std::optional tiles; + static std::optional letterTiles; public: int x, y, w, h; // Position and size of the element @@ -502,7 +506,15 @@ class GUIElement { UIStateSet visibleStates; GUIElement(int x, int y, int w, int h, UIStateSet visibleStates) - : x(x), y(y), w(w), h(h), hovered(false), visibleStates(visibleStates) {} + : x(x), y(y), w(w), h(h), hovered(false), visibleStates(visibleStates) { + if (al_is_system_installed()) { + initSheets(); + } + } + void initSheets() { + tiles.emplace(std::filesystem::path{"hack/data/art/border-window.png"}, al_map_rgb(255, 0, 255)); + letterTiles.emplace(std::filesystem::path{"stonesense/GUI/text.png"}, al_map_rgb(255, 0, 255)); + } virtual ~GUIElement() {} @@ -549,8 +561,8 @@ class GUIElement { } }; -Tileset GUIElement::tiles{ "hack/data/art/border-window.png", al_map_rgb(255, 0, 255) }; -Tileset GUIElement::letterTiles{ "stonesense/GUI/text.png", al_map_rgb(255, 0, 255) }; +std::optional GUIElement::tiles = std::nullopt; +std::optional GUIElement::letterTiles = std::nullopt; class textElement : public GUIElement { public: @@ -596,7 +608,7 @@ class textElement : public GUIElement { // Draw each character using CP437 tiles for (int i = 0; i < label_length; i++) { int tile_index = get_cp437_tile_index(text[i]); // Get CP437 tile index - letterTiles.draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, fg, bg); + letterTiles->draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, fg, bg); } } }; @@ -614,27 +626,27 @@ class WindowPanel : public GUIElement { if (height < 3) height = 3; // Draw corners - tiles.draw_tile(0, x, y); // Top-left - tiles.draw_tile(2, x + (width - 1) * TILE_WIDTH, y); // Top-right - tiles.draw_tile(14, x, y + (height - 1) * TILE_HEIGHT); // Bottom-left - tiles.draw_tile(16, x + (width - 1) * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT); // Bottom-right + tiles->draw_tile(0, x, y); // Top-left + tiles->draw_tile(2, x + (width - 1) * TILE_WIDTH, y); // Top-right + tiles->draw_tile(14, x, y + (height - 1) * TILE_HEIGHT); // Bottom-left + tiles->draw_tile(16, x + (width - 1) * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT); // Bottom-right // Draw top & bottom edges for (int col = 1; col < width - 1; ++col) { - tiles.draw_tile(1, x + col * TILE_WIDTH, y); // Top - tiles.draw_tile(15, x + col * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT); // Bottom + tiles->draw_tile(1, x + col * TILE_WIDTH, y); // Top + tiles->draw_tile(15, x + col * TILE_WIDTH, y + (height - 1) * TILE_HEIGHT); // Bottom } // Draw left & right edges for (int row = 1; row < height - 1; ++row) { - tiles.draw_tile(7, x, y + row * TILE_HEIGHT); // Left - tiles.draw_tile(9, x + (width - 1) * TILE_WIDTH, y + row * TILE_HEIGHT); // Right + tiles->draw_tile(7, x, y + row * TILE_HEIGHT); // Left + tiles->draw_tile(9, x + (width - 1) * TILE_WIDTH, y + row * TILE_HEIGHT); // Right } // Fill center area for (int row = 1; row < height - 1; ++row) { for (int col = 1; col < width - 1; ++col) { - tiles.draw_tile(8, x + col * TILE_WIDTH, y + row * TILE_HEIGHT); // Center + tiles->draw_tile(8, x + col * TILE_WIDTH, y + row * TILE_HEIGHT); // Center } } @@ -645,7 +657,7 @@ class WindowPanel : public GUIElement { for (int i = 0; i < label_length; i++) { int tile_index = textElement::get_cp437_tile_index(label[i]); - letterTiles.draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, fg, bg); + letterTiles->draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y, fg, bg); } } } @@ -738,7 +750,7 @@ class simpleButton : public clickElement { // Draw the centered title using CP437 letter tiles if (!text.empty()) { int tile_index = textElement::get_cp437_tile_index(text[0]); - letterTiles.draw_tinted_tile(tile_index, x + TILE_WIDTH, y + TILE_HEIGHT, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0)); + letterTiles->draw_tinted_tile(tile_index, x + TILE_WIDTH, y + TILE_HEIGHT, uiColor(dfColors::white), al_map_rgba(0, 0, 0, 0)); } } @@ -830,7 +842,7 @@ class Tab : public clickElement { for (int i = 0; i < label_length; i++) { int tile_index = textElement::get_cp437_tile_index(text[i]); - letterTiles.draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y + 7, fg, al_map_rgba(0, 0, 0, 0)); + letterTiles->draw_tinted_tile(tile_index, start_x + i * TILE_WIDTH, y + 7, fg, al_map_rgba(0, 0, 0, 0)); } } From c444dc07f1d7b5d10a4a6e045119339c1764f2a2 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 27 Mar 2025 15:09:30 -0500 Subject: [PATCH 32/36] Fix up the force track mode --- GUI.cpp | 3 +++ UserInput.cpp | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 7d862a3d..76c39ee5 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -1715,6 +1715,9 @@ void paintboard() segment->DrawAllTiles(); + if(ssConfig.config.force_track){ + ssConfig.config.track_mode = Config::TRACKING_CENTER; + } if (ssConfig.config.show_osd) { DrawCurrentLevelOutline(false); } diff --git a/UserInput.cpp b/UserInput.cpp index 7d377de6..64f3b230 100644 --- a/UserInput.cpp +++ b/UserInput.cpp @@ -523,7 +523,6 @@ void action_decrY(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); - if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -538,7 +537,6 @@ void action_incrY(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); - if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -553,7 +551,6 @@ void action_decrX(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); - if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -568,7 +565,6 @@ void action_incrX(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); - if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { stonesenseState.ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -586,7 +582,6 @@ void action_decrZ(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); - if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { ssConfig.config.track_mode = Config::TRACKING_NONE; } @@ -611,7 +606,6 @@ void action_incrZ(uint32_t keymod) return; } char stepsize = ((keymod&ALLEGRO_KEYMOD_SHIFT) ? MAPNAVIGATIONSTEPBIG : MAPNAVIGATIONSTEP); - if (stonesenseState.ssConfig.config.force_track) { return; } if (!(keymod&ALLEGRO_KEYMOD_ALT)) { ssConfig.config.track_mode = Config::TRACKING_NONE; } From 096df530c3a763d282ac21f9f3ca6f4e437ebc57 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Thu, 27 Mar 2025 15:50:43 -0500 Subject: [PATCH 33/36] apply code review things --- GUI.cpp | 45 +++++++++++++++++++++++---------------------- StonesenseState.h | 2 +- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 76c39ee5..81c7909b 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -408,10 +408,18 @@ std::vector splitLinesToWidth(const std::string& input, int width) #include #include -constexpr size_t MAX_UI_STATES = static_cast(StonesenseState::UIState::COUNT); +constexpr size_t MAX_UI_STATES = (StonesenseState::UIState::COUNT); using UIStateSet = std::bitset; class Tileset { +private: + // Clean up existing bitmaps + void cleanUp() { + for (auto& bitmap : tileset) { + al_destroy_bitmap(bitmap); + } + al_destroy_bitmap(tilesheet); + } protected: std::vector tileset; ALLEGRO_BITMAP* tilesheet = nullptr; @@ -445,10 +453,7 @@ class Tileset { } ~Tileset() { - for (auto& bitmap : tileset) { - al_destroy_bitmap(bitmap); - } - al_destroy_bitmap(tilesheet); + cleanUp(); } Tileset(const Tileset&) = delete; @@ -461,11 +466,7 @@ class Tileset { Tileset& operator=(Tileset&& other) noexcept { if (this != &other) { - // Clean up existing bitmaps - for (auto& bitmap : tileset) { - al_destroy_bitmap(bitmap); - } - al_destroy_bitmap(tilesheet); + cleanUp(); tileset = std::move(other.tileset); tilesheet = other.tilesheet; @@ -527,7 +528,7 @@ class GUIElement { virtual void draw() = 0; bool isVisible(StonesenseState::UIState currentState) const { - return visibleStates.test(static_cast(currentState)); + return visibleStates.test((currentState)); } void update() { //al_draw_text(stonesenseState.font, uiColor(dfColors::white), 0, 40, 0, stonesenseState.UIState.c_str()); @@ -929,7 +930,7 @@ void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, void addButton(int x, int y, int w, int h, int32_t borderColor, int32_t bgColor, OnClickCallback onClickCallback, StonesenseState::UIState visibleState) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addButton(x, y, w, h, borderColor, bgColor, onClickCallback, temp); } @@ -945,7 +946,7 @@ void addLabeledButton(int x, int y, int w, int h, int32_t borderColor, int32_t b std::string label, ALLEGRO_FONT* font, OnClickCallback onClickCallback, StonesenseState::UIState visibleState) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addLabeledButton(x, y, w, h, borderColor, bgColor, label, font, onClickCallback, temp); } @@ -957,7 +958,7 @@ void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag void addSimpleButton(int x, int y, int w, int h, std::string icon, int colorFlag, OnClickCallback onClickCallback, StonesenseState::UIState visibleState) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addSimpleButton(x, y, w, h, icon, colorFlag, onClickCallback, temp); } @@ -969,7 +970,7 @@ void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, void addControlButton(int x, int y, int w, int h, bool thick, bool& enabledVar, StonesenseState::UIState visibleState) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addControlButton(x, y, w, h, thick, enabledVar, temp); } @@ -981,7 +982,7 @@ void addTab(int x, int y, int w, int h, int tabIndex, std::string label, OnClick void addTab(int x, int y, int w, int h, int tabIndex, std::string label, OnClickCallback onClickCallback, StonesenseState::UIState visibleState, bool upside_down) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addTab(x, y, w, h, tabIndex, label, onClickCallback, temp, upside_down); } @@ -993,7 +994,7 @@ void addPanel(int x, int y, int w, int h, UIStateSet visibleStates, std::string void addPanel(int x, int y, int w, int h, StonesenseState::UIState visibleState, std::string label) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addPanel(x, y, w, h, temp, label); } @@ -1005,7 +1006,7 @@ void addText(int x, int y, int w, int h, UIStateSet visibleStates, std::string t void addText(int x, int y, int w, int h, StonesenseState::UIState visibleState, std::string text, ALLEGRO_COLOR fg, ALLEGRO_COLOR bg, textElement::TextAlign align) { UIStateSet temp; - temp.set(static_cast(visibleState)); + temp.set((visibleState)); addText(x, y, w, h, temp, text, fg, bg, align); } @@ -1738,10 +1739,10 @@ void paintboard() }; UIStateSet infoStates; - infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL)); - infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL_ANNOUNCEMENTS)); - infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL_KEYBINDS)); - infoStates.set(static_cast(StonesenseState::UIState::INFO_PANEL_SETTING)); + infoStates.set((StonesenseState::UIState::INFO_PANEL)); + infoStates.set((StonesenseState::UIState::INFO_PANEL_ANNOUNCEMENTS)); + infoStates.set((StonesenseState::UIState::INFO_PANEL_KEYBINDS)); + infoStates.set((StonesenseState::UIState::INFO_PANEL_SETTING)); addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH), 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "i", buttonColors::blue, action_toggleinfopanel, StonesenseState::UIState::DEFAULT); addSimpleButton(stonesenseState.ssState.ScreenW - (3 * TILE_WIDTH) - stonesenseState.ssState.InfoW, 0, 3 * TILE_WIDTH, 3 * TILE_HEIGHT, "x", buttonColors::red, action_toggleinfopanel, infoStates); drawInfoPanel(infoStates); diff --git a/StonesenseState.h b/StonesenseState.h index d0cfade0..88dd280a 100644 --- a/StonesenseState.h +++ b/StonesenseState.h @@ -37,7 +37,7 @@ class StonesenseState ALLEGRO_MOUSE_STATE mouse; int randomCube[RANDOM_CUBE][RANDOM_CUBE][RANDOM_CUBE]; - enum class UIState { + enum UIState { DEFAULT, OSD, DEBUG, From 31d6b5dd287804c0ee46e1bc30adafa19130ad58 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 28 Mar 2025 12:02:56 -0500 Subject: [PATCH 34/36] Use the real curses (later switch to call the real data path) --- resources/GUI/text.png | Bin 4839 -> 1568 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/GUI/text.png b/resources/GUI/text.png index a17f51c7f19ae34cc183d050484bb9ee53c60e93..f94daf391c08a444c9aba435a7c90c7503cacf50 100644 GIT binary patch delta 1563 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 N002ovPDHLkV1j&SK_dVF From 1261cd2e3cc71e187e0ea6fa94be46ab12cfcc94 Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Fri, 28 Mar 2025 12:50:11 -0500 Subject: [PATCH 35/36] Fix the tabs not sitting flush with the edge --- GUI.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GUI.cpp b/GUI.cpp index 81c7909b..23da5c49 100644 --- a/GUI.cpp +++ b/GUI.cpp @@ -1499,10 +1499,10 @@ ALLEGRO_BITMAP * CreateSpriteFromSheet( int spriteNum, ALLEGRO_BITMAP* spriteShe void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCallback, UIStateSet visibleStates) { int numTabs = 3; int tabWidth = (stonesenseState.ssState.InfoW - 16) / numTabs; - int tabHeight = 24; + int tabHeight = (TILE_HEIGHT*2); int panelX = stonesenseState.ssState.ScreenW - stonesenseState.ssState.InfoW; // Left edge of info panel int tabX = panelX + (tabIndex * tabWidth); // Position inside the panel - int tabY = stonesenseState.ssState.ScreenH - tabHeight-4; // Flush with bottom + int tabY = stonesenseState.ssState.ScreenH - tabHeight; // Flush with bottom // Truncate label to fit inside tab auto temp = fitTextToWidth(label, tabWidth - 20); @@ -1515,7 +1515,7 @@ void drawTab(const std::string& label, int tabIndex, OnClickCallback onClickCall void drawInfoPanel(UIStateSet infoStates) { auto panelWidth = int(stonesenseState.ssState.InfoW / TILE_WIDTH); - auto panelHeight = int((stonesenseState.ssState.ScreenH-(TILE_HEIGHT*2)) / TILE_HEIGHT); + auto panelHeight = int((stonesenseState.ssState.ScreenH-TILE_HEIGHT) / TILE_HEIGHT); std::string label = " Info Panel "; //draw panel From eef600a80943f376f21d1fcd6174aaee2b92e4bb Mon Sep 17 00:00:00 2001 From: Squid Coder Date: Sat, 29 Mar 2025 18:20:29 -0500 Subject: [PATCH 36/36] Update ContentLoader.cpp --- ContentLoader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ContentLoader.cpp b/ContentLoader.cpp index 54db634b..22c8cf44 100644 --- a/ContentLoader.cpp +++ b/ContentLoader.cpp @@ -518,6 +518,8 @@ const char *lookupBuildingSubtype(int main_type, int i) return enum_item_key_str((df::workshop_type)i); case building_type::Trap: return enum_item_key_str((df::trap_type)i); + case building_type::Civzone: + return enum_item_key_str((df::civzone_type)i); default: return "UnhandledType"; }