mirror of
https://github.com/Dadido3/noita-mapcap.git
synced 2024-11-18 17:17:31 +00:00
Several updates
- Add full map capture mode - Show error on screen if screencapturing failed - Add additional frame delay for jumps larger than the grid size - Show progress for map capture mode - Tweak streaming chunk target - Update README.md
This commit is contained in:
parent
c87a4d05d0
commit
8cf46232de
@ -22,7 +22,9 @@ A resulting image with close to 3 gigapixels can be [seen here](https://easyzoom
|
|||||||
3. Unpack it into your mods folder, so that you get the following file structure `.../Noita/mods/noita-mapcap/mod.xml`.
|
3. Unpack it into your mods folder, so that you get the following file structure `.../Noita/mods/noita-mapcap/mod.xml`.
|
||||||
4. Set your resolution to 1280x720, and use the `Windowed` mode. (Not `Fullscreen (Windowed)`!) If you have to use a different resolution, see advanced usage.
|
4. Set your resolution to 1280x720, and use the `Windowed` mode. (Not `Fullscreen (Windowed)`!) If you have to use a different resolution, see advanced usage.
|
||||||
5. Enable the mod and restart Noita.
|
5. Enable the mod and restart Noita.
|
||||||
6. In the game you should see a `>> Start capturing map <<` text on the screen, click it.
|
6. In the game you should see text on screen.
|
||||||
|
- Either press `>> Start capturing map around view <<` to capture in a spiral around your current view.
|
||||||
|
- Or press `>> Start capturing full map <<` to capture the whole map.
|
||||||
7. The screen will jump around, and the game will take screenshots automatically.
|
7. The screen will jump around, and the game will take screenshots automatically.
|
||||||
- Screenshots are saved in `.../Noita/mods/noita-mapcap/output/`.
|
- Screenshots are saved in `.../Noita/mods/noita-mapcap/output/`.
|
||||||
- Don't cover the game window.
|
- Don't cover the game window.
|
||||||
@ -50,6 +52,8 @@ The following two formulae have to be true:
|
|||||||
|
|
||||||
You can also change how much the tiles overlap by adjusting the `CAPTURE_GRID_SIZE` in `.../Noita/mods/noita-mapcap/files/capture.lua`. If you increase the grid size, you can capture more area per time. But on the other hand the stitcher may not be able to remove artifacts if the tiles don't overlap enough.
|
You can also change how much the tiles overlap by adjusting the `CAPTURE_GRID_SIZE` in `.../Noita/mods/noita-mapcap/files/capture.lua`. If you increase the grid size, you can capture more area per time. But on the other hand the stitcher may not be able to remove artifacts if the tiles don't overlap enough.
|
||||||
|
|
||||||
|
The rectangle for the full map capture mode is defined in `.../Noita/mods/noita-mapcap/files/capture.lua`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
@ -6,8 +6,14 @@
|
|||||||
CAPTURE_PIXEL_SIZE = 1 -- Screen to virtual pixel ratio
|
CAPTURE_PIXEL_SIZE = 1 -- Screen to virtual pixel ratio
|
||||||
CAPTURE_GRID_SIZE = 420 -- in ingame pixels. There will always be 3 to 6 images overlapping
|
CAPTURE_GRID_SIZE = 420 -- in ingame pixels. There will always be 3 to 6 images overlapping
|
||||||
CAPTURE_DELAY = 15 -- in frames
|
CAPTURE_DELAY = 15 -- in frames
|
||||||
|
CAPTURE_BIGJUMP_DELAY = 20 -- in frames. Additional delay after doing a "larger than grid jump"
|
||||||
CAPTURE_FORCE_HP = 4 -- * 25HP
|
CAPTURE_FORCE_HP = 4 -- * 25HP
|
||||||
|
|
||||||
|
CAPTURE_LEFT = -25000 -- in ingame pixels. Left edge of the full map capture rectangle
|
||||||
|
CAPTURE_TOP = -36000 -- in ingame pixels. Top edge of the full map capture rectangle
|
||||||
|
CAPTURE_RIGHT = 25000 -- in ingame pixels. Right edge of the full map capture rectangle (Pixels are not included in the rectangle)
|
||||||
|
CAPTURE_BOTTOM = 36000 -- in ingame pixels. Bottom edge of the full map capture rectangle (Pixels are not included in the rectangle)
|
||||||
|
|
||||||
local function preparePlayer()
|
local function preparePlayer()
|
||||||
local playerEntity = getPlayer()
|
local playerEntity = getPlayer()
|
||||||
addEffectToEntity(playerEntity, "PROTECTION_ALL")
|
addEffectToEntity(playerEntity, "PROTECTION_ALL")
|
||||||
@ -25,9 +31,8 @@ local function resetPlayer()
|
|||||||
setPlayerHP(CAPTURE_FORCE_HP)
|
setPlayerHP(CAPTURE_FORCE_HP)
|
||||||
end
|
end
|
||||||
|
|
||||||
function startCapturing()
|
function startCapturingSpiral()
|
||||||
local ox, oy = GameGetCameraPos()
|
local ox, oy = GameGetCameraPos()
|
||||||
--getPlayerPos()
|
|
||||||
ox, oy = math.floor(ox / CAPTURE_GRID_SIZE) * CAPTURE_GRID_SIZE, math.floor(oy / CAPTURE_GRID_SIZE) * CAPTURE_GRID_SIZE
|
ox, oy = math.floor(ox / CAPTURE_GRID_SIZE) * CAPTURE_GRID_SIZE, math.floor(oy / CAPTURE_GRID_SIZE) * CAPTURE_GRID_SIZE
|
||||||
local x, y = ox, oy
|
local x, y = ox, oy
|
||||||
|
|
||||||
@ -47,7 +52,9 @@ function startCapturing()
|
|||||||
wait(CAPTURE_DELAY - 1)
|
wait(CAPTURE_DELAY - 1)
|
||||||
UiHide = true -- Hide UI while capturing the screenshot
|
UiHide = true -- Hide UI while capturing the screenshot
|
||||||
wait(1)
|
wait(1)
|
||||||
TriggerCapture(rx, ry)
|
if not TriggerCapture(rx, ry) then
|
||||||
|
UiCaptureProblem = "Screen capture failed. Please restart Noita."
|
||||||
|
end
|
||||||
UiHide = false
|
UiHide = false
|
||||||
end
|
end
|
||||||
x, y = x + CAPTURE_GRID_SIZE, y
|
x, y = x + CAPTURE_GRID_SIZE, y
|
||||||
@ -60,7 +67,9 @@ function startCapturing()
|
|||||||
wait(CAPTURE_DELAY - 1)
|
wait(CAPTURE_DELAY - 1)
|
||||||
UiHide = true
|
UiHide = true
|
||||||
wait(1)
|
wait(1)
|
||||||
TriggerCapture(rx, ry)
|
if not TriggerCapture(rx, ry) then
|
||||||
|
UiCaptureProblem = "Screen capture failed. Please restart Noita."
|
||||||
|
end
|
||||||
UiHide = false
|
UiHide = false
|
||||||
end
|
end
|
||||||
x, y = x, y + CAPTURE_GRID_SIZE
|
x, y = x, y + CAPTURE_GRID_SIZE
|
||||||
@ -74,7 +83,9 @@ function startCapturing()
|
|||||||
wait(CAPTURE_DELAY - 1)
|
wait(CAPTURE_DELAY - 1)
|
||||||
UiHide = true
|
UiHide = true
|
||||||
wait(1)
|
wait(1)
|
||||||
TriggerCapture(rx, ry)
|
if not TriggerCapture(rx, ry) then
|
||||||
|
UiCaptureProblem = "Screen capture failed. Please restart Noita."
|
||||||
|
end
|
||||||
UiHide = false
|
UiHide = false
|
||||||
end
|
end
|
||||||
x, y = x - CAPTURE_GRID_SIZE, y
|
x, y = x - CAPTURE_GRID_SIZE, y
|
||||||
@ -87,7 +98,9 @@ function startCapturing()
|
|||||||
wait(CAPTURE_DELAY - 1)
|
wait(CAPTURE_DELAY - 1)
|
||||||
UiHide = true
|
UiHide = true
|
||||||
wait(1)
|
wait(1)
|
||||||
TriggerCapture(rx, ry)
|
if not TriggerCapture(rx, ry) then
|
||||||
|
UiCaptureProblem = "Screen capture failed. Please restart Noita."
|
||||||
|
end
|
||||||
UiHide = false
|
UiHide = false
|
||||||
end
|
end
|
||||||
x, y = x, y - CAPTURE_GRID_SIZE
|
x, y = x, y - CAPTURE_GRID_SIZE
|
||||||
@ -96,3 +109,63 @@ function startCapturing()
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function startCapturingHilbert()
|
||||||
|
local ox, oy = GameGetCameraPos()
|
||||||
|
|
||||||
|
-- Get size of the rectangle in grid/chunk coordinates
|
||||||
|
local gridLeft = math.floor(CAPTURE_LEFT / CAPTURE_GRID_SIZE)
|
||||||
|
local gridTop = math.floor(CAPTURE_TOP / CAPTURE_GRID_SIZE)
|
||||||
|
local gridRight = math.ceil(CAPTURE_RIGHT / CAPTURE_GRID_SIZE) + 1
|
||||||
|
local gridBottom = math.ceil(CAPTURE_BOTTOM / CAPTURE_GRID_SIZE) + 1
|
||||||
|
|
||||||
|
-- Size of the grid in chunks
|
||||||
|
local gridWidth = gridRight - gridLeft
|
||||||
|
local gridHeight = gridBottom - gridTop
|
||||||
|
|
||||||
|
-- Hilbert curve can only fit into a square, so get the longest side
|
||||||
|
local gridPOTSize = math.ceil(math.log(math.max(gridWidth, gridHeight)) / math.log(2))
|
||||||
|
-- Max size (Already rounded up to the next power of two)
|
||||||
|
local gridMaxSize = math.pow(2, gridPOTSize)
|
||||||
|
|
||||||
|
local t, tLimit = 0, gridMaxSize * gridMaxSize
|
||||||
|
|
||||||
|
UiProgress = {Progress = 0, Max = gridWidth * gridHeight}
|
||||||
|
|
||||||
|
preparePlayer()
|
||||||
|
|
||||||
|
GameSetCameraFree(true)
|
||||||
|
|
||||||
|
-- Coroutine to calculate next coordinate, and trigger screenshots
|
||||||
|
async(
|
||||||
|
function()
|
||||||
|
while t < tLimit do
|
||||||
|
local hx, hy = mapHilbert(t, gridPOTSize)
|
||||||
|
if hx < gridWidth and hy < gridHeight then
|
||||||
|
local x, y = (hx + gridLeft) * CAPTURE_GRID_SIZE, (hy + gridTop) * CAPTURE_GRID_SIZE
|
||||||
|
local rx, ry = x * CAPTURE_PIXEL_SIZE, y * CAPTURE_PIXEL_SIZE
|
||||||
|
if not fileExists(string.format("mods/noita-mapcap/output/%d,%d.png", rx, ry)) then
|
||||||
|
GameSetCameraPos(x, y)
|
||||||
|
|
||||||
|
-- "Larger than grid jump" delay
|
||||||
|
if math.abs(x - ox) > CAPTURE_GRID_SIZE or math.abs(y - oy) > CAPTURE_GRID_SIZE then
|
||||||
|
wait(CAPTURE_BIGJUMP_DELAY)
|
||||||
|
end
|
||||||
|
ox, oy = x, y
|
||||||
|
|
||||||
|
wait(CAPTURE_DELAY - 1)
|
||||||
|
UiHide = true -- Hide UI while capturing the screenshot
|
||||||
|
wait(1)
|
||||||
|
if not TriggerCapture(rx, ry) then
|
||||||
|
UiCaptureProblem = "Screen capture failed. Please restart Noita."
|
||||||
|
end
|
||||||
|
UiHide = false
|
||||||
|
end
|
||||||
|
UiProgress.Progress = UiProgress.Progress + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
t = t + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
48
files/hilbert.lua
Normal file
48
files/hilbert.lua
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
-- Copyright (c) 2019 David Vogel
|
||||||
|
--
|
||||||
|
-- This software is released under the MIT License.
|
||||||
|
-- https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
local function hilbertRotate(n, x, y, rx, ry)
|
||||||
|
if not ry then
|
||||||
|
if rx then
|
||||||
|
x = n - 1 - x
|
||||||
|
y = n - 1 - y
|
||||||
|
end
|
||||||
|
|
||||||
|
x, y = y, x
|
||||||
|
end
|
||||||
|
return x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Maps a variable t to a hilbert curve with the side length of 2^potSize (Power of two size)
|
||||||
|
function mapHilbert(t, potSize)
|
||||||
|
local size = math.pow(2, potSize)
|
||||||
|
local x, y = 0, 0
|
||||||
|
|
||||||
|
if t < 0 or t >= size * size then
|
||||||
|
error("Variable t is outside of the range")
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 0, potSize - 1, 1 do
|
||||||
|
local iPOT = math.pow(2, i)
|
||||||
|
local rx = bit.band(t, 2) == 2
|
||||||
|
local ry = bit.band(t, 1) == 1
|
||||||
|
if rx then
|
||||||
|
ry = not ry
|
||||||
|
end
|
||||||
|
|
||||||
|
x, y = hilbertRotate(iPOT, x, y, rx, ry)
|
||||||
|
|
||||||
|
if rx then
|
||||||
|
x = x + iPOT
|
||||||
|
end
|
||||||
|
if ry then
|
||||||
|
y = y + iPOT
|
||||||
|
end
|
||||||
|
|
||||||
|
t = math.floor(t / 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
return x, y
|
||||||
|
end
|
@ -10,6 +10,7 @@ dofile("data/scripts/perks/perk_list.lua")
|
|||||||
|
|
||||||
dofile("mods/noita-mapcap/files/compatibility.lua")
|
dofile("mods/noita-mapcap/files/compatibility.lua")
|
||||||
dofile("mods/noita-mapcap/files/util.lua")
|
dofile("mods/noita-mapcap/files/util.lua")
|
||||||
|
dofile("mods/noita-mapcap/files/hilbert.lua")
|
||||||
dofile("mods/noita-mapcap/files/external.lua")
|
dofile("mods/noita-mapcap/files/external.lua")
|
||||||
dofile("mods/noita-mapcap/files/capture.lua")
|
dofile("mods/noita-mapcap/files/capture.lua")
|
||||||
dofile("mods/noita-mapcap/files/ui.lua")
|
dofile("mods/noita-mapcap/files/ui.lua")
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
<MagicNumbers VIRTUAL_RESOLUTION_X="1280"
|
<MagicNumbers VIRTUAL_RESOLUTION_X="1280"
|
||||||
VIRTUAL_RESOLUTION_Y="720"
|
VIRTUAL_RESOLUTION_Y="720"
|
||||||
STREAMING_CHUNK_TARGET="24"
|
STREAMING_CHUNK_TARGET="16"
|
||||||
DRAW_PARALLAX_BACKGROUND="0"
|
DRAW_PARALLAX_BACKGROUND="0"
|
||||||
DEBUG_FREE_CAMERA_SPEED="10"
|
DEBUG_FREE_CAMERA_SPEED="10"
|
||||||
DEBUG_NO_LOGO_SPLASHES="1"
|
DEBUG_NO_LOGO_SPLASHES="1"
|
||||||
DEBUG_PAUSE_GRID_UPDATE="1"
|
DEBUG_PAUSE_GRID_UPDATE="1"
|
||||||
DEBUG_PAUSE_BOX2D="1"
|
DEBUG_PAUSE_BOX2D="1"
|
||||||
DEBUG_DISABLE_POSTFX_DITHERING="1"
|
DEBUG_DISABLE_POSTFX_DITHERING="1"
|
||||||
DEBUG_NO_PAUSE_ON_WINDOW_FOCUS_LOST="1"
|
DEBUG_NO_PAUSE_ON_WINDOW_FOCUS_LOST="1">
|
||||||
DEBUG_LUA="1">
|
|
||||||
</MagicNumbers>
|
</MagicNumbers>
|
||||||
|
26
files/ui.lua
26
files/ui.lua
@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
UiHide = false
|
UiHide = false
|
||||||
local UiReduce = false
|
local UiReduce = false
|
||||||
|
UiProgress = nil
|
||||||
|
UiCaptureProblem = nil
|
||||||
|
|
||||||
async_loop(
|
async_loop(
|
||||||
function()
|
function()
|
||||||
@ -86,7 +88,7 @@ async_loop(
|
|||||||
end
|
end
|
||||||
|
|
||||||
GuiTextCentered(modGUI, 0, 0, "You can freely look around and search a place to start capturing.")
|
GuiTextCentered(modGUI, 0, 0, "You can freely look around and search a place to start capturing.")
|
||||||
GuiTextCentered(modGUI, 0, 0, "The mod will then take images in a spiral around your current view.")
|
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, "Use ESC to pause, and close the game to stop the process.")
|
||||||
GuiTextCentered(
|
GuiTextCentered(
|
||||||
modGUI,
|
modGUI,
|
||||||
@ -102,8 +104,12 @@ async_loop(
|
|||||||
'If you want to start a new map, you have to delete all images from the "output" folder!'
|
'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, " ")
|
||||||
if GuiButton(modGUI, 0, 0, ">> Start capturing map <<", 1) then
|
if GuiButton(modGUI, 0, 0, ">> Start capturing map around view <<", 1) then
|
||||||
startCapturing()
|
startCapturingSpiral()
|
||||||
|
UiReduce = true
|
||||||
|
end
|
||||||
|
if GuiButton(modGUI, 0, 0, ">> Start capturing full map <<", 1) then
|
||||||
|
startCapturingHilbert()
|
||||||
UiReduce = true
|
UiReduce = true
|
||||||
end
|
end
|
||||||
GuiTextCentered(modGUI, 0, 0, " ")
|
GuiTextCentered(modGUI, 0, 0, " ")
|
||||||
@ -111,6 +117,20 @@ async_loop(
|
|||||||
if not UiHide then
|
if not UiHide then
|
||||||
local x, y = GameGetCameraPos()
|
local x, y = GameGetCameraPos()
|
||||||
GuiTextCentered(modGUI, 0, 0, string.format("Coordinates: %d, %d", x, y))
|
GuiTextCentered(modGUI, 0, 0, string.format("Coordinates: %d, %d", x, y))
|
||||||
|
if UiProgress 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
|
||||||
end
|
end
|
||||||
GuiLayoutEnd(modGUI)
|
GuiLayoutEnd(modGUI)
|
||||||
end
|
end
|
||||||
|
@ -97,3 +97,11 @@ function fileExists(fileName)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function progressBarString(progress, look)
|
||||||
|
local factor = progress.Progress / progress.Max
|
||||||
|
local count = math.ceil(look.BarLength * factor)
|
||||||
|
local barString = string.rep(look.CharFull, count) .. string.rep(look.CharEmpty, look.BarLength - count)
|
||||||
|
|
||||||
|
return string.format(look.Format, barString, progress.Progress, progress.Max, factor * 100)
|
||||||
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user