//
// Map module
//
include "inc/cmdsys.plh"
include "inc/conio.plh"
include "inc/fileio.plh"

//
// Title page
//

byte[] initstr
byte = " (        )\n"
byte = " )\\ )  ( /(  (\n"
byte = "(()/(  )\\()) )\\ )       (   (\n"
byte = " /(_))((_)\\ (()/(       )\\  )\\\n"
byte = "(_))    ((_) /(_))_  _ ((_)((_)\n"
byte = "| _ \\  / _ \\(_)) __|| | | || __|\n"
byte = "|   / | (_) | | (_ || |_| || _|\n"
byte = "|_|_\\  \\___/   \\___| \\___/ |___|\n"
byte = "\n"
byte = "       By Resman\n"
byte = "       Artwork by Seth Sternberger\n"
byte = ""
word titlestr = @initstr

//
// Octant beam parameters
//

const beamdepth = 23

byte dbeam = 0,2,4,8,12,17,22,28,37,44,53,63,72,83,96,109,123,137,151,166,183,202,220,239
const beampts = 240

byte[] xbeam
byte = 0
byte = 1,0
byte = 1,0
byte = 2,2,1,0
byte = 3,2,1,0
byte = 4,3,2,1,0
byte = 4,3,2,1,0
byte = 5,4,3,2,1,0
byte = 6,5,5,4,3,3,2,1,0
byte = 6,5,4,3,2,1,0
byte = 7,7,6,5,4,3,2,1,0
byte = 8,7,6,6,5,4,3,2,1,0
byte = 8,7,6,5,4,3,2,1,0
byte = 9,9,8,7,6,5,4,3,2,1,0
byte = 10,9,8,8,7,6,5,4,4,3,2,1,0
byte = 11,10,10,9,8,7,6,5,4,3,2,1,0
byte = 11,10,9,9,8,7,7,6,5,4,3,2,1,0
byte = 12,12,11,10,9,8,7,6,5,4,3,2,1,0
byte = 13,12,11,10,9,8,7,6,5,4,3,2,1,0
byte = 13,12,11,11,10,9,8,7,6,5,4,3,2,1,0
byte = 14,14,13,12,11,10,10,9,8,7,6,5,4,3,2,1,0
byte = 15,14,13,13,12,12,11,10,9,8,8,7,6,5,4,3,2,1,0
byte = 16,15,14,13,12,11,10,9,8,7,6,5,5,4,3,2,1,0
byte = 16,15,15,14,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0

byte[] ybeam
byte = 0
byte = 1,1
byte = 2,2
byte = 2,3,3,3
byte = 3,4,4,4
byte = 4,4,5,5,5
byte = 5,5,6,6,6
byte = 5,6,6,7,7,7
byte = 6,6,7,7,7,8,8,8,8
byte = 7,8,8,9,9,9,9
byte = 7,8,8,9,9,10,10,10,10
byte = 8,9,9,10,10,10,11,11,11,11
byte = 9,10,11,11,11,12,12,12,12
byte = 9,10,10,11,12,12,12,13,13,13,13
byte = 10,11,11,12,12,13,13,13,14,14,14,14,14
byte = 11,11,12,12,13,13,14,14,15,15,15,15,15
byte = 12,13,13,14,14,14,15,15,15,16,16,16,16,16
byte = 12,13,13,14,15,15,16,16,16,17,17,17,17,17
byte = 13,14,14,15,16,16,17,17,17,18,18,18,18,18
byte = 14,15,15,16,16,17,17,18,18,18,19,19,19,19,19
byte = 14,15,15,16,17,17,18,18,18,19,19,19,20,20,20,20,20
byte = 15,16,16,17,17,18,18,19,19,19,20,20,20,20,21,21,21,21,21
byte = 16,16,17,18,19,19,20,20,21,21,21,21,22,22,22,22,22,22
byte = 17,17,18,18,19,19,20,20,21,21,22,22,22,23,23,23,23,23,23

