Controls & camera

Endless runner camera

endless-runner-camera · an adoption of Camera framing, version 1

Tests included

Use in your game

Opens the authoring tool with these answers and settings already filled in. You still add your game's own rules and tuning.

What is in the ZIP

The ZIP holds the adoption and its acceptance tests. In the authoring tool, choose Add contract and pick this ZIP. If you keep your design as files, put them in contracts/.

The contract behind it

Covers the views used during play and what each one keeps visible. It records fixed, follow, and free-camera modes, manual panning, view size, travel limits, safe areas, clearance, camera activities, and failure handling. Camera feel, physical input mappings, unit commands, HUD layout, and 3D occlusion stay elsewhere.

Read the full Camera framing contract →

Questions

The supplied answer is marked on each question. Pick other answers to see what changes. Nothing is saved here: the ZIP and the authoring tool use the supplied answers.

In normal play, must these cameras keep anything from your game on screen?

Choices for In normal play, must these cameras keep anything from your game on screen?
The cameras keep named game content visible. A party view keeps every active hero on screen.

Every visibility promise lives in a coverage-promises row and every effect on one lives in a camera-activities row; nothing outside those rows is promised.

Supplied answer The cameras make no promise about game content staying visible. An endless runner may leave the view during a fast fall.

No visibility promise exists; the promise and activity row sets are declared empty.

Why this is asked

A follow target or camera travel limit does not prove that game content remains visible. This choice states whether these cameras make that promise.

Numbersno numbers

This contract has no numbers to set.

Rulesno rules

A rule is a check between the numbers. Validation reports a rule that fails.

This contract has no rules between its numbers.

Lists3 lists

Each list holds the rows this adoption supplies. A list can be empty.

Cameras

cameras

IdActive whenView declared inFollow modeFollow target declared inManual panPan actions declared inRecenter action declared inTravel limitTravel limit declared inView sizeView size rule declared inFailure responseFailure handling declared in
runner-camerarunner.run-in-progresspresentation.runner-play-viewslack-followrunner.runner-anchorno-camera-travel-limitfixed-size

Coverage promises

coverage-promises

This adoption declares no coverage promises.

Camera activities

camera-activities

This adoption declares no camera activities.

Test inputsscope and seeds

Some tests need a scope or seeds from the adoption before they can run.

This adoption supplies none; every test uses its defaults.

Acceptance tests11 tests apply

The contract comes with 51 tests. A test that runs once per row is counted once for each row. Tests that do not apply to these answers are still listed, with the reason.

framing-vocabulary-is-observable

the framing vocabulary is observed at the displayed result

scenarioonce

Applies to endless-runner-camera.

This test fixes two observable meanings. A displayed play moment is the final play image after every listed camera change, scaling, and HUD composition. Any overlap makes two screen areas one play region.

Test steps and diagnostics
Given

a trace of camera settlement and displayed output for endless-runner-camera

When
  • displayed play moments and play regions are classified
Then
  • each displayed play moment is the final play image after follow motion, manual pan, aim or peek offset, player-held zoom, shake, temporary zoom, scripted movement, viewport scaling, and HUD composition
  • two play regions are treated as one region whenever their screen areas overlap at all
Diagnostics
  • endless-runner-camera-displayed-frame-log
  • endless-runner-camera-active-camera-trace
  • endless-runner-camera-candidate-trace

declared-content-framing-outcomes-are-observable

valid frames and framing failures are observable

scenarioonce

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

camera-supplies-region-while-active · runner-camera

runner-camera supplies its region exactly while active

scenarioper cameras row

Applies to the runner-camera row.

Camera runner-camera supplies exactly the play region cited at presentation.runner-play-view, and only while the rule cited at runner.run-in-progress holds. The test names both addresses and restates nothing from them.

Test steps and diagnostics
Given

camera row runner-camera and the rules cited at runner.run-in-progress and presentation.runner-play-view

When
  • the active-camera trace crosses both edges of the rule cited at runner.run-in-progress
Then
  • runner-camera supplies exactly the play region cited at presentation.runner-play-view while runner.run-in-progress holds
  • runner-camera supplies no play region while runner.run-in-progress does not hold
