diff options
Diffstat (limited to 'src/LivingVillage.Desktop/Game.fs')
| -rw-r--r-- | src/LivingVillage.Desktop/Game.fs | 104 |
1 files changed, 86 insertions, 18 deletions
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs index 2e28760..62fccb9 100644 --- a/src/LivingVillage.Desktop/Game.fs +++ b/src/LivingVillage.Desktop/Game.fs @@ -79,6 +79,13 @@ type LivingVillageGame() as this = let autoplay = Environment.GetEnvironmentVariable("LV_AUTOPLAY") = "1" let titleSuffix = if autoplay then " [AUTOPLAY]" else "" let modeName = if autoplay then "autoplay" else "keyboard" + let stepsPerFrame = + match Environment.GetEnvironmentVariable("LV_STEPS_PER_FRAME") with + | null | "" -> 1 + | s -> + match Int32.TryParse s with + | true, n when n > 0 -> n + | _ -> 1 let graphics = new GraphicsDeviceManager(this) let mutable spriteBatch = Unchecked.defaultof<SpriteBatch> let mutable atlas = Unchecked.defaultof<Texture2D> @@ -88,6 +95,9 @@ type LivingVillageGame() as this = let mutable camera = Vector2.Zero let mutable fpsFrames = 0 let mutable fpsSeconds = 0.0 + let mutable prevKb = Unchecked.defaultof<KeyboardState> + let mutable relationView = false + let mutable relationSnapshot: float32[,] option = None do graphics.PreferredBackBufferWidth <- 1280 @@ -108,6 +118,10 @@ type LivingVillageGame() as this = let y = MathHelper.Clamp(world.Avatar.Pos.Y + half - vh / 2.0f, 0.0f, max 0.0f (worldH - vh)) camera <- Vector2(x, y) + override this.Initialize() = + base.Initialize() + prevKb <- Keyboard.GetState() + override _.LoadContent() = spriteBatch <- new SpriteBatch(this.GraphicsDevice) atlas <- Atlas.build this.GraphicsDevice @@ -117,23 +131,32 @@ type LivingVillageGame() as this = override this.Update(gameTime: GameTime) = let kb = Keyboard.GetState() if kb.IsKeyDown(Keys.Escape) then this.Exit() - let input = - if autoplay then - let t = float32 world.Tick - { MoveX = MathF.Sin(t / 60.0f) * 0.8f - MoveY = MathF.Cos(t / 90.0f) * 0.6f } - else - { MoveX = - (if kb.IsKeyDown(Keys.D) then 1.0f - elif kb.IsKeyDown(Keys.A) then -1.0f - else 0.0f) - MoveY = - (if kb.IsKeyDown(Keys.S) then 1.0f - elif kb.IsKeyDown(Keys.W) then -1.0f - else 0.0f) } - let ts = { Input = input } - world <- Sim.step ts world - this.CenterCamera() + let lPressed = kb.IsKeyDown(Keys.L) && not (prevKb.IsKeyDown(Keys.L)) + prevKb <- kb + if lPressed then + relationView <- not relationView + if relationView then relationSnapshot <- Some(Sim.relationMatrix world) + let state = if relationView then "on" else "off" + printfn $"relation view={state} tick={world.Tick}" + if not relationView then + let input = + if autoplay then + let t = float32 world.Tick + { MoveX = MathF.Sin(t / 60.0f) * 0.8f + MoveY = MathF.Cos(t / 90.0f) * 0.6f } + else + { MoveX = + (if kb.IsKeyDown(Keys.D) then 1.0f + elif kb.IsKeyDown(Keys.A) then -1.0f + else 0.0f) + MoveY = + (if kb.IsKeyDown(Keys.S) then 1.0f + elif kb.IsKeyDown(Keys.W) then -1.0f + else 0.0f) } + let ts = { Input = input } + for _ in 1 .. stepsPerFrame do + world <- Sim.step ts world + this.CenterCamera() fpsFrames <- fpsFrames + 1 fpsSeconds <- fpsSeconds + gameTime.ElapsedGameTime.TotalSeconds if fpsSeconds >= 1.0 then @@ -144,10 +167,55 @@ type LivingVillageGame() as this = if world.Npcs.Length > 0 then NpcView.actionName world.Npcs.[0].Mind.Action else "none" - this.Window.Title <- $"Living Village M3a{titleSuffix} | fps {fps:F1} | tick {world.Tick} | npc {npcAction}" + let viewTag = if relationView then " | [RELATIONS]" else "" + this.Window.Title <- $"Living Village M3b{titleSuffix} | fps {fps:F1} | tick {world.Tick} | npc {npcAction}{viewTag}" printfn $"fps={fps:F1} tick={world.Tick} pos=({world.Avatar.Pos.X:F0},{world.Avatar.Pos.Y:F0})" + member private this.DrawLine (a: Vector2) (b: Vector2) (thickness: float32) (color: Color) = + let d = b - a + let len = d.Length() + if len > 1.0f then + let angle = MathF.Atan2(d.Y, d.X) + spriteBatch.Draw(pixel, a, System.Nullable(), color, angle, Vector2.Zero, Vector2(len, thickness), SpriteEffects.None, 0.0f) + + member private this.DrawRelationView() = + this.GraphicsDevice.Clear(Color(16, 20, 26)) + let viewport = this.GraphicsDevice.Viewport + let vw = float32 viewport.Width + let vh = float32 viewport.Height + let mapW = float32 (Sim.mapWidthTiles * Sim.tilePixels) + let mapH = float32 (Sim.mapHeightTiles * Sim.tilePixels) + let margin = 48.0f + let scale = min ((vw - 2.0f * margin) / mapW) ((vh - 2.0f * margin) / mapH) + let ox = (vw - mapW * scale) / 2.0f + let oy = (vh - mapH * scale) / 2.0f + let toScreen (p: Vec2) : Vector2 = Vector2(ox + p.X * scale, oy + p.Y * scale) + spriteBatch.Begin() + match relationSnapshot with + | Some m -> + let n = Array2D.length1 m + for i in 0 .. n - 1 do + for j in i + 1 .. n - 1 do + let r = m.[i, j] + if abs r > Sim.relationThreshold then + let a = toScreen world.Npcs.[i].Pos + let b = toScreen world.Npcs.[j].Pos + let color = if r > 0.0f then Color.LimeGreen else Color.IndianRed + let thickness = Sim.clamp (1.5f + 3.0f * abs r) 1.5f 12.0f + this.DrawLine a b thickness color + for npc in world.Npcs do + let p = toScreen npc.Pos + spriteBatch.Draw(pixel, Rectangle(int p.X - 5, int p.Y - 5, 10, 10), Color.Cyan) + | None -> () + spriteBatch.End() + override this.Draw(gameTime: GameTime) = + if relationView then + this.DrawRelationView() + else + this.DrawWorldView() + + member private this.DrawWorldView() = this.GraphicsDevice.Clear(Color.CornflowerBlue) let viewport = this.GraphicsDevice.Viewport let camX = int camera.X |
