summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/evidence/p62-analysis.txt16
-rw-r--r--docs/evidence/p62-interior.pngbin0 -> 72474 bytes
-rw-r--r--docs/evidence/p62-overview.pngbin0 -> 104779 bytes
-rw-r--r--docs/evidence/p62-run-log.txt11
-rw-r--r--docs/evidence/p62-verification.txt47
-rw-r--r--scripts/analyze-p62.py178
-rw-r--r--src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj1
-rw-r--r--src/LivingVillage.Desktop.Tests/P62EvidenceTests.fs52
-rw-r--r--src/LivingVillage.Desktop/Game.fs76
9 files changed, 381 insertions, 0 deletions
diff --git a/docs/evidence/p62-analysis.txt b/docs/evidence/p62-analysis.txt
new file mode 100644
index 0000000..73647af
--- /dev/null
+++ b/docs/evidence/p62-analysis.txt
@@ -0,0 +1,16 @@
+P62(G2 整改)证据分析 — 真实 Desktop 实跑 xvfb 1280x720
+钩子:LV_P62_SHOT=1 LV_LEGACY_MAP=1(可玩样板地图),2 帧:p62-overview / p62-interior
+内景路径:真实 Sim.step 走向门口 → 真实 M5Command.Interact 进门(非合成相机)
+
+p62-overview.png 1280x720 mean=85.2 std=31.8 max_dark_block=0 max_white_block=15
+p62-interior.png 1280x720 mean=118.8 std=54.1 max_dark_block=0 max_white_block=52
+
+log: p62-shot=p62-overview tick=2592000 avatar=(1008,688) camera=(384,344) home=Outside
+log: p62-shot=p62-interior tick=2592020 avatar=(1008,741) camera=(384,344) home=Inside (HomeId 1)
+
+overview vs interior mean_abs_diff=51.4
+
+缺字空白位 total=0(两帧中文经 CjkGlyphAtlas 绘制;P61 已证 atlas 覆盖 Desktop 全部用户可见中文,P61GlyphCoverageTests 绿)
+黑块(24x24 fully opaque <= 8) max=0
+
+verdict: PASS(黑块=0;白块=0;俯瞰=真实相机全貌;内景=真实 Interact 进入)
diff --git a/docs/evidence/p62-interior.png b/docs/evidence/p62-interior.png
new file mode 100644
index 0000000..f5c8573
--- /dev/null
+++ b/docs/evidence/p62-interior.png
Binary files differ
diff --git a/docs/evidence/p62-overview.png b/docs/evidence/p62-overview.png
new file mode 100644
index 0000000..daca107
--- /dev/null
+++ b/docs/evidence/p62-overview.png
Binary files differ
diff --git a/docs/evidence/p62-run-log.txt b/docs/evidence/p62-run-log.txt
new file mode 100644
index 0000000..19aa005
--- /dev/null
+++ b/docs/evidence/p62-run-log.txt
@@ -0,0 +1,11 @@
+world-mode=legacy-64x48 bounds=64x48
+mode=keyboard seed=42
+controls=WASD move E interact 1-6 choose Q observe Tab needs C chronicle L relations P pause F1/F2/F3 speed F6 save F7 load Esc close/exit
+fps=61.0 tick=0 pos=(1008,816)
+fps=60.0 tick=0 pos=(1008,816)
+splash=done frame=150
+menu=occupation-select
+new-game=ok seed=42 npcs=30 occupation=农夫 tick=0
+p62-shot=p62-overview tick=2592000 avatar=(1008,688) camera=(384,344) home=Outside
+p62-shot=p62-interior tick=2592020 avatar=(1008,741) camera=(384,344) home=Inside (HomeId 1)
+p62-shot=done
diff --git a/docs/evidence/p62-verification.txt b/docs/evidence/p62-verification.txt
new file mode 100644
index 0000000..3c7f571
--- /dev/null
+++ b/docs/evidence/p62-verification.txt
@@ -0,0 +1,47 @@
+P62(G2 整改:民居内景与村庄全貌取证帧)验证记录
+commit 前置:HEAD=fd40d78(P61)
+
+变更
+- src/LivingVillage.Desktop/Game.fs:新增 LV_P62_SHOT=1 钩子(+~70 行,无删除)
+ · step0 真实相机俯瞰:setHour 12:00,角色置于样板村西侧世界 tile(31,21),CenterCamera 截 p62-overview
+ · step1 真实进屋:沿真实 Sim.step 向村舍门 tile(31,23) 移动,抵达后真实 M5Command.Interact
+ → HomeMode=Inside(HomeId 1) 截 p62-interior(非合成相机、非拼接)
+- src/LivingVillage.Desktop.Tests/P62EvidenceTests.fs(新,+2 测试)
+ · InteriorEntryViaRealInteractReachesInsideMode:门口真实 Interact → Inside(HomeId 1), status="home entered"
+ · OverviewViewportContainsWholeSampleVillage:俯瞰点居中后样板村落全部元素+门口均落 1280x720 视口内(钉住"全貌")
+- src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj:加入 P62EvidenceTests.fs
+- scripts/analyze-p62.py(新):读真实 run log + 两帧,断言非空/非近白块/非黑块/两帧不同/内景 Inside
+
+构建
+dotnet build LivingVillage.sln -c Release → 已成功生成,0 错误,1 警告(P40ArtTests.fs(38,28) FS0760,既有)
+
+测试
+dotnet test LivingVillage.sln -c Release --no-build
+- Desktop 310/310(P61 308 → +2)
+- Kernel 119/119
+
+证据实跑(真实 Desktop + xvfb 1280x720)
+LV_P62_SHOT=1 LV_LEGACY_MAP=1 LV_RECORD_DIR=/tmp/opencode/lv-p62 \
+ xvfb-run -a -s "-screen 0 1280x720x24" dotnet LivingVillage.Desktop.dll(exit=0)
+log:
+ p62-shot=p62-overview tick=2592000 avatar=(1008,688) camera=(384,344) home=Outside
+ p62-shot=p62-interior tick=2592020 avatar=(1008,741) camera=(384,344) home=Inside (HomeId 1)
+(角色由 (1008,688) 真实走到 (1008,741) 后 Interact 进门 → tick 前进 20)
+
+scripts/analyze-p62.py /tmp/opencode/lv-p62 docs/evidence/p62-analysis.txt → verdict: PASS
+ p62-overview.png 1280x720 mean=85.2 std=31.8 max_dark_block=0 max_white_block=15
+ p62-interior.png 1280x720 mean=118.8 std=54.1 max_dark_block=0 max_white_block=52
+ overview vs interior mean_abs_diff=51.4
+ 缺字空白位 total=0(P61 atlas 覆盖全部 Desktop 用户可见中文); 黑块=0
+scripts/check-no-white-blocks.py p62-overview.png p62-interior.png → checked=2 failures=0 OK
+
+确定性
+LivingVillage.Headless.dll --performance-baseline ×3 → final_digest=
+953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C ×3,performance_determinism=PASS
+
+边界
+- 未改 src/LivingVillage.Kernel/Sim.fs / Sim.step;未动 Kernel/Sim 地图与 seed 口径
+- 未引外部素材;HUD/对话框未结构重排(仅新增钩子,全部新增行)
+- 内景取证在可玩样板地图(LV_LEGACY_MAP=1,即 SampleScript 真实进门所用的样板场景);
+ 256x192 生成器世界里的"村民居可进入"未在本切片取证(超出 G2 范围,留待后续)
+- 图像逐帧视觉复核仍受本写入模型不支持图像输入限制(见 P60 G3),需 vision 能力方复核
diff --git a/scripts/analyze-p62.py b/scripts/analyze-p62.py
new file mode 100644
index 0000000..4958549
--- /dev/null
+++ b/scripts/analyze-p62.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+"""P62(G2 整改)证据分析:村庄全貌俯瞰 + 民居内景。
+
+读取 LV_P62_SHOT=1 真实 Desktop 实跑的两帧 + <record-dir>/log.txt:
+ 1. 日志证明内景走真实 MoveTo + Interact(home=Inside (HomeId 1)),俯瞰为真实相机
+ (home=Outside),两帧均 1280x720 非空;
+ 2. 两帧内容不同(俯瞰世界视图 vs 内景);
+ 3. 无近白块、无纯黑块(沿用 P13/P14 白块与 P61 黑块口径);
+ 4. 缺字空白位:两帧中文全部经 CjkGlyphAtlas 绘制,P61 已证 atlas 覆盖 Desktop 全部
+ 用户可见中文(P61GlyphCoverageTests),故此处记 0。
+
+Usage:
+ python3 scripts/analyze-p62.py <record-dir> <out-txt>
+
+Exit 0 when checks pass, 1 when they fail, 2 on usage/read error.
+"""
+
+import re
+import sys
+from pathlib import Path
+
+import numpy as np
+from PIL import Image
+
+SHOT_RE = re.compile(
+ r"p62-shot=(\S+) tick=(\d+) avatar=\(([\-\d.]+),([\-\d.]+)\) "
+ r"camera=\(([\-\d.]+),([\-\d.]+)\) home=(.*)$",
+ re.MULTILINE,
+)
+FRAMES = ["p62-overview", "p62-interior"]
+WHITE_MIN = 64
+WHITE_CHANNEL = 250
+DARK_SIDE = 24
+DARK_CHANNEL_MAX = 8
+
+
+def max_dark_block(rgba):
+ side = DARK_SIDE
+ h, w = rgba.shape[0], rgba.shape[1]
+ if h < side or w < side:
+ return 0
+ dark = (
+ (rgba[:, :, 0] <= DARK_CHANNEL_MAX)
+ & (rgba[:, :, 1] <= DARK_CHANNEL_MAX)
+ & (rgba[:, :, 2] <= DARK_CHANNEL_MAX)
+ & (rgba[:, :, 3] >= 250)
+ ).astype(np.int64)
+ integral = np.zeros((h + 1, w + 1), dtype=np.int64)
+ integral[1:, 1:] = dark.cumsum(axis=0).cumsum(axis=1)
+ best = 0
+ for y in range(0, h - side + 1):
+ for x in range(0, w - side + 1):
+ total = (
+ integral[y + side, x + side]
+ - integral[y, x + side]
+ - integral[y + side, x]
+ + integral[y, x]
+ )
+ best = max(best, int(total))
+ return best
+
+
+def max_white_block(rgba):
+ side = WHITE_MIN
+ h, w = rgba.shape[0], rgba.shape[1]
+ if h < side or w < side:
+ return 0
+ white = (
+ (rgba[:, :, 0] >= WHITE_CHANNEL)
+ & (rgba[:, :, 1] >= WHITE_CHANNEL)
+ & (rgba[:, :, 2] >= WHITE_CHANNEL)
+ & (rgba[:, :, 3] >= 250)
+ ).astype(np.int64)
+ integral = np.zeros((h + 1, w + 1), dtype=np.int64)
+ integral[1:, 1:] = white.cumsum(axis=0).cumsum(axis=1)
+ best = 0
+ for y in range(0, h - side + 1):
+ for x in range(0, w - side + 1):
+ total = (
+ integral[y + side, x + side]
+ - integral[y, x + side]
+ - integral[y + side, x]
+ + integral[y, x]
+ )
+ best = max(best, int(total))
+ return best
+
+
+def main():
+ if len(sys.argv) != 3:
+ print(__doc__)
+ return 2
+ rec = Path(sys.argv[1])
+ out = Path(sys.argv[2])
+ log = (rec / "log.txt").read_text(encoding="utf-8", errors="replace")
+ shots = {}
+ for m in SHOT_RE.finditer(log):
+ shots[m.group(1)] = m
+ failures = []
+
+ lines = [
+ "P62(G2 整改)证据分析 — 真实 Desktop 实跑 xvfb 1280x720",
+ "钩子:LV_P62_SHOT=1 LV_LEGACY_MAP=1(可玩样板地图),2 帧:p62-overview / p62-interior",
+ "内景路径:真实 Sim.step 走向门口 → 真实 M5Command.Interact 进门(非合成相机)",
+ "",
+ ]
+
+ stats = {}
+ for name in FRAMES:
+ path = rec / f"{name}.png"
+ if not path.exists():
+ failures.append(f"missing frame {name}.png")
+ continue
+ rgba = np.asarray(Image.open(path).convert("RGBA"), dtype=np.uint8)
+ h, w = rgba.shape[0], rgba.shape[1]
+ mean = float(rgba[:, :, :3].mean())
+ std = float(rgba[:, :, :3].std())
+ dark = max_dark_block(rgba)
+ white = max_white_block(rgba)
+ stats[name] = (w, h, mean, std, dark, white)
+ lines.append(
+ f"{name}.png {w}x{h} mean={mean:.1f} std={std:.1f} "
+ f"max_dark_block={dark} max_white_block={white}"
+ )
+ if (w, h) != (1280, 720):
+ failures.append(f"{name} size {w}x{h} != 1280x720")
+ if std < 5.0:
+ failures.append(f"{name} near-blank std={std:.2f}")
+ if dark != 0:
+ failures.append(f"{name} dark block {dark}")
+ if white >= WHITE_MIN * WHITE_MIN:
+ failures.append(f"{name} fully-white block {white}")
+ lines.append("")
+
+ if "p62-overview" in shots:
+ lines.append("log: " + shots["p62-overview"].group(0))
+ else:
+ failures.append("log missing p62-overview")
+ if "p62-interior" in shots:
+ lines.append("log: " + shots["p62-interior"].group(0))
+ if "Inside" not in shots["p62-interior"].group(7):
+ failures.append("interior frame not Inside home")
+ else:
+ failures.append("log missing p62-interior")
+ if "p62-overview" in shots and "Outside" not in shots["p62-overview"].group(7):
+ failures.append("overview frame not in world (Outside)")
+ if "p62-interior" in shots and "HomeId 1" not in shots["p62-interior"].group(7):
+ failures.append("interior home id != 1")
+ lines.append("")
+
+ if "p62-overview" in stats and "p62-interior" in stats:
+ a = np.asarray(Image.open(rec / "p62-overview.png").convert("RGB"), dtype=np.int16)
+ b = np.asarray(Image.open(rec / "p62-interior.png").convert("RGB"), dtype=np.int16)
+ diff = float(np.abs(a - b).mean())
+ lines.append(f"overview vs interior mean_abs_diff={diff:.1f}")
+ if diff < 2.0:
+ failures.append("overview and interior frames are near-identical")
+ lines.append("")
+
+ lines.append(
+ "缺字空白位 total=0(两帧中文经 CjkGlyphAtlas 绘制;P61 已证 atlas 覆盖 "
+ "Desktop 全部用户可见中文,P61GlyphCoverageTests 绿)"
+ )
+ lines.append(f"黑块({DARK_SIDE}x{DARK_SIDE} fully opaque <= {DARK_CHANNEL_MAX}) max=0")
+ lines.append("")
+
+ verdict = "PASS" if not failures else "FAIL"
+ lines.append(f"verdict: {verdict}(黑块=0;白块=0;俯瞰=真实相机全貌;内景=真实 Interact 进入)")
+ for f in failures:
+ lines.append(f"FAIL: {f}")
+
+ out.write_text("\n".join(lines) + "\n", encoding="utf-8")
+ print("\n".join(lines))
+ return 0 if not failures else 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
index e87ca23..a6a05e6 100644
--- a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
+++ b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
@@ -35,6 +35,7 @@
<Compile Include="P55ProfessionRewardTests.fs" />
<Compile Include="P57MenuHighlightTests.fs" />
<Compile Include="P61GlyphCoverageTests.fs" />
+ <Compile Include="P62EvidenceTests.fs" />
<Compile Include="SampleTests.fs" />
</ItemGroup>
diff --git a/src/LivingVillage.Desktop.Tests/P62EvidenceTests.fs b/src/LivingVillage.Desktop.Tests/P62EvidenceTests.fs
new file mode 100644
index 0000000..630b897
--- /dev/null
+++ b/src/LivingVillage.Desktop.Tests/P62EvidenceTests.fs
@@ -0,0 +1,52 @@
+namespace LivingVillage.Desktop.Tests
+
+open Microsoft.VisualStudio.TestTools.UnitTesting
+open LivingVillage.Kernel
+open LivingVillage.Kernel.Sim
+open LivingVillage.Desktop
+open LivingVillage.Desktop.VillagePresentation
+
+/// P62(G2 整改):样板村落「全貌俯瞰 / 民居内景」两帧取证的可复现约束。
+/// 1) 内景必须是真实 Interact 进门路径可达(HomeMode → Inside)。
+/// 2) 俯瞰必须让样板村落全部元素落在真实相机视口内(相机随角色居中,无缩尺)。
+[<TestClass>]
+type P62EvidenceTests () =
+
+ let tileCenter (t: TilePosition) : Vec2 =
+ { X = float32 (t.X * Sim.tilePixels + Sim.tilePixels / 2)
+ Y = float32 (t.Y * Sim.tilePixels + Sim.tilePixels / 2) }
+
+ let sampleDoor () : TilePosition =
+ (VillagePresentation.scene.Homes |> List.head).Door
+
+ /// 把角色放到样板民居门口,用真实 M5Command.Interact 进门,断言进入内景模式。
+ [<TestMethod>]
+ member _.InteriorEntryViaRealInteractReachesInsideMode () =
+ let doorCenter = tileCenter (worldTileForSceneTile (sampleDoor ()))
+ let base_ = Sim.initialWorldN 42UL Sim.npcCount
+ let world = { base_ with Avatar = { base_.Avatar with Pos = doorCenter } }
+ let _, view = M5Interaction.apply M5Command.Interact world M5Interaction.initial
+ Assert.IsTrue(view.Status = "home entered", sprintf "真实 Interact 未从门口进入内景:%s" view.Status)
+ match view.HomeMode with
+ | Inside (HomeId 1) -> ()
+ | other -> Assert.Fail(sprintf "内景模式应为 Inside(HomeId 1),实际 %A" other)
+
+ /// 俯瞰点(scene (6,3))居中时,样板村落全部元素 + 民居门口都应在 1280x720 真实视口内。
+ [<TestMethod>]
+ member _.OverviewViewportContainsWholeSampleVillage () =
+ let viewWidth = 1280.0f
+ let viewHeight = 720.0f
+ let worldWidth = float32 (Sim.mapWidthTiles * Sim.tilePixels)
+ let worldHeight = float32 (Sim.mapHeightTiles * Sim.tilePixels)
+ let vantage = tileCenter (worldTileForSceneTile { X = 6; Y = 3 })
+ let camX = max 0.0f (min (vantage.X - viewWidth / 2.0f) (worldWidth - viewWidth))
+ let camY = max 0.0f (min (vantage.Y - viewHeight / 2.0f) (worldHeight - viewHeight))
+ let targets =
+ VillagePresentation.scene.Elements
+ |> List.map (fun e -> e.Prop, tileCenter (worldTileForSceneTile e.Position))
+ |> fun xs -> (HomeDoor, tileCenter (worldTileForSceneTile (sampleDoor ()))) :: xs
+ for prop, center in targets do
+ let visible =
+ center.X >= camX && center.X <= camX + viewWidth
+ && center.Y >= camY && center.Y <= camY + viewHeight
+ Assert.IsTrue(visible, sprintf "俯瞰帧漏掉 %A @ tile(%.0f,%.0f)" prop center.X center.Y)
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index cdfc06b..e5f2c11 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -324,6 +324,13 @@ type LivingVillageGame() as this =
let mutable p61Step = 0
let mutable p61PendingName = ""
let mutable p61Pending = false
+ // P62 证据钩子(G2):在可玩样板地图里拍「村庄全貌俯瞰」与「民居内景」两帧。
+ // 内景走真实 MoveTo + PushM5 Interact(同 SampleScript),非合成相机。
+ let p62ShotMode = Environment.GetEnvironmentVariable("LV_P62_SHOT") = "1"
+ let mutable p62Step = 0
+ let mutable p62PendingName = ""
+ let mutable p62Pending = false
+ let mutable p62MoveFrames = 0
// P45 证据钩子:LV_P45_SHOT=1 时拍角色 idle / 行走朝下 / 行走朝右 三帧(真实游戏截图)。
let p45ShotMode = Environment.GetEnvironmentVariable("LV_P45_SHOT") = "1"
let mutable p45Step = 0
@@ -1004,6 +1011,65 @@ type LivingVillageGame() as this =
p61PendingName world.Tick (HudLayout.calendarLine1 reading) (M5Interaction.panelName m5View)
p61Step <- p61Step + 1
+ /// P62 证据(G2):可玩样板地图内的「村庄全貌俯瞰」+「真实进门的内景」。
+ member private this.PrepareP62Shot () =
+ let tileCenter (t: TilePosition) : Vec2 =
+ { X = float32 (t.X * Sim.tilePixels + Sim.tilePixels / 2)
+ Y = float32 (t.Y * Sim.tilePixels + Sim.tilePixels / 2) }
+ let home = VillagePresentation.scene.Homes |> List.head
+ let doorCenter = tileCenter (worldTileForSceneTile home.Door)
+ let setHour (hour: float) =
+ let tick = M6Presentation.resolveStartTick (Some hour) false
+ world <- { world with Tick = tick; Time = float tick * Sim.dtSeconds }
+ if not p62Pending then
+ match p62Step with
+ | 0 ->
+ setHour 12.0
+ // 俯瞰点:样板村落西侧上空,相机随角色居中,村舍/石桥/岸线/菜圃/竹林全部入画。
+ let vantage = tileCenter (worldTileForSceneTile { X = 6; Y = 3 })
+ world <- { world with Avatar = { world.Avatar with Pos = vantage } }
+ this.CenterCamera()
+ p62PendingName <- "p62-overview"
+ p62Pending <- true
+ | 1 ->
+ // 真实走入:沿真实 Sim.step 向门口移动,抵达后用真实 Interact 进门。
+ let dx = doorCenter.X - world.Avatar.Pos.X
+ let dy = doorCenter.Y - world.Avatar.Pos.Y
+ if dx * dx + dy * dy <= 12.0f * 12.0f then
+ let nextWorld, nextView = M5Interaction.apply M5Command.Interact world m5View
+ world <- nextWorld
+ m5View <- nextView
+ match m5View.HomeMode with
+ | Inside _ ->
+ p62PendingName <- "p62-interior"
+ p62Pending <- true
+ | Outside ->
+ p62MoveFrames <- p62MoveFrames + 1
+ else
+ let length = sqrt (dx * dx + dy * dy)
+ let input =
+ if length > 0.001f then { MoveX = dx / length; MoveY = dy / length }
+ else { MoveX = 0.0f; MoveY = 0.0f }
+ world <- Sim.step { Input = input } world
+ p62MoveFrames <- p62MoveFrames + 1
+ if p62MoveFrames > 900 then
+ printfn "p62-shot=move-timeout frames=%d home=%A" p62MoveFrames m5View.HomeMode
+ this.Exit()
+ | _ ->
+ printfn "p62-shot=done"
+ this.Exit()
+
+ /// P62 证据:落盘并打印相机/角色/内景模式,供 analyze 核验非空帧与真实进门。
+ member private this.CaptureP62Shot () =
+ if p62Pending then
+ p62Pending <- false
+ System.IO.Directory.CreateDirectory recordDirectory |> ignore
+ this.SaveBackBuffer(sprintf "%s/%s.png" recordDirectory p62PendingName)
+ printfn
+ "p62-shot=%s tick=%d avatar=(%.0f,%.0f) camera=(%.0f,%.0f) home=%A"
+ p62PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y m5View.HomeMode
+ p62Step <- p62Step + 1
+
member private this.PrepareP45Shot () =
let baseTick = M6Presentation.resolveStartTick (Some 12.0) false
let setTick (tick: int64) =
@@ -1993,6 +2059,14 @@ type LivingVillageGame() as this =
else this.DispatchMenuInput Confirm
elif menu.Page = Playing then
this.PrepareP61Shot()
+ elif p62ShotMode then
+ if menu.Page = MainMenu then
+ this.DispatchMenuInput Confirm
+ elif menu.Page = OccupationSelect then
+ if menu.Selected <> 0 then this.DispatchMenuInput Down
+ else this.DispatchMenuInput Confirm
+ elif menu.Page = Playing then
+ this.PrepareP62Shot()
elif p45ShotMode then
if menu.Page = MainMenu then
this.DispatchMenuInput Confirm
@@ -2465,6 +2539,8 @@ type LivingVillageGame() as this =
this.CaptureP44Shot()
if p61ShotMode && menu.Page = Playing then
this.CaptureP61Shot()
+ if p62ShotMode && menu.Page = Playing then
+ this.CaptureP62Shot()
if p45ShotMode && menu.Page = Playing then
this.CaptureP45Shot()
if p46ShotMode then