Diagnostics
  • endless-runner-camera-active-camera-trace
  • endless-runner-camera-displayed-frame-log

active-regions-are-exclusive

active cameras occupy exclusive play regions

scenarioonce

Applies to endless-runner-camera.

At most one camera is active in one play region. Overlap is the whole test; how two areas are composited is outside this contract. No pair settles independently unless its play regions are distinct, and each distinct camera's settlement and promise results remain independent. Where the adoption can activate no pair, there is no pair behavior to assert.

Test steps and diagnostics
Given

every pair of cameras that endless-runner-camera can activate at the same displayed play moment; an adoption with no such pair asserts only the no-pair result

When
  • their play regions are compared after display scaling
Then
  • no pair is active where the two screen areas overlap at all; a violating pair is a defect this test fails by construction
  • no pair settles independently unless its play regions are distinct; where they are distinct, each camera's base position, settled view size, suspensions, and promise results are recorded per camera and no value of one is a function of the other's
  • an adoption that can activate no pair has no simultaneous-region settlement to assert
Diagnostics
  • endless-runner-camera-active-camera-trace
  • endless-runner-camera-play-region-map
  • endless-runner-camera-candidate-trace
  • endless-runner-camera-settled-margin-record
  • endless-runner-camera-suspension-log

promise-applies-exactly-when-cited

Row.id applies exactly when its cited rule holds

scenarioper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

whole-extent-stays-visible

the whole extent of Row.id remains inside its visibility area

generalper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

reference-point-stays-visible

the declared point for Row.id remains inside its visibility area

generalper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

rendered-view-defines-visibility-area

Row.id uses the rendered play region

scenarioper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

hud-safe-area-defines-visibility-area

Row.id uses its cited HUD-safe area

scenarioper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

margin-insets-the-visibility-area

Row.id insets its visibility area by four margins

scenarioper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

activity-selects-affected-promises

Row.id affects exactly its selected promises

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

may-suspend-releases-and-restores-promises

Row.id suspends only its affected promises

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

keeps-promise-preserves-other-suspensions

Row.id keeps its promise without cancelling another suspension

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

recenter-manual-pan-defines-suspension-interval

suspending manual pan uses the recentered interval

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

controller-disposes-of-kept-player-proposals

the controller disposes of kept player proposals

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

camera-without-activity-uses-base-alone

a camera without an activity builds from its base alone

scenarioonce

Applies to endless-runner-camera.

For each active camera and moment with no active camera-activities row, the candidate is built from the settled base position alone and has no activity offset. If this adoption has no such moment, the test asserts nothing.

Test steps and diagnostics
Given

each active camera and displayed play moment for which endless-runner-camera has no active camera-activities row; if every active camera has an activity, this test asserts nothing

When
  • the candidate position is built
Then
  • the candidate has the settled base position and no activity offset
Diagnostics
  • endless-runner-camera-candidate-trace
  • endless-runner-camera-activity-log

fixed-view-uses-authored-base

Row.id uses only its authored base position

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches follow-mode fixed view.

locked-follow-uses-target-point

Row.id follows only its cited target point

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches follow-mode locked follow.

slack-follow-stays-in-allowed-set · runner-camera

runner-camera stays in its cited slack set

scenarioper cameras row

Applies to the runner-camera row.

Slack-follow camera runner-camera selects only positions inside the allowed set around its target cited at runner.runner-anchor. It never searches outside that set. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

active slack-follow camera runner-camera and the target and allowed positions cited at runner.runner-anchor

When
  • the controller selects a base position across target motion and direction changes
Then
  • every selected base position lies inside the cited allowed set around the target before the travel limit is applied
  • the controller searches no position outside that set
Diagnostics
  • endless-runner-camera-candidate-trace
  • endless-runner-camera-follow-target-trace

free-camera-starts-from-player-pan

Row.id starts from its player pan proposal

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches follow-mode free view or free with recenter.

free-with-recenter-selects-target

Row.id returns to its cited target on recenter

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches follow-mode free with recenter.

no-travel-limit-does-not-clamp · runner-camera

runner-camera applies no camera travel clamp

scenarioper cameras row