byte[] vbeam
byte = 0
byte = 0,0
byte = 2,2
byte = 1,3,4,4
byte = 5,7,7,8
byte = 9,6,11,11,12
byte = 14,10,16,16,17
byte = 13,19,15,21,21,22
byte = 23,18,24,25,20,26,26,27,28
byte = 30,32,33,35,35,36,37
byte = 29,38,31,40,34,42,42,43,44
byte = 45,47,39,48,49,41,51,51,52,53
byte = 46,56,58,59,50,60,61,62,63
byte = 54,64,55,57,67,68,60,69,70,71,72
byte = 73,75,65,76,66,78,79,69,80,80,81,82,83
byte = 84,74,85,86,88,77,90,91,93,93,94,95,96
byte = 98,100,87,101,102,89,103,104,92,105,106,107,108,109
byte = 97,110,99,112,114,115,117,118,105,119,120,121,122,123
byte = 124,126,111,113,129,116,131,132,119,133,134,135,136,137
byte = 125,140,127,141,128,143,130,145,146,133,147,148,149,150,151
byte = 138,152,139,154,156,142,157,158,144,160,161,147,162,163,164,165,166
byte = 167,169,153,170,155,171,172,174,175,159,176,177,178,178,179,180,181,182,183
byte = 184,168,186,188,190,173,192,193,195,196,197,197,214,198,199,200,201,202
byte = 204,185,205,187,206,189,208,191,210,194,212,213,214,215,216,217,218,219,220

byte[beampts] vispix = 1

const rowshift = 6
const maprows  = 64
const mapcols  = 64
const mapsize  = maprows*mapcols

const WALL_TILE   = '#'
const FLOOR_TILE  = '.'
const TORCH_TILE  = '*'
const PIT_TILE    = ' '
const DOOR_TILE   = '+'
const LOCKED_TILE = '%'
const WATER1_TILE = '>'
const WATER2_TILE = '<'
const KEY_TILE    = ','
const RAFT_TILE   = '@'
const GOLD_TILE   = '$'
const FOOD_TILE   = '&'
const ENTER_TILE  = '-'
const EXIT_TILE   = '='
const OPAQUE_TILE = $01
const VIEWED_TILE = $80
const LIT_TILE    = $80
const INV_TILE    = $3F
const MAP_TILE    = $7F

byte catacomb = "LEVEL0"
word map, viewmap

const xcentr     = 20
const ycentr     = 12
const scrnwidth  = 40
const scrnheight = 22

//
// Buffer to void checks in octant caster
//

byte[scrnwidth] linebuffer

word = @linebuffer, @linebuffer, @linebuffer, @linebuffer, @linebuffer
word = @linebuffer, @linebuffer, @linebuffer, @linebuffer, @linebuffer

word[] screen
word = $400, $480, $500, $580, $600, $680, $700, $780
word = $428, $4A8, $528, $5A8, $628, $6A8, $728, $7A8
word = $450, $4D0, $550, $5D0, $650, $6D0, $750, @linebuffer // $7D0

word = @linebuffer, @linebuffer, @linebuffer, @linebuffer, @linebuffer
word = @linebuffer, @linebuffer, @linebuffer, @linebuffer, @linebuffer

//
// Load map - levels 0 to 9
//

export def loadmap(level)
  byte mapref, row, l

  //
  // Init the view map
  //
  if not viewmap
    viewmap = heapalloc(mapsize)
  fin
  memset(viewmap, $A0A0, mapsize)
  //
  // Load catacomb map from file
  //
  if not map
    map = heapalloc(mapsize)
  fin
  memset(map, $2323, mapsize) // Solid walls
  //
  // Set level map and read it
  //
  catacomb[catacomb] = '0' + level
  mapref = fileio:open(@catacomb)
  if mapref
    fileio:newline(mapref, $7F, $0D)
    for row = 1 to maprows - 2
      l = fileio:read(mapref, map + (row << 6) + 1, mapcols)
      ^(map + (row << 6) + l) = WALL_TILE
    next
    fileio:close(mapref)
    return TRUE
  fin
  return FALSE
end

//
// Tile access to map
//

export def getmaptile(xmap, ymap)
  return ^(map + (ymap << rowshift) + xmap)
end

export def setmaptile(xmap, ymap, tile)#0
  word imap

  imap = (ymap << rowshift) + xmap
  ^(map + imap) = tile
  if ^(viewmap + imap) <> $A0
    ^(viewmap + imap) = tile | VIEWED_TILE
  fin
end

