stadium_all_shots.lua
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
-- Driver: one STADIUM screenshot per species, every one of the 151 standing -- on the ENEMY mark in its standby loop. -- -- stadium_shots.lua photographs a handful of deliberately awkward cases; this -- is the exhaustive sweep behind it. What it exists to catch is the class of -- fault that is per-MODEL and invisible to the headless QA pass -- a texture -- that comes out magenta, a mon standing in the floor or floating over it, a -- silhouette that is subtly the wrong shape -- none of which is a number -- stadium_anim_qa.lua can measure. -- -- SHOT_DIR=mods/DramaticShapeVoxelMod/.claude/stadium_verify \ -- POKEPORT_SPEED=4 \ -- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/stadium_all_shots.lua \ -- lovec.exe . -- -- Files land as NNN_species.png in dex order. Run from the PROJECT ROOT. -- -- ------- everything but the foe is nailed down -- -- The whole value of 151 shots is that they are comparable, so the staging is -- identical in every one: same map, same cells, same hour (the day/night -- cycle would otherwise relight every shot), same rung, and the same small -- Pokemon on the player's mark -- DIGLETT, chosen because it is the shortest -- model in the set and so hides the least of the arena behind it. Whatever -- differs between two of these shots is the foe. -- -- ------- the model/sprite verdict is the other half -- -- A species whose pack will not load, or whose animation data the packer -- declined, does not error: StadiumMon.setSpecies answers false and the mode -- quietly stands the Game Boy's flat pic on the tile instead. That is the -- correct behaviour and it is nearly invisible in a screenshot at a glance -- -- which is exactly why it needs counting rather than looking. Stadium.showing -- is asked for every species and the ones that decline are listed at the end. return function(game) local U = dofile("tests/drivers/util.lua") local DIR = os.getenv("SHOT_DIR") or ".claude/stadium_verify" local Pokemon = require("src.pokemon.Pokemon") local BattleState = require("src.battle.BattleState") local exports = game.mods and game.mods.exports local lib = exports and exports.DRAMATIC_SHAPE and exports.DRAMATIC_SHAPE.lib if not lib then U.log("DRAMATIC_SHAPE is not loaded -- enable it and run again") return end local Battles = lib.require("OverworldBattle") local Stadium = lib.require("Stadium") local Pack = lib.require("StadiumPack") local Install = lib.require("StadiumInstall") -- every real species the merged data carries, in dex order local species = {} for id, def in pairs(game.data.pokemon) do if type(id) == "string" and type(def) == "table" and def.dex and def.dex >= 1 and def.dex <= 151 then species[#species + 1] = { id = id, dex = def.dex } end end table.sort(species, function(a, b) return a.dex < b.dex end) game.save.player.name = "RED" -- ONTO THE MAP FIRST: the model build (if one is owed) is pushed from the -- mod's update hook on the first frame the overworld is the top state, so -- waiting for it before teleporting waits forever. U.teleport(game, "ROUTE_1", 5, 8, "down") local waited = 0 while (Install.pending() or Install.status.state == "building") and waited < 5400 do U.wait(10) waited = waited + 10 end if waited > 0 then U.log(("waited %.1fs for the model build -- %s") :format(waited / 60, tostring(Install.status.state))) end Battles.setting:setValue("stadium", game) Battles.backSetting:setValue(false, game) lib.require("DayNight").setting:setValue(os.getenv("DS_TOD") or "day", game) local have = 0 for dex = 1, 151 do if Pack.available(dex) then have = have + 1 end end U.log(("%d species, %d/151 packs on disk, rung = %s") :format(#species, have, tostring(Battles.setting:get()))) -- let the neighbourhood's meshes land, so the first fight opens on the -- real arena rather than the flat fallback U.wait(90) local asPic, stuck, missed = {}, {}, {} for _, s in ipairs(species) do -- the player's side is a constant (see the header), so the only thing -- that changes between two shots is the Pokemon being verified game.save.party = { Pokemon.new(game.data, "DIGLETT", 40) } local battle = BattleState.newWild(game, s.id, 30) battle.onFinish = function() end game.overworld:pushBattle(battle) -- the wipe and the intro chatter, then far enough past it that the -- player's own Pokemon has been sent out -- before that the player's -- side is legitimately the trainer's back sprite and the foe is still -- sliding in U.wait(70) for _ = 1, 40 do if battle.phase == "menu" then break end U.tap(game, "a") U.wait(10) end if battle.phase ~= "menu" then stuck[#stuck + 1] = s.id U.log(("STUCK before menu: %s (phase %s)") :format(s.id, tostring(battle.phase))) end -- and let the send-out beat finish so the mon is standing still in its -- standby loop rather than mid-arrival U.wait(45) -- model or flat pic? asked here, with the fight actually on screen if not Stadium.showing("enemy") then asPic[#asPic + 1] = ("%03d %s"):format(s.dex, s.id) end local path = ("%s/%03d_%s.png"):format(DIR, s.dex, s.id:lower()) if not U.shot(game, path) then missed[#missed + 1] = s.id end while game.stack:top() and game.stack:top() ~= game.overworld do game.stack:pop() end U.wait(10) end U.log(("---- %d shots -> %s"):format(#species - #missed, DIR)) if #asPic > 0 then U.log(("%d species declined to a model and were shot as FLAT PICS: %s") :format(#asPic, table.concat(asPic, ", "))) else U.log("all species stood as MODELS -- none fell back to a flat pic") end if #stuck > 0 then U.log(("%d never reached the battle menu: %s") :format(#stuck, table.concat(stuck, ", "))) end if #missed > 0 then U.log(("%d screenshots did not reach disk: %s") :format(#missed, table.concat(missed, ", "))) end U.log("done -- " .. DIR) end