summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/Game.fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/LivingVillage.Desktop/Game.fs')
-rw-r--r--src/LivingVillage.Desktop/Game.fs77
1 files changed, 64 insertions, 13 deletions
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index 6252117..3d04ca7 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -388,6 +388,10 @@ type LivingVillageGame() as this =
let mutable p47Step = 0
let mutable p47PendingName = ""
let mutable p47Pending = false
+ // P73 证据钩子:LV_P73_SHOT=1 时在同一生成器世界的桥头光源处,按
+ // 6/12/18/22 时各拍一帧(同机位),四张真实帧用于四时段光照档差异核验。
+ let p73ShotMode = Environment.GetEnvironmentVariable("LV_P73_SHOT") = "1"
+ let mutable p73Step = 0
// P52 证据钩子:LV_P52_SHOT=1 时选职业开局 → 走到 NPC 旁用真实 Interact 开对话 →
// 真实 BuyFromTarget / SellToTarget 各一次,拍「对话入口 / 买入后 / 卖出后」三帧;
// 每笔交易打印钱与背包数量前后值,供 analyze 脚本算 delta。
@@ -1474,7 +1478,39 @@ type LivingVillageGame() as this =
let placeCameraOn (tileX: int) (tileY: int) =
world <- { world with Avatar = { world.Avatar with Pos = { X = float32 (tileX * Sim.tilePixels); Y = float32 (tileY * Sim.tilePixels) } } }
this.CenterCamera()
- if not p47Pending then
+ if p73ShotMode then
+ // 同一桥头光源机位的四时段巡拍;tick 只经 setHour 改变,保证画面差异只来自光照档。
+ let hours = [ ("p73-morning", 6.0); ("p73-noon", 12.0); ("p73-dusk", 18.0); ("p73-night", 22.0) ]
+ if p73Step < hours.Length then
+ match riverscapeMap with
+ | Some map ->
+ let pick =
+ SceneDetail.lightSources map
+ |> List.sortByDescending (fun (x, y, _) -> SceneDetail.reflectionLength map x y)
+ |> List.tryHead
+ let spot = match pick with Some (x, y, _) -> x, y | None -> map.Core.MinX, map.Core.MinY
+ let name, hour = hours.[p73Step]
+ setHour hour
+ placeCameraOn (fst spot) (snd spot)
+ p47PendingName <- name
+ let tint = (M6Presentation.profileAtTick world.Tick).WorldTint
+ printfn
+ "p73-shot=%s hour=%.1f tick=%d period=%s blend=%.3f lantern=%.2f tint=%d,%d,%d veil=%d"
+ name hour world.Tick
+ (M6Presentation.lightingPeriodName (M6Presentation.periodAtTick world.Tick))
+ (M6Presentation.lightingBlend world.Tick)
+ (M6Presentation.effectiveLanternGlow world.Tick)
+ (int tint.R) (int tint.G) (int tint.B)
+ (M6Presentation.periodLightingAtTick world.Tick).VeilAlpha
+ p47Pending <- true
+ | None ->
+ printfn "p73-shot=error no-riverscape-map"
+ this.Exit()
+ else
+ printfn "p73-shot=done"
+ this.Exit()
+ ()
+ elif not p47Pending then
match riverscapeMap with
| None -> this.Exit()
| Some map ->
@@ -1582,9 +1618,14 @@ type LivingVillageGame() as this =
p47Pending <- false
System.IO.Directory.CreateDirectory recordDirectory |> ignore
this.SaveBackBuffer(sprintf "%s/%s.png" recordDirectory p47PendingName)
- printfn "p47-shot=%s tick=%d pos=(%.0f,%.0f) camera=(%.0f,%.0f)"
- p47PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y
- p47Step <- p47Step + 1
+ if p73ShotMode then
+ printfn "p73-frame=%s tick=%d pos=(%.0f,%.0f) camera=(%.0f,%.0f)"
+ p47PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y
+ p73Step <- p73Step + 1
+ else
+ printfn "p47-shot=%s tick=%d pos=(%.0f,%.0f) camera=(%.0f,%.0f)"
+ p47PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y
+ p47Step <- p47Step + 1
/// P52 证据:选职业开局 → 真实 Interact 开对话 → 真实 BuyFromTarget / SellToTarget 各一次。
member private this.PrepareP52Shot () =
@@ -2386,7 +2427,7 @@ type LivingVillageGame() as this =
elif p57ShotMode then
if menu.Page = MainMenu then
this.PrepareP57Shot()
- elif p47ShotMode then
+ elif p47ShotMode || p73ShotMode then
if menu.Page = MainMenu then
this.DispatchMenuInput Confirm
elif menu.Page = OccupationSelect then
@@ -2864,7 +2905,7 @@ type LivingVillageGame() as this =
this.CaptureP46Shot()
if p57ShotMode then
this.CaptureP57Shot()
- if p47ShotMode && menu.Page = Playing then
+ if (p47ShotMode || p73ShotMode) && menu.Page = Playing then
this.CaptureP47Shot()
if p52ShotMode && menu.Page = Playing then
this.CaptureP52Shot()
@@ -3126,6 +3167,10 @@ type LivingVillageGame() as this =
member private this.DrawWorldView() =
let profile = M6Presentation.profileAtTick world.Tick
+ // P73:时段档并入世界色调与灯光强度;正午档为恒等,不影响既有正午观感。
+ let period = M6Presentation.periodLightingAtTick world.Tick
+ let worldTint = M6Presentation.multiplyTint profile.WorldTint period.AmbientTint
+ let lanternGlow = M6Presentation.effectiveLanternGlow world.Tick
let viewport = this.GraphicsDevice.Viewport
this.GraphicsDevice.Clear(profile.Background)
spriteBatch.Begin()
@@ -3145,14 +3190,14 @@ type LivingVillageGame() as this =
renderPlan
riverscapeMap
world.Tick
- profile.WorldTint
+ worldTint
// Warm lantern / window glows strengthen as night falls (pure function of tick phase).
// Concentric premultiplied circles follow a quadratic radial falloff (radius 3-4 tiles),
// so the core stays a soft warm disc instead of saturating to a white block and no glow
// is painted where there is no light entity. 生成器世界用桥头灯笼 + 民居门窗。
- if profile.LanternGlow > 0.3f then
- let glow = profile.LanternGlow
+ if lanternGlow > 0.3f then
+ let glow = lanternGlow
let peakAlpha = int (140.0f * glow)
let tileX x = x * Sim.tilePixels - int camera.X
let tileY y = y * Sim.tilePixels - int camera.Y
@@ -3180,9 +3225,9 @@ type LivingVillageGame() as this =
spriteBatch.Draw(pixel, rect, color)
| _ -> ()
- match riverscapeMap, profile.LanternGlow > 0.3f with
+ match riverscapeMap, lanternGlow > 0.3f with
| Some map, true ->
- VillageArt.drawCc0BoatOverlay spriteBatch artTextures camera map profile.WorldTint
+ VillageArt.drawCc0BoatOverlay spriteBatch artTextures camera map worldTint
| _ -> ()
// P47 场景细节:河道倒影 + 岸边芦苇加密度 + 漂浮元素 + 近景楼层层错落。
@@ -3202,12 +3247,12 @@ type LivingVillageGame() as this =
VillageArt.drawGroundShadow spriteBatch pixel camera world.Avatar.Pos
let avatarSpec = VillageArt.avatarSpriteSpec avatarFacing avatarMoving world.Tick
- VillageArt.drawCharacter spriteBatch artTextures camera world.Avatar.Pos avatarSpec profile.WorldTint
+ VillageArt.drawCharacter spriteBatch artTextures camera world.Avatar.Pos avatarSpec worldTint
for npc in world.Npcs do
VillageArt.drawGroundShadow spriteBatch pixel camera npc.Pos
let spec = VillageArt.npcSpriteSpecAtTarget npc.Id npc.Pos npc.Mind.Target world.Tick
- VillageArt.drawCharacter spriteBatch artTextures camera npc.Pos spec profile.WorldTint
+ VillageArt.drawCharacter spriteBatch artTextures camera npc.Pos spec worldTint
// Golden-hour wash: the multiplicative WorldTint can only darken, so a warm
// premultiplied veil is layered over the whole scene. Because rgb is premultiplied
@@ -3224,6 +3269,12 @@ type LivingVillageGame() as this =
if moonAlpha > 0 then
let mr, mg, mb = M6Presentation.moonlightRgb
spriteBatch.Draw(pixel, Rectangle(0, 0, viewport.Width, viewport.Height), VillageArt.premultiply mr mg mb moonAlpha)
+
+ // P73:时段档加性覆盖色,最后叠上,保证 6/12/18/22 四时段色调肉眼可辨;
+ // 正午 alpha=0 故为恒等,清晨偏冷、黄昏暖橙、夜晚冷蓝。
+ if period.VeilAlpha > 0 then
+ let v = period.Veil
+ spriteBatch.Draw(pixel, Rectangle(0, 0, viewport.Width, viewport.Height), VillageArt.premultiply (int v.R) (int v.G) (int v.B) period.VeilAlpha)
spriteBatch.End()
// Contextual floating prompt over the unique target; same CJK atlas font path as the HUD.