Skip to content

City Generation

Carmina16 edited this page Mar 15, 2018 · 10 revisions

Arena Random Generator

This generator controls most of procedural world generation in Arena.

seed <- 12345 # (unsigned 32-bit)

srand(x) <-
   seed <- x

rnd() <-
   seed <- seed * 7143469
   return (seed >> 16) & 0xFFFF

City Templates

There are eight city templates (five land-locked, three coastal) and seven town and village templates (five land-locked, two coastal):

templateCount <- cityId in coastalCities ? (isCity(cityId) ? 3 : 2) : 5
templateId <- cityId mod templateCount
templateName <- format("%s%d.MIF", baseName, templateId + 1)

where baseName is CITY, CITYW, TOWN, TOWNW, VILLAGE, or VILLAGW.

Several template IDs are associated with a reserved block list:

  • 1 => (15, 21)
  • 2 => (12, 14, 15, 17)
  • 5 (CITYW1/TOWNW1/VILLAGW1) => (29, 33, 34, 35)
  • 6 => (33, 34, 35)
  • 7 (CITYW3) => (4, 5, 11, 17)

Other lists are empty.

  • The coastal city list is 58 bytes @43BD8
  • The template filenames are szlist @43C12 (6 elements)
  • The X, Y starting positions of templates are 44 bytes (22 pairs) @43C85
  • The name of the MIF for the IC (with the static layout) is @43CB1
  • The lists of the reserved blocks are szlist @43CBE (8 elements)

City Plans

Each city is constructed from a square array of blocks numbered right-to-left, top-to-bottom. Cities are 6x6, towns 5x5, and villages 4x4.

# Block types
EMPTY=0
RESERVED=1
EQUIPMENT=2
MAGEGUILD=3
NOBLEHOUSE=4
TEMPLE=5
TAVERN=6
SPACER=7
HOUSES=8

# Helper functions
placeBlock(blockType) <-
   do
      pos <- rnd() mod SIZE
   while plan[pos] != EMPTY
   plan[pos] <- blockType

SIZE <- N*N
plan <- [EMPTY]*SIZE

citySeed <- (cityX << 16) + cityY
srand(citySeed)

for block in reservedBlocks
   plan[block] <- RESERVED

placeBlock(EQUIPMENT)
placeBlock(MAGEGUILD)
placeBlock(NOBLEHOUSE)
placeBlock(TEMPLE)
placeBlock(TAVERN)
placeBlock(SPACER)

remainder <- countof EMPTY in plan
while remainder > 0
   random <- rnd()
   if random <= 0x7333 then placeBlock(HOUSES)
   elseif random <= 0xA666 then placeBlock(TAVERN)
   elseif random <= 0xCCCC then placeBlock(EQUIPMENT)
   elseif random <= 0xE666 then placeBlock(TEMPLE)
   else placeBlock(NOBLEHOUSE)
   remainder <- remainder - 1

Building a City

Each city template has a unique point where the layout begins. It is important to preserve the random number generator state after generating the plan.

X <- startX
Y <- startY

for block in plan
   if block = RESERVED then continue
   blockCode <- ("eq","mg","nb","tp","tv","ts","bs")[block-2]
   variationCount <- (13,11,10,12,15,11,20)[block-2]
   rotation <- ("a","b","c","d")[rnd() mod 4]
   variation <- rnd() mod variationCount
   if variation = 0 then variation <- variation + 1
   blockName <- format("%sBD%d%s.MIF", blockCode, variation, rotation)
   load blockName and merge it with the template at (X, Y)
   X <- X + 20
   if row ended
      X <- startX
      Y <- Y + 20

Interior names

Interior names are generated immediately after the city creation in the following order: taverns, shops, temples. The tavern seed value is carried over from the city generation. X, Y are the city local coordinates.

if shop or temple then
   seed <- (X << 16) + Y
   srand(seed)
endif
tavernSfx <- defaultTavernSfx
if tavern and cityid in coastalCities then
   tavernSfx <- marineTavernSfx
seen <- []
for block in cityBlocks
   if block menutype is the object currently generated then
      if tavern then
         do
            m <- rnd() mod 23
            n <- rnd() mod 23
            hash <- (m << 8) + n
         while hash in seen
         tavernName <- tavernPfx[m] + " " + tavernSfx[n]
      else if shop then
         do
            m <- rnd() mod 20
            n <- rnd() mod 10
            hash <- (m << 8) + n
         while hash in seen
         shopName <- shopPfx[m] + " " + shopSfx[n]
      else # temple
         do
            model <- rnd() mod 3
            vars <- (5,9,10)[model]
            n <- rnd() mod vars
            hash <- (model << 8) + n
         while hash in seen
         shopName <- templePfx[model] + " " + (templeSfx[model])[n]
      endif
      <add the name and the current block pos into the corresponding list>
      seen.add(hash)
if temple and cityid in (2, 0xE0) then
   if cityid == 2 then
      model, n <- 1, 7
   else
      model, n <- 2, 8
   <generate a temple name with those values and replace the last temple with it>
endif

Variables

The shop names can have variables. x, y are the position of the shop door block on the city map.

%ct  Replace with the city type string: "City-state", etc.
%ef  Generate a male name for the current province with the seed (y<<16)+x, take the first name only
%n   Generate a male name for the current province with the seed (x<<16)+y [sic!]

TODO: string positions.