export def updtmaptile(xmap, ymap, tile)#0
  word imap

  imap = (ymap << rowshift) + xmap
  ^(map + imap) = (^(map + imap) & LIT_TILE) | tile
  if ^(viewmap + imap) <> $A0
    ^(viewmap + imap) = tile | VIEWED_TILE
  fin
end

//
// Light torches in map
//

export def lighttorches#0
  word imap, tmap
  byte xmap, ymap, xt, yt

  for ymap = 1 to maprows - 2
    for xmap = 1 to mapcols - 2
      imap = (ymap << rowshift) + xmap
      if ^(map + imap) & MAP_TILE == TORCH_TILE
        for yt = ymap - 1 to ymap + 1
          for xt = xmap - 1 to xmap + 1
            tmap = (yt << rowshift) + xt
            ^(map + tmap) = ^(map + tmap) | LIT_TILE
          next
        next
      fin
    next
    if not (ymap & 7)
      putc('.')
    fin
  next
end

//
// Draw the map. Return 0 if any light visible, 1 if in complete darkness
//

export def drawmap(xorg, yorg, viewfield, viewdir, lightdist, viewdist)
  byte l, dist, tile, adjtile, occluded, darkness
  word ymap, xmap, imap, o
  byte yscr, xscr

  if viewdist > beamdepth
    viewdist = beamdepth
  fin
  if lightdist > viewdist
    lightdist = viewdist
  fin
  //
  // Clear screen
  //
  conio:home()
  //
  // Draw background map if in light
  //
  darkness = 1
  imap     = (yorg << rowshift) + xorg
  if lightdist or ^(map + imap) & LIT_TILE
    //
    // Update current spot in viewmap
    //
    ^(viewmap + imap) = ^(map + imap) | VIEWED_TILE
    //
    // Draw the viewmap
    //
    xmap = xorg - xcentr
    if xmap < 0
      l    = mapcols
      xscr = -xmap
      xmap = 0
    else
      l    = mapcols - xmap
      xscr = 0
    fin
    if xscr + l > scrnwidth
      l = scrnwidth - xscr
    fin
    for yscr = 0 to scrnheight
      ymap = yscr - ycentr + yorg
      if ymap >= 0 and ymap < maprows
        memcpy(screen[yscr] + xscr, viewmap + (ymap << rowshift) + xmap, l)
      fin
    next
    darkness = 0
  fin
  //
  // Draw visible octants
  //
  for o = viewdir - viewfield - 1 to viewdir + viewfield
    dist     = lightdist + 1
    occluded = 1
    when o & 7
      is 0
        //
        // Run through lit octant beam points
        //
        for l = 1 to dbeam[lightdist]
          //
          // Check parent visiblity
          //
          if vispix[vbeam[l]]
            imap = ((yorg - ybeam[l]) << rowshift) + xorg + xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              //
              // The view stops here
              //
              vispix[l] = 0
            else
              //
              // This tile is transparent
              //
              vispix[l] = 1
              //
              // Check adjacent tile for opaqueness - improves wall display
              //
              adjtile = ^(map + imap + 1) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap + 1) = adjtile | VIEWED_TILE
                screen.[ycentr-ybeam[l], xcentr+xbeam[l]+1] = adjtile
              fin
            fin
            //
            // Update view
            //
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr-ybeam[l], xcentr+xbeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        //
        // Run through visible octant beam points
        //
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          //
          // Check parent visiblity
          //
          if vispix[vbeam[l]]
            imap = ((yorg - ybeam[l]) << rowshift) + xorg + xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              //
              // The view stops here
              //
              vispix[l] = 0
            else
              //
              // This tile is transparent
              //
              vispix[l] = 1
              occluded  = 0
            fin
            //
            // If the tile is in light, update view
            //
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr-ybeam[l], xcentr+xbeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          //
          // Advance beam distance
          //
          if l == dbeam[dist]
            if occluded
              //
              // Beam fully occluded
              //
              break
            fin
            //
            // Update distance
            //
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 1
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg - xbeam[l]) << rowshift) + xorg + ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap - mapcols) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap - mapcols) = adjtile | VIEWED_TILE
                screen.[ycentr-xbeam[l]-1, xcentr+ybeam[l]] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr-xbeam[l], xcentr+ybeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          if vispix[vbeam[l]]
            imap = ((yorg - xbeam[l]) << rowshift) + xorg + ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr-xbeam[l], xcentr+ybeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 2
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg + xbeam[l]) << rowshift) + xorg + ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap + mapcols) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap + mapcols) = adjtile | VIEWED_TILE
                screen.[ycentr+xbeam[l]+1, xcentr+ybeam[l]] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr+xbeam[l], xcentr+ybeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          if vispix[vbeam[l]]
            imap = ((yorg + xbeam[l]) << rowshift) + xorg + ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr+xbeam[l], xcentr+ybeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 3
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg + ybeam[l]) << rowshift) + xorg + xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap + 1) & INV_TILE
              if  adjtile & OPAQUE_TILE
                ^(viewmap + imap + 1) = adjtile | VIEWED_TILE
                screen.[ycentr+ybeam[l], xcentr+xbeam[l]+1] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr+ybeam[l], xcentr+xbeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          if vispix[vbeam[l]]
            imap = ((yorg + ybeam[l]) << rowshift) + xorg + xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr+ybeam[l], xcentr+xbeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 4
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg + ybeam[l]) << rowshift) + xorg - xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap - 1) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap - 1) = adjtile | VIEWED_TILE
                screen.[ycentr+ybeam[l], xcentr-xbeam[l]-1] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr+ybeam[l], xcentr-xbeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          if vispix[vbeam[l]]
            imap = ((yorg + ybeam[l]) << rowshift) + xorg - xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr+ybeam[l], xcentr-xbeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 5
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg + xbeam[l]) << rowshift) + xorg - ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap + mapcols) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap + mapcols) = adjtile | VIEWED_TILE
                screen.[ycentr+xbeam[l]+1, xcentr-ybeam[l]] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr+xbeam[l], xcentr-ybeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          if vispix[vbeam[l]]
            imap = ((yorg + xbeam[l]) << rowshift) + xorg - ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr+xbeam[l], xcentr-ybeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 6
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg - xbeam[l]) << rowshift) + xorg - ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap - mapcols) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap - mapcols) = adjtile | VIEWED_TILE
                screen.[ycentr-xbeam[l]-1, xcentr-ybeam[l]] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr-xbeam[l], xcentr-ybeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          if vispix[vbeam[l]]
            imap = ((yorg - xbeam[l]) << rowshift) + xorg - ybeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr-xbeam[l], xcentr-ybeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
      is 7
        for l = 1 to dbeam[lightdist]
          if vispix[vbeam[l]]
            imap = ((yorg - ybeam[l]) << rowshift) + xorg - xbeam[l]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              adjtile = ^(map + imap - 1) & INV_TILE
              if adjtile & OPAQUE_TILE
                ^(viewmap + imap - 1) = adjtile | VIEWED_TILE
                screen.[ycentr-ybeam[l], xcentr-xbeam[l]-1] = adjtile
              fin
            fin
            ^(viewmap + imap) =  tile | VIEWED_TILE
            if tile <> PIT_TILE
              screen.[ycentr-ybeam[l], xcentr-xbeam[l]] = tile & INV_TILE
            fin
          else
            vispix[l] = 0
          fin
        next
        for l = dbeam[lightdist]+1 to dbeam[viewdist]
          imap = ((yorg - ybeam[l]) << rowshift) + xorg - xbeam[l]
          if vispix[vbeam[l]]
            tile = ^(map + imap)
            if tile & OPAQUE_TILE
              vispix[l] = 0
            else
              vispix[l] = 1
              occluded  = 0
            fin
            if tile & LIT_TILE
              ^(viewmap + imap) =  tile | VIEWED_TILE
              screen.[ycentr-ybeam[l], xcentr-xbeam[l]] = tile & INV_TILE
              darkness = 0
            fin
          else
            vispix[l] = 0
          fin
          if l == dbeam[dist]
            if occluded
              break
            fin
            occluded = 1
            dist     = dist + 1
          fin
        next
        break
    wend
  next
  return darkness
end

//
// Draw other entities
//

export def drawvisentity(xofst, yofst, tile)#0
  if screen.[ycentr+yofst, xcentr+xofst] < $80
    screen.[ycentr+yofst, xcentr+xofst] = tile
  fin
end

//
// Print title page
//

conio:home()
while ^titlestr
  puts(titlestr)
  titlestr = titlestr + ^titlestr + 1
loop

done