Applies to the runner-camera row.

Camera runner-camera has no camera travel clamp. Its base position passes this stage unchanged.

Test steps and diagnostics
Given

camera runner-camera with base proposals across the set this follow mode supplies

When
  • the travel-limit stage settles the base position
Then
  • no camera travel clamp changes the base position
Diagnostics
  • endless-runner-camera-candidate-trace

travel-limit-clamps-base-position

Row.id clamps its base position to the cited region

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches travel-limit one world region or changes by situation.

activity-offset-follows-settled-base

Row.id applies its bounded change after the base settles

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

temporary-zoom-settles-one-proposed-size

Row.id proposes and settles one temporary view size

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

aim-offset-uses-cited-actions

Row.id receives proposals through its cited aim actions

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

fixed-size-uses-authored-size · runner-camera

runner-camera uses its authored view size

scenarioper cameras row

Applies to the runner-camera row.

Fixed-size camera runner-camera uses the authored size named at presentation.runner-play-view. Only an active temporary-zoom activity can propose another temporary size. The test names the address and restates nothing from it.

Test steps and diagnostics
Given

active fixed-size camera runner-camera across ordinary activity and, where this adoption has such an activity, every active temporary-zoom activity it can construct

When
  • view size settles
Then
  • without an active temporary-zoom activity the size is exactly the authored size named at presentation.runner-play-view
  • only an active temporary-zoom activity can propose a different temporary size
Diagnostics
  • endless-runner-camera-view-size-trace
  • endless-runner-camera-activity-log

player-held-zoom-starts-in-cited-range

Row.id starts from held zoom inside its cited range

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches view-size player held zoom.

automatic-fit-chooses-largest-valid-size

Row.id chooses the largest allowed fitting size

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches view-size automatic fit.

automatic-fit-respects-travel-region

Row.id continues automatic fitting after its centre is clamped

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches view-size automatic fit and travel-limit one world region or changes by situation.

final-composed-test-decides-frame

the final composed-view test decides every promise

scenarioonce

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

keeps-promise-holds-through-full-interval

Row.id keeps every affected promise for its full interval

generalper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

keeps-promise-holds-for-pan-and-zoom

Row.id keeps every affected promise across its proposals

generalper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

failure-record-follows-exhausted-search

Row.id creates one complete failure record after search

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

hold-valid-frame-keeps-invalid-candidate-offscreen

Row.id holds a valid frame on failure

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

keep-priority-content-displays-best-allowed-frame

Row.id applies its total priority on failure

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

show-framing-failure-adds-cited-state

Row.id shows its cited framing-failure state

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

change-world-reruns-framing-before-display

Row.id changes the world and reruns framing

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

failure-unreachable-invalidates-reached-claim

Row.id treats a reached failure as an invalid claim

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

failure-persists-until-valid-or-inactive

a framing failure persists until settlement or deactivation

scenarioonce

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

recovery-and-handover-insert-no-unchecked-frame

recovery and handover insert no unchecked frame

scenarioonce

Applies to endless-runner-camera.

Neither the end of a suspension nor a camera switch inserts an unchecked frame, and neither leaves two cameras active in one play region. An adoption in which no camera activation follows a deactivation has no handover case to construct.

Test steps and diagnostics
Given

each suspension end and camera handover endless-runner-camera can construct; an adoption in which no camera activation follows a deactivation has no handover case to construct

When
  • the next displayed play moment is prepared
Then
  • neither recovery nor switching inserts an unchecked frame or leaves two cameras active in one play region
Diagnostics
  • endless-runner-camera-active-camera-trace
  • endless-runner-camera-displayed-frame-log

recovery-and-handover-first-frame-is-settled

recovery and handover settle a checked first frame

scenarioonce

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

camera-first-frame-is-checked

Row.id checks its first frame after every activation

scenarioper cameras row

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

promise-camera-id-joins-one-camera

declaration check: Row.id names exactly one camera in this adoption

scenarioper coverage-promises row

Does not apply to endless-runner-camera: coverage-promises has no rows.

activity-camera-id-joins-one-camera

declaration check: Row.id names exactly one camera in this adoption

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

activity-promise-list-stays-on-camera

