When I started this project I came up with an overly complicated plan for an open world system using chunks.

/// <summary> /// A 2d Chunk of tiles. Used in the overworld and in dungeons. /// </summary> /// <param name="area">edge-inclusive area of tiles</param> /// <param name="tiles">2d array [row,column]</param> type Chunk = { Area : Morgemil.Math.Rectangle Tiles : TileDefinition array } /// <summary> /// Local coordinates. Zero-based indices: [y * area.Width + x] /// No check for valid coordinates /// </summary> member this.TileLocal(vec2 : Morgemil.Math.Vector2i) = this.Tiles.[vec2.Y * this.Area.Width + vec2.X] /// <summary> /// Global coordinates. Zero-based indices relative to this.Area.Position ///</summary> member this.Tile(vec2 : Morgemil.Math.Vector2i) = this.Tiles.[this.Area.FlatCoord(vec2)] /// <summary> /// True if every tile is TileDefinition.Default /// </summary> member this.IsEmpty = this.Tiles |> Array.forall (TileDefinition.IsDefault) ///Seq<Math.Vector2i * TileDefinition> member this.TileCoordinates = Seq.map (fun coord -> coord, (this.Tile coord)) this.Area.Coordinates |

As it turns out, generating chunks in a continuous world is not the difficult part. The difficult part is keeping a continuous world. What happens when buildings are spread across multiple chunks? When pathfinding needs to go across unloaded chunks? When loaded entities are no longer in loaded chunks? How to continuously write/read chunks from files?

So I’ve replaced every instance where I’m operating on a collection of chunks, with an operation on a single level. The level structure has simplified from the chunk since there is now no difference between local coordinates and global coordinates.

/// <summary> /// A 2d Level. /// </summary> /// <param name="area">edge-inclusive area of tiles</param> /// <param name="tiles">2d array [row,column]</param> type Level = { ///[0,0] (MaxX,MaxY) Area : Morgemil.Math.Rectangle Tiles : TileDefinition array } /// <summary> /// Global coordinates. Zero-based indices relative to this.Area.Position ///</summary> member this.Tile(vec2 : Morgemil.Math.Vector2i) = this.Tiles.[this.Area.FlatCoord(vec2)] ///Seq<Math.Vector2i * TileDefinition> member this.TileCoordinates = Seq.map (fun coord -> coord, (this.Tile coord)) this.Area.Coordinates |

As you might imagine, this also signifies a change in direction. From an open-world system towards a more traditional dungeon crawler.