DramaticShapeVoxelMod archival mirror · read-only
tests/forest_fog_shots.lua

forest_fog_shots.lua

tests/forest_fog_shots.lua4.3 KB100644d3a2e06ece
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
-- Driver: screenshot the forest's atmosphere.
--
-- Viridian Forest through the pins: gold shafts and drifting pollen by
-- day, silver moon rays and fireflies at night, the haze under both, one
-- first-person frame standing inside a beam, and the control shots -- the
-- row OFF (which must be pixel-identical to a run before the feature
-- existed) and LOW (haze, half the shafts, no particles).
--
-- Deterministic on purpose: encounters killed, tile animation frozen, the
-- atmosphere's own clock pinned, so an AB_TAG=before/after pair diffs
-- clean (see tools/voxel-survey.md for the workflow).
--
--   SHOT_DIR=.scratchpad/forestfog AB_TAG=after \
--   POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/forest_fog_shots.lua lovec.exe .
return function(game)
  local U = dofile("tests/drivers/util.lua")
  local Pipelines = require("src.render.Pipelines")

  local ROOT = (os.getenv("SHOT_DIR") or ".scratchpad/forestfog")
    .. "/" .. (os.getenv("AB_TAG") or "after")

  local exports = game.mods and game.mods.exports
  local handle = exports and exports.DRAMATIC_SHAPE
  if not (handle and handle.lib) then
    U.log("DRAMATIC_SHAPE is not loaded -- enable it and run again")
    return
  end
  local lib = handle.lib
  local DayNight = lib.require("DayNight")
  local ChunkMesher = lib.require("ChunkMesher")
  local Voxel = lib.require("VoxelState")
  local ForestAtmos = lib.require("ForestAtmos")

  if not (game.data.maps and game.data.maps.VIRIDIAN_FOREST) then
    U.log("no VIRIDIAN_FOREST in this dataset")
    return
  end

  -- ------- the determinism recipe (see round_regress_shots)
  require("src.world.OverworldController").rollEncounter =
    function() return nil end
  local TileRenderer = require("src.render.TileRenderer")
  TileRenderer.tick = function() end
  TileRenderer.animFrame = function() return 0 end
  local Zoom = require("src.render.Zoom")
  pcall(function()
    game.save.options.zoom = 1
    Zoom.applyOptions(game.save.options)
  end)
  -- pin the atmosphere's own clock: every shimmer and every mote is a
  -- pure function of this number
  ForestAtmos.frozen = true
  ForestAtmos.time = 5

  local function setTime(value)
    DayNight.setting:sync(value)
    DayNight.update(0)
  end

  local function settle()
    for _ = 1, 900 do
      if ChunkMesher.pending() == 0 then break end
      U.wait(1)
    end
    for _ = 1, 300 do
      if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then
        break
      end
      U.wait(1)
    end
    U.wait(40)
  end

  local function go(x, y, face, rung)
    U.teleport(game, "VIRIDIAN_FOREST", x, y, face or "up")
    Pipelines.setLevel("voxel", rung or 5)
    Pipelines.setLevel("tiltshift", 0)   -- judge the atmosphere, not the blur
    settle()
  end

  ForestAtmos.setting:sync("full")

  -- ------- day: gold spears and pollen, three vantages
  setTime("day")
  go(17, 20)
  U.shot(game, ROOT .. "/10_day_17_20.png")
  go(16, 20)
  U.shot(game, ROOT .. "/11_day_16_20.png")
  go(16, 24)
  U.shot(game, ROOT .. "/12_day_16_24.png")

  -- ------- night: moon rays and fireflies, same vantages
  setTime("night")
  go(17, 20)
  U.shot(game, ROOT .. "/20_night_17_20.png")
  go(16, 20)
  U.shot(game, ROOT .. "/21_night_16_20.png")
  go(16, 24)
  U.shot(game, ROOT .. "/22_night_16_24.png")

  -- ------- first person, standing in a beam
  --
  -- The shafts are dealt by seed, so ask the layout where one landed and
  -- walk into it rather than guessing a cell.
  local shaft
  pcall(function()
    local map = game.overworld and game.overworld.map
    local L = map and ForestAtmos.layoutFor(map)
    shaft = L and L.shafts and L.shafts[1]
  end)
  if shaft then
    local cx = math.floor(shaft.x / 16)
    local cy = math.floor(shaft.z / 16)
    setTime("day")
    go(cx, cy + 1, "up", 6)              -- the 1ST rung, facing the beam
    U.shot(game, ROOT .. "/30_fp_in_beam_day.png")
    setTime("night")
    U.wait(30)
    U.shot(game, ROOT .. "/31_fp_in_beam_night.png")
  else
    U.log("no shaft landed -- check the layout seed")
  end

  -- ------- the controls
  setTime("day")
  ForestAtmos.setting:sync("low")
  go(17, 20)
  U.shot(game, ROOT .. "/40_low_day.png")
  ForestAtmos.setting:sync("off")
  go(17, 20)
  U.shot(game, ROOT .. "/41_off_day.png")   -- must match a pre-feature run

  ForestAtmos.setting:sync("full")
  ForestAtmos.frozen = false
  setTime("day")
  U.log("done -- " .. ROOT)
end