declaration check: Row.id cites promises only from its own camera

scenarioper camera-activities row

Does not apply to endless-runner-camera: camera-activities has no rows.

adoption-declares-a-camera

declaration check: the adoption declares at least one camera

scenarioonce

Applies to endless-runner-camera.

Declaration check: a camera-framing adoption declares at least one cameras row. An adoption with none describes no play view and is a defect this test fails by construction. This check reads the adoption, not the running game.

Test steps and diagnostics
Given

the cameras row set for endless-runner-camera

When
  • the adoption is reviewed as a camera-framing adoption
Then
  • at least one cameras row exists; an adoption with none describes no play view and is a defect this test fails by construction
Diagnostics
  • endless-runner-camera-declaration-record

declared-content-has-a-promise

declaration check: declared content includes at least one coverage promise

scenarioonce

Does not apply to endless-runner-camera: content visibility is no content guarantee, not declared content.

free-promise-camera-has-manual-pan-activity

declaration check: a free promise-bearing camera declares manual-pan activity

scenarioper cameras row

Does not apply to endless-runner-camera: no row in cameras matches follow-mode free view or free with recenter.

no-content-guarantee-keeps-promise-shape-empty

declaration check: no-content-guarantee declares no promise machinery

scenarioonce

Applies to endless-runner-camera.

Declaration check: an adoption answering no-content-guarantee declares both coverage-promises and camera-activities empty. A non-empty row set is a defect this test fails by construction. This check reads the adoption, not the running game.

Test steps and diagnostics
Given

endless-runner-camera answering no-content-guarantee

When
  • its promise and activity declarations are inspected
Then
  • the coverage-promises and camera-activities row sets are both empty; a non-empty set is a defect this test fails by construction
Diagnostics
  • endless-runner-camera-declaration-record

framing-holds

the framing authority order holds for the whole run

generalonce

Applies to endless-runner-camera.

Across sequences of displayed play moments across activations, activities, suspensions, handovers, and failures, every displayed play moment follows the mechanism's authority order. Later stages never rewrite an earlier declaration, no question answer or row field changes the settlement order, and no displayed moment is both invalid and outside failure settlement. An adoption answering no-content-guarantee audits only the active-region and displayed-moment stages. The adoption supplies the audit seeds through verification inputs.

Test steps and diagnostics
Holds

every displayed play moment passes in order through active play region, applicable promise, each coverage row's visibility area and margin, activity suspension, player proposal and controller disposal, camera-position travel limit, allowed position and view-size settlement, final composed-view test, and failure response; later stages never rewrite an earlier declaration; no question answer or row field changes the order in which these stages settle; no displayed play moment is both invalid and outside steps 11 through 13; an adoption answering no-content-guarantee audits only the active-region and displayed-moment stages

Seeds

["framing-audit-a","framing-audit-b"]

Scope

sequences of displayed play moments across activations, activities, suspensions, handovers, and failures

Diagnostics
  • endless-runner-camera-displayed-frame-log
  • endless-runner-camera-active-camera-trace
  • endless-runner-camera-candidate-trace
  • endless-runner-camera-suspension-log
  • endless-runner-camera-failure-record
  • endless-runner-camera-first-authority-order-violation
JSONthe adoption as one file

The adoption file as supplied. Answers you try on this page are not written into it; to change an adoption, open it in the authoring tool.

