mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 08:31:06 +02:00
fix battle rotations in vr
This commit is contained in:
+74
-64
@@ -63,14 +63,14 @@ return {
|
||||
-- height along the sightline and a hedge in the apron row is not terrain.
|
||||
-- So the choice is where in the clearing, and this is its west end: tree
|
||||
-- line square behind the pair, nothing crossing either of them.
|
||||
["ROUTE_1"] = { x = 4, y = 14, shape = "narrow" },
|
||||
["ROUTE_1"] = { x = 10, y = 16, shape = "narrow", turn = 90 },
|
||||
["ROUTE_2"] = { x = 1, y = 49, shape = "wide" },
|
||||
["ROUTE_3"] = { x = 57, y = 1, shape = "wide" },
|
||||
["ROUTE_4"] = { x = 46, y = 7, shape = "wide" },
|
||||
["ROUTE_5"] = { x = 13, y = 24, shape = "wide" },
|
||||
["ROUTE_6"] = { x = 5, y = 17, shape = "narrow" },
|
||||
["ROUTE_7"] = { x = 8, y = 8, shape = "narrow" },
|
||||
["ROUTE_8"] = { x = 25, y = 7, shape = "wide" },
|
||||
["ROUTE_3"] = { x = 52, y = 10, shape = "wide", turn = 90 },
|
||||
["ROUTE_4"] = { x = 46, y = 4, shape = "wide" },
|
||||
["ROUTE_5"] = { x = 7, y = 0, shape = "wide", turn = 270 },
|
||||
["ROUTE_6"] = { x = 4, y = 15, shape = "narrow" },
|
||||
["ROUTE_7"] = { x = 4, y = 3, shape = "narrow", cam = "wide" },
|
||||
["ROUTE_8"] = { x = 24, y = 5, shape = "wide", turn = 90 },
|
||||
-- the whole route admits six bare wide arenas, all in the west cliff
|
||||
-- corridor; this is the best of them. A flower cluster crosses the far
|
||||
-- mon's hind legs, which the brief allows -- every alternative put a
|
||||
@@ -78,13 +78,13 @@ return {
|
||||
["ROUTE_9"] = { x = 1, y = 11, shape = "wide", cam = "wide" },
|
||||
["ROUTE_10"] = { x = 7, y = 40, shape = "wide" },
|
||||
["ROUTE_11"] = { x = 9, y = 6, shape = "wide" },
|
||||
["ROUTE_12"] = { x = 0, y = 73, shape = "wide" },
|
||||
["ROUTE_13"] = { x = 50, y = 8, shape = "narrow" },
|
||||
["ROUTE_14"] = { x = 11, y = 25, shape = "wide" },
|
||||
["ROUTE_15"] = { x = 9, y = 10, shape = "wide" },
|
||||
["ROUTE_16"] = { x = 6, y = 10, shape = "wide" },
|
||||
["ROUTE_12"] = { x = 10, y = 71, shape = "wide", turn = 90 },
|
||||
["ROUTE_13"] = { x = 51, y = 5, shape = "narrow" },
|
||||
["ROUTE_14"] = { x = 11, y = 24, shape = "wide" },
|
||||
["ROUTE_15"] = { x = 30, y = 10, shape = "wide", turn = 90 },
|
||||
["ROUTE_16"] = { x = 8, y = 10, shape = "wide", turn = 90 },
|
||||
["ROUTE_17"] = { x = 14, y = 70, shape = "wide" },
|
||||
["ROUTE_18"] = { x = 11, y = 4, shape = "wide" },
|
||||
["ROUTE_18"] = { x = 11, y = 3, shape = "wide" },
|
||||
|
||||
-- ------- buildings and caves
|
||||
--
|
||||
@@ -92,10 +92,10 @@ return {
|
||||
-- or gravestones in it rarely holds a 3x6 clearing whose whole width is
|
||||
-- also SEEN, and giving up the apron is usually the difference between a
|
||||
-- fight in the open and one behind a console.
|
||||
["POKEMON_MANSION_1F"] = { x = 4, y = 12, shape = "wide" },
|
||||
["POKEMON_MANSION_2F"] = { x = 15, y = 17, shape = "wide" },
|
||||
["POKEMON_MANSION_3F"] = { x = 23, y = 2, shape = "narrow", cam = "wide" },
|
||||
["POKEMON_MANSION_B1F"] = { x = 19, y = 10, shape = "wide" },
|
||||
["POKEMON_MANSION_1F"] = { x = 4, y = 12, shape = "wide", cam = "wide" },
|
||||
["POKEMON_MANSION_2F"] = { x = 10, y = 1, shape = "wide", cam = "wide" },
|
||||
["POKEMON_MANSION_3F"] = { x = 24, y = 3, shape = "narrow", cam = "wide" },
|
||||
["POKEMON_MANSION_B1F"] = { x = 6, y = 19, shape = "wide", turn = 90 },
|
||||
["POKEMON_TOWER_2F"] = { x = 4, y = 7, shape = "narrow" },
|
||||
["POKEMON_TOWER_3F"] = { x = 4, y = 6, shape = "wide" },
|
||||
-- every one of 4F's thirty candidate spots puts a gravestone through a
|
||||
@@ -103,73 +103,79 @@ return {
|
||||
-- is shot there
|
||||
["POKEMON_TOWER_4F"] = { map = "POKEMON_TOWER_3F", x = 4, y = 6,
|
||||
shape = "wide" },
|
||||
["POKEMON_TOWER_5F"] = { x = 10, y = 1, shape = "narrow" },
|
||||
["POKEMON_TOWER_6F"] = { x = 14, y = 6, shape = "narrow" },
|
||||
["POKEMON_TOWER_5F"] = { x = 8, y = 9, shape = "narrow", cam = "wide" },
|
||||
["POKEMON_TOWER_6F"] = { x = 14, y = 6, shape = "narrow", cam = "wide" },
|
||||
["POKEMON_TOWER_7F"] = { x = 9, y = 5, shape = "wide" },
|
||||
["POWER_PLANT"] = { x = 18, y = 5, shape = "narrow" },
|
||||
["ROCK_TUNNEL_1F"] = { x = 14, y = 15, shape = "wide" },
|
||||
["ROCK_TUNNEL_B1F"] = { x = 20, y = 17, shape = "wide" },
|
||||
["ROCKET_HIDEOUT_B1F"] = { x = 11, y = 6, shape = "narrow" },
|
||||
["ROCKET_HIDEOUT_B2F"] = { x = 19, y = 7, shape = "narrow" },
|
||||
["ROCKET_HIDEOUT_B3F"] = { x = 22, y = 11, shape = "narrow" },
|
||||
["ROCKET_HIDEOUT_B4F"] = { x = 17, y = 3, shape = "narrow" },
|
||||
["ROCKET_HIDEOUT_B1F"] = { x = 20, y = 18, shape = "narrow" },
|
||||
["ROCKET_HIDEOUT_B2F"] = { x = 20, y = 10, shape = "narrow", cam = "wide" },
|
||||
["ROCKET_HIDEOUT_B3F"] = { x = 24, y = 15,
|
||||
shape = "narrow", turn = 90, cam = "wide" },
|
||||
["ROCKET_HIDEOUT_B4F"] = { x = 19, y = 17, shape = "wide", cam = "wide" },
|
||||
["SAFARI_ZONE_CENTER"] = { x = 1, y = 8, shape = "wide" },
|
||||
["SAFARI_ZONE_EAST"] = { x = 21, y = 8, shape = "wide" },
|
||||
["SAFARI_ZONE_NORTH"] = { x = 19, y = 14, shape = "wide" },
|
||||
["SAFARI_ZONE_WEST"] = { x = 18, y = 3, shape = "wide" },
|
||||
["SAFARI_ZONE_EAST"] = { x = 16, y = 8,
|
||||
shape = "wide", turn = 90, cam = "wide" },
|
||||
["SAFARI_ZONE_NORTH"] = { x = 22, y = 12, shape = "wide", turn = 90 },
|
||||
["SAFARI_ZONE_WEST"] = { x = 20, y = 2, shape = "wide" },
|
||||
["SEAFOAM_ISLANDS_1F"] = { x = 14, y = 7, shape = "wide" },
|
||||
["SEAFOAM_ISLANDS_B1F"] = { x = 11, y = 1, shape = "wide" },
|
||||
["SEAFOAM_ISLANDS_B2F"] = { x = 16, y = 2, shape = "wide" },
|
||||
["SEAFOAM_ISLANDS_B3F"] = { x = 25, y = 7, shape = "wide" },
|
||||
["SEAFOAM_ISLANDS_B4F"] = { x = 12, y = 6, shape = "narrow" },
|
||||
["SEAFOAM_ISLANDS_B1F"] = { x = 9, y = 8,
|
||||
shape = "wide", turn = 90, cam = "wide" },
|
||||
["SEAFOAM_ISLANDS_B2F"] = { x = 15, y = 9, shape = "wide", turn = 90 },
|
||||
["SEAFOAM_ISLANDS_B3F"] = { x = 26, y = 7, shape = "wide", turn = 180 },
|
||||
["SEAFOAM_ISLANDS_B4F"] = { x = 9, y = 7, shape = "narrow" },
|
||||
-- Silph Co is office floors partitioned into small rooms, so the long lens
|
||||
-- often lands outside the walls it is meant to be looking between; the
|
||||
-- floors that could not be framed any other way ask for the wide one.
|
||||
["SILPH_CO_2F"] = { x = 16, y = 8, shape = "narrow" },
|
||||
["SILPH_CO_4F"] = { x = 24, y = 2, shape = "narrow" },
|
||||
["SILPH_CO_2F"] = { x = 4, y = 9, shape = "narrow", turn = 270 },
|
||||
["SILPH_CO_4F"] = { x = 14, y = 14, shape = "narrow", turn = 90 },
|
||||
["SILPH_CO_5F"] = { x = 16, y = 7, shape = "wide" },
|
||||
["SILPH_CO_6F"] = { x = 10, y = 8, shape = "narrow" },
|
||||
["SILPH_CO_6F"] = { x = 19, y = 2,
|
||||
shape = "narrow", turn = 90, cam = "wide" },
|
||||
["SILPH_CO_7F"] = { x = 1, y = 2, shape = "wide", cam = "wide" },
|
||||
["SILPH_CO_8F"] = { x = 8, y = 6, shape = "narrow" },
|
||||
["SILPH_CO_9F"] = { x = 20, y = 11, shape = "wide", cam = "wide" },
|
||||
["SS_ANNE_1F_ROOMS"] = { x = 10, y = 1, shape = "narrow", cam = "wide" },
|
||||
["SS_ANNE_1F_ROOMS"] = { x = 11, y = 1, shape = "narrow", cam = "wide" },
|
||||
-- the ship is all two-cell corridors, so the wide arena shape fits nowhere
|
||||
-- aboard and the long lens always lands outside the hull
|
||||
["SS_ANNE_2F"] = { x = 36, y = 8, shape = "narrow", cam = "wide" },
|
||||
-- these two decks are byte-identical geometry, so they take the same spot
|
||||
["SS_ANNE_2F_ROOMS"] = { x = 11, y = 12, shape = "narrow" },
|
||||
["SS_ANNE_B1F_ROOMS"] = { x = 11, y = 12, shape = "narrow" },
|
||||
["SS_ANNE_2F_ROOMS"] = { x = 11, y = 12, shape = "narrow", cam = "wide" },
|
||||
["SS_ANNE_B1F_ROOMS"] = { x = 11, y = 12, shape = "narrow", cam = "wide" },
|
||||
["SS_ANNE_BOW"] = { x = 8, y = 3, shape = "wide" },
|
||||
["VICTORY_ROAD_2F"] = { x = 16, y = 6, shape = "wide" },
|
||||
["VICTORY_ROAD_3F"] = { x = 20, y = 1, shape = "wide" },
|
||||
["VICTORY_ROAD_2F"] = { x = 16, y = 6, shape = "wide", cam = "wide" },
|
||||
["VICTORY_ROAD_3F"] = { x = 20, y = 1, shape = "wide", cam = "wide" },
|
||||
|
||||
-- ------- towns and the last interiors
|
||||
["CERULEAN_CITY"] = { x = 15, y = 16, shape = "wide" },
|
||||
["GAME_CORNER"] = { x = 8, y = 7, shape = "wide" },
|
||||
["GAME_CORNER"] = { x = 8, y = 5, shape = "wide" },
|
||||
-- the lab is ten cells by twelve, so the long lens is always off-map, and
|
||||
-- on it a desk clipped one mon and a pillar the other
|
||||
["OAKS_LAB"] = { x = 3, y = 2, shape = "narrow", cam = "wide" },
|
||||
["OAKS_LAB"] = { x = 1, y = 3, shape = "narrow", turn = 90, cam = "wide" },
|
||||
-- Saffron's gym is a grid of small walled cells: no wide shape exists
|
||||
-- anywhere in it, and the long lens sits inside a divider
|
||||
["SAFFRON_GYM"] = { x = 9, y = 7, shape = "narrow", cam = "wide" },
|
||||
["SILPH_CO_3F"] = { x = 18, y = 11, shape = "wide", cam = "wide" },
|
||||
["SILPH_CO_10F"] = { x = 1, y = 2, shape = "wide" },
|
||||
["SILPH_CO_11F"] = { x = 1, y = 11, shape = "wide", cam = "wide" },
|
||||
["SILPH_CO_10F"] = { x = 1, y = 1, shape = "wide", cam = "wide" },
|
||||
["SILPH_CO_11F"] = { x = 10, y = 6,
|
||||
shape = "wide", turn = 180, cam = "wide" },
|
||||
-- the upper corridor (cols 4-5, rows 1-4) is sealed at runtime by the
|
||||
-- gym's barrier, so arenas there silently fail the fit test
|
||||
["VERMILION_GYM"] = { x = 4, y = 11, shape = "narrow" },
|
||||
["VICTORY_ROAD_1F"] = { x = 11, y = 2, shape = "narrow" },
|
||||
["VERMILION_GYM"] = { x = 3, y = 2,
|
||||
shape = "narrow", turn = 90, cam = "wide" },
|
||||
["VICTORY_ROAD_1F"] = { x = 11, y = 5, shape = "narrow", cam = "wide" },
|
||||
["VIRIDIAN_FOREST"] = { x = 16, y = 34, shape = "narrow" },
|
||||
|
||||
-- ------- the remaining routes
|
||||
["ROUTE_19"] = { x = 8, y = 6, shape = "narrow" },
|
||||
["ROUTE_19"] = { x = 8, y = 31, shape = "narrow" },
|
||||
-- the two surf routes fight AFLOAT, in the middle of their own sea rather
|
||||
-- than on the rim of beach the land search would otherwise find
|
||||
["ROUTE_20"] = { x = 23, y = 7, shape = "wide" },
|
||||
["ROUTE_21"] = { x = 8, y = 46, shape = "wide" },
|
||||
["ROUTE_22"] = { x = 35, y = 7, shape = "wide" },
|
||||
["ROUTE_23"] = { x = 4, y = 36, shape = "wide" },
|
||||
["ROUTE_24"] = { x = 13, y = 15, shape = "wide" },
|
||||
["ROUTE_22"] = { x = 35, y = 7, shape = "wide", cam = "wide" },
|
||||
["ROUTE_23"] = { x = 2, y = 34, shape = "wide", cam = "wide" },
|
||||
["ROUTE_24"] = { x = 6, y = 9, shape = "wide", turn = 270, cam = "wide" },
|
||||
["ROUTE_25"] = { x = 32, y = 2, shape = "wide", cam = "wide" },
|
||||
|
||||
-- ------- caves, gyms and the Elite Four
|
||||
@@ -177,25 +183,29 @@ return {
|
||||
-- None of these tilesets has a grass tile at all, so the no-grass rule
|
||||
-- constrained nothing here; what constrains them is furniture, rock
|
||||
-- pillars and how small the rooms are.
|
||||
["AGATHAS_ROOM"] = { x = 2, y = 1, shape = "narrow", cam = "wide" },
|
||||
["BRUNOS_ROOM"] = { x = 3, y = 1, shape = "narrow" },
|
||||
["CELADON_GYM"] = { x = 0, y = 3, shape = "narrow" },
|
||||
["CERULEAN_CAVE_1F"] = { x = 1, y = 7, shape = "narrow" },
|
||||
["AGATHAS_ROOM"] = { x = 4, y = 2, shape = "narrow", cam = "wide" },
|
||||
["BRUNOS_ROOM"] = { x = 2, y = 1, shape = "wide", turn = 90 },
|
||||
["CELADON_GYM"] = { x = 3, y = 4,
|
||||
shape = "narrow", turn = 90, cam = "wide" },
|
||||
["CERULEAN_CAVE_1F"] = { x = 12, y = 8, shape = "narrow", cam = "wide" },
|
||||
-- 2F is a maze of one-cell rock corridors; all 24 of its candidate spots
|
||||
-- hide a mon, so it borrows the floor below -- the same cave
|
||||
["CERULEAN_CAVE_2F"] = { map = "CERULEAN_CAVE_B1F", x = 2, y = 0,
|
||||
shape = "wide" },
|
||||
["CERULEAN_CAVE_B1F"] = { x = 2, y = 0, shape = "wide" },
|
||||
["CERULEAN_GYM"] = { x = 0, y = 1, shape = "narrow" },
|
||||
["CERULEAN_CAVE_B1F"] = { x = 2, y = 0, shape = "wide", cam = "wide" },
|
||||
["CERULEAN_GYM"] = { x = 4, y = 2, shape = "wide" },
|
||||
["CHAMPIONS_ROOM"] = { x = 2, y = 2, shape = "narrow", cam = "wide" },
|
||||
["CINNABAR_GYM"] = { x = 18, y = 10, shape = "narrow" },
|
||||
["DIGLETTS_CAVE"] = { x = 19, y = 16, shape = "wide" },
|
||||
["FIGHTING_DOJO"] = { x = 4, y = 1, shape = "narrow" },
|
||||
["LANCES_ROOM"] = { x = 5, y = 15, shape = "wide" },
|
||||
["LORELEIS_ROOM"] = { x = 5, y = 2, shape = "narrow" },
|
||||
["MT_MOON_1F"] = { x = 24, y = 17, shape = "wide" },
|
||||
["MT_MOON_B1F"] = { x = 5, y = 12, shape = "wide" },
|
||||
["MT_MOON_B2F"] = { x = 2, y = 16, shape = "wide" },
|
||||
["CINNABAR_GYM"] = { x = 9, y = 16,
|
||||
shape = "narrow", turn = 90, cam = "wide" },
|
||||
["DIGLETTS_CAVE"] = { x = 14, y = 26,
|
||||
shape = "wide", turn = 90, cam = "wide" },
|
||||
["FIGHTING_DOJO"] = { x = 3, y = 5,
|
||||
shape = "narrow", turn = 90, cam = "wide" },
|
||||
["LANCES_ROOM"] = { x = 4, y = 2, shape = "wide", cam = "wide" },
|
||||
["LORELEIS_ROOM"] = { x = 4, y = 2, shape = "narrow" },
|
||||
["MT_MOON_1F"] = { x = 25, y = 16, shape = "wide" },
|
||||
["MT_MOON_B1F"] = { x = 4, y = 11, shape = "wide" },
|
||||
["MT_MOON_B2F"] = { x = 8, y = 20, shape = "wide" },
|
||||
|
||||
-- The three gyms the default rig cannot stand back from. Five blocks is
|
||||
-- further than these rooms are wide, so the eye landed outside the map and
|
||||
@@ -204,7 +214,7 @@ return {
|
||||
-- BattleCam), which fits inside the room; the mons come out smaller and all
|
||||
-- three became stageable. It is asked for HERE, per map, so every area that
|
||||
-- does not ask keeps the long lens it was framed for.
|
||||
["FUCHSIA_GYM"] = { x = 7, y = 6, shape = "narrow", cam = "wide" },
|
||||
["PEWTER_GYM"] = { x = 4, y = 8, shape = "narrow", cam = "wide" },
|
||||
["FUCHSIA_GYM"] = { x = 8, y = 6, shape = "narrow", cam = "wide" },
|
||||
["PEWTER_GYM"] = { x = 4, y = 1, shape = "narrow", turn = 90, cam = "wide" },
|
||||
["VIRIDIAN_GYM"] = { x = 10, y = 8, shape = "narrow", cam = "wide" },
|
||||
}
|
||||
|
||||
@@ -113,6 +113,41 @@ function Diorama.reset()
|
||||
Diorama.release()
|
||||
end
|
||||
|
||||
-- ------- which way a staged fight lies on the table
|
||||
--
|
||||
-- The disc's bearing while a battle is up: the player's own hand-turn, with
|
||||
-- the ARENA's quarter turn taken back out of it.
|
||||
--
|
||||
-- An arena may be laid down any of the four ways (BattleArena's `turn`), and
|
||||
-- the promise that field makes everywhere else is that turning it changes the
|
||||
-- GROUND under the fight and never the fight itself -- the two Pokemon land
|
||||
-- on the same marks, seen the same way round. Every other camera keeps that
|
||||
-- promise by construction: the flat shot and the standard VR mount are both
|
||||
-- built from BattleCam's eye, which turns with the arena, so the composition
|
||||
-- follows it round.
|
||||
--
|
||||
-- This one is not built from that eye. It is a disc of map lifted onto the
|
||||
-- table, and its bearing is the arena's bearing in the WORLD -- so a fight
|
||||
-- staged on a turned arena arrived on the table lying across the head that
|
||||
-- was looking at it, while the same fight on an unturned one faced properly.
|
||||
-- Same fight, same composition everywhere else, sideways here.
|
||||
--
|
||||
-- So the turn comes back out. Subtracted, matching the sign the standard
|
||||
-- mount already lands on: its yaw is atan2 of (eye - focus), and rotating
|
||||
-- that pair by +turn takes the bearing to (bearing - turn). One rule, two
|
||||
-- seats.
|
||||
--
|
||||
-- The hand-turn stays on top of it, because that is the player moving the
|
||||
-- model and is theirs to keep.
|
||||
function Diorama.battleYaw(arena)
|
||||
local turn = (arena and arena.turn) or 0
|
||||
if turn == 0 then return Diorama.yaw end
|
||||
local yaw = Diorama.yaw - math.rad(turn)
|
||||
-- kept in (-pi, pi] like the grips leave it, so nothing downstream has to
|
||||
-- care which way round it came
|
||||
return (yaw + math.pi) % (2 * math.pi) - math.pi
|
||||
end
|
||||
|
||||
-- Open a diorama frame. `mode` is VR.mode()'s answer; anything that is
|
||||
-- not a diorama mode closes it.
|
||||
function Diorama.begin(mode)
|
||||
|
||||
+5
-1
@@ -371,7 +371,11 @@ local function renderWorld(views, ctl)
|
||||
end
|
||||
anchor = VRRig.dioramaAnchor(Voxel.angle, Diorama.offset)
|
||||
scale = VRRig.dioramaScale(frame, Voxel.FOCAL) / zoom
|
||||
if Diorama.yaw ~= 0 then mountYaw = Diorama.yaw end
|
||||
-- the hand-turn, and -- while a fight is staged -- the arena's own
|
||||
-- quarter turn taken back out, so a turned arena arrives on the table
|
||||
-- facing the head rather than lying across it (Diorama.battleYaw)
|
||||
local dioYaw = battle and Diorama.battleYaw(battle) or Diorama.yaw
|
||||
if dioYaw ~= 0 then mountYaw = dioYaw end
|
||||
elseif battle then
|
||||
-- the over-the-shoulder seat the flat battle shot stands in, pulled
|
||||
-- close enough for a headset's own lens (see VRRig.battleMount), at
|
||||
|
||||
@@ -5027,6 +5027,68 @@ local eyaw = select(2, VRRig.battleMount({ 150, 10, 200 }, { 100, 10, 200 }))
|
||||
T.check(near(eyaw, math.pi / 2),
|
||||
"a camera east of its focus turns the mapping a quarter toward west")
|
||||
|
||||
-- ------- both VR seats follow the arena's quarter turn
|
||||
--
|
||||
-- `turn` promises the same composition on different ground, and every camera
|
||||
-- has to keep it or the promise is only true on the flat screen. The two VR
|
||||
-- seats keep it in different ways and both are pinned here.
|
||||
--
|
||||
-- THE STANDARD MOUNT keeps it by construction: it is built from the flat
|
||||
-- battle camera's own eye and aim, which turn with the arena, so the seat
|
||||
-- goes round with them. That is asserted rather than assumed, because the
|
||||
-- day someone builds this seat from the arena's cells instead, the picture
|
||||
-- stays plausible and starts pointing the wrong way.
|
||||
--
|
||||
-- THE DIORAMA disc does NOT get it for free: a fight arrives there as a
|
||||
-- pillar of map about the arena's midpoint, whose bearing is its bearing in
|
||||
-- the WORLD, so a turned arena landed on the table lying across the head.
|
||||
-- Diorama.battleYaw takes the turn back out; the hand-turn rides on top.
|
||||
--
|
||||
-- In its own scope: the suite's main chunk is at LuaJIT's local ceiling.
|
||||
;(function()
|
||||
local Arena = run.loader.exports.DRAMATIC_SHAPE.lib.require("BattleArena")
|
||||
local Cam = run.loader.exports.DRAMATIC_SHAPE.lib.require("BattleCam")
|
||||
local Dio = run.loader.exports.DRAMATIC_SHAPE.lib.require("Diorama")
|
||||
|
||||
local function mountYawFor(turn)
|
||||
Cam.reset()
|
||||
local a = Arena.at(6, 6, "wide", turn)
|
||||
local rig = Cam.rig(a, 0, true)
|
||||
return select(2, VRRig.battleMount(rig.eye, rig.focus))
|
||||
end
|
||||
|
||||
local base = mountYawFor(0)
|
||||
for _, turn in ipairs({ 90, 180, 270 }) do
|
||||
-- rotating (eye - focus) by +turn takes atan2(dx, dz) to (bearing - turn)
|
||||
local want = (base - math.rad(turn) + math.pi) % (2 * math.pi) - math.pi
|
||||
local got = (mountYawFor(turn) + math.pi) % (2 * math.pi) - math.pi
|
||||
T.check(near(got, want, 1e-6),
|
||||
("the standard VR mount turns with a %d-degree arena: %.4f, wanted %.4f")
|
||||
:format(turn, got, want))
|
||||
end
|
||||
|
||||
-- the disc: the turn comes back out, so the fight lies the same way on the
|
||||
-- table however the ground under it is laid
|
||||
local hand = Dio.yaw
|
||||
Dio.yaw = 0
|
||||
T.eq(Dio.battleYaw({ turn = 0 }), 0,
|
||||
"an unturned arena leaves the disc where the hands left it")
|
||||
T.check(near(Dio.battleYaw({ turn = 90 }), -math.pi / 2, 1e-9),
|
||||
"a quarter-turned arena counter-turns the disc a quarter")
|
||||
T.check(near(Dio.battleYaw({ turn = 270 }), math.pi / 2, 1e-9),
|
||||
"and three quarters comes round the other way, inside (-pi, pi]")
|
||||
T.eq(Dio.battleYaw(nil), 0, "no arena is no turn, not an error")
|
||||
-- and the same rule the standard mount lands on, so the two agree
|
||||
T.check(near(Dio.battleYaw({ turn = 90 }) - Dio.battleYaw({ turn = 0 }),
|
||||
mountYawFor(90) - mountYawFor(0), 1e-6),
|
||||
"the disc and the standard mount answer a turn by the same amount")
|
||||
-- the player's own hand-turn still rides on top of it
|
||||
Dio.yaw = math.rad(30)
|
||||
T.check(near(Dio.battleYaw({ turn = 90 }), math.rad(30) - math.pi / 2, 1e-9),
|
||||
"the hands keep whatever they did to the model, on top of the turn")
|
||||
Dio.yaw = hand
|
||||
end)()
|
||||
|
||||
-- an eye seated with that yaw really faces the arena: an identity head at
|
||||
-- the seat comes out looking WEST, and the view agrees to the metre
|
||||
local seated = VRRig.eyeCamera(pose, fov, { 150, 10, 200 }, { 0, 0, 0 },
|
||||
|
||||
Reference in New Issue
Block a user