Improve user experience
- Modernise UI, and simplify its logic - Add UI graphics - Add modification.lua which contains everything to modify Noita settings - Add message.lua which handles messages for users - Add check.lua which checks things, triggers messages and suggest user actions - Remove ACTIONS category from settings - Add more live capturing parameters to settings - Restrict vector input fields in settings - Rename pixel-size setting to pixel-scale - Let GetRect return two vectors instead of RECT object - Add VirtualOffsetPixelPerfect and FullscreenMode field to Coords - Fix captureScreenshot when the outputPixelScale is 0 - Show runtime errors in UI via message.lua - Other small fixes
1
.gitignore
vendored
@ -106,3 +106,4 @@ $RECYCLE.BIN/
|
||||
/output/
|
||||
/dist/
|
||||
/bin/stitch/output.png
|
||||
/files/magic-numbers/generated.xml
|
@ -64,7 +64,12 @@ local function captureScreenshot(pos, ensureLoaded, dontOverwrite, ctx, outputPi
|
||||
|
||||
---Top left in output coordinates.
|
||||
---@type Vec2
|
||||
local outputTopLeft = (topLeftWorld * outputPixelScale):Rounded()
|
||||
local outputTopLeft
|
||||
if outputPixelScale > 0 then
|
||||
outputTopLeft = (topLeftWorld * outputPixelScale):Rounded()
|
||||
else
|
||||
outputTopLeft = topLeftWorld
|
||||
end
|
||||
|
||||
-- Check if the file exists, and if we are allowed to overwrite it.
|
||||
if dontOverwrite and Utils.FileExists(string.format("mods/noita-mapcap/output/%d,%d.png", outputTopLeft.x, outputTopLeft.y)) then
|
||||
@ -94,14 +99,18 @@ local function captureScreenshot(pos, ensureLoaded, dontOverwrite, ctx, outputPi
|
||||
end
|
||||
|
||||
-- Suspend UI drawing for 1 frame.
|
||||
UI.SuspendDrawing(1)
|
||||
UI:SuspendDrawing(1)
|
||||
|
||||
wait(0)
|
||||
|
||||
-- Fetch coordinates again, as they may have changed.
|
||||
if not pos then
|
||||
topLeftCapture, bottomRightCapture, topLeftWorld, bottomRightWorld = calculateCaptureRectangle(pos)
|
||||
if outputPixelScale > 0 then
|
||||
outputTopLeft = (topLeftWorld * outputPixelScale):Rounded()
|
||||
else
|
||||
outputTopLeft = topLeftWorld
|
||||
end
|
||||
end
|
||||
|
||||
-- The top left world position needs to be upscaled by the pixel scale.
|
||||
@ -119,7 +128,7 @@ end
|
||||
---@param scope "init"|"do"|"end"
|
||||
local function mapCapturingCtxErrHandler(err, scope)
|
||||
print(string.format("Failed to capture map: %s", err))
|
||||
-- TODO: Forward error to user interface
|
||||
Message:ShowRuntimeError("MapCaptureError", "Failed to capture map:", tostring(err))
|
||||
end
|
||||
|
||||
---Starts the capturing process in a spiral around origin.
|
||||
@ -128,6 +137,11 @@ end
|
||||
---@param captureGridSize number -- The grid size in world pixels.
|
||||
---@param outputPixelScale number|nil -- The resulting image pixel to world pixel ratio.
|
||||
function Capture:StartCapturingSpiral(origin, captureGridSize, outputPixelScale)
|
||||
|
||||
-- Create file that signals that there are files in the output directory.
|
||||
local file = io.open("mods/noita-mapcap/output/nonempty", "a")
|
||||
if file ~= nil then file:close() end
|
||||
|
||||
---Origin rounded to capture grid.
|
||||
---@type Vec2
|
||||
local origin = (origin / captureGridSize):Rounded("Floor") * captureGridSize
|
||||
@ -180,6 +194,11 @@ end
|
||||
---@param captureGridSize number -- The grid size in world pixels.
|
||||
---@param outputPixelScale number|nil -- The resulting image pixel to world pixel ratio.
|
||||
function Capture:StartCapturingArea(topLeft, bottomRight, captureGridSize, outputPixelScale)
|
||||
|
||||
-- Create file that signals that there are files in the output directory.
|
||||
local file = io.open("mods/noita-mapcap/output/nonempty", "a")
|
||||
if file ~= nil then file:close() end
|
||||
|
||||
---The rectangle in grid coordinates.
|
||||
---@type Vec2, Vec2
|
||||
local gridTopLeft, gridBottomRight = (topLeft / captureGridSize):Rounded("floor"), (bottomRight / captureGridSize):Rounded("floor")
|
||||
@ -241,6 +260,10 @@ function Capture:StartCapturingLive(interval, minDistance, maxDistance, outputPi
|
||||
minDistance = minDistance or 10
|
||||
maxDistance = maxDistance or 50
|
||||
|
||||
-- Create file that signals that there are files in the output directory.
|
||||
local file = io.open("mods/noita-mapcap/output/nonempty", "a")
|
||||
if file ~= nil then file:close() end
|
||||
|
||||
---Process main callback.
|
||||
---@param ctx ProcessRunnerCtx
|
||||
local function handleDo(ctx)
|
||||
@ -429,8 +452,31 @@ function Capture:StartCapturingEntities(store, modify)
|
||||
---@param scope "init"|"do"|"end"
|
||||
local function handleErr(err, scope)
|
||||
print(string.format("Failed to capture entities: %s", err))
|
||||
Message:ShowRuntimeError("EntitiesCaptureError", "Failed to capture entities:", tostring(err))
|
||||
end
|
||||
|
||||
-- Run process, if there is no other running right now.
|
||||
self.EntityCapturingCtx:Run(handleInit, handleDo, handleEnd, handleErr)
|
||||
end
|
||||
|
||||
---Starts the capturing process based on user/mod settings.
|
||||
function Capture:StartCapturing()
|
||||
local mode = ModSettingGet("noita-mapcap.capture-mode")
|
||||
local outputPixelScale = ModSettingGet("noita-mapcap.pixel-scale")
|
||||
|
||||
if mode == "live" then
|
||||
local interval = ModSettingGet("noita-mapcap.live-interval")
|
||||
local minDistance = ModSettingGet("noita-mapcap.live-min-distance")
|
||||
local maxDistance = ModSettingGet("noita-mapcap.live-max-distance")
|
||||
|
||||
self:StartCapturingLive(interval, minDistance, maxDistance, outputPixelScale)
|
||||
else
|
||||
Message:ShowRuntimeError("StartCapturing", string.format("Unknown capturing mode %q", tostring(mode)))
|
||||
end
|
||||
end
|
||||
|
||||
---Stops all capturing processes.
|
||||
function Capture:StopCapturing()
|
||||
self.EntityCapturingCtx:Stop()
|
||||
self.MapCapturingCtx:Stop()
|
||||
end
|
||||
|
87
files/check.lua
Normal file
@ -0,0 +1,87 @@
|
||||
-- Copyright (c) 2019-2022 David Vogel
|
||||
--
|
||||
-- This software is released under the MIT License.
|
||||
-- https://opensource.org/licenses/MIT
|
||||
|
||||
-- Check if everything is alright.
|
||||
-- This does mainly trigger user messages and suggest actions.
|
||||
|
||||
-----------------------
|
||||
-- Load global stuff --
|
||||
-----------------------
|
||||
|
||||
--------------------------
|
||||
-- Load library modules --
|
||||
--------------------------
|
||||
|
||||
local Coords = require("coordinates")
|
||||
local ScreenCap = require("screen-capture")
|
||||
local Vec2 = require("noita-api.vec2")
|
||||
local Utils= require("noita-api.utils")
|
||||
|
||||
----------
|
||||
-- Code --
|
||||
----------
|
||||
|
||||
---Runs a list of checks at addon startup.
|
||||
function Check:Startup()
|
||||
if Utils.FileExists("mods/noita-mapcap/output/nonempty") then
|
||||
Message:ShowOutputNonEmpty()
|
||||
end
|
||||
|
||||
if not Utils.FileExists("mods/noita-mapcap/bin/capture-b/capture.dll") then
|
||||
Message:ShowGeneralInstallationProblem("`capture.dll` is missing.", "Make sure you have installed the mod correctly.")
|
||||
end
|
||||
|
||||
if not Utils.FileExists("mods/noita-mapcap/bin/stitch/stitch.exe") then
|
||||
Message:ShowGeneralInstallationProblem("`stitch.exe` is missing.", "Make sure you have installed the mod correctly.", " ", "You can still use the mod to capture, though.")
|
||||
end
|
||||
end
|
||||
|
||||
---Runs a list of checks for everything resolution related.
|
||||
---@param interval integer -- Check interval in frames.
|
||||
function Check:Resolutions(interval)
|
||||
interval = interval or 60
|
||||
self.Counter = (self.Counter or 0) - 1
|
||||
if self.Counter > 0 then return end
|
||||
self.Counter = interval
|
||||
|
||||
-- Compare Noita config and actual window resolution.
|
||||
local topLeft, bottomRight = ScreenCap.GetRect() -- Actual window client area.
|
||||
if topLeft and bottomRight then
|
||||
local actual = bottomRight - topLeft
|
||||
if actual ~= Coords.WindowResolution then
|
||||
Message:ShowWrongResolution(Modification.AutoSet, string.format("Old window resolution is %s. Current resolution is %s.", Coords.WindowResolution, actual))
|
||||
end
|
||||
else
|
||||
Message:ShowRuntimeError("GetRect", "Couldn't determine window resolution.")
|
||||
end
|
||||
|
||||
-- Check if we have the required settings.
|
||||
local config, magic = Modification.RequiredChanges()
|
||||
if config["fullscreen"] then
|
||||
local expected = tonumber(config["fullscreen"])
|
||||
if expected ~= Coords.FullscreenMode then
|
||||
Message:ShowSetNoitaSettings(Modification.AutoSet, string.format("Expected fullscreen mode %s. But got %s.", expected, Coords.FullscreenMode))
|
||||
end
|
||||
end
|
||||
if config["window_w"] and config["window_h"] then
|
||||
local expected = Vec2(tonumber(config["window_w"]), tonumber(config["window_h"]))
|
||||
if expected ~= Coords.WindowResolution then
|
||||
Message:ShowSetNoitaSettings(Modification.AutoSet, string.format("Expected window resolution is %s. But got %s.", expected, Coords.WindowResolution))
|
||||
end
|
||||
end
|
||||
if config["internal_size_w"] and config["internal_size_h"] then
|
||||
local expected = Vec2(tonumber(config["internal_size_w"]), tonumber(config["internal_size_h"]))
|
||||
if expected ~= Coords.InternalResolution then
|
||||
Message:ShowSetNoitaSettings(Modification.AutoSet, string.format("Expected internal resolution is %s. But got %s.", expected, Coords.InternalResolution))
|
||||
end
|
||||
end
|
||||
if magic["VIRTUAL_RESOLUTION_X"] and magic["VIRTUAL_RESOLUTION_Y"] then
|
||||
local expected = Vec2(tonumber(magic["VIRTUAL_RESOLUTION_X"]), tonumber(magic["VIRTUAL_RESOLUTION_Y"]))
|
||||
if expected ~= Coords.VirtualResolution then
|
||||
Message:ShowSetNoitaSettings(Modification.AutoSet, string.format("Expected virtual resolution is %s. But got %s.", expected, Coords.VirtualResolution))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -34,18 +34,20 @@ local Vec2 = require("noita-api.vec2")
|
||||
-- Code --
|
||||
----------
|
||||
|
||||
local virtualOffsetPixelPerfect = Vec2(-2, 0)
|
||||
|
||||
---@class Coords
|
||||
---@field InternalResolution Vec2 -- Size of the internal rectangle in window pixels.
|
||||
---@field WindowResolution Vec2 -- Size of the window client area in window pixels.
|
||||
---@field VirtualResolution Vec2 -- Size of the virtual rectangle in world/virtual pixels.
|
||||
---@field VirtualOffset Vec2 -- Offset of the virtual rectangle in world/virtual pixels.
|
||||
---@field VirtualOffsetPixelPerfect Vec2 -- Offset of the virtual rectangle that maps chunks perfectly to the window.
|
||||
---@field FullscreenMode integer -- The fullscreen mode the game is in. 0 is windowed.
|
||||
local Coords = {
|
||||
InternalResolution = Vec2(0, 0),
|
||||
WindowResolution = Vec2(0, 0),
|
||||
VirtualResolution = Vec2(0, 0),
|
||||
VirtualOffset = Vec2(0, 0),
|
||||
VirtualOffsetPixelPerfect = Vec2(-2, 0),
|
||||
FullscreenMode = 0,
|
||||
}
|
||||
|
||||
---Reads and updates the internal, window and virtual resolutions from Noita's config files and API.
|
||||
@ -62,6 +64,7 @@ function Coords:ReadResolutions()
|
||||
self.InternalResolution = Vec2(tonumber(xml.attr["internal_size_w"]), tonumber(xml.attr["internal_size_h"]))
|
||||
self.VirtualResolution = Vec2(tonumber(MagicNumbersGetValue("VIRTUAL_RESOLUTION_X")), tonumber(MagicNumbersGetValue("VIRTUAL_RESOLUTION_Y")))
|
||||
self.VirtualOffset = Vec2(tonumber(MagicNumbersGetValue("VIRTUAL_RESOLUTION_OFFSET_X")), tonumber(MagicNumbersGetValue("VIRTUAL_RESOLUTION_OFFSET_Y")))
|
||||
self.FullscreenMode = tonumber(xml.attr["fullscreen"]) or 0
|
||||
|
||||
f:close()
|
||||
return nil
|
||||
@ -130,7 +133,7 @@ function Coords:ToWindow(world, viewportCenter)
|
||||
local internalTopLeft, internalBottomRight = self:InternalRect()
|
||||
local pixelScale = self:PixelScale()
|
||||
|
||||
return internalTopLeft + (self.VirtualResolution / 2 + world - viewportCenter - virtualOffsetPixelPerfect + self.VirtualOffset) * pixelScale
|
||||
return internalTopLeft + (self.VirtualResolution / 2 + world - viewportCenter - self.VirtualOffsetPixelPerfect + self.VirtualOffset) * pixelScale
|
||||
end
|
||||
|
||||
---Converts the given window coordinates into world/virtual coordinates.
|
||||
@ -143,7 +146,7 @@ function Coords:ToWorld(window, viewportCenter)
|
||||
local internalTopLeft, internalBottomRight = self:InternalRect()
|
||||
local pixelScale = self:PixelScale()
|
||||
|
||||
return viewportCenter - self.VirtualResolution / 2 + (window - internalTopLeft) / pixelScale + virtualOffsetPixelPerfect - self.VirtualOffset
|
||||
return viewportCenter - self.VirtualResolution / 2 + (window - internalTopLeft) / pixelScale + self.VirtualOffsetPixelPerfect - self.VirtualOffset
|
||||
end
|
||||
|
||||
-------------
|
||||
|
@ -42,14 +42,15 @@ function ScreenCap.Capture(topLeft, bottomRight, topLeftOutput, finalDimensions)
|
||||
end
|
||||
|
||||
---Returns the client rectangle of the "Main" window of this process in screen coordinates.
|
||||
---@return any
|
||||
---@return Vec2|nil topLeft
|
||||
---@return Vec2|nil bottomRight
|
||||
function ScreenCap.GetRect()
|
||||
local rect = ffi.new("RECT")
|
||||
if not res.GetRect(rect) then
|
||||
return nil
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
return rect
|
||||
return Vec2(rect.left, rect.top), Vec2(rect.right, rect.bottom)
|
||||
end
|
||||
|
||||
return ScreenCap
|
||||
|
133
files/message.lua
Normal file
@ -0,0 +1,133 @@
|
||||
-- Copyright (c) 2019-2022 David Vogel
|
||||
--
|
||||
-- This software is released under the MIT License.
|
||||
-- https://opensource.org/licenses/MIT
|
||||
|
||||
-----------------------
|
||||
-- Load global stuff --
|
||||
-----------------------
|
||||
|
||||
--------------------------
|
||||
-- Load library modules --
|
||||
--------------------------
|
||||
|
||||
local Coords = require("coordinates")
|
||||
|
||||
----------
|
||||
-- Code --
|
||||
----------
|
||||
|
||||
---Add a general runtime error message to the message list.
|
||||
---This will always overwrite the last runtime error with the same id.
|
||||
---@param id string
|
||||
---@param ... string
|
||||
function Message:ShowRuntimeError(id, ...)
|
||||
self.List = self.List or {}
|
||||
|
||||
self.List["RuntimeError" .. id] = {
|
||||
Type = "error",
|
||||
Lines = { ... },
|
||||
}
|
||||
end
|
||||
|
||||
---Calls func and catches any exception.
|
||||
---If there is one, a runtime error message will be shown to the user.
|
||||
---@param id string
|
||||
---@param func function
|
||||
function Message:CatchException(id, func)
|
||||
local ok, err = pcall(func)
|
||||
if not ok then
|
||||
self:ShowRuntimeError(id, string.format("An exception happened in %s", id), err)
|
||||
end
|
||||
end
|
||||
|
||||
---Request the user to let the addon automatically reset some Noita settings.
|
||||
function Message:ShowResetNoitaSettings()
|
||||
self.List = self.List or {}
|
||||
|
||||
self.List["ResetNoitaSettings"] = {
|
||||
Type = "info",
|
||||
Lines = {
|
||||
"You requested to reset some game settings like:",
|
||||
"- Custom resolutions",
|
||||
" ",
|
||||
"Press the following button to reset the settings and close Noita automatically:",
|
||||
},
|
||||
Actions = {
|
||||
{ Name = "Reset and close", Hint = nil, HintDesc = nil, Callback = function() Modification:Reset() end },
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
---Request the user to let the addon automatically set Noita settings based on the given callback.
|
||||
---@param callback function
|
||||
---@param desc string -- What's wrong.
|
||||
function Message:ShowSetNoitaSettings(callback, desc)
|
||||
self.List = self.List or {}
|
||||
|
||||
self.List["SetNoitaSettings"] = {
|
||||
Type = "warning",
|
||||
Lines = {
|
||||
"It seems that not all requested settings are applied to Noita:",
|
||||
desc or "",
|
||||
" ",
|
||||
"Press the button at the bottom to set up and close Noita automatically.",
|
||||
"Alternatively disable `Use custom resolution` in the mod settings.",
|
||||
" ",
|
||||
"You can always reset these settings by right clicking the `start capture`",
|
||||
"button at the top left.",
|
||||
},
|
||||
Actions = {
|
||||
{ Name = "Setup and close", Hint = nil, HintDesc = nil, Callback = callback },
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
---Request the user to let the addon automatically set Noita settings based on the given callback.
|
||||
---@param callback function
|
||||
---@param desc string -- What's wrong.
|
||||
function Message:ShowWrongResolution(callback, desc)
|
||||
self.List = self.List or {}
|
||||
|
||||
self.List["WrongResolution"] = {
|
||||
Type = "warning",
|
||||
Lines = {
|
||||
"The resolution changed:",
|
||||
desc or "",
|
||||
" ",
|
||||
"To fix: Restart Noita or revert the change."
|
||||
},
|
||||
Actions = {
|
||||
{ Name = "Query settings again", Hint = nil, HintDesc = nil, Callback = function() Coords:ReadResolutions() end },
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
---Tell the user that there are files in the output directory.
|
||||
function Message:ShowOutputNonEmpty()
|
||||
self.List = self.List or {}
|
||||
|
||||
self.List["OutputNonEmpty"] = {
|
||||
Type = "hint",
|
||||
Lines = {
|
||||
"There are already files in the output directory.",
|
||||
"If you are continuing a capture session, ignore this message.",
|
||||
" ",
|
||||
"If you are about to capture a new map, make sure to delete all files in the output directory first."
|
||||
},
|
||||
Actions = {
|
||||
{ Name = "Open output directory", Hint = nil, HintDesc = nil, Callback = function() os.execute("start .\\mods\\noita-mapcap\\output\\") end },
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
---Tell the user that there is something wrong with the mod installation.
|
||||
---@param ... string
|
||||
function Message:ShowGeneralInstallationProblem(...)
|
||||
self.List = self.List or {}
|
||||
|
||||
self.List["GeneralInstallationProblem"] = {
|
||||
Type = "error",
|
||||
Lines = { ... },
|
||||
}
|
||||
end
|
120
files/modification.lua
Normal file
@ -0,0 +1,120 @@
|
||||
-- Copyright (c) 2022 David Vogel
|
||||
--
|
||||
-- This software is released under the MIT License.
|
||||
-- https://opensource.org/licenses/MIT
|
||||
|
||||
-- Noita settings/configuration modifications.
|
||||
-- We try to keep modifications to a minimum, but some things have to be changed in order for the mod to work correctly.
|
||||
|
||||
--------------------------
|
||||
-- Load library modules --
|
||||
--------------------------
|
||||
|
||||
local NXML = require("luanxml.nxml")
|
||||
local Utils = require("noita-api.utils")
|
||||
local Vec2 = require("noita-api.vec2")
|
||||
local Coords = require("coordinates")
|
||||
|
||||
----------
|
||||
-- Code --
|
||||
----------
|
||||
|
||||
---Will update Noita's `config.xml` with the values in the given table.
|
||||
---
|
||||
---This will force close Noita!
|
||||
---@param config table<string, string> -- List of `config.xml` attributes that should be changed.
|
||||
function Modification.SetConfig(config)
|
||||
local configFilename = Utils.GetSpecialDirectory("save-shared") .. "config.xml"
|
||||
|
||||
-- Read and modify config.
|
||||
local f, err = io.open(configFilename, "r")
|
||||
if not f then error(string.format("failed to read config file: %s", err)) end
|
||||
local xml = NXML.parse(f:read("*a"))
|
||||
|
||||
for k, v in pairs(config) do
|
||||
xml.attr[k] = v
|
||||
end
|
||||
|
||||
f:close()
|
||||
|
||||
-- Write modified config back.
|
||||
local f, err = io.open(configFilename, "w")
|
||||
if not f then error(string.format("failed to create config file: %s", err)) end
|
||||
f:write(tostring(xml))
|
||||
f:close()
|
||||
|
||||
-- We need to force close Noita, so it doesn't have any chance to overwrite the file.
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
---Will update Noita's `magic_numbers.xml` with the values in the given table.
|
||||
---
|
||||
---Should be called on mod initialization only.
|
||||
---@param magic table<string, string> -- List of `magic_numbers.xml` attributes that should be changed.
|
||||
function Modification.SetMagicNumbers(magic)
|
||||
local xml = NXML.new_element("MagicNumbers", magic)
|
||||
|
||||
-- Write magic number file.
|
||||
local f, err = io.open("mods/noita-mapcap/files/magic-numbers/generated.xml", "w")
|
||||
if not f then error(string.format("failed to create config file: %s", err)) end
|
||||
f:write(tostring(xml))
|
||||
f:close()
|
||||
|
||||
ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/generated.xml")
|
||||
end
|
||||
|
||||
---Returns tables with user requested game configuration changes.
|
||||
---@return table config -- List of `config.xml` attributes that should be changed.
|
||||
---@return table magic -- List of `magic_number.xml` attributes that should be changed.
|
||||
function Modification.RequiredChanges()
|
||||
local config, magic = {}, {}
|
||||
|
||||
-- Does the user request a custom resolution?
|
||||
local customResolution = (ModSettingGet("noita-mapcap.custom-resolution-live") and ModSettingGet("noita-mapcap.capture-mode") == "live")
|
||||
or (ModSettingGet("noita-mapcap.custom-resolution-other") and ModSettingGet("noita-mapcap.capture-mode") ~= "live")
|
||||
|
||||
if customResolution then
|
||||
config["window_w"] = tostring(Vec2(ModSettingGet("noita-mapcap.window-resolution")).x)
|
||||
config["window_h"] = tostring(Vec2(ModSettingGet("noita-mapcap.window-resolution")).y)
|
||||
config["internal_size_w"] = tostring(Vec2(ModSettingGet("noita-mapcap.internal-resolution")).x)
|
||||
config["internal_size_h"] = tostring(Vec2(ModSettingGet("noita-mapcap.internal-resolution")).y)
|
||||
config["backbuffer_width"] = config["window_w"]
|
||||
config["backbuffer_height"] = config["window_h"]
|
||||
magic["VIRTUAL_RESOLUTION_X"] = tostring(Vec2(ModSettingGet("noita-mapcap.virtual-resolution")).x)
|
||||
magic["VIRTUAL_RESOLUTION_Y"] = tostring(Vec2(ModSettingGet("noita-mapcap.virtual-resolution")).y)
|
||||
end
|
||||
|
||||
-- Set virtual offset to be pixel perfect.
|
||||
--magic["VIRTUAL_RESOLUTION_OFFSET_X"] = tostring(Coords.VirtualOffsetPixelPerfect.x)
|
||||
--magic["VIRTUAL_RESOLUTION_OFFSET_Y"] = tostring(Coords.VirtualOffsetPixelPerfect.y)
|
||||
|
||||
-- Always expect a fullscreen mode of 0 (windowed).
|
||||
-- Capturing will not work in fullscreen.
|
||||
config["fullscreen"] = "0"
|
||||
|
||||
return config, magic
|
||||
end
|
||||
|
||||
---Will change the game settings according to `Modification.RequiredChanges()`.
|
||||
---
|
||||
---This will force close Noita!
|
||||
function Modification.AutoSet()
|
||||
local config, magic = Modification.RequiredChanges()
|
||||
Modification.SetConfig(config)
|
||||
end
|
||||
|
||||
---Will reset all settings that may have been changed by this mod.
|
||||
---
|
||||
---This will force close Noita!
|
||||
function Modification.Reset()
|
||||
local config = {
|
||||
window_w = "1280",
|
||||
window_h = "720",
|
||||
internal_size_w = "1280",
|
||||
internal_size_h = "720",
|
||||
backbuffer_width = "1280",
|
||||
backbuffer_height = "720",
|
||||
}
|
||||
|
||||
Modification.SetConfig(config)
|
||||
end
|
BIN
files/ui-gfx/dismiss-8x8.png
Normal file
After Width: | Height: | Size: 171 B |
BIN
files/ui-gfx/hint-16x16.png
Normal file
After Width: | Height: | Size: 206 B |
BIN
files/ui-gfx/open-output-16x16.png
Normal file
After Width: | Height: | Size: 253 B |
BIN
files/ui-gfx/record-16x16.png
Normal file
After Width: | Height: | Size: 231 B |
BIN
files/ui-gfx/reset-16x16.png
Normal file
After Width: | Height: | Size: 236 B |
BIN
files/ui-gfx/stop-16x16.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
files/ui-gfx/warning-16x16.png
Normal file
After Width: | Height: | Size: 217 B |
239
files/ui.lua
@ -3,17 +3,120 @@
|
||||
-- This software is released under the MIT License.
|
||||
-- https://opensource.org/licenses/MIT
|
||||
|
||||
-----------------------
|
||||
-- Load global stuff --
|
||||
-----------------------
|
||||
|
||||
-- TODO: Wrap Noita utilities and wrap them into a table: https://stackoverflow.com/questions/9540732/loadfile-without-polluting-global-environment
|
||||
require("utilities") -- Loads Noita's utilities from `data/scripts/lib/utilitites.lua`.
|
||||
|
||||
--------------------------
|
||||
-- Load library modules --
|
||||
--------------------------
|
||||
|
||||
local Utils = require("noita-api.utils")
|
||||
local ScreenCap = require("screen-capture")
|
||||
|
||||
----------
|
||||
-- Code --
|
||||
----------
|
||||
|
||||
---Returns unique IDs for the widgets.
|
||||
---`_ResetID` has to be called every time before the UI is rebuilt.
|
||||
---@return integer
|
||||
function UI:_GenID()
|
||||
self.CurrentID = (self.CurrentID or 0) + 1
|
||||
return self.CurrentID
|
||||
end
|
||||
|
||||
function UI:_ResetID()
|
||||
self.CurrentID = nil
|
||||
end
|
||||
|
||||
function UI:_DrawToolbar()
|
||||
local gui = self.gui
|
||||
GuiZSet(gui, 0)
|
||||
|
||||
GuiLayoutBeginHorizontal(gui, 2, 2, true, 2, 2)
|
||||
|
||||
if Capture.MapCapturingCtx:IsRunning() then
|
||||
local clicked, clickedRight = GuiImageButton(gui, self:_GenID(), 0, 0, "", "mods/noita-mapcap/files/ui-gfx/stop-16x16.png")
|
||||
GuiTooltip(gui, "Stop capture", "Stop the capturing process.\n \nRight click: Reset any modifications that this mod has done to Noita.")
|
||||
if clicked then Capture:StopCapturing() end
|
||||
if clickedRight then Message:ShowResetNoitaSettings() end
|
||||
else
|
||||
local clicked, clickedRight = GuiImageButton(gui, self:_GenID(), 0, 0, "", "mods/noita-mapcap/files/ui-gfx/record-16x16.png")
|
||||
GuiTooltip(gui, "Start capture", "Start the capturing process based on mod settings.\n \nRight click: Reset any modifications that this mod has done to Noita.")
|
||||
if clicked then Capture:StartCapturing() end
|
||||
if clickedRight then Message:ShowResetNoitaSettings() end
|
||||
end
|
||||
|
||||
local clicked = GuiImageButton(gui, self:_GenID(), 0, 0, "", "mods/noita-mapcap/files/ui-gfx/open-output-16x16.png")
|
||||
GuiTooltip(gui, "Open output directory", "Reveals the output directory in your file browser.")
|
||||
if clicked then os.execute("start .\\mods\\noita-mapcap\\output\\") end
|
||||
|
||||
GuiLayoutEnd(gui)
|
||||
end
|
||||
|
||||
function UI:_DrawMessages(messages)
|
||||
local gui = self.gui
|
||||
|
||||
-- Abort if there is no messages list.
|
||||
if not messages then return end
|
||||
|
||||
GuiZSet(gui, 0)
|
||||
|
||||
-- Unfortunately you can't stack multiple layout containers with the same direction.
|
||||
-- So keep track of the y position manually.
|
||||
local posY = 60
|
||||
for key, message in pairs(messages) do
|
||||
GuiZSet(gui, -10)
|
||||
GuiBeginAutoBox(gui)
|
||||
|
||||
GuiLayoutBeginHorizontal(gui, 27, posY, true, 5, 0) posY = posY + 20
|
||||
|
||||
if message.Type == "warning" or message.Type == "error" then
|
||||
GuiImage(gui, self:_GenID(), 0, 0, "mods/noita-mapcap/files/ui-gfx/warning-16x16.png", 1, 1, 0, 0, 0, "")
|
||||
elseif message.Type == "hint" or message.Type == "info" then
|
||||
GuiImage(gui, self:_GenID(), 0, 0, "mods/noita-mapcap/files/ui-gfx/hint-16x16.png", 1, 1, 0, 0, 0, "")
|
||||
else
|
||||
GuiImage(gui, self:_GenID(), 0, 0, "mods/noita-mapcap/files/ui-gfx/hint-16x16.png", 1, 1, 0, 0, 0, "")
|
||||
end
|
||||
|
||||
GuiLayoutBeginVertical(gui, 0, 0, false, 0, 0)
|
||||
if type(message.Lines) == "table" then
|
||||
for _, line in ipairs(message.Lines) do
|
||||
GuiText(gui, 0, 0, tostring(line)) posY = posY + 11
|
||||
end
|
||||
end
|
||||
if type(message.Actions) == "table" then
|
||||
posY = posY + 11
|
||||
for _, action in ipairs(message.Actions) do
|
||||
local clicked = GuiButton(gui, self:_GenID(), 0, 11, ">" .. action.Name .. " <") posY = posY + 11
|
||||
if action.Hint or action.HintDesc then
|
||||
GuiTooltip(gui, action.Hint or "", action.HintDesc or "")
|
||||
end
|
||||
if clicked then
|
||||
local ok, err = pcall(action.Callback)
|
||||
if not ok then
|
||||
Message:ShowRuntimeError("MessageAction", "Message action error:", err)
|
||||
end
|
||||
messages[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
GuiLayoutEnd(gui)
|
||||
|
||||
local clicked = GuiImageButton(gui, self:_GenID(), 5, 0, "", "mods/noita-mapcap/files/ui-gfx/dismiss-8x8.png")
|
||||
--GuiTooltip(gui, "Dismiss message", "")
|
||||
if clicked then messages[key] = nil end
|
||||
|
||||
GuiLayoutEnd(gui)
|
||||
|
||||
GuiZSet(gui, -9)
|
||||
GuiEndAutoBoxNinePiece(gui, 5, 0, 0, false, 0, "data/ui_gfx/decorations/9piece0_gray.png", "data/ui_gfx/decorations/9piece0_gray.png")
|
||||
end
|
||||
end
|
||||
|
||||
---Stops the UI from drawing for the next few frames.
|
||||
---@param frames integer
|
||||
function UI:SuspendDrawing(frames)
|
||||
self.suspendFrames = math.max(self.suspendFrames or 0, frames)
|
||||
end
|
||||
@ -26,131 +129,15 @@ function UI:Draw()
|
||||
if self.suspendFrames and self.suspendFrames > 0 then self.suspendFrames = self.suspendFrames - 1 return end
|
||||
self.suspendFrames = nil
|
||||
|
||||
-- Reset ID generator.
|
||||
self:_ResetID()
|
||||
|
||||
GuiStartFrame(gui)
|
||||
|
||||
GuiLayoutBeginVertical(gui, 50, 20, false, 0, 0)
|
||||
GuiIdPushString(gui, "noita-mapcap")
|
||||
|
||||
GuiTextCentered(gui, 0, 0, "Heyho")
|
||||
self:_DrawToolbar()
|
||||
self:_DrawMessages(Message.List)
|
||||
|
||||
GuiLayoutEnd(gui)
|
||||
|
||||
if true then return end
|
||||
|
||||
GuiStartFrame(modGUI)
|
||||
|
||||
GuiLayoutBeginVertical(modGUI, 50, 20)
|
||||
if not UiProgress then
|
||||
-- Show informations
|
||||
local problem
|
||||
local rect = ScreenCap.GetRect()
|
||||
|
||||
if not rect then
|
||||
GuiTextCentered(modGUI, 0, 0, '!!! WARNING !!! You are not using "Windowed" mode.')
|
||||
GuiTextCentered(modGUI, 0, 0, "To fix the problem, do one of these:")
|
||||
GuiTextCentered(modGUI, 0, 0, '- Change the window mode in the game options to "Windowed"')
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
problem = true
|
||||
end
|
||||
|
||||
if rect then
|
||||
local screenWidth, screenHeight = rect.right - rect.left, rect.bottom - rect.top
|
||||
local virtualWidth, virtualHeight = tonumber(MagicNumbersGetValue("VIRTUAL_RESOLUTION_X")), tonumber(MagicNumbersGetValue("VIRTUAL_RESOLUTION_Y"))
|
||||
local ratioX, ratioY = screenWidth / virtualWidth, screenHeight / virtualHeight
|
||||
--GuiTextCentered(modGUI, 0, 0, string.format("SCREEN_RESOLUTION_*: %d, %d", screenWidth, screenHeight))
|
||||
--GuiTextCentered(modGUI, 0, 0, string.format("VIRTUAL_RESOLUTION_*: %d, %d", virtualWidth, virtualHeight))
|
||||
if math.abs(ratioX - CAPTURE_PIXEL_SIZE) > 0.0001 or math.abs(ratioY - CAPTURE_PIXEL_SIZE) > 0.0001 then
|
||||
GuiTextCentered(modGUI, 0, 0, "!!! WARNING !!! Screen and virtual resolution differ.")
|
||||
GuiTextCentered(modGUI, 0, 0, "To fix the problem, do one of these:")
|
||||
GuiTextCentered(modGUI, 0, 0, string.format(
|
||||
"- Change the resolution in the game options to %dx%d",
|
||||
virtualWidth * CAPTURE_PIXEL_SIZE,
|
||||
virtualHeight * CAPTURE_PIXEL_SIZE
|
||||
))
|
||||
GuiTextCentered(modGUI, 0, 0, string.format(
|
||||
"- Change the virtual resolution in the mod to %dx%d",
|
||||
screenWidth / CAPTURE_PIXEL_SIZE,
|
||||
screenHeight / CAPTURE_PIXEL_SIZE
|
||||
))
|
||||
if math.abs(ratioX - ratioY) < 0.0001 then
|
||||
GuiTextCentered(modGUI, 0, 0, string.format("- Change the CAPTURE_PIXEL_SIZE in the mod to %f", ratioX))
|
||||
end
|
||||
GuiTextCentered(modGUI, 0, 0, '- Make sure that the console is not selected')
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
problem = true
|
||||
end
|
||||
end
|
||||
|
||||
if not Utils.FileExists("mods/noita-mapcap/bin/capture-b/capture.dll") then
|
||||
GuiTextCentered(modGUI, 0, 0, "!!! WARNING !!! Can't find library for screenshots.")
|
||||
GuiTextCentered(modGUI, 0, 0, "To fix the problem, do one of these:")
|
||||
GuiTextCentered(modGUI, 0, 0, "- Redownload a release of this mod from GitHub, don't download the sourcecode")
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
problem = true
|
||||
end
|
||||
|
||||
if not Utils.FileExists("mods/noita-mapcap/bin/stitch/stitch.exe") then
|
||||
GuiTextCentered(modGUI, 0, 0, "!!! WARNING !!! Can't find software for stitching.")
|
||||
GuiTextCentered(modGUI, 0, 0, "You can still take screenshots, but you won't be able to stitch those screenshots.")
|
||||
GuiTextCentered(modGUI, 0, 0, "To fix the problem, do one of these:")
|
||||
GuiTextCentered(modGUI, 0, 0, "- Redownload a release of this mod from GitHub, don't download the sourcecode")
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
problem = true
|
||||
end
|
||||
|
||||
if not problem then
|
||||
GuiTextCentered(modGUI, 0, 0, "No problems found.")
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
end
|
||||
|
||||
GuiTextCentered(modGUI, 0, 0, "You can freely look around and search a place to start capturing.")
|
||||
GuiTextCentered(modGUI, 0, 0, "When started the mod will take pictures automatically.")
|
||||
GuiTextCentered(modGUI, 0, 0, "Use ESC to pause, and close the game to stop the process.")
|
||||
GuiTextCentered(modGUI, 0, 0, 'You can resume capturing just by restarting noita and pressing "Start capturing map" again,')
|
||||
GuiTextCentered(modGUI, 0, 0, "the mod will skip already captured files.")
|
||||
GuiTextCentered(modGUI, 0, 0, 'If you want to start a new map, you have to delete all images from the "output" folder!')
|
||||
--GuiTextCentered(modGUI, 0, 0, " ")
|
||||
--GuiTextCentered(modGUI, 0, 0, MagicNumbersGetValue("VIRTUAL_RESOLUTION_X"))
|
||||
--GuiTextCentered(modGUI, 0, 0, MagicNumbersGetValue("VIRTUAL_RESOLUTION_Y"))
|
||||
--GuiTextCentered(modGUI, 0, 0, MagicNumbersGetValue("VIRTUAL_RESOLUTION_OFFSET_X"))
|
||||
--GuiTextCentered(modGUI, 0, 0, MagicNumbersGetValue("VIRTUAL_RESOLUTION_OFFSET_Y"))
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
if GuiButton(modGUI, 0, 0, ">> Start capturing map around view <<", 1) then
|
||||
UiProgress = {}
|
||||
startCapturingSpiral()
|
||||
end
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
if GuiButton(modGUI, 0, 0, ">> Start capturing base layout <<", 1) then
|
||||
UiProgress = {}
|
||||
startCapturingHilbert(CAPTURE_AREA_BASE_LAYOUT)
|
||||
end
|
||||
if GuiButton(modGUI, 0, 0, ">> Start capturing main world <<", 1) then
|
||||
UiProgress = {}
|
||||
startCapturingHilbert(CAPTURE_AREA_MAIN_WORLD)
|
||||
end
|
||||
if GuiButton(modGUI, 0, 0, ">> Start capturing extended map <<", 1) then
|
||||
UiProgress = {}
|
||||
startCapturingHilbert(CAPTURE_AREA_EXTENDED)
|
||||
end
|
||||
if GuiButton(modGUI, 0, 0, ">> Start capturing run live <<", 1) then
|
||||
UiProgress = {}
|
||||
StartCapturingLive()
|
||||
end
|
||||
GuiTextCentered(modGUI, 0, 0, " ")
|
||||
elseif not UiProgress.Done then
|
||||
-- Show progress
|
||||
local x, y = GameGetCameraPos()
|
||||
GuiTextCentered(modGUI, 0, 0, string.format("Coordinates: %d, %d", x, y))
|
||||
GuiTextCentered(modGUI, 0, 0, string.format("Waiting %d frames...", UiCaptureDelay))
|
||||
if UiProgress.Progress then
|
||||
GuiTextCentered(modGUI, 0, 0, progressBarString(
|
||||
UiProgress, { BarLength = 100, CharFull = "l", CharEmpty = ".", Format = "|%s| [%d / %d] [%1.2f%%]" }
|
||||
))
|
||||
end
|
||||
if UiCaptureProblem then
|
||||
GuiTextCentered(modGUI, 0, 0, string.format("A problem occurred while capturing: %s", UiCaptureProblem))
|
||||
end
|
||||
else
|
||||
GuiTextCentered(modGUI, 0, 0, "Done!")
|
||||
end
|
||||
GuiLayoutEnd(modGUI)
|
||||
GuiIdPop(gui)
|
||||
end
|
||||
|
35
init.lua
@ -12,6 +12,7 @@
|
||||
local libPath = "mods/noita-mapcap/files/libraries/"
|
||||
dofile(libPath .. "noita-api/compatibility.lua")(libPath)
|
||||
|
||||
-- TODO: Replace Noita's coroutine lib with something better
|
||||
if not async then
|
||||
require("coroutines") -- Loads Noita's coroutines library from `data/scripts/lib/coroutines.lua`.
|
||||
end
|
||||
@ -23,7 +24,7 @@ end
|
||||
local CameraAPI = require("noita-api.camera")
|
||||
local Coords = require("coordinates")
|
||||
local DebugAPI = require("noita-api.debug")
|
||||
--local LiveReload = require("noita-api.live-reload")
|
||||
local LiveReload = require("noita-api.live-reload")
|
||||
local Vec2 = require("noita-api.vec2")
|
||||
|
||||
-----------------------
|
||||
@ -31,7 +32,10 @@ local Vec2 = require("noita-api.vec2")
|
||||
-----------------------
|
||||
|
||||
Capture = Capture or {}
|
||||
Check = Check or {}
|
||||
Config = Config or {}
|
||||
Message = Message or {}
|
||||
Modification = Modification or {}
|
||||
UI = UI or {}
|
||||
|
||||
-------------------------------
|
||||
@ -40,6 +44,9 @@ UI = UI or {}
|
||||
|
||||
dofile("mods/noita-mapcap/files/capture.lua")
|
||||
dofile("mods/noita-mapcap/files/config.lua")
|
||||
dofile("mods/noita-mapcap/files/check.lua")
|
||||
dofile("mods/noita-mapcap/files/message.lua")
|
||||
dofile("mods/noita-mapcap/files/modification.lua")
|
||||
dofile("mods/noita-mapcap/files/ui.lua")
|
||||
|
||||
--------------------
|
||||
@ -48,9 +55,13 @@ dofile("mods/noita-mapcap/files/ui.lua")
|
||||
|
||||
---Called in order upon loading a new(?) game.
|
||||
function OnModPreInit()
|
||||
-- Set magic numbers based on mod settings.
|
||||
local config, magic = Modification.RequiredChanges()
|
||||
Modification.SetMagicNumbers(magic)
|
||||
|
||||
-- Override virtual resolution and some other stuff.
|
||||
--ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/64.xml")
|
||||
--ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/fast-cam.xml")
|
||||
--ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/1024.xml")
|
||||
ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/fast-cam.xml")
|
||||
--ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/no-ui.xml")
|
||||
--ModMagicNumbersFileAdd("mods/noita-mapcap/files/magic-numbers/offset.xml")
|
||||
|
||||
@ -84,19 +95,29 @@ end
|
||||
|
||||
---Called *every* time the game is about to start updating the world.
|
||||
function OnWorldPreUpdate()
|
||||
Message:CatchException("OnWorldPreUpdate", function ()
|
||||
|
||||
-- Coroutines aren't run every frame in this lua sandbox, do it manually here.
|
||||
wake_up_waiting_threads(1)
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
---Called *every* time the game has finished updating the world.
|
||||
function OnWorldPostUpdate()
|
||||
-- Draw UI after coroutines have been resumed.
|
||||
UI:Draw()
|
||||
|
||||
-- Reload mod every 60 frames.
|
||||
-- This allows live updates to the mod while Noita is running.
|
||||
-- !!! DISABLE THIS LINE AND THE CORRESPONDING REQUIRE BEFORE COMMITTING !!!
|
||||
--LiveReload:Reload("mods/noita-mapcap/", 60)
|
||||
|
||||
Message:CatchException("OnWorldPostUpdate", function ()
|
||||
|
||||
Check:Resolutions(60)
|
||||
|
||||
-- Draw UI after coroutines have been resumed.
|
||||
UI:Draw()
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
---Called when the biome config is loaded.
|
||||
@ -109,6 +130,8 @@ function OnMagicNumbersAndWorldSeedInitialized()
|
||||
-- Get resolutions for correct coordinate transformations.
|
||||
-- This needs to be done once all magic numbers are set.
|
||||
Coords:ReadResolutions()
|
||||
|
||||
Check:Startup()
|
||||
end
|
||||
|
||||
---Called when the game is paused or unpaused.
|
||||
|
74
settings.lua
@ -12,6 +12,7 @@
|
||||
local libPath = "mods/noita-mapcap/files/libraries/"
|
||||
dofile(libPath .. "noita-api/compatibility.lua")(libPath)
|
||||
|
||||
-- TODO: Replace Noita's mod settings lib with something better. Or at least wrap it: https://stackoverflow.com/questions/9540732/loadfile-without-polluting-global-environment
|
||||
require("mod_settings") -- Loads Noita's mod settings library from `data/scripts/lib/mod_settings.lua`.
|
||||
|
||||
--------------------------
|
||||
@ -85,6 +86,7 @@ modSettings = {
|
||||
ui_name = " Origin",
|
||||
ui_description = "",
|
||||
value_default = "0,0",
|
||||
allowed_characters = "0123456789,",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
show_fn = function() return not modSettings:Get("capture-mode-spiral-origin").hidden and modSettings:GetNextValue("capture-mode-spiral-origin") == "custom" end,
|
||||
},
|
||||
@ -102,6 +104,7 @@ modSettings = {
|
||||
ui_name = " Top left corner",
|
||||
ui_description = "The top left corner of the to be captured rectangle.",
|
||||
value_default = "-512,-512",
|
||||
allowed_characters = "0123456789,",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
show_fn = function() return not modSettings:Get("area").hidden and modSettings:GetNextValue("area") == "custom" end,
|
||||
},
|
||||
@ -110,6 +113,7 @@ modSettings = {
|
||||
ui_name = " Bottom right corner",
|
||||
ui_description = "The bottom right corner of the to be captured rectangle.",
|
||||
value_default = "512,512",
|
||||
allowed_characters = "0123456789,",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
show_fn = function() return not modSettings:Get("area").hidden and modSettings:GetNextValue("area") == "custom" end,
|
||||
},
|
||||
@ -134,9 +138,9 @@ modSettings = {
|
||||
show_fn = function() return modSettings:GetNextValue("capture-mode") ~= "live" end,
|
||||
},
|
||||
{
|
||||
id = "pixel-size",
|
||||
ui_name = "Pixel size",
|
||||
ui_description = "How big a single resulting pixel will be.\nThis is the ratio of image to world pixels.\nA setting of 0 disables any scaling.",
|
||||
id = "pixel-scale",
|
||||
ui_name = "Pixel scale",
|
||||
ui_description = "How big a single resulting pixel will be.\nThis is the ratio of image to world pixels.\nA setting of 0 disables any scaling.\n \nDon't change while capturing,\nOr you will get unstitchable results.",
|
||||
value_default = 1,
|
||||
value_min = 0,
|
||||
value_max = 8,
|
||||
@ -166,6 +170,7 @@ modSettings = {
|
||||
ui_name = " Window resolution",
|
||||
ui_description = "Size of the window in screen pixels.",
|
||||
value_default = "1024,1024",
|
||||
allowed_characters = "0123456789,",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME_RESTART,
|
||||
show_fn = function()
|
||||
return (not modSettings:Get("advanced.settings.custom-resolution-live").hidden and modSettings:GetNextValue("advanced.settings.custom-resolution-live"))
|
||||
@ -177,6 +182,7 @@ modSettings = {
|
||||
ui_name = " Internal resolution",
|
||||
ui_description = "Size of the viewport in screen pixels.\nIdeally set to the window resolution.",
|
||||
value_default = "1024,1024",
|
||||
allowed_characters = "0123456789,",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME_RESTART,
|
||||
show_fn = function()
|
||||
return (not modSettings:Get("advanced.settings.custom-resolution-live").hidden and modSettings:GetNextValue("advanced.settings.custom-resolution-live"))
|
||||
@ -188,12 +194,58 @@ modSettings = {
|
||||
ui_name = " Virtual resolution",
|
||||
ui_description = "Size of the viewport in world pixels.",
|
||||
value_default = "1024,1024",
|
||||
allowed_characters = "0123456789,",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME_RESTART,
|
||||
show_fn = function()
|
||||
return (not modSettings:Get("advanced.settings.custom-resolution-live").hidden and modSettings:GetNextValue("advanced.settings.custom-resolution-live"))
|
||||
or (not modSettings:Get("advanced.settings.custom-resolution-other").hidden and modSettings:GetNextValue("advanced.settings.custom-resolution-other"))
|
||||
end,
|
||||
},
|
||||
{
|
||||
ui_fn = mod_setting_vertical_spacing,
|
||||
not_setting = true,
|
||||
show_fn = function() return modSettings:GetNextValue("capture-mode") == "live" end,
|
||||
},
|
||||
{
|
||||
id = "live-interval",
|
||||
ui_name = "Capture interval",
|
||||
ui_description = "Capturing interval in frames.",
|
||||
value_default = 60,
|
||||
value_min = 5,
|
||||
value_max = 240,
|
||||
value_display_multiplier = 1,
|
||||
value_display_formatting = " $0 frames",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
show_fn = function() return modSettings:GetNextValue("capture-mode") == "live" end,
|
||||
},
|
||||
{
|
||||
id = "live-min-distance",
|
||||
ui_name = "Min. capture distance",
|
||||
ui_description = "The distance the viewport has to move to allow another screenshot.\nIn world pixels.",
|
||||
value_default = 10,
|
||||
value_min = 0,
|
||||
value_max = 200,
|
||||
value_display_multiplier = 1,
|
||||
value_display_formatting = " $0 pixels",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
show_fn = function() return modSettings:GetNextValue("capture-mode") == "live" end,
|
||||
},
|
||||
{
|
||||
id = "live-max-distance",
|
||||
ui_name = "Max. capture distance",
|
||||
ui_description = "The distance the viewport has to move to force another screenshot.\nIn world pixels.",
|
||||
value_default = 50,
|
||||
value_min = 0,
|
||||
value_max = 200,
|
||||
value_display_multiplier = 1,
|
||||
value_display_formatting = " $0 pixels",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
show_fn = function() return modSettings:GetNextValue("capture-mode") == "live" end,
|
||||
},
|
||||
{
|
||||
ui_fn = mod_setting_vertical_spacing,
|
||||
not_setting = true,
|
||||
},
|
||||
{
|
||||
id = "capture-entities",
|
||||
ui_name = "Capture entities",
|
||||
@ -227,22 +279,6 @@ modSettings = {
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
category_id = "actions",
|
||||
ui_name = "ACTIONS",
|
||||
foldable = true,
|
||||
_folded = true,
|
||||
settings = {
|
||||
{
|
||||
id = "button-open-output",
|
||||
ui_name = "Open output directory",
|
||||
ui_description = "Reveals the output directory in your file browser.",
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
ui_fn = customSettingButton,
|
||||
change_fn = function() print("test") end,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
---Hide/unhide some settings based on other settings.
|
||||
|