{
  "contract": "camera-framing",
  "version": 1,
  "summary": "Covers the views used during play and what each one keeps visible. It records fixed, follow, and free-camera modes, manual panning, view size, travel limits, safe areas, clearance, camera activities, and failure handling. Camera feel, physical input mappings, unit commands, HUD layout, and 3D occlusion stay elsewhere.",
  "questions": {
    "content-visibility": {
      "asks": "In normal play, must these cameras keep anything from your game on screen?",
      "rationale": "A follow target or camera travel limit does not prove that game content remains visible. This choice states whether these cameras make that promise.",
      "options": {
        "declared-content": {
          "meaning": "The cameras keep named game content visible. A party view keeps every active hero on screen.",
          "semantics": "Every visibility promise lives in a coverage-promises row and every effect on one lives in a camera-activities row; nothing outside those rows is promised."
        },
        "no-content-guarantee": {
          "meaning": "The cameras make no promise about game content staying visible. An endless runner may leave the view during a fast fall.",
          "semantics": "No visibility promise exists; the promise and activity row sets are declared empty."
        }
      }
    }
  },
  "declares": {
    "values": {},
    "rows": {
      "cameras": {
        "description": "The play views in this camera family and the rules that move or limit each one.",
        "when-empty": "An adoption without a camera row describes no play view, and a reviewer rejects it.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A stable camera name in game language, such as board-camera or tactical-view."
          },
          "active-when": {
            "type": "citation",
            "required": true,
            "description": "The complete rule for when this camera supplies a displayed play view."
          },
          "view-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The rule naming this camera's final play region, aspect handling, and display sizes. For a fixed view it also names the authored base position; for a fixed size it also names the authored view size."
          },
          "follow-mode": {
            "type": "string",
            "required": true,
            "options": [
              "fixed-view",
              "locked-follow",
              "slack-follow",
              "free-view",
              "free-with-recenter"
            ],
            "description": "A fixed room view's base position never moves or follows anything, and no input moves it; only a declared activity can offset it. Locked follow keeps a hero at the centre. Slack follow lets a runner move before the view follows. A free tactical view begins where the player moves it. A free map with recenter can return to a selected group."
          },
          "follow-target-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "follow-mode": [
                  "locked-follow",
                  "slack-follow",
                  "free-with-recenter"
                ]
              }
            },
            "description": "The rule naming the followed target or the target used by recenter. For slack follow it also names the allowed positions around that target."
          },
          "manual-pan": {
            "type": "string",
            "when": {
              "row": {
                "follow-mode": [
                  "free-view",
                  "free-with-recenter"
                ]
              }
            },
            "options": [
              "edge-scroll",
              "drag-view",
              "move-view-actions",
              "several-methods"
            ],
            "description": "Edge scroll moves a strategy view at the screen edge. Drag view pulls a mission map. Move-view actions pan a tactics camera through mapped actions. Several methods gives a world map at least two of these."
          },
          "pan-actions-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "follow-mode": [
                  "free-view",
                  "free-with-recenter"
                ]
              }
            },
            "description": "The game actions that pan this camera. Several methods names at least two methods; platform inputs stay with Control options."
          },
          "recenter-action-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "follow-mode": [
                  "free-with-recenter"
                ]
              }
            },
            "description": "The game action that returns this view to its cited target."
          },
          "travel-limit": {
            "type": "string",
            "when": {
              "row": {
                "follow-mode": [
                  "locked-follow",
                  "slack-follow",
                  "free-view",
                  "free-with-recenter"
                ]
              }
            },
            "options": [
              "no-camera-travel-limit",
              "one-world-region",
              "changes-by-situation"
            ],
            "description": "No limit lets a runner view keep moving. One region keeps a board view inside its room. Situation changes can give a tactical map different regions by mission phase. A fixed view travels nowhere and is not asked."
          },
          "travel-limit-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "travel-limit": [
                  "one-world-region",
                  "changes-by-situation"
                ]
              }
            },
            "description": "The complete camera-position region and, when needed, the rule selecting the current region."
          },
          "view-size": {
            "type": "string",
            "required": true,
            "options": [
              "fixed-size",
              "player-held-zoom",
              "automatic-fit"
            ],
            "description": "Fixed size keeps a runner at its authored view size. Player-held zoom lets a strategy player hold a chosen zoom level. Automatic fit widens a co-op view until the party and margins fit."
          },
          "view-size-rule-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "view-size": [
                  "player-held-zoom",
                  "automatic-fit"
                ]
              }
            },
            "description": "For player-held zoom, the zoom actions and allowed range. For automatic fit, the allowed range, aspect response, update timing, and simultaneous-promise rule."
          },
          "failure-response": {
            "type": "string",
            "when": {
              "flag": {
                "content-visibility": [
                  "declared-content"
                ]
              }
            },
            "options": [
              "hold-valid-frame",
              "keep-priority-content",
              "show-framing-failure",
              "change-world-to-restore-frame",
              "failure-unreachable"
            ],
            "description": "Hold valid frame leaves a puzzle transition on its last valid view. Keep priority content protects a tactics leader and records hidden allies as failed. Show framing failure adds a warning to a strategy view. Change world to restore frame applies a co-op tether or defeat rule. Failure unreachable cites why a fixed arena fits on every reachable display."
          },
          "failure-handling-declared-in": {
            "type": "citation",
            "when": {
              "flag": {
                "content-visibility": [
                  "declared-content"
                ]
              }
            },
            "description": "The exact wait, priority, warning, or game rule used after failure, or the complete argument that failure cannot be reached."
          }
        }
      },
      "coverage-promises": {
        "description": "The subjects or areas each camera keeps visible in named play situations.",
        "when-empty": "No camera in this adoption promises to keep game content visible.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A stable promise name in game language, such as whole-board or selected-group."
          },
          "camera-id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "description": "The camera that keeps this promise, by the name you gave it."
          },
          "subject": {
            "type": "citation",
            "required": true,
            "description": "The Fixed game prose naming the subject or area that stays visible."
          },
          "applies-when": {
            "type": "citation",
            "required": true,
            "description": "The complete play conditions in which this promise applies."
          },
          "coverage": {
            "type": "string",
            "required": true,
            "options": [
              "whole-extent",
              "reference-point"
            ],
            "description": "Whole extent keeps every edge of a selected squad visible. Reference point keeps a runner's cited centre visible even when the sprite reaches past the view."
          },
          "reference-point": {
            "type": "citation",
            "when": {
              "row": {
                "coverage": [
                  "reference-point"
                ]
              }
            },
            "description": "The exact point that stays visible."
          },
          "visibility-area": {
            "type": "string",
            "required": true,
            "options": [
              "rendered-view",
              "hud-safe-area"
            ],
            "description": "Rendered view measures a runner from the final view edges, so HUD overlap alone does not fail. HUD safe area measures a tactics objective from the cited unobscured area, so HUD-covered space counts outside."
          },
          "safe-area-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "visibility-area": [
                  "hud-safe-area"
                ]
              }
            },
            "description": "The final unobscured rectangle or shape after the HUD elements named by this promise's conditions are placed."
          },
          "margin-declared-in": {
            "type": "citation",
            "required": true,
            "description": "The top, right, bottom, and left clearance inside the chosen visibility area, with units. Zero is stated explicitly. A camera clamped at its travel limit may hold less than this clearance on the clamped side, but the subject still stays inside the area."
          }
        }
      },
      "camera-activities": {
        "description": "Frame-changing situations and whether each one keeps or suspends all or named promises on its camera.",
        "when-empty": "No camera activity has a visibility-promise effect in this adoption. With declared content, the promise-bearing cameras use none of the seven listed activities.",
        "record": {
          "id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "unique": true,
            "description": "A stable activity name in game language, such as sprint-look-ahead or boss-hit-shake."
          },
          "camera-id": {
            "type": "string",
            "required": true,
            "pattern": "kebab-case",
            "description": "The camera this activity changes, by the name you gave it."
          },
          "activity-kind": {
            "type": "string",
            "required": true,
            "options": [
              "look-ahead",
              "manual-pan",
              "aim-or-peek-offset",
              "player-zoom",
              "shake",
              "temporary-zoom",
              "scripted-move"
            ],
            "description": "Look-ahead leads a runner. Manual pan moves a tactical view. Aim or peek offsets a follow camera toward a target. Player zoom holds a strategy view at a chosen size. Shake offsets a boss hit. Temporary zoom changes a combat view's size. A scripted move frames a dialogue reveal."
          },
          "applies-when": {
            "type": "citation",
            "required": true,
            "description": "The complete start and end conditions for this camera activity."
          },
          "extent-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "activity-kind": [
                  "look-ahead",
                  "aim-or-peek-offset",
                  "shake",
                  "temporary-zoom",
                  "scripted-move"
                ]
              }
            },
            "description": "The rule naming this activity's strongest displacement or size change. Manual pan is bounded by the travel limit and controller instead; player zoom is bounded by the view-size citation."
          },
          "actions-declared-in": {
            "type": "citation",
            "when": {
              "row": {
                "activity-kind": [
                  "aim-or-peek-offset"
                ]
              }
            },
            "description": "The game actions that produce this offset. Platform inputs stay with Control options."
          },
          "promises": {
            "type": "citation",
            "description": "The fixed list of promise names affected by this activity. When absent, the activity affects every promise on its camera."
          },
          "promise-effect": {
            "type": "string",
            "required": true,
            "options": [
              "keeps-promise",
              "may-suspend"
            ],
            "description": "Keeps promise preserves the affected rows, as when runner look-ahead stays inside its margin. May suspend allows those rows to fail only for this activity, as during a boss reveal."
          }
        }
      }
    }
  },
  "rules": {},
  "origin": "https://opengdd.org/contracts/camera-framing-1",
  "mechanism": [
    "This is the single normative authority for each displayed play moment: which camera is active, what must remain visible, who moves the view, what may suspend a promise, how travel and view size are applied, what happens when content cannot fit, and when a suspended promise returns. Questions and rows supply choices and cited game rules; they do not define another order.",
    "A **displayed play moment** is the final play image presented at one moment, after follow motion, manual pan, aim or peek offset, player-held zoom, shake, temporary zoom, scripted movement, viewport scaling, and HUD composition. A **play region** is the screen area named by `view-declared-in`; two regions are the same region whenever their screen areas overlap at all. A coverage row's **visibility area** is either the final rendered play region or that row's cited HUD-safe area. A **valid frame** satisfies every applicable, unsuspended coverage row at its settled margin. A **framing failure** exists only when no camera position and view size allowed by the camera can produce a valid frame.",
    "### Select the view and promises",
    "1. Read every camera whose `active-when` rule holds. Each active camera supplies the final play region named by `view-declared-in`. At most one camera may be active in one play region. Simultaneous split views settle independently only when their regions are distinct. 2. For each active camera, collect its coverage rows whose `applies-when` rules hold. `whole-extent` contributes the subject's complete displayed extent. For a set-valued subject, the whole extent is the union of its members' displayed extents. `reference-point` contributes only the cited point. 3. Construct each row's visibility area after display scaling and the HUD placement named by that row's conditions. HUD-covered space is outside a HUD-safe area; a rendered-view row deliberately ignores HUD overlap. Inset the area by the cited top, right, bottom, and left margin. Attach the one contract-supplied edge condition: a later travel clamp may reduce only the margin on its clamped side, never the underlying visibility area. A whole extent fits only when all of it lies inside the settled inset area. A reference point fits when that point lies inside it. A subject or point lying exactly on the boundary of the settled inset area fits.",
    "### Apply activity permissions",
    "4. Read every active activity row for the camera. Its `promises` citation, when present, selects the affected applicable promises; when absent, it selects every applicable promise on that camera. An active `may-suspend` row suspends only that set for its cited interval. Active `keeps-promise` rows do not cancel a different active suspension affecting the same promise. 5. A suspended promise is not tested and cannot create a framing failure. It becomes required again on the first displayed play moment after the last suspending activity affecting it ends. On a free-with-recenter camera, a suspending manual-pan interval begins when the view leaves its recentered state and ends on recenter or camera deactivation. 6. The player proposes motion and held zoom through the cited game actions; the camera controller disposes of the final frame. `free-view` and `free-with-recenter` name who initiates position changes, not who owns the displayed result. A `keeps-promise` manual-pan or aim-or-peek row therefore requires the controller to stop, redirect, or resize the proposed view before an affected promise fails.",
    "### Build and test the frame",
    "7. Build the candidate position in two stages. Stage one selects the base position inside the follow mode's set. A fixed view has only the base position named by `view-declared-in`. A locked follow has only its target point. A slack follow may use the allowed positions its citation names around the target. A free view starts from the player's proposal; a free-with-recenter view does the same until recenter selects its target point. The controller may search only within the set supplied by that mode. Apply any travel limit to the base position; a fixed view declares none. It constrains camera position, not the visible region, and it never proves that a subject is visible. Resolve the step-3 edge condition now: when this clamp alone prevents the subject from meeting a margin, reduce only the clamped-side margin by the exact shortfall, no lower than zero. The subject must still lie inside the underlying, uninset visibility area. Stage two applies each active position-changing activity as an offset to the settled base, bounded by its cited extent. An activity offset is outside the mode's search set and may carry the view past the travel limit; the final test in step 9 still decides every promise. 8. Settle view size once. `fixed-size` uses the size named by `view-declared-in`. `player-held-zoom` starts from the player's held level inside the cited range; its `player-zoom` activity row's effect decides whether the controller must keep every promise or may let the named ones suspend; with no such row, the controller must keep every applicable promise at every held level. `automatic-fit` applies the cited fit rule, choosing the largest allowed size at which the extent, the margins, the strongest cited displacement of every position-changing `keeps-promise` activity, and the largest cited size of every size-changing one fit together. On any view-size answer, an active temporary-zoom activity may propose its cited extent as a temporary size; an accepted temporary size replaces the size the answer would otherwise settle, for the activity's cited interval, and the authored size, held level, or fit result returns on the first displayed moment after it ends. The controller disposes of every proposal under the activity permission and all unsuspended promises together. The travel region still binds camera position only: if fitting needs a centre outside it, clamp the centre and continue fitting inside the allowed zoom range, even when the visible region extends beyond the travel region. If no allowed size and clamped position fits, step 11 applies. 9. Compose the final view and HUD, then test every unsuspended promise against its inset visibility area. This final test decides the outcome; a follow trigger, recenter action, camera travel limit, or earlier candidate frame never substitutes for it. 10. If every row passes, display the valid frame. A `keeps-promise` activity is demonstrated only when this final test passes on every displayed moment of its cited interval, including the strongest displacement or size change its extent citation names.",
    "### Settle a framing failure and recovery",
    "11. Only after no allowed candidate can pass, create one framing failure containing the camera id, failed promise ids, display size, visibility areas, settled margins, attempted position and view size, travel limit, active activities, and selected failure response. A failure response is not an ordinary precondition and cannot weaken a coverage row before this step. 12. `hold-valid-frame` does not display the invalid candidate. It keeps the last valid displayed frame or, before one exists, the cited non-play transition surface; the cited game rule decides whether play or a transition advances. `keep-priority-content` applies the cited total priority, displays the best allowed frame, and records each promise that did not fit. `show-framing-failure` displays the best allowed frame with its cited player-facing failure state. `change-world-to-restore-frame` applies the cited game rule to tether, defeat, or move content, then reruns steps 2–10 before another play moment is displayed; the cited rule may not change the active camera set or create a play region. `failure-unreachable` declares no runtime handling: reaching this step contradicts its cited argument and invalidates the design claim. 13. A failure remains a failure until a later settlement produces a valid frame or the camera deactivates. Priority, a visible notice, or starting a cited world change does not by itself turn an invalid frame into a kept promise. 14. When a suspension ends, run steps 2–10 before displaying the next play moment. The first displayed moment without an active suspension must be valid or must enter steps 11–13. At a camera handover, run steps 1–10: the incoming camera brings its own promise set and visibility areas, and its first frame follows this same first-valid-frame recovery path. Switching cannot insert an unchecked frame or leave two cameras active in one region.",
    "The authority order is therefore: active play region, applicable promise, each coverage row's visibility area and margin, activity suspension, player proposal and controller disposal, camera-position travel limit, allowed position and view-size settlement, final composed-view test, failure response. Later stages never rewrite an earlier declaration."
  ],
  "answers": {
    "content-visibility": "no-content-guarantee"
  },
  "values": {},
  "rows": {
    "cameras": [
      {
        "id": "runner-camera",
        "active-when": "runner.run-in-progress",
        "view-declared-in": "presentation.runner-play-view",
        "follow-mode": "slack-follow",
        "follow-target-declared-in": "runner.runner-anchor",
        "travel-limit": "no-camera-travel-limit",
        "view-size": "fixed-size"
      }
    ],
    "coverage-promises": [],
    "camera-activities": []
  },
  "pack": "sha256:4caf436eee5950d06c4368a78c14f9c4bff965e124c8d84695700ba82e2e7129"
}