# Map Chunk Loader Renders regions of the world straight from the cache: minimap images, terrain height maps and collision maps. A region is 64 by 64 tiles drawn at `SCALE` pixels each. Rendering one means blending its ground colours - which reaches into the neighbouring regions, so those are loaded too - drawing the walls, doors and map scene symbols on top, and then compositing the planes each tile is visible on. Finished planes and tile classifications are cached per region, so panning back over ground already drawn is cheap. Example: ```pascal CacheReader := TCacheReader.Create(); CacheReader.Indices[ECacheIndex.MAPS].Cache(); chunkLoader.MapData.Load(); chunkLoader.GetChunksImage([48, 48, 52, 52], 0).Show(); ``` - - - ## TileShapes.Setup ```pascal procedure TTileShapes.Setup(); ``` Applies every rotation to every tile shape up front, so that drawing an overlaid tile reads one value per pixel instead of a rotation and then a shape. `Masks[shape * 4 + rotation]` is the 16 pixel mask of that combination: `1` where the overlay is drawn and `0` where the underlay shows through. There are only 13 shapes and 4 rotations, so the whole table is 52 masks. Called once at startup by `cache.simba`. - - - ## MapChunkLoader.EnsurePlaneCache ```pascal procedure TMapChunkLoader.EnsurePlaneCache(regionIndex: Integer); ``` Makes room in the render caches for a region that has just been loaded. Nothing is rendered here - the slots start empty and are filled the first time each plane of that region is asked for. Regions are never evicted, so a chunk is only ever built once. - - - ## MapChunkLoader.EnsureColorsCurrent ```pascal procedure TMapChunkLoader.EnsureColorsCurrent(); ``` Throws away everything that was coloured through the old palette, if {ref}`TColorUtils.SetBrightness` has been called since it was built. `ColorUtils` cannot reach in and do this itself - it is defined long before anything that draws, and there may be more than one chunk loader - so it counts its changes in `Revision` and this compares against the revision these caches were filled at. What goes is the rendered planes and the overlay colours, both of which are palette colours, and the tile classifications with them. The classifications do not depend on the palette, but {ref}`TMapChunkLoader.GetChunkImage` only rebuilds a missing plane while it is classifying the tiles that ask for it - so dropping planes and keeping classifications would leave it compositing planes that are no longer there. They are one pass over 4096 tiles to rebuild, against the plane renders being thrown away regardless. The icon lists and the loaded regions themselves are untouched: neither depends on the palette, and they are the expensive half. - - - ## MapChunkLoader.ConvertLocal ```pascal function TMapChunkLoader.ConvertLocal(d: Integer): Integer; static; ``` Wraps a coordinate that may have run off the edge of a region back into `0` to `63`. Blending reads a few tiles past the region it is drawing, into whichever neighbour is there, and this is what turns the out of range coordinate into an index into that neighbour. - - - ## MapChunkLoader pixel buffers Every `pixels` matrix built here is row-major, indexed `[y, x]`, matching how Simba indexes a {ref}`TIntegerMatrix`. `x` and `y` below are tile coordinates, so the square each tile occupies is `SCALE` pixels wide and tall. The minimap's matrices hold `ColorUtils.Palette` entries and the collision matrices the `TColorUtils.COLLISION_` colours, both alpha, red, green, blue from the top byte down, so both reach an image through `TImage.FromData` without being converted. The minimap composites its planes into one flat buffer first; the collision image is copied out of its matrix a row at a time. The square goes through `TIntegerMatrix.Fill` rather than a loop over its `SCALE * SCALE` pixels. That looks like the long way round for 16 pixels, but `Fill` is native while a loop here is not, and measuring the two puts the native call ahead by about a factor of two - which matters, since this runs for most of the 4096 tiles of every plane of every region. - - - ## MapChunkLoader.BlitMapDecoration ```pascal procedure TMapChunkLoader.BlitMapDecoration(var pixels: TIntegerMatrix; x, y: Integer; constref obj: TObjectDefinition); ``` Draws an object's map scene sprite - the staircase, altar or furnace symbol the minimap uses in place of the object itself. The sprite is centred on the object's footprint and clipped to the chunk. Its pixels come from {ref}`TMapDataLoader.PrepareForRendering`, which has already swapped them into the buffer's channel order and flattened a transparent pixel to `0`, so this is a copy that skips zeroes. - - - ## MapChunkLoader.DrawObjectAtTile ```pascal procedure TMapChunkLoader.DrawObjectAtTile(var pixels: TIntegerMatrix; constref loc: TWorldLocation; x, y: Integer); ``` Draws one object onto a minimap plane, if it is the kind of object the minimap shows at all - walls, doors and the objects that carry a map scene symbol. Walls are drawn as a line along whichever tile edge their orientation points at, corner walls along the two edges they occupy, corner posts as a single pixel, and diagonal walls across the tile. `y` is flipped on the way in because the map is drawn with north at the top while the world counts y upwards. - - - ## MapChunkLoader.DrawObjectsOnPlane ```pascal procedure TMapChunkLoader.DrawObjectsOnPlane(var pixels: TIntegerMatrix; constref region: TRegion; z: Integer); ``` Draws every object of a region that sits on plane `z`. - - - ## MapChunkLoader.DrawMapIcons ```pascal procedure TMapChunkLoader.DrawMapIcons(img: TImage; regionIdx, z, offsetX, offsetY: Integer); ``` Draws the area icons of the region at `regionIdx` in `RegionLoader.Regions` on top of an already rendered map, offset by `offsetX, offsetY` within `img`. The region's render caches have to exist already, which {ref}`TMapChunkLoader.GetChunkImage` sees to. Icons go on last, and onto the whole image rather than a single chunk, so that one sitting on a chunk boundary is not cut in half - which is why {ref}`TMapChunkLoader.GetChunksImage` renders its chunks with icons turned off and then comes back for them. Only a handful of a region's several thousand locations carry an icon at all, so which ones they are is worked out once per region and plane and kept - packed one integer per icon, tile x and y in the low twelve bits and the area's index above them. Panning back over a region then costs the drawing alone rather than another walk of every location on it. - - - ## MapChunkLoader.BuildRegionPlane ```pascal function TMapChunkLoader.BuildRegionPlane(constref region: TRegion; z: Integer): TIntegerMatrix; ``` Renders one plane of one region into a pixel matrix: ground colours first, then the objects the minimap draws on top of them. Underlay colours are blended over a `BLEND` tile radius so that terrain types fade into each other instead of meeting at a hard edge. That blend is a running sum: rather than re-adding a whole neighbourhood per tile, each step adds the row entering the window and subtracts the one leaving it. Where the window runs past the edge of the region it continues into the neighbouring one, which is why {ref}`TRegionLoader.LoadRegionAndNeighbors` loads four neighbours as well. Overlays are drawn over the result in one of 13 shapes at one of 4 rotations - a full tile, a half, a corner, and so on - which is how the map gets diagonal shorelines and path edges out of square tiles. The result is cached by {ref}`TMapChunkLoader.GetChunkImage`, so this only runs once per region and plane. - - - ## MapChunkLoader.CompositePlanes ```pascal procedure TMapChunkLoader.CompositePlanes(var pixels: TIntegerArray; constref planes: array of TIntegerMatrix; constref tileCodes: TIntegerArray); static; ``` Flattens the planes a chunk uses into `pixels`, following the per-tile plan in `tileCodes`. `pixels` is one row-major `IMAGE_SIZE * IMAGE_SIZE` buffer rather than a matrix, so it is a single block of memory that `TImage.FromData` can read as it is. It copies runs of tiles that share a plan in one go, because aliasing a row out of a plane costs far more than the handful of pixel copies it saves. The runs are found once per tile row and reused for each of the `SCALE` pixel rows that tile row covers, since those rows all share it. Within a run the topmost plane is copied over with a single `Move` and the ones below it only fill in whatever the topmost left clear. - - - ## MapChunkLoader.GetChunkImage ```pascal function TMapChunkLoader.GetChunkImage(regionX, regionY, z: Integer; drawIcons: Boolean = True): TImage; ``` Renders one region as a `IMAGE_SIZE` by `IMAGE_SIZE` minimap image. Anywhere nothing is drawn comes out opaque black. Each tile picks up to 3 planes to composite, topmost first. Those are packed into a single integer per tile - the low nibble is how many planes the tile uses and each nibble above it holds one plane index - so classifying every tile costs one `TIntegerArray` rather than one array per plane slot. - - - ## HeightMap.ToImage ```pascal function THeightMap.ToImage(): TImage; ``` The height map as a greyscale image, black at ground level and white at the highest terrain the game uses. The map is indexed `[x, y]`, the way {ref}`TMapChunkLoader.GetChunksHeightMap` builds it, so the image is as wide as the outer array is long and as tall as each column. Each height's grey is read out of `ColorUtils.HeightColors` rather than worked out again, and the pixels are laid out into one buffer and handed over in a single call, rather than written one at a time through `TImage.Pixel` - a property write per pixel is most of what building the image would otherwise cost. - - - ## MapChunkLoader.GetChunkHeightMap ```pascal function TMapChunkLoader.GetChunkHeightMap(regionX, regionY, z: Integer): THeightMap; ``` The terrain height of every tile of one region on plane `z`, one entry per tile rather than per pixel. A bridge tile reports the height of the surface walked on rather than the one underneath it: like the client, when plane 1 flags a tile as a bridge, every plane below 3 reads the height of the plane above it there. - - - ## MapChunkLoader.GetChunksImage ```pascal function TMapChunkLoader.GetChunksImage(bounds: TBox; z: Integer): TImage; ``` Renders a rectangle of regions into one image, in region coordinates. The chunks are drawn without their icons and the icons are added afterwards over the finished image, so an icon straddling a chunk boundary stays whole. Example: ```pascal img := chunkLoader.GetChunksImage([48, 48, 52, 52], 0); img.Show(); ``` - - - ## MapChunkLoader.GetChunksHeightMap ```pascal function TMapChunkLoader.GetChunksHeightMap(bounds: TBox; z: Integer): THeightMap; ``` {ref}`TMapChunkLoader.GetChunkHeightMap` across a rectangle of regions, stitched into one map laid out the way the image is - north at the top. - - - ## CollisionMap.ToImage ```pascal function TCollisionMap.ToImage(): TImage; ``` The collision map as an image: white where a tile can be walked on, black where it is blocked, grey where an object stands on it. The map is indexed `[y, x]`, the way {ref}`TMapChunkLoader.GetChunksCollisionMap` builds it. The pixels are laid out into one buffer and handed over in a single call, rather than written one at a time through `TImage.Pixel` - a property write per pixel is most of what building the image would otherwise cost. - - - ## MapChunkLoader.DrawCollisionEdge ```pascal procedure TMapChunkLoader.DrawCollisionEdge(var pixels: TIntegerMatrix; drawX, drawY, edge, color: Integer); static; ``` Draws one edge of the tile whose top-left pixel is `drawX, drawY`. `edge` is `0` for the left edge, `1` for the top, `2` for the right and `3` for the bottom, which is also the order a wall's orientation uses. Nothing is drawn if that tile is outside the chunk, which happens to walls belonging to a neighbouring region. - - - ## MapChunkLoader.DrawCollisionWall ```pascal procedure TMapChunkLoader.DrawCollisionWall(var pixels: TIntegerMatrix; drawX, drawY, orientation, color: Integer; isDoor: Boolean); static; ``` Draws a wall on the edge its `orientation` points at. A door blocks the tile it swings into rather than the one it stands on, so it is drawn one edge further round, and a south-facing one lands on the row below the tile entirely - which can be the top row of the chunk to the south. - - - ## MapChunkLoader.DrawCollisionObjectAtTile ```pascal procedure TMapChunkLoader.DrawCollisionObjectAtTile(var pixels: TIntegerMatrix; constref loc: TWorldLocation; x, y: Integer); ``` Marks the tiles one object blocks on a collision map. Walls and corners block the edge they sit on - or the next edge round, if the object is an openable door, since a closed door blocks the tile it opens into rather than the one it stands on. Everything larger blocks its whole footprint, rotated with the object, and does not paint over a wall that was already marked there. `x, y` are relative to the chunk being drawn and can fall outside it: an object standing in a neighbouring region is passed in too, and only the part of it that reaches into this chunk is drawn, the same way the client clips a footprint to its collision map. - - - ## MapChunkLoader.DrawCollisionLocation ```pascal procedure TMapChunkLoader.DrawCollisionLocation(var pixels: TIntegerMatrix; constref region: TRegion; constref loc: TWorldLocation; z, offsetX, offsetY: Integer); ``` Marks one of `region`'s objects on the collision map of plane `z`, if it is on that plane. On the ground plane a bridge tile takes its objects from plane 1, since that is the surface being walked on, and tiles the plane says are not rendered are skipped. `offsetX, offsetY` is where `region` sits relative to the chunk being drawn, in tiles: `0, 0` for the chunk itself, or `-64, 0` for the region west of it. - - - ## MapChunkLoader.DrawCollisionObjectsOnPlane ```pascal procedure TMapChunkLoader.DrawCollisionObjectsOnPlane(var pixels: TIntegerMatrix; constref region: TRegion; z: Integer); ``` Marks every object of a region that blocks movement on plane `z`. - - - ## MapChunkLoader.GetRegionOverhangs ```pascal function TMapChunkLoader.GetRegionOverhangs(regionIdx: Integer): TIntegerArray; ``` The indices into a region's `Locations` of the objects that can reach into a neighbouring chunk's collision map: footprints running past the region's east or north edge, and walls on its bottom row, since a door there can draw on the row below. Worked out once per region and cached, so drawing a chunk's neighbours only goes through the few objects on their edges instead of all of them. - - - ## MapChunkLoader.DrawCollisionObjects ```pascal procedure TMapChunkLoader.DrawCollisionObjects(var pixels: TIntegerMatrix; regionX, regionY, z: Integer); ``` Marks every object that blocks movement on plane `z` of the chunk at `regionX, regionY`. An object's position is its south-west tile and its footprint grows east and north, so objects standing in the regions to the west, south and south-west can reach into this chunk, and a door on the bottom row of the region to the north can reach its top row. Those are drawn as well, clipped to the chunk, so the collision has no gaps along chunk boundaries. The chunk's own objects go last, so where a neighbour's footprint meets one of its doors the door is what shows. - - - ## MapChunkLoader.GetChunkCollisionMap ```pascal function TMapChunkLoader.GetChunkCollisionMap(regionX, regionY, z: Integer): TCollisionMap; ``` Which tiles of one region are blocked on plane `z`, one entry per tile: `1` for blocked terrain and `0` for walkable. This is the terrain flag only - objects are not included. Use {ref}`TMapChunkLoader.GetChunkCollisionImage` for a picture that has them. - - - ## MapChunkLoader.GetChunkCollisionImage ```pascal function TMapChunkLoader.GetChunkCollisionImage(regionX, regionY, z: Integer): TImage; ``` One region's collision drawn at the same scale as its minimap image, with blocked terrain, walls and doors, and the footprints of the objects standing on it. - - - ## MapChunkLoader.GetChunksCollisionMap ```pascal function TMapChunkLoader.GetChunksCollisionMap(bounds: TBox; z: Integer): TCollisionMap; ``` {ref}`TMapChunkLoader.GetChunkCollisionMap` across a rectangle of regions, stitched into one map indexed `[y, x]`. - - - ## MapChunkLoader.GetChunksCollisionImage ```pascal function TMapChunkLoader.GetChunksCollisionImage(bounds: TBox; z: Integer): TImage; ``` {ref}`TMapChunkLoader.GetChunkCollisionImage` across a rectangle of regions, stitched into one image.