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.fs104
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