Map painting

Map painting is the act of translating a building/surface definition, and instancing parameters, into a set of blocks and block stacks inside the map structure.

The canvas

All painting is performed using a 'canvas' object - an uncompressed 3D array of blocks. Using canvases over direct map editing is forseen as having several advantages - mainly in terms of speed and simplification of code. It also helps to reduce the number of obsolete block and stack definitions that will be present in the finished map structure, as typically a stack will only be modified once. Functions are used to paste canvases into the map once they are complete, and to convert existing map areas into canvases in order to amend them. Each canvas can also be rotated, to simplify the task of painting buildings which need to be rotated to fit in their space, as well as simplify the task of painting the walls on each side of a building.

Surface definitions

Surface definiton files describe what buildings, roads, paths, etc. look like graphically. They contain lists of tile textures, each texture flagged with its intended use. Depending on the surface type being defined, there will be significant differences in the number and type of textures specified.

Surface definition files also allow for the definiton of 'patterns' - selections of tiles that must be displayed next to each other.

Surfdef pattern names

These will consist of a number part and a letter part. The number part indicates which sides of the tile have dissimilar surface types (using the arrangement of numbers on the numeric keypad). The letter part indicates the rotations in which the definition can be used, i.e. UDLR for floor definitions and NSEW for wall definitions.

This scheme appears to be appropriate for use in the main three types of surface - grass/roofs, walls, and roads. However the intepretation for roads may need to be slightly different, in order to account for the road markings.

Painting

Internally, the definition will be stored in a 5x256-entry array, where each major entry represents a different block side (top/n/s/e/w), and each minor entry a different number configuration. Each entry therefore has a pointer to the pattern, and the rotation for which that pattern is to be used (always unrotated for wall definitions)

Painting surfdefs is then just a case of examining each block in turn, to work out whether its neighbours have identical surface types, and then looking up the correct pattern to apply to that tile. This will work fine for buildings and grass, as they are all painted in one contiguous block, but will need some extra code for roads, as they are painted section by section.

Building definitions

The current implementation of the map generator allows for the use of building definition files. These indicate the building type (chiefly in terms of its shape, e.g. rectangular), the minimum and maximum size of the building, the surface type of the surrounding ground (grass, dirt, pavement, etc.), and the surface definition to use for building itself. Flags are also present, to indicate whether the building can have added extras like a car park, stick-out signs, escape ladders, etc.

Custom buildings

Custom buildings are the ones used for mission script support. They allow for complete control over the buildings shape and appearance, as well as for relaying coordinates of certain areas to the mission script. There are two main parts to the builddef file: A 3D grid of characters representing the building shape, and a list of character definitions. The character definition lists what ASCII character is being defined, as well as the block attributes of that character. It also lists any mission script relevant info (TBD!)

Roads, paths, and the ground

The zonedef file for the current zone contains a list of surface definitions to use for the road surface, footpaths, and general ground. This list of surfaces will be examined when deciding what surface definition to paint for the grass/pavement around buildings, the roads between buildings, etc.

Road intersections

When painting navigation flags for a road intersection, the basic plan is to make sure that every entrance can reach every exit. An algorithm that fulfills this goal has been implemented in the 'roadplan' BASIC file, and has been used as the basis for DeathDawn's road intersection code. The advantage of roadplan is that is is able to deal with all types of road junction (i.e. from 1-3 exit roads with 1-3 lanes each). It cannot cope with dead-ends, but this is a trivial case to add support for.

Although the painting algorithm has now been integrated, it is still unclear how best to make use of it, considering that the code paints by edge instead of by node (corner). However, since the algorithm deals with one entrance road at a time, it is feasible that it could be adapted to work on an edge-by-edge basis - i.e. it would:

  1. Paint the navigation flags for the first end of the edge
  2. Examine the first edge, to see what the bounds are of the intersection area. If a tile contains more than one navigation flag, then it is considered to be inside the intersection.
  3. Make sure all tiles within the intersection have the appropriate 'intersection' tile sprites. This is basically an iterative process that involves fully repainting the intersection each time it is updated.
  4. Repeat this process for the other end of the edge
  5. Fill the remaining area between the intersections with regular, straight road sections

This should produce an algorithm that works to an acceptable manner with intersections that overlap (i.e. staggered crossroads) - although the performance cannot be known for certain until the algorithm is implemented.

The intersection-size-by-interrogation calculation can also be used to mark other areas - such as traffic lights and pedestrian crossings. Similarly, these can be brute-force modified as well.

An alternative to repainting the intersection every time it is updated would be to paint the straight sections of roads, and the intersection navigation flags, and then afterwards run through the road list again to calculate the sizes of the intersections and paint them. Code could detect if multiple intersections overlap, and merge them, placing one set of traffic lights in control (if traffic lights are required). The traffic light control system can then examine the navigation flags to work out what sequence to play the lights in (so that no paths cross). This would be a round-robin algorithm, where each incoming road is turned on and checked in turn to see if its movement paths intersect other roads (However, it is unlikely - perhaps impossible - that multiple active roads are possible? Unless it is performed on a per-lane basis rather than a per-road basis). Calculations of movement paths in this manner would require similar block-level navigation code to that which would be held in cars; i.e. code to ensure the car doesn't do a U-turn unless that is the only path available. Pedestrian crossings can also be installed at the appropriate points.

Road tile painting

The road tile to be used can be calculated by looking at the following factors:

  1. The entrance points to the tile (by examining surrounding tiles)
  2. The exit points of the tile (by examining the tile itself)
  3. Which sides/corners of the tile are at the edge of the road (by using dissimilar flags)
  4. If the tile should have a stripe or not

The first two entries dictate what the inside of the tile will look like - a total of 16 combinations (not taking into account identical rotations). The dissimilar flags are then combined with this value; however some combinations are impossible (all roadways should be fully contained, so it should be impossible for a road surface to lead off into a dissimilar tile). This means that it won't quite be a multiple of the two combinations. Finally the stripe flag will be checked; rather than implement this as a seperate surfdef type, it is probably best for all appropriate tiles to have a 2-tile surfdef, where the first tile is unstriped and the second striped.

Code that sets road flags must also clear pavement flags (in the case where a pavement has already been painted into the spaces where a road should be); similarly, pavement painting must first check to make sure no road flags are set. Any pedestrian crossings will be added to the map afterwards, using the intersection-intersection (traffic lights) algorithm.

TODO:
MUST-DOCS: UNKNOWN: Finish builddef/surfdef design. Building types, sprites required for the different surfdef types, etc.
MUST-CODE: MED: Other painting bits - block content types (concrete/grass/dirt/water, radiation for irradiated map zones/maps, electric for railways, etc.)
MUST-DOCS: UNKNOWN: map objects (traffic lights, rocks, trees, etc.)
MUST-CODE: QUICK: Add block content type to surfdef, fix mappaint to use
MUST-DOCS: MED: Update road intersections section above with details of current implementation/changes that need to be made etc.
MUST-CODE: MED: Improve building placement code. Needs to be possible for terraces to be generated, etc.
MUST-CODE: MED: Fix sloping road/path painting setting the edge heights of blocks that are beyond the sloping zone
???
Profit!