From a4ba86aeef7729775f1bfec697fef69139f1af10 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 2 Dec 2025 20:44:08 -0500 Subject: [PATCH 01/50] Allow specifying a UI scale factor Currently this is quite blurry, and the pick hospital screen is broken. --- CorsixTH/Lua/config_finder.lua | 7 ++++++- CorsixTH/Lua/game_ui.lua | 2 ++ CorsixTH/Lua/ui.lua | 2 ++ CorsixTH/Lua/window.lua | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CorsixTH/Lua/config_finder.lua b/CorsixTH/Lua/config_finder.lua index 73815c6e1..90ed6d3dd 100644 --- a/CorsixTH/Lua/config_finder.lua +++ b/CorsixTH/Lua/config_finder.lua @@ -96,6 +96,7 @@ local config_defaults = { fullscreen = false, width = 800, height = 600, + ui_scale = 1, language = [[English]], audio = true, free_build_mode = false, @@ -206,11 +207,15 @@ local string_01 = [=[ -- Screen size. Must be at least 640x480. Larger sizes will require better -- hardware in order to maintain a playable framerate. The fullscreen setting -- can be true or false, and the game will run windowed if not fullscreen. +-- ui_scale can be set to 1, 2, or 3 to scale the user interface for higher +-- resolution displays. For example, at 1920x1080 resolution, setting ui_scale +-- to 2 will make the interface elements twice as large. --]=] .. '\n' .. 'fullscreen = ' .. tostring(config_values.fullscreen) .. '\n' .. '\n' .. 'width = ' .. tostring(config_values.width) .. '\n' .. -'height = ' .. tostring(config_values.height) .. '\n' .. [=[ +'height = ' .. tostring(config_values.height) .. '\n' .. +'ui_scale = ' .. tostring(config_values.ui_scale) .. '\n' .. [=[ ------------------------------------------------------------------------------- -- Language to use for ingame text. Between the square braces should be one of: diff --git a/CorsixTH/Lua/game_ui.lua b/CorsixTH/Lua/game_ui.lua index 1ea9d35cd..d7fea87fe 100644 --- a/CorsixTH/Lua/game_ui.lua +++ b/CorsixTH/Lua/game_ui.lua @@ -257,11 +257,13 @@ function GameUI:draw(canvas) self:setZoom(1) app.map:draw(canvas, dx, dy, config.width, config.height, 0, 0) end + canvas:scale(TheApp.config.ui_scale) Window.draw(self, canvas, 0, 0) -- NB: not calling UI.draw on purpose self:drawTooltip(canvas) if self.simulated_cursor then self.simulated_cursor.draw(canvas, self.cursor_x, self.cursor_y) end + canvas:scale(1) end function GameUI:onChangeResolution() diff --git a/CorsixTH/Lua/ui.lua b/CorsixTH/Lua/ui.lua index f24c5d27d..a74c8f2de 100644 --- a/CorsixTH/Lua/ui.lua +++ b/CorsixTH/Lua/ui.lua @@ -314,11 +314,13 @@ function UI:draw(canvas) self.background:draw(canvas, math.floor((screen_w - bg_w) / 2), math.floor((screen_h - bg_h) / 2)) end end + canvas:scale(TheApp.config.ui_scale) Window.draw(self, canvas, 0, 0) self:drawTooltip(canvas) if self.simulated_cursor then self.simulated_cursor.draw(canvas, self.cursor_x, self.cursor_y) end + canvas:scale(1) end --! Register a key handler / hotkey for a window. diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 132538a86..9d50a4d9d 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -76,6 +76,9 @@ function Window:setPosition(x, y) self.y_original = y -- Convert x and y to absolute pixel positions with regard to top/left local w, h = TheApp.config.width, TheApp.config.height + local scale = TheApp.config.ui_scale + w = w / scale + h = h / scale if x < 0 then x = math.ceil(w - self.width + x) elseif x < 1 then From 041fbde56d14e0b91d15a85b07c6d7a04f9f2959 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 2 Dec 2025 20:57:11 -0500 Subject: [PATCH 02/50] Keep fonts crisp with UI scaling --- CorsixTH/Src/th_gfx_font.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CorsixTH/Src/th_gfx_font.cpp b/CorsixTH/Src/th_gfx_font.cpp index 60c0116bd..de64a454c 100644 --- a/CorsixTH/Src/th_gfx_font.cpp +++ b/CorsixTH/Src/th_gfx_font.cpp @@ -39,6 +39,8 @@ SOFTWARE. #include #include +#include "th_gfx.h" + bitmap_font::bitmap_font() = default; void bitmap_font::set_sprite_sheet(sprite_sheet* pSpriteSheet) { @@ -73,7 +75,7 @@ void bitmap_font::draw_text(render_target* pCanvas, const char* sMessage, iChar -= iFirstASCII; int iWidth; int iHeight; - sheet->draw_sprite(pCanvas, iChar, iX, iY, 0); + sheet->draw_sprite(pCanvas, iChar, iX, iY, thdf_nearest); sheet->get_sprite_size_unchecked(iChar, &iWidth, &iHeight); iX += iWidth + letter_spacing; } From 237d67bffb9a2ef71a34b7e207d3c81c25a6fcdf Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 2 Dec 2025 21:17:56 -0500 Subject: [PATCH 03/50] Add settings window for setting the scale --- CorsixTH/Lua/dialogs/resizables/options.lua | 39 +++++++++++++++++++++ CorsixTH/Lua/languages/english.lua | 3 ++ 2 files changed, 42 insertions(+) diff --git a/CorsixTH/Lua/dialogs/resizables/options.lua b/CorsixTH/Lua/dialogs/resizables/options.lua index f1511dc31..f9e4cf49d 100644 --- a/CorsixTH/Lua/dialogs/resizables/options.lua +++ b/CorsixTH/Lua/dialogs/resizables/options.lua @@ -115,6 +115,12 @@ function UIOptions:UIOptions(ui, mode) {text = _S.options_window.custom_resolution, custom = true}, } + self.available_ui_scales = { + {text = "100%", scale = 1}, + {text = "200%", scale = 2}, + {text = "300%", scale = 3}, + } + -- Window parts definition -- Title local title_y_pos = self:_getOptionYPos() @@ -151,6 +157,14 @@ function UIOptions:UIOptions(ui, mode) self.resolution_button = self.resolution_panel:makeToggleButton(0, 0, BTN_WIDTH, BTN_HEIGHT, nil, self.dropdownResolution):setTooltip(_S.tooltip.options_window.select_resolution) + local scale_ui_y_pos = self:_getOptionYPos() + local scale_label = TheApp.config.ui_scale * 100 .. "%" + self:addBevelPanel(20, scale_ui_y_pos, BTN_WIDTH, BTN_HEIGHT, col_shadow, col_bg, col_bg) + :setLabel(_S.options_window.scale_ui):setTooltip(_S.tooltip.options_window.scale_ui).lowered = true + self.scale_ui_panel = self:addBevelPanel(165, scale_ui_y_pos, BTN_WIDTH, BTN_HEIGHT, col_bg):setLabel(scale_label) + + self.scale_ui_button = self.scale_ui_panel:makeToggleButton(0, 0, BTN_WIDTH, BTN_HEIGHT, nil, self.dropdownUIScale):setTooltip(_S.tooltip.options_window.select_ui_scale) + -- Mouse capture local capture_mouse_y_pos = self:_getOptionYPos() self:addBevelPanel(20, capture_mouse_y_pos, BTN_WIDTH, BTN_HEIGHT, col_shadow, col_bg, col_bg) @@ -257,6 +271,7 @@ end function UIOptions:dropdownLanguage(activate) if activate then self:dropdownResolution(false) + self:dropdownUIScale(false) self.language_dropdown = UIDropdown(self.ui, self, self.language_button, self.available_languages, self.selectLanguage) self:addWindow(self.language_dropdown) else @@ -279,6 +294,7 @@ end function UIOptions:dropdownResolution(activate) if activate then self:dropdownLanguage(false) + self:dropdownUIScale(false) self.resolution_dropdown = UIDropdown(self.ui, self, self.resolution_button, self.available_resolutions, self.selectResolution) self:addWindow(self.resolution_dropdown) else @@ -309,6 +325,29 @@ function UIOptions:selectResolution(number) end end +function UIOptions:dropdownUIScale(activate) + if activate then + self:dropdownLanguage(false) + self:dropdownResolution(false) + self.scale_ui_dropdown = UIDropdown(self.ui, self, self.scale_ui_button, self.available_ui_scales, self.selectUIScale) + self:addWindow(self.scale_ui_dropdown) + else + self.scale_ui_button:setToggleState(false) + if self.scale_ui_dropdown then + self.scale_ui_dropdown:close() + self.scale_ui_dropdown = nil + end + end +end + +function UIOptions:selectUIScale(number) + local res = self.available_ui_scales[number] + TheApp.config.ui_scale = res.scale + TheApp:saveConfig() + self.scale_ui_panel:setLabel(res.text) + self.ui:onChangeResolution() +end + --! Changes check for update setting to on/of function UIOptions:toggleUpdateCheck() self.ui.app.config.check_for_updates = not self.ui.app.config.check_for_updates diff --git a/CorsixTH/Lua/languages/english.lua b/CorsixTH/Lua/languages/english.lua index 189b3bf26..3cbee5347 100644 --- a/CorsixTH/Lua/languages/english.lua +++ b/CorsixTH/Lua/languages/english.lua @@ -473,6 +473,7 @@ options_window = { option_disabled = "Disabled", fullscreen = "Fullscreen", resolution = "Resolution", + scale_ui = "UI Scale", capture_mouse = "Capture Mouse", right_mouse_scrolling = "Mouse Scrolling", right_mouse_scrolling_option_middle = "Middle Button", @@ -499,6 +500,8 @@ tooltip.options_window = { fullscreen_button = "Click to toggle fullscreen mode", resolution = "The resolution the game should run in", select_resolution = "Select a new resolution", + scale_ui = "Scale the user interface", + select_ui_scale = "Select a new user interface scale", capture_mouse = "Click to toggle capturing the cursor while in game", right_mouse_scrolling = "Toggle the mouse button that is used to scroll the map", width = "Enter desired screen width", From b2c825824ee821ce98ad88472920707da10fdda8 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 2 Dec 2025 22:17:46 -0500 Subject: [PATCH 04/50] Fix the cursor position when not drawn When the cursor isn't drawn, e.g. the choose theme hospital directory screen, it was in completely the wrong position. This fixes that by compensating the cursor position. There are still some gaps in game_ui, but the main menu and settings screens are working well. --- CorsixTH/Lua/game_ui.lua | 5 ++++- CorsixTH/Lua/ui.lua | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CorsixTH/Lua/game_ui.lua b/CorsixTH/Lua/game_ui.lua index d7fea87fe..73b6bf01b 100644 --- a/CorsixTH/Lua/game_ui.lua +++ b/CorsixTH/Lua/game_ui.lua @@ -261,7 +261,10 @@ function GameUI:draw(canvas) Window.draw(self, canvas, 0, 0) -- NB: not calling UI.draw on purpose self:drawTooltip(canvas) if self.simulated_cursor then - self.simulated_cursor.draw(canvas, self.cursor_x, self.cursor_y) + self.simulated_cursor.draw( + canvas, + math.floor(self.cursor_x / TheApp.config.ui_scale), + math.floor(self.cursor_y / TheApp.config.ui_scale)) end canvas:scale(1) end diff --git a/CorsixTH/Lua/ui.lua b/CorsixTH/Lua/ui.lua index a74c8f2de..911ba71fb 100644 --- a/CorsixTH/Lua/ui.lua +++ b/CorsixTH/Lua/ui.lua @@ -722,7 +722,8 @@ function UI:onKeyDown(rawchar, modifiers) do local mapped_button = self.key_to_button_remaps[key] if mapped_button then - self:onMouseDown(mapped_button, self.cursor_x, self.cursor_y) + local s = TheApp.config.ui_scale + self:onMouseDown(mapped_button, self.cursor_x * s, self.cursor_y * s) return true end key = self.key_remaps[key] or key @@ -851,6 +852,10 @@ function UI:onTextInput(text) end function UI:onMouseDown(code, x, y) + local ui_scale = TheApp.config.ui_scale + x = math.floor(x / ui_scale) + y = math.floor(y / ui_scale) + self:setMouseReleased(false) local repaint = false local button = self.button_codes[code] or code @@ -875,6 +880,10 @@ function UI:onMouseDown(code, x, y) end function UI:onMouseUp(code, x, y) + local ui_scale = TheApp.config.ui_scale + x = math.floor(x / ui_scale) + y = math.floor(y / ui_scale) + local repaint = false local button = self.button_codes[code] or code self.down_count = self.down_count - 1 @@ -979,6 +988,10 @@ function UI:onMouseMove(x, y, dx, dy) return false end + local ui_scale = TheApp.config.ui_scale + x = math.floor(x / ui_scale) + y = math.floor(y / ui_scale) + local repaint = UpdateCursorPosition(self.app.video, x, y) self.cursor_x = x From 7f2a1b458b80d89ab5a4b0f96882d6fbd28e3b54 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Wed, 3 Dec 2025 22:04:38 -0500 Subject: [PATCH 05/50] Revert scaling full UI in favour of targetting scaling. Scale panel sprites because that can't be helped. Scale UIResizable, buttons, and panels Scale some text drawing - though I will likely replace that with a draw argument later. --- CorsixTH/Lua/dialogs/confirm_dialog.lua | 9 ++-- CorsixTH/Lua/dialogs/resizable.lua | 3 ++ CorsixTH/Lua/game_ui.lua | 7 +--- CorsixTH/Lua/ui.lua | 17 +------- CorsixTH/Lua/window.lua | 55 +++++++++++++++---------- 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/CorsixTH/Lua/dialogs/confirm_dialog.lua b/CorsixTH/Lua/dialogs/confirm_dialog.lua index fc70c5e7c..7612d5bc0 100644 --- a/CorsixTH/Lua/dialogs/confirm_dialog.lua +++ b/CorsixTH/Lua/dialogs/confirm_dialog.lua @@ -46,8 +46,8 @@ function UIConfirmDialog:UIConfirmDialog(ui, must_pause, text, callback_ok, call self.esc_closes = true self.on_top = true self.ui = ui - self.width = 183 - self.height = 199 + self.width = 183 * TheApp.config.ui_scale + self.height = 199 * TheApp.config.ui_scale self:setDefaultPosition(0.5, 0.5) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req04V", true) self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") @@ -122,7 +122,10 @@ function UIConfirmDialog:draw(canvas, x, y) Window.draw(self, canvas, x, y) x, y = x + self.x, y + self.y - self.white_font:drawWrapped(canvas, self.text, x + 17, y + 17, text_width) + local s = TheApp.config.ui_scale + canvas:scale(TheApp.config.ui_scale) + self.white_font:drawWrapped(canvas, self.text, math.floor(x / s + 17), math.floor(y / s + 17), text_width) + canvas:scale(1) end function UIConfirmDialog:afterLoad(old, new) diff --git a/CorsixTH/Lua/dialogs/resizable.lua b/CorsixTH/Lua/dialogs/resizable.lua index 1b9f83b2a..217e86116 100644 --- a/CorsixTH/Lua/dialogs/resizable.lua +++ b/CorsixTH/Lua/dialogs/resizable.lua @@ -66,6 +66,9 @@ function UIResizable:setSize(width, height) width = math.max(self.min_width, width) height = math.max(self.min_height, height) + width = width * TheApp.config.ui_scale + height = height * TheApp.config.ui_scale + self.width = width self.height = height self.background_panel.w = width diff --git a/CorsixTH/Lua/game_ui.lua b/CorsixTH/Lua/game_ui.lua index 73b6bf01b..1ea9d35cd 100644 --- a/CorsixTH/Lua/game_ui.lua +++ b/CorsixTH/Lua/game_ui.lua @@ -257,16 +257,11 @@ function GameUI:draw(canvas) self:setZoom(1) app.map:draw(canvas, dx, dy, config.width, config.height, 0, 0) end - canvas:scale(TheApp.config.ui_scale) Window.draw(self, canvas, 0, 0) -- NB: not calling UI.draw on purpose self:drawTooltip(canvas) if self.simulated_cursor then - self.simulated_cursor.draw( - canvas, - math.floor(self.cursor_x / TheApp.config.ui_scale), - math.floor(self.cursor_y / TheApp.config.ui_scale)) + self.simulated_cursor.draw(canvas, self.cursor_x, self.cursor_y) end - canvas:scale(1) end function GameUI:onChangeResolution() diff --git a/CorsixTH/Lua/ui.lua b/CorsixTH/Lua/ui.lua index 911ba71fb..f24c5d27d 100644 --- a/CorsixTH/Lua/ui.lua +++ b/CorsixTH/Lua/ui.lua @@ -314,13 +314,11 @@ function UI:draw(canvas) self.background:draw(canvas, math.floor((screen_w - bg_w) / 2), math.floor((screen_h - bg_h) / 2)) end end - canvas:scale(TheApp.config.ui_scale) Window.draw(self, canvas, 0, 0) self:drawTooltip(canvas) if self.simulated_cursor then self.simulated_cursor.draw(canvas, self.cursor_x, self.cursor_y) end - canvas:scale(1) end --! Register a key handler / hotkey for a window. @@ -722,8 +720,7 @@ function UI:onKeyDown(rawchar, modifiers) do local mapped_button = self.key_to_button_remaps[key] if mapped_button then - local s = TheApp.config.ui_scale - self:onMouseDown(mapped_button, self.cursor_x * s, self.cursor_y * s) + self:onMouseDown(mapped_button, self.cursor_x, self.cursor_y) return true end key = self.key_remaps[key] or key @@ -852,10 +849,6 @@ function UI:onTextInput(text) end function UI:onMouseDown(code, x, y) - local ui_scale = TheApp.config.ui_scale - x = math.floor(x / ui_scale) - y = math.floor(y / ui_scale) - self:setMouseReleased(false) local repaint = false local button = self.button_codes[code] or code @@ -880,10 +873,6 @@ function UI:onMouseDown(code, x, y) end function UI:onMouseUp(code, x, y) - local ui_scale = TheApp.config.ui_scale - x = math.floor(x / ui_scale) - y = math.floor(y / ui_scale) - local repaint = false local button = self.button_codes[code] or code self.down_count = self.down_count - 1 @@ -988,10 +977,6 @@ function UI:onMouseMove(x, y, dx, dy) return false end - local ui_scale = TheApp.config.ui_scale - x = math.floor(x / ui_scale) - y = math.floor(y / ui_scale) - local repaint = UpdateCursorPosition(self.app.video, x, y) self.cursor_x = x diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 9d50a4d9d..3fec56f30 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -76,9 +76,6 @@ function Window:setPosition(x, y) self.y_original = y -- Convert x and y to absolute pixel positions with regard to top/left local w, h = TheApp.config.width, TheApp.config.height - local scale = TheApp.config.ui_scale - w = w / scale - h = h / scale if x < 0 then x = math.ceil(w - self.width + x) elseif x < 1 then @@ -318,15 +315,18 @@ function Panel:drawLabel(canvas, x, y, limit) line = self:clipLine(line, self.w - 4) end + local scale = TheApp.config.ui_scale + canvas:scale(scale) if wrapped then - next_y, last_x = self.label_font:drawWrapped(canvas, line, x + self.x + 2, old_y, self.w - 4, self.align) + next_y, last_x = self.label_font:drawWrapped(canvas, line, math.floor((x + self.x + 2) / scale), math.floor(old_y / scale), math.floor((self.w - 4) / scale), self.align) else - next_y, last_x = self.label_font:draw(canvas, line, x + self.x + 2, old_y, self.w - 4, center_y and self.h or 0, self.align) + next_y, last_x = self.label_font:draw(canvas, line, math.floor((x + self.x + 2) / scale), math.floor(old_y / scale), math.floor((self.w - 4) / scale), math.floor((center_y and self.h or 0) / scale), self.align) end if not line:find("%S") then -- Special handling for empty lines or lines with only space - next_y = self.label_font:draw(nil, "A", x + self.x + 2, old_y, self.w - 4, center_y and self.h or 0, self.align) + next_y = self.label_font:draw(nil, "A", math.floor((x + self.x + 2) / scale), math.floor(old_y / scale), math.floor((self.w - 4) / scale), math.floor((center_y and self.h or 0) / scale), self.align) end + canvas:scale(1) if limit and limit[1] == i then break end @@ -345,9 +345,11 @@ end --! Set the size of a panel. --!param width (int) New width of the panel. --!param height (int) New height of the panel. -function Panel:setSize(width, height) - self.w = width - self.h = height +--!param scale (int|nil) Scale factor to apply to width and height. Default is TheApp.config.ui_scale. +function Panel:setSize(width, height, scale) + scale = scale or TheApp.config.ui_scale + self.w = width * scale + self.h = height * scale end --! Set the visibility of the panel. @@ -375,10 +377,10 @@ panel (in pixels) is known, it should be specified here to speed up hit-tests. function Window:addPanel(sprite_index, x, y, w, h) local panel = setmetatable({ window = self, - x = x, - y = y, - w = w, - h = h, + x = x * TheApp.config.ui_scale, + y = y * TheApp.config.ui_scale, + w = w and w * TheApp.config.ui_scale or w, + h = h and h * TheApp.config.ui_scale or h, sprite_index = sprite_index, visible = true, }, panel_mt) @@ -472,10 +474,10 @@ function Window:addBevelPanel(x, y, w, h, colour, highlight_colour, shadow_colou local panel = setmetatable({ window = self, - x = x, - y = y, - w = w, - h = h, + x = x * TheApp.config.ui_scale, + y = y * TheApp.config.ui_scale, + w = w * TheApp.config.ui_scale, + h = h * TheApp.config.ui_scale, colour = TheApp.video:mapRGB(colour.red, colour.green, colour.blue), highlight_colour = TheApp.video:mapRGB(highlight_colour.red, highlight_colour.green, highlight_colour.blue), shadow_colour = TheApp.video:mapRGB(shadow_colour.red, shadow_colour.green, shadow_colour.blue), @@ -729,8 +731,8 @@ end --!param height (int) New height of the button. function Button:setSize(width, height) self.panel_for_sprite:setSize(width, height) - self.r = self.x + width - self.b = self.y + height + self.r = self.x + width * TheApp.config.ui_scale + self.b = self.y + height * TheApp.config.ui_scale if self.tooltip then self.tooltip.tooltip_x = math.round((self.x + self.r) / 2, 1) self.tooltip.tooltip_y = self.y @@ -768,8 +770,10 @@ nil or not given, then the window is passed as the first argument. right-clicks the button. ]] function Window:makeButtonOnPanel(panel, x, y, w, h, sprite, on_click, on_click_self, on_rightclick) - x = x + panel.x - y = y + panel.y + x = x * TheApp.config.ui_scale + panel.x + y = y * TheApp.config.ui_scale + panel.y + w = w * TheApp.config.ui_scale + h = h * TheApp.config.ui_scale local button = setmetatable({ ui = self.ui, is_toggle = false, @@ -1475,7 +1479,14 @@ function Window:draw(canvas, x, y) if panel.custom_draw then panel:custom_draw(canvas, x, y) else - panel_sprites_draw(panel_sprites, canvas, panel.sprite_index, x + panel.x, y + panel.y) + canvas:scale(TheApp.config.ui_scale) + panel_sprites_draw( + panel_sprites, + canvas, + panel.sprite_index, + math.floor((x + panel.x) / TheApp.config.ui_scale), + math.floor((y + panel.y) / TheApp.config.ui_scale)) + canvas:scale(1) end end end From f3e6089858a7a82f4847d925004f5c669961dc65 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 5 Dec 2025 04:55:07 -0500 Subject: [PATCH 06/50] Add scale factor to fonts Use scale factor instead of canvas scale for window label text. The visual difference with TTF fonts is very clear. Yey HiDPI. --- CorsixTH/Lua/dialogs/resizables/main_menu.lua | 16 +++++++----- CorsixTH/Lua/dialogs/resizables/options.lua | 1 + CorsixTH/Lua/graphics.lua | 26 ++++++++++++++++--- CorsixTH/Lua/window.lua | 11 +++----- CorsixTH/Src/th_gfx_font.cpp | 24 ++++++++++++----- CorsixTH/Src/th_gfx_font.h | 3 +++ CorsixTH/Src/th_gfx_sdl.cpp | 9 ++++--- CorsixTH/Src/th_gfx_sdl.h | 4 ++- CorsixTH/Src/th_lua_gfx.cpp | 16 ++++++++++++ 9 files changed, 82 insertions(+), 28 deletions(-) diff --git a/CorsixTH/Lua/dialogs/resizables/main_menu.lua b/CorsixTH/Lua/dialogs/resizables/main_menu.lua index 1a0d52a8f..d0e5b0545 100644 --- a/CorsixTH/Lua/dialogs/resizables/main_menu.lua +++ b/CorsixTH/Lua/dialogs/resizables/main_menu.lua @@ -82,7 +82,7 @@ function UIMainMenu:UIMainMenu(ui) self:setDefaultPosition(0.5, 0.25) -- The main menu also shows the version number of the player's copy of the game. - self.label_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, {ttf_color = label_ttf_col}) + self.label_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, {ttf_color = label_ttf_col, apply_ui_scale = true}) self.release_string = release_string -- individual buttons @@ -116,19 +116,21 @@ function UIMainMenu:draw(canvas, x, y) UIResizable.draw(self, canvas, x, y) x, y = self.x + x, self.y + y + local s = TheApp.config.ui_scale + -- The following strings are drawn in reverse order - local ly = y + self.height - 15 + local ly = y + self.height - (15 * s) -- Move the version string up a bit if showing check for updates disabled warning. if TheApp:isUpdateCheckDisabledByConfig() then - self.label_font:draw(canvas, _S.main_menu.updates_off, x + 5, ly, 190, 0, "right") - ly = ly - 15 + self.label_font:draw(canvas, _S.main_menu.updates_off, x + 5 * s, ly, 190, 0, "right") + ly = ly - (15 * s) end -- Move if also showing the savegame version. if TheApp.config.debug then - self.label_font:draw(canvas, _S.main_menu.savegame_version .. TheApp.savegame_version, x + 5, ly, 190, 0, "right") - ly = ly - 15 + self.label_font:draw(canvas, _S.main_menu.savegame_version .. TheApp.savegame_version, x + 5 * s, ly, 190, 0, "right") + ly = ly - (15 * s) end - self.label_font:draw(canvas, _S.main_menu.version .. self.release_string, x + 5, ly, 190, 0, "right") + self.label_font:draw(canvas, _S.main_menu.version .. self.release_string, x + 5 * s, ly, 190, 0, "right") end function UIMainMenu:onTick() diff --git a/CorsixTH/Lua/dialogs/resizables/options.lua b/CorsixTH/Lua/dialogs/resizables/options.lua index f9e4cf49d..0abecc11a 100644 --- a/CorsixTH/Lua/dialogs/resizables/options.lua +++ b/CorsixTH/Lua/dialogs/resizables/options.lua @@ -346,6 +346,7 @@ function UIOptions:selectUIScale(number) TheApp:saveConfig() self.scale_ui_panel:setLabel(res.text) self.ui:onChangeResolution() + TheApp.gfx:onChangeUIScale() end --! Changes check for update setting to on/of diff --git a/CorsixTH/Lua/graphics.lua b/CorsixTH/Lua/graphics.lua index 7895dd491..1d1ee9fe0 100644 --- a/CorsixTH/Lua/graphics.lua +++ b/CorsixTH/Lua/graphics.lua @@ -390,6 +390,11 @@ function Graphics:onChangeLanguage() self.load_info = load_info end +function Graphics:onChangeUIScale() + -- Reload fonts + self:onChangeLanguage() +end + --! Font reload function. --!param font The font to (force) reloading. local function font_reloader(font) @@ -434,13 +439,14 @@ local shadow_to_cache_key = function(ttf_shadow) end local language_font_cache_key = function(name, font_options) - return string.format("%s,%d,%d,%s,%s,%s", + return string.format("%s,%d,%d,%s,%s,%s,%s", name or "nil", font_options.x_sep or 0, font_options.y_sep or 0, ttf_col_to_cache_key(font_options.ttf_color), font_options.force_bitmap and "T" or "F", - shadow_to_cache_key(font_options.ttf_shadow)) + shadow_to_cache_key(font_options.ttf_shadow), + font_options.apply_ui_scale and "s" or "f") end --! Load a true type font @@ -468,6 +474,8 @@ end -- offset_y are optional and default to 1. If the field is omitted entirely, -- no shadow is drawn. Alternately ttf_shadow can be set to true to draw a -- shadow with default parameters, or false to disable any shadow. +-- apply_ui_scale (boolean) Whether to apply the UI scale factor to the font +-- size. --!param y_sep (int) Deprecated: use font_options.y_sep instead. --!param ttf_color (table) Deprecated: use font_options.ttf_color instead. --!param force_bitmap (boolean) Deprecated: use font_options.force_bitmap @@ -491,7 +499,12 @@ function Graphics:loadLanguageFont(name, sprite_table, font_options, y_sep, ttf_ local cache_key = language_font_cache_key(name, font_options) local cache = self.cache.language_fonts[cache_key] font = cache and cache[sprite_table] - if not font then + if not font or (font_options.apply_ui_scale and font_options.scale_factor ~= self.app.config.ui_scale) then + -- Adjust scale based on current UI scale factor + if font_options.apply_ui_scale then + font_options.scale_factor = self.app.config.ui_scale + end + font = TH.freetype_font() -- TODO: Choose face based on "name" rather than always using same face. font:setFace(self.ttf_font_data) @@ -537,6 +550,8 @@ end -- offset_y are optional and default to 1. If the field is omitted entirely, -- no shadow is drawn. Alternately ttf_shadow can be set to true to draw a -- shadow with default parameters, or false to disable any shadow. +-- apply_ui_scale (boolean) Whether to apply the UI scale factor to the font +-- size. function Graphics:loadFontAndSpriteTable(dir, name, complex, palette, font_options) local sprite_table = self:loadSpriteTable(dir, name, complex, palette) @@ -570,6 +585,8 @@ end -- offset_y are optional and default to 1. If the field is omitted entirely, -- no shadow is drawn. Alternately ttf_shadow can be set to true to draw a -- shadow with default parameters, or false to disable any shadow. +-- apply_ui_scale (boolean) Whether to apply the UI scale factor to the font +-- size. function Graphics:loadFont(sprite_table, font_options, y_sep, ttf_color, force_bitmap) -- afterLoad fix. Saves before 215 would double the y_sep argument, but did not have ttf_col @@ -596,6 +613,9 @@ function Graphics:loadFont(sprite_table, font_options, y_sep, ttf_color, force_b font = TH.bitmap_font() font:setSeparation(font_options.x_sep or 0, font_options.y_sep or 0) font:setSheet(sprite_table) + if font_options.apply_ui_scale then + font:setScaleFactor(TheApp.config.ui_scale) + end else font = self:loadLanguageFont(self.language_font, sprite_table, font_options) end diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 3fec56f30..1d5d205f5 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -256,7 +256,7 @@ end --! can be either of "left", "center", "right" function Panel:setLabel(label, font, align) self.label = label or "" - self.label_font = font or self.label_font or TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, {ttf_shadow = true}) + self.label_font = font or self.label_font or TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, {ttf_shadow = true, apply_ui_scale = true}) self.align = align or self.align return self end @@ -315,18 +315,15 @@ function Panel:drawLabel(canvas, x, y, limit) line = self:clipLine(line, self.w - 4) end - local scale = TheApp.config.ui_scale - canvas:scale(scale) if wrapped then - next_y, last_x = self.label_font:drawWrapped(canvas, line, math.floor((x + self.x + 2) / scale), math.floor(old_y / scale), math.floor((self.w - 4) / scale), self.align) + next_y, last_x = self.label_font:drawWrapped(canvas, line, x + self.x + 2, old_y, self.w - 4, self.align) else - next_y, last_x = self.label_font:draw(canvas, line, math.floor((x + self.x + 2) / scale), math.floor(old_y / scale), math.floor((self.w - 4) / scale), math.floor((center_y and self.h or 0) / scale), self.align) + next_y, last_x = self.label_font:draw(canvas, line, x + self.x + 2, old_y, (self.w - 4), (center_y and self.h or 0), self.align) end if not line:find("%S") then -- Special handling for empty lines or lines with only space - next_y = self.label_font:draw(nil, "A", math.floor((x + self.x + 2) / scale), math.floor(old_y / scale), math.floor((self.w - 4) / scale), math.floor((center_y and self.h or 0) / scale), self.align) + next_y = self.label_font:draw(nil, "A", (x + self.x + 2), old_y, (self.w - 4), (center_y and self.h or 0), self.align) end - canvas:scale(1) if limit and limit[1] == i then break end diff --git a/CorsixTH/Src/th_gfx_font.cpp b/CorsixTH/Src/th_gfx_font.cpp index de64a454c..d64e3cf9c 100644 --- a/CorsixTH/Src/th_gfx_font.cpp +++ b/CorsixTH/Src/th_gfx_font.cpp @@ -52,6 +52,8 @@ void bitmap_font::set_separation(int iCharSep, int iLineSep) { line_spacing = iLineSep; } +void bitmap_font::set_scale_factor(int factor) { scale_factor = factor; } + text_layout bitmap_font::get_text_dimensions(const char* sMessage, size_t iMessageLength, int iMaxWidth) const { @@ -67,6 +69,7 @@ void bitmap_font::draw_text(render_target* pCanvas, const char* sMessage, unsigned int iLastASCII = static_cast(sheet->get_sprite_count()) + iFirstASCII; const char* sMessageEnd = sMessage + iMessageLength; + int scaled_letter_spacing = letter_spacing * scale_factor; while (sMessage != sMessageEnd) { unsigned int iChar = @@ -75,9 +78,12 @@ void bitmap_font::draw_text(render_target* pCanvas, const char* sMessage, iChar -= iFirstASCII; int iWidth; int iHeight; - sheet->draw_sprite(pCanvas, iChar, iX, iY, thdf_nearest); + sheet->draw_sprite(pCanvas, iChar, iX, iY, thdf_nearest, 0, + animation_effect::none, scale_factor); sheet->get_sprite_size_unchecked(iChar, &iWidth, &iHeight); - iX += iWidth + letter_spacing; + iWidth *= scale_factor; + iHeight *= scale_factor; + iX += iWidth + scaled_letter_spacing; } } } @@ -101,7 +107,9 @@ text_layout bitmap_font::draw_text_wrapped(render_target* pCanvas, while (sMessage != sMessageEnd && oDrawArea.row_count < iMaxRows) { const char* sBreakPosition = sMessageEnd; const char* sLastGoodBreakPosition = sBreakPosition; - int iMsgWidth = -letter_spacing; + int scaled_letter_spacing = letter_spacing * scale_factor; + int scaled_line_spacing = line_spacing * scale_factor; + int iMsgWidth = -scaled_letter_spacing; int iMsgBreakWidth = iMsgWidth; int iTallest = 0; const char* s; @@ -125,8 +133,10 @@ text_layout bitmap_font::draw_text_wrapped(render_target* pCanvas, if (iFirstASCII <= iChar && iChar <= iLastASCII) { sheet->get_sprite_size_unchecked(iChar - iFirstASCII, &iCharWidth, &iCharHeight); + iCharWidth *= scale_factor; + iCharHeight *= scale_factor; } - iMsgWidth += letter_spacing + iCharWidth; + iMsgWidth += scaled_letter_spacing + iCharWidth; if (iChar == ' ') { sLastGoodBreakPosition = sOld; iMsgBreakWidth = iMsgWidth - iCharWidth; @@ -150,18 +160,18 @@ text_layout bitmap_font::draw_text_wrapped(render_target* pCanvas, draw_text(pCanvas, sMessage, sBreakPosition - sMessage, iX + iXOffset, iY); } - iY += static_cast(iTallest) + line_spacing; + iY += static_cast(iTallest) + scaled_line_spacing; oDrawArea.end_x = iMsgWidth; oDrawArea.row_count++; if (foundNewLine) { - iY += static_cast(iTallest) + line_spacing; + iY += static_cast(iTallest) + scaled_line_spacing; oDrawArea.row_count++; } } else { iSkippedRows++; if (foundNewLine) { if (iSkippedRows == iSkipRows) { - iY += static_cast(iTallest) + line_spacing; + iY += static_cast(iTallest) + scaled_line_spacing; oDrawArea.row_count++; } iSkippedRows++; diff --git a/CorsixTH/Src/th_gfx_font.h b/CorsixTH/Src/th_gfx_font.h index b5eec223d..6243582bb 100644 --- a/CorsixTH/Src/th_gfx_font.h +++ b/CorsixTH/Src/th_gfx_font.h @@ -146,6 +146,8 @@ class bitmap_font final : public font { */ void set_sprite_sheet(sprite_sheet* pSpriteSheet); + void set_scale_factor(int factor); + sprite_sheet* get_sprite_sheet() { return sheet; } //! Set the separation between characters and between lines @@ -170,6 +172,7 @@ class bitmap_font final : public font { sprite_sheet* sheet{nullptr}; int letter_spacing{}; int line_spacing{}; + int scale_factor{1}; }; //! Adaptor around the FreeType2 library to a THFont. diff --git a/CorsixTH/Src/th_gfx_sdl.cpp b/CorsixTH/Src/th_gfx_sdl.cpp index 30d897e2b..145990fec 100644 --- a/CorsixTH/Src/th_gfx_sdl.cpp +++ b/CorsixTH/Src/th_gfx_sdl.cpp @@ -1328,7 +1328,7 @@ bool sprite_sheet::is_sprite_visible(size_t iSprite) const { void sprite_sheet::draw_sprite(render_target* pCanvas, size_t iSprite, int iX, int iY, uint32_t iFlags, size_t effect_ticks, - animation_effect effect) { + animation_effect effect, int scale_factor) { if (iSprite >= sprite_count || pCanvas == nullptr || pCanvas != target) return; sprite& sprite = sprites[iSprite]; @@ -1391,7 +1391,9 @@ void sprite_sheet::draw_sprite(render_target* pCanvas, size_t iSprite, int iX, // If the current offset rounds to a different value, render the // previous offset and start a new offset. SDL_Rect rcSrc = {0, y1, sprite.width, y2 - y1}; - SDL_Rect rcDest = {iX + x_offset, iY + y1, sprite.width, y2 - y1}; + SDL_Rect rcDest = {iX + x_offset, iY + y1, + sprite.width * scale_factor, + (y2 - y1) * scale_factor}; pCanvas->draw(pTexture, &rcSrc, &rcDest, iFlags); } y1 = y2; @@ -1400,7 +1402,8 @@ void sprite_sheet::draw_sprite(render_target* pCanvas, size_t iSprite, int iX, } } else { SDL_Rect rcSrc = {0, 0, sprite.width, sprite.height}; - SDL_Rect rcDest = {iX, iY, sprite.width, sprite.height}; + SDL_Rect rcDest = {iX, iY, sprite.width * scale_factor, + sprite.height * scale_factor}; pCanvas->draw(pTexture, &rcSrc, &rcDest, iFlags); } diff --git a/CorsixTH/Src/th_gfx_sdl.h b/CorsixTH/Src/th_gfx_sdl.h index c17278f92..312065396 100644 --- a/CorsixTH/Src/th_gfx_sdl.h +++ b/CorsixTH/Src/th_gfx_sdl.h @@ -599,10 +599,12 @@ class sprite_sheet { @param iFlags Flags to apply for drawing. @param effect_ticks The number of ticks into the effect animation. @param effect The animation effect to apply to the sprite. + @param scale_factor How much to scale the sprite when drawing. */ void draw_sprite(render_target* pCanvas, size_t iSprite, int iX, int iY, uint32_t iFlags, size_t effect_ticks = 0u, - animation_effect effect = animation_effect::none); + animation_effect effect = animation_effect::none, + int scale_factor = 1); //! Test whether a sprite was hit. /*! diff --git a/CorsixTH/Src/th_lua_gfx.cpp b/CorsixTH/Src/th_lua_gfx.cpp index 816beeab8..f9dfd978a 100644 --- a/CorsixTH/Src/th_lua_gfx.cpp +++ b/CorsixTH/Src/th_lua_gfx.cpp @@ -262,6 +262,13 @@ int l_bitmap_font_set_sep(lua_State* L) { return 1; } +int l_bitmap_font_set_scale(lua_State* L) { + bitmap_font* pFont = luaT_testuserdata(L); + pFont->set_scale_factor(static_cast(luaL_checkinteger(L, 2))); + lua_settop(L, 1); + return 1; +} + void l_freetype_throw_error_code(lua_State* L, FT_Error e) { if (e != FT_Err_Ok) { switch (e) { @@ -395,6 +402,14 @@ int l_freetype_font_set_font_options(lua_State* L) { } lua_pop(L, 1); + lua_getfield(L, -1, "scale_factor"); + int scale_factor = static_cast(luaL_optinteger(L, -1, 1)); + width *= scale_factor; + height *= scale_factor; + shadowOptions.offset_x *= scale_factor; + shadowOptions.offset_y *= scale_factor; + lua_pop(L, 1); + pFont->set_font_color(color); pFont->set_shadow_options(shadowOptions); l_freetype_throw_error_code(L, @@ -1011,6 +1026,7 @@ void lua_register_gfx(const lua_register_state* pState) { lcb.add_function(l_bitmap_font_get_spritesheet, "getSheet", lua_metatable::sheet); lcb.add_function(l_bitmap_font_set_sep, "setSeparation"); + lcb.add_function(l_bitmap_font_set_scale, "setScaleFactor"); } // FreeTypeFont From 0b039cbac589d5948c4c67d22f067b9096a2820c Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 5 Dec 2025 05:40:54 -0500 Subject: [PATCH 07/50] Reset option menu to apply scale change --- CorsixTH/Lua/dialogs/resizables/options.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CorsixTH/Lua/dialogs/resizables/options.lua b/CorsixTH/Lua/dialogs/resizables/options.lua index 0abecc11a..92b727f39 100644 --- a/CorsixTH/Lua/dialogs/resizables/options.lua +++ b/CorsixTH/Lua/dialogs/resizables/options.lua @@ -347,6 +347,10 @@ function UIOptions:selectUIScale(number) self.scale_ui_panel:setLabel(res.text) self.ui:onChangeResolution() TheApp.gfx:onChangeUIScale() + + -- Reset the options window to apply the new scale + UIResizable.close(self) + self.ui:addWindow(UIOptions(self.ui, self.mode)) end --! Changes check for update setting to on/of From c2eaac3759d096c7b105aeaf6ccfaa5a44b19427 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 5 Dec 2025 05:46:23 -0500 Subject: [PATCH 08/50] Apply font scale to confirm dialog --- CorsixTH/Lua/dialogs/confirm_dialog.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CorsixTH/Lua/dialogs/confirm_dialog.lua b/CorsixTH/Lua/dialogs/confirm_dialog.lua index 7612d5bc0..c9fecb867 100644 --- a/CorsixTH/Lua/dialogs/confirm_dialog.lua +++ b/CorsixTH/Lua/dialogs/confirm_dialog.lua @@ -50,14 +50,14 @@ function UIConfirmDialog:UIConfirmDialog(ui, must_pause, text, callback_ok, call self.height = 199 * TheApp.config.ui_scale self:setDefaultPosition(0.5, 0.5) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req04V", true) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) self.text = text self.callback_ok = callback_ok -- Callback function to launch if user chooses ok self.callback_cancel = callback_cancel -- Callback function to launch if user chooses cancel self.must_pause = must_pause -- Check how "high" the dialog must be - local _, text_height = self.white_font:sizeOf(text, text_width) + local _, text_height = self.white_font:sizeOf(text, text_width * TheApp.config.ui_scale) self:addPanel(top_frame, 0, 0) -- Dialog header local last_y = top_frame_height @@ -123,9 +123,7 @@ function UIConfirmDialog:draw(canvas, x, y) x, y = x + self.x, y + self.y local s = TheApp.config.ui_scale - canvas:scale(TheApp.config.ui_scale) - self.white_font:drawWrapped(canvas, self.text, math.floor(x / s + 17), math.floor(y / s + 17), text_width) - canvas:scale(1) + self.white_font:drawWrapped(canvas, self.text, x + 17 * s, y + 17 * s, text_width * s) end function UIConfirmDialog:afterLoad(old, new) From c7102d173c295611dbf43e24631dbb07809368fa Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 5 Dec 2025 20:02:39 -0500 Subject: [PATCH 09/50] UI scaling for tip of the day --- CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua b/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua index ef8a55a93..88748ac74 100644 --- a/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua +++ b/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua @@ -41,7 +41,7 @@ function UITipOfTheDay:UITipOfTheDay(ui) self.ui = ui self.resizable = false self:setDefaultPosition(-20, -20) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) self.num_tips = #_S.totd_window.tips if self.num_tips == 0 then @@ -73,8 +73,10 @@ function UITipOfTheDay:draw(canvas, x, y) -- Draw tip x, y = self.x + x, self.y + y + + local s = TheApp.config.ui_scale local text = _S.totd_window.tips[self.tip_num] - self.white_font:drawWrapped(canvas, text, x + 10, y + 10, self.width - 20) + self.white_font:drawWrapped(canvas, text, x + 10 * s, y + 10 * s, self.width - 20 * s) end function UITipOfTheDay:buttonPrev() From 3da9193c5a3434c102fb401263bb28b57453441a Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 5 Dec 2025 20:34:29 -0500 Subject: [PATCH 10/50] Progress towards bottom panel scaling --- CorsixTH/Lua/dialogs/bottom_panel.lua | 31 ++++++++++++++++----------- CorsixTH/Lua/window.lua | 6 ++++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CorsixTH/Lua/dialogs/bottom_panel.lua b/CorsixTH/Lua/dialogs/bottom_panel.lua index 2e19abd34..1ebaced2a 100644 --- a/CorsixTH/Lua/dialogs/bottom_panel.lua +++ b/CorsixTH/Lua/dialogs/bottom_panel.lua @@ -32,8 +32,7 @@ function UIBottomPanel:UIBottomPanel(ui) self.ui = ui self.world = app.world self.on_top = false - self.width = 640 - self.height = 48 + self:setSize(640, 48) self:setDefaultPosition(0.5, -0.1) self:_initFonts(app.gfx) @@ -75,7 +74,7 @@ function UIBottomPanel:drawPanels() -- If there is a machine menu button, then lets adjust bottom panel width so it can fit if self:machineMenuButtonExists() then - self.width = 676 + self.width = 676 * TheApp.config.ui_scale for x = 377, 660, 10 do self:addPanel(13, x, 0) end @@ -83,7 +82,7 @@ function UIBottomPanel:drawPanels() self.offset = 38 else - self.width = 640 + self.width = 640 * TheApp.config.ui_scale for x = 377, 630, 10 do self:addPanel(13, x, 0) end @@ -106,16 +105,19 @@ function UIBottomPanel:drawPanels() .panel_for_sprite.custom_draw = --[[persistable:machine_menu_buttons]] function(panel, canvas, x, y) x = x + panel.x y = y + panel.y - panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y) + local s = TheApp.config.ui_scale + canvas:scale(s) + panel.window.panel_sprites:draw(canvas, panel.sprite_index, math.floor(x / s), math.floor(y / s)) local btn = panel.window.active_button if panels[1].visible then local w = self.ui:getWindow(UIMachineMenu) if w or btn and btn.panel_for_sprite == panel and btn.active then - aux_sprites:draw(canvas, 23, x, y) + aux_sprites:draw(canvas, 23, math.floor(x / s), math.floor(y / s)) else - aux_sprites:draw(canvas, 22, x, y) + aux_sprites:draw(canvas, 22, math.floor(x / s), math.floor(y / s)) end end + canvas:scale(1) end end panels[2] = self:addPanel(17, 407 + self.offset, 0) -- Town map button @@ -149,10 +151,10 @@ function UIBottomPanel:_initFonts(gfx) local date_label_color = { red = 175, green = 50, blue = 15 } local pause_label_color = { red = 35, green = 138, blue = 173 } self.panel_sprites = gfx:loadSpriteTable("Data", "Panel02V", true) - self.money_font = gfx:loadFontAndSpriteTable("QData", "Font05V") - self.date_font = gfx:loadFontAndSpriteTable("QData", "Font16V", nil, nil, {ttf_color = date_label_color}) - self.white_font = gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, {y_sep = -2}) - self.pause_font = gfx:loadFontAndSpriteTable("QData", "Font124V", nil, nil, {ttf_color = pause_label_color}) + self.money_font = gfx:loadFontAndSpriteTable("QData", "Font05V", nil, nil, { apply_ui_scale = true }) + self.date_font = gfx:loadFontAndSpriteTable("QData", "Font16V", nil, nil, {ttf_color = date_label_color, apply_ui_scale = true}) + self.white_font = gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, {y_sep = -2, apply_ui_scale = true}) + self.pause_font = gfx:loadFontAndSpriteTable("QData", "Font124V", nil, nil, {ttf_color = pause_label_color, apply_ui_scale = true}) end function UIBottomPanel:registerKeyHandlers() @@ -233,6 +235,7 @@ function UIBottomPanel:draw(canvas, x, y) -- Draw balance with temporary offset in unicode languages x, y = x + self.x, y + self.y + local s = TheApp.config.ui_scale local offset_x, offset_y = 0, 0 if self.ui.app.gfx:drawNumbersFromUnicode() then offset_x = 4 @@ -241,12 +244,12 @@ function UIBottomPanel:draw(canvas, x, y) local balance = math.floor(self.ui.hospital.balance) local i = 7 - tostring(balance):len() -- Indent balances under 100k for digit in ("%7i"):format(balance):gmatch("[-0-9]") do - self.money_font:draw(canvas, digit, x + offset_x + 44 + i * 13, y + offset_y + 9) + self.money_font:draw(canvas, digit, x + offset_x * s + 44 * s + i * 13 * s, y + offset_y * s + 9 * s) i = i + 1 end local game_date = self.world:date() local month, day = game_date:monthOfYear(), game_date:dayOfMonth() - self.date_font:draw(canvas, _S.date_format.daymonth:format(day, month), x + 140, y + 20, 60, 0) + self.date_font:draw(canvas, _S.date_format.daymonth:format(day, month), x + 140 * s, y + 20 * s, 60 * s, 0) -- Draw possible information in the dynamic info bar if not self.additional_panels[1].visible then @@ -941,6 +944,8 @@ function UIBottomPanel:editRoom() end function UIBottomPanel:afterLoad(old, new) + self:setSize(640, 48) + self:setDefaultPosition(0.5, -0.1) self:removeAllPanels() self:drawPanels() self:updateButtonStates() diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 1d5d205f5..ff7890810 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -64,6 +64,12 @@ function Window:mustPause() return false end +function Window:setSize(width, height, scale) + scale = scale or TheApp.config.ui_scale + self.width = width * scale + self.height = height * scale +end + -- Sets the window's onscreen position. Each of x and y can be: -- Integers >= 0 - Absolute pixel positions of top/left edge of window relative -- to top/left edge of screen From 321a07760a28c6ff9f7b4b7f87f3e24fc6f5841a Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sat, 6 Dec 2025 12:31:06 -0500 Subject: [PATCH 11/50] Add option to draw scaled sprites Use that instead of canvas:scale for panel sprites --- CorsixTH/Lua/api_version.lua | 2 +- CorsixTH/Lua/window.lua | 7 +++--- CorsixTH/Src/th_lua_gfx.cpp | 42 ++++++++++++++++++++++++++++++++---- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/CorsixTH/Lua/api_version.lua b/CorsixTH/Lua/api_version.lua index b27c5ade9..62b4dfd88 100644 --- a/CorsixTH/Lua/api_version.lua +++ b/CorsixTH/Lua/api_version.lua @@ -32,4 +32,4 @@ Note: This file compiles as both Lua and C++. */ #endif /*]] --*/ -return 2703; +return 2704; diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index ff7890810..7fc65271f 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -1482,14 +1482,13 @@ function Window:draw(canvas, x, y) if panel.custom_draw then panel:custom_draw(canvas, x, y) else - canvas:scale(TheApp.config.ui_scale) panel_sprites_draw( panel_sprites, canvas, panel.sprite_index, - math.floor((x + panel.x) / TheApp.config.ui_scale), - math.floor((y + panel.y) / TheApp.config.ui_scale)) - canvas:scale(1) + x + panel.x, + y + panel.y, + { scaleFactor = TheApp.config.ui_scale }) end end end diff --git a/CorsixTH/Src/th_lua_gfx.cpp b/CorsixTH/Src/th_lua_gfx.cpp index f9dfd978a..38ac9fa82 100644 --- a/CorsixTH/Src/th_lua_gfx.cpp +++ b/CorsixTH/Src/th_lua_gfx.cpp @@ -199,10 +199,44 @@ int l_spritesheet_draw(lua_State* L) { int iSprite = static_cast(luaL_checkinteger(L, 3)); // No array adjustment - pSheet->draw_sprite(pCanvas, iSprite, - static_cast(luaL_optinteger(L, 4, 0)), - static_cast(luaL_optinteger(L, 5, 0)), - static_cast(luaL_optinteger(L, 6, 0))); + int x = static_cast(luaL_optinteger(L, 4, 0)); + int y = static_cast(luaL_optinteger(L, 5, 0)); + + int scaleFactor = 1; + uint32_t flags = 0; + int arg6type = lua_type(L, 6); + if (arg6type == LUA_TTABLE) { + lua_getfield(L, 6, "flags"); + flags = static_cast(luaL_optinteger(L, -1, 0)); + lua_pop(L, 1); + + lua_getfield(L, 6, "nearest"); + if (lua_toboolean(L, -1)) { + flags |= thdf_nearest; + } + lua_pop(L, 1); + + lua_getfield(L, 6, "flipHorizontal"); + if (lua_toboolean(L, -1)) { + flags |= thdf_flip_horizontal; + } + lua_pop(L, 1); + + lua_getfield(L, 6, "flipVertical"); + if (lua_toboolean(L, -1)) { + flags |= thdf_flip_vertical; + } + lua_pop(L, 1); + + lua_getfield(L, 6, "scaleFactor"); + scaleFactor = static_cast(luaL_optinteger(L, -1, 1)); + lua_pop(L, 1); + } else if (arg6type != LUA_TNONE) { + luaL_error(L, "Expected table for draw options"); + } + + pSheet->draw_sprite(pCanvas, iSprite, x, y, flags, 0, animation_effect::none, + scaleFactor); lua_settop(L, 1); return 1; From 92545c173b0bb22214e4908947e7687c09803848 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sat, 6 Dec 2025 23:31:31 -0500 Subject: [PATCH 12/50] More bottom panel scaling --- CorsixTH/Lua/dialogs/bottom_panel.lua | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/CorsixTH/Lua/dialogs/bottom_panel.lua b/CorsixTH/Lua/dialogs/bottom_panel.lua index 1ebaced2a..01263c4c4 100644 --- a/CorsixTH/Lua/dialogs/bottom_panel.lua +++ b/CorsixTH/Lua/dialogs/bottom_panel.lua @@ -106,18 +106,16 @@ function UIBottomPanel:drawPanels() x = x + panel.x y = y + panel.y local s = TheApp.config.ui_scale - canvas:scale(s) - panel.window.panel_sprites:draw(canvas, panel.sprite_index, math.floor(x / s), math.floor(y / s)) + panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) local btn = panel.window.active_button if panels[1].visible then local w = self.ui:getWindow(UIMachineMenu) if w or btn and btn.panel_for_sprite == panel and btn.active then - aux_sprites:draw(canvas, 23, math.floor(x / s), math.floor(y / s)) + aux_sprites:draw(canvas, 23, x, y, { scaleFactor = s }) else - aux_sprites:draw(canvas, 22, math.floor(x / s), math.floor(y / s)) + aux_sprites:draw(canvas, 22, x, y, { scaleFactor = s }) end end - canvas:scale(1) end end panels[2] = self:addPanel(17, 407 + self.offset, 0) -- Town map button @@ -253,26 +251,26 @@ function UIBottomPanel:draw(canvas, x, y) -- Draw possible information in the dynamic info bar if not self.additional_panels[1].visible then - self:drawDynamicInfo(canvas, x + 364, y) + self:drawDynamicInfo(canvas, x + 364 * s, y) end if self.show_animation then if self.factory_counter >= 1 then - self.panel_sprites:draw(canvas, 40, x + 177, y + 1) + self.panel_sprites:draw(canvas, 40, x + 177 * s, y + 1, { scaleFactor = s }) end if self.factory_counter > 1 and self.factory_counter <= 22 then for dx = 0, self.factory_counter do - self.panel_sprites:draw(canvas, 41, x + 179 + dx, y + 1) + self.panel_sprites:draw(canvas, 41, x + 179 * s + dx, y + 1 * s, { scaleFactor = s }) end end if self.factory_counter == 22 then - self.panel_sprites:draw(canvas, 42, x + 201, y + 1) + self.panel_sprites:draw(canvas, 42, x + 201 * s, y + 1 * s, { scaleFactor = s }) end end - self:drawReputationMeter(canvas, x + 55, y + 35) + self:drawReputationMeter(canvas, x + 55 * s, y + 35 * s) end function UIBottomPanel:setPosition(x, y) @@ -284,9 +282,10 @@ end -- x_left is the leftmost x-coordinate of the reputation meter -- y is the y-coordinate of the reputation meter function UIBottomPanel:drawReputationMeter(canvas, x_left, y) - local width = 65 -- Reputation meter width + local s = TheApp.config.ui_scale + local width = 65 * s -- Reputation meter width local step = width / (self.ui.hospital.reputation_max - self.ui.hospital.reputation_min) - self.panel_sprites:draw(canvas, 36, x_left + math.floor(step * (self.ui.hospital.reputation - self.ui.hospital.reputation_min)), y) + self.panel_sprites:draw(canvas, 36, x_left + math.floor(step * (self.ui.hospital.reputation - self.ui.hospital.reputation_min)), y, { scaleFactor = s }) end --! Adds dynamic text to the bottom panel based on cursor position @@ -294,14 +293,15 @@ end --!param x (num) coordinate --!param y (num) coordinate function UIBottomPanel:drawDynamicInfo(canvas, x, y) + local s = TheApp.config.ui_scale if self.world:isCurrentSpeed("Pause") then if not self.world.user_actions_allowed then -- Original pause behaviour, show pause text - self.pause_font:drawWrapped(canvas, _S.misc.pause, x + 10, y + 14, 255, "center") + self.pause_font:drawWrapped(canvas, _S.misc.pause, x + 10 * s, y + 14 * s, 255 * s, "center") return elseif not (self.dynamic_info and self.dynamic_info["text"]) then -- User allows editing while paused, only show pause text where dynamic text not present - self.pause_font:drawWrapped(canvas, _S.misc.pause, x + 10, y + 14, 255, "center") + self.pause_font:drawWrapped(canvas, _S.misc.pause, x + 10 * s, y + 14 * s, 255 * s, "center") return end end @@ -313,17 +313,17 @@ function UIBottomPanel:drawDynamicInfo(canvas, x, y) local info = self.dynamic_info local font = self.white_font for i, text in ipairs(info["text"]) do - font:drawWrapped(canvas, text, x + 20, y + 10 * i, 240) + font:drawWrapped(canvas, text, x + 20 * s, y + 10 * i * s, 240 * s) if i == #info["text"] and info["progress"] then local white = canvas:mapRGB(255, 255, 255) local black = canvas:mapRGB(0, 0, 0) local orange = canvas:mapRGB(221, 83, 0) - canvas:drawRect(white, x + 165, y + 10 * i, 100, 10) - canvas:drawRect(black, x + 166, y + 1 + 10 * i, 98, 8) - canvas:drawRect(orange, x + 166, y + 1 + 10 * i, math.floor(98 * info["progress"]), 8) + canvas:drawRect(white, x + 165 * s, y + 10 * i * s, 100 * s, 10 * s) + canvas:drawRect(black, x + 166 * s, y + s + 10 * i * s, 98 * s, 8 * s) + canvas:drawRect(orange, x + 166 * s, y + s + 10 * i * s, math.floor(98 * info["progress"] * s), 8 * s) if info["dividers"] then for _, value in ipairs(info["dividers"]) do - canvas:drawRect(white, x + 165 + math.floor(value * 100), y + 10 * i, 1, 10) + canvas:drawRect(white, x + 165 * s + math.floor(value * 100 * s), y + 10 * i * s, s, 10 * s) end end end From 4a4ead8903ded79fb7e084638090a510d534d49e Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sun, 7 Dec 2025 00:10:07 -0500 Subject: [PATCH 13/50] scale the information window --- CorsixTH/Lua/dialogs/information.lua | 70 ++++++++++++++++------------ CorsixTH/Lua/graphics.lua | 1 + CorsixTH/Lua/window.lua | 11 +++-- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/CorsixTH/Lua/dialogs/information.lua b/CorsixTH/Lua/dialogs/information.lua index 67c736e04..08d171443 100644 --- a/CorsixTH/Lua/dialogs/information.lua +++ b/CorsixTH/Lua/dialogs/information.lua @@ -41,7 +41,7 @@ function UIInformation:UIInformation(ui, text, use_built_in_font) self.panel_sprites = app.gfx:loadSpriteTable("Data", "PulldV", true) self.active_hover = false if not use_built_in_font then - self.black_font = app.gfx:loadFontAndSpriteTable("QData", "Font00V") + self.black_font = app.gfx:loadFontAndSpriteTable("QData", "Font00V", nil, nil, { apply_ui_scale = true }) else self.black_font = app.gfx:loadBuiltinFont() self.black_background = true @@ -55,13 +55,15 @@ function UIInformation:UIInformation(ui, text, use_built_in_font) self.text = text end + local s = TheApp.config.ui_scale + -- Window size parameters - self.text_width = 300 + self.text_width = 300 * s self.spacing = { - l = 15, - r = 15, - t = 15, - b = 18 + 15, -- Size of close button + padding + l = 15 * s, + r = 15 * s, + t = 15 * s, + b = 18 * s + 15 * s, -- Size of close button + padding } self:onChangeLanguage() @@ -88,35 +90,37 @@ function UIInformation:onChangeLanguage() self:removeAllPanels() - for x = 4, self.width - 4, 4 do - self:addPanel(12, x, 0) -- Dialog top and bottom borders - self:addPanel(16, x, self.height - 4) + local s = TheApp.config.ui_scale + for x = 4 * s, self.width - 4 * s, 4 do + self:addPanel(12, x, 0, 0, 0, 1) -- Dialog top and bottom borders + self:addPanel(16, x, self.height - 4 * s, 0, 0, 1) end - for y = 4, self.height - 4, 4 do - self:addPanel(18, 0, y) -- Dialog left and right borders - self:addPanel(14, self.width - 4, y) + for y = 4 * s, self.height - 4 * s, 4 do + self:addPanel(18, 0, y, 0, 0, 1) -- Dialog left and right borders + self:addPanel(14, self.width - 4 * s, y, 0, 0, 1) end - self:addPanel(11, 0, 0) -- Border top left corner - self:addPanel(17, 0, self.height - 4) -- Border bottom left corner - self:addPanel(13, self.width - 4, 0) -- Border top right corner - self:addPanel(15, self.width - 4, self.height - 4) -- Border bottom right corner + self:addPanel(11, 0, 0, 0, 0, 1) -- Border top left corner + self:addPanel(17, 0, self.height - 4 * s, 0, 0, 1) -- Border bottom left corner + self:addPanel(13, self.width - 4 * s, 0, 0, 0, 1) -- Border top right corner + self:addPanel(15, self.width - 4 * s, self.height - 4 * s, 0, 0, 1) -- Border bottom right corner -- Close button - self:addPanel(19, self.width - 28, self.height - 28):makeButton(0, 0, 18, 18, 20, self.close):setTooltip(_S.tooltip.information.close) + self:addPanel(19, self.width - 28 * s, self.height - 28 * s, 18 * s, 18 * s, 1):makeButton(0, 0, 18, 18, 20, self.close):setTooltip(_S.tooltip.information.close) .panel_for_sprite.custom_draw = --[[persistable:information_close_button]] function(panel, canvas, x, y) x = x + panel.x y = y + panel.y - panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y) + panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) if self.active_hover then - self.panel_sprites:draw(canvas, 20, x, y) + self.panel_sprites:draw(canvas, 20, x, y, { scaleFactor = s }) end end end function UIInformation:draw(canvas, x, y) local dx, dy = x + self.x, y + self.y + local s = TheApp.config.ui_scale local background = self.black_background and canvas:mapRGB(0, 0, 0) or canvas:mapRGB(255, 255, 255) - canvas:drawRect(background, dx + 4, dy + 4, self.width - 8, self.height - 8) + canvas:drawRect(background, dx + 4 * s, dy + 4 * s, self.width - 8 * s, self.height - 8 * s) local last_y = dy + self.spacing.t for _, text in ipairs(self.text) do last_y = self.black_font:drawWrapped(canvas, text, dx + self.spacing.l, last_y, self.text_width) @@ -126,7 +130,12 @@ function UIInformation:draw(canvas, x, y) end function UIInformation:onMouseMove(x, y, dx, dy) - self.active_hover = self:hoverTest(self.active_hover, x, y, self.width - 29, self.width - 10, self.height - 29, self.height - 10) + local s = TheApp.config.ui_scale + self.active_hover = self:hoverTest( + self.active_hover, + x, y, + self.width - 29 * s, self.width - 10 * s, + self.height - 29 * s, self.height - 10 * s) return Window:onMouseMove(x, y, dx, dy) end @@ -147,16 +156,15 @@ function UIInformation:close() end function UIInformation:afterLoad(old, new) - if old < 182 then - -- box resized to be more like the original - self.spacing = { - l = 15, - r = 15, - t = 15, - b = 33, -- includes space for close button (18px + 15px padding) - } - self:onChangeLanguage() - end + -- box resized to be more like the original + local s = TheApp.config.ui_scale + self.spacing = { + l = 15 * s, + r = 15 * s, + t = 15 * s, + b = 18 * s + 15 * s, + } + self:onChangeLanguage() Window.afterLoad(self, old, new) self:registerKeyHandlers() end diff --git a/CorsixTH/Lua/graphics.lua b/CorsixTH/Lua/graphics.lua index 1d1ee9fe0..101472793 100644 --- a/CorsixTH/Lua/graphics.lua +++ b/CorsixTH/Lua/graphics.lua @@ -337,6 +337,7 @@ function Graphics:loadBuiltinFont() font = TH.bitmap_font() font:setSheet(sheet) font:setSeparation(1, 0) + font:setScaleFactor(TheApp.config.ui_scale) self.load_info[font] = {self.loadBuiltinFont, self} self.builtin_font = font end diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 7fc65271f..25010431d 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -377,13 +377,14 @@ panel (in pixels) is known, it should be specified here to speed up hit-tests. !param h (integer, nil) If the panel is totally opaque, and the height of the panel (in pixels) is known, it should be specified here to speed up hit-tests. ]] -function Window:addPanel(sprite_index, x, y, w, h) +function Window:addPanel(sprite_index, x, y, w, h, scale) + scale = scale or TheApp.config.ui_scale local panel = setmetatable({ window = self, - x = x * TheApp.config.ui_scale, - y = y * TheApp.config.ui_scale, - w = w and w * TheApp.config.ui_scale or w, - h = h and h * TheApp.config.ui_scale or h, + x = x * scale, + y = y * scale, + w = w and w * scale or w, + h = h and h * scale or h, sprite_index = sprite_index, visible = true, }, panel_mt) From 6fa82e36a1e9c2ba4abbf15a25777b4acf2e3b2b Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sun, 7 Dec 2025 00:23:43 -0500 Subject: [PATCH 14/50] scale the jukebox --- CorsixTH/Lua/dialogs/jukebox.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CorsixTH/Lua/dialogs/jukebox.lua b/CorsixTH/Lua/dialogs/jukebox.lua index 72648bd31..cf33ec8fa 100644 --- a/CorsixTH/Lua/dialogs/jukebox.lua +++ b/CorsixTH/Lua/dialogs/jukebox.lua @@ -37,8 +37,8 @@ function UIJukebox:UIJukebox(app) self.height = 74 + 30 * #self.audio.background_playlist + 18 self:setDefaultPosition(26, 26) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req13V", true) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") - self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V") + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, { apply_ui_scale = true }) -- Dialog head (current track title & exit button) self:addPanel(389, 0, 0) @@ -159,20 +159,21 @@ function UIJukebox:draw(canvas, x, y) Window.draw(self, canvas, x, y) x, y = self.x + x, self.y + y + local s = TheApp.config.ui_scale local playing = self.audio.background_music or "" for i, info in ipairs(self.audio.background_playlist) do - local ypos = y + 47 + i * 30 + local ypos = y + 47 * s + i * 30 * s local font = self.white_font if info.music == playing then font = self.blue_font end local str = info.title - while font:sizeOf(str) > 185 do + while font:sizeOf(str) > 185 * s do str = string.sub(str, 1, string.len(str) - 5) .. "..." end - font:draw(canvas, str, x + 24, ypos + 11) + font:draw(canvas, str, x + 24 * s, ypos + 11 * s) if info.music == playing then - font:draw(canvas, str, x + 24, self.y + 27) + font:draw(canvas, str, x + 24 * s, self.y + 27 * s) end end end From 24a5a2bf9875cba06edc19a7af9ae53a0801a73f Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Mon, 8 Dec 2025 22:05:00 -0500 Subject: [PATCH 15/50] Use new draw sprites API for map editor --- CorsixTH/Lua/dialogs/resizables/map_editor.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CorsixTH/Lua/dialogs/resizables/map_editor.lua b/CorsixTH/Lua/dialogs/resizables/map_editor.lua index d585694b4..353246182 100644 --- a/CorsixTH/Lua/dialogs/resizables/map_editor.lua +++ b/CorsixTH/Lua/dialogs/resizables/map_editor.lua @@ -728,7 +728,7 @@ function UIMapEditor:buildSpriteButtons(buttons) for _, spr in ipairs(panel.editor_button.sprites) do local xspr = x + (spr.xpos - spr.ypos) * 32 local yspr = y + (spr.xpos + spr.ypos) * 16 - 32 - panel.window.panel_sprites:draw(canvas, spr.sprite % 256, xspr, yspr, math.floor(spr.sprite / 256)) + panel.window.panel_sprites:draw(canvas, spr.sprite % 256, xspr, yspr, { flags = math.floor(spr.sprite / 256) }) end end From 2774bcdfadbe3c7ddf446e3c2a5c6137dc703938 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Mon, 8 Dec 2025 22:05:41 -0500 Subject: [PATCH 16/50] Scale pixel coordinates for panels,buttons,windows Instead of storing the scaled height and width as state store whether the Window / Panel is using scale independent pixels. If so perform the adjustment in the draw/hitTest functions. The goal is to avoid mixing scale information into the save state in ways that will be hard to deal with. Some regression here, mainly tooltips are not functioning --- CorsixTH/Lua/dialogs/confirm_dialog.lua | 5 +- CorsixTH/Lua/dialogs/resizable.lua | 40 ++++--- CorsixTH/Lua/dialogs/resizables/main_menu.lua | 2 +- .../Lua/dialogs/resizables/tip_of_the_day.lua | 2 +- CorsixTH/Lua/window.lua | 100 ++++++++++-------- 5 files changed, 81 insertions(+), 68 deletions(-) diff --git a/CorsixTH/Lua/dialogs/confirm_dialog.lua b/CorsixTH/Lua/dialogs/confirm_dialog.lua index c9fecb867..5504247a8 100644 --- a/CorsixTH/Lua/dialogs/confirm_dialog.lua +++ b/CorsixTH/Lua/dialogs/confirm_dialog.lua @@ -46,8 +46,8 @@ function UIConfirmDialog:UIConfirmDialog(ui, must_pause, text, callback_ok, call self.esc_closes = true self.on_top = true self.ui = ui - self.width = 183 * TheApp.config.ui_scale - self.height = 199 * TheApp.config.ui_scale + self.width = 183 + self.height = 199 self:setDefaultPosition(0.5, 0.5) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req04V", true) self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) @@ -58,6 +58,7 @@ function UIConfirmDialog:UIConfirmDialog(ui, must_pause, text, callback_ok, call -- Check how "high" the dialog must be local _, text_height = self.white_font:sizeOf(text, text_width * TheApp.config.ui_scale) + text_height = text_height / TheApp.config.ui_scale -- Scale independent pixels self:addPanel(top_frame, 0, 0) -- Dialog header local last_y = top_frame_height diff --git a/CorsixTH/Lua/dialogs/resizable.lua b/CorsixTH/Lua/dialogs/resizable.lua index 217e86116..67feb1997 100644 --- a/CorsixTH/Lua/dialogs/resizable.lua +++ b/CorsixTH/Lua/dialogs/resizable.lua @@ -66,9 +66,6 @@ function UIResizable:setSize(width, height) width = math.max(self.min_width, width) height = math.max(self.min_height, height) - width = width * TheApp.config.ui_scale - height = height * TheApp.config.ui_scale - self.width = width self.height = height self.background_panel.w = width @@ -89,26 +86,23 @@ end function UIResizable:draw(canvas, x, y) local sprites = self.border_sprites if sprites then - local draw = sprites.draw + local s = TheApp.config.ui_scale local xabs = self.x + x local yabs = self.y + y - canvas:nonOverlapping(true) - draw(sprites, canvas, 10, xabs + self.border_pos.left , yabs + self.border_pos.upper) -- upper left corner - draw(sprites, canvas, 12, xabs + self.border_pos.corner_right, yabs + self.border_pos.upper) -- upper right corner - draw(sprites, canvas, 15, xabs + self.border_pos.left , yabs + self.border_pos.corner_lower) -- lower left corner - draw(sprites, canvas, 17, xabs + self.border_pos.corner_right, yabs + self.border_pos.corner_lower) -- lower right corner - - for xpos = xabs + border_size_x, xabs + self.border_pos.corner_right - 1, border_size_x do - draw(sprites, canvas, 11, xpos, yabs + self.border_pos.upper) -- upper edge - draw(sprites, canvas, 16, xpos, yabs + self.border_pos.lower) -- lower edge + for xpos = xabs + border_size_x * s, xabs + self.border_pos.corner_right * s - 1, border_size_x * s do + sprites:draw(canvas, 11, xpos, yabs + self.border_pos.upper * s, { scaleFactor = s }) -- upper edge + sprites:draw(canvas, 16, xpos, yabs + self.border_pos.lower * s, { scaleFactor = s }) -- lower edge end - for ypos = yabs + border_size_y, yabs + self.border_pos.corner_lower - 1, border_size_y do - draw(sprites, canvas, 13, xabs + self.border_pos.left, ypos) -- left edge - draw(sprites, canvas, 14, xabs + self.border_pos.right, ypos) -- right edge + for ypos = yabs + border_size_y * s, yabs + self.border_pos.corner_lower * s - 1, border_size_y * s do + sprites:draw(canvas, 13, xabs + self.border_pos.left * s, ypos, { scaleFactor = s }) -- left edge + sprites:draw(canvas, 14, xabs + self.border_pos.right * s, ypos, { scaleFactor = s }) -- right edge end - canvas:nonOverlapping(false) + sprites:draw(canvas, 10, xabs + self.border_pos.left * s, yabs + self.border_pos.upper * s, { scaleFactor = s }) -- upper left corner + sprites:draw(canvas, 12, xabs + self.border_pos.corner_right * s, yabs + self.border_pos.upper * s, { scaleFactor = s }) -- upper right corner + sprites:draw(canvas, 15, xabs + self.border_pos.left * s, yabs + self.border_pos.corner_lower * s, { scaleFactor = s }) -- lower left corner + sprites:draw(canvas, 17, xabs + self.border_pos.corner_right * s, yabs + self.border_pos.corner_lower * s, { scaleFactor = s }) -- lower right corner end -- Draw window components Window.draw(self, canvas, x, y) @@ -124,17 +118,18 @@ function UIResizable:onMouseDown(button, x, y) end function UIResizable:hitTest(x, y) - if x >= 0 and y >= 0 and x < self.width and y < self.height then -- inside window + local s = TheApp.config.ui_scale + if x >= 0 and y >= 0 and x < self.width * s and y < self.height * s then -- inside window return Window.hitTest(self, x, y) end local sprites = self.border_sprites if not sprites then return false end - if x < -9 or y < -9 or x >= self.width + 9 or y >= self.height + 9 then -- outside border bounds + if x < -9 * s or y < -9 * s or x >= self.width * s + 9 * s or y >= self.height * s + 9 * s then -- outside border bounds return false end - if (0 <= x and x < self.width) or (0 <= y and y < self.height) then -- edges (upper/lower/left/right) + if (0 <= x and x < self.width * s) or (0 <= y and y < self.height * s) then -- edges (upper/lower/left/right) return true end return self:hitTestCorners(x, y) and true @@ -146,8 +141,9 @@ end --!return (boolean or string) false if not hit, else a string to denote which corner was hit (can be "ul", "ur", "ll" or "lr") function UIResizable:hitTestCorners(x, y) if self.border_sprites then - local yzone = (-9 <= y and y < 0) and "u" or (self.height <= y and y < self.height + 9) and "l" - local xzone = (-9 <= x and x < 0) and "l" or (self.width <= x and x < self.width + 9) and "r" + local s = TheApp.config.ui_scale + local yzone = (-9 * s <= y and y < 0) and "u" or (self.height * s <= y and y < self.height * s + 9 * s) and "l" + local xzone = (-9 * s <= x and x < 0) and "l" or (self.width * s <= x and x < self.width * s + 9 * s) and "r" local sprite_ids = {ul = 10, ur = 12, ll = 15, lr = 17} if yzone and xzone then diff --git a/CorsixTH/Lua/dialogs/resizables/main_menu.lua b/CorsixTH/Lua/dialogs/resizables/main_menu.lua index d0e5b0545..624fd94f7 100644 --- a/CorsixTH/Lua/dialogs/resizables/main_menu.lua +++ b/CorsixTH/Lua/dialogs/resizables/main_menu.lua @@ -119,7 +119,7 @@ function UIMainMenu:draw(canvas, x, y) local s = TheApp.config.ui_scale -- The following strings are drawn in reverse order - local ly = y + self.height - (15 * s) + local ly = y + self.height * s - (15 * s) -- Move the version string up a bit if showing check for updates disabled warning. if TheApp:isUpdateCheckDisabledByConfig() then self.label_font:draw(canvas, _S.main_menu.updates_off, x + 5 * s, ly, 190, 0, "right") diff --git a/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua b/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua index 88748ac74..52899e084 100644 --- a/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua +++ b/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua @@ -76,7 +76,7 @@ function UITipOfTheDay:draw(canvas, x, y) local s = TheApp.config.ui_scale local text = _S.totd_window.tips[self.tip_num] - self.white_font:drawWrapped(canvas, text, x + 10 * s, y + 10 * s, self.width - 20 * s) + self.white_font:drawWrapped(canvas, text, x + 10 * s, y + 10 * s, self.width * s - 20 * s) end function UITipOfTheDay:buttonPrev() diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 25010431d..79b05232f 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -57,6 +57,7 @@ function Window:Window() self.panel_sprites = false self.visible = true self.draggable = true + self.apply_ui_scale = true end -- Most windows don't pause the game. Specific windows: fax, annual report, staff rise, and errors do pause and are set in the specific files @@ -64,10 +65,10 @@ function Window:mustPause() return false end -function Window:setSize(width, height, scale) - scale = scale or TheApp.config.ui_scale - self.width = width * scale - self.height = height * scale +function Window:setSize(width, height, apply_ui_scale) + self.apply_ui_scale = apply_ui_scale + self.width = width + self.height = height end -- Sets the window's onscreen position. Each of x and y can be: @@ -82,15 +83,17 @@ function Window:setPosition(x, y) self.y_original = y -- Convert x and y to absolute pixel positions with regard to top/left local w, h = TheApp.config.width, TheApp.config.height + local sw = self.width * (self.apply_ui_scale and TheApp.config.ui_scale or 1) + local sh = self.height * (self.apply_ui_scale and TheApp.config.ui_scale or 1) if x < 0 then - x = math.ceil(w - self.width + x) + x = math.ceil(w - sw + x) elseif x < 1 then - x = math.floor((w - self.width) * x + 0.5) + x = math.floor((w - sw) * x + 0.5) end if y < 0 then - y = math.ceil(h - self.height + y) + y = math.ceil(h - sh + y) elseif y < 1 then - y = math.floor((h - self.height) * y + 0.5) + y = math.floor((h - sh) * y + 0.5) end self.x = x self.y = y @@ -303,6 +306,7 @@ function Panel:drawLabel(canvas, x, y, limit) local multi_line = type(text) == "table" local wrapped = not self.auto_clip local center_y = false + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 if not multi_line then text = {text} @@ -310,25 +314,25 @@ function Panel:drawLabel(canvas, x, y, limit) center_y = true end - local next_y = y + self.y + 1 - local last_x = x + self.x + 2 + local next_y = y + self.y * s + s + local last_x = x + self.x * s + 2 * s for i, line in ipairs(text) do local old_y = next_y if limit and limit[1] == i then line = line:sub(1, limit[2]) end if self.auto_clip then - line = self:clipLine(line, self.w - 4) + line = self:clipLine(line, self.w * s - 4 * s) end if wrapped then - next_y, last_x = self.label_font:drawWrapped(canvas, line, x + self.x + 2, old_y, self.w - 4, self.align) + next_y, last_x = self.label_font:drawWrapped(canvas, line, x + self.x * s + 2 * s, old_y, self.w * s - 4 * s, self.align) else - next_y, last_x = self.label_font:draw(canvas, line, x + self.x + 2, old_y, (self.w - 4), (center_y and self.h or 0), self.align) + next_y, last_x = self.label_font:draw(canvas, line, x + self.x * s + 2 * s, old_y, (self.w * s - 4 * s), (center_y and self.h * s or 0), self.align) end if not line:find("%S") then -- Special handling for empty lines or lines with only space - next_y = self.label_font:draw(nil, "A", (x + self.x + 2), old_y, (self.w - 4), (center_y and self.h or 0), self.align) + next_y = self.label_font:draw(nil, "A", (x + self.x * s + 2 * s), old_y, (self.w * s - 4 * s), (center_y and self.h * s or 0), self.align) end if limit and limit[1] == i then break @@ -349,10 +353,9 @@ end --!param width (int) New width of the panel. --!param height (int) New height of the panel. --!param scale (int|nil) Scale factor to apply to width and height. Default is TheApp.config.ui_scale. -function Panel:setSize(width, height, scale) - scale = scale or TheApp.config.ui_scale - self.w = width * scale - self.h = height * scale +function Panel:setSize(width, height) + self.w = width + self.h = height end --! Set the visibility of the panel. @@ -398,7 +401,8 @@ function Window:removeAllPanels() end local --[[persistable: window_panel_colour_draw]] function panel_colour_draw(panel, canvas, x, y) - canvas:drawRect(panel.colour, x + panel.x, y + panel.y, panel.w, panel.h) + local s = panel.apply_ui_scale and TheApp.config.ui_scale or 1 + canvas:drawRect(panel.colour, x + panel.x * s, y + panel.y * s, panel.w * s, panel.h * s) if panel.label then panel:drawLabel(canvas, x, y) end @@ -414,10 +418,13 @@ colour rather than a bitmap. !param r (integer) Value in [0, 255] giving the red component of the colour. !param g (integer) Value in [0, 255] giving the green component of the colour. !param b (integer) Value in [0, 255] giving the blue component of the colour. +!param apply_ui_scale (boolean) Whether to apply UI scale to position and size. + Default is to match window. ]] -function Window:addColourPanel(x, y, w, h, r, g, b) +function Window:addColourPanel(x, y, w, h, r, g, b, apply_ui_scale) local panel = setmetatable({ window = self, + apply_ui_scale = apply_ui_scale ~= nil and apply_ui_scale or self.apply_ui_scale, x = x, y = y, w = w, @@ -431,14 +438,16 @@ function Window:addColourPanel(x, y, w, h, r, g, b) end local --[[persistable: window_panel_bevel_draw]] function panel_bevel_draw(panel, canvas, x, y) + local s = panel.apply_ui_scale and TheApp.config.ui_scale or 1 + if panel.lowered then - canvas:drawRect(panel.highlight_colour, x + panel.x, y + panel.y, panel.w, panel.h) - canvas:drawRect(panel.shadow_colour, x + panel.x, y + panel.y, panel.w - 1, panel.h - 1) - canvas:drawRect(panel.lowered_colour, x + panel.x + 1, y + panel.y + 1, panel.w - 2, panel.h - 2) + canvas:drawRect(panel.highlight_colour, x + panel.x * s, y + panel.y * s, panel.w * s, panel.h * s) + canvas:drawRect(panel.shadow_colour, x + panel.x * s, y + panel.y * s, panel.w * s - s, panel.h * s - s) + canvas:drawRect(panel.lowered_colour, x + panel.x * s + s, y + panel.y * s + s, panel.w * s - 2 * s, panel.h * s - 2 * s) else - canvas:drawRect(panel.shadow_colour, x + panel.x + 1, y + panel.y + 1, panel.w - 1, panel.h - 1) - canvas:drawRect(panel.highlight_colour, x + panel.x, y + panel.y, panel.w - 1, panel.h - 1) - canvas:drawRect(panel.colour, x + panel.x + 1, y + panel.y + 1, panel.w - 2, panel.h - 2) + canvas:drawRect(panel.shadow_colour, x + panel.x * s + s, y + panel.y * s + s, panel.w * s - s, panel.h * s - s) + canvas:drawRect(panel.highlight_colour, x + panel.x * s, y + panel.y * s, panel.w * s - s, panel.h * s - s) + canvas:drawRect(panel.colour, x + panel.x * s + s, y + panel.y * s + s, panel.w * s - 2 * s, panel.h * s - 2 * s) end if panel.label then panel:drawLabel(canvas, x, y) @@ -458,7 +467,7 @@ features a highlight and a shadow that makes it appear either lowered or raised. !param disabled_colour (colour in form .red, .green and .blue or nil) [optional] The colour for the disabled panel. !param lowered_colour (colour in form .red, .green and .blue or nil) [optional] The colour for the lowered (toggled) panel. ]] -function Window:addBevelPanel(x, y, w, h, colour, highlight_colour, shadow_colour, disabled_colour, lowered_colour) +function Window:addBevelPanel(x, y, w, h, colour, highlight_colour, shadow_colour, disabled_colour, lowered_colour, apply_ui_scale) highlight_colour = highlight_colour or { red = sanitize(colour.red + 40), green = sanitize(colour.green + 40), @@ -478,10 +487,11 @@ function Window:addBevelPanel(x, y, w, h, colour, highlight_colour, shadow_colou local panel = setmetatable({ window = self, - x = x * TheApp.config.ui_scale, - y = y * TheApp.config.ui_scale, - w = w * TheApp.config.ui_scale, - h = h * TheApp.config.ui_scale, + apply_ui_scale = apply_ui_scale ~= nil and apply_ui_scale or self.apply_ui_scale, + x = x, + y = y, + w = w, + h = h, colour = TheApp.video:mapRGB(colour.red, colour.green, colour.blue), highlight_colour = TheApp.video:mapRGB(highlight_colour.red, highlight_colour.green, highlight_colour.blue), shadow_colour = TheApp.video:mapRGB(shadow_colour.red, shadow_colour.green, shadow_colour.blue), @@ -774,10 +784,8 @@ nil or not given, then the window is passed as the first argument. right-clicks the button. ]] function Window:makeButtonOnPanel(panel, x, y, w, h, sprite, on_click, on_click_self, on_rightclick) - x = x * TheApp.config.ui_scale + panel.x - y = y * TheApp.config.ui_scale + panel.y - w = w * TheApp.config.ui_scale - h = h * TheApp.config.ui_scale + x = x + panel.x + y = y + panel.y local button = setmetatable({ ui = self.ui, is_toggle = false, @@ -1535,10 +1543,11 @@ function Window:onCursorWorldPositionChange(x, y) end function Window:hitTestPanel(x, y, panel) - local xpos, ypos = x - panel.x, y - panel.y + local s = panel.apply_ui_scale and TheApp.config.ui_scale or 1 + local xpos, ypos = x - panel.x * s, y - panel.y * s if panel.visible and xpos >= 0 and ypos >= 0 then if panel.w and panel.h then - if xpos <= panel.w and ypos <= panel.h then + if xpos <= panel.w * s and ypos <= panel.h * s then return true end else @@ -1607,7 +1616,10 @@ function Window:onMouseDown(button, x, y) end if not repaint and (button == "left" or button == "right") then for _, btn in ipairs(self.buttons) do - if btn.enabled and btn.x <= x and x < btn.r and btn.y <= y and y < btn.b and (button == "left" or btn.on_rightclick ~= nil) then + local bs = btn.panel_for_sprite.apply_ui_scale and TheApp.config.ui_scale or 1 + if btn.enabled and + btn.x * bs <= x and x < btn.r * bs and btn.y * bs <= y and y < btn.b * bs and + (button == "left" or btn.on_rightclick ~= nil) then btn.panel_for_sprite.sprite_index = btn.sprite_index_active self.active_button = btn btn.active = true @@ -1688,12 +1700,13 @@ function Window:onMouseUp(button, x, y) if button == "left" or button == "right" then local btn = self.active_button if btn then + local bs = btn.panel_for_sprite.apply_ui_scale and TheApp.config.ui_scale or 1 btn.panel_for_sprite.sprite_index = btn.sprite_index_normal btn.active = false btn.panel_for_sprite.lowered = btn.panel_lowered_normal self.active_button = false self.btn_repeat_delay = nil - if btn.enabled and not btn.is_repeat and btn.x <= x and x < btn.r and btn.y <= y and y < btn.b then + if btn.enabled and not btn.is_repeat and btn.x * bs <= x and x < btn.r * bs and btn.y * bs <= y and y < btn.b * bs then btn:handleClick(button) end repaint = true @@ -1813,7 +1826,8 @@ function Window:onMouseMove(x, y, dx, dy) if self.active_button then local btn = self.active_button local index = btn.sprite_index_blink or btn.sprite_index_normal - if btn.x <= x and x < btn.r and btn.y <= y and y < btn.b then + local bs = btn.panel_for_sprite.apply_ui_scale and TheApp.config.ui_scale or 1 + if btn.x * bs <= x and x < btn.r * bs and btn.y * bs <= y and y < btn.b * bs then index = btn.sprite_index_active self.active_button.active = true btn.panel_for_sprite.lowered = btn.panel_lowered_active @@ -1821,7 +1835,8 @@ function Window:onMouseMove(x, y, dx, dy) self.active_button.active = false btn.panel_for_sprite.lowered = btn.panel_lowered_normal for _, button in ipairs(self.buttons) do - if button.enabled and button.x <= x and x < button.r and button.y <= y and y < button.b then + bs = btn.panel_for_sprite.apply_ui_scale and TheApp.config.ui_scale or 1 + if button.enabled and button.x * bs <= x and x < button.r * bs and button.y * bs <= y and y < button.b * bs then button.panel_for_sprite.sprite_index = button.sprite_index_active button.active = true button.panel_for_sprite.lowered = button.panel_lowered_active @@ -2029,7 +2044,8 @@ end --!param x (integer) The X coordinate relative to the top-left corner. --!param y (integer) The Y coordinate relative to the top-left corner. function Window:getTooltipAt(x, y) - if x < 0 or y < 0 or (self.width and x >= self.width) or (self.height and y >= self.height) then + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 + if x < 0 or y < 0 or (self.width and x >= self.width * s) or (self.height and y >= self.height * s) then return end if self.windows then From 272e9ecdb265149c4dd9541849c6c49f506b4c18 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Mon, 8 Dec 2025 22:11:52 -0500 Subject: [PATCH 17/50] Fix dragging --- CorsixTH/Lua/window.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 79b05232f..849131ce4 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -1771,6 +1771,7 @@ function Window:beginDrag(x, y) return false end + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 self.dragging = true self.ui.drag_mouse_move = --[[persistable:window_drag_mouse_move]] function (sx, sy) -- sx and sy are cursor screen co-ords. Convert to window's new abs co-ords @@ -1779,8 +1780,8 @@ function Window:beginDrag(x, y) -- Calculate best positioning local w, h = TheApp.config.width, TheApp.config.height if TheApp.key_modifiers.ctrl then - local px = round(sx / (w - self.width), 0.1) - local py = round(sy / (h - self.height), 0.1) + local px = round(sx / (w - self.width * s), 0.1) + local py = round(sy / (h - self.height * s), 0.1) if px >= 1 then px = -0.1 elseif px < 0 then @@ -1793,8 +1794,8 @@ function Window:beginDrag(x, y) end self:setPosition(px, py) else - local px = getNicestPositionRepresentation(sx, self.width , w) - local py = getNicestPositionRepresentation(sy, self.height, h) + local px = getNicestPositionRepresentation(sx, self.width * s , w) + local py = getNicestPositionRepresentation(sy, self.height * s, h) self:setPosition(px, py) end end From 4e578439124c39c994ae115c90f40510c7c157bc Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Mon, 8 Dec 2025 23:11:25 -0500 Subject: [PATCH 18/50] Window positions also use scale coordinates --- CorsixTH/Lua/dialogs/resizable.lua | 4 +- CorsixTH/Lua/dialogs/resizables/main_menu.lua | 3 +- CorsixTH/Lua/dialogs/resizables/options.lua | 4 -- .../Lua/dialogs/resizables/tip_of_the_day.lua | 4 +- CorsixTH/Lua/window.lua | 47 ++++++++++--------- 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/CorsixTH/Lua/dialogs/resizable.lua b/CorsixTH/Lua/dialogs/resizable.lua index 67feb1997..9a90acd39 100644 --- a/CorsixTH/Lua/dialogs/resizable.lua +++ b/CorsixTH/Lua/dialogs/resizable.lua @@ -87,8 +87,8 @@ function UIResizable:draw(canvas, x, y) local sprites = self.border_sprites if sprites then local s = TheApp.config.ui_scale - local xabs = self.x + x - local yabs = self.y + y + local xabs = self.x * s + x + local yabs = self.y * s + y for xpos = xabs + border_size_x * s, xabs + self.border_pos.corner_right * s - 1, border_size_x * s do sprites:draw(canvas, 11, xpos, yabs + self.border_pos.upper * s, { scaleFactor = s }) -- upper edge diff --git a/CorsixTH/Lua/dialogs/resizables/main_menu.lua b/CorsixTH/Lua/dialogs/resizables/main_menu.lua index 624fd94f7..06087d49a 100644 --- a/CorsixTH/Lua/dialogs/resizables/main_menu.lua +++ b/CorsixTH/Lua/dialogs/resizables/main_menu.lua @@ -114,9 +114,8 @@ end function UIMainMenu:draw(canvas, x, y) UIResizable.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y - local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y -- The following strings are drawn in reverse order local ly = y + self.height * s - (15 * s) diff --git a/CorsixTH/Lua/dialogs/resizables/options.lua b/CorsixTH/Lua/dialogs/resizables/options.lua index 92b727f39..0abecc11a 100644 --- a/CorsixTH/Lua/dialogs/resizables/options.lua +++ b/CorsixTH/Lua/dialogs/resizables/options.lua @@ -347,10 +347,6 @@ function UIOptions:selectUIScale(number) self.scale_ui_panel:setLabel(res.text) self.ui:onChangeResolution() TheApp.gfx:onChangeUIScale() - - -- Reset the options window to apply the new scale - UIResizable.close(self) - self.ui:addWindow(UIOptions(self.ui, self.mode)) end --! Changes check for update setting to on/of diff --git a/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua b/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua index 52899e084..aab96ff27 100644 --- a/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua +++ b/CorsixTH/Lua/dialogs/resizables/tip_of_the_day.lua @@ -72,9 +72,9 @@ function UITipOfTheDay:draw(canvas, x, y) UIResizable.draw(self, canvas, x, y) -- Draw tip - x, y = self.x + x, self.y + y - local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y + local text = _S.totd_window.tips[self.tip_num] self.white_font:drawWrapped(canvas, text, x + 10 * s, y + 10 * s, self.width * s - 20 * s) end diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 849131ce4..ce702664c 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -82,18 +82,17 @@ function Window:setPosition(x, y) self.x_original = x self.y_original = y -- Convert x and y to absolute pixel positions with regard to top/left - local w, h = TheApp.config.width, TheApp.config.height - local sw = self.width * (self.apply_ui_scale and TheApp.config.ui_scale or 1) - local sh = self.height * (self.apply_ui_scale and TheApp.config.ui_scale or 1) + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 + local w, h = TheApp.config.width / s, TheApp.config.height / s if x < 0 then - x = math.ceil(w - sw + x) + x = math.ceil(w - self.width + x) elseif x < 1 then - x = math.floor((w - sw) * x + 0.5) + x = math.floor((w - self.width) * x + 0.5) end if y < 0 then - y = math.ceil(h - sh + y) + y = math.ceil(h - self.height + y) elseif y < 1 then - y = math.floor((h - sh) * y + 0.5) + y = math.floor((h - self.height) * y + 0.5) end self.x = x self.y = y @@ -384,10 +383,10 @@ function Window:addPanel(sprite_index, x, y, w, h, scale) scale = scale or TheApp.config.ui_scale local panel = setmetatable({ window = self, - x = x * scale, - y = y * scale, - w = w and w * scale or w, - h = h and h * scale or h, + x = x, + y = y, + w = w, + h = h, sprite_index = sprite_index, visible = true, }, panel_mt) @@ -1482,7 +1481,8 @@ function Window:makeHotkeyBoxOnPanel(panel, confirm_callback, abort_callback) end function Window:draw(canvas, x, y) - x, y = x + self.x, y + self.y + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 + x, y = x + self.x * s, y + self.y * s if self.panels[1] then local panel_sprites = self.panel_sprites local panel_sprites_draw = panel_sprites and panel_sprites.draw @@ -1595,7 +1595,8 @@ function Window:hitTest(x, y) end if self.windows then for _, child in ipairs(self.windows) do - if child:hitTest(x - child.x, y - child.y) then + local s = child.apply_ui_scale and TheApp.config.ui_scale or 1 + if child:hitTest(x - child.x * s, y - child.y * s) then return true end end @@ -1608,7 +1609,8 @@ function Window:onMouseDown(button, x, y) if not self.visible then return false end if self.windows then for _, window in ipairs(self.windows) do - if window:onMouseDown(button, x - window.x, y - window.y) then + local ws = window.apply_ui_scale and TheApp.config.ui_scale or 1 + if window:onMouseDown(button, x - window.x * ws, y - window.y * ws) then repaint = true break end @@ -1690,7 +1692,8 @@ function Window:onMouseUp(button, x, y) if self.windows then for _, window in ipairs(self.windows) do - if window:onMouseUp(button, x - window.x, y - window.y) then + local s = window.apply_ui_scale and TheApp.config.ui_scale or 1 + if window:onMouseUp(button, x - window.x * s, y - window.y * s) then repaint = true break -- Click has been handled. No need to look any further. end @@ -1736,14 +1739,14 @@ function Window:onMouseWheel(x, y) return repaint end -local --[[persistable:window_drag_position_representation]] function getNicestPositionRepresentation(pos, size, dim_size) +local --[[persistable:window_drag_position_representation]] function getNicestPositionRepresentation(pos, scale, size, dim_size) if size == dim_size then return 0.5 end local left_rel = pos - local right_rel = pos + size - dim_size - local rel = pos / (dim_size - size) + local right_rel = pos + size * scale - dim_size + local rel = pos / (dim_size - size * scale) if 0.15 < rel and rel < 0.85 then return rel end @@ -1754,9 +1757,9 @@ local --[[persistable:window_drag_position_representation]] function getNicestPo return -0.1 end if left_rel <= -right_rel then - return left_rel + return left_rel / scale else - return right_rel + return right_rel / scale end end @@ -1794,8 +1797,8 @@ function Window:beginDrag(x, y) end self:setPosition(px, py) else - local px = getNicestPositionRepresentation(sx, self.width * s , w) - local py = getNicestPositionRepresentation(sy, self.height * s, h) + local px = getNicestPositionRepresentation(sx, s, self.width, w) + local py = getNicestPositionRepresentation(sy, s, self.height, h) self:setPosition(px, py) end end From 078eb5e02581ff2dcd1ddf392788e28501007071 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 9 Dec 2025 21:03:15 -0500 Subject: [PATCH 19/50] Fix confirm dialog --- CorsixTH/Lua/dialogs/confirm_dialog.lua | 2 +- CorsixTH/Lua/window.lua | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CorsixTH/Lua/dialogs/confirm_dialog.lua b/CorsixTH/Lua/dialogs/confirm_dialog.lua index 5504247a8..7d187fc0f 100644 --- a/CorsixTH/Lua/dialogs/confirm_dialog.lua +++ b/CorsixTH/Lua/dialogs/confirm_dialog.lua @@ -122,8 +122,8 @@ end function UIConfirmDialog:draw(canvas, x, y) Window.draw(self, canvas, x, y) - x, y = x + self.x, y + self.y local s = TheApp.config.ui_scale + x, y = x + self.x * s, y + self.y * s self.white_font:drawWrapped(canvas, self.text, x + 17 * s, y + 17 * s, text_width * s) end diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index ce702664c..fcfc67a3c 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -379,10 +379,10 @@ panel (in pixels) is known, it should be specified here to speed up hit-tests. !param h (integer, nil) If the panel is totally opaque, and the height of the panel (in pixels) is known, it should be specified here to speed up hit-tests. ]] -function Window:addPanel(sprite_index, x, y, w, h, scale) - scale = scale or TheApp.config.ui_scale +function Window:addPanel(sprite_index, x, y, w, h, apply_ui_scale) local panel = setmetatable({ window = self, + apply_ui_scale = apply_ui_scale ~= nil and apply_ui_scale or self.apply_ui_scale, x = x, y = y, w = w, @@ -1495,9 +1495,9 @@ function Window:draw(canvas, x, y) panel_sprites, canvas, panel.sprite_index, - x + panel.x, - y + panel.y, - { scaleFactor = TheApp.config.ui_scale }) + x + panel.x * s, + y + panel.y * s, + { scaleFactor = s }) end end end @@ -1551,7 +1551,7 @@ function Window:hitTestPanel(x, y, panel) return true end else - if self.panel_sprites:hitTest(panel.sprite_index, xpos, ypos) then + if self.panel_sprites:hitTest(panel.sprite_index, math.floor(xpos / s), math.floor(ypos / s)) then return true end end From 7abf0fbec1cfe19a152bb713b56ddf87009290a9 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 9 Dec 2025 21:13:19 -0500 Subject: [PATCH 20/50] Fix information dialog --- CorsixTH/Lua/dialogs/information.lua | 47 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/CorsixTH/Lua/dialogs/information.lua b/CorsixTH/Lua/dialogs/information.lua index 08d171443..238621d4f 100644 --- a/CorsixTH/Lua/dialogs/information.lua +++ b/CorsixTH/Lua/dialogs/information.lua @@ -55,15 +55,13 @@ function UIInformation:UIInformation(ui, text, use_built_in_font) self.text = text end - local s = TheApp.config.ui_scale - -- Window size parameters - self.text_width = 300 * s + self.text_width = 300 self.spacing = { - l = 15 * s, - r = 15 * s, - t = 15 * s, - b = 18 * s + 15 * s, -- Size of close button + padding + l = 15, + r = 15, + t = 15, + b = 18 + 15, -- Size of close button + padding } self:onChangeLanguage() @@ -79,9 +77,10 @@ end function UIInformation:onChangeLanguage() local total_req_height = 0 + local s = TheApp.config.ui_scale for _, text in ipairs(self.text) do - local _, req_height = self.black_font:sizeOf(text, self.text_width) - total_req_height = total_req_height + req_height + local _, req_height = self.black_font:sizeOf(text, self.text_width * s) + total_req_height = total_req_height + req_height / s end self.width = self.spacing.l + self.text_width + self.spacing.r @@ -90,25 +89,25 @@ function UIInformation:onChangeLanguage() self:removeAllPanels() - local s = TheApp.config.ui_scale - for x = 4 * s, self.width - 4 * s, 4 do + for x = 4, self.width - 4, 4 do self:addPanel(12, x, 0, 0, 0, 1) -- Dialog top and bottom borders - self:addPanel(16, x, self.height - 4 * s, 0, 0, 1) + self:addPanel(16, x, self.height - 4, 0, 0, 1) end - for y = 4 * s, self.height - 4 * s, 4 do + for y = 4, self.height - 4, 4 do self:addPanel(18, 0, y, 0, 0, 1) -- Dialog left and right borders - self:addPanel(14, self.width - 4 * s, y, 0, 0, 1) + self:addPanel(14, self.width - 4, y, 0, 0, 1) end self:addPanel(11, 0, 0, 0, 0, 1) -- Border top left corner - self:addPanel(17, 0, self.height - 4 * s, 0, 0, 1) -- Border bottom left corner - self:addPanel(13, self.width - 4 * s, 0, 0, 0, 1) -- Border top right corner - self:addPanel(15, self.width - 4 * s, self.height - 4 * s, 0, 0, 1) -- Border bottom right corner + self:addPanel(17, 0, self.height - 4, 0, 0, 1) -- Border bottom left corner + self:addPanel(13, self.width - 4, 0, 0, 0, 1) -- Border top right corner + self:addPanel(15, self.width - 4, self.height - 4, 0, 0, 1) -- Border bottom right corner -- Close button - self:addPanel(19, self.width - 28 * s, self.height - 28 * s, 18 * s, 18 * s, 1):makeButton(0, 0, 18, 18, 20, self.close):setTooltip(_S.tooltip.information.close) + self:addPanel(19, self.width - 28, self.height - 28, 18, 18, 1):makeButton(0, 0, 18, 18, 20, self.close):setTooltip(_S.tooltip.information.close) .panel_for_sprite.custom_draw = --[[persistable:information_close_button]] function(panel, canvas, x, y) - x = x + panel.x - y = y + panel.y + local s = TheApp.config.ui_scale + x = x + panel.x * s + y = y + panel.y * s panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) if self.active_hover then self.panel_sprites:draw(canvas, 20, x, y, { scaleFactor = s }) @@ -117,13 +116,13 @@ function UIInformation:onChangeLanguage() end function UIInformation:draw(canvas, x, y) - local dx, dy = x + self.x, y + self.y local s = TheApp.config.ui_scale + local dx, dy = x + self.x * s, y + self.y * s local background = self.black_background and canvas:mapRGB(0, 0, 0) or canvas:mapRGB(255, 255, 255) - canvas:drawRect(background, dx + 4 * s, dy + 4 * s, self.width - 8 * s, self.height - 8 * s) - local last_y = dy + self.spacing.t + canvas:drawRect(background, dx + 4 * s, dy + 4 * s, self.width * s - 8 * s, self.height * s - 8 * s) + local last_y = dy + self.spacing.t * s for _, text in ipairs(self.text) do - last_y = self.black_font:drawWrapped(canvas, text, dx + self.spacing.l, last_y, self.text_width) + last_y = self.black_font:drawWrapped(canvas, text, dx + self.spacing.l, last_y, self.text_width * s) end Window.draw(self, canvas, x, y) From e84df686ede3f27a0b18545989a5292aeb3e3a55 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 9 Dec 2025 21:29:44 -0500 Subject: [PATCH 21/50] Fix bottom panel --- CorsixTH/Lua/dialogs/bottom_panel.lua | 10 +++++----- CorsixTH/Lua/window.lua | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CorsixTH/Lua/dialogs/bottom_panel.lua b/CorsixTH/Lua/dialogs/bottom_panel.lua index 01263c4c4..8ab50a6bf 100644 --- a/CorsixTH/Lua/dialogs/bottom_panel.lua +++ b/CorsixTH/Lua/dialogs/bottom_panel.lua @@ -74,7 +74,7 @@ function UIBottomPanel:drawPanels() -- If there is a machine menu button, then lets adjust bottom panel width so it can fit if self:machineMenuButtonExists() then - self.width = 676 * TheApp.config.ui_scale + self.width = 676 for x = 377, 660, 10 do self:addPanel(13, x, 0) end @@ -82,7 +82,7 @@ function UIBottomPanel:drawPanels() self.offset = 38 else - self.width = 640 * TheApp.config.ui_scale + self.width = 640 for x = 377, 630, 10 do self:addPanel(13, x, 0) end @@ -103,9 +103,9 @@ function UIBottomPanel:drawPanels() self:addPanel(0, 407, 0):makeToggleButton(2, 6, 36, 36, 0, self.dialogMachineMenu) :setTooltip(_S.tooltip.toolbar.machine_menu) .panel_for_sprite.custom_draw = --[[persistable:machine_menu_buttons]] function(panel, canvas, x, y) - x = x + panel.x - y = y + panel.y local s = TheApp.config.ui_scale + x = x + panel.x * s + y = y + panel.y * s panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) local btn = panel.window.active_button if panels[1].visible then @@ -232,8 +232,8 @@ function UIBottomPanel:draw(canvas, x, y) Window.draw(self, canvas, x, y) -- Draw balance with temporary offset in unicode languages - x, y = x + self.x, y + self.y local s = TheApp.config.ui_scale + x, y = x + self.x * s, y + self.y * s local offset_x, offset_y = 0, 0 if self.ui.app.gfx:drawNumbersFromUnicode() then offset_x = 4 diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index fcfc67a3c..98b0866eb 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -66,7 +66,7 @@ function Window:mustPause() end function Window:setSize(width, height, apply_ui_scale) - self.apply_ui_scale = apply_ui_scale + self.apply_ui_scale = apply_ui_scale ~= nil and apply_ui_scale or self.apply_ui_scale self.width = width self.height = height end From 908288c42ffd48ec66fcbf4605e2cd194991587c Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 9 Dec 2025 21:46:50 -0500 Subject: [PATCH 22/50] scale watch and fix information Improve hit tests / hover --- CorsixTH/Lua/dialogs/information.lua | 16 ++++++++-------- CorsixTH/Lua/dialogs/watch.lua | 21 ++++++++++++--------- CorsixTH/Lua/window.lua | 3 ++- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/CorsixTH/Lua/dialogs/information.lua b/CorsixTH/Lua/dialogs/information.lua index 238621d4f..478360cca 100644 --- a/CorsixTH/Lua/dialogs/information.lua +++ b/CorsixTH/Lua/dialogs/information.lua @@ -133,13 +133,14 @@ function UIInformation:onMouseMove(x, y, dx, dy) self.active_hover = self:hoverTest( self.active_hover, x, y, - self.width - 29 * s, self.width - 10 * s, - self.height - 29 * s, self.height - 10 * s) + self.width * s - 29 * s, self.width * s - 10 * s, + self.height * s - 29 * s, self.height * s - 10 * s) return Window:onMouseMove(x, y, dx, dy) end function UIInformation:hitTest(x, y) - if x >= 0 and y >= 0 and x < self.width and y < self.height then + local s = TheApp.config.ui_scale + if x >= 0 and y >= 0 and x < self.width * s and y < self.height * s then return true else return Window.hitTest(self, x, y) @@ -156,12 +157,11 @@ end function UIInformation:afterLoad(old, new) -- box resized to be more like the original - local s = TheApp.config.ui_scale self.spacing = { - l = 15 * s, - r = 15 * s, - t = 15 * s, - b = 18 * s + 15 * s, + l = 15, + r = 15, + t = 15, + b = 18 + 15, } self:onChangeLanguage() Window.afterLoad(self, old, new) diff --git a/CorsixTH/Lua/dialogs/watch.lua b/CorsixTH/Lua/dialogs/watch.lua index 65eae620d..dd8fafadc 100644 --- a/CorsixTH/Lua/dialogs/watch.lua +++ b/CorsixTH/Lua/dialogs/watch.lua @@ -75,11 +75,12 @@ function UIWatch:UIWatch(ui, count_type) self:addPanel(end_sprite, 4, 0) .custom_draw = --[[persistable:epidemic_timer_button]] function(panel, canvas, x, y) - x = x + panel.x - y = y + panel.y - panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y) + local s = TheApp.config.ui_scale + x = x + panel.x * s + y = y + panel.y * s + panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) if self.active_hover then - self.panel_sprites:draw(canvas, 15, x, y) + self.panel_sprites:draw(canvas, 15, x, y, { scaleFactor = s }) end end @@ -98,11 +99,12 @@ function UIWatch:UIWatch(ui, count_type) else self:addPanel(timer_sprite, 0, 28):setTooltip(tooltips[count_type]) .custom_draw = --[[persistable:open_hospital_timer_button]] function(panel, canvas, x, y) - x = x + panel.x - y = y + panel.y - panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y) + local s = TheApp.config.ui_scale + x = x + panel.x * s + y = y + panel.y * s + panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) if self.active_hover then - self.panel_sprites:draw(canvas, 17, x + 4, y - 28) + self.panel_sprites:draw(canvas, 17, x + 4 * s, y - 28 * s, { scaleFactor = s }) end end end @@ -125,7 +127,8 @@ function UIWatch:onCountdownEnd() end function UIWatch:onMouseMove(x, y, dx, dy) - self.active_hover = self:hoverTest(self.active_hover, x, y, 4, 31, 0, 29) + local s = TheApp.config.ui_scale + self.active_hover = self:hoverTest(self.active_hover, x, y, 4 * s, 31 * s, 0, 29 * s) return Window:onMouseMove(x, y, dx, dy) end diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 98b0866eb..61f07c612 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -1821,7 +1821,8 @@ function Window:onMouseMove(x, y, dx, dy) if self.windows then for _, window in ipairs(self.windows) do - if window:onMouseMove(x - window.x, y - window.y, dx, dy) then + local s = window.apply_ui_scale and TheApp.config.ui_scale or 1 + if window:onMouseMove(x - window.x * s, y - window.y * s, dx, dy) then repaint = true end end From 49a2c34d0a45a74e0e8720a762fa11b15f418462 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 9 Dec 2025 21:55:22 -0500 Subject: [PATCH 23/50] Start of save game compatibility --- CorsixTH/Lua/app.lua | 2 +- CorsixTH/Lua/window.lua | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CorsixTH/Lua/app.lua b/CorsixTH/Lua/app.lua index b3178f94a..347de3d9e 100644 --- a/CorsixTH/Lua/app.lua +++ b/CorsixTH/Lua/app.lua @@ -28,7 +28,7 @@ local SDL = require("sdl") -- and add compatibility code in afterLoad functions -- Recommended: Also replace/Update the summary comment -local SAVEGAME_VERSION = 235 -- Fix staff_member_set applying to all rooms +local SAVEGAME_VERSION = 236 -- UI Scale class "App" diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 61f07c612..ea0d791dc 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -2084,6 +2084,9 @@ function Panel:afterLoad(old, new) if old < 160 then self.lowered_colour = self.colour end + if old < 236 then + self.apply_ui_scale = true + end end --! Stub to be extended in subclasses, if needed. @@ -2109,6 +2112,9 @@ function Window:afterLoad(old, new) -- Hotkey boxes were added. self.hotkeyboxes = {} end + if old < 236 then + self.apply_ui_scale = true + end if self.windows then for _, w in pairs(self.windows) do From c83212a998345cc4e107242dbec425a752b1324b Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Wed, 10 Dec 2025 20:54:22 -0500 Subject: [PATCH 24/50] Draw tooltips correctly for scale --- CorsixTH/Lua/ui.lua | 11 ++++++----- CorsixTH/Lua/window.lua | 29 ++++++++++++++++++++--------- CorsixTH/Src/th_lua_gfx.cpp | 2 +- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CorsixTH/Lua/ui.lua b/CorsixTH/Lua/ui.lua index f24c5d27d..1c5460e18 100644 --- a/CorsixTH/Lua/ui.lua +++ b/CorsixTH/Lua/ui.lua @@ -150,7 +150,7 @@ function UI:UI(app, minimal) self.tooltip_font = app.gfx:loadBuiltinFont() else local palette = app.gfx:loadPalette("QData", "PREF01V.PAL", true) - self.tooltip_font = app.gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette) + self.tooltip_font = app.gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette, { apply_ui_scale = true }) end self.tooltip = nil self.tooltip_counter = 0 @@ -296,7 +296,7 @@ function UI:drawTooltip(canvas) end if self.tooltip_font then - self.tooltip_font:drawTooltip(canvas, self.tooltip.text, x, y) + self.tooltip_font:drawTooltip(canvas, self.tooltip.text, x, y, 200 * TheApp.config.ui_scale) end end @@ -1105,11 +1105,12 @@ function UI:afterLoad(old, new) gfx.cache.palette_greyscale_ghost = {} gfx.cache.language_fonts = {} gfx.builtin_font = nil - - local palette = gfx:loadPalette("QData", "PREF01V.PAL", true) - self.tooltip_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette) end end + if old < 236 then + local palette = gfx:loadPalette("QData", "PREF01V.PAL", true) + self.tooltip_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette, { apply_ui_scale = true }) + end self:setupGlobalKeyHandlers() diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index ea0d791dc..047d67fb4 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -1996,7 +1996,7 @@ end --!param b (integer) The bottom (Y + height) coordinate relative to the top-left corner. --!param tooltip_x (integer) [optional] The X coordinate to display the tooltip at. --!param tooltip_y (integer) [optional] The Y coordinate to display the tooltip at. -function Window:makeTooltip(text, x, y, r, b, tooltip_x, tooltip_y) +function Window:makeTooltip(text, x, y, r, b, tooltip_x, tooltip_y, apply_ui_scale) local region = { text = text, x = x, y = y, r = r, b = b, tooltip_x = tooltip_x or round((x + r) / 2, 1), -- optional @@ -2033,9 +2033,19 @@ function Window:getTooltipForElement(elem, x, y) else text = elem.text end - local xpos, ypos = elem.tooltip_x, elem.tooltip_y - if xpos then xpos = xpos + self.x end -- NB: can be nil, then it means position at mouse cursor - if ypos then ypos = ypos + self.y end + local apply_ui_scale = self.apply_ui_scale + if elem.apply_ui_scale ~= nil then + apply_ui_scale = elem.apply_ui_scale + elseif elem.panel_for_sprite and elem.panel_for_sprite.apply_ui_scale ~= nil then + apply_ui_scale = elem.panel_for_sprite.apply_ui_scale + end + local elem_scale = apply_ui_scale and TheApp.config.ui_scale or 1 + local xpos = elem.tooltip_x and elem.tooltip_x * elem_scale or nil + local ypos = elem.tooltip_y and elem.tooltip_y * elem_scale or nil + + local window_scale = self.apply_ui_scale and TheApp.config.ui_scale or 1 + if xpos then xpos = xpos + self.x * window_scale end -- NB: can be nil, then it means position at mouse cursor + if ypos then ypos = ypos + self.y * window_scale end if text then return { text = text, x = xpos, y = ypos } end @@ -2055,21 +2065,22 @@ function Window:getTooltipAt(x, y) end if self.windows then for _, window in ipairs(self.windows) do - if window:hitTest(x - window.x, y - window.y) then - return window:getTooltipAt(x - window.x, y - window.y) + local ws = window.apply_ui_scale and TheApp.config.ui_scale or 1 + if window:hitTest(x - window.x * ws, y - window.y * ws) then + return window:getTooltipAt(x - window.x * ws, y - window.y * ws) end end end for _, btn in ipairs(self.buttons) do + local bs = btn.panel_for_sprite.apply_ui_scale and TheApp.config.ui_scale or 1 if btn.panel_for_sprite.visible ~= false and btn.tooltip and - btn.x <= x and x < btn.r and btn.y <= y and y < btn.b then + btn.x * bs <= x and x < btn.r * bs and btn.y * bs <= y and y < btn.b * bs then return self:getTooltipForElement(btn.tooltip, x, y) end end - if not self.tooltip_regions then self.tooltip_regions = {} end -- TEMPORARY for compatibility of pre-r649 savegames. Remove when compatibility is broken anyway. for _, region in ipairs(self.tooltip_regions) do - if region.enabled ~= false and region.x <= x and x < region.r and region.y <= y and y < region.b then + if region.enabled ~= false and region.x * s <= x and x < region.r * s and region.y * s <= y and y < region.b * s then return self:getTooltipForElement(region, x, y) end end diff --git a/CorsixTH/Src/th_lua_gfx.cpp b/CorsixTH/Src/th_lua_gfx.cpp index 38ac9fa82..23ece2de9 100644 --- a/CorsixTH/Src/th_lua_gfx.cpp +++ b/CorsixTH/Src/th_lua_gfx.cpp @@ -594,9 +594,9 @@ int l_font_draw_tooltip(lua_State* L) { const char* sMsg = luaT_checkstring(L, 3, &iMsgLen); int iX = static_cast(luaL_checkinteger(L, 4)); int iY = static_cast(luaL_checkinteger(L, 5)); + int iW = static_cast(luaL_optinteger(L, 6, 200)); int iScreenWidth = pCanvas->get_width(); - int iW = 200; // (for now) hardcoded width of tooltips uint32_t iBlack = render_target::map_colour(0x00, 0x00, 0x00); uint32_t iWhite = render_target::map_colour(0xFF, 0xFF, 0xFF); text_layout oArea = pFont->draw_text_wrapped(nullptr, sMsg, iMsgLen, iX + 2, From 3a9cf23eba078352ecb56333540ef33b8f1468d0 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Wed, 10 Dec 2025 21:14:28 -0500 Subject: [PATCH 25/50] Add scaling for file/directory dialogs --- .../dialogs/resizables/directory_browser.lua | 11 +++--- .../Lua/dialogs/resizables/file_browser.lua | 3 +- CorsixTH/Lua/dialogs/tree_ctrl.lua | 35 ++++++++++--------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/CorsixTH/Lua/dialogs/resizables/directory_browser.lua b/CorsixTH/Lua/dialogs/resizables/directory_browser.lua index 2b8562f50..e74edc28b 100644 --- a/CorsixTH/Lua/dialogs/resizables/directory_browser.lua +++ b/CorsixTH/Lua/dialogs/resizables/directory_browser.lua @@ -152,7 +152,7 @@ function UIDirectoryBrowser:UIDirectoryBrowser(ui, mode, instruction, treenode_c self.exit_button = self:addBevelPanel(260, 400, 100, 18, self.col_bg) if mode ~= nil then - self.font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V") + self.font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) self:setDefaultPosition(0.5, 0.25) self.on_top = true self.esc_closes = true @@ -212,11 +212,12 @@ end function UIDirectoryBrowser:draw(canvas, x, y) UIResizable.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y if not self.mode then - self.font:drawWrapped(canvas, _S.install.title, x + 5, y + 5, self.width - 10, "center") - self.font:drawWrapped(canvas, self.instruction, x + 5, y + 15, self.width - 10) + self.font:drawWrapped(canvas, _S.install.title, x + 5 * s, y + 5 * s, self.width * s - 10 * s, "center") + self.font:drawWrapped(canvas, self.instruction, x + 5 * s, y + 15 * s, self.width * s - 10 * s) else - self.font:drawWrapped(canvas, self.instruction, x + 5, y + 15, self.width - 10) + self.font:drawWrapped(canvas, self.instruction, x + 5 * s, y + 15 * s, self.width * s - 10 * s) end end diff --git a/CorsixTH/Lua/dialogs/resizables/file_browser.lua b/CorsixTH/Lua/dialogs/resizables/file_browser.lua index 15ff4c252..9e0ba7bb1 100644 --- a/CorsixTH/Lua/dialogs/resizables/file_browser.lua +++ b/CorsixTH/Lua/dialogs/resizables/file_browser.lua @@ -136,7 +136,8 @@ function FilteredTreeControl:drawExtraOnRow(canvas, node, x, y) if not node:hasChildren() and self.show_dates then local last_mod = node:getLastModification() local daytime = _S.date_format.daymonth:format(os.date("%d", last_mod), tonumber(os.date("%m", last_mod))) - self.font:draw(canvas, daytime .. " " .. os.date("%Y %X", last_mod), x + self.tree_rect.w - 140, y) + local s = TheApp.config.ui_scale + self.font:draw(canvas, daytime .. " " .. os.date("%Y %X", last_mod), x + self.tree_rect.w * s - 140 * s, y) end end diff --git a/CorsixTH/Lua/dialogs/tree_ctrl.lua b/CorsixTH/Lua/dialogs/tree_ctrl.lua index 07504679f..ca37a1507 100644 --- a/CorsixTH/Lua/dialogs/tree_ctrl.lua +++ b/CorsixTH/Lua/dialogs/tree_ctrl.lua @@ -512,7 +512,7 @@ function TreeControl:TreeControl(root, x, y, width, height, col_bg, col_fg, y_of if not has_font then self.font = gfx:loadBuiltinFont() else - self.font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V") + self.font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) end self.tree_sprites = gfx:loadSpriteTable("Bitmap", "tree_ctrl", true, gfx:loadPalette("Bitmap", "tree_ctrl.pal")) @@ -548,11 +548,12 @@ end function TreeControl:hitTestTree(x, y) + local s = TheApp.config.ui_scale local rect = self.tree_rect - x = x - rect.x - y = y - rect.y - self.y_offset - if 0 <= x and 0 <= y and x < rect.w and y < rect.h then - local n = math.floor(y / self.row_height) + x = x - rect.x * s + y = y - rect.y * s - self.y_offset * s + if 0 <= x and 0 <= y and x < rect.w * s and y < rect.h * s then + local n = math.floor(y / (self.row_height * s)) local node = self.first_visible_node while n ~= 0 and node do node = node:getNextVisible() @@ -561,8 +562,8 @@ function TreeControl:hitTestTree(x, y) if n == 0 then if node then local level = node:getLevel() - if x > level * 14 then - if x < (level+1) * 14 then + if x > level * 14 * s then + if x < (level+1) * 14 * s then return node, true else return node, false @@ -589,9 +590,10 @@ end function TreeControl:onMouseDown(button, x, y) local redraw = Window.onMouseDown(self, button, x, y) if button ~= 4 and button ~= 5 then + local s = TheApp.config.ui_scale -- NB: 4 and 5 are scrollwheel self.mouse_down_in_self = false - if 0 <= x and 0 <= y and x < self.width and y < self.height then + if 0 <= x and 0 <= y and x < self.width * s and y < self.height * s then self.mouse_down_in_self = true redraw = true end @@ -678,26 +680,27 @@ end function TreeControl:draw(canvas, x, y) Window.draw(self, canvas, x, y) - x = x + self.x + self.tree_rect.x - y = y + self.y + self.tree_rect.y + self.y_offset + local s = TheApp.config.ui_scale + x = x + self.x * s + self.tree_rect.x * s + y = y + self.y * s + self.tree_rect.y * s + self.y_offset * s local node = self.first_visible_node local num_nodes_drawn = 0 while node and num_nodes_drawn < self.num_rows do local level = node:getLevel() for i = 0, level - 1 do - self.tree_sprites:draw(canvas, 1, x + i * 14, y) + self.tree_sprites:draw(canvas, 1, x + i * 14 * s, y, { scaleFactor = s }) end if node == self.highlighted_node then local offset = (level + 1) * 14 local colour = node:getHighlightColour(canvas) or self.scrollbar.slider.colour - canvas:drawRect(colour, x + offset - 1, y, self.tree_rect.w - offset - 1, self.row_height) + canvas:drawRect(colour, x + offset * s - s, y, self.tree_rect.w * s - offset * s - s, self.row_height * s) end if node == self.selected_node then local offset = (level + 1) * 14 local colour = node:getSelectColour(canvas) or self.scrollbar.slider.colour - canvas:drawRect(colour, x + offset - 1, y, self.tree_rect.w - offset - 1, self.row_height) + canvas:drawRect(colour, x + offset * s - s, y, self.tree_rect.w * s - offset * s - s, self.row_height * s) end local icon @@ -708,10 +711,10 @@ function TreeControl:draw(canvas, x, y) else icon = 3 end - self.tree_sprites:draw(canvas, icon, x + level * 14, y) - self.font:draw(canvas, node:getLabel(), x + (level + 1) * 14, y + 2) + self.tree_sprites:draw(canvas, icon, x + level * 14 * s, y, { scaleFactor = s }) + self.font:draw(canvas, node:getLabel(), x + (level + 1) * 14 * s, y + 2 * s) self:drawExtraOnRow(canvas, node, x, y) - y = y + self.row_height + y = y + self.row_height * s num_nodes_drawn = num_nodes_drawn + 1 node = node:getNextVisible() end From cf933c42cee05ab95632a71ce41931f262c784a3 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Wed, 10 Dec 2025 21:23:01 -0500 Subject: [PATCH 26/50] tooltip load fix --- CorsixTH/Lua/ui.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/CorsixTH/Lua/ui.lua b/CorsixTH/Lua/ui.lua index 1c5460e18..671779bd5 100644 --- a/CorsixTH/Lua/ui.lua +++ b/CorsixTH/Lua/ui.lua @@ -1108,6 +1108,7 @@ function UI:afterLoad(old, new) end end if old < 236 then + local gfx = self.app.gfx local palette = gfx:loadPalette("QData", "PREF01V.PAL", true) self.tooltip_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette, { apply_ui_scale = true }) end From 7ede97b728715c216459cff290d92cfac16d1aa8 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Wed, 10 Dec 2025 22:30:35 -0500 Subject: [PATCH 27/50] Patient dialog Include fix for line width --- CorsixTH/Lua/dialogs/patient.lua | 36 +++++++++++++++++++------------- CorsixTH/Src/th_gfx_sdl.cpp | 12 +++++++---- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/CorsixTH/Lua/dialogs/patient.lua b/CorsixTH/Lua/dialogs/patient.lua index 1f76ba80f..ea92989c4 100644 --- a/CorsixTH/Lua/dialogs/patient.lua +++ b/CorsixTH/Lua/dialogs/patient.lua @@ -22,7 +22,8 @@ local TH = require("TH") -- Test for hit within the view circle local --[[persistable:patient_window_is_in_view_circle]] function is_in_view_circle(x, y) - return (x - 55)^2 + (y - 254)^2 < 39^2 + local s = TheApp.config.ui_scale + return (x - 55 * s)^2 + (y - 254 * s)^2 < (39 * s)^2 end --! Individual patient information dialog @@ -42,7 +43,7 @@ function UIPatient:UIPatient(ui, patient) self.height = 310 self:setDefaultPosition(-20, 30) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req02V", true) - self.font = app.gfx:loadFontAndSpriteTable("QData", "Font74V") -- Font used in the treatment history + self.font = app.gfx:loadFontAndSpriteTable("QData", "Font74V", nil, nil, { apply_ui_scale = true }) -- Font used in the treatment history self.patient = patient self.visible_diamond = ui:makeVisibleDiamond(75, 76) @@ -118,20 +119,22 @@ end --! Draw a bar in the patient dialogue window. --!param canvas Canvas to draw at. ---!param xbase Horizontal base position. +--!param sprite The sprite to draw --!param ybase Vertical base position. --!param xpos Horizontal offset. --!param ypos Vertical offset. --!param value Fraction to draw. -function UIPatient:drawBar(canvas, xbase, ybase, xpos, ypos, value) +function UIPatient:drawBar(canvas, sprite, ybase, xpos, ypos, value) + local s = TheApp.config.ui_scale local width = math.floor(value * 40 + 0.5) for dx = 0, width - 1 do - self.panel_sprites:draw(canvas, xbase, xpos + 58 + dx, ypos + ybase) + self.panel_sprites:draw(canvas, sprite, xpos + 58 * s + dx * s, ypos + ybase * s, { scaleFactor = s }) end end function UIPatient:draw(canvas, x_, y_) - local x, y = self.x + x_, self.y + y_ + local s = TheApp.config.ui_scale + local x, y = self.x * s + x_, self.y * s + y_ local map = self.ui.app.map local patient = self.patient -- If the patient has just despawned, then it will have no tile, hence @@ -154,7 +157,9 @@ function UIPatient:draw(canvas, x_, y_) -- within the map (this situation doesn't occur very often, but we need to -- handle it properly when it does occur). px, py = self.ui.limitPointToDiamond(px, py, self.visible_diamond, true) - self.ui.app.map:draw(canvas, px, py, 75, 76, x + 17, y + 216) + canvas:scale(s) + self.ui.app.map:draw(canvas, px, py, 75, 76, math.floor(x / s) + 17, math.floor(y / s) + 216) + canvas:scale(1) Window.draw(self, canvas, x_, y_) -- The patient bars (happiness, thirst, and warmth). @@ -165,7 +170,7 @@ function UIPatient:draw(canvas, x_, y_) self:drawBar(canvas, 349, 183, x, y, warmth) if self.history_panel.visible then - self:drawTreatmentHistory(canvas, x + 40, y + 25) + self:drawTreatmentHistory(canvas, x + 40 * s, y + 25 * s) elseif patient.health_history then self:drawHealthHistory(canvas, x, y) end @@ -177,8 +182,9 @@ end --!param x (int) X position of the top of the list. --!param y (int) Y position of the top of the list. function UIPatient:drawTreatmentHistory(canvas, x, y) + local s = TheApp.config.ui_scale for _, room in ipairs(self.patient.treatment_history) do - y = self.font:drawWrapped(canvas, room:upper(), x, y, 95) + y = self.font:drawWrapped(canvas, room:upper(), x, y, 95 * s) end end @@ -187,11 +193,13 @@ end --!param x (int) X position of the top-left of the graph. --!param y (int) Y position of the top-left of the graph. function UIPatient:drawHealthHistory(canvas, x, y) + local s = TheApp.config.ui_scale + -- Sizes and positions of the graph in the window. - local hor_length = 76 - local vert_length = 70 - local startx = 58 - local starty = 27 + local hor_length = 76 * s + local vert_length = 70 * s + local startx = 58 * s + local starty = 27 * s -- Health history information. local hh = self.patient.health_history @@ -207,7 +215,7 @@ function UIPatient:drawHealthHistory(canvas, x, y) if not line then line = TH.line() - line:setWidth(2) + line:setWidth(2 * s) line:setColour(200, 55, 30, 255) line:moveTo(startx, posy) else diff --git a/CorsixTH/Src/th_gfx_sdl.cpp b/CorsixTH/Src/th_gfx_sdl.cpp index 145990fec..c28035dd2 100644 --- a/CorsixTH/Src/th_gfx_sdl.cpp +++ b/CorsixTH/Src/th_gfx_sdl.cpp @@ -946,10 +946,14 @@ void render_target::draw_line(line_sequence* pLine, int iX, int iY) { double lastY = pLine->line_elements[0].y; for (const line_sequence::line_element& op : pLine->line_elements) { if (op.type == line_sequence::line_command::line) { - SDL_RenderDrawLine(renderer, static_cast((lastX + iX) * scale), - static_cast((lastY + iY) * scale), - static_cast((op.x + iX) * scale), - static_cast((op.y + iY) * scale)); + // Not the true width, but good enough for graphs + for (int i = 0; i < pLine->width; ++i) { + int adjI = static_cast(i - pLine->width / 2); + SDL_RenderDrawLine(renderer, static_cast((lastX + iX) * scale), + static_cast((lastY + iY + adjI) * scale), + static_cast((op.x + iX) * scale), + static_cast((op.y + iY + adjI) * scale)); + } } lastX = op.x; From c4d496830e1a5348eff35ad10d68f576cefb546a Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Thu, 11 Dec 2025 19:03:49 -0500 Subject: [PATCH 28/50] Scale top menu --- CorsixTH/Lua/dialogs/menu.lua | 101 +++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/CorsixTH/Lua/dialogs/menu.lua b/CorsixTH/Lua/dialogs/menu.lua index 8689ba873..ed61cf076 100644 --- a/CorsixTH/Lua/dialogs/menu.lua +++ b/CorsixTH/Lua/dialogs/menu.lua @@ -41,8 +41,8 @@ function UIMenuBar:UIMenuBar(ui, map_editor) self.visible = false local selected_label_color = { red = 40, green = 40, blue = 250 } self.panel_sprites = app.gfx:loadSpriteTable("Data", "PullDV", true) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") - self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color}) + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color, apply_ui_scale = true }) -- The list of top-level menus, from left to right self.menus = {} -- The menu which the cursor was most recently over @@ -138,13 +138,13 @@ function UIMenuBar:addMenu(title, menu) title = title, menu = menu, x = 0, - y = 0, + y = 1, height = 16, } if self.menus[1] then menu_item.x = self.menus[#self.menus].x + self.menus[#self.menus].width end - menu_item.width = self.white_font:sizeOf(title) + 32 + menu_item.width = self.white_font:sizeOf(title) / TheApp.config.ui_scale + 32 self.menus[#self.menus + 1] = menu_item end @@ -152,29 +152,34 @@ function UIMenuBar:draw(canvas) if not self.visible then return end + local s = TheApp.config.ui_scale local panel_sprites = self.panel_sprites local panel_sprites_draw = panel_sprites.draw canvas:nonOverlapping() - panel_sprites_draw(panel_sprites, canvas, 1, 0, 0) - panel_sprites_draw(panel_sprites, canvas, 4, 0, 6) - panel_sprites_draw(panel_sprites, canvas, 7, 0, 10) - for x = 10, self.width - 10, 10 do - panel_sprites_draw(panel_sprites, canvas, 2, x, 0) + panel_sprites_draw(panel_sprites, canvas, 1, 0, 0, { scaleFactor = s }) + panel_sprites_draw(panel_sprites, canvas, 4, 0, 6 * s, { scaleFactor = s }) + panel_sprites_draw(panel_sprites, canvas, 7, 0, 10 * s, { scaleFactor = s }) + + -- Currently the width is the full pixel width of the window, unlike + -- the height and other panels which are in scale units. + local scaled_width = self.width + for x = 10 * s, scaled_width - 10 * s, 10 * s do + panel_sprites_draw(panel_sprites, canvas, 2, x, 0, { scaleFactor = s }) end - for x = 10, self.width - 10, 10 do - panel_sprites_draw(panel_sprites, canvas, 5, x, 6) + for x = 10 * s, scaled_width - 10 * s, 10 * s do + panel_sprites_draw(panel_sprites, canvas, 5, x, 6 * s, { scaleFactor = s }) end - for x = 10, self.width - 10, 10 do - panel_sprites_draw(panel_sprites, canvas, 8, x, 10) + for x = 10 * s, scaled_width - 10 * s, 10 * s do + panel_sprites_draw(panel_sprites, canvas, 8, x, 10 * s, { scaleFactor = s }) end canvas:nonOverlapping(false) - local x = self.width - 10 - panel_sprites_draw(panel_sprites, canvas, 3, x, 0) - panel_sprites_draw(panel_sprites, canvas, 6, x, 6) - panel_sprites_draw(panel_sprites, canvas, 9, x, 10) + local x = scaled_width - 10 * s + panel_sprites_draw(panel_sprites, canvas, 3, x, 0, { scaleFactor = s }) + panel_sprites_draw(panel_sprites, canvas, 6, x, 6 * s, { scaleFactor = s }) + panel_sprites_draw(panel_sprites, canvas, 9, x, 10 * s, { scaleFactor = s }) for _, menu in ipairs(self.menus) do - self.white_font:draw(canvas, menu.title, menu.x, menu.y, 0, menu.height) + self.white_font:draw(canvas, menu.title, menu.x * s, menu.y * s, 0, menu.height * s) end for _, menu in ipairs(self.open_menus) do self:drawMenu(menu, canvas) @@ -182,28 +187,32 @@ function UIMenuBar:draw(canvas) -- Draw clock if self.ui.app.config.twentyfour_hour_clock then - self.white_font:draw(canvas, os.date("%H:%M"), self.width-45, 2, 0) + self.white_font:draw(canvas, os.date("%H:%M"), scaled_width - 45 * s, s, 0, 16 * s) else - self.white_font:draw(canvas, os.date("%I:%M %p"), self.width-65, 2, 0) + self.white_font:draw(canvas, os.date("%I:%M %p"), scaled_width - 65 * s, s, 0, 16 * s) end end function UIMenuBar:drawMenu(menu, canvas) + local s = TheApp.config.ui_scale local panel_sprites = self.panel_sprites local panel_sprites_draw = panel_sprites.draw - local x, y, w, h = menu.x, menu.y, menu.width, menu.height - canvas:nonOverlapping() - menu.render_list:draw(canvas, x, y) - canvas:nonOverlapping(false) - local btmy = y + h - 6 - panel_sprites_draw(panel_sprites, canvas, 3, x + w - 10, y) - for ypos = y + 6, y + h - 6, 4 do - panel_sprites_draw(panel_sprites, canvas, 6, x + w - 10, ypos) + local x, y, w, h = menu.x * s, menu.y * s, menu.width * s, menu.height * s + + -- It would be better if spriteList supported scaling directly + canvas:scale(s) + menu.render_list:draw(canvas, menu.x, menu.y) + canvas:scale(1) + + local btmy = y + h - 6 * s + panel_sprites_draw(panel_sprites, canvas, 3, x + w - 10 * s, y, { scaleFactor = s }) + for ypos = y + 6 * s, y + h - 6 * s, 4 * s do + panel_sprites_draw(panel_sprites, canvas, 6, x + w - 10 * s, ypos, { scaleFactor = s }) end - panel_sprites_draw(panel_sprites, canvas, 9, x + w - 10, btmy) + panel_sprites_draw(panel_sprites, canvas, 9, x + w - 10 * s, btmy, { scaleFactor = s }) - x = menu.x - y = menu.y + 4 + x = menu.x * s + y = menu.y * s + 4 * s for i, item in ipairs(menu.items) do -- Update the checkbox status if necessary before drawing if item.is_check_item and item.condition then @@ -216,18 +225,19 @@ function UIMenuBar:drawMenu(menu, canvas) end font:draw(canvas, item.title, x, y) if item.submenu then - font:draw(canvas, "+", x + w - 10, y) + font:draw(canvas, "+", x + w - 10 * s, y) elseif item.checked then - panel_sprites_draw(panel_sprites, canvas, 10, x, y) + panel_sprites_draw(panel_sprites, canvas, 10, x, y, { scaleFactor = s }) end - y = y + 14 + y = y + 14 * s end end function UIMenuBar:hitTestBar(x, y) - if y < 16 then + local s = TheApp.config.ui_scale + if y < 16 * s then for _, menu in ipairs(self.menus) do - if menu.x <= x and x < menu.x + menu.width then + if menu.x * s <= x and x < menu.x * s + menu.width * s then local submenu = menu.menu submenu.x = menu.x submenu.y = menu.y + menu.height - 2 @@ -241,8 +251,9 @@ function UIMenuBar:hitTestBar(x, y) end function UIMenuBar:onMouseMove(x, y, dx, dy) + local s = TheApp.config.ui_scale local padding = 6 - local visible = y < self.height + padding + local visible = y < self.height * s + padding * s local newactive = false if not self.active_menu then for i = #self.open_menus, 1, -1 do @@ -379,10 +390,11 @@ function UIMenuBar:onMouseUp(button, x, y) return end local repaint = false + local s = TheApp.config.ui_scale while self.active_menu do local index = self.active_menu:hitTest(x, y, 0) if index == false then - if not self.active_menu.parent and y < 16 then + if not self.active_menu.parent and y < 16 * s then break else self.active_menu = self.active_menu.parent @@ -416,7 +428,7 @@ function UIMenuBar:onMouseUp(button, x, y) if item.handler then item.handler(item, self.active_menu) end - if y > 22 then + if y > 22 * s then self:disappear() end self.active_menu = false @@ -437,7 +449,7 @@ function UIMenuBar:calculateMenuSize(menu) local w = 20 local h = 6 for _, item in ipairs(menu.items) do - local item_w = self.white_font:sizeOf(item.title) + 10 + local item_w = self.white_font:sizeOf(item.title) / TheApp.config.ui_scale + 10 if item_w > w then w = item_w end @@ -490,10 +502,11 @@ function UIMenu:hitTest(x, y, padding) -- number -> hit that item -- true -> hit menu, but not an item -- false -> no hit - if self.x - padding <= x and x < self.x + self.width + padding and - self.y - padding <= y and y < self.y + self.height + padding then - if self.x <= x and x < self.x + self.width then - local index = math_floor((y - self.y + 12) / 14) + local s = TheApp.config.ui_scale + if self.x * s - padding * s <= x and x < self.x * s + self.width * s + padding * s and + self.y * s - padding * s <= y and y < self.y * s + self.height * s + padding * s then + if self.x * s <= x and x < self.x * s + self.width * s then + local index = math_floor((y - self.y * s + 12 * s) / (14 * s)) if 1 <= index and index <= #self.items then return index end From 7752a1d8b27544dd7f7d9588165a45c6ec0bcfcc Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Thu, 11 Dec 2025 21:27:08 -0500 Subject: [PATCH 29/50] Fix jukebox scale --- CorsixTH/Lua/dialogs/jukebox.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CorsixTH/Lua/dialogs/jukebox.lua b/CorsixTH/Lua/dialogs/jukebox.lua index cf33ec8fa..771142579 100644 --- a/CorsixTH/Lua/dialogs/jukebox.lua +++ b/CorsixTH/Lua/dialogs/jukebox.lua @@ -156,10 +156,10 @@ function UIJukebox:draw(canvas, x, y) for i, info in ipairs(TheApp.audio.background_playlist) do self.track_buttons[i]:setToggleState(not info.enabled) end + local s = TheApp.config.ui_scale Window.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y - local s = TheApp.config.ui_scale local playing = self.audio.background_music or "" for i, info in ipairs(self.audio.background_playlist) do local ypos = y + 47 * s + i * 30 * s @@ -173,7 +173,7 @@ function UIJukebox:draw(canvas, x, y) end font:draw(canvas, str, x + 24 * s, ypos + 11 * s) if info.music == playing then - font:draw(canvas, str, x + 24 * s, self.y + 27 * s) + font:draw(canvas, str, x + 24 * s, self.y * s + 27 * s) end end end From 73e33800b05bc10e9f8107abd7fcec129513c8d1 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Thu, 11 Dec 2025 21:40:57 -0500 Subject: [PATCH 30/50] Add broken drag handling from new_game Was redundant to UIResizable code. --- CorsixTH/Lua/dialogs/resizables/new_game.lua | 29 -------------------- 1 file changed, 29 deletions(-) diff --git a/CorsixTH/Lua/dialogs/resizables/new_game.lua b/CorsixTH/Lua/dialogs/resizables/new_game.lua index 08198686f..e0d3ac134 100644 --- a/CorsixTH/Lua/dialogs/resizables/new_game.lua +++ b/CorsixTH/Lua/dialogs/resizables/new_game.lua @@ -139,35 +139,6 @@ function UINewGame:saveToConfig() self.ui.app:saveConfig() end -function UINewGame:onMouseDown(button, x, y) - local repaint = UIResizable.onMouseDown(self, button, x, y) - if button == "left" and not repaint and not (x >= 0 and y >= 0 and - x < self.width and y < self.height) and self:hitTest(x, y) then - return self:beginDrag(x, y) - end - return repaint -end - -function UINewGame:hitTest(x, y) - if x >= 0 and y >= 0 and x < self.width and y < self.height then - return true - end - local sprites = self.border_sprites - if not sprites then - return false - end - if x < -9 or y < -9 or x >= self.width + 9 or y >= self.height + 9 then - return false - end - if (0 <= x and x < self.width) or (0 <= y and y < self.height) then - return true - end - return sprites.hitTest(sprites, 10, x + 9, y + 9) or - sprites.hitTest(sprites, 12, x - 160, y + 9) or - sprites.hitTest(sprites, 15, x + 9, y - 240) or - sprites.hitTest(sprites, 17, x - 160, y - 240) -end - function UINewGame:buttonTutorial(checked, button) self.start_tutorial = checked button.panel_for_sprite:setLabel(checked and _S.new_game_window.option_on or _S.new_game_window.option_off) From 99df5685ba6d30e440e33c3192123764ca31848e Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Thu, 11 Dec 2025 22:18:59 -0500 Subject: [PATCH 31/50] ui scalling for scrollbars and menu list dialogs --- .../menu_list_dialogs/custom_campaign.lua | 13 +++++++------ .../resizables/menu_list_dialogs/custom_game.lua | 15 ++++++++------- .../menu_list_dialogs/make_debug_patient.lua | 1 - CorsixTH/Lua/window.lua | 10 ++++++---- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_campaign.lua b/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_campaign.lua index 544ff0271..f0abc11de 100644 --- a/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_campaign.lua +++ b/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_campaign.lua @@ -33,7 +33,7 @@ local col_scrollbar = { local details_width = 280 function UICustomCampaign:UICustomCampaign(ui) - self.label_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V") + self.label_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) self.unique_names, self.campaigns, self.duplicates = {}, {}, 0 self.paths_table = {ui.app.campaign_dir, ui.app.user_campaign_dir} @@ -124,7 +124,7 @@ function UICustomCampaign:buttonClicked(num) local item = self.items[num + self.scrollbar.value - 1] self.chosen_item = item if item.description then - local _, _, rows = self.label_font:sizeOf(item.description, details_width) + local _, _, rows = self.label_font:sizeOf(item.description, details_width * TheApp.config.ui_scale) self.details_scrollbar:setRange(1, rows, 13, 1) else self.details_scrollbar:setRange(1, 13, 13, 1) @@ -140,16 +140,17 @@ end function UICustomCampaign:draw(canvas, x, y) UIMenuList.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y if self.chosen_item and self.chosen_item.name then self.label_font:drawWrapped(canvas, self.chosen_item.name, - x + 270, y + 10, details_width) + x + 270 * s, y + 10 * s, details_width * s) self.label_font:drawWrapped(canvas, "(levels: " .. - self.chosen_item.no_levels .. ")", x+ 270, y + 22, details_width) + self.chosen_item.no_levels .. ")", x + 270 * s, y + 22 * s, details_width * s) end if self.chosen_item and self.chosen_item.description then self.label_font:drawWrapped(canvas, self.chosen_item.description, - x + 270, y + 40, details_width, nil, 13, self.description_offset) + x + 270 * s, y + 40 * s, details_width * s, nil, 13, self.description_offset) end end diff --git a/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_game.lua b/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_game.lua index e8fc9bf46..1c3e6a1ed 100644 --- a/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_game.lua +++ b/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/custom_game.lua @@ -55,7 +55,7 @@ local findLevelsInDir = function(path, items) end function UICustomGame:UICustomGame(ui) - self.label_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V") + self.label_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) -- Supply the required list of items to UIMenuList -- Create the actual list @@ -110,9 +110,9 @@ function UICustomGame:buttonClicked(num) self.chosen_level_name = item.name self.chosen_level_description = item.intro if self.chosen_level_description then - local _, y, rows = self.label_font:sizeOf(self.chosen_level_description, details_width) - local row_height = y / rows - self.max_rows_shown = math.floor(self.num_rows*17 / row_height) + local _, y, rows = self.label_font:sizeOf(self.chosen_level_description, details_width * TheApp.config.ui_scale) + local row_height = y / rows / TheApp.config.ui_scale + self.max_rows_shown = math.floor(self.num_rows * 17 / row_height) self.details_scrollbar:setRange(1, rows, math.min(rows, self.max_rows_shown), 1) else self.details_scrollbar:setRange(1, 1, 1, 1) @@ -145,14 +145,15 @@ end function UICustomGame:draw(canvas, x, y) UIMenuList.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y if self.chosen_level_name then self.label_font:drawWrapped(canvas, self.chosen_level_name, - x + 270, y + 10, details_width) + x + 270 * s, y + 10 * s, details_width * s) end if self.chosen_level_description then self.label_font:drawWrapped(canvas, self.chosen_level_description, - x + 270, y + 40, details_width, nil, self.max_rows_shown, self.description_offset) + x + 270 * s, y + 40 * s, details_width * s, nil, self.max_rows_shown, self.description_offset) end end diff --git a/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/make_debug_patient.lua b/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/make_debug_patient.lua index 48ecfed5a..902d6fa15 100644 --- a/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/make_debug_patient.lua +++ b/CorsixTH/Lua/dialogs/resizables/menu_list_dialogs/make_debug_patient.lua @@ -51,5 +51,4 @@ function UIMakeDebugPatient:buttonClicked(num) patient:setTile(math.floor(x), math.floor(y)) patient:setMood("idea1", "activate") -- temporary, to make debug patients distinguishable from normal ones patient:setHospital(self.ui.hospital) - end diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 047d67fb4..735308018 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -1635,12 +1635,13 @@ function Window:onMouseDown(button, x, y) break end end + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 for _, bar in ipairs(self.scrollbars) do if bar.enabled and self:hitTestPanel(x, y, bar.slider) then self.active_scrollbar = bar bar.active = true - bar.down_x = x - bar.slider.x - bar.down_y = y - bar.slider.y + bar.down_x = x / s - bar.slider.x + bar.down_y = y / s - bar.slider.y repaint = true break end @@ -1858,11 +1859,12 @@ function Window:onMouseMove(x, y, dx, dy) end if self.active_scrollbar then + local s = self.apply_ui_scale and TheApp.config.ui_scale or 1 local bar = self.active_scrollbar if bar.direction == "x" then - bar:setXorY(x - bar.down_x) + bar:setXorY(math.floor(x / s - bar.down_x)) elseif bar.direction == "y" then - bar:setXorY(y - bar.down_y) + bar:setXorY(math.floor(y / s - bar.down_y)) end end From 866ec61cbafef50c8917ff74ed87ba07783ac5e3 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 12 Dec 2025 21:47:39 -0500 Subject: [PATCH 32/50] information fix, do not shadow s --- CorsixTH/Lua/dialogs/information.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CorsixTH/Lua/dialogs/information.lua b/CorsixTH/Lua/dialogs/information.lua index 478360cca..823316c60 100644 --- a/CorsixTH/Lua/dialogs/information.lua +++ b/CorsixTH/Lua/dialogs/information.lua @@ -105,12 +105,12 @@ function UIInformation:onChangeLanguage() -- Close button self:addPanel(19, self.width - 28, self.height - 28, 18, 18, 1):makeButton(0, 0, 18, 18, 20, self.close):setTooltip(_S.tooltip.information.close) .panel_for_sprite.custom_draw = --[[persistable:information_close_button]] function(panel, canvas, x, y) - local s = TheApp.config.ui_scale - x = x + panel.x * s - y = y + panel.y * s - panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) + local ds = TheApp.config.ui_scale + x = x + panel.x * ds + y = y + panel.y * ds + panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = ds }) if self.active_hover then - self.panel_sprites:draw(canvas, 20, x, y, { scaleFactor = s }) + self.panel_sprites:draw(canvas, 20, x, y, { scaleFactor = ds }) end end end From a560dee6597d266a8c487084f4a3d932863769ed Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 12 Dec 2025 21:54:40 -0500 Subject: [PATCH 33/50] build_room dialog scale --- CorsixTH/Lua/dialogs/build_room.lua | 31 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/CorsixTH/Lua/dialogs/build_room.lua b/CorsixTH/Lua/dialogs/build_room.lua index 3f83cd524..0103a4d76 100644 --- a/CorsixTH/Lua/dialogs/build_room.lua +++ b/CorsixTH/Lua/dialogs/build_room.lua @@ -37,8 +37,8 @@ function UIBuildRoom:UIBuildRoom(ui) self:setDefaultPosition(0.5, 0.5) local selected_label_color = { red = 40, green = 40, blue = 250 } self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req09V", true) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") - self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color}) + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color, apply_ui_scale = true }) self.category_index = 0 self.list_hover_index = 0 self.hover_sound = nil @@ -80,12 +80,13 @@ function UIBuildRoom:UIBuildRoom(ui) local build_room_dialog_close = TheApp.gfx:loadSpriteTable("Bitmap", "aux_ui", true) self:addPanel(224, 146, 224):makeButton(8, 34, 134, 27, 224, self.close):setTooltip(_S.tooltip.build_room_window.close) .panel_for_sprite.custom_draw = --[[persistable:build_room_draw_close_button]] function(panel, canvas, x, y) - x = x + panel.x - y = y + panel.y - panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y) + local s = TheApp.config.ui_scale + x = x + panel.x * s + y = y + panel.y * s + panel.window.panel_sprites:draw(canvas, panel.sprite_index, x, y, { scaleFactor = s }) local btn = panel.window.active_button if btn and btn.panel_for_sprite == panel and btn.active then - build_room_dialog_close:draw(canvas, 1, x + 8, y + 34) + build_room_dialog_close:draw(canvas, 1, x + 8 * s, y + 34 * s, { scaleFactor = s }) end end @@ -128,22 +129,23 @@ local cat_label_y = {21, 53, 84, 116} function UIBuildRoom:draw(canvas, x, y) Window.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y - self.white_font:draw(canvas, self.list_title, x + 163, y + 18) + local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y + self.white_font:draw(canvas, self.list_title, x + 163 * s, y + 18 * s) for i = 1, 4 do (i == self.category_index and self.blue_font or self.white_font) - :draw(canvas, self.category_titles[i], x + 19, y + cat_label_y[i]) + :draw(canvas, self.category_titles[i], x + 19 * s, y + cat_label_y[i] * s) end for i, room in ipairs(self.list) do (i == self.list_hover_index and self.blue_font or self.white_font) - :draw(canvas, room.name, x + 163, y + 21 + i * 19) + :draw(canvas, room.name, x + 163 * s, y + 21 * s + i * 19 * s) end - self.white_font:draw(canvas, self.cost_box, x + 163, y + 232) + self.white_font:draw(canvas, self.cost_box, x + 163 * s, y + 232 * s) if self.preview_anim then - self.preview_anim:draw(canvas, x + 70, y + 200) + self.preview_anim:draw(canvas, x + 70 * s, y + 200 * s, { scaleFactor = s}) end end @@ -195,11 +197,12 @@ end function UIBuildRoom:onMouseMove(x, y, dx, dy) local repaint = Window.onMouseMove(self, x, y, dx, dy) + local s = TheApp.config.ui_scale local hover_idx = 0 - if 156 <= x and x < 287 and 31 <= y and y < 226 then + if 156 * s <= x and x < 287 * s and 31 * s <= y and y < 226 * s then for i = 5, 14 do local btn = self.buttons[i] - if btn.enabled and btn.x <= x and x < btn.r and btn.y <= y and y < btn.b then + if btn.enabled and btn.x * s <= x and x < btn.r * s and btn.y * s <= y and y < btn.b * s then hover_idx = i - 4 break end From 81e938ab3e63701d22986f9ebf6cb1c7fac6002a Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 12 Dec 2025 22:15:22 -0500 Subject: [PATCH 34/50] scale edit room and place objects --- CorsixTH/Lua/dialogs/edit_room.lua | 6 ++-- CorsixTH/Lua/dialogs/place_objects.lua | 45 ++++++++++++++------------ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/CorsixTH/Lua/dialogs/edit_room.lua b/CorsixTH/Lua/dialogs/edit_room.lua index 022b1e2d6..82e5cb745 100644 --- a/CorsixTH/Lua/dialogs/edit_room.lua +++ b/CorsixTH/Lua/dialogs/edit_room.lua @@ -1095,12 +1095,12 @@ local window_floor_blueprint_markers = { west = 36, } - function UIEditRoom:onLeftButtonDown(x, y) + local s = TheApp.config.ui_scale if self.phase == "walls" then - if 0 <= x and x < self.width and 0 <= y and y < self.height then -- luacheck: ignore 542 + if 0 <= x and x < self.width * s and 0 <= y and y < self.height * s then -- luacheck: ignore 542 else - local mouse_x, mouse_y = self.ui:ScreenToWorld(self.x + x, self.y + y) + local mouse_x, mouse_y = self.ui:ScreenToWorld(self.x * s + x, self.y * s + y) self.mouse_down_x = math.floor(mouse_x) self.mouse_down_y = math.floor(mouse_y) if self.move_rect then diff --git a/CorsixTH/Lua/dialogs/place_objects.lua b/CorsixTH/Lua/dialogs/place_objects.lua index dac7b8135..74c081e72 100644 --- a/CorsixTH/Lua/dialogs/place_objects.lua +++ b/CorsixTH/Lua/dialogs/place_objects.lua @@ -57,8 +57,8 @@ function UIPlaceObjects:UIPlaceObjects(ui, object_list, pay_for) self:setDefaultPosition(0.9, 0.1) local selected_label_color = { red = 40, green = 40, blue = 250 } self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req05V", true) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") - self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color}) + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = app.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color, apply_ui_scale = true }) self.title_text = _S.rooms_short.corridor_objects self.desc_text = _S.place_objects_window.place_objects_in_corridor @@ -466,7 +466,8 @@ function UIPlaceObjects:onMouseUp(button, x, y) repaint = true elseif button == "left" then if #self.objects > 0 then - if 0 <= x and x < self.width and 0 <= y and y < self.height then -- luacheck: ignore 542 + local s = TheApp.config.ui_scale + if 0 <= x and x < self.width * s and 0 <= y and y < self.height * s then -- luacheck: ignore 542 -- Click within window - do nothing elseif self.object_cell_x and self.object_cell_y and self.object_blueprint_good then self:placeObject() @@ -559,42 +560,44 @@ function UIPlaceObjects:draw(canvas, x, y) Window.draw(self, canvas, x, y) - x, y = x + self.x, y + self.y - self.white_font:draw(canvas, self.title_text, x + 17, y + 21, 153, 0) - self.white_font:drawWrapped(canvas, self.desc_text, x + 20, y + 46, 147) + local s = TheApp.config.ui_scale + x, y = x + self.x * s, y + self.y * s + self.white_font:draw(canvas, self.title_text, x + 17 * s, y + 21 * s, 153 * s, 0) + self.white_font:drawWrapped(canvas, self.desc_text, x + 20 * s, y + 46 * s, 147 * s) for i, o in ipairs(self.objects) do local font = self.white_font - local font_ypos = y + 136 + i * 29 + local font_ypos = y + 136 * s + i * 29 * s if i == self.active_index then - local frame_xpos = x + 20 - local frame_ypos = y + 134 + i * 29 - local frame_width = 119 - local frame_height = 12 + local frame_xpos = x + 20 * s + local frame_ypos = y + 134 * s + i * 29 * s + local frame_width = 119 * s + local frame_height = 12 * s local red = canvas:mapRGB(221, 83, 0) - canvas:drawRect(red, frame_xpos, frame_ypos, frame_width, 1) - canvas:drawRect(red, frame_xpos, frame_ypos + frame_height, frame_width+1, 1) - canvas:drawRect(red, frame_xpos, frame_ypos, 1, frame_height) - canvas:drawRect(red, frame_xpos + frame_width, frame_ypos, 1, frame_height) + canvas:drawRect(red, frame_xpos, frame_ypos, frame_width, s) + canvas:drawRect(red, frame_xpos, frame_ypos + frame_height, frame_width + s, s) + canvas:drawRect(red, frame_xpos, frame_ypos, s, frame_height) + canvas:drawRect(red, frame_xpos + frame_width, frame_ypos, s, frame_height) end if i == self.active_hover_index then font = self.blue_font end - font:draw(canvas, o.object.name, x + 15, font_ypos, 130, 0) - font:draw(canvas, o.qty, x + 151, font_ypos, 19, 0) + font:draw(canvas, o.object.name, x + 15 * s, font_ypos, 130 * s, 0) + font:draw(canvas, o.qty, x + 151 * s, font_ypos, 19 * s, 0) end end function UIPlaceObjects:onMouseMove(x, y, dx, dy) + local s = TheApp.config.ui_scale local current_hover_id - local header_height = 159 - local bar_height = 29 - local inside_objects_area = (x > 0 and x < 186) and (y > header_height and y < header_height + bar_height*#self.objects) + local header_height = 159 * s + local bar_height = 29 * s + local inside_objects_area = (x > 0 and x < 186 * s) and (y > header_height and y < header_height + bar_height * #self.objects) if inside_objects_area then -- Check if player hovers over a button, not between. - if (header_height+y) % bar_height < 21 then + if (header_height + y) % bar_height < 21 * s then current_hover_id = math_floor((y - header_height)/bar_height) + 1 if self.active_hover_index ~= current_hover_id then if self.hover_sound then From b05fddbed08c5fe54f3bcf2b13869b9cc75a2860 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 12 Dec 2025 22:56:15 -0500 Subject: [PATCH 35/50] scale advisor --- CorsixTH/Lua/dialogs/adviser.lua | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/CorsixTH/Lua/dialogs/adviser.lua b/CorsixTH/Lua/dialogs/adviser.lua index 5d70b7071..801abf6c5 100644 --- a/CorsixTH/Lua/dialogs/adviser.lua +++ b/CorsixTH/Lua/dialogs/adviser.lua @@ -52,7 +52,7 @@ function UIAdviser:UIAdviser(ui) self.y = -16 self.balloon_width = 0 self.panel_sprites = app.gfx:loadSpriteTable("Data", "Panel02V", true) - self.black_font = app.gfx:loadFontAndSpriteTable("QData", "Font50V") + self.black_font = app.gfx:loadFontAndSpriteTable("QData", "Font50V", nil, nil, { apply_ui_scale = true }) local th = TH.animation() self.th = th @@ -180,19 +180,25 @@ end function UIAdviser:draw(canvas, x, y) Window.draw(self, canvas, x, y) - x, y = x + self.x, y + self.y - self.th:draw(canvas, x + 200, y) + local s = TheApp.config.ui_scale + x, y = x + self.x * s, y + self.y * s + + -- no scaleFactor for animation yet + canvas:scale(s) + self.th:draw(canvas, math.floor(x / s) + 200, math.floor(y / s)) + canvas:scale(1) + if self.phase == 2 then -- Draw balloon only in the "talk" phase. local x_left_sprite - for dx = 0, self.balloon_width, 16 do - x_left_sprite = x + 139 - dx - self.panel_sprites:draw(canvas, 38, x_left_sprite, y - 25) + for dx = 0, self.balloon_width * s, 16 * s do + x_left_sprite = x + 139 * s - dx + self.panel_sprites:draw(canvas, 38, x_left_sprite, y - 25 * s, { scaleFactor = s }) end - self.panel_sprites:draw(canvas, 37, x_left_sprite - 16, y - 25) - self.panel_sprites:draw(canvas, 39, x + 155, y - 40) + self.panel_sprites:draw(canvas, 37, x_left_sprite - 16 * s, y - 25 * s, { scaleFactor = s }) + self.panel_sprites:draw(canvas, 39, x + 155 * s, y - 40 * s, { scaleFactor = s }) -- Draw text - self.black_font:drawWrapped(canvas, self.speech, x_left_sprite - 8, y - 20, self.balloon_width + 60) + self.black_font:drawWrapped(canvas, self.speech, x_left_sprite - 8 * s, y - 20 * s, self.balloon_width * s + 60 * s) end end @@ -201,11 +207,13 @@ function UIAdviser:onMouseDown(button, x, y) if self.phase == 0 or self.phase == 4 then return Window.onMouseDown(self, button, x, y) end + local s = TheApp.config.ui_scale + -- Normal operation outside the adviser bounds - if x + self.balloon_width < 128 or x > 200 or - y + self.y > 0 or y + self.y + 40 < 0 then - if x < self.x - 200 or y < self.y - 40 or - x > self.x - 200 + self.width or y > self.y + self.height - 40 then + if x + self.balloon_width * s < 128 * s or x > 200 * s or + y + self.y * s > 0 or y + self.y * s + 40 * s < 0 then + if x < self.x * s - 200 * s or y < self.y * s - 40 * s or + x > self.x * s - 200 * s + self.width * s or y > self.y * s + self.height * s - 40 * s then return Window.onMouseDown(self, button, x, y) end end From 494b2f153fc243811426a868412103fbbf0c53c6 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 12 Dec 2025 23:05:59 -0500 Subject: [PATCH 36/50] Add ui_scale to busted mock --- CorsixTH/Luatest/TH.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/CorsixTH/Luatest/TH.lua b/CorsixTH/Luatest/TH.lua index ff2ba8d49..7a9d50c35 100644 --- a/CorsixTH/Luatest/TH.lua +++ b/CorsixTH/Luatest/TH.lua @@ -39,6 +39,7 @@ TheApp = { config = { width = 600, height = 800, + ui_scale = 1, }, world = { speed = "Normal", From 35721bb89eae6c2785fe389b8698a610b16ed0b7 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Fri, 12 Dec 2025 23:12:46 -0500 Subject: [PATCH 37/50] Fix bottom panel hit test --- CorsixTH/Lua/dialogs/bottom_panel.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CorsixTH/Lua/dialogs/bottom_panel.lua b/CorsixTH/Lua/dialogs/bottom_panel.lua index 8ab50a6bf..2cba372ca 100644 --- a/CorsixTH/Lua/dialogs/bottom_panel.lua +++ b/CorsixTH/Lua/dialogs/bottom_panel.lua @@ -391,7 +391,8 @@ function UIBottomPanel:showAdditionalButtons(x, y) end function UIBottomPanel:hitTest(x, y, x_offset) - return x >= (x_offset and x_offset or 0) and y >= 0 and x < self.width and y < self.height + local s = TheApp.config.ui_scale + return x >= (x_offset and x_offset * s or 0) and y >= 0 and x < self.width * s and y < self.height * s end --! Queue a fax notification message to appear. From 3311fad9a67c2a90d6efb7c5496db8a04767711a Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sat, 13 Dec 2025 12:46:34 -0500 Subject: [PATCH 38/50] Fix windows config template for ui_scale --- WindowsInstaller/config_template.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WindowsInstaller/config_template.txt b/WindowsInstaller/config_template.txt index 8c73691a1..a878fcb42 100644 --- a/WindowsInstaller/config_template.txt +++ b/WindowsInstaller/config_template.txt @@ -13,11 +13,15 @@ -- Screen size. Must be at least 640x480. Larger sizes will require better -- hardware in order to maintain a playable framerate. The fullscreen setting -- can be true or false, and the game will run windowed if not fullscreen. +-- ui_scale can be set to 1, 2, or 3 to scale the user interface for higher +-- resolution displays. For example, at 1920x1080 resolution, setting ui_scale +-- to 2 will make the interface elements twice as large. -- fullscreen = SCREEN_FULLSCREEN width = SCREEN_SIZE_WIDTH height = SCREEN_SIZE_HEIGHT +ui_scale = 1 ------------------------------------------------------------------------------- -- Language to use for ingame text. Between the square braces should be one of: From c909a0fbad57ab8c3ba2fd176656f65edb69c1f3 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sun, 14 Dec 2025 01:07:58 -0500 Subject: [PATCH 39/50] some comments for parameter changes --- CorsixTH/Lua/window.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CorsixTH/Lua/window.lua b/CorsixTH/Lua/window.lua index 735308018..d073ec1b0 100644 --- a/CorsixTH/Lua/window.lua +++ b/CorsixTH/Lua/window.lua @@ -351,7 +351,6 @@ end --! Set the size of a panel. --!param width (int) New width of the panel. --!param height (int) New height of the panel. ---!param scale (int|nil) Scale factor to apply to width and height. Default is TheApp.config.ui_scale. function Panel:setSize(width, height) self.w = width self.h = height @@ -378,6 +377,9 @@ bitmap to be displayed. panel (in pixels) is known, it should be specified here to speed up hit-tests. !param h (integer, nil) If the panel is totally opaque, and the height of the panel (in pixels) is known, it should be specified here to speed up hit-tests. +!param apply_ui_scale (bool|nil) If true apply the ui_scale to the dimensions +when doing the draw and hit test. If nil then apply_ui_scale if enabled for the +parent window. ]] function Window:addPanel(sprite_index, x, y, w, h, apply_ui_scale) local panel = setmetatable({ From 5d760c1eb2a4b5f4b93a55db8123a1ab6eb31436 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sun, 14 Dec 2025 19:06:34 -0500 Subject: [PATCH 40/50] scale staff profile page Add the ability to specify flags when loading a bitmap. This is needed because linear scaling causes artifacts at the border where the face is composed of multiple images. --- CorsixTH/Lua/dialogs/hire_staff.lua | 56 ++++++++++++++++------------- CorsixTH/Lua/graphics.lua | 7 ++-- CorsixTH/Lua/staff_profile.lua | 11 +++--- CorsixTH/Src/th_gfx_sdl.cpp | 6 ++-- CorsixTH/Src/th_gfx_sdl.h | 4 ++- CorsixTH/Src/th_lua_gfx.cpp | 54 ++++++++++++++++------------ 6 files changed, 81 insertions(+), 57 deletions(-) diff --git a/CorsixTH/Lua/dialogs/hire_staff.lua b/CorsixTH/Lua/dialogs/hire_staff.lua index bded9ed2c..f3f96ece0 100644 --- a/CorsixTH/Lua/dialogs/hire_staff.lua +++ b/CorsixTH/Lua/dialogs/hire_staff.lua @@ -33,8 +33,8 @@ function UIHireStaff:UIHireStaff(ui) self.height = 323 self:setDefaultPosition(100, 100) self.panel_sprites = ui.app.gfx:loadSpriteTable("QData", "Req11V", true) - self.white_font = ui.app.gfx:loadFontAndSpriteTable("QData", "Font01V") - self.face_parts = ui.app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat") + self.white_font = ui.app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.face_parts = ui.app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { nearest = true }) -- Left hand side tab backgrounds self:addPanel(253, 0, 0) @@ -144,6 +144,7 @@ function UIHireStaff:hire() table.remove(self.world.available_staff[self.category], self.current_index) self.ui:addWindow(UIPlaceStaff(self.ui, profile, self.mouse_up_x, self.mouse_up_y)) end + function UIHireStaff:cannotAfford() local msg = { (_A.warnings.cannot_afford), @@ -153,60 +154,65 @@ function UIHireStaff:cannotAfford() self.world.ui.adviser:say(msg[math.random(1, #msg)]) end end + function UIHireStaff:draw(canvas, x, y) Window.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + + local s = TheApp.config.ui_scale + x, y = self.x * s + x, self.y * s + y local font = self.white_font local staff = self.world.available_staff - font:draw(canvas, #staff.Doctor , x + 16, y + 58, 26, 0) - font:draw(canvas, #staff.Nurse , x + 16, y + 137, 26, 0) - font:draw(canvas, #staff.Handyman , x + 16, y + 216, 26, 0) - font:draw(canvas, #staff.Receptionist, x + 16, y + 295, 26, 0) + local staff_label_x = x + 16 * s + local staff_label_w = 26 * s + font:draw(canvas, #staff.Doctor , staff_label_x, y + 58 * s, staff_label_w, 0) + font:draw(canvas, #staff.Nurse , staff_label_x, y + 137 * s, staff_label_w, 0) + font:draw(canvas, #staff.Handyman , staff_label_x, y + 216 * s, staff_label_w, 0) + font:draw(canvas, #staff.Receptionist, staff_label_x, y + 295 * s, staff_label_w, 0) if self.category and self.current_index then local profile = staff[self.category] profile = profile and profile[self.current_index] if not profile then return end - font:draw(canvas, profile:getFullName(), x + 79, y + 21) - profile:drawFace(canvas, x + 158, y + 48, self.face_parts) - font:draw(canvas, "$" .. profile.wage, x + 116, y + 179) - font:drawWrapped(canvas, profile.desc, x + 74, y + 205, 149) + font:draw(canvas, profile:getFullName(), x + 79 * s, y + 21 * s) + profile:drawFace(canvas, x + 158 * s, y + 48 * s, self.face_parts, s) + font:draw(canvas, "$" .. profile.wage, x + 116 * s, y + 179 * s) + font:drawWrapped(canvas, profile.desc, x + 74 * s, y + 205 * s, 149 * s) -- Skill bar if self.skill_bg_panel.visible then - local skill_bar_width = math.floor(profile.skill * 40 + 0.5) + local skill_bar_width = math.floor(profile.skill * 40 * s + 0.5) if skill_bar_width ~= 0 then local px, py = self.skill_bg_panel.x, self.skill_bg_panel.y - px = px + x - py = py + y - for dx = 0, skill_bar_width - 1 do - self.panel_sprites:draw(canvas, 3, px + 22 + dx, py + 9) + px = px * s + x + py = py * s + y + for dx = 0, skill_bar_width - 1, s do + self.panel_sprites:draw(canvas, 3, px + 22 * s + dx, py + 9 * s, { scaleFactor = s }) end end end if self.category == "Doctor" then -- Junior / Doctor / Consultant marker - self.panel_sprites:draw(canvas, 258, x + 71, y + 49) + self.panel_sprites:draw(canvas, 258, x + 71 * s, y + 49 * s, { scaleFactor = s }) if profile.is_junior then - self.panel_sprites:draw(canvas, 296, x + 79, y + 80) + self.panel_sprites:draw(canvas, 296, x + 79 * s, y + 80 * s, { scaleFactor = s }) elseif profile.is_consultant then - self.panel_sprites:draw(canvas, 296, x + 131, y + 80) + self.panel_sprites:draw(canvas, 296, x + 131 * s, y + 80 * s, { scaleFactor = s }) else - self.panel_sprites:draw(canvas, 296, x + 101, y + 80) + self.panel_sprites:draw(canvas, 296, x + 101 * s, y + 80 * s, { scaleFactor = s }) end -- Ability markers local px, py = self.ability_bg_panel.x, self.ability_bg_panel.y - px = px + x - py = py + y + px = px * s + x + py = py * s + y if profile.is_surgeon >= 1.0 then - self.panel_sprites:draw(canvas, 292, px + 65, py + 22) + self.panel_sprites:draw(canvas, 292, px + 65 * s, py + 22 * s, { scaleFactor = s }) end if profile.is_psychiatrist >= 1.0 then - self.panel_sprites:draw(canvas, 293, px + 82, py + 28) + self.panel_sprites:draw(canvas, 293, px + 82 * s, py + 28 * s, { scaleFactor = s }) end if profile.is_researcher >= 1.0 then - self.panel_sprites:draw(canvas, 294, px + 109, py + 27) + self.panel_sprites:draw(canvas, 294, px + 109 * s, py + 27 * s, { scaleFactor = s }) end end end diff --git a/CorsixTH/Lua/graphics.lua b/CorsixTH/Lua/graphics.lua index 101472793..a9f4c65cf 100644 --- a/CorsixTH/Lua/graphics.lua +++ b/CorsixTH/Lua/graphics.lua @@ -288,7 +288,8 @@ end --!param paldir (string) The directory of the palette. --!param pal (string) The name of the palette --!param transparent_255 (boolean) Whether the 255th entry of the palette should be transparent -function Graphics:loadRaw(name, width, height, dir, paldir, pal, transparent_255) +--!param flags (table) Additional flags for loading the bitmap +function Graphics:loadRaw(name, width, height, dir, paldir, pal, transparent_255, flags) if self.cache.raw[name] then return self.cache.raw[name] end @@ -304,7 +305,7 @@ function Graphics:loadRaw(name, width, height, dir, paldir, pal, transparent_255 local bitmap = TH.bitmap() local palette = self:loadPalette(paldir, pal, transparent_255) bitmap:setPalette(palette) - assert(bitmap:load(data, width, self.target)) + assert(bitmap:load(data, width, self.target, flags)) local function bitmap_reloader(bm) bm:setPalette(palette) @@ -315,7 +316,7 @@ function Graphics:loadRaw(name, width, height, dir, paldir, pal, transparent_255 self.reload_functions[bitmap] = bitmap_reloader self.cache.raw[name] = bitmap - self.load_info[bitmap] = {self.loadRaw, self, name, width, height, dir, paldir, pal, transparent_255} + self.load_info[bitmap] = {self.loadRaw, self, name, width, height, dir, paldir, pal, transparent_255, flags} return bitmap end diff --git a/CorsixTH/Lua/staff_profile.lua b/CorsixTH/Lua/staff_profile.lua index 2b8c70cbb..789fde596 100644 --- a/CorsixTH/Lua/staff_profile.lua +++ b/CorsixTH/Lua/staff_profile.lua @@ -197,10 +197,13 @@ function StaffProfile:randomiseOrganical() end end -function StaffProfile:drawFace(canvas, x, y, parts_bitmap) - parts_bitmap:draw(canvas, x, y , 0, self.hair_index * 29, 65, 29) - parts_bitmap:draw(canvas, x, y + 29, 0, 522 + self.face_index * 24, 65, 24) - parts_bitmap:draw(canvas, x, y + 53, 0, 954 + self.chin_index * 22, 65, 22) +function StaffProfile:drawFace(canvas, x, y, parts_bitmap, scale) + scale = scale or 1 + canvas:scale(scale, "bitmap") + parts_bitmap:draw(canvas, x, y , 0, self.hair_index * 29, 65, 29) + parts_bitmap:draw(canvas, x, y + 29 * scale, 0, 522 + self.face_index * 24, 65, 24) + parts_bitmap:draw(canvas, x, y + 53 * scale, 0, 954 + self.chin_index * 22, 65, 22) + canvas:scale(1, "bitmap") end -- Update junior and consultant status diff --git a/CorsixTH/Src/th_gfx_sdl.cpp b/CorsixTH/Src/th_gfx_sdl.cpp index c28035dd2..88d23b8a4 100644 --- a/CorsixTH/Src/th_gfx_sdl.cpp +++ b/CorsixTH/Src/th_gfx_sdl.cpp @@ -995,7 +995,8 @@ void raw_bitmap::set_palette(const palette* pPalette) { void raw_bitmap::load_from_th_file(const uint8_t* pPixelData, size_t iPixelDataLength, int iWidth, - render_target* pEventualCanvas) { + render_target* pEventualCanvas, + uint32_t spriteFlags) { if (pEventualCanvas == nullptr) { throw std::invalid_argument("pEventualCanvas cannot be null"); } @@ -1004,7 +1005,8 @@ void raw_bitmap::load_from_th_file(const uint8_t* pPixelData, int iHeight = static_cast(iPixelDataLength) / iWidth; texture = pEventualCanvas->create_palettized_texture( - iWidth, iHeight, converted_sprite, bitmap_palette, thdf_alt32_plain); + iWidth, iHeight, converted_sprite, bitmap_palette, + thdf_alt32_plain | spriteFlags); delete[] converted_sprite; width = iWidth; diff --git a/CorsixTH/Src/th_gfx_sdl.h b/CorsixTH/Src/th_gfx_sdl.h index 312065396..b2df675ff 100644 --- a/CorsixTH/Src/th_gfx_sdl.h +++ b/CorsixTH/Src/th_gfx_sdl.h @@ -447,10 +447,12 @@ class raw_bitmap { @param iPixelDataLength Size of the loaded image data. @param iWidth Width of the image. @param pEventualCanvas Canvas to render the image to (eventually). + @param spriteFlags @return Loading was a success. */ void load_from_th_file(const uint8_t* pPixelData, size_t iPixelDataLength, - int iWidth, render_target* pEventualCanvas); + int iWidth, render_target* pEventualCanvas, + uint32_t spriteFlags); //! Draw the image at a given position at the given canvas. /*! diff --git a/CorsixTH/Src/th_lua_gfx.cpp b/CorsixTH/Src/th_lua_gfx.cpp index 23ece2de9..b558fe7d8 100644 --- a/CorsixTH/Src/th_lua_gfx.cpp +++ b/CorsixTH/Src/th_lua_gfx.cpp @@ -43,6 +43,35 @@ SOFTWARE. #include FT_TYPES_H namespace { +uint32_t flags_from_table(lua_State* L, int index) { + if (lua_type(L, index) != LUA_TTABLE) { + return 0; + } + + lua_getfield(L, index, "flags"); + uint32_t flags = static_cast(luaL_optinteger(L, -1, 0)); + lua_pop(L, 1); + + lua_getfield(L, index, "nearest"); + if (lua_toboolean(L, -1)) { + flags |= thdf_nearest; + } + lua_pop(L, 1); + + lua_getfield(L, index, "flipHorizontal"); + if (lua_toboolean(L, -1)) { + flags |= thdf_flip_horizontal; + } + lua_pop(L, 1); + + lua_getfield(L, index, "flipVertical"); + if (lua_toboolean(L, -1)) { + flags |= thdf_flip_vertical; + } + lua_pop(L, 1); + + return flags; +} font* luaT_getfont(lua_State* L) { font* p = luaT_touserdata_base( @@ -104,9 +133,10 @@ int l_rawbitmap_load(lua_State* L) { int iWidth = static_cast(luaL_checkinteger(L, 3)); render_target* pSurface = luaT_testuserdata(L, 4, luaT_upvalueindex(1), false); + uint32_t flags = flags_from_table(L, 5); try { - pBitmap->load_from_th_file(pData, iDataLen, iWidth, pSurface); + pBitmap->load_from_th_file(pData, iDataLen, iWidth, pSurface, flags); } catch (const std::exception& ex) { lua_pushstring(L, ex.what()); lua_error(L); @@ -206,27 +236,7 @@ int l_spritesheet_draw(lua_State* L) { uint32_t flags = 0; int arg6type = lua_type(L, 6); if (arg6type == LUA_TTABLE) { - lua_getfield(L, 6, "flags"); - flags = static_cast(luaL_optinteger(L, -1, 0)); - lua_pop(L, 1); - - lua_getfield(L, 6, "nearest"); - if (lua_toboolean(L, -1)) { - flags |= thdf_nearest; - } - lua_pop(L, 1); - - lua_getfield(L, 6, "flipHorizontal"); - if (lua_toboolean(L, -1)) { - flags |= thdf_flip_horizontal; - } - lua_pop(L, 1); - - lua_getfield(L, 6, "flipVertical"); - if (lua_toboolean(L, -1)) { - flags |= thdf_flip_vertical; - } - lua_pop(L, 1); + flags = flags_from_table(L, 6); lua_getfield(L, 6, "scaleFactor"); scaleFactor = static_cast(luaL_optinteger(L, -1, 1)); From 8070b2bd50d3e1a252ac05c39dce19c7ca88fcbc Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sun, 14 Dec 2025 21:30:58 -0500 Subject: [PATCH 41/50] Add scale to staff dialog --- CorsixTH/Lua/dialogs/staff_dialog.lua | 70 ++++++++++++++------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/CorsixTH/Lua/dialogs/staff_dialog.lua b/CorsixTH/Lua/dialogs/staff_dialog.lua index 83ad8b41b..e9d4a9f6d 100644 --- a/CorsixTH/Lua/dialogs/staff_dialog.lua +++ b/CorsixTH/Lua/dialogs/staff_dialog.lua @@ -20,8 +20,9 @@ SOFTWARE. --]] -- Test for hit within the view circle local --[[persistable:staff_dialog_is_in_view_circle]] function is_in_view_circle(x, y, is_handyman) - local circle_center_y = is_handyman and 276 or 248 - return (x - 55)^2 + (y - circle_center_y)^2 < 39^2 + local s = TheApp.config.ui_scale + local circle_center_y = is_handyman and 276 * s or 248 * s + return (x - 55 * s)^2 + (y - circle_center_y)^2 < (39 * s)^2 end --! Individual staff information dialog @@ -74,8 +75,8 @@ function UIStaff:UIStaff(ui, staff) end self:setDefaultPosition(-20, 30) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req01V", true) - self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V") - self.face_parts = app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat") + self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.face_parts = app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { nearest = true }) self:addPanel(297, 15, 0) -- Dialog header for y = 51, 121, 10 do @@ -162,90 +163,93 @@ function UIStaff:getStaffPosition(dx, dy) end function UIStaff:draw(canvas, x_, y_) - local x, y = self.x + x_, self.y + y_ + local s = TheApp.config.ui_scale + local x, y = self.x * s + x_, self.y * s + y_ local px, py = self:getStaffPosition(37, 61) - self.ui.app.map:draw(canvas, px, py, 75, 75, x + 17, y + self.height - 93) + canvas:scale(s) + self.ui.app.map:draw(canvas, px, py, 75, 75, math.floor(x / s) + 17, math.floor(y / s) + self.height - 93) + canvas:scale(1) Window.draw(self, canvas, x_, y_) local profile = self.staff.profile local font = self.white_font - font:draw(canvas, profile:getFullName(), x + 42, y + 28) -- Name + font:draw(canvas, profile:getFullName(), x + 42 * s, y + 28 * s) -- Name if class.is(self.staff, Handyman)then - font:draw(canvas, "$" .. profile.wage, x + 135, y + 226) -- Wage - font:draw(canvas, self:getParcelText(), x + 35, y + 215, 50, 0) + font:draw(canvas, "$" .. profile.wage, x + 135 * s, y + 225 * s) -- Wage + font:draw(canvas, self:getParcelText(), x + 35 * s, y + 215 * s, 50 * s, 0) -- The concentration areas - local cleaning_width = math.floor(self.staff:getAttribute("cleaning") * 40 + 0.5) - local watering_width = math.floor(self.staff:getAttribute("watering") * 40 + 0.5) - local repairing_width = math.floor(self.staff:getAttribute("repairing") * 40 + 0.5) + local cleaning_width = math.floor(self.staff:getAttribute("cleaning") * 40 * s + 0.5) + local watering_width = math.floor(self.staff:getAttribute("watering") * 40 * s + 0.5) + local repairing_width = math.floor(self.staff:getAttribute("repairing") * 40 * s + 0.5) if cleaning_width ~= 0 then - for dx = 0, cleaning_width - 1 do - self.panel_sprites:draw(canvas, 351, x + 43 + dx, y + 200) + for dx = 0, cleaning_width - 1, s do + self.panel_sprites:draw(canvas, 351, x + 43 * s + dx, y + 200 * s, { scaleFactor = s }) end end if watering_width ~= 0 then for dx = 0, watering_width - 1 do - self.panel_sprites:draw(canvas, 351, x + 99 + dx, y + 200) + self.panel_sprites:draw(canvas, 351, x + 99 * s + dx, y + 200 * s, { scaleFactor = s }) end end if repairing_width ~= 0 then for dx = 0, repairing_width - 1 do - self.panel_sprites:draw(canvas, 351, x + 155 + dx, y + 200) + self.panel_sprites:draw(canvas, 351, x + 155 * s + dx, y + 200 * s, { scaleFactor = s }) end end else - font:draw(canvas, "$" .. profile.wage, x + 135, y + 199) -- Wage + font:draw(canvas, "$" .. profile.wage, x + 135 * s, y + 198 * s) -- Wage end if self.staff:getAttribute("happiness") then - local happiness_bar_width = math.floor(self.staff:getAttribute("happiness") * 40 + 0.5) + local happiness_bar_width = math.floor(self.staff:getAttribute("happiness") * 40 * s + 0.5) if happiness_bar_width ~= 0 then - for dx = 0, happiness_bar_width - 1 do - self.panel_sprites:draw(canvas, 348, x + 139 + dx, y + 56) + for dx = 0, happiness_bar_width - 1, s do + self.panel_sprites:draw(canvas, 348, x + 139 * s + dx, y + 56 * s, { scaleFactor = s }) end end end local fatigue_bar_width = 40.5 if self.staff:getAttribute("fatigue") then - fatigue_bar_width = math.floor((1 - self.staff:getAttribute("fatigue")) * 40 + 0.5) + fatigue_bar_width = math.floor((1 - self.staff:getAttribute("fatigue")) * 40 * s + 0.5) end if fatigue_bar_width ~= 0 then - for dx = 0, fatigue_bar_width - 1 do - self.panel_sprites:draw(canvas, 349, x + 139 + dx, y + 89) + for dx = 0, fatigue_bar_width - 1, s do + self.panel_sprites:draw(canvas, 349, x + 139 * s + dx, y + 89 * s, { scaleFactor = s }) end end - local skill_bar_width = math.floor(profile.skill * 40 + 0.5) + local skill_bar_width = math.floor(profile.skill * 40 * s + 0.5) if skill_bar_width ~= 0 then - for dx = 0, skill_bar_width - 1 do - self.panel_sprites:draw(canvas, 350, x + 139 + dx, y + 120) + for dx = 0, skill_bar_width - 1, s do + self.panel_sprites:draw(canvas, 350, x + 139 * s + dx, y + 120 * s, { scaleFactor = s }) end end if class.is(self.staff, Doctor) then -- Junior / Doctor / Consultant marker if profile.is_junior then - self.panel_sprites:draw(canvas, 347, x + 38, y + 173) + self.panel_sprites:draw(canvas, 347, x + 38 * s, y + 173 * s, { scaleFactor = s }) elseif profile.is_consultant then - self.panel_sprites:draw(canvas, 347, x + 89, y + 173) + self.panel_sprites:draw(canvas, 347, x + 89 * s, y + 173 * s, { scaleFactor = s }) else - self.panel_sprites:draw(canvas, 347, x + 60, y + 173) + self.panel_sprites:draw(canvas, 347, x + 60 * s, y + 173 * s, { scaleFactor = s }) end -- Ability markers if profile.is_surgeon >= 1.0 then - self.panel_sprites:draw(canvas, 344, x + 144, y + 148) + self.panel_sprites:draw(canvas, 344, x + 144 * s, y + 148 * s, { scaleFactor = s }) end if profile.is_psychiatrist >= 1.0 then - self.panel_sprites:draw(canvas, 345, x + 155, y + 154) + self.panel_sprites:draw(canvas, 345, x + 155 * s, y + 154 * s, { scaleFactor = s }) end if profile.is_researcher >= 1.0 then - self.panel_sprites:draw(canvas, 346, x + 178, y + 153) + self.panel_sprites:draw(canvas, 346, x + 178 * s, y + 153 * s, { scaleFactor = s }) end end - profile:drawFace(canvas, x + 38, y + 54, self.face_parts) -- Portrait + profile:drawFace(canvas, x + 38 * s, y + 54 * s, self.face_parts, s) -- Portrait end function UIStaff:onMouseDown(button, x, y) From fe602d222d41c61d0fcad9c06a1251d965c5f35c Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Mon, 15 Dec 2025 21:07:08 -0500 Subject: [PATCH 42/50] Use DrawFlags instead of support for flag names in binding --- CorsixTH/Lua/dialogs/hire_staff.lua | 2 +- CorsixTH/Lua/dialogs/staff_dialog.lua | 2 +- CorsixTH/Lua/utility.lua | 1 + CorsixTH/Src/th_lua_gfx.cpp | 42 +++++++-------------------- 4 files changed, 13 insertions(+), 34 deletions(-) diff --git a/CorsixTH/Lua/dialogs/hire_staff.lua b/CorsixTH/Lua/dialogs/hire_staff.lua index f3f96ece0..844bec0d0 100644 --- a/CorsixTH/Lua/dialogs/hire_staff.lua +++ b/CorsixTH/Lua/dialogs/hire_staff.lua @@ -34,7 +34,7 @@ function UIHireStaff:UIHireStaff(ui) self:setDefaultPosition(100, 100) self.panel_sprites = ui.app.gfx:loadSpriteTable("QData", "Req11V", true) self.white_font = ui.app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) - self.face_parts = ui.app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { nearest = true }) + self.face_parts = ui.app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { flags = DrawFlags.Nearest }) -- Left hand side tab backgrounds self:addPanel(253, 0, 0) diff --git a/CorsixTH/Lua/dialogs/staff_dialog.lua b/CorsixTH/Lua/dialogs/staff_dialog.lua index e9d4a9f6d..d6be59517 100644 --- a/CorsixTH/Lua/dialogs/staff_dialog.lua +++ b/CorsixTH/Lua/dialogs/staff_dialog.lua @@ -76,7 +76,7 @@ function UIStaff:UIStaff(ui, staff) self:setDefaultPosition(-20, 30) self.panel_sprites = app.gfx:loadSpriteTable("QData", "Req01V", true) self.white_font = app.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) - self.face_parts = app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { nearest = true }) + self.face_parts = app.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { flags = DrawFlags.Nearest }) self:addPanel(297, 15, 0) -- Dialog header for y = 51, 121, 10 do diff --git a/CorsixTH/Lua/utility.lua b/CorsixTH/Lua/utility.lua index 2da3241a6..83589ac3a 100644 --- a/CorsixTH/Lua/utility.lua +++ b/CorsixTH/Lua/utility.lua @@ -224,6 +224,7 @@ DrawFlags.EarlyList = 2^10 DrawFlags.ListBottom = 2^11 DrawFlags.BoundBoxHitTest = 2^12 DrawFlags.Crop = 2^13 +DrawFlags.Nearest = 2^14 -- Order of animations within a tile. Animations with a smaller number are -- drawn first. diff --git a/CorsixTH/Src/th_lua_gfx.cpp b/CorsixTH/Src/th_lua_gfx.cpp index b558fe7d8..43f679c04 100644 --- a/CorsixTH/Src/th_lua_gfx.cpp +++ b/CorsixTH/Src/th_lua_gfx.cpp @@ -43,36 +43,6 @@ SOFTWARE. #include FT_TYPES_H namespace { -uint32_t flags_from_table(lua_State* L, int index) { - if (lua_type(L, index) != LUA_TTABLE) { - return 0; - } - - lua_getfield(L, index, "flags"); - uint32_t flags = static_cast(luaL_optinteger(L, -1, 0)); - lua_pop(L, 1); - - lua_getfield(L, index, "nearest"); - if (lua_toboolean(L, -1)) { - flags |= thdf_nearest; - } - lua_pop(L, 1); - - lua_getfield(L, index, "flipHorizontal"); - if (lua_toboolean(L, -1)) { - flags |= thdf_flip_horizontal; - } - lua_pop(L, 1); - - lua_getfield(L, index, "flipVertical"); - if (lua_toboolean(L, -1)) { - flags |= thdf_flip_vertical; - } - lua_pop(L, 1); - - return flags; -} - font* luaT_getfont(lua_State* L) { font* p = luaT_touserdata_base( L, 1, {"bitmap_font", "freetype_font"}); @@ -133,7 +103,13 @@ int l_rawbitmap_load(lua_State* L) { int iWidth = static_cast(luaL_checkinteger(L, 3)); render_target* pSurface = luaT_testuserdata(L, 4, luaT_upvalueindex(1), false); - uint32_t flags = flags_from_table(L, 5); + + uint32_t flags = 0; + if (lua_type(L, 5) == LUA_TTABLE) { + lua_getfield(L, 5, "flags"); + flags = static_cast(luaL_optinteger(L, -1, 0)); + lua_pop(L, 1); + } try { pBitmap->load_from_th_file(pData, iDataLen, iWidth, pSurface, flags); @@ -236,7 +212,9 @@ int l_spritesheet_draw(lua_State* L) { uint32_t flags = 0; int arg6type = lua_type(L, 6); if (arg6type == LUA_TTABLE) { - flags = flags_from_table(L, 6); + lua_getfield(L, 6, "flags"); + flags = static_cast(luaL_optinteger(L, -1, 0)); + lua_pop(L, 1); lua_getfield(L, 6, "scaleFactor"); scaleFactor = static_cast(luaL_optinteger(L, -1, 1)); From 904c2825bc91b5414b23bd656be68c6339875b96 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Mon, 15 Dec 2025 21:26:23 -0500 Subject: [PATCH 43/50] update handling for scaled dialogs Fonts and graphics need to be reloaded when the flags changed. --- CorsixTH/Lua/dialogs/adviser.lua | 3 +++ CorsixTH/Lua/dialogs/bottom_panel.lua | 4 ++-- CorsixTH/Lua/dialogs/build_room.lua | 10 ++++++++++ CorsixTH/Lua/dialogs/confirm_dialog.lua | 4 ++++ CorsixTH/Lua/dialogs/hire_staff.lua | 5 +++++ CorsixTH/Lua/dialogs/jukebox.lua | 4 ++++ CorsixTH/Lua/dialogs/patient.lua | 4 ++++ CorsixTH/Lua/dialogs/place_objects.lua | 5 +++++ CorsixTH/Lua/dialogs/staff_dialog.lua | 4 ++++ 9 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CorsixTH/Lua/dialogs/adviser.lua b/CorsixTH/Lua/dialogs/adviser.lua index 801abf6c5..2623ef6ef 100644 --- a/CorsixTH/Lua/dialogs/adviser.lua +++ b/CorsixTH/Lua/dialogs/adviser.lua @@ -282,5 +282,8 @@ function UIAdviser:afterLoad(old, new) if old < 47 then self.enabled = true end + if old < 236 then + self.black_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font50V", nil, nil, { apply_ui_scale = true }) + end Window.afterLoad(self, old, new) end diff --git a/CorsixTH/Lua/dialogs/bottom_panel.lua b/CorsixTH/Lua/dialogs/bottom_panel.lua index 2cba372ca..6a4787f63 100644 --- a/CorsixTH/Lua/dialogs/bottom_panel.lua +++ b/CorsixTH/Lua/dialogs/bottom_panel.lua @@ -972,11 +972,11 @@ function UIBottomPanel:afterLoad(old, new) end self.bank_button = self.buttons[1]:makeToggle() end - if old < 215 then + if old < 236 then self:_initFonts(self.ui.app.gfx) end -- Hotfix to force re-calculation of the money font (see issue #1193) - self.money_font = self.ui.app.gfx:loadFontAndSpriteTable("QData", "Font05V") + self.money_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font05V", nil, nil, { apply_ui_scale = true }) self:registerKeyHandlers() Window.afterLoad(self, old, new) diff --git a/CorsixTH/Lua/dialogs/build_room.lua b/CorsixTH/Lua/dialogs/build_room.lua index 0103a4d76..58d245d29 100644 --- a/CorsixTH/Lua/dialogs/build_room.lua +++ b/CorsixTH/Lua/dialogs/build_room.lua @@ -237,3 +237,13 @@ function UIBuildRoom:close() self.ui:tutorialStep(3, {2, 3}, 1) return Window.close(self) end + +function UIBuildRoom:afterLoad(old, new) + Window.afterLoad(self, old, new) + + if old < 236 then + local selected_label_color = { red = 40, green = 40, blue = 250 } + self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color, apply_ui_scale = true }) + end +end diff --git a/CorsixTH/Lua/dialogs/confirm_dialog.lua b/CorsixTH/Lua/dialogs/confirm_dialog.lua index 7d187fc0f..44a1943e3 100644 --- a/CorsixTH/Lua/dialogs/confirm_dialog.lua +++ b/CorsixTH/Lua/dialogs/confirm_dialog.lua @@ -130,4 +130,8 @@ end function UIConfirmDialog:afterLoad(old, new) Window.afterLoad(self, old, new) self:registerKeyHandlers() + + if old < 236 then + self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + end end diff --git a/CorsixTH/Lua/dialogs/hire_staff.lua b/CorsixTH/Lua/dialogs/hire_staff.lua index 844bec0d0..1f8ef058d 100644 --- a/CorsixTH/Lua/dialogs/hire_staff.lua +++ b/CorsixTH/Lua/dialogs/hire_staff.lua @@ -280,4 +280,9 @@ end function UIHireStaff:afterLoad(old, new) Window.afterLoad(self, old, new) self:registerKeyHandlers() + + if old < 236 then + self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.face_parts = TheApp.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { flags = DrawFlags.Nearest }) + end end diff --git a/CorsixTH/Lua/dialogs/jukebox.lua b/CorsixTH/Lua/dialogs/jukebox.lua index 771142579..527936c66 100644 --- a/CorsixTH/Lua/dialogs/jukebox.lua +++ b/CorsixTH/Lua/dialogs/jukebox.lua @@ -183,4 +183,8 @@ function UIJukebox:afterLoad(old, new) self:close() end Window.afterLoad(self, old, new) + if old < 236 then + self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, { apply_ui_scale = true }) + end end diff --git a/CorsixTH/Lua/dialogs/patient.lua b/CorsixTH/Lua/dialogs/patient.lua index ea92989c4..b7626119f 100644 --- a/CorsixTH/Lua/dialogs/patient.lua +++ b/CorsixTH/Lua/dialogs/patient.lua @@ -359,4 +359,8 @@ end function UIPatient:afterLoad(old, new) Window.afterLoad(self, old, new) self:registerKeyHandlers() + + if old < 236 then + self.font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font74V", nil, nil, { apply_ui_scale = true }) + end end diff --git a/CorsixTH/Lua/dialogs/place_objects.lua b/CorsixTH/Lua/dialogs/place_objects.lua index 74c081e72..ba78eb693 100644 --- a/CorsixTH/Lua/dialogs/place_objects.lua +++ b/CorsixTH/Lua/dialogs/place_objects.lua @@ -886,4 +886,9 @@ end function UIPlaceObjects:afterLoad(old, new) Window.afterLoad(self, old, new) UIPlaceObjects.registerKeyHandlers(self) + + if old < 236 then + self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.blue_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color, apply_ui_scale = true }) + end end diff --git a/CorsixTH/Lua/dialogs/staff_dialog.lua b/CorsixTH/Lua/dialogs/staff_dialog.lua index d6be59517..bff715fd4 100644 --- a/CorsixTH/Lua/dialogs/staff_dialog.lua +++ b/CorsixTH/Lua/dialogs/staff_dialog.lua @@ -406,4 +406,8 @@ function UIStaff:afterLoad(old, new) self:close() end Window.afterLoad(self, old, new) + if old < 236 then + self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) + self.face_parts = TheApp.gfx:loadRaw("Face01V", 65, 1350, nil, "Data", "MPalette.dat", false, { flags = DrawFlags.Nearest }) + end end From 37cc5c0ec53a9f70b1ae797660ee8d5e845c8442 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 16 Dec 2025 12:36:29 -0500 Subject: [PATCH 44/50] Fix missing variable for afterLoad place_objects --- CorsixTH/Lua/dialogs/place_objects.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/CorsixTH/Lua/dialogs/place_objects.lua b/CorsixTH/Lua/dialogs/place_objects.lua index ba78eb693..4274fbdec 100644 --- a/CorsixTH/Lua/dialogs/place_objects.lua +++ b/CorsixTH/Lua/dialogs/place_objects.lua @@ -888,6 +888,7 @@ function UIPlaceObjects:afterLoad(old, new) UIPlaceObjects.registerKeyHandlers(self) if old < 236 then + local selected_label_color = { red = 40, green = 40, blue = 250 } self.white_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font01V", nil, nil, { apply_ui_scale = true }) self.blue_font = TheApp.gfx:loadFontAndSpriteTable("QData", "Font02V", nil, nil, {ttf_color = selected_label_color, apply_ui_scale = true }) end From 85c067ab996bce2ed1db0b9eb6ac4edbe90014c9 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 16 Dec 2025 21:16:23 -0500 Subject: [PATCH 45/50] ui_scale town map --- CorsixTH/Lua/dialogs/fullscreen.lua | 56 ++++++----- CorsixTH/Lua/dialogs/fullscreen/town_map.lua | 99 ++++++++++---------- CorsixTH/Src/th_lua_ui.cpp | 25 +++-- 3 files changed, 98 insertions(+), 82 deletions(-) diff --git a/CorsixTH/Lua/dialogs/fullscreen.lua b/CorsixTH/Lua/dialogs/fullscreen.lua index 4b4abc178..9456144e6 100644 --- a/CorsixTH/Lua/dialogs/fullscreen.lua +++ b/CorsixTH/Lua/dialogs/fullscreen.lua @@ -44,7 +44,10 @@ end function UIFullscreen:onChangeResolution() local app = self.ui.app - if app.config.width > self.width or app.config.height > self.height then + local s = app.config.ui_scale + local sw = app.config.width / s + local sh = app.config.height / s + if sw > self.width or sh > self.height then if not self.border_sprites then self.border_sprites = app.gfx:loadSpriteTable("Bitmap", "aux_ui", true) end @@ -62,36 +65,37 @@ function UIFullscreen:onChangeResolution() end end - self.x = math.floor((app.config.width - self.width) / 2) + self.x = math.floor((sw - self.width) / 2) -- NB: Bottom panel is 48 pixels high - if app.config.height > 480 + 48 then - self.y = math.floor((app.config.height - 48 - self.height) / 2) + if sh > 480 + 48 then + self.y = math.floor((sh - 48 - self.height) / 2) elseif app.config.height >= 480 then self.y = 0 else - self.y = math.floor((app.config.height - self.height) / 2) + self.y = math.floor((sh - self.height) / 2) end end function UIFullscreen:draw(canvas, x, y) local sprites = self.border_sprites if sprites then + local s = TheApp.config.ui_scale local draw = sprites.draw - local scr_x = self.x + x - local scr_y = self.y + y + local scr_x = self.x * s + x + local scr_y = self.y * s + y canvas:nonOverlapping(true) - draw(sprites, canvas, 10, scr_x - 9, scr_y - 9) - draw(sprites, canvas, 12, scr_x + 600, scr_y - 9) - draw(sprites, canvas, 15, scr_x - 9, scr_y + 440) - draw(sprites, canvas, 17, scr_x + 600, scr_y + 440) - for loop_x = scr_x + 40, scr_x + 560, 40 do - draw(sprites, canvas, 11, loop_x, scr_y - 9) - draw(sprites, canvas, 16, loop_x, scr_y + 480) + draw(sprites, canvas, 10, scr_x - 9 * s, scr_y - 9 * s, { scaleFactor = s }) + draw(sprites, canvas, 12, scr_x + 600 * s, scr_y - 9 * s, { scaleFactor = s }) + draw(sprites, canvas, 15, scr_x - 9 * s, scr_y + 440 * s, { scaleFactor = s }) + draw(sprites, canvas, 17, scr_x + 600 * s, scr_y + 440 * s, { scaleFactor = s }) + for loop_x = scr_x + 40 * s, scr_x + 560 * s, 40 * s do + draw(sprites, canvas, 11, loop_x, scr_y - 9 * s, { scaleFactor = s }) + draw(sprites, canvas, 16, loop_x, scr_y + 480 * s, { scaleFactor = s }) end - for loop_y = scr_y + 40, scr_y + 400, 40 do - draw(sprites, canvas, 13, scr_x - 9, loop_y) - draw(sprites, canvas, 14, scr_x + 640, loop_y) + for loop_y = scr_y + 40 * s, scr_y + 400 * s, 40 * s do + draw(sprites, canvas, 13, scr_x - 9 * s, loop_y, { scaleFactor = s }) + draw(sprites, canvas, 14, scr_x + 640 * s, loop_y, { scaleFactor = s }) end canvas:nonOverlapping(false) end @@ -100,32 +104,34 @@ end function UIFullscreen:onMouseDown(button, x, y) local repaint = Window.onMouseDown(self, button, x, y) + local s = TheApp.config.ui_scale if button == "left" and not repaint and not (x >= 0 and y >= 0 and - x < self.width and y < self.height) and self:hitTest(x, y) then + x < self.width * s and y < self.height * s) and self:hitTest(x, y) then return self:beginDrag(x, y) end return repaint end function UIFullscreen:hitTest(x, y) - if x >= 0 and y >= 0 and x < self.width and y < self.height then + local s = TheApp.config.ui_scale + if x >= 0 and y >= 0 and x < self.width * s and y < self.height * s then return true end local sprites = self.border_sprites if not sprites then return false end - if x < -9 or y < -9 or x >= self.width + 9 or y >= self.height + 9 then + if x < -9 * s or y < -9 * s or x >= self.width * s + 9 * s or y >= self.height * s + 9 * s then return false end - if (0 <= x and x < self.width) or (0 <= y and y < self.height) then + if (0 <= x and x < self.width * s) or (0 <= y and y < self.height * s) then return true end - return sprites.hitTest(sprites, 10, x + 9, y + 9) or - sprites.hitTest(sprites, 12, x - 600, y + 9) or - sprites.hitTest(sprites, 15, x + 9, y - 440) or - sprites.hitTest(sprites, 17, x - 600, y - 440) + return sprites.hitTest(sprites, 10, x + 9 * s, y + 9 * s) or + sprites.hitTest(sprites, 12, x - 600 * s, y + 9 * s) or + sprites.hitTest(sprites, 15, x + 9 * s, y - 440 * s) or + sprites.hitTest(sprites, 17, x - 600 * s, y - 440 * s) end function UIFullscreen:afterLoad(old, new) diff --git a/CorsixTH/Lua/dialogs/fullscreen/town_map.lua b/CorsixTH/Lua/dialogs/fullscreen/town_map.lua index e71609c8e..6aaafee5a 100644 --- a/CorsixTH/Lua/dialogs/fullscreen/town_map.lua +++ b/CorsixTH/Lua/dialogs/fullscreen/town_map.lua @@ -37,9 +37,9 @@ function UITownMap:UITownMap(ui) local palette = gfx:loadPalette("QData", "Town01V.pal", true) self.background = gfx:loadRaw("Town01V", 640, 480, "QData", "QData", "Town01V.pal", true) - self.info_font = gfx:loadFontAndSpriteTable("QData", "Font34V", false, palette) - self.city_font = gfx:loadFontAndSpriteTable("QData", "Font31V", false, palette) - self.money_font = gfx:loadFontAndSpriteTable("QData", "Font05V") + self.info_font = gfx:loadFontAndSpriteTable("QData", "Font34V", false, palette, { apply_ui_scale = true }) + self.city_font = gfx:loadFontAndSpriteTable("QData", "Font31V", false, palette, { apply_ui_scale = true }) + self.money_font = gfx:loadFontAndSpriteTable("QData", "Font05V", nil, nil, { apply_ui_scale = true }) self.panel_sprites = gfx:loadSpriteTable("QData", "Town02V", true, palette) end) then ui:addWindow(UIInformation(ui, {_S.errors.dialog_missing_graphics})) @@ -108,8 +108,9 @@ end local flag_cache = {} function UITownMap:onMouseMove(x, y, dx, dy) - local tx = math.floor((x - 227) / 3) - local ty = math.floor((y - 25) / 3) + local s = TheApp.config.ui_scale + local tx = math.floor((x - 227 * s) / (3 * s)) + local ty = math.floor((y - 25 * s) / (3 * s)) self.hover_plot = nil local map = self.ui.hospital.world.map.th local width, height = map:size() @@ -121,9 +122,10 @@ end function UITownMap:onMouseUp(button, x, y) local redraw = false + local s = TheApp.config.ui_scale if button == "left" then - local tx = math.floor((x - 227) / 3) - local ty = math.floor((y - 25) / 3) + local tx = math.floor((x - 227 * s) / (3 * s)) + local ty = math.floor((y - 25 * s) / (3 * s)) local map = self.ui.hospital.world.map.th local width, height = map:size() if 0 <= tx and tx < width and 0 <= ty and ty < height then @@ -138,8 +140,8 @@ function UITownMap:onMouseUp(button, x, y) end end elseif button == "right" then - local tx = math.floor((x - 227) / 3) - local ty = math.floor((y - 25) / 3) + local tx = math.floor((x - 227 * s) / (3 * s)) + local ty = math.floor((y - 25 * s) / (3 * s)) local map = self.ui.hospital.world.map.th local width, height = map:size() if 0 <= tx and tx < width and 0 <= ty and ty < height then @@ -156,10 +158,13 @@ function UITownMap:onMouseUp(button, x, y) end function UITownMap:draw(canvas, x, y) - self.background:draw(canvas, self.x + x, self.y + y) + local s = TheApp.config.ui_scale + canvas:scale(s, "bitmap") + self.background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y local app = self.ui.app local hospital = self.ui.hospital local world = hospital.world @@ -181,51 +186,51 @@ function UITownMap:draw(canvas, x, y) local objs = hospital:countGeneralObjects() local radiators = hospital:countRadiators() - self.info_font:draw(canvas, patientcount, x + 95, y + 57) - self.info_font:draw(canvas, plants, x + 95, y + 110) - self.info_font:draw(canvas, fireext, x + 95, y + 157) - self.info_font:draw(canvas, objs, x + 95, y + 211) - self.info_font:draw(canvas, radiators, x + 95, y + 265) + self.info_font:draw(canvas, patientcount, x + 95 * s, y + 57 * s) + self.info_font:draw(canvas, plants, x + 95 * s, y + 110 * s) + self.info_font:draw(canvas, fireext, x + 95 * s, y + 157 * s) + self.info_font:draw(canvas, objs, x + 95 * s, y + 211 * s) + self.info_font:draw(canvas, radiators, x + 95 * s, y + 265 * s) -- Heating costs local heating_costs = world.free_build_mode and 0 or math.floor(((hospital.heating.radiator_heat *10)* radiators)* 7.5) - self.info_font:draw(canvas, ("%8i"):format(heating_costs), x + 100, y + 355) + self.info_font:draw(canvas, ("%8i"):format(heating_costs), x + 100 * s, y + 355 * s) -- Draw balance with temporary offset in unicode languages local offset_x, offset_y = 0, 0 if self.ui.app.gfx:drawNumbersFromUnicode() then - offset_x = 4 - offset_y = 2 + offset_x = 4 * s + offset_y = 2 * s end local balance = math.floor(hospital.balance) local i = 7 - tostring(balance):len() -- Indent balances under 100k for digit in ("%7i"):format(balance):gmatch("[-0-9]") do - self.money_font:draw(canvas, digit, x + offset_x + 49 + i * 13, y + offset_y + 431) + self.money_font:draw(canvas, digit, x + offset_x + 49 * s + i * 13 * s, y + offset_y + 431 * s) i = i + 1 end -- radiator heat - local rad_max_width = 60 -- Radiator indicator width + local rad_max_width = 60 * s -- Radiator indicator width local rad_width = rad_max_width * hospital.heating.radiator_heat - for dx = 0, rad_width do - self.panel_sprites:draw(canvas, 9, x + 101 + dx, y + 319) + for dx = 0, rad_width, s do + self.panel_sprites:draw(canvas, 9, x + 101 * s + dx, y + 319 * s, { scaleFactor = s }) end -- city name - self.city_font:draw(canvas, map.level_name, x + 300, y + 43, 260, 15) + self.city_font:draw(canvas, map.level_name, x + 300 * s, y + 43 * s, 260 * s, 15 * s) - local town_map_offset_x = x + 227 - local town_map_offset_y = y + 25 + local town_map_offset_x = x + 227 * s + local town_map_offset_y = y + 25 * s TH.windowHelpers.townMapDraw(self, map.th, canvas, town_map_offset_x, town_map_offset_y, - config.radiators_enabled) + config.radiators_enabled, s) -- Draw entities local function draw_entities(list, color, size) for _, ent in ipairs(list) do -- 3 is the number of pixel that are used to represent one world tile in the map - canvas:drawRect(color, town_map_offset_x + ent.tile_x * 3 - 2, - town_map_offset_y + ent.tile_y * 3 + 1, size, size) + canvas:drawRect(color, town_map_offset_x + ent.tile_x * 3 * s - 2 * s, + town_map_offset_y + ent.tile_y * 3 * s + s, size * s, size * s) end end @@ -234,8 +239,8 @@ function UITownMap:draw(canvas, x, y) local tile_x, tile_y = ent.tile_x, ent.tile_y if tile_x and hospital:isInHospital(tile_x, tile_y) then -- 3 is the number of pixel that are used to represent one world tile in the map - canvas:drawRect(color, town_map_offset_x + tile_x * 3 - 2, - town_map_offset_y + tile_y * 3 + 1, size, size) + canvas:drawRect(color, town_map_offset_x + tile_x * 3 * s - 2 * s, + town_map_offset_y + tile_y * 3 * s + s, size * s, size * s) end end end @@ -312,18 +317,18 @@ function UITownMap:draw(canvas, x, y) plot_num = self.hover_plot end end - self.city_font:draw(canvas, _S.town_map.number, x + 227, y + 435) - self.city_font:draw(canvas, ":", x + 300, y + 435) - self.city_font:draw(canvas, plot_num, x + 315, y + 435) - self.city_font:draw(canvas, _S.town_map.owner, x + 227, y + 450) - self.city_font:draw(canvas, ":", x + 300, y + 450) - self.city_font:draw(canvas, owner, x + 315, y + 450) - self.city_font:draw(canvas, _S.town_map.area, x + 432, y + 435) - self.city_font:draw(canvas, ":", x + 495, y + 435) - self.city_font:draw(canvas, tile_count, x + 515, y + 435) - self.city_font:draw(canvas, _S.town_map.price, x + 432, y + 450) - self.city_font:draw(canvas, ":", x + 495, y + 450) - self.city_font:draw(canvas, price, x + 515, y + 450) + self.city_font:draw(canvas, _S.town_map.number, x + 227 * s, y + 435 * s) + self.city_font:draw(canvas, ":", x + 300 * s, y + 435 * s) + self.city_font:draw(canvas, plot_num, x + 315 * s, y + 435 * s) + self.city_font:draw(canvas, _S.town_map.owner, x + 227 * s, y + 450 * s) + self.city_font:draw(canvas, ":", x + 300 * s, y + 450 * s) + self.city_font:draw(canvas, owner, x + 315 * s, y + 450 * s) + self.city_font:draw(canvas, _S.town_map.area, x + 432 * s, y + 435 * s) + self.city_font:draw(canvas, ":", x + 495 * s, y + 435 * s) + self.city_font:draw(canvas, tile_count, x + 515 * s, y + 435 * s) + self.city_font:draw(canvas, _S.town_map.price, x + 432 * s, y + 450 * s) + self.city_font:draw(canvas, ":", x + 495 * s, y + 450 * s) + self.city_font:draw(canvas, price, x + 515 * s, y + 450 * s) end function UITownMap:decreaseHeat() @@ -358,13 +363,13 @@ function UITownMap:bankStats() end function UITownMap:afterLoad(old, new) - if old < 179 then + if old < 236 then local gfx = TheApp.gfx local palette = gfx:loadPalette("QData", "Town01V.pal", true) self.background = gfx:loadRaw("Town01V", 640, 480, "QData", "QData", "Town01V.pal", true) - self.info_font = gfx:loadFontAndSpriteTable("QData", "Font34V", false, palette) - self.city_font = gfx:loadFontAndSpriteTable("QData", "Font31V", false, palette) - self.panel_sprites = gfx:loadSpriteTable("QData", "Town02V", true, palette) + self.info_font = gfx:loadFontAndSpriteTable("QData", "Font34V", false, palette, { apply_ui_scale = true }) + self.city_font = gfx:loadFontAndSpriteTable("QData", "Font31V", false, palette, { apply_ui_scale = true }) + self.panel_sprites = gfx:loadSpriteTable("QData", "Town02V", true, palette, { apply_ui_scale = true }) end UIFullscreen.afterLoad(self, old, new) diff --git a/CorsixTH/Src/th_lua_ui.cpp b/CorsixTH/Src/th_lua_ui.cpp index 2171c9292..1fea5e7f4 100644 --- a/CorsixTH/Src/th_lua_ui.cpp +++ b/CorsixTH/Src/th_lua_ui.cpp @@ -64,6 +64,7 @@ int l_town_map_draw(lua_State* L) { int iCanvasXBase = static_cast(luaL_checkinteger(L, 4)); int iCanvasYBase = static_cast(luaL_checkinteger(L, 5)); bool bShowHeat = lua_toboolean(L, 6) != 0; + int scale = static_cast(luaL_optinteger(L, 7, 1)); uint32_t iColourMyHosp = render_target::map_colour(0, 0, 70); uint32_t iColourWall = render_target::map_colour(255, 255, 255); @@ -72,12 +73,12 @@ int l_town_map_draw(lua_State* L) { const map_tile* pNode = pMap->get_tile_unchecked(0, 0); const map_tile* pOriginalNode = pMap->get_original_tile_unchecked(0, 0); - int iCanvasY = iCanvasYBase + 3; + int iCanvasY = iCanvasYBase + 3 * scale; int iMapWidth = pMap->get_width(); - for (int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3) { + for (int iY = 0; iY < pMap->get_height(); ++iY, iCanvasY += 3 * scale) { int iCanvasX = iCanvasXBase; for (int iX = 0; iX < iMapWidth; - ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3) { + ++iX, ++pNode, ++pOriginalNode, iCanvasX += 3 * scale) { if (pOriginalNode->flags.hospital) { uint32_t iColour = iColourMyHosp; if (!(pNode->flags.hospital)) { @@ -130,33 +131,37 @@ int l_town_map_draw(lua_State* L) { iColour = render_target::map_colour(iR, iG, iB); } - pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3, 3); + pCanvas->fill_rect(iColour, iCanvasX, iCanvasY, 3 * scale, 3 * scale); } dont_paint_tile: if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, tile_layer::north_wall)) { - pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3, 1); + pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 3 * scale, scale); // Draw entrance door auto l = (pNode - 1)->objects; if (!l.empty() && l.front() == object_type::entrance_right_door) { if (pNode->flags.hospital) { - pCanvas->fill_rect(iColourDoor, iCanvasX - 6, iCanvasY - 2, 9, 3); + pCanvas->fill_rect(iColourDoor, iCanvasX - 6 * scale, + iCanvasY - 2 * scale, 9 * scale, 3 * scale); } else { - pCanvas->fill_rect(iColourDoor, iCanvasX - 6, iCanvasY, 9, 3); + pCanvas->fill_rect(iColourDoor, iCanvasX - 6 * scale, iCanvasY, + 9 * scale, 3 * scale); } } } if (is_wall_drawn(*pMap, *pNode, *pOriginalNode, tile_layer::west_wall)) { - pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, 1, 3); + pCanvas->fill_rect(iColourWall, iCanvasX, iCanvasY, scale, 3 * scale); // Draw entrance door auto l = (pNode - iMapWidth)->objects; if (!l.empty() && l.front() == object_type::entrance_right_door) { if (pNode->flags.hospital) { - pCanvas->fill_rect(iColourDoor, iCanvasX - 2, iCanvasY - 6, 3, 9); + pCanvas->fill_rect(iColourDoor, iCanvasX - 2 * scale, + iCanvasY - 6 * scale, 3 * scale, 9 * scale); } else { - pCanvas->fill_rect(iColourDoor, iCanvasX, iCanvasY - 6, 3, 9); + pCanvas->fill_rect(iColourDoor, iCanvasX, iCanvasY - 6 * scale, + 3 * scale, 9 * scale); } } } From 5c401cfd693c62daee3c28d4484e6ebe28011060 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 16 Dec 2025 21:34:41 -0500 Subject: [PATCH 46/50] ui_scale fax --- CorsixTH/Lua/dialogs/fullscreen/fax.lua | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/CorsixTH/Lua/dialogs/fullscreen/fax.lua b/CorsixTH/Lua/dialogs/fullscreen/fax.lua index 95511c647..ae7dad47e 100644 --- a/CorsixTH/Lua/dialogs/fullscreen/fax.lua +++ b/CorsixTH/Lua/dialogs/fullscreen/fax.lua @@ -33,7 +33,7 @@ function UIFax:UIFax(ui, icon) self.background = gfx:loadRaw("Fax01V", 640, 480, "QData", "QData", "Fax01V.pal", true) local palette = gfx:loadPalette("QData", "Fax01V.pal", true) self.panel_sprites = gfx:loadSpriteTable("QData", "Fax02V", true, palette) - self.fax_font = gfx:loadFontAndSpriteTable("QData", "Font51V", false, palette) + self.fax_font = gfx:loadFontAndSpriteTable("QData", "Font51V", false, palette, { apply_ui_scale = true }) self.icon = icon self.message = icon.message or {} self.owner = icon.owner @@ -102,24 +102,27 @@ function UIFax:updateChoices() end function UIFax:draw(canvas, x, y) - self.background:draw(canvas, self.x + x, self.y + y) + local s = TheApp.config.ui_scale + canvas:scale(s, "bitmap") + self.background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y if self.message then - local last_y = y + 40 + local last_y = y + 40 * s for _, message in ipairs(self.message) do - last_y = self.fax_font:drawWrapped(canvas, message.text, x + 190, - last_y + (message.offset or 0), 360, + last_y = self.fax_font:drawWrapped(canvas, message.text, x + 190 * s, + last_y + (message.offset or 0), 360 * s, "center") end local choices = self.message.choices if choices then - local orig_y = y + 190 + local orig_y = y + 190 * s for i = 1, #choices do - last_y = orig_y + ((i - 1) + (3 - #choices)) * 48 - self.fax_font:drawWrapped(canvas, choices[i].text, x + 190, - last_y + (choices[i].offset or 0), 300) + last_y = orig_y + ((i - 1) + (3 - #choices)) * 48 * s + self.fax_font:drawWrapped(canvas, choices[i].text, x + 190 * s, + last_y + (choices[i].offset or 0), 300 * s) end end end @@ -292,12 +295,12 @@ function UIFax:close() end function UIFax:afterLoad(old, new) - if old < 179 then + if old < 236 then local gfx = TheApp.gfx self.background = gfx:loadRaw("Fax01V", 640, 480, "QData", "QData", "Fax01V.pal", true) local palette = gfx:loadPalette("QData", "Fax01V.pal", true) self.panel_sprites = gfx:loadSpriteTable("QData", "Fax02V", true, palette) - self.fax_font = gfx:loadFontAndSpriteTable("QData", "Font51V", false, palette) + self.fax_font = gfx:loadFontAndSpriteTable("QData", "Font51V", false, palette, { apply_ui_scale = true }) end UIFullscreen.afterLoad(self, old, new) if old < 59 then From db08e42a0cc03a5f5b1bea8ee9f9f9d8fefa2f81 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Tue, 16 Dec 2025 23:28:17 -0500 Subject: [PATCH 47/50] ui_scale annual reports --- .../Lua/dialogs/fullscreen/annual_report.lua | 108 +++++++++--------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/CorsixTH/Lua/dialogs/fullscreen/annual_report.lua b/CorsixTH/Lua/dialogs/fullscreen/annual_report.lua index 961aa61d4..46ec0ac79 100644 --- a/CorsixTH/Lua/dialogs/fullscreen/annual_report.lua +++ b/CorsixTH/Lua/dialogs/fullscreen/annual_report.lua @@ -60,9 +60,9 @@ function UIAnnualReport:UIAnnualReport(ui, world) self.background = self.stat_background local stone_text_color = { red = 80, green = 60, blue = 35 } - self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font45V", false, palette) - self.write_font = gfx:loadFontAndSpriteTable("QData", "Font47V", false, palette) - self.stone_font = gfx:loadFontAndSpriteTable("QData", "Font46V", false, palette, {ttf_color = stone_text_color}) + self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font45V", false, palette, { apply_ui_scale = true }) + self.write_font = gfx:loadFontAndSpriteTable("QData", "Font47V", false, palette, { apply_ui_scale = true }) + self.stone_font = gfx:loadFontAndSpriteTable("QData", "Font46V", false, palette, {ttf_color = stone_text_color, apply_ui_scale = true }) self.panel_sprites = gfx:loadSpriteTable("QData", "Award03V", true, palette) end) then @@ -510,10 +510,14 @@ function UIAnnualReport:changePage(page_no) end function UIAnnualReport:draw(canvas, x, y) - self.background:draw(canvas, self.x + x, self.y + y) + local s = TheApp.config.ui_scale + canvas:scale(s, "bitmap") + self.background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") + UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y local font = self.stat_font local world = self.ui.app.world @@ -524,18 +528,18 @@ function UIAnnualReport:draw(canvas, x, y) -- it no longer becomes accessible. -- Title and column names - font:draw(canvas, _S.high_score.best_scores, x + 220, y + 104, 200, 0) - font:draw(canvas, _S.high_score.pos, x + 218, y + 132) - font:draw(canvas, _S.high_score.player, x + 260, y + 132) - font:draw(canvas, _S.high_score.score, x + 360, y + 132) + font:draw(canvas, _S.high_score.best_scores, x + 220 * s, y + 104 * s, 200 * s, 0) + font:draw(canvas, _S.high_score.pos, x + 218 * s, y + 132 * s) + font:draw(canvas, _S.high_score.player, x + 260 * s, y + 132 * s) + font:draw(canvas, _S.high_score.score, x + 360 * s, y + 132 * s) -- Players and their score local i = 1 local dy = 0 --for i = 1, 10 do - font:draw(canvas, i .. ".", x + 220, y + 160 + dy) - font:draw(canvas, world:getLocalPlayerHospital().name:upper(), x + 260, y + 160 + dy) - font:draw(canvas, "NA", x + 360, y + 160 + dy) + font:draw(canvas, i .. ".", x + 220 * s, y + 160 * s + dy) + font:draw(canvas, world:getLocalPlayerHospital().name:upper(), x + 260 * s, y + 160 * s + dy) + font:draw(canvas, "NA", x + 360 * s, y + 160 * s + dy) -- dy = dy + 25 --end elseif self.state == 2 then -- Statistics screen @@ -546,26 +550,26 @@ function UIAnnualReport:draw(canvas, x, y) if self.trophy_motivation then -- If it is a plaque showing we write in stone text. local info = self.trophies[self.trophy_motivation].info - self.stone_font:drawWrapped(canvas, info.text, x + 225, y + 105, 185, "center") + self.stone_font:drawWrapped(canvas, info.text, x + 225 * s, y + 105 * s, 185 * s, "center") -- Type of award local award_type = _S.trophy_room.cash if info.award_type == "reputation" then award_type = _S.trophy_room.reputation end - self.stone_font:draw(canvas, award_type, x + 220, y + 330, 200, 0) + self.stone_font:draw(canvas, award_type, x + 220 * s, y + 330 * s, 200 * s, 0) -- Amount won/lost - self.stone_font:draw(canvas, string.format("+%.0f", info.amount), x + 220, y + 355, 200, 0) + self.stone_font:draw(canvas, string.format("+%.0f", info.amount), x + 220 * s, y + 355 * s, 200 * s, 0) elseif self.award_motivation then local info = self.awards[self.award_motivation].info - self.write_font:drawWrapped(canvas, info.text, x + 235, y + 125, 165, "center") + self.write_font:drawWrapped(canvas, info.text, x + 235 * s, y + 125 * s, 165 * s, "center") -- Type of award local award_type = _S.trophy_room.cash if info.award_type == "reputation" then award_type = _S.trophy_room.reputation end - self.write_font:draw(canvas, award_type, x + 220, y + 290, 200, 0) + self.write_font:draw(canvas, award_type, x + 220 * s, y + 290 * s, 200 * s, 0) -- The amount won/lost - self.write_font:draw(canvas, string.format("%+.0f", info.amount), x + 220, y + 315, 200, 0) + self.write_font:draw(canvas, string.format("%+.0f", info.amount), x + 220 * s, y + 315 * s, 200 * s, 0) end end end @@ -574,16 +578,17 @@ function UIAnnualReport:drawStatisticsScreen(canvas, x, y) local font = self.stat_font local world = self.ui.app.world + local s = TheApp.config.ui_scale -- Draw titles -- world date year is + 1, so adding it to 1998 realigns it - font:draw(canvas, _S.menu.charts .. " " .. (world:date():year() + 1998), x + 210, y + 30, 200, 0) - font:draw(canvas, _S.high_score.categories.money, x + 140, y + 98, 170, 0) - font:draw(canvas, _S.high_score.categories.salary, x + 328, y + 98, 170, 0) - font:draw(canvas, _S.high_score.categories.cures, x + 140, y + 205, 170, 0) - font:draw(canvas, _S.high_score.categories.deaths, x + 328, y + 205, 170, 0) - font:draw(canvas, _S.high_score.categories.visitors, x + 140, y + 310, 170, 0) - font:draw(canvas, _S.high_score.categories.total_value, x + 328, y + 310, 170, 0) + font:draw(canvas, _S.menu.charts .. " " .. (world:date():year() + 1998), x + 210 * s, y + 30 * s, 200 * s, 0) + font:draw(canvas, _S.high_score.categories.money, x + 140 * s, y + 98 * s, 170 * s, 0) + font:draw(canvas, _S.high_score.categories.salary, x + 328 * s, y + 98 * s, 170 * s, 0) + font:draw(canvas, _S.high_score.categories.cures, x + 140 * s, y + 205 * s, 170 * s, 0) + font:draw(canvas, _S.high_score.categories.deaths, x + 328 * s, y + 205 * s, 170 * s, 0) + font:draw(canvas, _S.high_score.categories.visitors, x + 140 * s, y + 310 * s, 170 * s, 0) + font:draw(canvas, _S.high_score.categories.total_value, x + 328 * s, y + 310 * s, 170 * s, 0) -- TODO: Add possibility to right align text. @@ -597,10 +602,10 @@ function UIAnnualReport:drawStatisticsScreen(canvas, x, y) end end - local row_y = 128 - local row_dy = 15 - local col_x = 190 - local row_no_y = 106 + local row_y = 128 * s + local row_dy = 15 * s + local col_x = 190 * s + local row_no_y = 106 * s for i, hospital in ipairs(world.hospitals) do local name = hospital.name @@ -611,59 +616,60 @@ function UIAnnualReport:drawStatisticsScreen(canvas, x, y) -- index_* is the returned value of the sorted place for this player. -- However there might be many players with the same value, so each iteration a -- duplicate has been found, one additional row lower is the right place to be. - font:draw(canvas, name:upper(), x + 140, + font:draw(canvas, name:upper(), x + 140 * s, y + row_y + row_dy * (index_m - 1)) - font:draw(canvas, string.format("%.0f", self.money_sort[index_m].value), x + 240, - y + row_y + row_dy * (index_m - 1), 70, 0, "right") + font:draw(canvas, string.format("%.0f", self.money_sort[index_m].value), x + 240 * s, + y + row_y + row_dy * (index_m - 1), 70 * s, 0, "right") -- Highest Salary local index_s = getindex(self.salary_sort, i) - font:draw(canvas, name:upper(), x + 140 + col_x, + font:draw(canvas, name:upper(), x + 140 * s + col_x, y + row_y + row_dy * (index_s - 1)) - font:draw(canvas, string.format("%.0f", self.salary_sort[index_s].value), x + 240 + col_x, - y + row_y + row_dy * (index_s - 1), 70, 0, "right") + font:draw(canvas, string.format("%.0f", self.salary_sort[index_s].value), x + 240 * s + col_x, + y + row_y + row_dy * (index_s - 1), 70 * s, 0, "right") -- Most Cures local index_c = getindex(self.cures_sort, i) - font:draw(canvas, name:upper(), x + 140, + font:draw(canvas, name:upper(), x + 140 * s, y + row_y + row_no_y + row_dy * (index_c - 1)) - font:draw(canvas, string.format("%.0f", self.cures_sort[index_c].value), x + 240, - y + row_y + row_no_y + row_dy * (index_c - 1), 70, 0, "right") + font:draw(canvas, string.format("%.0f", self.cures_sort[index_c].value), x + 240 * s, + y + row_y + row_no_y + row_dy * (index_c - 1), 70 * s, 0, "right") -- Most Deaths local index_d = getindex(self.deaths_sort, i) - font:draw(canvas, name:upper(), x + 140 + col_x, + font:draw(canvas, name:upper(), x + 140 * s + col_x, y + row_y + row_no_y + row_dy * (index_d - 1)) - font:draw(canvas, string.format("%.0f", self.deaths_sort[index_d].value), x + 240 + col_x, - y + row_y + row_no_y + row_dy * (index_d - 1), 70, 0, "right") + font:draw(canvas, string.format("%.0f", self.deaths_sort[index_d].value), x + 240 * s + col_x, + y + row_y + row_no_y + row_dy * (index_d - 1), 70 * s, 0, "right") -- Most Visitors local index_v = getindex(self.visitors_sort, i) - font:draw(canvas, name:upper(), x + 140, + font:draw(canvas, name:upper(), x + 140 * s, y + row_y + row_no_y * 2 + row_dy * (index_v - 1)) - font:draw(canvas, string.format("%.0f", self.visitors_sort[index_v].value), x + 240, - y + row_y + row_no_y * 2 + row_dy * (index_v - 1), 70, 0, "right") + font:draw(canvas, string.format("%.0f", self.visitors_sort[index_v].value), x + 240 * s, + y + row_y + row_no_y * 2 + row_dy * (index_v - 1), 70 * s, 0, "right") -- Highest Value local index_v2 = getindex(self.value_sort, i) - font:draw(canvas, name:upper(), x + 140 + col_x, + font:draw(canvas, name:upper(), x + 140 * s + col_x, y + row_y + row_no_y * 2 + row_dy * (index_v2 - 1)) - font:draw(canvas, string.format("%.0f", self.value_sort[index_v2].value), x + 240 + col_x, - y + row_y + row_no_y * 2 + row_dy * (index_v2 - 1), 70, 0, "right") + font:draw(canvas, string.format("%.0f", self.value_sort[index_v2].value), x + 240 * s + col_x, + y + row_y + row_no_y * 2 + row_dy * (index_v2 - 1), 70 * s, 0, "right") end end function UIAnnualReport:afterLoad(old, new) - if old < 179 then + if old < 236 then local gfx = TheApp.gfx local palette = gfx:loadPalette("QData", "Award02V.pal", true) self.award_background = gfx:loadRaw("Award01V", 640, 480) self.stat_background = gfx:loadRaw("Award02V", 640, 480, "QData", "QData", "Award02V.pal", true) self.background = self.stat_background - self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font45V", false, palette) - self.write_font = gfx:loadFontAndSpriteTable("QData", "Font47V", false, palette) - self.stone_font = gfx:loadFontAndSpriteTable("QData", "Font46V", false, palette) + local stone_text_color = { red = 80, green = 60, blue = 35 } + self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font45V", false, palette, { apply_ui_scale = true }) + self.write_font = gfx:loadFontAndSpriteTable("QData", "Font47V", false, palette, { apply_ui_scale = true }) + self.stone_font = gfx:loadFontAndSpriteTable("QData", "Font46V", false, palette, {ttf_color = stone_text_color, apply_ui_scale = true }) self.panel_sprites = gfx:loadSpriteTable("QData", "Award03V", true, palette) end From e6cbcd59f77d045226b1f8c15d68b377b7481de0 Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sat, 20 Dec 2025 09:50:49 -0500 Subject: [PATCH 48/50] ui_scale bank_manager --- .../Lua/dialogs/fullscreen/bank_manager.lua | 107 ++++++++++-------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/CorsixTH/Lua/dialogs/fullscreen/bank_manager.lua b/CorsixTH/Lua/dialogs/fullscreen/bank_manager.lua index 21c07d5ab..8e44eff27 100644 --- a/CorsixTH/Lua/dialogs/fullscreen/bank_manager.lua +++ b/CorsixTH/Lua/dialogs/fullscreen/bank_manager.lua @@ -33,11 +33,11 @@ function UIBankManager:UIBankManager(ui) self.stat_background = gfx:loadRaw("Stat01V", 640, 480, "QData", "QData", "Stat01V.pal", true) local palette = gfx:loadPalette("QData", "Bank01V.pal", true) self.panel_sprites = gfx:loadSpriteTable("QData", "Bank02V", true, palette) - self.font = gfx:loadFontAndSpriteTable("QData", "Font36V", false, palette) + self.font = gfx:loadFontAndSpriteTable("QData", "Font36V", false, palette, { apply_ui_scale = true }) -- The statistics font palette = gfx:loadPalette("QData", "Stat01V.pal", true) - self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font37V", false, palette) + self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font37V", false, palette, { apply_ui_scale = true }) end) then ui:addWindow(UIInformation(ui, {_S.errors.dialog_missing_graphics})) self:close() @@ -119,16 +119,16 @@ function UIBankManager:afterLoad(old, new) self.eyesblink = self:addPanel(7, 298, 173) self.browslift = self:addPanel(9, 296, 165) end - if old < 179 then + if old < 236 then local gfx = TheApp.gfx self.background = gfx:loadRaw("Bank01V", 640, 480, "QData", "QData", "Bank01V.pal", true) self.stat_background = gfx:loadRaw("Stat01V", 640, 480, "QData", "QData", "Stat01V.pal", true) local palette = gfx:loadPalette("QData", "Bank01V.pal", true) self.panel_sprites = gfx:loadSpriteTable("QData", "Bank02V", true, palette) - self.font = gfx:loadFontAndSpriteTable("QData", "Font36V", false, palette) + self.font = gfx:loadFontAndSpriteTable("QData", "Font36V", false, palette, { apply_ui_scale = true }) palette = gfx:loadPalette("QData", "Stat01V.pal", true) - self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font37V", false, palette) + self.stat_font = gfx:loadFontAndSpriteTable("QData", "Font37V", false, palette, { apply_ui_scale = true }) end UIFullscreen.afterLoad(self, old, new) end @@ -226,20 +226,23 @@ end function UIBankManager:draw(canvas, x, y) local hospital = self.ui.hospital + local s = TheApp.config.ui_scale -- Either draw the statistics page or the normal bank page if self.showingStatistics then local font = self.stat_font - self.stat_background:draw(canvas, self.x + x, self.y + y) + canvas:scale(s, "bitmap") + self.stat_background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y -- Titles - font:draw(canvas, _S.bank_manager.statistics_page.date, x + 44, y + 37, 65, 0) - font:draw(canvas, _S.bank_manager.statistics_page.details, x + 125, y + 40, 230, 0) - font:draw(canvas, _S.bank_manager.statistics_page.money_out, x + 373, y + 42, 70, 0) - font:draw(canvas, _S.bank_manager.statistics_page.money_in, x + 449, y + 41, 70, 0) - font:draw(canvas, _S.bank_manager.statistics_page.balance, x + 525, y + 40, 70, 0) + font:draw(canvas, _S.bank_manager.statistics_page.date, x + 44 * s, y + 37 * s, 65 * s, 0) + font:draw(canvas, _S.bank_manager.statistics_page.details, x + 125 * s, y + 40 * s, 230 * s, 0) + font:draw(canvas, _S.bank_manager.statistics_page.money_out, x + 373 * s, y + 42 * s, 70 * s, 0) + font:draw(canvas, _S.bank_manager.statistics_page.money_in, x + 449 * s, y + 41 * s, 70 * s, 0) + font:draw(canvas, _S.bank_manager.statistics_page.balance, x + 525 * s, y + 40 * s, 70 * s, 0) --[[ Lua < 5.3 stored integer money amount in floating point values. @@ -258,67 +261,71 @@ function UIBankManager:draw(canvas, x, y) -- A for loop going backwards for no = 1, #hospital.transactions do local values = hospital.transactions[#hospital.transactions - no + 1] - local current_y = no * 15 + y + 60 - font:draw(canvas, _S.date_format.daymonth:format(values.day, values.month), x + 48, current_y) - font:draw(canvas, values.desc, x + 129, current_y) + local current_y = no * 15 * s + y + 60 * s + font:draw(canvas, _S.date_format.daymonth:format(values.day, values.month), x + 48 * s, current_y) + font:draw(canvas, values.desc, x + 129 * s, current_y) if values.spend then - font:draw(canvas, "$ " .. math.floor(values.spend), x + 377, current_y) + font:draw(canvas, "$ " .. math.floor(values.spend), x + 377 * s, current_y) else - font:draw(canvas, "$ " .. math.floor(values.receive), x + 453, current_y) + font:draw(canvas, "$ " .. math.floor(values.receive), x + 453 * s, current_y) end - font:draw(canvas, "$ " .. math.floor(values.balance), x + 529, current_y) + font:draw(canvas, "$ " .. math.floor(values.balance), x + 529 * s, current_y) end -- Summary - font:draw(canvas, _S.bank_manager.statistics_page.current_balance, x + 373, y + 420, 140, 0) - font:draw(canvas, string.format("$%.0f", hospital.balance), x + 526, y + 421, 70, 0) + font:draw(canvas, _S.bank_manager.statistics_page.current_balance, x + 373 * s, y + 420 * s, 140 * s, 0) + font:draw(canvas, string.format("$%.0f", hospital.balance), x + 526 * s, y + 421 * s, 70 * s, 0) else local font = self.font - self.background:draw(canvas, self.x + x, self.y + y) + canvas:scale(s, "bitmap") + self.background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y -- The left side - font:draw(canvas, _S.bank_manager.hospital_value, x + 60, y + 109, 143, 0) - font:draw(canvas, "$ " .. math.floor(hospital.value), x + 60, y + 139, 143, 0) - font:draw(canvas, _S.bank_manager.balance, x + 60, y + 174, 143, 0) - font:draw(canvas, "$ " .. math.floor(hospital.balance), x + 60, y + 204, 143, 0) - font:draw(canvas, _S.bank_manager.current_loan, x + 60, y + 239, 143, 0) - font:draw(canvas, "$ " .. math.floor(hospital.loan), x + 60, y + 269, 143, 0) - font:draw(canvas, _S.bank_manager.interest_payment, x + 60, y + 305, 143, 0) + font:draw(canvas, _S.bank_manager.hospital_value, x + 60 * s, y + 109 * s, 143 * s, 0) + font:draw(canvas, "$ " .. math.floor(hospital.value), x + 60 * s, y + 139 * s, 143 * s, 0) + font:draw(canvas, _S.bank_manager.balance, x + 60 * s, y + 174 * s, 143 * s, 0) + font:draw(canvas, "$ " .. math.floor(hospital.balance), x + 60 * s, y + 204 * s, 143 * s, 0) + font:draw(canvas, _S.bank_manager.current_loan, x + 60 * s, y + 239 * s, 143 * s, 0) + font:draw(canvas, "$ " .. math.floor(hospital.loan), x + 60 * s, y + 269 * s, 143 * s, 0) + font:draw(canvas, _S.bank_manager.interest_payment, x + 60 * s, y + 305 * s, 143 * s, 0) local interest = math.floor(hospital.loan * hospital.interest_rate / 12) - font:draw(canvas, "$ " .. interest, x + 60, y + 334, 143, 0) + font:draw(canvas, "$ " .. interest, x + 60 * s, y + 334 * s, 143 * s, 0) -- The right side - font:draw(canvas, _S.bank_manager.insurance_owed, x + 430, y + 102, 158, 0) + font:draw(canvas, _S.bank_manager.insurance_owed, x + 430 * s, y + 102 * s, 158 * s, 0) if self.graph.visible then - font:draw(canvas, hospital.insurance[self.chosen_insurance], x + 430, y + 132, 158, 0) + font:draw(canvas, hospital.insurance[self.chosen_insurance], x + 430 * s, y + 132 * s, 158 * s, 0) else - font:draw(canvas, hospital.insurance[1], x + 430, y + 132, 158, 0) - font:draw(canvas, "$ ".. math.floor(sum(hospital.insurance_balance[1])), x + 430, y + 162, 100, 0) - font:draw(canvas, hospital.insurance[2], x + 430, y + 192, 158, 0) - font:draw(canvas, "$ ".. math.floor(sum(hospital.insurance_balance[2])), x + 430, y + 222, 100, 0) - font:draw(canvas, hospital.insurance[3], x + 430, y + 252, 158, 0) - font:draw(canvas, "$ ".. math.floor(sum(hospital.insurance_balance[3])), x + 430, y + 282, 100, 0) + font:draw(canvas, hospital.insurance[1], x + 430 * s, y + 132 * s, 158 * s, 0) + font:draw(canvas, "$ ".. math.floor(sum(hospital.insurance_balance[1])), x + 430 * s, y + 162 * s, 100 * s, 0) + font:draw(canvas, hospital.insurance[2], x + 430 * s, y + 192 * s, 158 * s, 0) + font:draw(canvas, "$ ".. math.floor(sum(hospital.insurance_balance[2])), x + 430 * s, y + 222 * s, 100 * s, 0) + font:draw(canvas, hospital.insurance[3], x + 430 * s, y + 252 * s, 158 * s, 0) + font:draw(canvas, "$ ".. math.floor(sum(hospital.insurance_balance[3])), x + 430 * s, y + 282 * s, 100 * s, 0) end - font:draw(canvas, _S.bank_manager.inflation_rate, x + 430, y + 312, 100, 0) - font:draw(canvas, hospital.inflation_rate*100 .. " %", x + 550, y + 313, 38, 0) - font:draw(canvas, _S.bank_manager.interest_rate, x + 430, y + 342, 100, 0) - font:draw(canvas, hospital.interest_rate*100 .. " %", x + 550, y + 342, 38, 0) + font:draw(canvas, _S.bank_manager.inflation_rate, x + 430 * s, y + 312 * s, 100 * s, 0) + font:draw(canvas, hospital.inflation_rate * 100 .. " %", x + 550 * s, y + 313 * s, 38 * s, 0) + font:draw(canvas, _S.bank_manager.interest_rate, x + 430 * s, y + 342 * s, 100 * s, 0) + font:draw(canvas, hospital.interest_rate * 100 .. " %", x + 550 * s, y + 342 * s, 38 * s, 0) end end function UIBankManager:onMouseMove(x, y, dx, dy) - local ui = self.ui - if x > 0 and x < 640 and y > 0 and y < 480 then - if self.showingStatistics then - ui:setCursor(ui.app.gfx:loadMainCursor("banksummary")) -- Set pie chart cursor - else - ui:setCursor(ui.app.gfx:loadMainCursor("bank")) -- Set dollar cursor - end + local ui = self.ui + local s = TheApp.config.ui_scale + + if x > 0 and x < 640 * s and y > 0 and y < 480 * s then + if self.showingStatistics then + ui:setCursor(ui.app.gfx:loadMainCursor("banksummary")) -- Set pie chart cursor else - ui:setCursor(ui.default_cursor) -- Return to default cursor + ui:setCursor(ui.app.gfx:loadMainCursor("bank")) -- Set dollar cursor end + else + ui:setCursor(ui.default_cursor) -- Return to default cursor + end end function UIBankManager:close() From d20aab6d4e75796b84720673391e2db7c29181fe Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sat, 20 Dec 2025 11:28:51 -0500 Subject: [PATCH 49/50] ui_scale drug_casebook --- .../Lua/dialogs/fullscreen/drug_casebook.lua | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/CorsixTH/Lua/dialogs/fullscreen/drug_casebook.lua b/CorsixTH/Lua/dialogs/fullscreen/drug_casebook.lua index 1e84b8d40..cc3a4d843 100644 --- a/CorsixTH/Lua/dialogs/fullscreen/drug_casebook.lua +++ b/CorsixTH/Lua/dialogs/fullscreen/drug_casebook.lua @@ -30,10 +30,10 @@ function UICasebook:UICasebook(ui, disease_selection) if not pcall(function() self.background = gfx:loadRaw("DrugN01V", 640, 480, "QData", "QData", "DrugN01V.pal", true) local palette = gfx:loadPalette("QData", "DrugN01V.pal", true) - self.panel_sprites = gfx:loadSpriteTable("QData", "DrugN02V", true, palette) - self.title_font = gfx:loadFontAndSpriteTable("QData", "Font25V", false, palette) - self.selected_title_font = gfx:loadFontAndSpriteTable("QData", "Font26V", false, palette) - self.drug_font = gfx:loadFontAndSpriteTable("QData", "Font24V", false, palette) + self.panel_sprites = gfx:loadSpriteTable("QData", "DrugN02V", true, palette, { apply_ui_scale = true }) + self.title_font = gfx:loadFontAndSpriteTable("QData", "Font25V", false, palette, { apply_ui_scale = true }) + self.selected_title_font = gfx:loadFontAndSpriteTable("QData", "Font26V", false, palette, { apply_ui_scale = true }) + self.drug_font = gfx:loadFontAndSpriteTable("QData", "Font24V", false, palette, { apply_ui_scale = true }) end) then ui:addWindow(UIInformation(ui, {_S.errors.dialog_missing_graphics})) self:close() @@ -201,30 +201,33 @@ function UICasebook:updateIcons() end function UICasebook:draw(canvas, x, y) - self.background:draw(canvas, self.x + x, self.y + y) + local s = TheApp.config.ui_scale + canvas:scale(s, "bitmap") + self.background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y + x, y = self.x * s + x, self.y * s + y local titles = self.title_font local book = self.casebook local disease = self.selected_disease local selected = self.selected_index -- All titles - titles:draw(canvas, _S.casebook.reputation, x + 278, y + 68) - titles:draw(canvas, _S.casebook.treatment_charge, x + 260, y + 113) - titles:draw(canvas, _S.casebook.earned_money, x + 265, y + 157) - titles:draw(canvas, _S.casebook.cured, x + 276, y + 201) - titles:draw(canvas, _S.casebook.deaths, x + 279, y + 245) - titles:draw(canvas, _S.casebook.sent_home, x + 270, y + 289) - titles:draw(canvas, _S.casebook.cure, x + 255, y + 354) + titles:draw(canvas, _S.casebook.reputation, x + 278 * s, y + 68 * s) + titles:draw(canvas, _S.casebook.treatment_charge, x + 260 * s, y + 113 * s) + titles:draw(canvas, _S.casebook.earned_money, x + 265 * s, y + 157 * s) + titles:draw(canvas, _S.casebook.cured, x + 276 * s, y + 201 * s) + titles:draw(canvas, _S.casebook.deaths, x + 279 * s, y + 245 * s) + titles:draw(canvas, _S.casebook.sent_home, x + 270 * s, y + 289 * s) + titles:draw(canvas, _S.casebook.cure, x + 255 * s, y + 354 * s) -- Specific disease information if self.hospital:canConcentrateResearch(disease) then if book[disease].concentrate_research then -- Concentrate research - self.selected_title_font:draw(canvas, _S.casebook.research, x + 245, y + 398) + self.selected_title_font:draw(canvas, _S.casebook.research, x + 245 * s, y + 398 * s) else - titles:draw(canvas, _S.casebook.research, x + 245, y + 398) + titles:draw(canvas, _S.casebook.research, x + 245 * s, y + 398 * s) end end local rep = book[disease].reputation or self.hospital.reputation @@ -234,32 +237,32 @@ function UICasebook:draw(canvas, x, y) rep = self.hospital.reputation_max end - titles:draw(canvas, rep, x + 248, y + 92, 114, 0) -- Reputation + titles:draw(canvas, rep, x + 248 * s, y + 92 * s, 114 * s, 0) -- Reputation -- Treatment Charge is either displayed in percent, or normally local price_text = self.percentage_counter and ("%.0f%%"):format(book[disease].price * 100) or "$" .. self.hospital:getTreatmentPrice(disease) - titles:draw(canvas, price_text, x + 262, y + 137, 90, 0) -- Treatment Charge + titles:draw(canvas, price_text, x + 262 * s, y + 137 * s, 90 * s, 0) -- Treatment Charge - titles:draw(canvas, "$" .. book[disease].money_earned, x + 248, y + 181, 114, 0) -- Money Earned - titles:draw(canvas, book[disease].recoveries, x + 248, y + 225, 114, 0) -- Recoveries - titles:draw(canvas, book[disease].fatalities, x + 248, y + 269, 114, 0) -- Fatalities - titles:draw(canvas, book[disease].turned_away, x + 248, y + 313, 114, 0) -- Turned away + titles:draw(canvas, "$" .. book[disease].money_earned, x + 248 * s, y + 181 * s, 114 * s, 0) -- Money Earned + titles:draw(canvas, book[disease].recoveries, x + 248 * s, y + 225 * s, 114 * s, 0) -- Recoveries + titles:draw(canvas, book[disease].fatalities, x + 248 * s, y + 269 * s, 114 * s, 0) -- Fatalities + titles:draw(canvas, book[disease].turned_away, x + 248 * s, y + 313 * s, 114 * s, 0) -- Turned away -- Cure percentage if self.drug.visible then - self.drug_font:draw(canvas, book[disease].cure_effectiveness, x + 313, y + 364, 16, 0) + self.drug_font:draw(canvas, book[disease].cure_effectiveness, x + 313 * s, y + 364 * s, 16 * s, 0) end -- Right-hand side list of diseases (and pseudo diseases) local index = 1 while selected - index > 0 and index <= 7 do - titles:draw(canvas, book[self.names_sorted[selected - index]].disease.name:upper(), x + 409, y + 203 - index*18) + titles:draw(canvas, book[self.names_sorted[selected - index]].disease.name:upper(), x + 409 * s, y + 203 * s - index * 18 * s) index = index + 1 end - self.selected_title_font:draw(canvas, book[disease].disease.name:upper(), x + 409, y + 227) + self.selected_title_font:draw(canvas, book[disease].disease.name:upper(), x + 409 * s, y + 227 * s) index = 1 while index + selected <= #self.names_sorted and index <= 7 do - titles:draw(canvas, book[self.names_sorted[index + selected]].disease.name:upper(), x + 409, y + 251 + index*18) + titles:draw(canvas, book[self.names_sorted[index + selected]].disease.name:upper(), x + 409 * s, y + 251 * s + index * 18 * s) index = index + 1 end end @@ -341,16 +344,17 @@ function UICasebook:concentrateResearch() end function UICasebook:onMouseDown(button, x, y) + local s = TheApp.config.ui_scale -- Normal window operations if outside the disease list - if x < 395 or x > 540 or y < 77 or y > 394 then + if x < 395 * s or x > 540 * s or y < 77 * s or y > 394 * s then return UIFullscreen.onMouseDown(self, button, x, y) end local index_diff - if y < 203 then - index_diff = -7 + math.floor((y - 77) / 18) - elseif y > 269 then - index_diff = math.floor((y - 269) / 18) + 1 + if y < 203 * s then + index_diff = -7 + math.floor((y - (77 * s)) / (18 * s)) + elseif y > 269 * s then + index_diff = math.floor((y - (269 * s)) / (18 * s)) + 1 else return end @@ -394,14 +398,14 @@ function UICasebook:onTick() end function UICasebook:afterLoad(old, new) - if old < 179 then + if old < 236 then local gfx = TheApp.gfx self.background = gfx:loadRaw("DrugN01V", 640, 480, "QData", "QData", "DrugN01V.pal", true) local palette = gfx:loadPalette("QData", "DrugN01V.pal", true) - self.panel_sprites = gfx:loadSpriteTable("QData", "DrugN02V", true, palette) - self.title_font = gfx:loadFontAndSpriteTable("QData", "Font25V", false, palette) - self.selected_title_font = gfx:loadFontAndSpriteTable("QData", "Font26V", false, palette) - self.drug_font = gfx:loadFontAndSpriteTable("QData", "Font24V", false, palette) + self.panel_sprites = gfx:loadSpriteTable("QData", "DrugN02V", true, palette, { apply_ui_scale = true }) + self.title_font = gfx:loadFontAndSpriteTable("QData", "Font25V", false, palette, { apply_ui_scale = true }) + self.selected_title_font = gfx:loadFontAndSpriteTable("QData", "Font26V", false, palette, { apply_ui_scale = true }) + self.drug_font = gfx:loadFontAndSpriteTable("QData", "Font24V", false, palette, { apply_ui_scale = true }) end UIFullscreen.afterLoad(self, old, new) From 3015afec2d3240e6a2c793044d896df21735500e Mon Sep 17 00:00:00 2001 From: "Stephen E. Baker" Date: Sat, 20 Dec 2025 11:29:06 -0500 Subject: [PATCH 50/50] ui_scale graphs --- CorsixTH/Lua/dialogs/fullscreen/graphs.lua | 82 +++++++++++++--------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/CorsixTH/Lua/dialogs/fullscreen/graphs.lua b/CorsixTH/Lua/dialogs/fullscreen/graphs.lua index ca93e87f2..b79a94eec 100644 --- a/CorsixTH/Lua/dialogs/fullscreen/graphs.lua +++ b/CorsixTH/Lua/dialogs/fullscreen/graphs.lua @@ -46,8 +46,8 @@ function UIGraphs:UIGraphs(ui) self.background = gfx:loadRaw("Graph01V", 640, 480, "QData", "QData", "Graph01V.pal", true) local palette = gfx:loadPalette("QData", "Graph01V.pal", true) self.panel_sprites = gfx:loadSpriteTable("QData", "Graph02V", true, palette) - self.white_font = gfx:loadFontAndSpriteTable("QData", "Font01V", false, palette) - self.black_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette) + self.white_font = gfx:loadFontAndSpriteTable("QData", "Font01V", false, palette, { apply_ui_scale = true }) + self.black_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette, { apply_ui_scale = true }) end) then ui:addWindow(UIInformation(ui, {_S.errors.dialog_missing_graphics})) self:close() @@ -279,6 +279,7 @@ local function updateTextPositions(graph) end function UIGraphs:updateLines() + local s = TheApp.config.ui_scale self.values = self:getHospitalStatistics() -- Construct meta data about each graph line. @@ -304,7 +305,7 @@ function UIGraphs:updateLines() -- Add the line objects of the graph. for stat, graph_data in pairs(self.graph_datas) do local line = TH.line() - line:setWidth(2) + line:setWidth(2 * s) local hue = colours[stat] line:setColour(hue[1], hue[2], hue[3], 255) graph_data.line = line @@ -312,18 +313,18 @@ function UIGraphs:updateLines() -- Add the graph line pieces. Doing this separately is more efficient as all -- graph lines can be extended to the left in the same iteration. - local xpos = RIGHT_X + local xpos = (RIGHT_X * s) for i, stats in ipairs(self.values) do for stat, value in pairs(stats) do local line = graph_datas[stat].line - local ypos = computeVerticalValuePosition(graph_datas[stat], value) + local ypos = computeVerticalValuePosition(graph_datas[stat], value) * s if i == 1 then line:moveTo(xpos, ypos) else line:lineTo(xpos, ypos) end end - xpos = xpos - VERT_DX + xpos = xpos - (VERT_DX * s) end -- Compute label data for each statistic, and order by vertical position. @@ -358,14 +359,14 @@ function UIGraphs:updateLines() local aux_lines = {} self.aux_lines = aux_lines - xpos = RIGHT_X + xpos = (RIGHT_X * s) for _ = 1, #self.values do local line = TH.line() - line:setWidth(1) - line:moveTo(xpos, BOTTOM_Y + 4) - line:lineTo(xpos, BOTTOM_Y + 8) + line:setWidth(s) + line:moveTo(xpos, BOTTOM_Y * s + 4 * s) + line:lineTo(xpos, BOTTOM_Y * s + 8 * s) aux_lines[#aux_lines + 1] = line - xpos = xpos - VERT_DX + xpos = xpos - (VERT_DX * s) end end @@ -380,18 +381,24 @@ function UIGraphs:draw(canvas, x, y) self:updateLines() end - self.background:draw(canvas, self.x + x, self.y + y) + local s = TheApp.config.ui_scale + canvas:scale(s, "bitmap") + self.background:draw(canvas, self.x * s + x, self.y * s + y) + canvas:scale(1, "bitmap") UIFullscreen.draw(self, canvas, x, y) - x, y = self.x + x, self.y + y - - self.white_font:draw(canvas, _S.graphs.money_in, x + 502, y + 41, 80, 27) - self.white_font:draw(canvas, _S.graphs.money_out, x + 502, y + 93, 80, 27) - self.white_font:draw(canvas, _S.graphs.wages, x + 502, y + 145, 80, 27) - self.white_font:draw(canvas, _S.graphs.balance, x + 502, y + 197, 80, 27) - self.white_font:draw(canvas, _S.graphs.visitors, x + 502, y + 249, 80, 27) - self.white_font:draw(canvas, _S.graphs.cures, x + 502, y + 301, 80, 27) - self.white_font:draw(canvas, _S.graphs.deaths, x + 502, y + 353, 80, 27) - self.white_font:draw(canvas, _S.graphs.reputation, x + 502, y + 405, 80, 27) + x, y = self.x * s + x, self.y * s + y + + local lx = x + 502 * s + local lw = 80 * s + local lh = 27 * s + self.white_font:draw(canvas, _S.graphs.money_in, lx, y + 41 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.money_out, lx, y + 93 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.wages, lx, y + 145 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.balance, lx, y + 197 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.visitors, lx, y + 249 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.cures, lx, y + 301 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.deaths, lx, y + 353 * s, lw, lh) + self.white_font:draw(canvas, _S.graphs.reputation, lx, y + 405 * s, lw, lh) -- Draw the different lines for stat, graph in pairs(self.graph_datas) do @@ -404,14 +411,14 @@ function UIGraphs:draw(canvas, x, y) -- TODO: These should be coloured according to the colour of the corresponding line. for _, label in pairs(self.label_datas) do if label.pos_y then - local ypos = label.pos_y + label.shift_y - self.black_font:draw(canvas, label.text, x + RIGHT_X + 3, y + ypos) - self.black_font:draw(canvas, string.format("%.0f", label.value), x + RIGHT_X + 60, y + ypos) + local ypos = label.pos_y + label.shift_y * s + self.black_font:draw(canvas, label.text, x + RIGHT_X * s + 3 * s, y + ypos * s) + self.black_font:draw(canvas, string.format("%.0f", label.value), x + RIGHT_X * s + 60 * s, y + ypos * s) end end local stats_stepsize = getStatisticsStepsize(self.graph_scale) - local xpos = x + RIGHT_X - math.floor(VERT_DX / 2) + local xpos = x + RIGHT_X * s - math.floor(VERT_DX / 2) * s -- Draw numbers (or month names) below the graph assert(#self.hospital.statistics > 0) -- Avoid negative months and years. @@ -421,8 +428,8 @@ function UIGraphs:draw(canvas, x, y) local year_steps = math.floor(stats_stepsize / 12) year_number = year_number * year_steps for i = 1, #self.values do - self.black_font:drawWrapped(canvas, year_number, xpos, y + BOTTOM_Y + 10, 25, "center") - xpos = xpos - VERT_DX + self.black_font:drawWrapped(canvas, year_number, xpos, y + BOTTOM_Y * s + 10 * s, 25 * s, "center") + xpos = xpos - VERT_DX * s year_number = year_number - year_steps -- And the small black line @@ -432,8 +439,8 @@ function UIGraphs:draw(canvas, x, y) -- Display months local month_number = #self.hospital.statistics - math.floor((#self.hospital.statistics - 1) / 12) * 12 for i = 1, #self.values do - self.black_font:drawWrapped(canvas, _S.months[month_number], xpos, y + BOTTOM_Y + 10, 25, "center") - xpos = xpos - VERT_DX + self.black_font:drawWrapped(canvas, _S.months[month_number], xpos, y + BOTTOM_Y * s + 10 * s, 25 * s, "center") + xpos = xpos - VERT_DX * s month_number = month_number - stats_stepsize if month_number < 1 then month_number = month_number + 12 end @@ -469,18 +476,27 @@ function UIGraphs:close() self.ui:getWindow(UIBottomPanel):updateButtonStates() end +function UIGraphs:onChangeResolution() + UIFullscreen.onChangeResolution(self) + -- onChangeResolution gets called during initialization before the data is ready. + if self.graph_scale then + self:updateLines() + end +end + function UIGraphs:afterLoad(old, new) - if old < 179 then + if old < 236 then local gfx = TheApp.gfx self.background = gfx:loadRaw("Graph01V", 640, 480, "QData", "QData", "Graph01V.pal", true) local palette = gfx:loadPalette("QData", "Graph01V.pal", true) self.panel_sprites = gfx:loadSpriteTable("QData", "Graph02V", true, palette) - self.white_font = gfx:loadFontAndSpriteTable("QData", "Font01V", false, palette) - self.black_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette) + self.white_font = gfx:loadFontAndSpriteTable("QData", "Font01V", false, palette, { apply_ui_scale = true }) + self.black_font = gfx:loadFontAndSpriteTable("QData", "Font00V", false, palette, { apply_ui_scale = true }) end UIFullscreen.afterLoad(self, old, new) if old < 231 then self:close() end + self:updateLines() end