mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 06:11:10 +02:00
patch holes on flower/grass models
This commit is contained in:
@@ -45,6 +45,89 @@
|
||||
well-known way to make people ill. The rung still changes the walk and
|
||||
the sprites the same way.
|
||||
|
||||
- **Every camera zooms, on whatever the machine has.** The mouse wheel,
|
||||
`Q`/`E`, a two-finger pinch and the pad's two stick clicks all reach
|
||||
whichever camera is actually in front of you -- the third-person boom,
|
||||
the staged battle's lens, or the engine's own survey zoom on an orbit
|
||||
rung. One module (`lib/CamControl.lua`) answers "which camera is this
|
||||
aimed at" so the four cameras never race each other for an event, and
|
||||
forwards everything it does not claim. 1ST claims nothing: the eye is in
|
||||
the player's head, and a pinch there would only wind the survey zoom for
|
||||
whenever they stepped back out.
|
||||
|
||||
- **The battle camera is yours to steer.** The right stick, a drag across
|
||||
the screen or the mouse walks the staged shot around the arena and raises
|
||||
the seat; the wheel, `Q`/`E`, a pinch or a stick click work the lens.
|
||||
|
||||
Both axes stop where the composition does. LEFT stops at the shot the rig
|
||||
was solved for, because there is nothing to the left of it. RIGHT ends
|
||||
SIDE-ON -- the eye square to the arena's axis, both Pokemon at the same
|
||||
distance instead of one behind the other -- computed from each rig's own
|
||||
stance rather than written down. DOWN stops at the rig's low stance and
|
||||
UP is 45 degrees above it, raised about the focus at a constant radius so
|
||||
climbing never doubles as zooming. Input accumulates into a goal the eye
|
||||
eases after, so a flick reads as the camera being pushed rather than
|
||||
dragged.
|
||||
|
||||
The lens **opens by exactly the amount the pair spreads**: the solved
|
||||
shot looks along the arena's axis at a shallow angle, which foreshortens
|
||||
the gap between the two mons to less than half its length, and swinging
|
||||
round or climbing un-foreshortens it. Left alone that threw both Pokemon
|
||||
off the edges of the frame at the far end of either range, which made the
|
||||
whole far end unusable.
|
||||
|
||||
Where you leave the camera is where the next battle opens. An angle and a
|
||||
lens you chose are how you want to watch battles, not a fact about one
|
||||
encounter.
|
||||
|
||||
- **Move animations track the camera.** They already slid to follow the
|
||||
pair's midpoint; now they follow its SEPARATION too. Both mons are
|
||||
geometry standing on the map, so the camera sizes them -- and an effects
|
||||
layer that kept the authored 106-pixel spacing through a zoom and a
|
||||
60-degree swing fired its beams into the air beside the Pokemon they were
|
||||
aimed at.
|
||||
|
||||
- **BACK SPRITES locks the battle camera.** That setting pins your own mon
|
||||
to the GB's slot on the menu while the foe stands out on the map, and no
|
||||
angle holds a composition that is half frame and half world. The steer,
|
||||
the climb and the lens all stand down -- in the rig as well as at the
|
||||
inputs, so an angle stored from before the row was switched on cannot
|
||||
leave it steered anyway. The slow drift stays: it was always there under
|
||||
BACK SPRITES and two degrees is not a composition problem.
|
||||
|
||||
- **BATTLE BG is pinned to WHITE and its row comes off the menu.** The row
|
||||
picks what fills the screen AROUND the battle, and this mode fills the
|
||||
window with the map the fight is standing on -- there are no voids left
|
||||
for it to be about. WORLD was actively wrong under it: it makes the
|
||||
battle non-opaque so the engine draws a second, dimmed copy of the
|
||||
overworld beneath the arena pass's own. Pinned rather than merely hidden,
|
||||
so a save written before the mod was installed cannot carry a value the
|
||||
menu can no longer reach. Uninstall and the row is back.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Grass and flowers are closed off at the sides.** Both stand as
|
||||
per-pixel slabs built from runs of lit pixels, and only the front, the
|
||||
back and a lid were ever emitted -- so from any angle off square you
|
||||
looked straight in through the open end of every run and out the far
|
||||
side. At the low cameras this release adds, that is most of the time.
|
||||
Each run now wears end walls in the colour of the pixel they close off.
|
||||
|
||||
Flowers needed more than that, because a flower SWAYS: the mesh spans the
|
||||
union of every animation frame and each frame is cut back out in texture
|
||||
space, so a pixel that drops out of a frame takes the union's wall with
|
||||
it and leaves an interior boundary that never had one. The first cut of
|
||||
this looked solid on the base frame and still had gaps on every other.
|
||||
Every pixel of a flower now carries a cap on all four of its remaining
|
||||
faces: enclosed and invisible while its neighbour is there, and already
|
||||
in place the moment the animation takes that neighbour away.
|
||||
|
||||
- **No more machine-gun bonking in 1ST and 3RD.** The grid walk's collision
|
||||
sound marks a discrete event -- a direction pressed, a step refused. A
|
||||
free walk has no such moment: the body slides along every wall it grazes,
|
||||
continuously, so a corridor taken at a slight angle rang the bonk twice a
|
||||
second from end to end. The wall stopping you is the feedback.
|
||||
|
||||
## 1.5.2
|
||||
|
||||
### Added
|
||||
|
||||
@@ -40,6 +40,11 @@ through the engine's own machinery.
|
||||
| right stick | look |
|
||||
| a touch drag off the overlay's controls | look |
|
||||
| left stick / touch d-pad / arrow keys | walk, relative to where the camera looks |
|
||||
| wheel, `Q` / `E`, pinch, or a stick click | **3RD only** — let the boom out and pull it in (`Q` and left stick click out, `E` and right stick click in) |
|
||||
|
||||
On an **orbit rung** the same wheel, `Q`/`E` and pinch drive the engine's own
|
||||
survey zoom. On **1ST** they do nothing at all: the eye is in your head, and
|
||||
there is no distance to change.
|
||||
|
||||
On **3RD** the boom shortens against whatever is behind you, so backing into
|
||||
a wall walks the camera in to your shoulders rather than through it — squeeze
|
||||
|
||||
+77
-40
@@ -3314,29 +3314,56 @@ end
|
||||
-- carries the animation: when a frame keys that pixel out, the wall's own
|
||||
-- fragments discard with the faces either side of it, so a swaying tuft
|
||||
-- never leaves a wall standing where its blade no longer is.
|
||||
-- `everyPixel` is for a standee whose silhouette ANIMATES. The mesh is
|
||||
-- built once, over the UNION of every frame's mask, and each frame is cut
|
||||
-- out again in texture space -- so a run that is six pixels wide in the
|
||||
-- union may be two pixels wide in the frame on screen, and the four pixels
|
||||
-- that dropped out took the union's end walls with them. What is left
|
||||
-- exposed is an interior boundary, which had no wall because in the union
|
||||
-- it was not a boundary at all. That is the gap that survived closing the
|
||||
-- run ends: the first frame looked solid and every other frame did not.
|
||||
--
|
||||
-- So an animated standee gets a wall on BOTH sides of EVERY pixel. A wall
|
||||
-- between two lit pixels is enclosed by the front and back faces and never
|
||||
-- seen; the moment its neighbour is keyed out it becomes the edge, already
|
||||
-- in place and already wearing the right colour. Each is inset a hair into
|
||||
-- its own pixel so the two that meet at a boundary are not coplanar -- the
|
||||
-- voxel pass draws with culling off, and two quads in the same plane would
|
||||
-- z-fight rather than politely take turns.
|
||||
local SIDE_INSET = 0.03
|
||||
|
||||
local function sideQuads(quads, ix, ix2, yBot, yTop, zB, zF,
|
||||
ax0, ay0, atlasW, atlasH, py, lit)
|
||||
ax0, ay0, atlasW, atlasH, py, lit, everyPixel)
|
||||
local function texel(px)
|
||||
return (ax0 + px + 0.5) / atlasW, (ay0 + py + 0.5) / atlasH
|
||||
end
|
||||
if not lit(ix - 1, py) then
|
||||
local u, v = texel(ix)
|
||||
quads[#quads + 1] = { -- the run's left wall, facing -X
|
||||
{ ix, yBot, zB }, { ix, yBot, zF },
|
||||
{ ix, yTop, zF }, { ix, yTop, zB },
|
||||
local function left(px, at)
|
||||
local u, v = texel(px)
|
||||
quads[#quads + 1] = { -- facing -X
|
||||
{ at, yBot, zB }, { at, yBot, zF },
|
||||
{ at, yTop, zF }, { at, yTop, zB },
|
||||
uv = { { u, v }, { u, v }, { u, v }, { u, v } },
|
||||
shade = OBJ_SHADE.side,
|
||||
}
|
||||
end
|
||||
if not lit(ix2 + 1, py) then
|
||||
local u, v = texel(ix2)
|
||||
quads[#quads + 1] = { -- and its right wall, facing +X
|
||||
{ ix2 + 1, yBot, zF }, { ix2 + 1, yBot, zB },
|
||||
{ ix2 + 1, yTop, zB }, { ix2 + 1, yTop, zF },
|
||||
local function right(px, at)
|
||||
local u, v = texel(px)
|
||||
quads[#quads + 1] = { -- facing +X
|
||||
{ at, yBot, zF }, { at, yBot, zB },
|
||||
{ at, yTop, zB }, { at, yTop, zF },
|
||||
uv = { { u, v }, { u, v }, { u, v }, { u, v } },
|
||||
shade = OBJ_SHADE.side,
|
||||
}
|
||||
end
|
||||
if everyPixel then
|
||||
for px = ix, ix2 do
|
||||
left(px, px + SIDE_INSET)
|
||||
right(px, px + 1 - SIDE_INSET)
|
||||
end
|
||||
return
|
||||
end
|
||||
if not lit(ix - 1, py) then left(ix, ix) end
|
||||
if not lit(ix2 + 1, py) then right(ix2, ix2 + 1) end
|
||||
end
|
||||
|
||||
-- A tall-grass CELL is four tufts: 2x2 tiles, and each 8x8 tile is one
|
||||
@@ -3590,41 +3617,51 @@ local function flowerTemplate(map, data, tileId)
|
||||
uv = { { u1, v1 }, { u0, v1 }, { u0, v0 }, { u1, v0 } },
|
||||
shade = OBJ_SHADE.back,
|
||||
}
|
||||
-- petal tips: a top strip where the row above is clear. The
|
||||
-- strip samples its own row's texel, so a tip that is not in
|
||||
-- the current frame discards with the face beneath it
|
||||
if not on(ix, py - 1) then
|
||||
quads[#quads + 1] = {
|
||||
{ ix, yTop, zB }, { ix2 + 1, yTop, zB },
|
||||
{ ix2 + 1, yTop, zF }, { ix, yTop, zF },
|
||||
uv = { { u0, v0 }, { u1, v0 }, { u1, v0 }, { u0, v0 } },
|
||||
-- ------- the shell, closed on all four remaining faces
|
||||
--
|
||||
-- A flower SWAYS: the geometry spans the union of every animation
|
||||
-- frame's mask and each frame is cut back out of it in texture
|
||||
-- space (see the header). So "is there a pixel next door" has two
|
||||
-- different answers -- one in the union this mesh was built from,
|
||||
-- and one in the frame actually on screen -- and only the second
|
||||
-- decides what is exposed.
|
||||
--
|
||||
-- Closing the union's own edges is therefore not enough, and was
|
||||
-- the bug the first cut of this shipped: the base frame looked
|
||||
-- solid and every other frame still had gaps, because a pixel that
|
||||
-- drops out of a frame takes the union's wall with it and leaves an
|
||||
-- interior boundary that never had one.
|
||||
--
|
||||
-- So every pixel gets a cap on all four of its remaining faces,
|
||||
-- whatever its neighbours do. A cap between two lit pixels sits
|
||||
-- inside the slab, enclosed by the front and back faces, and is
|
||||
-- never seen; the moment its neighbour is keyed out it IS the edge,
|
||||
-- already there and already wearing the right colour. Each samples
|
||||
-- its own pixel's texel, so it appears and vanishes with the pixel
|
||||
-- it belongs to rather than with the one it is closing off.
|
||||
--
|
||||
-- Inset a hair into its own pixel, because the voxel pass draws
|
||||
-- with culling off: the two caps that meet at a boundary would be
|
||||
-- coplanar and z-fight rather than politely take turns.
|
||||
for px = ix, ix2 do
|
||||
local tu = (ax0 + px + 0.5) / atlasW
|
||||
local tv = (ay0 + py + 0.5) / atlasH
|
||||
local xa, xb = px, px + 1
|
||||
local yT = yTop - SIDE_INSET
|
||||
local yB = yBot + SIDE_INSET
|
||||
quads[#quads + 1] = { -- the pixel's own lid
|
||||
{ xa, yT, zB }, { xb, yT, zB }, { xb, yT, zF }, { xa, yT, zF },
|
||||
uv = { { tu, tv }, { tu, tv }, { tu, tv }, { tu, tv } },
|
||||
shade = OBJ_SHADE.top,
|
||||
}
|
||||
end
|
||||
-- and the same strip on the bottom, where the row below is clear:
|
||||
-- a petal that ends mid-air is a solid thing seen from underneath,
|
||||
-- and a low camera (1ST, 3RD, the battle's floor-level seat) is
|
||||
-- looking straight up at it
|
||||
if not on(ix, py + 1) then
|
||||
quads[#quads + 1] = {
|
||||
{ ix, yBot, zF }, { ix2 + 1, yBot, zF },
|
||||
{ ix2 + 1, yBot, zB }, { ix, yBot, zB },
|
||||
uv = { { u0, v1 }, { u1, v1 }, { u1, v1 }, { u0, v1 } },
|
||||
quads[#quads + 1] = { -- and its floor
|
||||
{ xa, yB, zF }, { xb, yB, zF }, { xb, yB, zB }, { xa, yB, zB },
|
||||
uv = { { tu, tv }, { tu, tv }, { tu, tv }, { tu, tv } },
|
||||
shade = OBJ_SHADE.bottom,
|
||||
}
|
||||
end
|
||||
-- The ENDS of the run, which close the slab off sideways. Without
|
||||
-- them the flower is two faces and a lid: from anywhere but square
|
||||
-- on you look straight through its open edge and out the far side,
|
||||
-- which is what stopped it reading as a solid thing.
|
||||
--
|
||||
-- Each wall samples ONE texel -- the run's own end pixel, at its
|
||||
-- centre -- so the side wears the colour of the pixel it is closing
|
||||
-- off (the nearest coloured pixel there is) rather than a keyed
|
||||
-- hole, and discards with that pixel when the animation frame it
|
||||
-- belongs to is not the one on screen.
|
||||
sideQuads(quads, ix, ix2, yBot, yTop, zB, zF,
|
||||
ax0, ay0, atlasW, atlasH, py, on)
|
||||
ax0, ay0, atlasW, atlasH, py, on, true)
|
||||
ix = ix2 + 1
|
||||
else
|
||||
ix = ix + 1
|
||||
|
||||
@@ -646,6 +646,25 @@ end
|
||||
--
|
||||
-- Everything they did is still reachable: uninstall the mod and both rows are
|
||||
-- back, at whatever they were last set to.
|
||||
-- BATTLE BG rides the same reasoning, and comes off for a reason of its own.
|
||||
-- The row picks what fills the screen AROUND the battle's 160x144 field --
|
||||
-- WHITE paper, BLACK bars, or the frozen overworld dimmed behind it -- and
|
||||
-- all three were answers to the same question: what to do with the voids,
|
||||
-- given the battle is a small picture in the middle of a big window.
|
||||
--
|
||||
-- This mod answers that question differently and permanently. A staged fight
|
||||
-- fills the whole window with the map the fight is standing on, and the
|
||||
-- flat battle screen it composites over it is drawn on the mode's own
|
||||
-- surface; there are no voids left for the row to fill. WORLD is the worst
|
||||
-- of the three under it -- it makes the battle non-opaque so the engine
|
||||
-- draws the overworld underneath, which is a SECOND copy of the world drawn
|
||||
-- under the one the arena pass already put there, dimmed and at a different
|
||||
-- camera. BLACK bars over a diorama read as a letterboxed screenshot.
|
||||
--
|
||||
-- So the value is pinned at WHITE, which is the one the mode was composed
|
||||
-- against, and the row comes off the menu on the same reasoning as TILT and
|
||||
-- GBC FX: a row that no longer decides anything is worse than no row.
|
||||
-- Uninstall the mod and it is back, at whatever it was last set to.
|
||||
local function pinEngineFx(game)
|
||||
game = game or require("src.core.Game")
|
||||
local opts = game and game.save and game.save.options
|
||||
@@ -654,7 +673,9 @@ local function pinEngineFx(game)
|
||||
local changed = false
|
||||
if opts then
|
||||
changed = (opts.tilt or 0) ~= 0 or (opts.gbcfx or 0) ~= 0
|
||||
or (opts.battleBg or "white") ~= "white"
|
||||
opts.tilt, opts.gbcfx = 0, 0
|
||||
opts.battleBg = "white"
|
||||
end
|
||||
pcall(Tilt.setLevel, 0)
|
||||
pcall(GBCFX.setLevel, 0)
|
||||
@@ -672,6 +693,10 @@ mod.hooks:wrap("ui.options.rows", function(next, game, rows)
|
||||
pinEngineFx(game)
|
||||
dropRow(out, "tilt")
|
||||
dropRow(out, "gbcfx")
|
||||
-- and BATTLE BG with them: this mode fills the window with the map, so
|
||||
-- the row's whole question -- what to put in the voids around the battle
|
||||
-- -- no longer has voids to be about (see pinEngineFx)
|
||||
dropRow(out, "battleBg")
|
||||
-- BATTLE LAYOUT is the ENGINE's row, and this is the one place the mod takes
|
||||
-- one away. While a fight can be staged on the map, OG is the only layout it
|
||||
-- can be composed in (OverworldBattle.forceOG), so the value is pinned there
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
-- Scratch driver: the cameras the player steers -- the third-person boom's
|
||||
-- zoom, and the battle shot's orbit, climb and lens, including the far
|
||||
-- stops (side-on, 45 degrees up) and what BACK SPRITES takes away.
|
||||
--
|
||||
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/cam_shots.lua \
|
||||
-- SHOT_DIR=.scratchpad/camshots 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 "shots/cam")
|
||||
|
||||
local handle = game.mods.exports["DRAMATIC_SHAPE"]
|
||||
if not (handle and handle.lib) then
|
||||
print("[cam] DRAMATIC_SHAPE mod not loaded")
|
||||
return love.event.quit()
|
||||
end
|
||||
local V = handle.lib
|
||||
local FirstPerson = V.require("FirstPerson")
|
||||
local ThirdPerson = V.require("ThirdPerson")
|
||||
local BattleCam = V.require("BattleCam")
|
||||
local OverworldBattle = V.require("OverworldBattle")
|
||||
local ChunkMesher = V.require("ChunkMesher")
|
||||
local Voxel = V.require("VoxelState")
|
||||
local DayNight = V.require("DayNight")
|
||||
|
||||
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
|
||||
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
|
||||
|
||||
require("src.world.OverworldController").rollEncounter = function() return nil end
|
||||
local TileRenderer = require("src.render.TileRenderer")
|
||||
TileRenderer.tick = function() end
|
||||
-- The flower's animation frame, pinned per shot rather than left to the
|
||||
-- clock: the mesh spans the union of every frame and each one is cut back
|
||||
-- out in texture space, so a gap that only opens on frame 2 is invisible
|
||||
-- to a driver that always photographs frame 0 -- which is exactly how the
|
||||
-- first cut of the closed sides shipped looking correct.
|
||||
local ANIM = { frame = 0 }
|
||||
TileRenderer.animFrame = function() return ANIM.frame end
|
||||
DayNight.setting:sync("day")
|
||||
|
||||
local function settle()
|
||||
for _ = 1, 900 do
|
||||
if ChunkMesher.pending() == 0 then break end
|
||||
U.wait(1)
|
||||
end
|
||||
U.wait(40)
|
||||
end
|
||||
|
||||
local shots = 0
|
||||
local function shot(name)
|
||||
if U.shot(game, ("%s/%s.png"):format(ROOT, name)) then
|
||||
shots = shots + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- ------- the boom's zoom
|
||||
U.teleport(game, "PALLET_TOWN", 13, 14, "down")
|
||||
Pipelines.setLevel("voxel", Voxel.TP_LEVEL)
|
||||
Pipelines.setLevel("tiltshift", 0)
|
||||
settle()
|
||||
for _ = 1, 200 do
|
||||
if FirstPerson.blend >= 1 then break end
|
||||
U.wait(1)
|
||||
end
|
||||
FirstPerson.yaw = math.pi
|
||||
U.wait(20)
|
||||
for _, z in ipairs({ "min", "default", "max" }) do
|
||||
ThirdPerson.zoomGoal = (z == "min" and ThirdPerson.ZOOM_MIN)
|
||||
or (z == "max" and ThirdPerson.ZOOM_MAX) or 1
|
||||
for _ = 1, 90 do U.wait(1) end
|
||||
print(("[cam] boom %-8s zoom %.2f len %.1f"):format(z, ThirdPerson.zoom,
|
||||
ThirdPerson.len))
|
||||
shot("boom_" .. z)
|
||||
end
|
||||
ThirdPerson.zoomGoal = 1
|
||||
U.wait(60)
|
||||
|
||||
-- ------- the standees' closed sides
|
||||
--
|
||||
-- Pallet's flower beds and Route 1's tall grass, from a low camera close
|
||||
-- in -- the angle that showed the slabs were open at the ends of every
|
||||
-- run and let you see straight through them.
|
||||
ThirdPerson.zoomGoal = ThirdPerson.ZOOM_MIN
|
||||
for _, s in ipairs({
|
||||
{ map = "PALLET_TOWN", x = 13, y = 14, yaw = math.pi,
|
||||
pitch = math.rad(6), label = "flowers_low" },
|
||||
{ map = "PALLET_TOWN", x = 13, y = 14, yaw = 3 * math.pi / 4,
|
||||
pitch = math.rad(30), label = "flowers_angled" },
|
||||
{ map = "ROUTE_1", x = 10, y = 28, yaw = math.pi,
|
||||
pitch = math.rad(8), label = "grass_low" },
|
||||
}) do
|
||||
U.teleport(game, s.map, s.x, s.y, "down")
|
||||
settle()
|
||||
FirstPerson.yaw, FirstPerson.pitch = s.yaw, s.pitch
|
||||
U.wait(60)
|
||||
-- EVERY animation frame, because a gap the union closed and a frame
|
||||
-- reopens is only visible on that frame
|
||||
for f = 0, 3 do
|
||||
ANIM.frame = f
|
||||
U.wait(6)
|
||||
shot(("%s_f%d"):format(s.label, f))
|
||||
end
|
||||
ANIM.frame = 0
|
||||
end
|
||||
ThirdPerson.zoomGoal = 1
|
||||
U.wait(60)
|
||||
|
||||
-- ------- the battle shot
|
||||
--
|
||||
-- Staged the way the game stages one, then steered to each stop with the
|
||||
-- module's own entry points -- the same ones the wheel, the stick and a
|
||||
-- drag reach.
|
||||
Pipelines.setLevel("voxel", 4)
|
||||
U.wait(60)
|
||||
do
|
||||
-- Somebody to fight WITH: a fresh driver save has an empty party, and
|
||||
-- a trainer battle with nobody to send out ends on the frame it starts
|
||||
-- -- which is what made every steered shot below sample a dead session.
|
||||
local Pokemon = require("src.pokemon.Pokemon")
|
||||
game.save.party = {
|
||||
Pokemon.new(game.data, "CHARIZARD", 45),
|
||||
Pokemon.new(game.data, "PIKACHU", 40),
|
||||
}
|
||||
game.save.player.name = "RED"
|
||||
local BattleState = require("src.battle.BattleState")
|
||||
local class = next(game.data.trainers)
|
||||
local battle = BattleState.newTrainer(game, class, 1)
|
||||
battle.onFinish = function() end
|
||||
game.overworld:pushBattle(battle)
|
||||
end
|
||||
-- The wipe, then just enough of the send-out chatter to get both mons
|
||||
-- standing on the arena -- and NOT one tap more. A is also FIGHT and then
|
||||
-- the first move, so tapping past the intro starts an exchange and the
|
||||
-- fight can be over before the camera has been steered anywhere.
|
||||
U.wait(70)
|
||||
for _ = 1, 40 do
|
||||
if OverworldBattle.shot() then break end
|
||||
U.tap(game, "a")
|
||||
U.wait(10)
|
||||
end
|
||||
U.wait(60)
|
||||
print(("[cam] staged: arena %s shot %s top %s")
|
||||
:format(tostring(OverworldBattle.arena() ~= nil),
|
||||
tostring(OverworldBattle.shot() ~= nil),
|
||||
tostring(game.stack:top() ~= game.overworld)))
|
||||
if not OverworldBattle.shot() then
|
||||
print("[cam] no staged battle -- skipping the battle shots")
|
||||
print(("[cam] %d shots into %s"):format(shots, ROOT))
|
||||
return love.event.quit()
|
||||
end
|
||||
|
||||
local function settleCam(label)
|
||||
for _ = 1, 120 do U.wait(1) end
|
||||
print(("[cam] battle %-20s orbit %.2f/%.2f pitch %.2f/%.2f "
|
||||
.. "zoom %.2f/%.2f steerable %s live %s")
|
||||
:format(label, BattleCam.orbit, BattleCam.orbitGoal,
|
||||
BattleCam.pitch, BattleCam.pitchGoal,
|
||||
BattleCam.zoom, BattleCam.zoomGoal,
|
||||
tostring(BattleCam.steerable),
|
||||
tostring(OverworldBattle.shot() ~= nil)))
|
||||
shot("battle_" .. label)
|
||||
end
|
||||
|
||||
BattleCam.recentre()
|
||||
settleCam("home")
|
||||
|
||||
-- right to the stop: this must land SQUARE to the arena's axis
|
||||
BattleCam.dragOrbit(10)
|
||||
settleCam("side_on")
|
||||
|
||||
-- and there is nothing to the left of home
|
||||
BattleCam.recentre()
|
||||
BattleCam.dragOrbit(-10)
|
||||
settleCam("left_stop")
|
||||
|
||||
-- up to the stop, and refusing to go below home
|
||||
BattleCam.recentre()
|
||||
BattleCam.dragPitch(10)
|
||||
settleCam("high")
|
||||
BattleCam.recentre()
|
||||
BattleCam.dragPitch(-10)
|
||||
settleCam("low_stop")
|
||||
|
||||
-- the lens at both ends
|
||||
BattleCam.recentre()
|
||||
for _ = 1, 40 do BattleCam.stepZoom(-1) end
|
||||
settleCam("zoom_in")
|
||||
BattleCam.recentre()
|
||||
for _ = 1, 40 do BattleCam.stepZoom(1) end
|
||||
settleCam("zoom_out")
|
||||
|
||||
-- everything at once, which is the shot a player would actually build
|
||||
BattleCam.recentre()
|
||||
BattleCam.dragOrbit(0.55)
|
||||
BattleCam.dragPitch(0.5)
|
||||
for _ = 1, 4 do BattleCam.stepZoom(-1) end
|
||||
settleCam("steered")
|
||||
|
||||
-- What BACK SPRITES takes away is NOT shot here: the flag it works
|
||||
-- through is re-derived from the row every frame
|
||||
-- (OverworldBattle.update), so a driver cannot hold it down for the
|
||||
-- hundred-odd frames a settled shot needs, and a picture that claimed to
|
||||
-- show a locked camera while the camera was in fact free would be worse
|
||||
-- than no picture. The suite asserts it instead, on both axes and the
|
||||
-- lens, and on the RIG as well as on the inputs.
|
||||
BattleCam.recentre()
|
||||
|
||||
print(("[cam] %d shots into %s"):format(shots, ROOT))
|
||||
love.event.quit()
|
||||
end
|
||||
Reference in New Issue
Block a user