From f6fdfd8df1e7114737644c1fb5f2a4c19dda64ee Mon Sep 17 00:00:00 2001 From: Maarten Bezemer Date: Thu, 23 May 2024 14:52:21 +0200 Subject: [PATCH] Add setting to use intervals instead of speed gain --- changelog.txt | 1 + locale/en/locale.cfg | 8 ++- scripts/camera.lua | 85 ++++++++++++++++++++++++++- scripts/config.lua | 6 +- scripts/gui.lua | 133 ++++++++++++++++++++++++++++++------------- settings.lua | 7 +++ 6 files changed, 196 insertions(+), 44 deletions(-) diff --git a/changelog.txt b/changelog.txt index a85cddf..7be136b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,7 @@ Version: 1.5.2 Minor Features: - Try to fix missing player data in order to be more flexible to unexpected situations. + - Add (per player) setting to use intervals (milliseconds) between frames instead of speed gain (factor). Bugfixes: - Fix optional dependency on StatsGui. diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index dc4ba90..265c3fb 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -27,6 +27,7 @@ label-entity-info=Show entity info label-show-gui=Show GUI label-always-day=Always daytime label-framerate=Frame rate: +label-interval=Interval: label-name=Name: label-position=Position: label-resolution=Resolution: @@ -46,6 +47,7 @@ label-cityblock-currentblock=Current: label-cityblock-blockScale=Scale: label-cityblock-centerOnPlayer=Select block: label-transition-speedgain=Transition speed gain: +label-transition-interval=Transition interval: item-add-tracker= item-new-tracker= tab-cameras=Cameras @@ -61,10 +63,12 @@ camera-entity-info=Show entity information (recipes) in the screenshots. camera-show-gui=Show the GUI in the screenshots (make sure camera resolution and game/screen resolution match). camera-always-day=Render in daylight, regardless of the time of day. camera-framerate=Number of frames per second that your movie is going to be (often/default 25 fps). +camera-interval=Interval (elapsed game time) between frames in milliseconds. (actual interval might be slightly off due to rounding to game ticks) camera-speedgain=Amount (factor) that the timelapse movie should speed up compared to actual time. camera-refresh=Recalculate the camera settings. (due to rounding issues the actual values might differ a little). camera-transitionperiod=Amount of the time takes to transition the camera to its new position. camera-transition-speedgain=Amount (factor) that the timelapse movie should speed up compared to actual time during transitions.\n\nSet to 0 for stop-motion transitions (only possible with sequential numbering active). Note that depending on transition period, and your hardware, it might take a few game ticks (so not 100% stop-motion transition). +camera-transition-interval=Interval (elapsed game time) between frames in millisecond during transitions.\n\nSet to 0 for stop-motion transitions (only possible with sequential numbering active). Note that depending on transition period, and your hardware, it might take a few game ticks (so not 100% stop-motion transition). pause-on-open=Pause game when this window is open. tracker-cannot-delete-inuse=Cannot delete tracker because it is in use. tracker-cannot-enable=This tracker cannot be enabled/disabled, this is managed by a specific event/trigger. @@ -84,7 +88,7 @@ tracker-cityblock-currentblock=The city block to focus on, counting in whole blo tracker-cityblock-currentblock-x=The position of the current city block, counting blocks rightwards. tracker-cityblock-currentblock-y=The position of the current city block, counting blocks downwards. tracker-cityblock-blockScale=How many city blocks to zoom out, centered on the current block. -tracker-cityblock-blockScale-value=A zoom of 3 will capture the current block and all of the neighbours. to set. +tracker-cityblock-blockScale-value=A zoom of 3 will capture the current block and all of the neighbors. to set. tracker-cityblock-player=Use player coordinates to select the current city block. [controls] @@ -96,11 +100,13 @@ tlbe-take-screenshot=Take a screenshot tlbe-save-folder=Save location tlbe-sequential-names=Sequential names tlbe-show-stats=Show camera status +tlbe-use-interval=Use interval as speed setting [mod-setting-description] tlbe-save-folder=Relative from 'User Data Directory' tlbe-sequential-names=Use sequential numbering per camera for the screenshot numbers. When disabled, game ticks are used for screenshot numbering. tlbe-show-stats=This requires the Stats GUI mod to be loaded. +tlbe-use-interval=Use intervals (between frames) instead of speed gain in the camera settings. When this setting is changed, reopen camera settings to update the UI. [shortcut] tlbe=Time Lapse Base Edition diff --git a/scripts/camera.lua b/scripts/camera.lua index 019f331..f759acb 100644 --- a/scripts/camera.lua +++ b/scripts/camera.lua @@ -31,10 +31,10 @@ local Camera = {} --- @field saveFolder string --- @field saveName string --- @field screenshotNumber integer Number for the next screenshot (when sequentialNames is set in player settings) ---- @field screenshotInterval number Interval (game ticks) between two screenshots (calculated from speedGain and frameRate) +--- @field screenshotInterval number Interval (game ticks) between two screenshots (calculated from speedGain and frameRate) --- @field screenshotIntervalRealtime number Interval (game ticks) between two screenshots for realtime transitions (calculated from frameRate) --- @field screenshotIntervalTransition number Interval (game ticks) between two screenshots during transitions (calculated from frameRate) ---- @field speedGain number Amount (factor) that the timelapse movie should speed up. +--- @field speedGain number Amount (factor) that the timelapse movie should speed up compared to the game. --- @field surfaceName string --- @field trackers Tracker.tracker[] --- @field chartTags table Chart tags used to render viewfinder boxes on the map @@ -57,6 +57,7 @@ Camera.CameraTransition = {} local maxZoom = 1 local minZoom = 0.031250 local ticks_per_second = 60 +local ms_per_tick = 1000 / ticks_per_second local tileSize = 32 --- @return Camera.camera @@ -319,6 +320,44 @@ function Camera.setSpeedGain(camera, speedGain) Camera.updateConfig(camera) end +---@param camera Camera.camera +---@param interval any +function Camera.setFrameInterval(camera, interval) + interval = tonumber(interval) + + if interval == nil or interval < ms_per_tick then + -- keep minimum interval + interval = ms_per_tick + end + + local speedGain = (interval * camera.frameRate) / 1000 + if speedGain < 1 then + -- keep minimum speed gain + speedGain = 1 + end + + camera.speedGain = speedGain + Camera.updateConfig(camera) +end + +---@param camera Camera.camera +---@return number|nil interval Camera interval +function Camera.calculateFrameInterval(camera) + if camera.speedGain == nil or camera.frameRate == nil then + return nil + end + + + local interval = ((1000 * camera.speedGain) / camera.frameRate) + + if interval < ms_per_tick then + -- keep minimum interval + interval = ms_per_tick + end + + return interval +end + ---@param camera Camera.camera ---@param speedGain any ---@param allowStopMotion boolean @@ -336,6 +375,48 @@ function Camera.setTransitionSpeedGain(camera, speedGain, allowStopMotion) Camera.updateConfig(camera) end +---@param camera Camera.camera +---@param interval any +---@param allowStopMotion boolean +function Camera.setTransitionFrameInterval(camera, interval, allowStopMotion) + interval = tonumber(interval) + + if interval == nil then + -- keep minimum interval + interval = ms_per_tick + end + if not allowStopMotion and interval == 0 then + interval = ms_per_tick + end + + local speedGain = (interval * camera.frameRate) / 1000 + if not allowStopMotion and speedGain < 1 then + -- keep minimum speed gain + speedGain = 1 + end + + camera.transitionSpeedGain = speedGain + Camera.updateConfig(camera) +end + +---@param camera Camera.camera +---@return number|nil interval Camera interval +function Camera.calculateTransitionFrameInterval(camera) + if camera.transitionSpeedGain == nil or camera.frameRate == nil then + return nil + end + + + local interval = ((1000 * camera.transitionSpeedGain) / camera.frameRate) + + if interval < ms_per_tick then + -- keep minimum interval + interval = ms_per_tick + end + + return interval +end + --- @param camera Camera.camera --- @param transitionPeriod any function Camera.setTransitionPeriod(camera, transitionPeriod) diff --git a/scripts/config.lua b/scripts/config.lua index e8f1ee8..21b84b1 100644 --- a/scripts/config.lua +++ b/scripts/config.lua @@ -7,9 +7,11 @@ local Tracker = require("scripts.tracker") --- @field cameras Camera.camera[] --- @field trackers Tracker.tracker[] --- @field pauseCameras boolean When true, pause all player cameras +--- @field pauseOnOpen boolean When true, pause cameras when the GUI is opened --- @field showCameraStatus boolean When true, the Stats GUI is used to show status of each camera --- @field saveFolder string --- @field sequentialNames boolean +--- @field useInterval boolean When true, use interval (between frames) instead of speed gain --- @field noticeMaxZoom boolean When true the warning about the max zoom is already raised --- @field gui table Contains all (volatile) GUI elements --- @field guiPersist persistedGUISettings Contains all persisted (between saves) GUI details @@ -37,10 +39,10 @@ function Config.reload(event) ---@diagnostic disable: assign-type-mismatch playerSettings.saveFolder = guiSettings["tlbe-save-folder"].value - ---@diagnostic disable: assign-type-mismatch playerSettings.sequentialNames = guiSettings["tlbe-sequential-names"].value - ---@diagnostic disable: assign-type-mismatch playerSettings.showCameraStatus = guiSettings["tlbe-show-stats"].value + playerSettings.useInterval = guiSettings["tlbe-use-interval"].value + ---@diagnostic enable: assign-type-mismatch end --- @return playerSettings diff --git a/scripts/gui.lua b/scripts/gui.lua index 287d455..c9d224b 100644 --- a/scripts/gui.lua +++ b/scripts/gui.lua @@ -119,6 +119,7 @@ function GUI.onClick(event) GUI.updateCameraList(playerSettings.gui, playerSettings.guiPersist, playerSettings.cameras) GUI.updateCameraActions(playerSettings.gui, playerSettings.guiPersist, playerSettings.cameras) GUI.updateCameraConfig( + playerSettings.useInterval, playerSettings.gui.cameraInfo, playerSettings.cameras[playerSettings.guiPersist.selectedCamera] ) @@ -143,6 +144,7 @@ function GUI.onClick(event) GUI.updateCameraList(playerSettings.gui, playerSettings.guiPersist, playerSettings.cameras) GUI.updateCameraActions(playerSettings.gui, playerSettings.guiPersist, playerSettings.cameras) GUI.updateCameraConfig( + playerSettings.useInterval, playerSettings.gui.cameraInfo, playerSettings.cameras[playerSettings.guiPersist.selectedCamera] ) @@ -164,6 +166,7 @@ function GUI.onClick(event) Camera.refreshConfig(playerSettings.cameras[playerSettings.guiPersist.selectedCamera]) GUI.updateCameraConfig( + playerSettings.useInterval, playerSettings.gui.cameraInfo, playerSettings.cameras[playerSettings.guiPersist.selectedCamera] ) @@ -421,6 +424,7 @@ function GUI.onSelected(event) GUI.updateCameraActions(playerSettings.gui, playerSettings.guiPersist, playerSettings.cameras) GUI.updateCameraConfig( + playerSettings.useInterval, playerSettings.gui.cameraInfo, playerSettings.cameras[playerSettings.guiPersist.selectedCamera] ) @@ -556,11 +560,16 @@ function GUI.onTextChanged(event) Camera.setFrameRate(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], event.element.text) elseif event.element.name == "camera-speed-gain" then Camera.setSpeedGain(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], event.element.text) + elseif event.element.name == "camera-interval" then + Camera.setFrameInterval(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], event.element.text) elseif event.element.name == "camera-transition-period" then Camera.setTransitionPeriod(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], event.element.text) elseif event.element.name == "camera-transition-speed-gain" then Camera.setTransitionSpeedGain(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], event.element.text, playerSettings.sequentialNames) + elseif event.element.name == "camera-transition-interval" then + Camera.setTransitionFrameInterval(playerSettings.cameras[playerSettings.guiPersist.selectedCamera], + event.element.text, playerSettings.sequentialNames) elseif event.element.name == "tlbe-tracker-top" then local value = tonumber(event.element.text) if value ~= nil then @@ -662,7 +671,7 @@ function GUI.onGuiConfirmed(event) local selectedTracker = playerSettings.trackers[playerSettings.guiPersist.selectedTracker] local value = tonumber(event.element.text) if value ~= nil then - selectedTracker.cityBlock.blockScale = math.floor(value * 100)/100 + selectedTracker.cityBlock.blockScale = math.floor(value * 100) / 100 Tracker.recalculateCityBlock(selectedTracker) GUI.updateTrackerConfig(playerSettings.gui.trackerInfo, selectedTracker) @@ -718,6 +727,7 @@ function GUI.onSurfacesUpdated() local playerSettings = global.playerSettings[player.index] if playerSettings.gui ~= nil then GUI.updateCameraConfig( + playerSettings.useInterval, playerSettings.gui.cameraInfo, playerSettings.cameras[playerSettings.guiPersist.selectedCamera] ) @@ -782,6 +792,7 @@ end function GUI.toggleMainWindow(event) local player = game.players[event.player_index] + ---@type playerSettings local playerSettings = global.playerSettings[event.player_index] local mainWindowOpen = player.gui.screen["tlbe-main-window"] ~= nil @@ -837,6 +848,7 @@ function GUI.toggleMainWindow(event) tabPane, playerSettings.gui, playerSettings.guiPersist, + playerSettings.useInterval, playerSettings.cameras, playerSettings.trackers ) @@ -858,7 +870,7 @@ function GUI.toggleMainWindow(event) end end -function GUI.createCameraSettings(parent, playerGUI, guiPersist, cameras, trackers) +function GUI.createCameraSettings(parent, playerGUI, guiPersist, useInterval, cameras, trackers) local flow = parent.add { type = "flow", direction = "vertical" } -- Cameras @@ -923,20 +935,39 @@ function GUI.createCameraSettings(parent, playerGUI, guiPersist, cameras, tracke style = "tlbe_config_half_width_textfield", numeric = true } - playerGUI.cameraInfo.add { - type = "label", - caption = { "gui.label-speedgain" }, - tooltip = { "tooltip.camera-speedgain" }, - style = "description_property_name_label" - } - playerGUI.cameraInfo.add { - type = "textfield", - name = "camera-speed-gain", - tooltip = { "tooltip.camera-speedgain" }, - style = "tlbe_config_half_width_textfield", - numeric = true, - allow_decimal = true - } + if useInterval then + playerGUI.cameraInfo.add { + type = "label", + caption = { "gui.label-interval" }, + tooltip = { "tooltip.camera-interval" }, + style = "description_property_name_label" + } + + playerGUI.cameraInfo.add { + type = "textfield", + name = "camera-interval", + tooltip = { "tooltip.camera-interval" }, + style = "tlbe_config_half_width_textfield", + numeric = true, + allow_decimal = true + } + else + playerGUI.cameraInfo.add { + type = "label", + caption = { "gui.label-speedgain" }, + tooltip = { "tooltip.camera-speedgain" }, + style = "description_property_name_label" + } + + playerGUI.cameraInfo.add { + type = "textfield", + name = "camera-speed-gain", + tooltip = { "tooltip.camera-speedgain" }, + style = "tlbe_config_half_width_textfield", + numeric = true, + allow_decimal = true + } + end playerGUI.cameraInfo.add { type = "label", caption = { "gui.label-transitionperiod" }, @@ -951,20 +982,37 @@ function GUI.createCameraSettings(parent, playerGUI, guiPersist, cameras, tracke numeric = true, allow_decimal = true } - playerGUI.cameraInfo.add { - type = "label", - caption = { "gui.label-transition-speedgain" }, - tooltip = { "tooltip.camera-transition-speedgain" }, - style = "description_property_name_label" - } - playerGUI.cameraInfo.add { - type = "textfield", - name = "camera-transition-speed-gain", - tooltip = { "tooltip.camera-transition-speedgain" }, - style = "tlbe_config_half_width_textfield", - numeric = true, - allow_decimal = true - } + if useInterval then + playerGUI.cameraInfo.add { + type = "label", + caption = { "gui.label-transition-interval" }, + tooltip = { "tooltip.camera-transition-interval" }, + style = "description_property_name_label" + } + playerGUI.cameraInfo.add { + type = "textfield", + name = "camera-transition-interval", + tooltip = { "tooltip.camera-transition-interval" }, + style = "tlbe_config_half_width_textfield", + numeric = true, + allow_decimal = true + } + else + playerGUI.cameraInfo.add { + type = "label", + caption = { "gui.label-transition-speedgain" }, + tooltip = { "tooltip.camera-transition-speedgain" }, + style = "description_property_name_label" + } + playerGUI.cameraInfo.add { + type = "textfield", + name = "camera-transition-speed-gain", + tooltip = { "tooltip.camera-transition-speedgain" }, + style = "tlbe_config_half_width_textfield", + numeric = true, + allow_decimal = true + } + end playerGUI.cameraInfo.add { type = "empty-widget" } playerGUI.cameraInfo.add { type = "checkbox", @@ -998,7 +1046,7 @@ function GUI.createCameraSettings(parent, playerGUI, guiPersist, cameras, tracke playerGUI.cameraInfo.add { type = "label", caption = { "gui.label-zoom" }, style = "description_property_name_label" } playerGUI.cameraInfo.add { type = "label", name = "camera-zoom" } - GUI.updateCameraConfig(playerGUI.cameraInfo, cameras[guiPersist.selectedCamera]) + GUI.updateCameraConfig(useInterval, playerGUI.cameraInfo, cameras[guiPersist.selectedCamera]) GUI.updateCameraInfo(playerGUI.cameraInfo, cameras[guiPersist.selectedCamera]) -- Trackers @@ -1337,17 +1385,24 @@ function GUI.updateCameraList(playerGUI, guiPersist, cameras) playerGUI.cameraSelector.selected_index = guiPersist.selectedCamera end +---@param useInterval boolean ---@param cameraInfo table ---@param camera Camera.camera -function GUI.updateCameraConfig(cameraInfo, camera) +function GUI.updateCameraConfig(useInterval, cameraInfo, camera) -- Paranoia check if camera ~= nil then local resolutionFlow = cameraInfo["camera-resolution"] cameraInfo["camera-name"].text = camera.name cameraInfo["camera-frame-rate"].text = string.format("%d", camera.frameRate or 25) - cameraInfo["camera-speed-gain"].text = string.format("%d", camera.speedGain or 60) + if useInterval then + cameraInfo["camera-interval"].text = string.format("%d", Camera.calculateFrameInterval(camera) or 2400) -- same as speed gain of 60 for 25 fps + cameraInfo["camera-transition-interval"].text = string.format("%d", + Camera.calculateTransitionFrameInterval(camera) or 2400) -- same as transition speed gain of 60 for 25 fps + else + cameraInfo["camera-speed-gain"].text = string.format("%d", camera.speedGain or 60) + cameraInfo["camera-transition-speed-gain"].text = string.format("%d", camera.transitionSpeedGain or 60) + end cameraInfo["camera-transition-period"].text = string.format("%2.2f", camera.transitionPeriod or 1.5) - cameraInfo["camera-transition-speed-gain"].text = string.format("%d", camera.transitionSpeedGain or 60) cameraInfo["camera-entity-info"].state = camera.entityInfo cameraInfo["camera-show-gui"].state = camera.showGUI cameraInfo["camera-always-day"].state = camera.alwaysDay @@ -1501,7 +1556,7 @@ function GUI.createTrackerConfigAndInfo(trackerInfo, tracker) state = tracker.untilBuild } elseif tracker.type == "cityblock" then - trackerInfo.add { + trackerInfo.add { type = "label", caption = { "gui.label-cityblock-size" }, tooltip = { "tooltip.tracker-cityblock-size" }, @@ -1526,7 +1581,7 @@ function GUI.createTrackerConfigAndInfo(trackerInfo, tracker) allow_negative = true } - trackerInfo.add { + trackerInfo.add { type = "label", caption = { "gui.label-cityblock-offset" }, tooltip = { "tooltip.tracker-cityblock-offset" }, @@ -1551,7 +1606,7 @@ function GUI.createTrackerConfigAndInfo(trackerInfo, tracker) allow_negative = true } - trackerInfo.add { + trackerInfo.add { type = "label", caption = { "gui.label-cityblock-currentblock" }, tooltip = { "tooltip.tracker-cityblock-currentblock" }, @@ -1576,7 +1631,7 @@ function GUI.createTrackerConfigAndInfo(trackerInfo, tracker) allow_negative = true } - trackerInfo.add { + trackerInfo.add { type = "label", caption = { "gui.label-cityblock-blockScale" }, tooltip = { "tooltip.tracker-cityblock-blockScale" }, @@ -1673,7 +1728,7 @@ function GUI.updateTrackerConfig(trackerInfo, tracker) if cityBlock == nil then return end - + local sizeFlow = trackerInfo["cityblock-size"] sizeFlow["tlbe-tracker-cityblock-size-x"].text = string.format("%d", cityBlock.blockSize.x) sizeFlow["tlbe-tracker-cityblock-size-y"].text = string.format("%d", cityBlock.blockSize.y) diff --git a/settings.lua b/settings.lua index 209adf9..93540ac 100644 --- a/settings.lua +++ b/settings.lua @@ -20,6 +20,13 @@ data:extend( setting_type = "runtime-per-user", default_value = true, order = "6" + }, + { + type = "bool-setting", + name = "tlbe-use-interval", + setting_type = "runtime-per-user", + default_value = false, + order = "9" } } )