@@ -166,6 +166,37 @@ local function captureScreenshot(pos, ensureLoaded, dontOverwrite, ctx, outputPi
166166 MonitorStandby .ResetTimer ()
167167end
168168
169+ --- Captures a screenshot of the current viewport.
170+ --- This is used to capture animations, therefore the resulting image may not be suitable for stitching.
171+ --- @param outputPixelScale number ? The resulting image pixel to world pixel ratio.
172+ --- @param frameNumber integer The frame number of the animation.
173+ local function captureScreenshotAnimation (outputPixelScale , frameNumber )
174+ if outputPixelScale == 0 or outputPixelScale == nil then
175+ outputPixelScale = Coords :PixelScale ()
176+ end
177+
178+ local rectTopLeft , rectBottomRight = ScreenCapture .GetRect ()
179+ if not rectTopLeft or not rectBottomRight then
180+ error (string.format (" couldn't determine capturing rectangle" ))
181+ end
182+ if Coords :InternalRectSize () ~= rectBottomRight - rectTopLeft then
183+ error (string.format (" internal rectangle size seems to have changed from %s to %s" , Coords :InternalRectSize (), rectBottomRight - rectTopLeft ))
184+ end
185+
186+ local topLeftWorld , bottomRightWorld = Coords :ToWorld (rectTopLeft ), Coords :ToWorld (rectBottomRight )
187+
188+ --- We will use this to get our fame number into the filename.
189+ --- @type Vec2
190+ local outputTopLeft = Vec2 (frameNumber , 0 )
191+
192+ if not ScreenCapture .Capture (rectTopLeft , rectBottomRight , outputTopLeft , (bottomRightWorld - topLeftWorld ) * outputPixelScale ) then
193+ error (string.format (" failed to capture screenshot" ))
194+ end
195+
196+ -- Reset monitor and PC standby every screenshot.
197+ MonitorStandby .ResetTimer ()
198+ end
199+
169200--- Map capture process runner context error handler callback. Just rolls off the tongue.
170201--- @param err string
171202--- @param scope " init" | " do" | " end"
@@ -750,6 +781,56 @@ function Capture:StartCapturingPlayerPath(interval, outputPixelScale)
750781 self .PlayerPathCapturingCtx :Run (handleInit , handleDo , handleEnd , handleErr )
751782end
752783
784+ --- Starts to capture an animation.
785+ --- This stores sequences of images that can't be stitched, but can be rendered into a video instead.
786+ --- Use `Capture.MapCapturingCtx` to stop, control or view the process.
787+ --- @param outputPixelScale number ? -- The resulting image pixel to world pixel ratio.
788+ function Capture :StartCapturingAnimation (outputPixelScale )
789+
790+ --- Queries the mod settings for the live capture parameters.
791+ --- @return integer interval -- The interval length in frames.
792+ local function querySettings ()
793+ local interval = 1 -- tonumber(ModSettingGet("noita-mapcap.live-interval")) or 30
794+ return interval
795+ end
796+
797+ -- Create file that signals that there are files in the output directory.
798+ local file = io.open (" mods/noita-mapcap/output/nonempty" , " a" )
799+ if file ~= nil then file :close () end
800+
801+ --- Process main callback.
802+ --- @param ctx ProcessRunnerCtx
803+ local function handleDo (ctx )
804+ Modification .SetCameraFree (false )
805+
806+ local frame = 0
807+
808+ repeat
809+ local interval = querySettings ()
810+
811+ -- Wait until we are allowed to take a new screenshot.
812+ local delayFrames = 0
813+ repeat
814+ wait (0 )
815+ delayFrames = delayFrames + 1
816+ until ctx :IsStopping () or delayFrames >= interval
817+
818+ captureScreenshotAnimation (outputPixelScale , frame )
819+
820+ frame = frame + 1
821+ until ctx :IsStopping ()
822+ end
823+
824+ --- Process end callback.
825+ --- @param ctx ProcessRunnerCtx
826+ local function handleEnd (ctx )
827+ Modification .SetCameraFree ()
828+ end
829+
830+ -- Run process, if there is no other running right now.
831+ self .MapCapturingCtx :Run (nil , handleDo , handleEnd , mapCapturingCtxErrHandler )
832+ end
833+
753834--- Starts the capturing process based on user/mod settings.
754835function Capture :StartCapturing ()
755836 Message :CatchException (" Capture:StartCapturing" , function ()
@@ -762,6 +843,8 @@ function Capture:StartCapturing()
762843 if mode == " live" then
763844 self :StartCapturingLive (outputPixelScale )
764845 self :StartCapturingPlayerPath (5 , outputPixelScale ) -- Capture player path with an interval of 5 frames.
846+ elseif mode == " animation" then
847+ self :StartCapturingAnimation (outputPixelScale )
765848 elseif mode == " area" then
766849 local area = ModSettingGet (" noita-mapcap.area" )
767850 if area == " custom" then
0 commit comments