Photo mode

Freeze the game, fly a free camera around the player, change field of view, roll, exposure and filter, hide the HUD and capture a picture.

Source: module-kits:kits/photo_mode/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs photo-modeMCP tool: gripforge_docs { "topic": "photo-mode" }

A photo mode for your game (photo.mode) #

What it is for #

photo.mode opens a photo mode in any game: a key (or a call) freezes the game, a free camera flies around the place where the player stood, and the player picks a field of view, a roll, an exposure and a filter, hides the HUD and takes a picture. The kit holds the camera state and the pause; the photo overlay (shipped with the kit as an ui.webui fragment) drives it and the page draws the picture, because only the page can read the canvas. It decides nothing of your game: a mod or the game can forbid opening it with a hook.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "photo.mode" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "photo.mode",
                              config: { "radius": 20, "filters": ["none", "sepia", "noir"] } }

It requires game.engine and input.actions. By default the key O opens and closes the mode; flight keys are input actions too (W A S D or the arrows, R and F) that the player can rebind in the input settings.

First example #

<!-- test: kits/game_engine/host/tests/photo_mode.rs::a_shot_is_taken_through_the_capture_source_while_the_world_is_frozen --> The test installs the kit with the filters none and noir, then drives the photo.mode service:

  1. capture() before entering answers null: there is no shot outside the mode.
  2. enter() answers true, and the game phase becomes paused.
  3. set({ filter: "noir" }) picks a filter; capture() calls the capture source with [width, height, camera] and answers its picture (a data URL). The size defaults to 1920 x 1080 and is clamped to 16..8192 per side.
  4. photo.shots counts the pictures taken. A source that answers nothing, or fails, is not a shot.

On the web, the photo overlay registers the capture source for you with setCaptureSource.

Settings and defaults #

SettingTypeDefaultMeaning
radiusnumber (1..500)12how far the camera may fly from where the player stood
fovnumber (10..120)60field of view when the mode opens, in degrees
fovMinnumber (5..90)15narrowest field of view
fovMaxnumber (30..150)110widest field of view
filterslist of stringsnone, mono, sepia, warm, cool, vivid, noir, dreamfilters offered, in order; none is always added first if missing
pauseWorldbooleantruefreeze the game while the mode is open

All six are moddable. The filter names are the ones the photo overlay knows.

Services, events, variables #

The service photo.mode:

  • enter(): opens the mode when the game is playing. Answers false if it is already open, if the game is not playing, or if a hook cancelled it.
  • exit(): closes it and, if the kit paused the game, resumes it. Answers false when it was not open.
  • active(): true while the mode is open.
  • camera(): the current camera { position, yaw, pitch, roll, fov, exposure, filter, hideHud, anchor, radius }, or null.
  • look(dyaw, dpitch): turns the camera; pitch is limited to 85 degrees.
  • move(forward, right, up): moves the camera along its view, kept inside radius of the anchor.
  • set(patch): any of fov (clamped to fovMin..fovMax), roll (+-90 degrees), exposure (-3..3), filter (must be one of filters), hideHud.
  • reset(): back to the camera the mode opened with.
  • filters(): the list of filters.
  • capture(width?, height?): asks the capture source for a picture; answers it, or null.
  • setCaptureSource(fn): gives the function that draws the picture.
  • shots(): pictures taken so far.

Events: photo.entered (anchor), photo.exited (shots), photo.captured (width, height, filter). The kit listens to game.phase: if anything else resumes the game (a menu), the mode closes with it.

Variables: photo.active (boolean, mode open) and photo.shots (number, pictures taken).

Input actions: photo_mode, photo.forward, photo.right, photo.up.

Hooks and modding #

The hook photo.enter is run just before the mode opens, with { anchor } (where the player stands). It is cancellable: a game or a mod forbids the photo mode during a scene, a fight or a timed run, and then enter() answers false, nothing is paused and nothing is emitted. The settings radius, fov, fovMin, fovMax, filters and pauseWorld can be changed by a mod.

Engines #

The logic is the same Rust module everywhere. The overlay (look, fly, field of view, roll, exposure, filters, HUD hiding, shutter, and the button that opens the mode) is an ui.webui fragment; the capture source reads the rendered canvas of the page.

When it does not do what you expect #

  • enter() answers false: the mode is already open, the game is not in the playing phase, or a photo.enter tap cancelled it.
  • capture() answers null: the mode is closed, no capture source was set, or the source answered nothing.
  • A filter does not apply: its name is not in filters.
  • The camera stops at a distance: it is confined to radius around where the player stood.
  • The game keeps running behind the overlay: pauseWorld is false.

Limits #

  • Only the number of pictures is saved. A loaded game is never in photo mode, and a save made inside it resumes playing.
  • The kit does not write the picture to disk: the page that gave the capture source decides what to do with it.
  • It needs a capture source to take a picture; without a page (headless) the camera still moves but nothing is captured.

Versions #

Kit version 1.2.0. 1.1.0 added the photo.enter hook and the moddable settings; 1.2.0 made the photo keys input actions that the player can rebind.